Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wirel...
authorJohn W. Linville <linville@tuxdriver.com>
Thu, 20 Feb 2014 20:02:02 +0000 (15:02 -0500)
committerJohn W. Linville <linville@tuxdriver.com>
Thu, 20 Feb 2014 20:02:02 +0000 (15:02 -0500)
211 files changed:
drivers/net/wireless/ath/ath.h
drivers/net/wireless/ath/ath10k/txrx.c
drivers/net/wireless/ath/ath6kl/cfg80211.c
drivers/net/wireless/ath/ath9k/Kconfig
drivers/net/wireless/ath/ath9k/Makefile
drivers/net/wireless/ath/ath9k/ar9003_calib.c
drivers/net/wireless/ath/ath9k/ath9k.h
drivers/net/wireless/ath/ath9k/beacon.c
drivers/net/wireless/ath/ath9k/common.c
drivers/net/wireless/ath/ath9k/common.h
drivers/net/wireless/ath/ath9k/debug.h
drivers/net/wireless/ath/ath9k/dfs_debug.h
drivers/net/wireless/ath/ath9k/hif_usb.c
drivers/net/wireless/ath/ath9k/htc.h
drivers/net/wireless/ath/ath9k/htc_drv_init.c
drivers/net/wireless/ath/ath9k/htc_drv_main.c
drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
drivers/net/wireless/ath/ath9k/htc_hst.c
drivers/net/wireless/ath/ath9k/htc_hst.h
drivers/net/wireless/ath/ath9k/hw.c
drivers/net/wireless/ath/ath9k/init.c
drivers/net/wireless/ath/ath9k/mac.h
drivers/net/wireless/ath/ath9k/main.c
drivers/net/wireless/ath/ath9k/rc.c [deleted file]
drivers/net/wireless/ath/ath9k/rc.h [deleted file]
drivers/net/wireless/ath/ath9k/recv.c
drivers/net/wireless/ath/ath9k/xmit.c
drivers/net/wireless/ath/wcn36xx/dxe.c
drivers/net/wireless/ath/wcn36xx/dxe.h
drivers/net/wireless/ath/wcn36xx/hal.h
drivers/net/wireless/ath/wcn36xx/main.c
drivers/net/wireless/ath/wcn36xx/smd.c
drivers/net/wireless/ath/wcn36xx/smd.h
drivers/net/wireless/ath/wcn36xx/txrx.c
drivers/net/wireless/ath/wcn36xx/wcn36xx.h
drivers/net/wireless/b43/main.h
drivers/net/wireless/b43/xmit.c
drivers/net/wireless/brcm80211/brcmfmac/Makefile
drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
drivers/net/wireless/brcm80211/brcmfmac/chip.c [new file with mode: 0644]
drivers/net/wireless/brcm80211/brcmfmac/chip.h [new file with mode: 0644]
drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
drivers/net/wireless/brcm80211/brcmfmac/fwil.c
drivers/net/wireless/brcm80211/brcmfmac/fwil.h
drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c [deleted file]
drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h [deleted file]
drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h
drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h
drivers/net/wireless/iwlegacy/3945-rs.c
drivers/net/wireless/iwlegacy/4965-rs.c
drivers/net/wireless/iwlwifi/Kconfig
drivers/net/wireless/iwlwifi/Makefile
drivers/net/wireless/iwlwifi/dvm/rs.c
drivers/net/wireless/iwlwifi/dvm/rs.h
drivers/net/wireless/iwlwifi/iwl-7000.c
drivers/net/wireless/iwlwifi/iwl-8000.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/iwl-config.h
drivers/net/wireless/iwlwifi/iwl-csr.h
drivers/net/wireless/iwlwifi/iwl-debug.h
drivers/net/wireless/iwlwifi/iwl-drv.c
drivers/net/wireless/iwlwifi/iwl-fw.h
drivers/net/wireless/iwlwifi/iwl-io.c
drivers/net/wireless/iwlwifi/iwl-io.h
drivers/net/wireless/iwlwifi/iwl-nvm-parse.c
drivers/net/wireless/iwlwifi/iwl-op-mode.h
drivers/net/wireless/iwlwifi/iwl-phy-db.c
drivers/net/wireless/iwlwifi/iwl-prph.h
drivers/net/wireless/iwlwifi/iwl-trans.h
drivers/net/wireless/iwlwifi/mvm/Makefile
drivers/net/wireless/iwlwifi/mvm/bt-coex.c
drivers/net/wireless/iwlwifi/mvm/constants.h
drivers/net/wireless/iwlwifi/mvm/d3.c
drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c
drivers/net/wireless/iwlwifi/mvm/debugfs.c
drivers/net/wireless/iwlwifi/mvm/fw-api-bt-coex.h
drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h
drivers/net/wireless/iwlwifi/mvm/fw-api-power.h
drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h
drivers/net/wireless/iwlwifi/mvm/fw-api.h
drivers/net/wireless/iwlwifi/mvm/fw.c
drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c
drivers/net/wireless/iwlwifi/mvm/mac80211.c
drivers/net/wireless/iwlwifi/mvm/mvm.h
drivers/net/wireless/iwlwifi/mvm/nvm.c
drivers/net/wireless/iwlwifi/mvm/ops.c
drivers/net/wireless/iwlwifi/mvm/power.c
drivers/net/wireless/iwlwifi/mvm/power_legacy.c [deleted file]
drivers/net/wireless/iwlwifi/mvm/quota.c
drivers/net/wireless/iwlwifi/mvm/rs.c
drivers/net/wireless/iwlwifi/mvm/rs.h
drivers/net/wireless/iwlwifi/mvm/rx.c
drivers/net/wireless/iwlwifi/mvm/scan.c
drivers/net/wireless/iwlwifi/mvm/sta.c
drivers/net/wireless/iwlwifi/mvm/sta.h
drivers/net/wireless/iwlwifi/mvm/time-event.c
drivers/net/wireless/iwlwifi/mvm/tt.c
drivers/net/wireless/iwlwifi/mvm/tx.c
drivers/net/wireless/iwlwifi/mvm/utils.c
drivers/net/wireless/iwlwifi/pcie/drv.c
drivers/net/wireless/iwlwifi/pcie/rx.c
drivers/net/wireless/iwlwifi/pcie/trans.c
drivers/net/wireless/iwlwifi/pcie/tx.c
drivers/net/wireless/libertas/cfg.c
drivers/net/wireless/mac80211_hwsim.c
drivers/net/wireless/mac80211_hwsim.h
drivers/net/wireless/mwifiex/11ac.c
drivers/net/wireless/mwifiex/11ac.h
drivers/net/wireless/mwifiex/11n.c
drivers/net/wireless/mwifiex/11n.h
drivers/net/wireless/mwifiex/11n_rxreorder.c
drivers/net/wireless/mwifiex/Makefile
drivers/net/wireless/mwifiex/cfg80211.c
drivers/net/wireless/mwifiex/cfp.c
drivers/net/wireless/mwifiex/cmdevt.c
drivers/net/wireless/mwifiex/decl.h
drivers/net/wireless/mwifiex/fw.h
drivers/net/wireless/mwifiex/init.c
drivers/net/wireless/mwifiex/ioctl.h
drivers/net/wireless/mwifiex/join.c
drivers/net/wireless/mwifiex/main.h
drivers/net/wireless/mwifiex/pcie.c
drivers/net/wireless/mwifiex/scan.c
drivers/net/wireless/mwifiex/sta_cmd.c
drivers/net/wireless/mwifiex/sta_cmdresp.c
drivers/net/wireless/mwifiex/sta_event.c
drivers/net/wireless/mwifiex/sta_ioctl.c
drivers/net/wireless/mwifiex/sta_rx.c
drivers/net/wireless/mwifiex/sta_tx.c
drivers/net/wireless/mwifiex/tdls.c [new file with mode: 0644]
drivers/net/wireless/mwifiex/uap_event.c
drivers/net/wireless/mwifiex/util.c
drivers/net/wireless/mwifiex/util.h
drivers/net/wireless/mwifiex/wmm.c
drivers/net/wireless/mwifiex/wmm.h
drivers/net/wireless/rndis_wlan.c
drivers/net/wireless/rt2x00/rt2800usb.c
drivers/net/wireless/rtl818x/rtl8180/dev.c
drivers/net/wireless/rtl818x/rtl8187/dev.c
drivers/net/wireless/rtl818x/rtl818x.h
drivers/net/wireless/rtlwifi/rc.c
drivers/net/wireless/rtlwifi/rtl8188ee/trx.c
drivers/net/wireless/rtlwifi/rtl8192ce/trx.c
drivers/net/wireless/rtlwifi/rtl8192se/trx.c
drivers/net/wireless/rtlwifi/rtl8723ae/trx.c
drivers/net/wireless/ti/wl12xx/main.c
drivers/net/wireless/ti/wl12xx/wl12xx.h
drivers/net/wireless/ti/wl18xx/main.c
drivers/net/wireless/ti/wl18xx/tx.c
drivers/net/wireless/ti/wl18xx/wl18xx.h
drivers/net/wireless/ti/wlcore/acx.c
drivers/net/wireless/ti/wlcore/acx.h
drivers/net/wireless/ti/wlcore/cmd.c
drivers/net/wireless/ti/wlcore/cmd.h
drivers/net/wireless/ti/wlcore/event.c
drivers/net/wireless/ti/wlcore/hw_ops.h
drivers/net/wireless/ti/wlcore/init.c
drivers/net/wireless/ti/wlcore/io.h
drivers/net/wireless/ti/wlcore/main.c
drivers/net/wireless/ti/wlcore/ps.c
drivers/net/wireless/ti/wlcore/rx.c
drivers/net/wireless/ti/wlcore/rx.h
drivers/net/wireless/ti/wlcore/tx.c
drivers/net/wireless/ti/wlcore/tx.h
drivers/net/wireless/ti/wlcore/wlcore.h
drivers/net/wireless/ti/wlcore/wlcore_i.h
drivers/staging/rtl8821ae/rc.c
include/linux/ieee80211.h
include/net/cfg80211.h
include/net/ieee80211_radiotap.h
include/net/mac80211.h
include/uapi/linux/nl80211.h
net/mac80211/agg-tx.c
net/mac80211/cfg.c
net/mac80211/cfg.h
net/mac80211/chan.c
net/mac80211/debugfs_sta.c
net/mac80211/ht.c
net/mac80211/ibss.c
net/mac80211/ieee80211_i.h
net/mac80211/iface.c
net/mac80211/main.c
net/mac80211/mesh.c
net/mac80211/mlme.c
net/mac80211/rate.c
net/mac80211/rate.h
net/mac80211/rc80211_minstrel.c
net/mac80211/rc80211_minstrel.h
net/mac80211/rc80211_minstrel_ht.c
net/mac80211/rc80211_pid_algo.c
net/mac80211/rx.c
net/mac80211/sta_info.h
net/mac80211/status.c
net/mac80211/tx.c
net/mac80211/util.c
net/mac80211/vht.c
net/mac80211/wpa.c
net/rfkill/core.c
net/wireless/ap.c
net/wireless/chan.c
net/wireless/core.c
net/wireless/core.h
net/wireless/ibss.c
net/wireless/mesh.c
net/wireless/mlme.c
net/wireless/nl80211.c
net/wireless/nl80211.h
net/wireless/reg.c
net/wireless/reg.h
net/wireless/trace.h
net/wireless/util.c

index b59cfbe..6260b83 100644 (file)
@@ -161,6 +161,8 @@ struct ath_common {
        bool btcoex_enabled;
        bool disable_ani;
        bool bt_ant_diversity;
+
+       int last_rssi;
 };
 
 struct sk_buff *ath_rxbuf_alloc(struct ath_common *common,
index 74f45fa..27f20e0 100644 (file)
@@ -204,7 +204,7 @@ static void process_rx_rates(struct ath10k *ar, struct htt_rx_info *info,
                        break;
                /* 80MHZ */
                case 2:
-                       status->flag |= RX_FLAG_80MHZ;
+                       status->vht_flag |= RX_VHT_FLAG_80MHZ;
                }
 
                status->flag |= RX_FLAG_VHT;
@@ -266,7 +266,7 @@ void ath10k_process_rx(struct ath10k *ar, struct htt_rx_info *info)
                   status->flag & RX_FLAG_HT ? "ht" : "",
                   status->flag & RX_FLAG_VHT ? "vht" : "",
                   status->flag & RX_FLAG_40MHZ ? "40" : "",
-                  status->flag & RX_FLAG_80MHZ ? "80" : "",
+                  status->vht_flag & RX_VHT_FLAG_80MHZ ? "80" : "",
                   status->flag & RX_FLAG_SHORT_GI ? "sgi " : "",
                   status->rate_idx,
                   status->vht_nss,
index fd4c89d..c2c6f46 100644 (file)
@@ -790,7 +790,7 @@ void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel,
        if (nw_type & ADHOC_NETWORK) {
                ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "ad-hoc %s selected\n",
                           nw_type & ADHOC_CREATOR ? "creator" : "joiner");
-               cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL);
+               cfg80211_ibss_joined(vif->ndev, bssid, chan, GFP_KERNEL);
                cfg80211_put_bss(ar->wiphy, bss);
                return;
        }
@@ -861,13 +861,9 @@ void ath6kl_cfg80211_disconnect_event(struct ath6kl_vif *vif, u8 reason,
        }
 
        if (vif->nw_type & ADHOC_NETWORK) {
-               if (vif->wdev.iftype != NL80211_IFTYPE_ADHOC) {
+               if (vif->wdev.iftype != NL80211_IFTYPE_ADHOC)
                        ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
                                   "%s: ath6k not in ibss mode\n", __func__);
-                       return;
-               }
-               memset(bssid, 0, ETH_ALEN);
-               cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL);
                return;
        }
 
@@ -3256,6 +3252,15 @@ static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy,
        struct ath6kl_vif *vif = netdev_priv(dev);
        u16 interval;
        int ret, rssi_thold;
+       int n_match_sets = request->n_match_sets;
+
+       /*
+        * If there's a matchset w/o an SSID, then assume it's just for
+        * the RSSI (nothing else is currently supported) and ignore it.
+        * The device only supports a global RSSI filter that we set below.
+        */
+       if (n_match_sets == 1 && !request->match_sets[0].ssid.ssid_len)
+               n_match_sets = 0;
 
        if (ar->state != ATH6KL_STATE_ON)
                return -EIO;
@@ -3268,11 +3273,11 @@ static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy,
        ret = ath6kl_set_probed_ssids(ar, vif, request->ssids,
                                      request->n_ssids,
                                      request->match_sets,
-                                     request->n_match_sets);
+                                     n_match_sets);
        if (ret < 0)
                return ret;
 
-       if (!request->n_match_sets) {
+       if (!n_match_sets) {
                ret = ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx,
                                               ALL_BSS_FILTER, 0);
                if (ret < 0)
@@ -3286,12 +3291,12 @@ static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy,
 
        if (test_bit(ATH6KL_FW_CAPABILITY_RSSI_SCAN_THOLD,
                     ar->fw_capabilities)) {
-               if (request->rssi_thold <= NL80211_SCAN_RSSI_THOLD_OFF)
+               if (request->min_rssi_thold <= NL80211_SCAN_RSSI_THOLD_OFF)
                        rssi_thold = 0;
-               else if (request->rssi_thold < -127)
+               else if (request->min_rssi_thold < -127)
                        rssi_thold = -127;
                else
-                       rssi_thold = request->rssi_thold;
+                       rssi_thold = request->min_rssi_thold;
 
                ret = ath6kl_wmi_set_rssi_filter_cmd(ar->wmi, vif->fw_vif_idx,
                                                     rssi_thold);
index 7b96b3e..8fcc029 100644 (file)
@@ -120,18 +120,6 @@ config ATH9K_WOW
          This option enables Wake on Wireless LAN support for certain cards.
          Currently, AR9462 is supported.
 
-config ATH9K_LEGACY_RATE_CONTROL
-       bool "Atheros ath9k rate control"
-       depends on ATH9K
-       default n
-       ---help---
-         Say Y, if you want to use the ath9k specific rate control
-         module instead of minstrel_ht. Be warned that there are various
-         issues with the ath9k RC and minstrel is a more robust algorithm.
-         Note that even if this option is selected, "ath9k_rate_control"
-         has to be passed to mac80211 using the module parameter,
-         ieee80211_default_rc_algo.
-
 config ATH9K_RFKILL
        bool "Atheros ath9k rfkill support" if EXPERT
        depends on ATH9K
index a40e5c5..747975e 100644 (file)
@@ -8,7 +8,6 @@ ath9k-y +=      beacon.o \
                antenna.o
 
 ath9k-$(CONFIG_ATH9K_BTCOEX_SUPPORT) += mci.o
-ath9k-$(CONFIG_ATH9K_LEGACY_RATE_CONTROL) += rc.o
 ath9k-$(CONFIG_ATH9K_PCI) += pci.o
 ath9k-$(CONFIG_ATH9K_AHB) += ahb.o
 ath9k-$(CONFIG_ATH9K_DFS_DEBUGFS) += dfs_debug.o
index a352128..ac8301e 100644 (file)
 #define MAX_MEASUREMENT        MAX_IQCAL_MEASUREMENT
 #define MAX_MAG_DELTA  11
 #define MAX_PHS_DELTA  10
+#define MAXIQCAL        3
 
 struct coeff {
-       int mag_coeff[AR9300_MAX_CHAINS][MAX_MEASUREMENT];
-       int phs_coeff[AR9300_MAX_CHAINS][MAX_MEASUREMENT];
+       int mag_coeff[AR9300_MAX_CHAINS][MAX_MEASUREMENT][MAXIQCAL];
+       int phs_coeff[AR9300_MAX_CHAINS][MAX_MEASUREMENT][MAXIQCAL];
        int iqc_coeff[2];
 };
 
@@ -655,9 +656,6 @@ static bool ar9003_hw_calc_iq_corr(struct ath_hw *ah,
        if (i2_m_q2_a0_d1 > 0x800)
                i2_m_q2_a0_d1 = -((0xfff - i2_m_q2_a0_d1) + 1);
 
-       if (i2_p_q2_a0_d1 > 0x1000)
-               i2_p_q2_a0_d1 = -((0x1fff - i2_p_q2_a0_d1) + 1);
-
        if (iq_corr_a0_d1 > 0x800)
                iq_corr_a0_d1 = -((0xfff - iq_corr_a0_d1) + 1);
 
@@ -800,7 +798,7 @@ static bool ar9003_hw_calc_iq_corr(struct ath_hw *ah,
        if (q_q_coff > 63)
                q_q_coff = 63;
 
-       iqc_coeff[0] = (q_q_coff * 128) + q_i_coff;
+       iqc_coeff[0] = (q_q_coff * 128) + (0x7f & q_i_coff);
 
        ath_dbg(common, CALIBRATE, "tx chain %d: iq corr coeff=%x\n",
                chain_idx, iqc_coeff[0]);
@@ -831,7 +829,7 @@ static bool ar9003_hw_calc_iq_corr(struct ath_hw *ah,
        if (q_q_coff > 63)
                q_q_coff = 63;
 
-       iqc_coeff[1] = (q_q_coff * 128) + q_i_coff;
+       iqc_coeff[1] = (q_q_coff * 128) + (0x7f & q_i_coff);
 
        ath_dbg(common, CALIBRATE, "rx chain %d: iq corr coeff=%x\n",
                chain_idx, iqc_coeff[1]);
@@ -839,7 +837,8 @@ static bool ar9003_hw_calc_iq_corr(struct ath_hw *ah,
        return true;
 }
 
-static void ar9003_hw_detect_outlier(int *mp_coeff, int nmeasurement,
+static void ar9003_hw_detect_outlier(int mp_coeff[][MAXIQCAL],
+                                    int nmeasurement,
                                     int max_delta)
 {
        int mp_max = -64, max_idx = 0;
@@ -848,20 +847,20 @@ static void ar9003_hw_detect_outlier(int *mp_coeff, int nmeasurement,
 
        /* find min/max mismatch across all calibrated gains */
        for (i = 0; i < nmeasurement; i++) {
-               if (mp_coeff[i] > mp_max) {
-                       mp_max = mp_coeff[i];
+               if (mp_coeff[i][0] > mp_max) {
+                       mp_max = mp_coeff[i][0];
                        max_idx = i;
-               } else if (mp_coeff[i] < mp_min) {
-                       mp_min = mp_coeff[i];
+               } else if (mp_coeff[i][0] < mp_min) {
+                       mp_min = mp_coeff[i][0];
                        min_idx = i;
                }
        }
 
        /* find average (exclude max abs value) */
        for (i = 0; i < nmeasurement; i++) {
-               if ((abs(mp_coeff[i]) < abs(mp_max)) ||
-                   (abs(mp_coeff[i]) < abs(mp_min))) {
-                       mp_avg += mp_coeff[i];
+               if ((abs(mp_coeff[i][0]) < abs(mp_max)) ||
+                   (abs(mp_coeff[i][0]) < abs(mp_min))) {
+                       mp_avg += mp_coeff[i][0];
                        mp_count++;
                }
        }
@@ -873,7 +872,7 @@ static void ar9003_hw_detect_outlier(int *mp_coeff, int nmeasurement,
        if (mp_count)
                mp_avg /= mp_count;
        else
-               mp_avg = mp_coeff[nmeasurement - 1];
+               mp_avg = mp_coeff[nmeasurement - 1][0];
 
        /* detect outlier */
        if (abs(mp_max - mp_min) > max_delta) {
@@ -882,15 +881,16 @@ static void ar9003_hw_detect_outlier(int *mp_coeff, int nmeasurement,
                else
                        outlier_idx = min_idx;
 
-               mp_coeff[outlier_idx] = mp_avg;
+               mp_coeff[outlier_idx][0] = mp_avg;
        }
 }
 
-static void ar9003_hw_tx_iqcal_load_avg_2_passes(struct ath_hw *ah,
-                                                struct coeff *coeff,
-                                                bool is_reusable)
+static void ar9003_hw_tx_iq_cal_outlier_detection(struct ath_hw *ah,
+                                                 struct coeff *coeff,
+                                                 bool is_reusable)
 {
        int i, im, nmeasurement;
+       int magnitude, phase;
        u32 tx_corr_coeff[MAX_MEASUREMENT][AR9300_MAX_CHAINS];
        struct ath9k_hw_cal_data *caldata = ah->caldata;
 
@@ -920,21 +920,30 @@ static void ar9003_hw_tx_iqcal_load_avg_2_passes(struct ath_hw *ah,
                if (nmeasurement > MAX_MEASUREMENT)
                        nmeasurement = MAX_MEASUREMENT;
 
-               /* detect outlier only if nmeasurement > 1 */
-               if (nmeasurement > 1) {
-                       /* Detect magnitude outlier */
-                       ar9003_hw_detect_outlier(coeff->mag_coeff[i],
-                                       nmeasurement, MAX_MAG_DELTA);
-
-                       /* Detect phase outlier */
-                       ar9003_hw_detect_outlier(coeff->phs_coeff[i],
-                                       nmeasurement, MAX_PHS_DELTA);
+               /*
+                * Skip normal outlier detection for AR9550.
+                */
+               if (!AR_SREV_9550(ah)) {
+                       /* detect outlier only if nmeasurement > 1 */
+                       if (nmeasurement > 1) {
+                               /* Detect magnitude outlier */
+                               ar9003_hw_detect_outlier(coeff->mag_coeff[i],
+                                                        nmeasurement,
+                                                        MAX_MAG_DELTA);
+
+                               /* Detect phase outlier */
+                               ar9003_hw_detect_outlier(coeff->phs_coeff[i],
+                                                        nmeasurement,
+                                                        MAX_PHS_DELTA);
+                       }
                }
 
                for (im = 0; im < nmeasurement; im++) {
+                       magnitude = coeff->mag_coeff[i][im][0];
+                       phase = coeff->phs_coeff[i][im][0];
 
-                       coeff->iqc_coeff[0] = (coeff->mag_coeff[i][im] & 0x7f) |
-                               ((coeff->phs_coeff[i][im] & 0x7f) << 7);
+                       coeff->iqc_coeff[0] =
+                               (phase & 0x7f) | ((magnitude & 0x7f) << 7);
 
                        if ((im % 2) == 0)
                                REG_RMW_FIELD(ah, tx_corr_coeff[im][i],
@@ -991,7 +1000,63 @@ static bool ar9003_hw_tx_iq_cal_run(struct ath_hw *ah)
        return true;
 }
 
-static void ar9003_hw_tx_iq_cal_post_proc(struct ath_hw *ah, bool is_reusable)
+static void __ar955x_tx_iq_cal_sort(struct ath_hw *ah,
+                                   struct coeff *coeff,
+                                   int i, int nmeasurement)
+{
+       struct ath_common *common = ath9k_hw_common(ah);
+       int im, ix, iy, temp;
+
+       for (im = 0; im < nmeasurement; im++) {
+               for (ix = 0; ix < MAXIQCAL - 1; ix++) {
+                       for (iy = ix + 1; iy <= MAXIQCAL - 1; iy++) {
+                               if (coeff->mag_coeff[i][im][iy] <
+                                   coeff->mag_coeff[i][im][ix]) {
+                                       temp = coeff->mag_coeff[i][im][ix];
+                                       coeff->mag_coeff[i][im][ix] =
+                                               coeff->mag_coeff[i][im][iy];
+                                       coeff->mag_coeff[i][im][iy] = temp;
+                               }
+                               if (coeff->phs_coeff[i][im][iy] <
+                                   coeff->phs_coeff[i][im][ix]) {
+                                       temp = coeff->phs_coeff[i][im][ix];
+                                       coeff->phs_coeff[i][im][ix] =
+                                               coeff->phs_coeff[i][im][iy];
+                                       coeff->phs_coeff[i][im][iy] = temp;
+                               }
+                       }
+               }
+               coeff->mag_coeff[i][im][0] = coeff->mag_coeff[i][im][MAXIQCAL / 2];
+               coeff->phs_coeff[i][im][0] = coeff->phs_coeff[i][im][MAXIQCAL / 2];
+
+               ath_dbg(common, CALIBRATE,
+                       "IQCAL: Median [ch%d][gain%d]: mag = %d phase = %d\n",
+                       i, im,
+                       coeff->mag_coeff[i][im][0],
+                       coeff->phs_coeff[i][im][0]);
+       }
+}
+
+static bool ar955x_tx_iq_cal_median(struct ath_hw *ah,
+                                   struct coeff *coeff,
+                                   int iqcal_idx,
+                                   int nmeasurement)
+{
+       int i;
+
+       if ((iqcal_idx + 1) != MAXIQCAL)
+               return false;
+
+       for (i = 0; i < AR9300_MAX_CHAINS; i++) {
+               __ar955x_tx_iq_cal_sort(ah, coeff, i, nmeasurement);
+       }
+
+       return true;
+}
+
+static void ar9003_hw_tx_iq_cal_post_proc(struct ath_hw *ah,
+                                         int iqcal_idx,
+                                         bool is_reusable)
 {
        struct ath_common *common = ath9k_hw_common(ah);
        const u32 txiqcal_status[AR9300_MAX_CHAINS] = {
@@ -1004,10 +1069,11 @@ static void ar9003_hw_tx_iq_cal_post_proc(struct ath_hw *ah, bool is_reusable)
                AR_PHY_CHAN_INFO_TAB_1,
                AR_PHY_CHAN_INFO_TAB_2,
        };
-       struct coeff coeff;
+       static struct coeff coeff;
        s32 iq_res[6];
        int i, im, j;
-       int nmeasurement;
+       int nmeasurement = 0;
+       bool outlier_detect = true;
 
        for (i = 0; i < AR9300_MAX_CHAINS; i++) {
                if (!(ah->txchainmask & (1 << i)))
@@ -1065,17 +1131,23 @@ static void ar9003_hw_tx_iq_cal_post_proc(struct ath_hw *ah, bool is_reusable)
                                goto tx_iqcal_fail;
                        }
 
-                       coeff.mag_coeff[i][im] = coeff.iqc_coeff[0] & 0x7f;
-                       coeff.phs_coeff[i][im] =
+                       coeff.phs_coeff[i][im][iqcal_idx] =
+                               coeff.iqc_coeff[0] & 0x7f;
+                       coeff.mag_coeff[i][im][iqcal_idx] =
                                (coeff.iqc_coeff[0] >> 7) & 0x7f;
 
-                       if (coeff.mag_coeff[i][im] > 63)
-                               coeff.mag_coeff[i][im] -= 128;
-                       if (coeff.phs_coeff[i][im] > 63)
-                               coeff.phs_coeff[i][im] -= 128;
+                       if (coeff.mag_coeff[i][im][iqcal_idx] > 63)
+                               coeff.mag_coeff[i][im][iqcal_idx] -= 128;
+                       if (coeff.phs_coeff[i][im][iqcal_idx] > 63)
+                               coeff.phs_coeff[i][im][iqcal_idx] -= 128;
                }
        }
-       ar9003_hw_tx_iqcal_load_avg_2_passes(ah, &coeff, is_reusable);
+
+       if (AR_SREV_9550(ah))
+               outlier_detect = ar955x_tx_iq_cal_median(ah, &coeff,
+                                                        iqcal_idx, nmeasurement);
+       if (outlier_detect)
+               ar9003_hw_tx_iq_cal_outlier_detection(ah, &coeff, is_reusable);
 
        return;
 
@@ -1409,7 +1481,7 @@ skip_tx_iqcal:
        }
 
        if (txiqcal_done)
-               ar9003_hw_tx_iq_cal_post_proc(ah, is_reusable);
+               ar9003_hw_tx_iq_cal_post_proc(ah, 0, is_reusable);
        else if (caldata && test_bit(TXIQCAL_DONE, &caldata->cal_flags))
                ar9003_hw_tx_iq_cal_reload(ah);
 
@@ -1455,14 +1527,38 @@ skip_tx_iqcal:
        return true;
 }
 
+static bool do_ar9003_agc_cal(struct ath_hw *ah)
+{
+       struct ath_common *common = ath9k_hw_common(ah);
+       bool status;
+
+       REG_WRITE(ah, AR_PHY_AGC_CONTROL,
+                 REG_READ(ah, AR_PHY_AGC_CONTROL) |
+                 AR_PHY_AGC_CONTROL_CAL);
+
+       status = ath9k_hw_wait(ah, AR_PHY_AGC_CONTROL,
+                              AR_PHY_AGC_CONTROL_CAL,
+                              0, AH_WAIT_TIMEOUT);
+       if (!status) {
+               ath_dbg(common, CALIBRATE,
+                       "offset calibration failed to complete in %d ms,"
+                       "noisy environment?\n",
+                       AH_WAIT_TIMEOUT / 1000);
+               return false;
+       }
+
+       return true;
+}
+
 static bool ar9003_hw_init_cal_soc(struct ath_hw *ah,
                                   struct ath9k_channel *chan)
 {
        struct ath_common *common = ath9k_hw_common(ah);
        struct ath9k_hw_cal_data *caldata = ah->caldata;
        bool txiqcal_done = false;
-       bool is_reusable = true, status = true;
+       bool status = true;
        bool run_agc_cal = false, sep_iq_cal = false;
+       int i = 0;
 
        /* Use chip chainmask only for calibration */
        ar9003_hw_set_chain_masks(ah, ah->caps.rx_chainmask, ah->caps.tx_chainmask);
@@ -1485,7 +1581,12 @@ static bool ar9003_hw_init_cal_soc(struct ath_hw *ah,
         * AGC calibration. Specifically, AR9550 in SoC chips.
         */
        if (ah->enabled_cals & TX_IQ_ON_AGC_CAL) {
-               txiqcal_done = true;
+               if (REG_READ_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_0,
+                                  AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL)) {
+                               txiqcal_done = true;
+               } else {
+                       txiqcal_done = false;
+               }
                run_agc_cal = true;
        } else {
                sep_iq_cal = true;
@@ -1512,27 +1613,37 @@ skip_tx_iqcal:
                if (AR_SREV_9330_11(ah))
                        ar9003_hw_manual_peak_cal(ah, 0, IS_CHAN_2GHZ(chan));
 
-               /* Calibrate the AGC */
-               REG_WRITE(ah, AR_PHY_AGC_CONTROL,
-                         REG_READ(ah, AR_PHY_AGC_CONTROL) |
-                         AR_PHY_AGC_CONTROL_CAL);
-
-               /* Poll for offset calibration complete */
-               status = ath9k_hw_wait(ah, AR_PHY_AGC_CONTROL,
-                                      AR_PHY_AGC_CONTROL_CAL,
-                                      0, AH_WAIT_TIMEOUT);
-       }
+               /*
+                * For non-AR9550 chips, we just trigger AGC calibration
+                * in the HW, poll for completion and then process
+                * the results.
+                *
+                * For AR955x, we run it multiple times and use
+                * median IQ correction.
+                */
+               if (!AR_SREV_9550(ah)) {
+                       status = do_ar9003_agc_cal(ah);
+                       if (!status)
+                               return false;
 
-       if (!status) {
-               ath_dbg(common, CALIBRATE,
-                       "offset calibration failed to complete in %d ms; noisy environment?\n",
-                       AH_WAIT_TIMEOUT / 1000);
-               return false;
+                       if (txiqcal_done)
+                               ar9003_hw_tx_iq_cal_post_proc(ah, 0, false);
+               } else {
+                       if (!txiqcal_done) {
+                               status = do_ar9003_agc_cal(ah);
+                               if (!status)
+                                       return false;
+                       } else {
+                               for (i = 0; i < MAXIQCAL; i++) {
+                                       status = do_ar9003_agc_cal(ah);
+                                       if (!status)
+                                               return false;
+                                       ar9003_hw_tx_iq_cal_post_proc(ah, i, false);
+                               }
+                       }
+               }
        }
 
-       if (txiqcal_done)
-               ar9003_hw_tx_iq_cal_post_proc(ah, is_reusable);
-
        /* Revert chainmask to runtime parameters */
        ar9003_hw_set_chain_masks(ah, ah->rxchainmask, ah->txchainmask);
 
index b5ac32c..21d13bc 100644 (file)
@@ -30,7 +30,6 @@
 #include "spectral.h"
 
 struct ath_node;
-struct ath_rate_table;
 
 extern struct ieee80211_ops ath9k_ops;
 extern int ath9k_modparam_nohwcrypt;
@@ -150,6 +149,11 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd,
 #define IS_CCK_RATE(rate)  ((rate >= 0x18) && (rate <= 0x1e))
 #define IS_OFDM_RATE(rate) ((rate >= 0x8) && (rate <= 0xf))
 
+enum {
+       WLAN_RC_PHY_OFDM,
+       WLAN_RC_PHY_CCK,
+};
+
 struct ath_txq {
        int mac80211_qnum; /* mac80211 queue number, -1 means not mac80211 Q */
        u32 axq_qnum; /* ath9k hardware queue number */
@@ -442,7 +446,8 @@ void ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif,
 void ath9k_beacon_assign_slot(struct ath_softc *sc, struct ieee80211_vif *vif);
 void ath9k_beacon_remove_slot(struct ath_softc *sc, struct ieee80211_vif *vif);
 void ath9k_set_beacon(struct ath_softc *sc);
-bool ath9k_csa_is_finished(struct ath_softc *sc);
+bool ath9k_csa_is_finished(struct ath_softc *sc, struct ieee80211_vif *vif);
+void ath9k_csa_update(struct ath_softc *sc);
 
 /*******************/
 /* Link Monitoring */
@@ -757,7 +762,6 @@ struct ath_softc {
 #endif
 
        struct ath9k_hw_cal_data caldata;
-       int last_rssi;
 
 #ifdef CONFIG_ATH9K_DEBUGFS
        struct ath9k_debug debug;
@@ -774,7 +778,6 @@ struct ath_softc {
 #endif
 
        struct ath_descdma txsdma;
-       struct ieee80211_vif *csa_vif;
 
        struct ath_ant_comb ant_comb;
        u8 ant_tx, ant_rx;
index 2e8bba0..32d00e8 100644 (file)
@@ -292,11 +292,8 @@ static void ath9k_set_tsfadjust(struct ath_softc *sc, struct ieee80211_vif *vif)
                (unsigned long long)tsfadjust, avp->av_bslot);
 }
 
-bool ath9k_csa_is_finished(struct ath_softc *sc)
+bool ath9k_csa_is_finished(struct ath_softc *sc, struct ieee80211_vif *vif)
 {
-       struct ieee80211_vif *vif;
-
-       vif = sc->csa_vif;
        if (!vif || !vif->csa_active)
                return false;
 
@@ -304,11 +301,23 @@ bool ath9k_csa_is_finished(struct ath_softc *sc)
                return false;
 
        ieee80211_csa_finish(vif);
-
-       sc->csa_vif = NULL;
        return true;
 }
 
+static void ath9k_csa_update_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
+{
+       struct ath_softc *sc = data;
+       ath9k_csa_is_finished(sc, vif);
+}
+
+void ath9k_csa_update(struct ath_softc *sc)
+{
+       ieee80211_iterate_active_interfaces(sc->hw,
+                                           IEEE80211_IFACE_ITER_NORMAL,
+                                           ath9k_csa_update_vif,
+                                           sc);
+}
+
 void ath9k_beacon_tasklet(unsigned long data)
 {
        struct ath_softc *sc = (struct ath_softc *)data;
@@ -362,13 +371,13 @@ void ath9k_beacon_tasklet(unsigned long data)
                return;
        }
 
-       /* EDMA devices check that in the tx completion function. */
-       if (!edma && ath9k_csa_is_finished(sc))
-               return;
-
        slot = ath9k_beacon_choose_slot(sc);
        vif = sc->beacon.bslot[slot];
 
+       /* EDMA devices check that in the tx completion function. */
+       if (!edma && ath9k_csa_is_finished(sc, vif))
+               return;
+
        if (!vif || !vif->bss_conf.enable_beacon)
                return;
 
index 768c733..c6dd7f1 100644 (file)
@@ -27,6 +27,250 @@ MODULE_AUTHOR("Atheros Communications");
 MODULE_DESCRIPTION("Shared library for Atheros wireless 802.11n LAN cards.");
 MODULE_LICENSE("Dual BSD/GPL");
 
+/* Assumes you've already done the endian to CPU conversion */
+bool ath9k_cmn_rx_accept(struct ath_common *common,
+                        struct ieee80211_hdr *hdr,
+                        struct ieee80211_rx_status *rxs,
+                        struct ath_rx_status *rx_stats,
+                        bool *decrypt_error,
+                        unsigned int rxfilter)
+{
+       struct ath_hw *ah = common->ah;
+       bool is_mc, is_valid_tkip, strip_mic, mic_error;
+       __le16 fc;
+
+       fc = hdr->frame_control;
+
+       is_mc = !!is_multicast_ether_addr(hdr->addr1);
+       is_valid_tkip = rx_stats->rs_keyix != ATH9K_RXKEYIX_INVALID &&
+               test_bit(rx_stats->rs_keyix, common->tkip_keymap);
+       strip_mic = is_valid_tkip && ieee80211_is_data(fc) &&
+               ieee80211_has_protected(fc) &&
+               !(rx_stats->rs_status &
+               (ATH9K_RXERR_DECRYPT | ATH9K_RXERR_CRC | ATH9K_RXERR_MIC |
+                ATH9K_RXERR_KEYMISS));
+
+       /*
+        * Key miss events are only relevant for pairwise keys where the
+        * descriptor does contain a valid key index. This has been observed
+        * mostly with CCMP encryption.
+        */
+       if (rx_stats->rs_keyix == ATH9K_RXKEYIX_INVALID ||
+           !test_bit(rx_stats->rs_keyix, common->ccmp_keymap))
+               rx_stats->rs_status &= ~ATH9K_RXERR_KEYMISS;
+
+       mic_error = is_valid_tkip && !ieee80211_is_ctl(fc) &&
+               !ieee80211_has_morefrags(fc) &&
+               !(le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG) &&
+               (rx_stats->rs_status & ATH9K_RXERR_MIC);
+
+       /*
+        * The rx_stats->rs_status will not be set until the end of the
+        * chained descriptors so it can be ignored if rs_more is set. The
+        * rs_more will be false at the last element of the chained
+        * descriptors.
+        */
+       if (rx_stats->rs_status != 0) {
+               u8 status_mask;
+
+               if (rx_stats->rs_status & ATH9K_RXERR_CRC) {
+                       rxs->flag |= RX_FLAG_FAILED_FCS_CRC;
+                       mic_error = false;
+               }
+
+               if ((rx_stats->rs_status & ATH9K_RXERR_DECRYPT) ||
+                   (!is_mc && (rx_stats->rs_status & ATH9K_RXERR_KEYMISS))) {
+                       *decrypt_error = true;
+                       mic_error = false;
+               }
+
+
+               /*
+                * Reject error frames with the exception of
+                * decryption and MIC failures. For monitor mode,
+                * we also ignore the CRC error.
+                */
+               status_mask = ATH9K_RXERR_DECRYPT | ATH9K_RXERR_MIC |
+                             ATH9K_RXERR_KEYMISS;
+
+               if (ah->is_monitoring && (rxfilter & FIF_FCSFAIL))
+                       status_mask |= ATH9K_RXERR_CRC;
+
+               if (rx_stats->rs_status & ~status_mask)
+                       return false;
+       }
+
+       /*
+        * For unicast frames the MIC error bit can have false positives,
+        * so all MIC error reports need to be validated in software.
+        * False negatives are not common, so skip software verification
+        * if the hardware considers the MIC valid.
+        */
+       if (strip_mic)
+               rxs->flag |= RX_FLAG_MMIC_STRIPPED;
+       else if (is_mc && mic_error)
+               rxs->flag |= RX_FLAG_MMIC_ERROR;
+
+       return true;
+}
+EXPORT_SYMBOL(ath9k_cmn_rx_accept);
+
+void ath9k_cmn_rx_skb_postprocess(struct ath_common *common,
+                                 struct sk_buff *skb,
+                                 struct ath_rx_status *rx_stats,
+                                 struct ieee80211_rx_status *rxs,
+                                 bool decrypt_error)
+{
+       struct ath_hw *ah = common->ah;
+       struct ieee80211_hdr *hdr;
+       int hdrlen, padpos, padsize;
+       u8 keyix;
+       __le16 fc;
+
+       /* see if any padding is done by the hw and remove it */
+       hdr = (struct ieee80211_hdr *) skb->data;
+       hdrlen = ieee80211_get_hdrlen_from_skb(skb);
+       fc = hdr->frame_control;
+       padpos = ieee80211_hdrlen(fc);
+
+       /* The MAC header is padded to have 32-bit boundary if the
+        * packet payload is non-zero. The general calculation for
+        * padsize would take into account odd header lengths:
+        * padsize = (4 - padpos % 4) % 4; However, since only
+        * even-length headers are used, padding can only be 0 or 2
+        * bytes and we can optimize this a bit. In addition, we must
+        * not try to remove padding from short control frames that do
+        * not have payload. */
+       padsize = padpos & 3;
+       if (padsize && skb->len>=padpos+padsize+FCS_LEN) {
+               memmove(skb->data + padsize, skb->data, padpos);
+               skb_pull(skb, padsize);
+       }
+
+       keyix = rx_stats->rs_keyix;
+
+       if (!(keyix == ATH9K_RXKEYIX_INVALID) && !decrypt_error &&
+           ieee80211_has_protected(fc)) {
+               rxs->flag |= RX_FLAG_DECRYPTED;
+       } else if (ieee80211_has_protected(fc)
+                  && !decrypt_error && skb->len >= hdrlen + 4) {
+               keyix = skb->data[hdrlen + 3] >> 6;
+
+               if (test_bit(keyix, common->keymap))
+                       rxs->flag |= RX_FLAG_DECRYPTED;
+       }
+       if (ah->sw_mgmt_crypto &&
+           (rxs->flag & RX_FLAG_DECRYPTED) &&
+           ieee80211_is_mgmt(fc))
+               /* Use software decrypt for management frames. */
+               rxs->flag &= ~RX_FLAG_DECRYPTED;
+}
+EXPORT_SYMBOL(ath9k_cmn_rx_skb_postprocess);
+
+int ath9k_cmn_process_rate(struct ath_common *common,
+                          struct ieee80211_hw *hw,
+                          struct ath_rx_status *rx_stats,
+                          struct ieee80211_rx_status *rxs)
+{
+       struct ieee80211_supported_band *sband;
+       enum ieee80211_band band;
+       unsigned int i = 0;
+       struct ath_hw *ah = common->ah;
+
+       band = ah->curchan->chan->band;
+       sband = hw->wiphy->bands[band];
+
+       if (IS_CHAN_QUARTER_RATE(ah->curchan))
+               rxs->flag |= RX_FLAG_5MHZ;
+       else if (IS_CHAN_HALF_RATE(ah->curchan))
+               rxs->flag |= RX_FLAG_10MHZ;
+
+       if (rx_stats->rs_rate & 0x80) {
+               /* HT rate */
+               rxs->flag |= RX_FLAG_HT;
+               rxs->flag |= rx_stats->flag;
+               rxs->rate_idx = rx_stats->rs_rate & 0x7f;
+               return 0;
+       }
+
+       for (i = 0; i < sband->n_bitrates; i++) {
+               if (sband->bitrates[i].hw_value == rx_stats->rs_rate) {
+                       rxs->rate_idx = i;
+                       return 0;
+               }
+               if (sband->bitrates[i].hw_value_short == rx_stats->rs_rate) {
+                       rxs->flag |= RX_FLAG_SHORTPRE;
+                       rxs->rate_idx = i;
+                       return 0;
+               }
+       }
+
+       return -EINVAL;
+}
+EXPORT_SYMBOL(ath9k_cmn_process_rate);
+
+void ath9k_cmn_process_rssi(struct ath_common *common,
+                           struct ieee80211_hw *hw,
+                           struct ath_rx_status *rx_stats,
+                           struct ieee80211_rx_status *rxs)
+{
+       struct ath_hw *ah = common->ah;
+       int last_rssi;
+       int rssi = rx_stats->rs_rssi;
+       int i, j;
+
+       /*
+        * RSSI is not available for subframes in an A-MPDU.
+        */
+       if (rx_stats->rs_moreaggr) {
+               rxs->flag |= RX_FLAG_NO_SIGNAL_VAL;
+               return;
+       }
+
+       /*
+        * Check if the RSSI for the last subframe in an A-MPDU
+        * or an unaggregated frame is valid.
+        */
+       if (rx_stats->rs_rssi == ATH9K_RSSI_BAD) {
+               rxs->flag |= RX_FLAG_NO_SIGNAL_VAL;
+               return;
+       }
+
+       for (i = 0, j = 0; i < ARRAY_SIZE(rx_stats->rs_rssi_ctl); i++) {
+               s8 rssi;
+
+               if (!(ah->rxchainmask & BIT(i)))
+                       continue;
+
+               rssi = rx_stats->rs_rssi_ctl[i];
+               if (rssi != ATH9K_RSSI_BAD) {
+                   rxs->chains |= BIT(j);
+                   rxs->chain_signal[j] = ah->noise + rssi;
+               }
+               j++;
+       }
+
+       /*
+        * Update Beacon RSSI, this is used by ANI.
+        */
+       if (rx_stats->is_mybeacon &&
+           ((ah->opmode == NL80211_IFTYPE_STATION) ||
+            (ah->opmode == NL80211_IFTYPE_ADHOC))) {
+               ATH_RSSI_LPF(common->last_rssi, rx_stats->rs_rssi);
+               last_rssi = common->last_rssi;
+
+               if (likely(last_rssi != ATH_RSSI_DUMMY_MARKER))
+                       rssi = ATH_EP_RND(last_rssi, ATH_RSSI_EP_MULTIPLIER);
+               if (rssi < 0)
+                       rssi = 0;
+
+               ah->stats.avgbrssi = rssi;
+       }
+
+       rxs->signal = ah->noise + rx_stats->rs_rssi;
+}
+EXPORT_SYMBOL(ath9k_cmn_process_rssi);
+
 int ath9k_cmn_get_hw_crypto_keytype(struct sk_buff *skb)
 {
        struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
index eb85e1b..38b5609 100644 (file)
 #define ATH_EP_RND(x, mul)                                             \
        (((x) + ((mul)/2)) / (mul))
 
+bool ath9k_cmn_rx_accept(struct ath_common *common,
+                        struct ieee80211_hdr *hdr,
+                        struct ieee80211_rx_status *rxs,
+                        struct ath_rx_status *rx_stats,
+                        bool *decrypt_error,
+                        unsigned int rxfilter);
+void ath9k_cmn_rx_skb_postprocess(struct ath_common *common,
+                                 struct sk_buff *skb,
+                                 struct ath_rx_status *rx_stats,
+                                 struct ieee80211_rx_status *rxs,
+                                 bool decrypt_error);
+int ath9k_cmn_process_rate(struct ath_common *common,
+                          struct ieee80211_hw *hw,
+                          struct ath_rx_status *rx_stats,
+                          struct ieee80211_rx_status *rxs);
+void ath9k_cmn_process_rssi(struct ath_common *common,
+                           struct ieee80211_hw *hw,
+                           struct ath_rx_status *rx_stats,
+                           struct ieee80211_rx_status *rxs);
 int ath9k_cmn_get_hw_crypto_keytype(struct sk_buff *skb);
 struct ath9k_channel *ath9k_cmn_get_channel(struct ieee80211_hw *hw,
                                            struct ath_hw *ah,
index cc7a025..559a68c 100644 (file)
@@ -18,7 +18,6 @@
 #define DEBUG_H
 
 #include "hw.h"
-#include "rc.h"
 #include "dfs_debug.h"
 
 struct ath_txq;
index 0a7ddf4..7936c91 100644 (file)
@@ -21,6 +21,8 @@
 
 #include "hw.h"
 
+struct ath_softc;
+
 /**
  * struct ath_dfs_stats - DFS Statistics per wiphy
  * @pulses_total:     pulses reported by HW
index 6d5d716..8e7153b 100644 (file)
@@ -54,6 +54,8 @@ static struct usb_device_id ath9k_hif_usb_ids[] = {
          .driver_info = AR9280_USB },  /* SMC Networks */
        { USB_DEVICE(0x0411, 0x017f),
          .driver_info = AR9280_USB },  /* Sony UWA-BR100 */
+       { USB_DEVICE(0x0411, 0x0197),
+         .driver_info = AR9280_USB },  /* Buffalo WLI-UV-AG300P */
        { USB_DEVICE(0x04da, 0x3904),
          .driver_info = AR9280_USB },
 
index 99a2031..ba83f58 100644 (file)
@@ -277,7 +277,6 @@ struct ath9k_htc_rxbuf {
 };
 
 struct ath9k_htc_rx {
-       int last_rssi; /* FIXME: per-STA */
        struct list_head rxbuf;
        spinlock_t rxbuflock;
 };
index c57d6b8..8d0b9bc 100644 (file)
@@ -611,6 +611,7 @@ static void ath9k_init_misc(struct ath9k_htc_priv *priv)
 
        memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN);
 
+       common->last_rssi = ATH_RSSI_DUMMY_MARKER;
        priv->ah->opmode = NL80211_IFTYPE_STATION;
 }
 
index c9254a6..90dad41 100644 (file)
@@ -1474,6 +1474,7 @@ static void ath9k_htc_bss_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
 
        if ((vif->type == NL80211_IFTYPE_STATION) && bss_conf->assoc) {
                common->curaid = bss_conf->aid;
+               common->last_rssi = ATH_RSSI_DUMMY_MARKER;
                memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
        }
 }
index 12e0f32..47b2bfc 100644 (file)
@@ -927,43 +927,39 @@ void ath9k_host_rx_init(struct ath9k_htc_priv *priv)
        ath9k_hw_rxena(priv->ah);
        ath9k_htc_opmode_init(priv);
        ath9k_hw_startpcureceive(priv->ah, test_bit(OP_SCANNING, &priv->op_flags));
-       priv->rx.last_rssi = ATH_RSSI_DUMMY_MARKER;
 }
 
-static void ath9k_process_rate(struct ieee80211_hw *hw,
-                              struct ieee80211_rx_status *rxs,
-                              u8 rx_rate, u8 rs_flags)
+static inline void convert_htc_flag(struct ath_rx_status *rx_stats,
+                                  struct ath_htc_rx_status *rxstatus)
 {
-       struct ieee80211_supported_band *sband;
-       enum ieee80211_band band;
-       unsigned int i = 0;
-
-       if (rx_rate & 0x80) {
-               /* HT rate */
-               rxs->flag |= RX_FLAG_HT;
-               if (rs_flags & ATH9K_RX_2040)
-                       rxs->flag |= RX_FLAG_40MHZ;
-               if (rs_flags & ATH9K_RX_GI)
-                       rxs->flag |= RX_FLAG_SHORT_GI;
-               rxs->rate_idx = rx_rate & 0x7f;
-               return;
-       }
-
-       band = hw->conf.chandef.chan->band;
-       sband = hw->wiphy->bands[band];
-
-       for (i = 0; i < sband->n_bitrates; i++) {
-               if (sband->bitrates[i].hw_value == rx_rate) {
-                       rxs->rate_idx = i;
-                       return;
-               }
-               if (sband->bitrates[i].hw_value_short == rx_rate) {
-                       rxs->rate_idx = i;
-                       rxs->flag |= RX_FLAG_SHORTPRE;
-                       return;
-               }
-       }
+       rx_stats->flag = 0;
+       if (rxstatus->rs_flags & ATH9K_RX_2040)
+               rx_stats->flag |= RX_FLAG_40MHZ;
+       if (rxstatus->rs_flags & ATH9K_RX_GI)
+               rx_stats->flag |= RX_FLAG_SHORT_GI;
+}
 
+static void rx_status_htc_to_ath(struct ath_rx_status *rx_stats,
+                                struct ath_htc_rx_status *rxstatus)
+{
+       rx_stats->rs_datalen    = rxstatus->rs_datalen;
+       rx_stats->rs_status     = rxstatus->rs_status;
+       rx_stats->rs_phyerr     = rxstatus->rs_phyerr;
+       rx_stats->rs_rssi       = rxstatus->rs_rssi;
+       rx_stats->rs_keyix      = rxstatus->rs_keyix;
+       rx_stats->rs_rate       = rxstatus->rs_rate;
+       rx_stats->rs_antenna    = rxstatus->rs_antenna;
+       rx_stats->rs_more       = rxstatus->rs_more;
+
+       memcpy(rx_stats->rs_rssi_ctl, rxstatus->rs_rssi_ctl,
+               sizeof(rx_stats->rs_rssi_ctl));
+       memcpy(rx_stats->rs_rssi_ext, rxstatus->rs_rssi_ext,
+               sizeof(rx_stats->rs_rssi_ext));
+
+       rx_stats->rs_isaggr     = rxstatus->rs_isaggr;
+       rx_stats->rs_moreaggr   = rxstatus->rs_moreaggr;
+       rx_stats->rs_num_delims = rxstatus->rs_num_delims;
+       convert_htc_flag(rx_stats, rxstatus);
 }
 
 static bool ath9k_rx_prepare(struct ath9k_htc_priv *priv,
@@ -975,10 +971,10 @@ static bool ath9k_rx_prepare(struct ath9k_htc_priv *priv,
        struct ieee80211_hw *hw = priv->hw;
        struct sk_buff *skb = rxbuf->skb;
        struct ath_common *common = ath9k_hw_common(priv->ah);
+       struct ath_hw *ah = common->ah;
        struct ath_htc_rx_status *rxstatus;
-       int hdrlen, padsize;
-       int last_rssi = ATH_RSSI_DUMMY_MARKER;
-       __le16 fc;
+       struct ath_rx_status rx_stats;
+       bool decrypt_error;
 
        if (skb->len < HTC_RX_FRAME_HEADER_SIZE) {
                ath_err(common, "Corrupted RX frame, dropping (len: %d)\n",
@@ -999,103 +995,39 @@ static bool ath9k_rx_prepare(struct ath9k_htc_priv *priv,
        ath9k_htc_err_stat_rx(priv, rxstatus);
 
        /* Get the RX status information */
-       memcpy(&rxbuf->rxstatus, rxstatus, HTC_RX_FRAME_HEADER_SIZE);
-       skb_pull(skb, HTC_RX_FRAME_HEADER_SIZE);
-
-       hdr = (struct ieee80211_hdr *)skb->data;
-       fc = hdr->frame_control;
-       hdrlen = ieee80211_get_hdrlen_from_skb(skb);
-
-       padsize = hdrlen & 3;
-       if (padsize && skb->len >= hdrlen+padsize+FCS_LEN) {
-               memmove(skb->data + padsize, skb->data, hdrlen);
-               skb_pull(skb, padsize);
-       }
 
        memset(rx_status, 0, sizeof(struct ieee80211_rx_status));
 
-       if (rxbuf->rxstatus.rs_status != 0) {
-               if (rxbuf->rxstatus.rs_status & ATH9K_RXERR_CRC)
-                       rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
-               if (rxbuf->rxstatus.rs_status & ATH9K_RXERR_PHY)
-                       goto rx_next;
-
-               if (rxbuf->rxstatus.rs_status & ATH9K_RXERR_DECRYPT) {
-                       /* FIXME */
-               } else if (rxbuf->rxstatus.rs_status & ATH9K_RXERR_MIC) {
-                       if (ieee80211_is_ctl(fc))
-                               /*
-                                * Sometimes, we get invalid
-                                * MIC failures on valid control frames.
-                                * Remove these mic errors.
-                                */
-                               rxbuf->rxstatus.rs_status &= ~ATH9K_RXERR_MIC;
-                       else
-                               rx_status->flag |= RX_FLAG_MMIC_ERROR;
-               }
-
-               /*
-                * Reject error frames with the exception of
-                * decryption and MIC failures. For monitor mode,
-                * we also ignore the CRC error.
-                */
-               if (priv->ah->opmode == NL80211_IFTYPE_MONITOR) {
-                       if (rxbuf->rxstatus.rs_status &
-                           ~(ATH9K_RXERR_DECRYPT | ATH9K_RXERR_MIC |
-                             ATH9K_RXERR_CRC))
-                               goto rx_next;
-               } else {
-                       if (rxbuf->rxstatus.rs_status &
-                           ~(ATH9K_RXERR_DECRYPT | ATH9K_RXERR_MIC)) {
-                               goto rx_next;
-                       }
-               }
-       }
-
-       if (!(rxbuf->rxstatus.rs_status & ATH9K_RXERR_DECRYPT)) {
-               u8 keyix;
-               keyix = rxbuf->rxstatus.rs_keyix;
-               if (keyix != ATH9K_RXKEYIX_INVALID) {
-                       rx_status->flag |= RX_FLAG_DECRYPTED;
-               } else if (ieee80211_has_protected(fc) &&
-                          skb->len >= hdrlen + 4) {
-                       keyix = skb->data[hdrlen + 3] >> 6;
-                       if (test_bit(keyix, common->keymap))
-                               rx_status->flag |= RX_FLAG_DECRYPTED;
-               }
-       }
-
-       ath9k_process_rate(hw, rx_status, rxbuf->rxstatus.rs_rate,
-                          rxbuf->rxstatus.rs_flags);
-
-       if (rxbuf->rxstatus.rs_rssi != ATH9K_RSSI_BAD &&
-           !rxbuf->rxstatus.rs_moreaggr)
-               ATH_RSSI_LPF(priv->rx.last_rssi,
-                            rxbuf->rxstatus.rs_rssi);
-
-       last_rssi = priv->rx.last_rssi;
+       /* Copy everything from ath_htc_rx_status (HTC_RX_FRAME_HEADER).
+        * After this, we can drop this part of skb. */
+       rx_status_htc_to_ath(&rx_stats, rxstatus);
+       rx_status->mactime = be64_to_cpu(rxstatus->rs_tstamp);
+       skb_pull(skb, HTC_RX_FRAME_HEADER_SIZE);
 
-       if (ath_is_mybeacon(common, hdr)) {
-               s8 rssi = rxbuf->rxstatus.rs_rssi;
+       /*
+        * everything but the rate is checked here, the rate check is done
+        * separately to avoid doing two lookups for a rate for each frame.
+        */
+       hdr = (struct ieee80211_hdr *)skb->data;
+       if (!ath9k_cmn_rx_accept(common, hdr, rx_status, &rx_stats,
+                       &decrypt_error, priv->rxfilter))
+               goto rx_next;
 
-               if (likely(last_rssi != ATH_RSSI_DUMMY_MARKER))
-                       rssi = ATH_EP_RND(last_rssi, ATH_RSSI_EP_MULTIPLIER);
+       ath9k_cmn_rx_skb_postprocess(common, skb, &rx_stats,
+                                    rx_status, decrypt_error);
 
-               if (rssi < 0)
-                       rssi = 0;
+       if (ath9k_cmn_process_rate(common, hw, &rx_stats, rx_status))
+               goto rx_next;
 
-               priv->ah->stats.avgbrssi = rssi;
-       }
+       rx_stats.is_mybeacon = ath_is_mybeacon(common, hdr);
+       ath9k_cmn_process_rssi(common, hw, &rx_stats, rx_status);
 
-       rx_status->mactime = be64_to_cpu(rxbuf->rxstatus.rs_tstamp);
-       rx_status->band = hw->conf.chandef.chan->band;
-       rx_status->freq = hw->conf.chandef.chan->center_freq;
-       rx_status->signal =  rxbuf->rxstatus.rs_rssi + ATH_DEFAULT_NOISE_FLOOR;
-       rx_status->antenna = rxbuf->rxstatus.rs_antenna;
+       rx_status->band = ah->curchan->chan->band;
+       rx_status->freq = ah->curchan->chan->center_freq;
+       rx_status->antenna = rx_stats.rs_antenna;
        rx_status->flag |= RX_FLAG_MACTIME_END;
 
        return true;
-
 rx_next:
        return false;
 }
index aac4a40..a0ff5b6 100644 (file)
@@ -358,6 +358,36 @@ ret:
                kfree_skb(skb);
 }
 
+static void ath9k_htc_fw_panic_report(struct htc_target *htc_handle,
+                                     struct sk_buff *skb)
+{
+       uint32_t *pattern = (uint32_t *)skb->data;
+
+       switch (*pattern) {
+       case 0x33221199:
+               {
+               struct htc_panic_bad_vaddr *htc_panic;
+               htc_panic = (struct htc_panic_bad_vaddr *) skb->data;
+               dev_err(htc_handle->dev, "ath: firmware panic! "
+                       "exccause: 0x%08x; pc: 0x%08x; badvaddr: 0x%08x.\n",
+                       htc_panic->exccause, htc_panic->pc,
+                       htc_panic->badvaddr);
+               break;
+               }
+       case 0x33221299:
+               {
+               struct htc_panic_bad_epid *htc_panic;
+               htc_panic = (struct htc_panic_bad_epid *) skb->data;
+               dev_err(htc_handle->dev, "ath: firmware panic! "
+                       "bad epid: 0x%08x\n", htc_panic->epid);
+               break;
+               }
+       default:
+               dev_err(htc_handle->dev, "ath: uknown panic pattern!\n");
+               break;
+       }
+}
+
 /*
  * HTC Messages are handled directly here and the obtained SKB
  * is freed.
@@ -379,6 +409,12 @@ void ath9k_htc_rx_msg(struct htc_target *htc_handle,
        htc_hdr = (struct htc_frame_hdr *) skb->data;
        epid = htc_hdr->endpoint_id;
 
+       if (epid == 0x99) {
+               ath9k_htc_fw_panic_report(htc_handle, skb);
+               kfree_skb(skb);
+               return;
+       }
+
        if (epid >= ENDPOINT_MAX) {
                if (pipe_id != USB_REG_IN_PIPE)
                        dev_kfree_skb_any(skb);
index e1ffbb6..06474cc 100644 (file)
@@ -77,6 +77,18 @@ struct htc_config_pipe_msg {
        u8 credits;
 } __packed;
 
+struct htc_panic_bad_vaddr {
+       __be32 pattern;
+       __be32 exccause;
+       __be32 pc;
+       __be32 badvaddr;
+} __packed;
+
+struct htc_panic_bad_epid {
+       __be32 pattern;
+       __be32 epid;
+} __packed;
+
 struct htc_ep_callbacks {
        void *priv;
        void (*tx) (void *, struct sk_buff *, enum htc_endpoint_id, bool txok);
index 11eab9f..5db01b4 100644 (file)
@@ -23,7 +23,6 @@
 
 #include "hw.h"
 #include "hw-ops.h"
-#include "rc.h"
 #include "ar9003_mac.h"
 #include "ar9003_mci.h"
 #include "ar9003_phy.h"
index 1fc2e5a..07a0315 100644 (file)
@@ -538,7 +538,7 @@ static void ath9k_init_misc(struct ath_softc *sc)
 
        setup_timer(&common->ani.timer, ath_ani_calibrate, (unsigned long)sc);
 
-       sc->last_rssi = ATH_RSSI_DUMMY_MARKER;
+       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;
@@ -1106,19 +1106,11 @@ static int __init ath9k_init(void)
 {
        int error;
 
-       /* Register rate control algorithm */
-       error = ath_rate_control_register();
-       if (error != 0) {
-               pr_err("Unable to register rate control algorithm: %d\n",
-                      error);
-               goto err_out;
-       }
-
        error = ath_pci_init();
        if (error < 0) {
                pr_err("No PCI devices found, driver not installed\n");
                error = -ENODEV;
-               goto err_rate_unregister;
+               goto err_out;
        }
 
        error = ath_ahb_init();
@@ -1131,9 +1123,6 @@ static int __init ath9k_init(void)
 
  err_pci_exit:
        ath_pci_exit();
-
- err_rate_unregister:
-       ath_rate_control_unregister();
  err_out:
        return error;
 }
@@ -1144,7 +1133,6 @@ static void __exit ath9k_exit(void)
        is_ath9k_unloaded = true;
        ath_ahb_exit();
        ath_pci_exit();
-       ath_rate_control_unregister();
        pr_info("%s: Driver unloaded\n", dev_info);
 }
 module_exit(ath9k_exit);
index 1027137..89df634 100644 (file)
@@ -155,12 +155,8 @@ struct ath_htc_rx_status {
        u8 rs_status;
        u8 rs_phyerr;
        int8_t rs_rssi;
-       int8_t rs_rssi_ctl0;
-       int8_t rs_rssi_ctl1;
-       int8_t rs_rssi_ctl2;
-       int8_t rs_rssi_ext0;
-       int8_t rs_rssi_ext1;
-       int8_t rs_rssi_ext2;
+       int8_t rs_rssi_ctl[3];
+       int8_t rs_rssi_ext[3];
        u8 rs_keyix;
        u8 rs_rate;
        u8 rs_antenna;
@@ -170,6 +166,7 @@ struct ath_htc_rx_status {
        u8 rs_num_delims;
        u8 rs_flags;
        u8 rs_dummy;
+       /* FIXME: evm* never used? */
        __be32 evm0;
        __be32 evm1;
        __be32 evm2;
index 5924f72..afce549 100644 (file)
@@ -1178,9 +1178,6 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw,
        if (ath9k_uses_beacons(vif->type))
                ath9k_beacon_remove_slot(sc, vif);
 
-       if (sc->csa_vif == vif)
-               sc->csa_vif = NULL;
-
        ath9k_ps_wakeup(sc);
        ath9k_calculate_summary_state(hw, NULL);
        ath9k_ps_restore(sc);
@@ -1609,7 +1606,7 @@ static void ath9k_set_assoc_state(struct ath_softc *sc,
        common->curaid = bss_conf->aid;
        ath9k_hw_write_associd(sc->sc_ah);
 
-       sc->last_rssi = ATH_RSSI_DUMMY_MARKER;
+       common->last_rssi = ATH_RSSI_DUMMY_MARKER;
        sc->sc_ah->stats.avgbrssi = ATH_RSSI_DUMMY_MARKER;
 
        spin_lock_irqsave(&sc->sc_pm_lock, flags);
@@ -1866,7 +1863,7 @@ static void ath9k_set_coverage_class(struct ieee80211_hw *hw, u8 coverage_class)
 
 static bool ath9k_has_tx_pending(struct ath_softc *sc)
 {
-       int i, npend;
+       int i, npend = 0;
 
        for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
                if (!ATH_TXQ_SETUP(sc, i))
@@ -2086,13 +2083,8 @@ static void ath9k_channel_switch_beacon(struct ieee80211_hw *hw,
                                        struct ieee80211_vif *vif,
                                        struct cfg80211_chan_def *chandef)
 {
-       struct ath_softc *sc = hw->priv;
-
-       /* mac80211 does not support CSA in multi-if cases (yet) */
-       if (WARN_ON(sc->csa_vif))
-               return;
-
-       sc->csa_vif = vif;
+       /* depend on vif->csa_active only */
+       return;
 }
 
 struct ieee80211_ops ath9k_ops = {
diff --git a/drivers/net/wireless/ath/ath9k/rc.c b/drivers/net/wireless/ath/ath9k/rc.c
deleted file mode 100644 (file)
index d829bb6..0000000
+++ /dev/null
@@ -1,1495 +0,0 @@
-/*
- * Copyright (c) 2004 Video54 Technologies, Inc.
- * Copyright (c) 2004-2011 Atheros Communications, Inc.
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <linux/slab.h>
-#include <linux/export.h>
-
-#include "ath9k.h"
-
-static const struct ath_rate_table ar5416_11na_ratetable = {
-       68,
-       8, /* MCS start */
-       {
-               [0] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 6000,
-                       5400, 0, 12 }, /* 6 Mb */
-               [1] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 9000,
-                       7800,  1, 18 }, /* 9 Mb */
-               [2] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 12000,
-                       10000, 2, 24 }, /* 12 Mb */
-               [3] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 18000,
-                       13900, 3, 36 }, /* 18 Mb */
-               [4] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 24000,
-                       17300, 4, 48 }, /* 24 Mb */
-               [5] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 36000,
-                       23000, 5, 72 }, /* 36 Mb */
-               [6] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 48000,
-                       27400, 6, 96 }, /* 48 Mb */
-               [7] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 54000,
-                       29300, 7, 108 }, /* 54 Mb */
-               [8] = { RC_HT_SDT_2040, WLAN_RC_PHY_HT_20_SS, 6500,
-                       6400, 0, 0 }, /* 6.5 Mb */
-               [9] = { RC_HT_SDT_20, WLAN_RC_PHY_HT_20_SS, 13000,
-                       12700, 1, 1 }, /* 13 Mb */
-               [10] = { RC_HT_SDT_20, WLAN_RC_PHY_HT_20_SS, 19500,
-                       18800, 2, 2 }, /* 19.5 Mb */
-               [11] = { RC_HT_SD_20, WLAN_RC_PHY_HT_20_SS, 26000,
-                       25000, 3, 3 }, /* 26 Mb */
-               [12] = { RC_HT_SD_20, WLAN_RC_PHY_HT_20_SS, 39000,
-                       36700, 4, 4 }, /* 39 Mb */
-               [13] = { RC_HT_S_20, WLAN_RC_PHY_HT_20_SS, 52000,
-                       48100, 5, 5 }, /* 52 Mb */
-               [14] = { RC_HT_S_20, WLAN_RC_PHY_HT_20_SS, 58500,
-                       53500, 6, 6 }, /* 58.5 Mb */
-               [15] = { RC_HT_S_20, WLAN_RC_PHY_HT_20_SS, 65000,
-                       59000, 7, 7 }, /* 65 Mb */
-               [16] = { RC_HT_S_20, WLAN_RC_PHY_HT_20_SS_HGI, 72200,
-                       65400, 7, 7 }, /* 75 Mb */
-               [17] = { RC_INVALID, WLAN_RC_PHY_HT_20_DS, 13000,
-                       12700, 8, 8 }, /* 13 Mb */
-               [18] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_DS, 26000,
-                       24800, 9, 9 }, /* 26 Mb */
-               [19] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_DS, 39000,
-                       36600, 10, 10 }, /* 39 Mb */
-               [20] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS, 52000,
-                       48100, 11, 11 }, /* 52 Mb */
-               [21] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS, 78000,
-                       69500, 12, 12 }, /* 78 Mb */
-               [22] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS, 104000,
-                       89500, 13, 13 }, /* 104 Mb */
-               [23] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS, 117000,
-                       98900, 14, 14 }, /* 117 Mb */
-               [24] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS, 130000,
-                       108300, 15, 15 }, /* 130 Mb */
-               [25] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS_HGI, 144400,
-                       120000, 15, 15 }, /* 144.4 Mb */
-               [26] = {  RC_INVALID, WLAN_RC_PHY_HT_20_TS, 19500,
-                       17400, 16, 16 }, /* 19.5 Mb */
-               [27] = {  RC_INVALID, WLAN_RC_PHY_HT_20_TS, 39000,
-                       35100, 17, 17 }, /* 39 Mb */
-               [28] = {  RC_INVALID, WLAN_RC_PHY_HT_20_TS, 58500,
-                       52600, 18, 18 }, /* 58.5 Mb */
-               [29] = {  RC_INVALID, WLAN_RC_PHY_HT_20_TS, 78000,
-                       70400, 19, 19 }, /* 78 Mb */
-               [30] = {  RC_INVALID, WLAN_RC_PHY_HT_20_TS, 117000,
-                       104900, 20, 20 }, /* 117 Mb */
-               [31] = {  RC_INVALID, WLAN_RC_PHY_HT_20_TS_HGI, 130000,
-                       115800, 20, 20 }, /* 130 Mb*/
-               [32] = {  RC_HT_T_20, WLAN_RC_PHY_HT_20_TS, 156000,
-                       137200, 21, 21 }, /* 156 Mb */
-               [33] = {  RC_HT_T_20, WLAN_RC_PHY_HT_20_TS_HGI, 173300,
-                       151100, 21, 21 }, /* 173.3 Mb */
-               [34] = {  RC_HT_T_20, WLAN_RC_PHY_HT_20_TS, 175500,
-                       152800, 22, 22 }, /* 175.5 Mb */
-               [35] = {  RC_HT_T_20, WLAN_RC_PHY_HT_20_TS_HGI, 195000,
-                       168400, 22, 22 }, /* 195 Mb*/
-               [36] = {  RC_HT_T_20, WLAN_RC_PHY_HT_20_TS, 195000,
-                       168400, 23, 23 }, /* 195 Mb */
-               [37] = {  RC_HT_T_20, WLAN_RC_PHY_HT_20_TS_HGI, 216700,
-                       185000, 23, 23 }, /* 216.7 Mb */
-               [38] = { RC_HT_SDT_40, WLAN_RC_PHY_HT_40_SS, 13500,
-                       13200, 0, 0 }, /* 13.5 Mb*/
-               [39] = { RC_HT_SDT_40, WLAN_RC_PHY_HT_40_SS, 27500,
-                       25900, 1, 1 }, /* 27.0 Mb*/
-               [40] = { RC_HT_SDT_40, WLAN_RC_PHY_HT_40_SS, 40500,
-                       38600, 2, 2 }, /* 40.5 Mb*/
-               [41] = { RC_HT_SD_40, WLAN_RC_PHY_HT_40_SS, 54000,
-                       49800, 3, 3 }, /* 54 Mb */
-               [42] = { RC_HT_SD_40, WLAN_RC_PHY_HT_40_SS, 81500,
-                       72200, 4, 4 }, /* 81 Mb */
-               [43] = { RC_HT_S_40, WLAN_RC_PHY_HT_40_SS, 108000,
-                       92900, 5, 5 }, /* 108 Mb */
-               [44] = { RC_HT_S_40, WLAN_RC_PHY_HT_40_SS, 121500,
-                       102700, 6, 6 }, /* 121.5 Mb*/
-               [45] = { RC_HT_S_40, WLAN_RC_PHY_HT_40_SS, 135000,
-                       112000, 7, 7 }, /* 135 Mb */
-               [46] = { RC_HT_S_40, WLAN_RC_PHY_HT_40_SS_HGI, 150000,
-                       122000, 7, 7 }, /* 150 Mb */
-               [47] = { RC_INVALID, WLAN_RC_PHY_HT_40_DS, 27000,
-                       25800, 8, 8 }, /* 27 Mb */
-               [48] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_DS, 54000,
-                       49800, 9, 9 }, /* 54 Mb */
-               [49] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_DS, 81000,
-                       71900, 10, 10 }, /* 81 Mb */
-               [50] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS, 108000,
-                       92500, 11, 11 }, /* 108 Mb */
-               [51] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS, 162000,
-                       130300, 12, 12 }, /* 162 Mb */
-               [52] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS, 216000,
-                       162800, 13, 13 }, /* 216 Mb */
-               [53] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS, 243000,
-                       178200, 14, 14 }, /* 243 Mb */
-               [54] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS, 270000,
-                       192100, 15, 15 }, /* 270 Mb */
-               [55] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS_HGI, 300000,
-                       207000, 15, 15 }, /* 300 Mb */
-               [56] = {  RC_INVALID, WLAN_RC_PHY_HT_40_TS, 40500,
-                       36100, 16, 16 }, /* 40.5 Mb */
-               [57] = {  RC_INVALID, WLAN_RC_PHY_HT_40_TS, 81000,
-                       72900, 17, 17 }, /* 81 Mb */
-               [58] = {  RC_INVALID, WLAN_RC_PHY_HT_40_TS, 121500,
-                       108300, 18, 18 }, /* 121.5 Mb */
-               [59] = {  RC_INVALID, WLAN_RC_PHY_HT_40_TS, 162000,
-                       142000, 19, 19 }, /*  162 Mb */
-               [60] = {  RC_INVALID, WLAN_RC_PHY_HT_40_TS, 243000,
-                       205100, 20, 20 }, /*  243 Mb */
-               [61] = {  RC_INVALID, WLAN_RC_PHY_HT_40_TS_HGI, 270000,
-                       224700, 20, 20 }, /*  270 Mb */
-               [62] = {  RC_HT_T_40, WLAN_RC_PHY_HT_40_TS, 324000,
-                       263100, 21, 21 }, /*  324 Mb */
-               [63] = {  RC_HT_T_40, WLAN_RC_PHY_HT_40_TS_HGI, 360000,
-                       288000, 21, 21 }, /*  360 Mb */
-               [64] = {  RC_HT_T_40, WLAN_RC_PHY_HT_40_TS, 364500,
-                       290700, 22, 22 }, /* 364.5 Mb */
-               [65] = {  RC_HT_T_40, WLAN_RC_PHY_HT_40_TS_HGI, 405000,
-                       317200, 22, 22 }, /* 405 Mb */
-               [66] = {  RC_HT_T_40, WLAN_RC_PHY_HT_40_TS, 405000,
-                       317200, 23, 23 }, /* 405 Mb */
-               [67] = {  RC_HT_T_40, WLAN_RC_PHY_HT_40_TS_HGI, 450000,
-                       346400, 23, 23 }, /* 450 Mb */
-       },
-       50,  /* probe interval */
-       WLAN_RC_HT_FLAG,  /* Phy rates allowed initially */
-};
-
-/* 4ms frame limit not used for NG mode.  The values filled
- * for HT are the 64K max aggregate limit */
-
-static const struct ath_rate_table ar5416_11ng_ratetable = {
-       72,
-       12, /* MCS start */
-       {
-               [0] = { RC_ALL, WLAN_RC_PHY_CCK, 1000,
-                       900, 0, 2 }, /* 1 Mb */
-               [1] = { RC_ALL, WLAN_RC_PHY_CCK, 2000,
-                       1900, 1, 4 }, /* 2 Mb */
-               [2] = { RC_ALL, WLAN_RC_PHY_CCK, 5500,
-                       4900, 2, 11 }, /* 5.5 Mb */
-               [3] = { RC_ALL, WLAN_RC_PHY_CCK, 11000,
-                       8100, 3, 22 }, /* 11 Mb */
-               [4] = { RC_INVALID, WLAN_RC_PHY_OFDM, 6000,
-                       5400, 4, 12 }, /* 6 Mb */
-               [5] = { RC_INVALID, WLAN_RC_PHY_OFDM, 9000,
-                       7800, 5, 18 }, /* 9 Mb */
-               [6] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 12000,
-                       10100, 6, 24 }, /* 12 Mb */
-               [7] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 18000,
-                       14100, 7, 36 }, /* 18 Mb */
-               [8] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 24000,
-                       17700, 8, 48 }, /* 24 Mb */
-               [9] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 36000,
-                       23700, 9, 72 }, /* 36 Mb */
-               [10] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 48000,
-                       27400, 10, 96 }, /* 48 Mb */
-               [11] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 54000,
-                       30900, 11, 108 }, /* 54 Mb */
-               [12] = { RC_INVALID, WLAN_RC_PHY_HT_20_SS, 6500,
-                       6400, 0, 0 }, /* 6.5 Mb */
-               [13] = { RC_HT_SDT_20, WLAN_RC_PHY_HT_20_SS, 13000,
-                       12700, 1, 1 }, /* 13 Mb */
-               [14] = { RC_HT_SDT_20, WLAN_RC_PHY_HT_20_SS, 19500,
-                       18800, 2, 2 }, /* 19.5 Mb*/
-               [15] = { RC_HT_SD_20, WLAN_RC_PHY_HT_20_SS, 26000,
-                       25000, 3, 3 }, /* 26 Mb */
-               [16] = { RC_HT_SD_20, WLAN_RC_PHY_HT_20_SS, 39000,
-                       36700, 4, 4 }, /* 39 Mb */
-               [17] = { RC_HT_S_20, WLAN_RC_PHY_HT_20_SS, 52000,
-                       48100, 5, 5 }, /* 52 Mb */
-               [18] = { RC_HT_S_20, WLAN_RC_PHY_HT_20_SS, 58500,
-                       53500, 6, 6 }, /* 58.5 Mb */
-               [19] = { RC_HT_S_20, WLAN_RC_PHY_HT_20_SS, 65000,
-                       59000, 7, 7 }, /* 65 Mb */
-               [20] = { RC_HT_S_20, WLAN_RC_PHY_HT_20_SS_HGI, 72200,
-                       65400, 7, 7 }, /* 65 Mb*/
-               [21] = { RC_INVALID, WLAN_RC_PHY_HT_20_DS, 13000,
-                       12700, 8, 8 }, /* 13 Mb */
-               [22] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_DS, 26000,
-                       24800, 9, 9 }, /* 26 Mb */
-               [23] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_DS, 39000,
-                       36600, 10, 10 }, /* 39 Mb */
-               [24] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS, 52000,
-                       48100, 11, 11 }, /* 52 Mb */
-               [25] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS, 78000,
-                       69500, 12, 12 }, /* 78 Mb */
-               [26] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS, 104000,
-                       89500, 13, 13 }, /* 104 Mb */
-               [27] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS, 117000,
-                       98900, 14, 14 }, /* 117 Mb */
-               [28] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS, 130000,
-                       108300, 15, 15 }, /* 130 Mb */
-               [29] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS_HGI, 144400,
-                       120000, 15, 15 }, /* 144.4 Mb */
-               [30] = {  RC_INVALID, WLAN_RC_PHY_HT_20_TS, 19500,
-                       17400, 16, 16 }, /* 19.5 Mb */
-               [31] = {  RC_INVALID, WLAN_RC_PHY_HT_20_TS, 39000,
-                       35100, 17, 17 }, /* 39 Mb */
-               [32] = {  RC_INVALID, WLAN_RC_PHY_HT_20_TS, 58500,
-                       52600, 18, 18 }, /* 58.5 Mb */
-               [33] = {  RC_INVALID, WLAN_RC_PHY_HT_20_TS, 78000,
-                       70400, 19, 19 }, /* 78 Mb */
-               [34] = {  RC_INVALID, WLAN_RC_PHY_HT_20_TS, 117000,
-                       104900, 20, 20 }, /* 117 Mb */
-               [35] = {  RC_INVALID, WLAN_RC_PHY_HT_20_TS_HGI, 130000,
-                       115800, 20, 20 }, /* 130 Mb */
-               [36] = {  RC_HT_T_20, WLAN_RC_PHY_HT_20_TS, 156000,
-                       137200, 21, 21 }, /* 156 Mb */
-               [37] = {  RC_HT_T_20, WLAN_RC_PHY_HT_20_TS_HGI, 173300,
-                       151100, 21, 21 }, /* 173.3 Mb */
-               [38] = {  RC_HT_T_20, WLAN_RC_PHY_HT_20_TS, 175500,
-                       152800, 22, 22 }, /* 175.5 Mb */
-               [39] = {  RC_HT_T_20, WLAN_RC_PHY_HT_20_TS_HGI, 195000,
-                       168400, 22, 22 }, /* 195 Mb */
-               [40] = {  RC_HT_T_20, WLAN_RC_PHY_HT_20_TS, 195000,
-                       168400, 23, 23 }, /* 195 Mb */
-               [41] = {  RC_HT_T_20, WLAN_RC_PHY_HT_20_TS_HGI, 216700,
-                       185000, 23, 23 }, /* 216.7 Mb */
-               [42] = { RC_HT_SDT_40, WLAN_RC_PHY_HT_40_SS, 13500,
-                       13200, 0, 0 }, /* 13.5 Mb */
-               [43] = { RC_HT_SDT_40, WLAN_RC_PHY_HT_40_SS, 27500,
-                       25900, 1, 1 }, /* 27.0 Mb */
-               [44] = { RC_HT_SDT_40, WLAN_RC_PHY_HT_40_SS, 40500,
-                       38600, 2, 2 }, /* 40.5 Mb */
-               [45] = { RC_HT_SD_40, WLAN_RC_PHY_HT_40_SS, 54000,
-                       49800, 3, 3 }, /* 54 Mb */
-               [46] = { RC_HT_SD_40, WLAN_RC_PHY_HT_40_SS, 81500,
-                       72200, 4, 4 }, /* 81 Mb */
-               [47] = { RC_HT_S_40 , WLAN_RC_PHY_HT_40_SS, 108000,
-                       92900, 5, 5 }, /* 108 Mb */
-               [48] = { RC_HT_S_40, WLAN_RC_PHY_HT_40_SS, 121500,
-                       102700, 6, 6 }, /* 121.5 Mb */
-               [49] = { RC_HT_S_40, WLAN_RC_PHY_HT_40_SS, 135000,
-                       112000, 7, 7 }, /* 135 Mb */
-               [50] = { RC_HT_S_40, WLAN_RC_PHY_HT_40_SS_HGI, 150000,
-                       122000, 7, 7 }, /* 150 Mb */
-               [51] = { RC_INVALID, WLAN_RC_PHY_HT_40_DS, 27000,
-                       25800, 8, 8 }, /* 27 Mb */
-               [52] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_DS, 54000,
-                       49800, 9, 9 }, /* 54 Mb */
-               [53] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_DS, 81000,
-                       71900, 10, 10 }, /* 81 Mb */
-               [54] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS, 108000,
-                       92500, 11, 11 }, /* 108 Mb */
-               [55] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS, 162000,
-                       130300, 12, 12 }, /* 162 Mb */
-               [56] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS, 216000,
-                       162800, 13, 13 }, /* 216 Mb */
-               [57] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS, 243000,
-                       178200, 14, 14 }, /* 243 Mb */
-               [58] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS, 270000,
-                       192100, 15, 15 }, /* 270 Mb */
-               [59] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS_HGI, 300000,
-                       207000, 15, 15 }, /* 300 Mb */
-               [60] = {  RC_INVALID, WLAN_RC_PHY_HT_40_TS, 40500,
-                       36100, 16, 16 }, /* 40.5 Mb */
-               [61] = {  RC_INVALID, WLAN_RC_PHY_HT_40_TS, 81000,
-                       72900, 17, 17 }, /* 81 Mb */
-               [62] = {  RC_INVALID, WLAN_RC_PHY_HT_40_TS, 121500,
-                       108300, 18, 18 }, /* 121.5 Mb */
-               [63] = {  RC_INVALID, WLAN_RC_PHY_HT_40_TS, 162000,
-                       142000, 19, 19 }, /* 162 Mb */
-               [64] = {  RC_INVALID, WLAN_RC_PHY_HT_40_TS, 243000,
-                       205100, 20, 20 }, /* 243 Mb */
-               [65] = {  RC_INVALID, WLAN_RC_PHY_HT_40_TS_HGI, 270000,
-                       224700, 20, 20 }, /* 270 Mb */
-               [66] = {  RC_HT_T_40, WLAN_RC_PHY_HT_40_TS, 324000,
-                       263100, 21, 21 }, /* 324 Mb */
-               [67] = {  RC_HT_T_40, WLAN_RC_PHY_HT_40_TS_HGI, 360000,
-                       288000, 21, 21 }, /* 360 Mb */
-               [68] = {  RC_HT_T_40, WLAN_RC_PHY_HT_40_TS, 364500,
-                       290700, 22, 22 }, /* 364.5 Mb */
-               [69] = {  RC_HT_T_40, WLAN_RC_PHY_HT_40_TS_HGI, 405000,
-                       317200, 22, 22 }, /* 405 Mb */
-               [70] = {  RC_HT_T_40, WLAN_RC_PHY_HT_40_TS, 405000,
-                       317200, 23, 23 }, /* 405 Mb */
-               [71] = {  RC_HT_T_40, WLAN_RC_PHY_HT_40_TS_HGI, 450000,
-                       346400, 23, 23 }, /* 450 Mb */
-       },
-       50,  /* probe interval */
-       WLAN_RC_HT_FLAG,  /* Phy rates allowed initially */
-};
-
-static const struct ath_rate_table ar5416_11a_ratetable = {
-       8,
-       0,
-       {
-               { RC_L_SDT, WLAN_RC_PHY_OFDM, 6000, /* 6 Mb */
-                       5400, 0, 12},
-               { RC_L_SDT, WLAN_RC_PHY_OFDM, 9000, /* 9 Mb */
-                       7800,  1, 18},
-               { RC_L_SDT, WLAN_RC_PHY_OFDM, 12000, /* 12 Mb */
-                       10000, 2, 24},
-               { RC_L_SDT, WLAN_RC_PHY_OFDM, 18000, /* 18 Mb */
-                       13900, 3, 36},
-               { RC_L_SDT, WLAN_RC_PHY_OFDM, 24000, /* 24 Mb */
-                       17300, 4, 48},
-               { RC_L_SDT, WLAN_RC_PHY_OFDM, 36000, /* 36 Mb */
-                       23000, 5, 72},
-               { RC_L_SDT, WLAN_RC_PHY_OFDM, 48000, /* 48 Mb */
-                       27400, 6, 96},
-               { RC_L_SDT, WLAN_RC_PHY_OFDM, 54000, /* 54 Mb */
-                       29300, 7, 108},
-       },
-       50,  /* probe interval */
-       0,   /* Phy rates allowed initially */
-};
-
-static const struct ath_rate_table ar5416_11g_ratetable = {
-       12,
-       0,
-       {
-               { RC_L_SDT, WLAN_RC_PHY_CCK, 1000, /* 1 Mb */
-                       900, 0, 2},
-               { RC_L_SDT, WLAN_RC_PHY_CCK, 2000, /* 2 Mb */
-                       1900, 1, 4},
-               { RC_L_SDT, WLAN_RC_PHY_CCK, 5500, /* 5.5 Mb */
-                       4900, 2, 11},
-               { RC_L_SDT, WLAN_RC_PHY_CCK, 11000, /* 11 Mb */
-                       8100, 3, 22},
-               { RC_INVALID, WLAN_RC_PHY_OFDM, 6000, /* 6 Mb */
-                       5400, 4, 12},
-               { RC_INVALID, WLAN_RC_PHY_OFDM, 9000, /* 9 Mb */
-                       7800, 5, 18},
-               { RC_L_SDT, WLAN_RC_PHY_OFDM, 12000, /* 12 Mb */
-                       10000, 6, 24},
-               { RC_L_SDT, WLAN_RC_PHY_OFDM, 18000, /* 18 Mb */
-                       13900, 7, 36},
-               { RC_L_SDT, WLAN_RC_PHY_OFDM, 24000, /* 24 Mb */
-                       17300, 8, 48},
-               { RC_L_SDT, WLAN_RC_PHY_OFDM, 36000, /* 36 Mb */
-                       23000, 9, 72},
-               { RC_L_SDT, WLAN_RC_PHY_OFDM, 48000, /* 48 Mb */
-                       27400, 10, 96},
-               { RC_L_SDT, WLAN_RC_PHY_OFDM, 54000, /* 54 Mb */
-                       29300, 11, 108},
-       },
-       50,  /* probe interval */
-       0,   /* Phy rates allowed initially */
-};
-
-static int ath_rc_get_rateindex(struct ath_rate_priv *ath_rc_priv,
-                               struct ieee80211_tx_rate *rate)
-{
-       const struct ath_rate_table *rate_table = ath_rc_priv->rate_table;
-       int rix, i, idx = 0;
-
-       if (!(rate->flags & IEEE80211_TX_RC_MCS))
-               return rate->idx;
-
-       for (i = 0; i < ath_rc_priv->max_valid_rate; i++) {
-               idx = ath_rc_priv->valid_rate_index[i];
-
-               if (WLAN_RC_PHY_HT(rate_table->info[idx].phy) &&
-                   rate_table->info[idx].ratecode == rate->idx)
-                       break;
-       }
-
-       rix = idx;
-
-       if (rate->flags & IEEE80211_TX_RC_SHORT_GI)
-               rix++;
-
-       return rix;
-}
-
-static void ath_rc_sort_validrates(struct ath_rate_priv *ath_rc_priv)
-{
-       const struct ath_rate_table *rate_table = ath_rc_priv->rate_table;
-       u8 i, j, idx, idx_next;
-
-       for (i = ath_rc_priv->max_valid_rate - 1; i > 0; i--) {
-               for (j = 0; j <= i-1; j++) {
-                       idx = ath_rc_priv->valid_rate_index[j];
-                       idx_next = ath_rc_priv->valid_rate_index[j+1];
-
-                       if (rate_table->info[idx].ratekbps >
-                               rate_table->info[idx_next].ratekbps) {
-                               ath_rc_priv->valid_rate_index[j] = idx_next;
-                               ath_rc_priv->valid_rate_index[j+1] = idx;
-                       }
-               }
-       }
-}
-
-static inline
-int ath_rc_get_nextvalid_txrate(const struct ath_rate_table *rate_table,
-                               struct ath_rate_priv *ath_rc_priv,
-                               u8 cur_valid_txrate,
-                               u8 *next_idx)
-{
-       u8 i;
-
-       for (i = 0; i < ath_rc_priv->max_valid_rate - 1; i++) {
-               if (ath_rc_priv->valid_rate_index[i] == cur_valid_txrate) {
-                       *next_idx = ath_rc_priv->valid_rate_index[i+1];
-                       return 1;
-               }
-       }
-
-       /* No more valid rates */
-       *next_idx = 0;
-
-       return 0;
-}
-
-/* Return true only for single stream */
-
-static int ath_rc_valid_phyrate(u32 phy, u32 capflag, int ignore_cw)
-{
-       if (WLAN_RC_PHY_HT(phy) && !(capflag & WLAN_RC_HT_FLAG))
-               return 0;
-       if (WLAN_RC_PHY_DS(phy) && !(capflag & WLAN_RC_DS_FLAG))
-               return 0;
-       if (WLAN_RC_PHY_TS(phy) && !(capflag & WLAN_RC_TS_FLAG))
-               return 0;
-       if (WLAN_RC_PHY_SGI(phy) && !(capflag & WLAN_RC_SGI_FLAG))
-               return 0;
-       if (!ignore_cw && WLAN_RC_PHY_HT(phy))
-               if (WLAN_RC_PHY_40(phy) && !(capflag & WLAN_RC_40_FLAG))
-                       return 0;
-       return 1;
-}
-
-static inline int
-ath_rc_get_lower_rix(struct ath_rate_priv *ath_rc_priv,
-                    u8 cur_valid_txrate, u8 *next_idx)
-{
-       int8_t i;
-
-       for (i = 1; i < ath_rc_priv->max_valid_rate ; i++) {
-               if (ath_rc_priv->valid_rate_index[i] == cur_valid_txrate) {
-                       *next_idx = ath_rc_priv->valid_rate_index[i-1];
-                       return 1;
-               }
-       }
-
-       return 0;
-}
-
-static u8 ath_rc_init_validrates(struct ath_rate_priv *ath_rc_priv)
-{
-       const struct ath_rate_table *rate_table = ath_rc_priv->rate_table;
-       u8 i, hi = 0;
-
-       for (i = 0; i < rate_table->rate_cnt; i++) {
-               if (rate_table->info[i].rate_flags & RC_LEGACY) {
-                       u32 phy = rate_table->info[i].phy;
-                       u8 valid_rate_count = 0;
-
-                       if (!ath_rc_valid_phyrate(phy, ath_rc_priv->ht_cap, 0))
-                               continue;
-
-                       valid_rate_count = ath_rc_priv->valid_phy_ratecnt[phy];
-
-                       ath_rc_priv->valid_phy_rateidx[phy][valid_rate_count] = i;
-                       ath_rc_priv->valid_phy_ratecnt[phy] += 1;
-                       ath_rc_priv->valid_rate_index[i] = true;
-                       hi = i;
-               }
-       }
-
-       return hi;
-}
-
-static inline bool ath_rc_check_legacy(u8 rate, u8 dot11rate, u16 rate_flags,
-                                      u32 phy, u32 capflag)
-{
-       if (rate != dot11rate || WLAN_RC_PHY_HT(phy))
-               return false;
-
-       if ((rate_flags & WLAN_RC_CAP_MODE(capflag)) != WLAN_RC_CAP_MODE(capflag))
-               return false;
-
-       if (!(rate_flags & WLAN_RC_CAP_STREAM(capflag)))
-               return false;
-
-       return true;
-}
-
-static inline bool ath_rc_check_ht(u8 rate, u8 dot11rate, u16 rate_flags,
-                                  u32 phy, u32 capflag)
-{
-       if (rate != dot11rate || !WLAN_RC_PHY_HT(phy))
-               return false;
-
-       if (!WLAN_RC_PHY_HT_VALID(rate_flags, capflag))
-               return false;
-
-       if (!(rate_flags & WLAN_RC_CAP_STREAM(capflag)))
-               return false;
-
-       return true;
-}
-
-static u8 ath_rc_setvalid_rates(struct ath_rate_priv *ath_rc_priv, bool legacy)
-{
-       const struct ath_rate_table *rate_table = ath_rc_priv->rate_table;
-       struct ath_rateset *rateset;
-       u32 phy, capflag = ath_rc_priv->ht_cap;
-       u16 rate_flags;
-       u8 i, j, hi = 0, rate, dot11rate, valid_rate_count;
-
-       if (legacy)
-               rateset = &ath_rc_priv->neg_rates;
-       else
-               rateset = &ath_rc_priv->neg_ht_rates;
-
-       for (i = 0; i < rateset->rs_nrates; i++) {
-               for (j = 0; j < rate_table->rate_cnt; j++) {
-                       phy = rate_table->info[j].phy;
-                       rate_flags = rate_table->info[j].rate_flags;
-                       rate = rateset->rs_rates[i];
-                       dot11rate = rate_table->info[j].dot11rate;
-
-                       if (legacy &&
-                           !ath_rc_check_legacy(rate, dot11rate,
-                                                rate_flags, phy, capflag))
-                               continue;
-
-                       if (!legacy &&
-                           !ath_rc_check_ht(rate, dot11rate,
-                                            rate_flags, phy, capflag))
-                               continue;
-
-                       if (!ath_rc_valid_phyrate(phy, capflag, 0))
-                               continue;
-
-                       valid_rate_count = ath_rc_priv->valid_phy_ratecnt[phy];
-                       ath_rc_priv->valid_phy_rateidx[phy][valid_rate_count] = j;
-                       ath_rc_priv->valid_phy_ratecnt[phy] += 1;
-                       ath_rc_priv->valid_rate_index[j] = true;
-                       hi = max(hi, j);
-               }
-       }
-
-       return hi;
-}
-
-static u8 ath_rc_get_highest_rix(struct ath_rate_priv *ath_rc_priv,
-                                int *is_probing)
-{
-       const struct ath_rate_table *rate_table = ath_rc_priv->rate_table;
-       u32 best_thruput, this_thruput, now_msec;
-       u8 rate, next_rate, best_rate, maxindex, minindex;
-       int8_t index = 0;
-
-       now_msec = jiffies_to_msecs(jiffies);
-       *is_probing = 0;
-       best_thruput = 0;
-       maxindex = ath_rc_priv->max_valid_rate-1;
-       minindex = 0;
-       best_rate = minindex;
-
-       /*
-        * Try the higher rate first. It will reduce memory moving time
-        * if we have very good channel characteristics.
-        */
-       for (index = maxindex; index >= minindex ; index--) {
-               u8 per_thres;
-
-               rate = ath_rc_priv->valid_rate_index[index];
-               if (rate > ath_rc_priv->rate_max_phy)
-                       continue;
-
-               /*
-                * For TCP the average collision rate is around 11%,
-                * so we ignore PERs less than this.  This is to
-                * prevent the rate we are currently using (whose
-                * PER might be in the 10-15 range because of TCP
-                * collisions) looking worse than the next lower
-                * rate whose PER has decayed close to 0.  If we
-                * used to next lower rate, its PER would grow to
-                * 10-15 and we would be worse off then staying
-                * at the current rate.
-                */
-               per_thres = ath_rc_priv->per[rate];
-               if (per_thres < 12)
-                       per_thres = 12;
-
-               this_thruput = rate_table->info[rate].user_ratekbps *
-                       (100 - per_thres);
-
-               if (best_thruput <= this_thruput) {
-                       best_thruput = this_thruput;
-                       best_rate    = rate;
-               }
-       }
-
-       rate = best_rate;
-
-       /*
-        * Must check the actual rate (ratekbps) to account for
-        * non-monoticity of 11g's rate table
-        */
-
-       if (rate >= ath_rc_priv->rate_max_phy) {
-               rate = ath_rc_priv->rate_max_phy;
-
-               /* Probe the next allowed phy state */
-               if (ath_rc_get_nextvalid_txrate(rate_table,
-                                       ath_rc_priv, rate, &next_rate) &&
-                   (now_msec - ath_rc_priv->probe_time >
-                    rate_table->probe_interval) &&
-                   (ath_rc_priv->hw_maxretry_pktcnt >= 1)) {
-                       rate = next_rate;
-                       ath_rc_priv->probe_rate = rate;
-                       ath_rc_priv->probe_time = now_msec;
-                       ath_rc_priv->hw_maxretry_pktcnt = 0;
-                       *is_probing = 1;
-               }
-       }
-
-       if (rate > (ath_rc_priv->rate_table_size - 1))
-               rate = ath_rc_priv->rate_table_size - 1;
-
-       if (RC_TS_ONLY(rate_table->info[rate].rate_flags) &&
-           (ath_rc_priv->ht_cap & WLAN_RC_TS_FLAG))
-               return rate;
-
-       if (RC_DS_OR_LATER(rate_table->info[rate].rate_flags) &&
-           (ath_rc_priv->ht_cap & (WLAN_RC_DS_FLAG | WLAN_RC_TS_FLAG)))
-               return rate;
-
-       if (RC_SS_OR_LEGACY(rate_table->info[rate].rate_flags))
-               return rate;
-
-       /* This should not happen */
-       WARN_ON_ONCE(1);
-
-       rate = ath_rc_priv->valid_rate_index[0];
-
-       return rate;
-}
-
-static void ath_rc_rate_set_series(const struct ath_rate_table *rate_table,
-                                  struct ieee80211_tx_rate *rate,
-                                  struct ieee80211_tx_rate_control *txrc,
-                                  u8 tries, u8 rix, int rtsctsenable)
-{
-       rate->count = tries;
-       rate->idx = rate_table->info[rix].ratecode;
-
-       if (txrc->rts || rtsctsenable)
-               rate->flags |= IEEE80211_TX_RC_USE_RTS_CTS;
-
-       if (WLAN_RC_PHY_HT(rate_table->info[rix].phy)) {
-               rate->flags |= IEEE80211_TX_RC_MCS;
-               if (WLAN_RC_PHY_40(rate_table->info[rix].phy) &&
-                   conf_is_ht40(&txrc->hw->conf))
-                       rate->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
-               if (WLAN_RC_PHY_SGI(rate_table->info[rix].phy))
-                       rate->flags |= IEEE80211_TX_RC_SHORT_GI;
-       }
-}
-
-static void ath_rc_rate_set_rtscts(struct ath_softc *sc,
-                                  const struct ath_rate_table *rate_table,
-                                  struct ieee80211_tx_info *tx_info)
-{
-       struct ieee80211_bss_conf *bss_conf;
-
-       if (!tx_info->control.vif)
-               return;
-       /*
-        * For legacy frames, mac80211 takes care of CTS protection.
-        */
-       if (!(tx_info->control.rates[0].flags & IEEE80211_TX_RC_MCS))
-               return;
-
-       bss_conf = &tx_info->control.vif->bss_conf;
-
-       if (!bss_conf->basic_rates)
-               return;
-
-       /*
-        * For now, use the lowest allowed basic rate for HT frames.
-        */
-       tx_info->control.rts_cts_rate_idx = __ffs(bss_conf->basic_rates);
-}
-
-static void ath_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
-                        struct ieee80211_tx_rate_control *txrc)
-{
-       struct ath_softc *sc = priv;
-       struct ath_rate_priv *ath_rc_priv = priv_sta;
-       const struct ath_rate_table *rate_table;
-       struct sk_buff *skb = txrc->skb;
-       struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
-       struct ieee80211_tx_rate *rates = tx_info->control.rates;
-       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
-       __le16 fc = hdr->frame_control;
-       u8 try_per_rate, i = 0, rix;
-       int is_probe = 0;
-
-       if (rate_control_send_low(sta, priv_sta, txrc))
-               return;
-
-       /*
-        * For Multi Rate Retry we use a different number of
-        * retry attempt counts. This ends up looking like this:
-        *
-        * MRR[0] = 4
-        * MRR[1] = 4
-        * MRR[2] = 4
-        * MRR[3] = 8
-        *
-        */
-       try_per_rate = 4;
-
-       rate_table = ath_rc_priv->rate_table;
-       rix = ath_rc_get_highest_rix(ath_rc_priv, &is_probe);
-
-       if (conf_is_ht(&sc->hw->conf) &&
-           (sta->ht_cap.cap & IEEE80211_HT_CAP_LDPC_CODING))
-               tx_info->flags |= IEEE80211_TX_CTL_LDPC;
-
-       if (conf_is_ht(&sc->hw->conf) &&
-           (sta->ht_cap.cap & IEEE80211_HT_CAP_TX_STBC))
-               tx_info->flags |= (1 << IEEE80211_TX_CTL_STBC_SHIFT);
-
-       if (is_probe) {
-               /*
-                * Set one try for probe rates. For the
-                * probes don't enable RTS.
-                */
-               ath_rc_rate_set_series(rate_table, &rates[i++], txrc,
-                                      1, rix, 0);
-               /*
-                * Get the next tried/allowed rate.
-                * No RTS for the next series after the probe rate.
-                */
-               ath_rc_get_lower_rix(ath_rc_priv, rix, &rix);
-               ath_rc_rate_set_series(rate_table, &rates[i++], txrc,
-                                      try_per_rate, rix, 0);
-
-               tx_info->flags |= IEEE80211_TX_CTL_RATE_CTRL_PROBE;
-       } else {
-               /*
-                * Set the chosen rate. No RTS for first series entry.
-                */
-               ath_rc_rate_set_series(rate_table, &rates[i++], txrc,
-                                      try_per_rate, rix, 0);
-       }
-
-       for ( ; i < 4; i++) {
-               /*
-                * Use twice the number of tries for the last MRR segment.
-                */
-               if (i + 1 == 4)
-                       try_per_rate = 8;
-
-               ath_rc_get_lower_rix(ath_rc_priv, rix, &rix);
-
-               /*
-                * All other rates in the series have RTS enabled.
-                */
-               ath_rc_rate_set_series(rate_table, &rates[i], txrc,
-                                      try_per_rate, rix, 1);
-       }
-
-       /*
-        * NB:Change rate series to enable aggregation when operating
-        * at lower MCS rates. When first rate in series is MCS2
-        * in HT40 @ 2.4GHz, series should look like:
-        *
-        * {MCS2, MCS1, MCS0, MCS0}.
-        *
-        * When first rate in series is MCS3 in HT20 @ 2.4GHz, series should
-        * look like:
-        *
-        * {MCS3, MCS2, MCS1, MCS1}
-        *
-        * So, set fourth rate in series to be same as third one for
-        * above conditions.
-        */
-       if ((sc->hw->conf.chandef.chan->band == IEEE80211_BAND_2GHZ) &&
-           (conf_is_ht(&sc->hw->conf))) {
-               u8 dot11rate = rate_table->info[rix].dot11rate;
-               u8 phy = rate_table->info[rix].phy;
-               if (i == 4 &&
-                   ((dot11rate == 2 && phy == WLAN_RC_PHY_HT_40_SS) ||
-                    (dot11rate == 3 && phy == WLAN_RC_PHY_HT_20_SS))) {
-                       rates[3].idx = rates[2].idx;
-                       rates[3].flags = rates[2].flags;
-               }
-       }
-
-       /*
-        * Force hardware to use computed duration for next
-        * fragment by disabling multi-rate retry, which
-        * updates duration based on the multi-rate duration table.
-        *
-        * FIXME: Fix duration
-        */
-       if (ieee80211_has_morefrags(fc) ||
-           (le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG)) {
-               rates[1].count = rates[2].count = rates[3].count = 0;
-               rates[1].idx = rates[2].idx = rates[3].idx = 0;
-               rates[0].count = ATH_TXMAXTRY;
-       }
-
-       ath_rc_rate_set_rtscts(sc, rate_table, tx_info);
-}
-
-static void ath_rc_update_per(struct ath_softc *sc,
-                             const struct ath_rate_table *rate_table,
-                             struct ath_rate_priv *ath_rc_priv,
-                                 struct ieee80211_tx_info *tx_info,
-                             int tx_rate, int xretries, int retries,
-                             u32 now_msec)
-{
-       int count, n_bad_frames;
-       u8 last_per;
-       static const u32 nretry_to_per_lookup[10] = {
-               100 * 0 / 1,
-               100 * 1 / 4,
-               100 * 1 / 2,
-               100 * 3 / 4,
-               100 * 4 / 5,
-               100 * 5 / 6,
-               100 * 6 / 7,
-               100 * 7 / 8,
-               100 * 8 / 9,
-               100 * 9 / 10
-       };
-
-       last_per = ath_rc_priv->per[tx_rate];
-       n_bad_frames = tx_info->status.ampdu_len - tx_info->status.ampdu_ack_len;
-
-       if (xretries) {
-               if (xretries == 1) {
-                       ath_rc_priv->per[tx_rate] += 30;
-                       if (ath_rc_priv->per[tx_rate] > 100)
-                               ath_rc_priv->per[tx_rate] = 100;
-               } else {
-                       /* xretries == 2 */
-                       count = ARRAY_SIZE(nretry_to_per_lookup);
-                       if (retries >= count)
-                               retries = count - 1;
-
-                       /* new_PER = 7/8*old_PER + 1/8*(currentPER) */
-                       ath_rc_priv->per[tx_rate] =
-                               (u8)(last_per - (last_per >> 3) + (100 >> 3));
-               }
-
-               /* xretries == 1 or 2 */
-
-               if (ath_rc_priv->probe_rate == tx_rate)
-                       ath_rc_priv->probe_rate = 0;
-
-       } else { /* xretries == 0 */
-               count = ARRAY_SIZE(nretry_to_per_lookup);
-               if (retries >= count)
-                       retries = count - 1;
-
-               if (n_bad_frames) {
-                       /* new_PER = 7/8*old_PER + 1/8*(currentPER)
-                        * Assuming that n_frames is not 0.  The current PER
-                        * from the retries is 100 * retries / (retries+1),
-                        * since the first retries attempts failed, and the
-                        * next one worked.  For the one that worked,
-                        * n_bad_frames subframes out of n_frames wored,
-                        * so the PER for that part is
-                        * 100 * n_bad_frames / n_frames, and it contributes
-                        * 100 * n_bad_frames / (n_frames * (retries+1)) to
-                        * the above PER.  The expression below is a
-                        * simplified version of the sum of these two terms.
-                        */
-                       if (tx_info->status.ampdu_len > 0) {
-                               int n_frames, n_bad_tries;
-                               u8 cur_per, new_per;
-
-                               n_bad_tries = retries * tx_info->status.ampdu_len +
-                                       n_bad_frames;
-                               n_frames = tx_info->status.ampdu_len * (retries + 1);
-                               cur_per = (100 * n_bad_tries / n_frames) >> 3;
-                               new_per = (u8)(last_per - (last_per >> 3) + cur_per);
-                               ath_rc_priv->per[tx_rate] = new_per;
-                       }
-               } else {
-                       ath_rc_priv->per[tx_rate] =
-                               (u8)(last_per - (last_per >> 3) +
-                                    (nretry_to_per_lookup[retries] >> 3));
-               }
-
-
-               /*
-                * If we got at most one retry then increase the max rate if
-                * this was a probe.  Otherwise, ignore the probe.
-                */
-               if (ath_rc_priv->probe_rate && ath_rc_priv->probe_rate == tx_rate) {
-                       if (retries > 0 || 2 * n_bad_frames > tx_info->status.ampdu_len) {
-                               /*
-                                * Since we probed with just a single attempt,
-                                * any retries means the probe failed.  Also,
-                                * if the attempt worked, but more than half
-                                * the subframes were bad then also consider
-                                * the probe a failure.
-                                */
-                               ath_rc_priv->probe_rate = 0;
-                       } else {
-                               u8 probe_rate = 0;
-
-                               ath_rc_priv->rate_max_phy =
-                                       ath_rc_priv->probe_rate;
-                               probe_rate = ath_rc_priv->probe_rate;
-
-                               if (ath_rc_priv->per[probe_rate] > 30)
-                                       ath_rc_priv->per[probe_rate] = 20;
-
-                               ath_rc_priv->probe_rate = 0;
-
-                               /*
-                                * Since this probe succeeded, we allow the next
-                                * probe twice as soon.  This allows the maxRate
-                                * to move up faster if the probes are
-                                * successful.
-                                */
-                               ath_rc_priv->probe_time =
-                                       now_msec - rate_table->probe_interval / 2;
-                       }
-               }
-
-               if (retries > 0) {
-                       /*
-                        * Don't update anything.  We don't know if
-                        * this was because of collisions or poor signal.
-                        */
-                       ath_rc_priv->hw_maxretry_pktcnt = 0;
-               } else {
-                       /*
-                        * It worked with no retries. First ignore bogus (small)
-                        * rssi_ack values.
-                        */
-                       if (tx_rate == ath_rc_priv->rate_max_phy &&
-                           ath_rc_priv->hw_maxretry_pktcnt < 255) {
-                               ath_rc_priv->hw_maxretry_pktcnt++;
-                       }
-
-               }
-       }
-}
-
-static void ath_rc_update_ht(struct ath_softc *sc,
-                            struct ath_rate_priv *ath_rc_priv,
-                            struct ieee80211_tx_info *tx_info,
-                            int tx_rate, int xretries, int retries)
-{
-       u32 now_msec = jiffies_to_msecs(jiffies);
-       int rate;
-       u8 last_per;
-       const struct ath_rate_table *rate_table = ath_rc_priv->rate_table;
-       int size = ath_rc_priv->rate_table_size;
-
-       if ((tx_rate < 0) || (tx_rate > rate_table->rate_cnt))
-               return;
-
-       last_per = ath_rc_priv->per[tx_rate];
-
-       /* Update PER first */
-       ath_rc_update_per(sc, rate_table, ath_rc_priv,
-                         tx_info, tx_rate, xretries,
-                         retries, now_msec);
-
-       /*
-        * If this rate looks bad (high PER) then stop using it for
-        * a while (except if we are probing).
-        */
-       if (ath_rc_priv->per[tx_rate] >= 55 && tx_rate > 0 &&
-           rate_table->info[tx_rate].ratekbps <=
-           rate_table->info[ath_rc_priv->rate_max_phy].ratekbps) {
-               ath_rc_get_lower_rix(ath_rc_priv, (u8)tx_rate,
-                                    &ath_rc_priv->rate_max_phy);
-
-               /* Don't probe for a little while. */
-               ath_rc_priv->probe_time = now_msec;
-       }
-
-       /* Make sure the rates below this have lower PER */
-       /* Monotonicity is kept only for rates below the current rate. */
-       if (ath_rc_priv->per[tx_rate] < last_per) {
-               for (rate = tx_rate - 1; rate >= 0; rate--) {
-
-                       if (ath_rc_priv->per[rate] >
-                           ath_rc_priv->per[rate+1]) {
-                               ath_rc_priv->per[rate] =
-                                       ath_rc_priv->per[rate+1];
-                       }
-               }
-       }
-
-       /* Maintain monotonicity for rates above the current rate */
-       for (rate = tx_rate; rate < size - 1; rate++) {
-               if (ath_rc_priv->per[rate+1] <
-                   ath_rc_priv->per[rate])
-                       ath_rc_priv->per[rate+1] =
-                               ath_rc_priv->per[rate];
-       }
-
-       /* Every so often, we reduce the thresholds
-        * and PER (different for CCK and OFDM). */
-       if (now_msec - ath_rc_priv->per_down_time >=
-           rate_table->probe_interval) {
-               for (rate = 0; rate < size; rate++) {
-                       ath_rc_priv->per[rate] =
-                               7 * ath_rc_priv->per[rate] / 8;
-               }
-
-               ath_rc_priv->per_down_time = now_msec;
-       }
-
-       ath_debug_stat_retries(ath_rc_priv, tx_rate, xretries, retries,
-                              ath_rc_priv->per[tx_rate]);
-
-}
-
-static void ath_rc_tx_status(struct ath_softc *sc,
-                            struct ath_rate_priv *ath_rc_priv,
-                            struct sk_buff *skb)
-{
-       struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
-       struct ieee80211_tx_rate *rates = tx_info->status.rates;
-       struct ieee80211_tx_rate *rate;
-       int final_ts_idx = 0, xretries = 0, long_retry = 0;
-       u8 flags;
-       u32 i = 0, rix;
-
-       for (i = 0; i < sc->hw->max_rates; i++) {
-               rate = &tx_info->status.rates[i];
-               if (rate->idx < 0 || !rate->count)
-                       break;
-
-               final_ts_idx = i;
-               long_retry = rate->count - 1;
-       }
-
-       if (!(tx_info->flags & IEEE80211_TX_STAT_ACK))
-               xretries = 1;
-
-       /*
-        * If the first rate is not the final index, there
-        * are intermediate rate failures to be processed.
-        */
-       if (final_ts_idx != 0) {
-               for (i = 0; i < final_ts_idx ; i++) {
-                       if (rates[i].count != 0 && (rates[i].idx >= 0)) {
-                               flags = rates[i].flags;
-
-                               /* If HT40 and we have switched mode from
-                                * 40 to 20 => don't update */
-
-                               if ((flags & IEEE80211_TX_RC_40_MHZ_WIDTH) &&
-                                   !(ath_rc_priv->ht_cap & WLAN_RC_40_FLAG))
-                                       return;
-
-                               rix = ath_rc_get_rateindex(ath_rc_priv, &rates[i]);
-                               ath_rc_update_ht(sc, ath_rc_priv, tx_info,
-                                                rix, xretries ? 1 : 2,
-                                                rates[i].count);
-                       }
-               }
-       }
-
-       flags = rates[final_ts_idx].flags;
-
-       /* If HT40 and we have switched mode from 40 to 20 => don't update */
-       if ((flags & IEEE80211_TX_RC_40_MHZ_WIDTH) &&
-           !(ath_rc_priv->ht_cap & WLAN_RC_40_FLAG))
-               return;
-
-       rix = ath_rc_get_rateindex(ath_rc_priv, &rates[final_ts_idx]);
-       ath_rc_update_ht(sc, ath_rc_priv, tx_info, rix, xretries, long_retry);
-       ath_debug_stat_rc(ath_rc_priv, rix);
-}
-
-static const
-struct ath_rate_table *ath_choose_rate_table(struct ath_softc *sc,
-                                            enum ieee80211_band band,
-                                            bool is_ht)
-{
-       switch(band) {
-       case IEEE80211_BAND_2GHZ:
-               if (is_ht)
-                       return &ar5416_11ng_ratetable;
-               return &ar5416_11g_ratetable;
-       case IEEE80211_BAND_5GHZ:
-               if (is_ht)
-                       return &ar5416_11na_ratetable;
-               return &ar5416_11a_ratetable;
-       default:
-               return NULL;
-       }
-}
-
-static void ath_rc_init(struct ath_softc *sc,
-                       struct ath_rate_priv *ath_rc_priv)
-{
-       const struct ath_rate_table *rate_table = ath_rc_priv->rate_table;
-       struct ath_rateset *rateset = &ath_rc_priv->neg_rates;
-       struct ath_common *common = ath9k_hw_common(sc->sc_ah);
-       u8 i, j, k, hi = 0, hthi = 0;
-
-       ath_rc_priv->rate_table_size = RATE_TABLE_SIZE;
-
-       for (i = 0 ; i < ath_rc_priv->rate_table_size; i++) {
-               ath_rc_priv->per[i] = 0;
-               ath_rc_priv->valid_rate_index[i] = 0;
-       }
-
-       for (i = 0; i < WLAN_RC_PHY_MAX; i++) {
-               for (j = 0; j < RATE_TABLE_SIZE; j++)
-                       ath_rc_priv->valid_phy_rateidx[i][j] = 0;
-               ath_rc_priv->valid_phy_ratecnt[i] = 0;
-       }
-
-       if (!rateset->rs_nrates) {
-               hi = ath_rc_init_validrates(ath_rc_priv);
-       } else {
-               hi = ath_rc_setvalid_rates(ath_rc_priv, true);
-
-               if (ath_rc_priv->ht_cap & WLAN_RC_HT_FLAG)
-                       hthi = ath_rc_setvalid_rates(ath_rc_priv, false);
-
-               hi = max(hi, hthi);
-       }
-
-       ath_rc_priv->rate_table_size = hi + 1;
-       ath_rc_priv->rate_max_phy = 0;
-       WARN_ON(ath_rc_priv->rate_table_size > RATE_TABLE_SIZE);
-
-       for (i = 0, k = 0; i < WLAN_RC_PHY_MAX; i++) {
-               for (j = 0; j < ath_rc_priv->valid_phy_ratecnt[i]; j++) {
-                       ath_rc_priv->valid_rate_index[k++] =
-                               ath_rc_priv->valid_phy_rateidx[i][j];
-               }
-
-               if (!ath_rc_valid_phyrate(i, rate_table->initial_ratemax, 1) ||
-                   !ath_rc_priv->valid_phy_ratecnt[i])
-                       continue;
-
-               ath_rc_priv->rate_max_phy = ath_rc_priv->valid_phy_rateidx[i][j-1];
-       }
-       WARN_ON(ath_rc_priv->rate_table_size > RATE_TABLE_SIZE);
-       WARN_ON(k > RATE_TABLE_SIZE);
-
-       ath_rc_priv->max_valid_rate = k;
-       ath_rc_sort_validrates(ath_rc_priv);
-       ath_rc_priv->rate_max_phy = (k > 4) ?
-               ath_rc_priv->valid_rate_index[k-4] :
-               ath_rc_priv->valid_rate_index[k-1];
-
-       ath_dbg(common, CONFIG, "RC Initialized with capabilities: 0x%x\n",
-               ath_rc_priv->ht_cap);
-}
-
-static u8 ath_rc_build_ht_caps(struct ath_softc *sc, struct ieee80211_sta *sta)
-{
-       u8 caps = 0;
-
-       if (sta->ht_cap.ht_supported) {
-               caps = WLAN_RC_HT_FLAG;
-               if (sta->ht_cap.mcs.rx_mask[1] && sta->ht_cap.mcs.rx_mask[2])
-                       caps |= WLAN_RC_TS_FLAG | WLAN_RC_DS_FLAG;
-               else if (sta->ht_cap.mcs.rx_mask[1])
-                       caps |= WLAN_RC_DS_FLAG;
-               if (sta->bandwidth >= IEEE80211_STA_RX_BW_40) {
-                       caps |= WLAN_RC_40_FLAG;
-                       if (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40)
-                               caps |= WLAN_RC_SGI_FLAG;
-               } else {
-                       if (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20)
-                               caps |= WLAN_RC_SGI_FLAG;
-               }
-       }
-
-       return caps;
-}
-
-static bool ath_tx_aggr_check(struct ath_softc *sc, struct ieee80211_sta *sta,
-                             u8 tidno)
-{
-       struct ath_node *an = (struct ath_node *)sta->drv_priv;
-       struct ath_atx_tid *txtid;
-
-       if (!sta->ht_cap.ht_supported)
-               return false;
-
-       txtid = ATH_AN_2_TID(an, tidno);
-       return !txtid->active;
-}
-
-
-/***********************************/
-/* mac80211 Rate Control callbacks */
-/***********************************/
-
-static void ath_tx_status(void *priv, struct ieee80211_supported_band *sband,
-                         struct ieee80211_sta *sta, void *priv_sta,
-                         struct sk_buff *skb)
-{
-       struct ath_softc *sc = priv;
-       struct ath_rate_priv *ath_rc_priv = priv_sta;
-       struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
-       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
-       __le16 fc = hdr->frame_control;
-
-       if (!priv_sta || !ieee80211_is_data(fc))
-               return;
-
-       /* This packet was aggregated but doesn't carry status info */
-       if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) &&
-           !(tx_info->flags & IEEE80211_TX_STAT_AMPDU))
-               return;
-
-       if (tx_info->flags & IEEE80211_TX_STAT_TX_FILTERED)
-               return;
-
-       ath_rc_tx_status(sc, ath_rc_priv, skb);
-
-       /* Check if aggregation has to be enabled for this tid */
-       if (conf_is_ht(&sc->hw->conf) &&
-           !(skb->protocol == cpu_to_be16(ETH_P_PAE))) {
-               if (ieee80211_is_data_qos(fc) &&
-                   skb_get_queue_mapping(skb) != IEEE80211_AC_VO) {
-                       u8 *qc, tid;
-
-                       qc = ieee80211_get_qos_ctl(hdr);
-                       tid = qc[0] & 0xf;
-
-                       if(ath_tx_aggr_check(sc, sta, tid))
-                               ieee80211_start_tx_ba_session(sta, tid, 0);
-               }
-       }
-}
-
-static void ath_rate_init(void *priv, struct ieee80211_supported_band *sband,
-                         struct cfg80211_chan_def *chandef,
-                          struct ieee80211_sta *sta, void *priv_sta)
-{
-       struct ath_softc *sc = priv;
-       struct ath_common *common = ath9k_hw_common(sc->sc_ah);
-       struct ath_rate_priv *ath_rc_priv = priv_sta;
-       int i, j = 0;
-       u32 rate_flags = ieee80211_chandef_rate_flags(&sc->hw->conf.chandef);
-
-       for (i = 0; i < sband->n_bitrates; i++) {
-               if (sta->supp_rates[sband->band] & BIT(i)) {
-                       if ((rate_flags & sband->bitrates[i].flags)
-                           != rate_flags)
-                               continue;
-
-                       ath_rc_priv->neg_rates.rs_rates[j]
-                               = (sband->bitrates[i].bitrate * 2) / 10;
-                       j++;
-               }
-       }
-       ath_rc_priv->neg_rates.rs_nrates = j;
-
-       if (sta->ht_cap.ht_supported) {
-               for (i = 0, j = 0; i < 77; i++) {
-                       if (sta->ht_cap.mcs.rx_mask[i/8] & (1<<(i%8)))
-                               ath_rc_priv->neg_ht_rates.rs_rates[j++] = i;
-                       if (j == ATH_RATE_MAX)
-                               break;
-               }
-               ath_rc_priv->neg_ht_rates.rs_nrates = j;
-       }
-
-       ath_rc_priv->rate_table = ath_choose_rate_table(sc, sband->band,
-                                                       sta->ht_cap.ht_supported);
-       if (!ath_rc_priv->rate_table) {
-               ath_err(common, "No rate table chosen\n");
-               return;
-       }
-
-       ath_rc_priv->ht_cap = ath_rc_build_ht_caps(sc, sta);
-       ath_rc_init(sc, priv_sta);
-}
-
-static void ath_rate_update(void *priv, struct ieee80211_supported_band *sband,
-                           struct cfg80211_chan_def *chandef,
-                           struct ieee80211_sta *sta, void *priv_sta,
-                           u32 changed)
-{
-       struct ath_softc *sc = priv;
-       struct ath_rate_priv *ath_rc_priv = priv_sta;
-
-       if (changed & IEEE80211_RC_BW_CHANGED) {
-               ath_rc_priv->ht_cap = ath_rc_build_ht_caps(sc, sta);
-               ath_rc_init(sc, priv_sta);
-
-               ath_dbg(ath9k_hw_common(sc->sc_ah), CONFIG,
-                       "Operating Bandwidth changed to: %d\n",
-                       sc->hw->conf.chandef.width);
-       }
-}
-
-#if defined(CONFIG_MAC80211_DEBUGFS) && defined(CONFIG_ATH9K_DEBUGFS)
-
-void ath_debug_stat_rc(struct ath_rate_priv *rc, int final_rate)
-{
-       struct ath_rc_stats *stats;
-
-       stats = &rc->rcstats[final_rate];
-       stats->success++;
-}
-
-void ath_debug_stat_retries(struct ath_rate_priv *rc, int rix,
-                           int xretries, int retries, u8 per)
-{
-       struct ath_rc_stats *stats = &rc->rcstats[rix];
-
-       stats->xretries += xretries;
-       stats->retries += retries;
-       stats->per = per;
-}
-
-static ssize_t read_file_rcstat(struct file *file, char __user *user_buf,
-                               size_t count, loff_t *ppos)
-{
-       struct ath_rate_priv *rc = file->private_data;
-       char *buf;
-       unsigned int len = 0, max;
-       int rix;
-       ssize_t retval;
-
-       if (rc->rate_table == NULL)
-               return 0;
-
-       max = 80 + rc->rate_table_size * 1024 + 1;
-       buf = kmalloc(max, GFP_KERNEL);
-       if (buf == NULL)
-               return -ENOMEM;
-
-       len += sprintf(buf, "%6s %6s %6s "
-                      "%10s %10s %10s %10s\n",
-                      "HT", "MCS", "Rate",
-                      "Success", "Retries", "XRetries", "PER");
-
-       for (rix = 0; rix < rc->max_valid_rate; rix++) {
-               u8 i = rc->valid_rate_index[rix];
-               u32 ratekbps = rc->rate_table->info[i].ratekbps;
-               struct ath_rc_stats *stats = &rc->rcstats[i];
-               char mcs[5];
-               char htmode[5];
-               int used_mcs = 0, used_htmode = 0;
-
-               if (WLAN_RC_PHY_HT(rc->rate_table->info[i].phy)) {
-                       used_mcs = scnprintf(mcs, 5, "%d",
-                                            rc->rate_table->info[i].ratecode);
-
-                       if (WLAN_RC_PHY_40(rc->rate_table->info[i].phy))
-                               used_htmode = scnprintf(htmode, 5, "HT40");
-                       else if (WLAN_RC_PHY_20(rc->rate_table->info[i].phy))
-                               used_htmode = scnprintf(htmode, 5, "HT20");
-                       else
-                               used_htmode = scnprintf(htmode, 5, "????");
-               }
-
-               mcs[used_mcs] = '\0';
-               htmode[used_htmode] = '\0';
-
-               len += scnprintf(buf + len, max - len,
-                                "%6s %6s %3u.%d: "
-                                "%10u %10u %10u %10u\n",
-                                htmode,
-                                mcs,
-                                ratekbps / 1000,
-                                (ratekbps % 1000) / 100,
-                                stats->success,
-                                stats->retries,
-                                stats->xretries,
-                                stats->per);
-       }
-
-       if (len > max)
-               len = max;
-
-       retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
-       kfree(buf);
-       return retval;
-}
-
-static const struct file_operations fops_rcstat = {
-       .read = read_file_rcstat,
-       .open = simple_open,
-       .owner = THIS_MODULE
-};
-
-static void ath_rate_add_sta_debugfs(void *priv, void *priv_sta,
-                                    struct dentry *dir)
-{
-       struct ath_rate_priv *rc = priv_sta;
-       rc->debugfs_rcstats = debugfs_create_file("rc_stats", S_IRUGO,
-                                                 dir, rc, &fops_rcstat);
-}
-
-static void ath_rate_remove_sta_debugfs(void *priv, void *priv_sta)
-{
-       struct ath_rate_priv *rc = priv_sta;
-       debugfs_remove(rc->debugfs_rcstats);
-}
-
-#endif /* CONFIG_MAC80211_DEBUGFS && CONFIG_ATH9K_DEBUGFS */
-
-static void *ath_rate_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)
-{
-       return hw->priv;
-}
-
-static void ath_rate_free(void *priv)
-{
-       return;
-}
-
-static void *ath_rate_alloc_sta(void *priv, struct ieee80211_sta *sta, gfp_t gfp)
-{
-       return kzalloc(sizeof(struct ath_rate_priv), gfp);
-}
-
-static void ath_rate_free_sta(void *priv, struct ieee80211_sta *sta,
-                             void *priv_sta)
-{
-       struct ath_rate_priv *rate_priv = priv_sta;
-       kfree(rate_priv);
-}
-
-static struct rate_control_ops ath_rate_ops = {
-       .module = NULL,
-       .name = "ath9k_rate_control",
-       .tx_status = ath_tx_status,
-       .get_rate = ath_get_rate,
-       .rate_init = ath_rate_init,
-       .rate_update = ath_rate_update,
-       .alloc = ath_rate_alloc,
-       .free = ath_rate_free,
-       .alloc_sta = ath_rate_alloc_sta,
-       .free_sta = ath_rate_free_sta,
-
-#if defined(CONFIG_MAC80211_DEBUGFS) && defined(CONFIG_ATH9K_DEBUGFS)
-       .add_sta_debugfs = ath_rate_add_sta_debugfs,
-       .remove_sta_debugfs = ath_rate_remove_sta_debugfs,
-#endif
-};
-
-int ath_rate_control_register(void)
-{
-       return ieee80211_rate_control_register(&ath_rate_ops);
-}
-
-void ath_rate_control_unregister(void)
-{
-       ieee80211_rate_control_unregister(&ath_rate_ops);
-}
diff --git a/drivers/net/wireless/ath/ath9k/rc.h b/drivers/net/wireless/ath/ath9k/rc.h
deleted file mode 100644 (file)
index b9a8738..0000000
+++ /dev/null
@@ -1,248 +0,0 @@
-/*
- * Copyright (c) 2004 Sam Leffler, Errno Consulting
- * Copyright (c) 2004 Video54 Technologies, Inc.
- * Copyright (c) 2008-2011 Atheros Communications Inc.
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef RC_H
-#define RC_H
-
-#include "hw.h"
-
-struct ath_softc;
-
-#define ATH_RATE_MAX     30
-#define RATE_TABLE_SIZE  72
-
-#define RC_INVALID     0x0000
-#define RC_LEGACY      0x0001
-#define RC_SS          0x0002
-#define RC_DS          0x0004
-#define RC_TS          0x0008
-#define RC_HT_20       0x0010
-#define RC_HT_40       0x0020
-
-#define RC_STREAM_MASK 0xe
-#define RC_DS_OR_LATER(f)      ((((f) & RC_STREAM_MASK) == RC_DS) || \
-                               (((f) & RC_STREAM_MASK) == (RC_DS | RC_TS)))
-#define RC_TS_ONLY(f)          (((f) & RC_STREAM_MASK) == RC_TS)
-#define RC_SS_OR_LEGACY(f)     ((f) & (RC_SS | RC_LEGACY))
-
-#define RC_HT_2040             (RC_HT_20 | RC_HT_40)
-#define RC_ALL_STREAM          (RC_SS | RC_DS | RC_TS)
-#define RC_L_SD                        (RC_LEGACY | RC_SS | RC_DS)
-#define RC_L_SDT               (RC_LEGACY | RC_SS | RC_DS | RC_TS)
-#define RC_HT_S_20             (RC_HT_20 | RC_SS)
-#define RC_HT_D_20             (RC_HT_20 | RC_DS)
-#define RC_HT_T_20             (RC_HT_20 | RC_TS)
-#define RC_HT_S_40             (RC_HT_40 | RC_SS)
-#define RC_HT_D_40             (RC_HT_40 | RC_DS)
-#define RC_HT_T_40             (RC_HT_40 | RC_TS)
-
-#define RC_HT_SD_20            (RC_HT_20 | RC_SS | RC_DS)
-#define RC_HT_DT_20            (RC_HT_20 | RC_DS | RC_TS)
-#define RC_HT_SD_40            (RC_HT_40 | RC_SS | RC_DS)
-#define RC_HT_DT_40            (RC_HT_40 | RC_DS | RC_TS)
-
-#define RC_HT_SD_2040          (RC_HT_2040 | RC_SS | RC_DS)
-#define RC_HT_SDT_2040         (RC_HT_2040 | RC_SS | RC_DS | RC_TS)
-
-#define RC_HT_SDT_20           (RC_HT_20 | RC_SS | RC_DS | RC_TS)
-#define RC_HT_SDT_40           (RC_HT_40 | RC_SS | RC_DS | RC_TS)
-
-#define RC_ALL                 (RC_LEGACY | RC_HT_2040 | RC_ALL_STREAM)
-
-enum {
-       WLAN_RC_PHY_OFDM,
-       WLAN_RC_PHY_CCK,
-       WLAN_RC_PHY_HT_20_SS,
-       WLAN_RC_PHY_HT_20_DS,
-       WLAN_RC_PHY_HT_20_TS,
-       WLAN_RC_PHY_HT_40_SS,
-       WLAN_RC_PHY_HT_40_DS,
-       WLAN_RC_PHY_HT_40_TS,
-       WLAN_RC_PHY_HT_20_SS_HGI,
-       WLAN_RC_PHY_HT_20_DS_HGI,
-       WLAN_RC_PHY_HT_20_TS_HGI,
-       WLAN_RC_PHY_HT_40_SS_HGI,
-       WLAN_RC_PHY_HT_40_DS_HGI,
-       WLAN_RC_PHY_HT_40_TS_HGI,
-       WLAN_RC_PHY_MAX
-};
-
-#define WLAN_RC_PHY_DS(_phy)   ((_phy == WLAN_RC_PHY_HT_20_DS)         \
-                               || (_phy == WLAN_RC_PHY_HT_40_DS)       \
-                               || (_phy == WLAN_RC_PHY_HT_20_DS_HGI)   \
-                               || (_phy == WLAN_RC_PHY_HT_40_DS_HGI))
-#define WLAN_RC_PHY_TS(_phy)   ((_phy == WLAN_RC_PHY_HT_20_TS)         \
-                               || (_phy == WLAN_RC_PHY_HT_40_TS)       \
-                               || (_phy == WLAN_RC_PHY_HT_20_TS_HGI)   \
-                               || (_phy == WLAN_RC_PHY_HT_40_TS_HGI))
-#define WLAN_RC_PHY_20(_phy)   ((_phy == WLAN_RC_PHY_HT_20_SS)         \
-                               || (_phy == WLAN_RC_PHY_HT_20_DS)       \
-                               || (_phy == WLAN_RC_PHY_HT_20_TS)       \
-                               || (_phy == WLAN_RC_PHY_HT_20_SS_HGI)   \
-                               || (_phy == WLAN_RC_PHY_HT_20_DS_HGI)   \
-                               || (_phy == WLAN_RC_PHY_HT_20_TS_HGI))
-#define WLAN_RC_PHY_40(_phy)   ((_phy == WLAN_RC_PHY_HT_40_SS)         \
-                               || (_phy == WLAN_RC_PHY_HT_40_DS)       \
-                               || (_phy == WLAN_RC_PHY_HT_40_TS)       \
-                               || (_phy == WLAN_RC_PHY_HT_40_SS_HGI)   \
-                               || (_phy == WLAN_RC_PHY_HT_40_DS_HGI)   \
-                               || (_phy == WLAN_RC_PHY_HT_40_TS_HGI))
-#define WLAN_RC_PHY_SGI(_phy)  ((_phy == WLAN_RC_PHY_HT_20_SS_HGI)      \
-                               || (_phy == WLAN_RC_PHY_HT_20_DS_HGI)   \
-                               || (_phy == WLAN_RC_PHY_HT_20_TS_HGI)   \
-                               || (_phy == WLAN_RC_PHY_HT_40_SS_HGI)   \
-                               || (_phy == WLAN_RC_PHY_HT_40_DS_HGI)   \
-                               || (_phy == WLAN_RC_PHY_HT_40_TS_HGI))
-
-#define WLAN_RC_PHY_HT(_phy)    (_phy >= WLAN_RC_PHY_HT_20_SS)
-
-#define WLAN_RC_CAP_MODE(capflag) (((capflag & WLAN_RC_HT_FLAG) ?      \
-       ((capflag & WLAN_RC_40_FLAG) ? RC_HT_40 : RC_HT_20) : RC_LEGACY))
-
-#define WLAN_RC_CAP_STREAM(capflag) (((capflag & WLAN_RC_TS_FLAG) ?    \
-       (RC_TS) : ((capflag & WLAN_RC_DS_FLAG) ? RC_DS : RC_SS)))
-
-/* Return TRUE if flag supports HT20 && client supports HT20 or
- * return TRUE if flag supports HT40 && client supports HT40.
- * This is used becos some rates overlap between HT20/HT40.
- */
-#define WLAN_RC_PHY_HT_VALID(flag, capflag)                    \
-       (((flag & RC_HT_20) && !(capflag & WLAN_RC_40_FLAG)) || \
-        ((flag & RC_HT_40) && (capflag & WLAN_RC_40_FLAG)))
-
-#define WLAN_RC_DS_FLAG         (0x01)
-#define WLAN_RC_TS_FLAG         (0x02)
-#define WLAN_RC_40_FLAG         (0x04)
-#define WLAN_RC_SGI_FLAG        (0x08)
-#define WLAN_RC_HT_FLAG         (0x10)
-
-/**
- * struct ath_rate_table - Rate Control table
- * @rate_cnt: total number of rates for the given wireless mode
- * @mcs_start: MCS rate index offset
- * @rate_flags: Rate Control flags
- * @phy: CCK/OFDM/HT20/HT40
- * @ratekbps: rate in Kbits per second
- * @user_ratekbps: user rate in Kbits per second
- * @ratecode: rate that goes into HW descriptors
- * @dot11rate: value that goes into supported
- *     rates info element of MLME
- * @ctrl_rate: Index of next lower basic rate, used for duration computation
- * @cw40index: Index of rates having 40MHz channel width
- * @sgi_index: Index of rates having Short Guard Interval
- * @ht_index: high throughput rates having 40MHz channel width and
- *     Short Guard Interval
- * @probe_interval: interval for rate control to probe for other rates
- * @initial_ratemax: initial ratemax value
- */
-struct ath_rate_table {
-       int rate_cnt;
-       int mcs_start;
-       struct {
-               u16 rate_flags;
-               u8 phy;
-               u32 ratekbps;
-               u32 user_ratekbps;
-               u8 ratecode;
-               u8 dot11rate;
-       } info[RATE_TABLE_SIZE];
-       u32 probe_interval;
-       u8 initial_ratemax;
-};
-
-struct ath_rateset {
-       u8 rs_nrates;
-       u8 rs_rates[ATH_RATE_MAX];
-};
-
-struct ath_rc_stats {
-       u32 success;
-       u32 retries;
-       u32 xretries;
-       u8 per;
-};
-
-/**
- * struct ath_rate_priv - Rate Control priv data
- * @state: RC state
- * @probe_rate: rate we are probing at
- * @probe_time: msec timestamp for last probe
- * @hw_maxretry_pktcnt: num of packets since we got HW max retry error
- * @max_valid_rate: maximum number of valid rate
- * @per_down_time: msec timestamp for last PER down step
- * @valid_phy_ratecnt: valid rate count
- * @rate_max_phy: phy index for the max rate
- * @per: PER for every valid rate in %
- * @probe_interval: interval for ratectrl to probe for other rates
- * @ht_cap: HT capabilities
- * @neg_rates: Negotatied rates
- * @neg_ht_rates: Negotiated HT rates
- */
-struct ath_rate_priv {
-       u8 rate_table_size;
-       u8 probe_rate;
-       u8 hw_maxretry_pktcnt;
-       u8 max_valid_rate;
-       u8 valid_rate_index[RATE_TABLE_SIZE];
-       u8 ht_cap;
-       u8 valid_phy_ratecnt[WLAN_RC_PHY_MAX];
-       u8 valid_phy_rateidx[WLAN_RC_PHY_MAX][RATE_TABLE_SIZE];
-       u8 rate_max_phy;
-       u8 per[RATE_TABLE_SIZE];
-       u32 probe_time;
-       u32 per_down_time;
-       u32 probe_interval;
-       struct ath_rateset neg_rates;
-       struct ath_rateset neg_ht_rates;
-       const struct ath_rate_table *rate_table;
-
-#if defined(CONFIG_MAC80211_DEBUGFS) && defined(CONFIG_ATH9K_DEBUGFS)
-       struct dentry *debugfs_rcstats;
-       struct ath_rc_stats rcstats[RATE_TABLE_SIZE];
-#endif
-};
-
-#if defined(CONFIG_MAC80211_DEBUGFS) && defined(CONFIG_ATH9K_DEBUGFS)
-void ath_debug_stat_rc(struct ath_rate_priv *rc, int final_rate);
-void ath_debug_stat_retries(struct ath_rate_priv *rc, int rix,
-                           int xretries, int retries, u8 per);
-#else
-static inline void ath_debug_stat_rc(struct ath_rate_priv *rc, int final_rate)
-{
-}
-static inline void ath_debug_stat_retries(struct ath_rate_priv *rc, int rix,
-                                         int xretries, int retries, u8 per)
-{
-}
-#endif
-
-#ifdef CONFIG_ATH9K_LEGACY_RATE_CONTROL
-int ath_rate_control_register(void);
-void ath_rate_control_unregister(void);
-#else
-static inline int ath_rate_control_register(void)
-{
-       return 0;
-}
-
-static inline void ath_rate_control_unregister(void)
-{
-}
-#endif
-
-#endif /* RC_H */
index a0ebdd0..076dae1 100644 (file)
@@ -755,204 +755,6 @@ static struct ath_rxbuf *ath_get_next_rx_buf(struct ath_softc *sc,
        return bf;
 }
 
-/* Assumes you've already done the endian to CPU conversion */
-static bool ath9k_rx_accept(struct ath_common *common,
-                           struct ieee80211_hdr *hdr,
-                           struct ieee80211_rx_status *rxs,
-                           struct ath_rx_status *rx_stats,
-                           bool *decrypt_error)
-{
-       struct ath_softc *sc = (struct ath_softc *) common->priv;
-       bool is_mc, is_valid_tkip, strip_mic, mic_error;
-       struct ath_hw *ah = common->ah;
-       __le16 fc;
-
-       fc = hdr->frame_control;
-
-       is_mc = !!is_multicast_ether_addr(hdr->addr1);
-       is_valid_tkip = rx_stats->rs_keyix != ATH9K_RXKEYIX_INVALID &&
-               test_bit(rx_stats->rs_keyix, common->tkip_keymap);
-       strip_mic = is_valid_tkip && ieee80211_is_data(fc) &&
-               ieee80211_has_protected(fc) &&
-               !(rx_stats->rs_status &
-               (ATH9K_RXERR_DECRYPT | ATH9K_RXERR_CRC | ATH9K_RXERR_MIC |
-                ATH9K_RXERR_KEYMISS));
-
-       /*
-        * Key miss events are only relevant for pairwise keys where the
-        * descriptor does contain a valid key index. This has been observed
-        * mostly with CCMP encryption.
-        */
-       if (rx_stats->rs_keyix == ATH9K_RXKEYIX_INVALID ||
-           !test_bit(rx_stats->rs_keyix, common->ccmp_keymap))
-               rx_stats->rs_status &= ~ATH9K_RXERR_KEYMISS;
-
-       mic_error = is_valid_tkip && !ieee80211_is_ctl(fc) &&
-               !ieee80211_has_morefrags(fc) &&
-               !(le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG) &&
-               (rx_stats->rs_status & ATH9K_RXERR_MIC);
-
-       /*
-        * The rx_stats->rs_status will not be set until the end of the
-        * chained descriptors so it can be ignored if rs_more is set. The
-        * rs_more will be false at the last element of the chained
-        * descriptors.
-        */
-       if (rx_stats->rs_status != 0) {
-               u8 status_mask;
-
-               if (rx_stats->rs_status & ATH9K_RXERR_CRC) {
-                       rxs->flag |= RX_FLAG_FAILED_FCS_CRC;
-                       mic_error = false;
-               }
-
-               if ((rx_stats->rs_status & ATH9K_RXERR_DECRYPT) ||
-                   (!is_mc && (rx_stats->rs_status & ATH9K_RXERR_KEYMISS))) {
-                       *decrypt_error = true;
-                       mic_error = false;
-               }
-
-               /*
-                * Reject error frames with the exception of
-                * decryption and MIC failures. For monitor mode,
-                * we also ignore the CRC error.
-                */
-               status_mask = ATH9K_RXERR_DECRYPT | ATH9K_RXERR_MIC |
-                             ATH9K_RXERR_KEYMISS;
-
-               if (ah->is_monitoring && (sc->rx.rxfilter & FIF_FCSFAIL))
-                       status_mask |= ATH9K_RXERR_CRC;
-
-               if (rx_stats->rs_status & ~status_mask)
-                       return false;
-       }
-
-       /*
-        * For unicast frames the MIC error bit can have false positives,
-        * so all MIC error reports need to be validated in software.
-        * False negatives are not common, so skip software verification
-        * if the hardware considers the MIC valid.
-        */
-       if (strip_mic)
-               rxs->flag |= RX_FLAG_MMIC_STRIPPED;
-       else if (is_mc && mic_error)
-               rxs->flag |= RX_FLAG_MMIC_ERROR;
-
-       return true;
-}
-
-static int ath9k_process_rate(struct ath_common *common,
-                             struct ieee80211_hw *hw,
-                             struct ath_rx_status *rx_stats,
-                             struct ieee80211_rx_status *rxs)
-{
-       struct ieee80211_supported_band *sband;
-       enum ieee80211_band band;
-       unsigned int i = 0;
-       struct ath_softc __maybe_unused *sc = common->priv;
-       struct ath_hw *ah = sc->sc_ah;
-
-       band = ah->curchan->chan->band;
-       sband = hw->wiphy->bands[band];
-
-       if (IS_CHAN_QUARTER_RATE(ah->curchan))
-               rxs->flag |= RX_FLAG_5MHZ;
-       else if (IS_CHAN_HALF_RATE(ah->curchan))
-               rxs->flag |= RX_FLAG_10MHZ;
-
-       if (rx_stats->rs_rate & 0x80) {
-               /* HT rate */
-               rxs->flag |= RX_FLAG_HT;
-               rxs->flag |= rx_stats->flag;
-               rxs->rate_idx = rx_stats->rs_rate & 0x7f;
-               return 0;
-       }
-
-       for (i = 0; i < sband->n_bitrates; i++) {
-               if (sband->bitrates[i].hw_value == rx_stats->rs_rate) {
-                       rxs->rate_idx = i;
-                       return 0;
-               }
-               if (sband->bitrates[i].hw_value_short == rx_stats->rs_rate) {
-                       rxs->flag |= RX_FLAG_SHORTPRE;
-                       rxs->rate_idx = i;
-                       return 0;
-               }
-       }
-
-       /*
-        * No valid hardware bitrate found -- we should not get here
-        * because hardware has already validated this frame as OK.
-        */
-       ath_dbg(common, ANY,
-               "unsupported hw bitrate detected 0x%02x using 1 Mbit\n",
-               rx_stats->rs_rate);
-       RX_STAT_INC(rx_rate_err);
-       return -EINVAL;
-}
-
-static void ath9k_process_rssi(struct ath_common *common,
-                              struct ieee80211_hw *hw,
-                              struct ath_rx_status *rx_stats,
-                              struct ieee80211_rx_status *rxs)
-{
-       struct ath_softc *sc = hw->priv;
-       struct ath_hw *ah = common->ah;
-       int last_rssi;
-       int rssi = rx_stats->rs_rssi;
-       int i, j;
-
-       /*
-        * RSSI is not available for subframes in an A-MPDU.
-        */
-       if (rx_stats->rs_moreaggr) {
-               rxs->flag |= RX_FLAG_NO_SIGNAL_VAL;
-               return;
-       }
-
-       /*
-        * Check if the RSSI for the last subframe in an A-MPDU
-        * or an unaggregated frame is valid.
-        */
-       if (rx_stats->rs_rssi == ATH9K_RSSI_BAD) {
-               rxs->flag |= RX_FLAG_NO_SIGNAL_VAL;
-               return;
-       }
-
-       for (i = 0, j = 0; i < ARRAY_SIZE(rx_stats->rs_rssi_ctl); i++) {
-               s8 rssi;
-
-               if (!(ah->rxchainmask & BIT(i)))
-                       continue;
-
-               rssi = rx_stats->rs_rssi_ctl[i];
-               if (rssi != ATH9K_RSSI_BAD) {
-                   rxs->chains |= BIT(j);
-                   rxs->chain_signal[j] = ah->noise + rssi;
-               }
-               j++;
-       }
-
-       /*
-        * Update Beacon RSSI, this is used by ANI.
-        */
-       if (rx_stats->is_mybeacon &&
-           ((ah->opmode == NL80211_IFTYPE_STATION) ||
-            (ah->opmode == NL80211_IFTYPE_ADHOC))) {
-               ATH_RSSI_LPF(sc->last_rssi, rx_stats->rs_rssi);
-               last_rssi = sc->last_rssi;
-
-               if (likely(last_rssi != ATH_RSSI_DUMMY_MARKER))
-                       rssi = ATH_EP_RND(last_rssi, ATH_RSSI_EP_MULTIPLIER);
-               if (rssi < 0)
-                       rssi = 0;
-
-               ah->stats.avgbrssi = rssi;
-       }
-
-       rxs->signal = ah->noise + rx_stats->rs_rssi;
-}
-
 static void ath9k_process_tsf(struct ath_rx_status *rs,
                              struct ieee80211_rx_status *rxs,
                              u64 tsf)
@@ -1051,7 +853,7 @@ static int ath9k_rx_skb_preprocess(struct ath_softc *sc,
         * everything but the rate is checked here, the rate check is done
         * separately to avoid doing two lookups for a rate for each frame.
         */
-       if (!ath9k_rx_accept(common, hdr, rx_status, rx_stats, decrypt_error)) {
+       if (!ath9k_cmn_rx_accept(common, hdr, rx_status, rx_stats, decrypt_error, sc->rx.rxfilter)) {
                ret = -EINVAL;
                goto exit;
        }
@@ -1069,12 +871,19 @@ static int ath9k_rx_skb_preprocess(struct ath_softc *sc,
                goto exit;
        }
 
-       if (ath9k_process_rate(common, hw, rx_stats, rx_status)) {
+       if (ath9k_cmn_process_rate(common, hw, rx_stats, rx_status)) {
+               /*
+                * No valid hardware bitrate found -- we should not get here
+                * because hardware has already validated this frame as OK.
+                */
+               ath_dbg(common, ANY, "unsupported hw bitrate detected 0x%02x using 1 Mbit\n",
+                       rx_stats->rs_rate);
+               RX_STAT_INC(rx_rate_err);
                ret =-EINVAL;
                goto exit;
        }
 
-       ath9k_process_rssi(common, hw, rx_stats, rx_status);
+       ath9k_cmn_process_rssi(common, hw, rx_stats, rx_status);
 
        rx_status->band = ah->curchan->chan->band;
        rx_status->freq = ah->curchan->chan->center_freq;
@@ -1092,57 +901,6 @@ exit:
        return ret;
 }
 
-static void ath9k_rx_skb_postprocess(struct ath_common *common,
-                                    struct sk_buff *skb,
-                                    struct ath_rx_status *rx_stats,
-                                    struct ieee80211_rx_status *rxs,
-                                    bool decrypt_error)
-{
-       struct ath_hw *ah = common->ah;
-       struct ieee80211_hdr *hdr;
-       int hdrlen, padpos, padsize;
-       u8 keyix;
-       __le16 fc;
-
-       /* see if any padding is done by the hw and remove it */
-       hdr = (struct ieee80211_hdr *) skb->data;
-       hdrlen = ieee80211_get_hdrlen_from_skb(skb);
-       fc = hdr->frame_control;
-       padpos = ieee80211_hdrlen(fc);
-
-       /* The MAC header is padded to have 32-bit boundary if the
-        * packet payload is non-zero. The general calculation for
-        * padsize would take into account odd header lengths:
-        * padsize = (4 - padpos % 4) % 4; However, since only
-        * even-length headers are used, padding can only be 0 or 2
-        * bytes and we can optimize this a bit. In addition, we must
-        * not try to remove padding from short control frames that do
-        * not have payload. */
-       padsize = padpos & 3;
-       if (padsize && skb->len>=padpos+padsize+FCS_LEN) {
-               memmove(skb->data + padsize, skb->data, padpos);
-               skb_pull(skb, padsize);
-       }
-
-       keyix = rx_stats->rs_keyix;
-
-       if (!(keyix == ATH9K_RXKEYIX_INVALID) && !decrypt_error &&
-           ieee80211_has_protected(fc)) {
-               rxs->flag |= RX_FLAG_DECRYPTED;
-       } else if (ieee80211_has_protected(fc)
-                  && !decrypt_error && skb->len >= hdrlen + 4) {
-               keyix = skb->data[hdrlen + 3] >> 6;
-
-               if (test_bit(keyix, common->keymap))
-                       rxs->flag |= RX_FLAG_DECRYPTED;
-       }
-       if (ah->sw_mgmt_crypto &&
-           (rxs->flag & RX_FLAG_DECRYPTED) &&
-           ieee80211_is_mgmt(fc))
-               /* Use software decrypt for management frames. */
-               rxs->flag &= ~RX_FLAG_DECRYPTED;
-}
-
 /*
  * Run the LNA combining algorithm only in these cases:
  *
@@ -1292,8 +1050,8 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
                        skb_pull(skb, ah->caps.rx_status_len);
 
                if (!rs.rs_more)
-                       ath9k_rx_skb_postprocess(common, hdr_skb, &rs,
-                                                rxs, decrypt_error);
+                       ath9k_cmn_rx_skb_postprocess(common, hdr_skb, &rs,
+                                                    rxs, decrypt_error);
 
                if (rs.rs_more) {
                        RX_STAT_INC(rx_frags);
index 0a75e2f..a650704 100644 (file)
@@ -2566,7 +2566,7 @@ void ath_tx_edma_tasklet(struct ath_softc *sc)
                        sc->beacon.tx_processed = true;
                        sc->beacon.tx_last = !(ts.ts_status & ATH9K_TXERR_MASK);
 
-                       ath9k_csa_is_finished(sc);
+                       ath9k_csa_update(sc);
                        continue;
                }
 
index ee25786..73f12f1 100644 (file)
@@ -44,6 +44,14 @@ static void wcn36xx_dxe_write_register(struct wcn36xx *wcn, int addr, int data)
        writel(data, wcn->mmio + addr);
 }
 
+#define wcn36xx_dxe_write_register_x(wcn, reg, reg_data)                \
+do {                                                                    \
+       if (wcn->chip_version == WCN36XX_CHIP_3680)                      \
+               wcn36xx_dxe_write_register(wcn, reg ## _3680, reg_data); \
+       else                                                             \
+               wcn36xx_dxe_write_register(wcn, reg ## _3660, reg_data); \
+} while (0)                                                             \
+
 static void wcn36xx_dxe_read_register(struct wcn36xx *wcn, int addr, int *data)
 {
        *data = readl(wcn->mmio + addr);
@@ -680,7 +688,7 @@ int wcn36xx_dxe_init(struct wcn36xx *wcn)
 
        /* Setting interrupt path */
        reg_data = WCN36XX_DXE_CCU_INT;
-       wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_REG_CCU_INT, reg_data);
+       wcn36xx_dxe_write_register_x(wcn, WCN36XX_DXE_REG_CCU_INT, reg_data);
 
        /***************************************/
        /* Init descriptors for TX LOW channel */
index c88562f..35ee7e9 100644 (file)
@@ -28,11 +28,11 @@ H2H_TEST_RX_TX = DMA2
 */
 
 /* DXE registers */
-#define WCN36XX_DXE_MEM_BASE                   0x03000000
 #define WCN36XX_DXE_MEM_REG                    0x202000
 
 #define WCN36XX_DXE_CCU_INT                    0xA0011
-#define WCN36XX_DXE_REG_CCU_INT                        0x200b10
+#define WCN36XX_DXE_REG_CCU_INT_3660           0x200b10
+#define WCN36XX_DXE_REG_CCU_INT_3680           0x2050dc
 
 /* TODO This must calculated properly but not hardcoded */
 #define WCN36XX_DXE_CTRL_TX_L                  0x328a44
index 3c2ef0c..a1f1127 100644 (file)
@@ -4384,11 +4384,13 @@ enum place_holder_in_cap_bitmap {
        MAX_FEATURE_SUPPORTED = 128,
 };
 
+#define WCN36XX_HAL_CAPS_SIZE 4
+
 struct wcn36xx_hal_feat_caps_msg {
 
        struct wcn36xx_hal_msg_header header;
 
-       u32 feat_caps[4];
+       u32 feat_caps[WCN36XX_HAL_CAPS_SIZE];
 } __packed;
 
 /* status codes to help debug rekey failures */
index e64a678..4ab5370 100644 (file)
@@ -17,6 +17,7 @@
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
 #include <linux/module.h>
+#include <linux/firmware.h>
 #include <linux/platform_device.h>
 #include "wcn36xx.h"
 
@@ -177,6 +178,60 @@ static inline u8 get_sta_index(struct ieee80211_vif *vif,
               sta_priv->sta_index;
 }
 
+static const char * const wcn36xx_caps_names[] = {
+       "MCC",                          /* 0 */
+       "P2P",                          /* 1 */
+       "DOT11AC",                      /* 2 */
+       "SLM_SESSIONIZATION",           /* 3 */
+       "DOT11AC_OPMODE",               /* 4 */
+       "SAP32STA",                     /* 5 */
+       "TDLS",                         /* 6 */
+       "P2P_GO_NOA_DECOUPLE_INIT_SCAN",/* 7 */
+       "WLANACTIVE_OFFLOAD",           /* 8 */
+       "BEACON_OFFLOAD",               /* 9 */
+       "SCAN_OFFLOAD",                 /* 10 */
+       "ROAM_OFFLOAD",                 /* 11 */
+       "BCN_MISS_OFFLOAD",             /* 12 */
+       "STA_POWERSAVE",                /* 13 */
+       "STA_ADVANCED_PWRSAVE",         /* 14 */
+       "AP_UAPSD",                     /* 15 */
+       "AP_DFS",                       /* 16 */
+       "BLOCKACK",                     /* 17 */
+       "PHY_ERR",                      /* 18 */
+       "BCN_FILTER",                   /* 19 */
+       "RTT",                          /* 20 */
+       "RATECTRL",                     /* 21 */
+       "WOW"                           /* 22 */
+};
+
+static const char *wcn36xx_get_cap_name(enum place_holder_in_cap_bitmap x)
+{
+       if (x >= ARRAY_SIZE(wcn36xx_caps_names))
+               return "UNKNOWN";
+       return wcn36xx_caps_names[x];
+}
+
+static void wcn36xx_feat_caps_info(struct wcn36xx *wcn)
+{
+       int i;
+
+       for (i = 0; i < MAX_FEATURE_SUPPORTED; i++) {
+               if (get_feat_caps(wcn->fw_feat_caps, i))
+                       wcn36xx_info("FW Cap %s\n", wcn36xx_get_cap_name(i));
+       }
+}
+
+static void wcn36xx_detect_chip_version(struct wcn36xx *wcn)
+{
+       if (get_feat_caps(wcn->fw_feat_caps, DOT11AC)) {
+               wcn36xx_info("Chip is 3680\n");
+               wcn->chip_version = WCN36XX_CHIP_3680;
+       } else {
+               wcn36xx_info("Chip is 3660\n");
+               wcn->chip_version = WCN36XX_CHIP_3660;
+       }
+}
+
 static int wcn36xx_start(struct ieee80211_hw *hw)
 {
        struct wcn36xx *wcn = hw->priv;
@@ -223,6 +278,16 @@ static int wcn36xx_start(struct ieee80211_hw *hw)
                goto out_free_smd_buf;
        }
 
+       if (!wcn36xx_is_fw_version(wcn, 1, 2, 2, 24)) {
+               ret = wcn36xx_smd_feature_caps_exchange(wcn);
+               if (ret)
+                       wcn36xx_warn("Exchange feature caps failed\n");
+               else
+                       wcn36xx_feat_caps_info(wcn);
+       }
+
+       wcn36xx_detect_chip_version(wcn);
+
        /* DMA channel initialization */
        ret = wcn36xx_dxe_init(wcn);
        if (ret) {
@@ -232,11 +297,6 @@ static int wcn36xx_start(struct ieee80211_hw *hw)
 
        wcn36xx_debugfs_init(wcn);
 
-       if (!wcn36xx_is_fw_version(wcn, 1, 2, 2, 24)) {
-               ret = wcn36xx_smd_feature_caps_exchange(wcn);
-               if (ret)
-                       wcn36xx_warn("Exchange feature caps failed\n");
-       }
        INIT_LIST_HEAD(&wcn->vif_list);
        return 0;
 
@@ -648,6 +708,7 @@ static void wcn36xx_bss_info_changed(struct ieee80211_hw *hw,
                            bss_conf->enable_beacon);
 
                if (bss_conf->enable_beacon) {
+                       vif_priv->dtim_period = bss_conf->dtim_period;
                        vif_priv->bss_index = 0xff;
                        wcn36xx_smd_config_bss(wcn, vif, NULL,
                                               vif->addr, false);
@@ -992,6 +1053,7 @@ static int wcn36xx_remove(struct platform_device *pdev)
        struct wcn36xx *wcn = hw->priv;
        wcn36xx_dbg(WCN36XX_DBG_MAC, "platform remove\n");
 
+       release_firmware(wcn->nv);
        mutex_destroy(&wcn->hal_mutex);
 
        ieee80211_unregister_hw(hw);
index 750626b..7bf0ef8 100644 (file)
@@ -195,9 +195,11 @@ static void wcn36xx_smd_set_sta_params(struct wcn36xx *wcn,
 static int wcn36xx_smd_send_and_wait(struct wcn36xx *wcn, size_t len)
 {
        int ret = 0;
+       unsigned long start;
        wcn36xx_dbg_dump(WCN36XX_DBG_SMD_DUMP, "HAL >>> ", wcn->hal_buf, len);
 
        init_completion(&wcn->hal_rsp_compl);
+       start = jiffies;
        ret = wcn->ctrl_ops->tx(wcn->hal_buf, len);
        if (ret) {
                wcn36xx_err("HAL TX failed\n");
@@ -205,10 +207,13 @@ static int wcn36xx_smd_send_and_wait(struct wcn36xx *wcn, size_t len)
        }
        if (wait_for_completion_timeout(&wcn->hal_rsp_compl,
                msecs_to_jiffies(HAL_MSG_TIMEOUT)) <= 0) {
-               wcn36xx_err("Timeout while waiting SMD response\n");
+               wcn36xx_err("Timeout! No SMD response in %dms\n",
+                           HAL_MSG_TIMEOUT);
                ret = -ETIME;
                goto out;
        }
+       wcn36xx_dbg(WCN36XX_DBG_SMD, "SMD command completed in %dms",
+                   jiffies_to_msecs(jiffies - start));
 out:
        return ret;
 }
@@ -246,21 +251,22 @@ static int wcn36xx_smd_rsp_status_check(void *buf, size_t len)
 
 int wcn36xx_smd_load_nv(struct wcn36xx *wcn)
 {
-       const struct firmware *nv;
        struct nv_data *nv_d;
        struct wcn36xx_hal_nv_img_download_req_msg msg_body;
        int fw_bytes_left;
        int ret;
        u16 fm_offset = 0;
 
-       ret = request_firmware(&nv, WLAN_NV_FILE, wcn->dev);
-       if (ret) {
-               wcn36xx_err("Failed to load nv file %s: %d\n",
-                             WLAN_NV_FILE, ret);
-               goto out_free_nv;
+       if (!wcn->nv) {
+               ret = request_firmware(&wcn->nv, WLAN_NV_FILE, wcn->dev);
+               if (ret) {
+                       wcn36xx_err("Failed to load nv file %s: %d\n",
+                                     WLAN_NV_FILE, ret);
+                       goto out;
+               }
        }
 
-       nv_d = (struct nv_data *)nv->data;
+       nv_d = (struct nv_data *)wcn->nv->data;
        INIT_HAL_MSG(msg_body, WCN36XX_HAL_DOWNLOAD_NV_REQ);
 
        msg_body.header.len += WCN36XX_NV_FRAGMENT_SIZE;
@@ -270,7 +276,7 @@ int wcn36xx_smd_load_nv(struct wcn36xx *wcn)
        mutex_lock(&wcn->hal_mutex);
 
        do {
-               fw_bytes_left = nv->size - fm_offset - 4;
+               fw_bytes_left = wcn->nv->size - fm_offset - 4;
                if (fw_bytes_left > WCN36XX_NV_FRAGMENT_SIZE) {
                        msg_body.last_fragment = 0;
                        msg_body.nv_img_buffer_size = WCN36XX_NV_FRAGMENT_SIZE;
@@ -308,10 +314,7 @@ int wcn36xx_smd_load_nv(struct wcn36xx *wcn)
 
 out_unlock:
        mutex_unlock(&wcn->hal_mutex);
-out_free_nv:
-       release_firmware(nv);
-
-       return ret;
+out:   return ret;
 }
 
 static int wcn36xx_smd_start_rsp(struct wcn36xx *wcn, void *buf, size_t len)
@@ -899,11 +902,12 @@ static int wcn36xx_smd_config_sta_rsp(struct wcn36xx *wcn,
 
        sta_priv->sta_index = params->sta_index;
        sta_priv->dpu_desc_index = params->dpu_index;
+       sta_priv->ucast_dpu_sign = params->uc_ucast_sig;
 
        wcn36xx_dbg(WCN36XX_DBG_HAL,
-                   "hal config sta rsp status %d sta_index %d bssid_index %d p2p %d\n",
+                   "hal config sta rsp status %d sta_index %d bssid_index %d uc_ucast_sig %d p2p %d\n",
                    params->status, params->sta_index, params->bssid_index,
-                   params->p2p);
+                   params->uc_ucast_sig, params->p2p);
 
        return 0;
 }
@@ -1118,7 +1122,7 @@ static int wcn36xx_smd_config_bss_rsp(struct wcn36xx *wcn,
                priv_vif->sta->bss_dpu_desc_index = params->dpu_desc_index;
        }
 
-       priv_vif->ucast_dpu_signature = params->ucast_dpu_signature;
+       priv_vif->self_ucast_dpu_sign = params->ucast_dpu_signature;
 
        return 0;
 }
@@ -1637,12 +1641,12 @@ int wcn36xx_smd_keep_alive_req(struct wcn36xx *wcn,
 
        ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len);
        if (ret) {
-               wcn36xx_err("Sending hal_exit_bmps failed\n");
+               wcn36xx_err("Sending hal_keep_alive failed\n");
                goto out;
        }
        ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len);
        if (ret) {
-               wcn36xx_err("hal_exit_bmps response failed err=%d\n", ret);
+               wcn36xx_err("hal_keep_alive response failed err=%d\n", ret);
                goto out;
        }
 out:
@@ -1682,8 +1686,7 @@ out:
        return ret;
 }
 
-static inline void set_feat_caps(u32 *bitmap,
-                                enum place_holder_in_cap_bitmap cap)
+void set_feat_caps(u32 *bitmap, enum place_holder_in_cap_bitmap cap)
 {
        int arr_idx, bit_idx;
 
@@ -1697,8 +1700,7 @@ static inline void set_feat_caps(u32 *bitmap,
        bitmap[arr_idx] |= (1 << bit_idx);
 }
 
-static inline int get_feat_caps(u32 *bitmap,
-                               enum place_holder_in_cap_bitmap cap)
+int get_feat_caps(u32 *bitmap, enum place_holder_in_cap_bitmap cap)
 {
        int arr_idx, bit_idx;
        int ret = 0;
@@ -1714,8 +1716,7 @@ static inline int get_feat_caps(u32 *bitmap,
        return ret;
 }
 
-static inline void clear_feat_caps(u32 *bitmap,
-                               enum place_holder_in_cap_bitmap cap)
+void clear_feat_caps(u32 *bitmap, enum place_holder_in_cap_bitmap cap)
 {
        int arr_idx, bit_idx;
 
@@ -1731,8 +1732,8 @@ static inline void clear_feat_caps(u32 *bitmap,
 
 int wcn36xx_smd_feature_caps_exchange(struct wcn36xx *wcn)
 {
-       struct wcn36xx_hal_feat_caps_msg msg_body;
-       int ret = 0;
+       struct wcn36xx_hal_feat_caps_msg msg_body, *rsp;
+       int ret = 0, i;
 
        mutex_lock(&wcn->hal_mutex);
        INIT_HAL_MSG(msg_body, WCN36XX_HAL_FEATURE_CAPS_EXCHANGE_REQ);
@@ -1746,12 +1747,15 @@ int wcn36xx_smd_feature_caps_exchange(struct wcn36xx *wcn)
                wcn36xx_err("Sending hal_feature_caps_exchange failed\n");
                goto out;
        }
-       ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len);
-       if (ret) {
-               wcn36xx_err("hal_feature_caps_exchange response failed err=%d\n",
-                           ret);
+       if (wcn->hal_rsp_len != sizeof(*rsp)) {
+               wcn36xx_err("Invalid hal_feature_caps_exchange response");
                goto out;
        }
+
+       rsp = (struct wcn36xx_hal_feat_caps_msg *) wcn->hal_buf;
+
+       for (i = 0; i < WCN36XX_HAL_CAPS_SIZE; i++)
+               wcn->fw_feat_caps[i] = rsp->feat_caps[i];
 out:
        mutex_unlock(&wcn->hal_mutex);
        return ret;
index e7c3901..008d034 100644 (file)
@@ -24,7 +24,7 @@
 
 #define WCN36XX_HAL_BUF_SIZE                           4096
 
-#define HAL_MSG_TIMEOUT 200
+#define HAL_MSG_TIMEOUT 500
 #define WCN36XX_SMSM_WLAN_TX_ENABLE                    0x00000400
 #define WCN36XX_SMSM_WLAN_TX_RINGS_EMPTY               0x00000200
 /* The PNO version info be contained in the rsp msg */
@@ -112,6 +112,9 @@ int wcn36xx_smd_keep_alive_req(struct wcn36xx *wcn,
 int wcn36xx_smd_dump_cmd_req(struct wcn36xx *wcn, u32 arg1, u32 arg2,
                             u32 arg3, u32 arg4, u32 arg5);
 int wcn36xx_smd_feature_caps_exchange(struct wcn36xx *wcn);
+void set_feat_caps(u32 *bitmap, enum place_holder_in_cap_bitmap cap);
+int get_feat_caps(u32 *bitmap, enum place_holder_in_cap_bitmap cap);
+void clear_feat_caps(u32 *bitmap, enum place_holder_in_cap_bitmap cap);
 
 int wcn36xx_smd_add_ba_session(struct wcn36xx *wcn,
                struct ieee80211_sta *sta,
index b2b60e3..32bb26a 100644 (file)
@@ -57,8 +57,7 @@ int wcn36xx_rx_skb(struct wcn36xx *wcn, struct sk_buff *skb)
                       RX_FLAG_MMIC_STRIPPED |
                       RX_FLAG_DECRYPTED;
 
-       wcn36xx_dbg(WCN36XX_DBG_RX, "status.flags=%x status->vendor_radiotap_len=%x\n",
-                   status.flag,  status.vendor_radiotap_len);
+       wcn36xx_dbg(WCN36XX_DBG_RX, "status.flags=%x\n", status.flag);
 
        memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status));
 
@@ -132,6 +131,7 @@ static void wcn36xx_set_tx_data(struct wcn36xx_tx_bd *bd,
                                   struct ieee80211_vif,
                                   drv_priv);
 
+               bd->dpu_sign = sta_priv->ucast_dpu_sign;
                if (vif->type == NL80211_IFTYPE_STATION) {
                        bd->sta_index = sta_priv->bss_sta_index;
                        bd->dpu_desc_idx = sta_priv->bss_dpu_desc_index;
@@ -145,10 +145,9 @@ static void wcn36xx_set_tx_data(struct wcn36xx_tx_bd *bd,
                __vif_priv = get_vif_by_addr(wcn, hdr->addr2);
                bd->sta_index = __vif_priv->self_sta_index;
                bd->dpu_desc_idx = __vif_priv->self_dpu_desc_index;
+               bd->dpu_sign = __vif_priv->self_ucast_dpu_sign;
        }
 
-       bd->dpu_sign = __vif_priv->ucast_dpu_signature;
-
        if (ieee80211_is_nullfunc(hdr->frame_control) ||
           (sta_priv && !sta_priv->is_data_encrypted))
                bd->dpu_ne = 1;
index 8fa5cba..f0fb81d 100644 (file)
@@ -125,10 +125,10 @@ struct wcn36xx_vif {
        enum wcn36xx_power_state pw_state;
 
        u8 bss_index;
-       u8 ucast_dpu_signature;
        /* Returned from WCN36XX_HAL_ADD_STA_SELF_RSP */
        u8 self_sta_index;
        u8 self_dpu_desc_index;
+       u8 self_ucast_dpu_sign;
 };
 
 /**
@@ -159,6 +159,7 @@ struct wcn36xx_sta {
        u16 tid;
        u8 sta_index;
        u8 dpu_desc_index;
+       u8 ucast_dpu_sign;
        u8 bss_sta_index;
        u8 bss_dpu_desc_index;
        bool is_data_encrypted;
@@ -171,10 +172,14 @@ struct wcn36xx {
        struct device           *dev;
        struct list_head        vif_list;
 
+       const struct firmware   *nv;
+
        u8                      fw_revision;
        u8                      fw_version;
        u8                      fw_minor;
        u8                      fw_major;
+       u32                     fw_feat_caps[WCN36XX_HAL_CAPS_SIZE];
+       u32                     chip_version;
 
        /* extra byte for the NULL termination */
        u8                      crm_version[WCN36XX_HAL_VERSION_LENGTH + 1];
@@ -222,6 +227,9 @@ struct wcn36xx {
 
 };
 
+#define WCN36XX_CHIP_3660      0
+#define WCN36XX_CHIP_3680      1
+
 static inline bool wcn36xx_is_fw_version(struct wcn36xx *wcn,
                                         u8 major,
                                         u8 minor,
index abac25e..f476fc3 100644 (file)
@@ -58,41 +58,6 @@ enum b43_verbosity {
 #endif
 };
 
-
-/* Lightweight function to convert a frequency (in Mhz) to a channel number. */
-static inline u8 b43_freq_to_channel_5ghz(int freq)
-{
-       return ((freq - 5000) / 5);
-}
-static inline u8 b43_freq_to_channel_2ghz(int freq)
-{
-       u8 channel;
-
-       if (freq == 2484)
-               channel = 14;
-       else
-               channel = (freq - 2407) / 5;
-
-       return channel;
-}
-
-/* Lightweight function to convert a channel number to a frequency (in Mhz). */
-static inline int b43_channel_to_freq_5ghz(u8 channel)
-{
-       return (5000 + (5 * channel));
-}
-static inline int b43_channel_to_freq_2ghz(u8 channel)
-{
-       int freq;
-
-       if (channel == 14)
-               freq = 2484;
-       else
-               freq = 2407 + (5 * channel);
-
-       return freq;
-}
-
 static inline int b43_is_cck_rate(int rate)
 {
        return (rate == B43_CCK_RATE_1MB ||
index 50e5ddb..218a0f3 100644 (file)
@@ -806,7 +806,8 @@ void b43_rx(struct b43_wldev *dev, struct sk_buff *skb, const void *_rxhdr)
                B43_WARN_ON(1);
                /* FIXME: We don't really know which value the "chanid" contains.
                 *        So the following assignment might be wrong. */
-               status.freq = b43_channel_to_freq_5ghz(chanid);
+               status.freq =
+                       ieee80211_channel_to_frequency(chanid, status.band);
                break;
        case B43_PHYTYPE_G:
                status.band = IEEE80211_BAND_2GHZ;
@@ -819,13 +820,12 @@ void b43_rx(struct b43_wldev *dev, struct sk_buff *skb, const void *_rxhdr)
        case B43_PHYTYPE_HT:
                /* chanid is the SHM channel cookie. Which is the plain
                 * channel number in b43. */
-               if (chanstat & B43_RX_CHAN_5GHZ) {
+               if (chanstat & B43_RX_CHAN_5GHZ)
                        status.band = IEEE80211_BAND_5GHZ;
-                       status.freq = b43_channel_to_freq_5ghz(chanid);
-               } else {
+               else
                        status.band = IEEE80211_BAND_2GHZ;
-                       status.freq = b43_channel_to_freq_2ghz(chanid);
-               }
+               status.freq =
+                       ieee80211_channel_to_frequency(chanid, status.band);
                break;
        default:
                B43_WARN_ON(1);
index 57cddee..1d2ceac 100644 (file)
@@ -24,6 +24,7 @@ ccflags-y += -D__CHECK_ENDIAN__
 obj-$(CONFIG_BRCMFMAC) += brcmfmac.o
 brcmfmac-objs += \
                wl_cfg80211.o \
+               chip.o \
                fwil.o \
                fweh.o \
                fwsignal.o \
@@ -36,8 +37,7 @@ brcmfmac-objs += \
                btcoex.o
 brcmfmac-$(CONFIG_BRCMFMAC_SDIO) += \
                dhd_sdio.o \
-               bcmsdh.o \
-               sdio_chip.o
+               bcmsdh.o
 brcmfmac-$(CONFIG_BRCMFMAC_USB) += \
                usb.o
 brcmfmac-$(CONFIG_BRCMDBG) += \
index fa35b23..07e7d25 100644 (file)
@@ -43,7 +43,6 @@
 #include "dhd_bus.h"
 #include "dhd_dbg.h"
 #include "sdio_host.h"
-#include "sdio_chip.h"
 
 #define SDIOH_API_ACCESS_RETRY_LIMIT   2
 
@@ -827,7 +826,7 @@ brcmf_sdiod_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address,
                }
                if (!write)
                        memcpy(data, pkt->data, dsize);
-               skb_trim(pkt, dsize);
+               skb_trim(pkt, 0);
 
                /* Adjust for next transfer (if any) */
                size -= dsize;
@@ -1115,11 +1114,12 @@ static struct sdio_driver brcmf_sdmmc_driver = {
        .remove = brcmf_ops_sdio_remove,
        .name = BRCMFMAC_SDIO_PDATA_NAME,
        .id_table = brcmf_sdmmc_ids,
-#ifdef CONFIG_PM_SLEEP
        .drv = {
+               .owner = THIS_MODULE,
+#ifdef CONFIG_PM_SLEEP
                .pm = &brcmf_sdio_pm_ops,
-       },
 #endif /* CONFIG_PM_SLEEP */
+       },
 };
 
 static int brcmf_sdio_pd_probe(struct platform_device *pdev)
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/brcm80211/brcmfmac/chip.c
new file mode 100644 (file)
index 0000000..a07b95e
--- /dev/null
@@ -0,0 +1,1029 @@
+/*
+ * 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/kernel.h>
+#include <linux/delay.h>
+#include <linux/list.h>
+#include <linux/ssb/ssb_regs.h>
+#include <linux/bcma/bcma.h>
+#include <linux/bcma/bcma_regs.h>
+
+#include <defs.h>
+#include <soc.h>
+#include <brcm_hw_ids.h>
+#include <brcmu_utils.h>
+#include <chipcommon.h>
+#include "dhd_dbg.h"
+#include "chip.h"
+
+/* SOC Interconnect types (aka chip types) */
+#define SOCI_SB                0
+#define SOCI_AI                1
+
+/* PL-368 DMP definitions */
+#define DMP_DESC_TYPE_MSK      0x0000000F
+#define  DMP_DESC_EMPTY                0x00000000
+#define  DMP_DESC_VALID                0x00000001
+#define  DMP_DESC_COMPONENT    0x00000001
+#define  DMP_DESC_MASTER_PORT  0x00000003
+#define  DMP_DESC_ADDRESS      0x00000005
+#define  DMP_DESC_ADDRSIZE_GT32        0x00000008
+#define  DMP_DESC_EOT          0x0000000F
+
+#define DMP_COMP_DESIGNER      0xFFF00000
+#define DMP_COMP_DESIGNER_S    20
+#define DMP_COMP_PARTNUM       0x000FFF00
+#define DMP_COMP_PARTNUM_S     8
+#define DMP_COMP_CLASS         0x000000F0
+#define DMP_COMP_CLASS_S       4
+#define DMP_COMP_REVISION      0xFF000000
+#define DMP_COMP_REVISION_S    24
+#define DMP_COMP_NUM_SWRAP     0x00F80000
+#define DMP_COMP_NUM_SWRAP_S   19
+#define DMP_COMP_NUM_MWRAP     0x0007C000
+#define DMP_COMP_NUM_MWRAP_S   14
+#define DMP_COMP_NUM_SPORT     0x00003E00
+#define DMP_COMP_NUM_SPORT_S   9
+#define DMP_COMP_NUM_MPORT     0x000001F0
+#define DMP_COMP_NUM_MPORT_S   4
+
+#define DMP_MASTER_PORT_UID    0x0000FF00
+#define DMP_MASTER_PORT_UID_S  8
+#define DMP_MASTER_PORT_NUM    0x000000F0
+#define DMP_MASTER_PORT_NUM_S  4
+
+#define DMP_SLAVE_ADDR_BASE    0xFFFFF000
+#define DMP_SLAVE_ADDR_BASE_S  12
+#define DMP_SLAVE_PORT_NUM     0x00000F00
+#define DMP_SLAVE_PORT_NUM_S   8
+#define DMP_SLAVE_TYPE         0x000000C0
+#define DMP_SLAVE_TYPE_S       6
+#define  DMP_SLAVE_TYPE_SLAVE  0
+#define  DMP_SLAVE_TYPE_BRIDGE 1
+#define  DMP_SLAVE_TYPE_SWRAP  2
+#define  DMP_SLAVE_TYPE_MWRAP  3
+#define DMP_SLAVE_SIZE_TYPE    0x00000030
+#define DMP_SLAVE_SIZE_TYPE_S  4
+#define  DMP_SLAVE_SIZE_4K     0
+#define  DMP_SLAVE_SIZE_8K     1
+#define  DMP_SLAVE_SIZE_16K    2
+#define  DMP_SLAVE_SIZE_DESC   3
+
+/* EROM CompIdentB */
+#define CIB_REV_MASK           0xff000000
+#define CIB_REV_SHIFT          24
+
+/* ARM CR4 core specific control flag bits */
+#define ARMCR4_BCMA_IOCTL_CPUHALT      0x0020
+
+/* D11 core specific control flag bits */
+#define D11_BCMA_IOCTL_PHYCLOCKEN      0x0004
+#define D11_BCMA_IOCTL_PHYRESET                0x0008
+
+/* chip core base & ramsize */
+/* bcm4329 */
+/* SDIO device core, ID 0x829 */
+#define BCM4329_CORE_BUS_BASE          0x18011000
+/* internal memory core, ID 0x80e */
+#define BCM4329_CORE_SOCRAM_BASE       0x18003000
+/* ARM Cortex M3 core, ID 0x82a */
+#define BCM4329_CORE_ARM_BASE          0x18002000
+#define BCM4329_RAMSIZE                        0x48000
+
+/* bcm43143 */
+/* SDIO device core */
+#define BCM43143_CORE_BUS_BASE         0x18002000
+/* internal memory core */
+#define BCM43143_CORE_SOCRAM_BASE      0x18004000
+/* ARM Cortex M3 core, ID 0x82a */
+#define BCM43143_CORE_ARM_BASE         0x18003000
+#define BCM43143_RAMSIZE               0x70000
+
+#define CORE_SB(base, field) \
+               (base + SBCONFIGOFF + offsetof(struct sbconfig, field))
+#define        SBCOREREV(sbidh) \
+       ((((sbidh) & SSB_IDHIGH_RCHI) >> SSB_IDHIGH_RCHI_SHIFT) | \
+         ((sbidh) & SSB_IDHIGH_RCLO))
+
+struct sbconfig {
+       u32 PAD[2];
+       u32 sbipsflag;  /* initiator port ocp slave flag */
+       u32 PAD[3];
+       u32 sbtpsflag;  /* target port ocp slave flag */
+       u32 PAD[11];
+       u32 sbtmerrloga;        /* (sonics >= 2.3) */
+       u32 PAD;
+       u32 sbtmerrlog; /* (sonics >= 2.3) */
+       u32 PAD[3];
+       u32 sbadmatch3; /* address match3 */
+       u32 PAD;
+       u32 sbadmatch2; /* address match2 */
+       u32 PAD;
+       u32 sbadmatch1; /* address match1 */
+       u32 PAD[7];
+       u32 sbimstate;  /* initiator agent state */
+       u32 sbintvec;   /* interrupt mask */
+       u32 sbtmstatelow;       /* target state */
+       u32 sbtmstatehigh;      /* target state */
+       u32 sbbwa0;             /* bandwidth allocation table0 */
+       u32 PAD;
+       u32 sbimconfiglow;      /* initiator configuration */
+       u32 sbimconfighigh;     /* initiator configuration */
+       u32 sbadmatch0; /* address match0 */
+       u32 PAD;
+       u32 sbtmconfiglow;      /* target configuration */
+       u32 sbtmconfighigh;     /* target configuration */
+       u32 sbbconfig;  /* broadcast configuration */
+       u32 PAD;
+       u32 sbbstate;   /* broadcast state */
+       u32 PAD[3];
+       u32 sbactcnfg;  /* activate configuration */
+       u32 PAD[3];
+       u32 sbflagst;   /* current sbflags */
+       u32 PAD[3];
+       u32 sbidlow;            /* identification */
+       u32 sbidhigh;   /* identification */
+};
+
+struct brcmf_core_priv {
+       struct brcmf_core pub;
+       u32 wrapbase;
+       struct list_head list;
+       struct brcmf_chip_priv *chip;
+};
+
+/* ARM CR4 core specific control flag bits */
+#define ARMCR4_BCMA_IOCTL_CPUHALT      0x0020
+
+/* D11 core specific control flag bits */
+#define D11_BCMA_IOCTL_PHYCLOCKEN      0x0004
+#define D11_BCMA_IOCTL_PHYRESET                0x0008
+
+struct brcmf_chip_priv {
+       struct brcmf_chip pub;
+       const struct brcmf_buscore_ops *ops;
+       void *ctx;
+       /* assured first core is chipcommon, second core is buscore */
+       struct list_head cores;
+       u16 num_cores;
+
+       bool (*iscoreup)(struct brcmf_core_priv *core);
+       void (*coredisable)(struct brcmf_core_priv *core, u32 prereset,
+                           u32 reset);
+       void (*resetcore)(struct brcmf_core_priv *core, u32 prereset, u32 reset,
+                         u32 postreset);
+};
+
+static void brcmf_chip_sb_corerev(struct brcmf_chip_priv *ci,
+                                 struct brcmf_core *core)
+{
+       u32 regdata;
+
+       regdata = ci->ops->read32(ci->ctx, CORE_SB(core->base, sbidhigh));
+       core->rev = SBCOREREV(regdata);
+}
+
+static bool brcmf_chip_sb_iscoreup(struct brcmf_core_priv *core)
+{
+       struct brcmf_chip_priv *ci;
+       u32 regdata;
+       u32 address;
+
+       ci = core->chip;
+       address = CORE_SB(core->pub.base, sbtmstatelow);
+       regdata = ci->ops->read32(ci->ctx, address);
+       regdata &= (SSB_TMSLOW_RESET | SSB_TMSLOW_REJECT |
+                   SSB_IMSTATE_REJECT | SSB_TMSLOW_CLOCK);
+       return SSB_TMSLOW_CLOCK == regdata;
+}
+
+static bool brcmf_chip_ai_iscoreup(struct brcmf_core_priv *core)
+{
+       struct brcmf_chip_priv *ci;
+       u32 regdata;
+       bool ret;
+
+       ci = core->chip;
+       regdata = ci->ops->read32(ci->ctx, core->wrapbase + BCMA_IOCTL);
+       ret = (regdata & (BCMA_IOCTL_FGC | BCMA_IOCTL_CLK)) == BCMA_IOCTL_CLK;
+
+       regdata = ci->ops->read32(ci->ctx, core->wrapbase + BCMA_RESET_CTL);
+       ret = ret && ((regdata & BCMA_RESET_CTL_RESET) == 0);
+
+       return ret;
+}
+
+static void brcmf_chip_sb_coredisable(struct brcmf_core_priv *core,
+                                     u32 prereset, u32 reset)
+{
+       struct brcmf_chip_priv *ci;
+       u32 val, base;
+
+       ci = core->chip;
+       base = core->pub.base;
+       val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow));
+       if (val & SSB_TMSLOW_RESET)
+               return;
+
+       val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow));
+       if ((val & SSB_TMSLOW_CLOCK) != 0) {
+               /*
+                * set target reject and spin until busy is clear
+                * (preserve core-specific bits)
+                */
+               val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow));
+               ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow),
+                                        val | SSB_TMSLOW_REJECT);
+
+               val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow));
+               udelay(1);
+               SPINWAIT((ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatehigh))
+                         & SSB_TMSHIGH_BUSY), 100000);
+
+               val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatehigh));
+               if (val & SSB_TMSHIGH_BUSY)
+                       brcmf_err("core state still busy\n");
+
+               val = ci->ops->read32(ci->ctx, CORE_SB(base, sbidlow));
+               if (val & SSB_IDLOW_INITIATOR) {
+                       val = ci->ops->read32(ci->ctx,
+                                             CORE_SB(base, sbimstate));
+                       val |= SSB_IMSTATE_REJECT;
+                       ci->ops->write32(ci->ctx,
+                                        CORE_SB(base, sbimstate), val);
+                       val = ci->ops->read32(ci->ctx,
+                                             CORE_SB(base, sbimstate));
+                       udelay(1);
+                       SPINWAIT((ci->ops->read32(ci->ctx,
+                                                 CORE_SB(base, sbimstate)) &
+                                 SSB_IMSTATE_BUSY), 100000);
+               }
+
+               /* set reset and reject while enabling the clocks */
+               val = SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK |
+                     SSB_TMSLOW_REJECT | SSB_TMSLOW_RESET;
+               ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow), val);
+               val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow));
+               udelay(10);
+
+               /* clear the initiator reject bit */
+               val = ci->ops->read32(ci->ctx, CORE_SB(base, sbidlow));
+               if (val & SSB_IDLOW_INITIATOR) {
+                       val = ci->ops->read32(ci->ctx,
+                                             CORE_SB(base, sbimstate));
+                       val &= ~SSB_IMSTATE_REJECT;
+                       ci->ops->write32(ci->ctx,
+                                        CORE_SB(base, sbimstate), val);
+               }
+       }
+
+       /* leave reset and reject asserted */
+       ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow),
+                        (SSB_TMSLOW_REJECT | SSB_TMSLOW_RESET));
+       udelay(1);
+}
+
+static void brcmf_chip_ai_coredisable(struct brcmf_core_priv *core,
+                                     u32 prereset, u32 reset)
+{
+       struct brcmf_chip_priv *ci;
+       u32 regdata;
+
+       ci = core->chip;
+
+       /* if core is already in reset, just return */
+       regdata = ci->ops->read32(ci->ctx, core->wrapbase + BCMA_RESET_CTL);
+       if ((regdata & BCMA_RESET_CTL_RESET) != 0)
+               return;
+
+       /* configure reset */
+       ci->ops->write32(ci->ctx, core->wrapbase + BCMA_IOCTL,
+                        prereset | BCMA_IOCTL_FGC | BCMA_IOCTL_CLK);
+       ci->ops->read32(ci->ctx, core->wrapbase + BCMA_IOCTL);
+
+       /* put in reset */
+       ci->ops->write32(ci->ctx, core->wrapbase + BCMA_RESET_CTL,
+                        BCMA_RESET_CTL_RESET);
+       usleep_range(10, 20);
+
+       /* wait till reset is 1 */
+       SPINWAIT(ci->ops->read32(ci->ctx, core->wrapbase + BCMA_RESET_CTL) !=
+                BCMA_RESET_CTL_RESET, 300);
+
+       /* in-reset configure */
+       ci->ops->write32(ci->ctx, core->wrapbase + BCMA_IOCTL,
+                        reset | BCMA_IOCTL_FGC | BCMA_IOCTL_CLK);
+       ci->ops->read32(ci->ctx, core->wrapbase + BCMA_IOCTL);
+}
+
+static void brcmf_chip_sb_resetcore(struct brcmf_core_priv *core, u32 prereset,
+                                   u32 reset, u32 postreset)
+{
+       struct brcmf_chip_priv *ci;
+       u32 regdata;
+       u32 base;
+
+       ci = core->chip;
+       base = core->pub.base;
+       /*
+        * Must do the disable sequence first to work for
+        * arbitrary current core state.
+        */
+       brcmf_chip_sb_coredisable(core, 0, 0);
+
+       /*
+        * Now do the initialization sequence.
+        * set reset while enabling the clock and
+        * forcing them on throughout the core
+        */
+       ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow),
+                        SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK |
+                        SSB_TMSLOW_RESET);
+       regdata = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow));
+       udelay(1);
+
+       /* clear any serror */
+       regdata = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatehigh));
+       if (regdata & SSB_TMSHIGH_SERR)
+               ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatehigh), 0);
+
+       regdata = ci->ops->read32(ci->ctx, CORE_SB(base, sbimstate));
+       if (regdata & (SSB_IMSTATE_IBE | SSB_IMSTATE_TO)) {
+               regdata &= ~(SSB_IMSTATE_IBE | SSB_IMSTATE_TO);
+               ci->ops->write32(ci->ctx, CORE_SB(base, sbimstate), regdata);
+       }
+
+       /* clear reset and allow it to propagate throughout the core */
+       ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow),
+                        SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK);
+       regdata = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow));
+       udelay(1);
+
+       /* leave clock enabled */
+       ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow),
+                        SSB_TMSLOW_CLOCK);
+       regdata = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow));
+       udelay(1);
+}
+
+static void brcmf_chip_ai_resetcore(struct brcmf_core_priv *core, u32 prereset,
+                                   u32 reset, u32 postreset)
+{
+       struct brcmf_chip_priv *ci;
+       int count;
+
+       ci = core->chip;
+
+       /* must disable first to work for arbitrary current core state */
+       brcmf_chip_ai_coredisable(core, prereset, reset);
+
+       count = 0;
+       while (ci->ops->read32(ci->ctx, core->wrapbase + BCMA_RESET_CTL) &
+              BCMA_RESET_CTL_RESET) {
+               ci->ops->write32(ci->ctx, core->wrapbase + BCMA_RESET_CTL, 0);
+               count++;
+               if (count > 50)
+                       break;
+               usleep_range(40, 60);
+       }
+
+       ci->ops->write32(ci->ctx, core->wrapbase + BCMA_IOCTL,
+                        postreset | BCMA_IOCTL_CLK);
+       ci->ops->read32(ci->ctx, core->wrapbase + BCMA_IOCTL);
+}
+
+static char *brcmf_chip_name(uint chipid, char *buf, uint len)
+{
+       const char *fmt;
+
+       fmt = ((chipid > 0xa000) || (chipid < 0x4000)) ? "%d" : "%x";
+       snprintf(buf, len, fmt, chipid);
+       return buf;
+}
+
+static struct brcmf_core *brcmf_chip_add_core(struct brcmf_chip_priv *ci,
+                                             u16 coreid, u32 base,
+                                             u32 wrapbase)
+{
+       struct brcmf_core_priv *core;
+
+       core = kzalloc(sizeof(*core), GFP_KERNEL);
+       if (!core)
+               return ERR_PTR(-ENOMEM);
+
+       core->pub.id = coreid;
+       core->pub.base = base;
+       core->chip = ci;
+       core->wrapbase = wrapbase;
+
+       list_add_tail(&core->list, &ci->cores);
+       return &core->pub;
+}
+
+#ifdef DEBUG
+/* safety check for chipinfo */
+static int brcmf_chip_cores_check(struct brcmf_chip_priv *ci)
+{
+       struct brcmf_core_priv *core;
+       bool need_socram = false;
+       bool has_socram = false;
+       int idx = 1;
+
+       list_for_each_entry(core, &ci->cores, list) {
+               brcmf_dbg(INFO, " [%-2d] core 0x%x:%-2d base 0x%08x wrap 0x%08x\n",
+                         idx++, core->pub.id, core->pub.rev, core->pub.base,
+                         core->wrapbase);
+
+               switch (core->pub.id) {
+               case BCMA_CORE_ARM_CM3:
+                       need_socram = true;
+                       break;
+               case BCMA_CORE_INTERNAL_MEM:
+                       has_socram = true;
+                       break;
+               case BCMA_CORE_ARM_CR4:
+                       if (ci->pub.rambase == 0) {
+                               brcmf_err("RAM base not provided with ARM CR4 core\n");
+                               return -ENOMEM;
+                       }
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       /* check RAM core presence for ARM CM3 core */
+       if (need_socram && !has_socram) {
+               brcmf_err("RAM core not provided with ARM CM3 core\n");
+               return -ENODEV;
+       }
+       return 0;
+}
+#else  /* DEBUG */
+static inline int brcmf_chip_cores_check(struct brcmf_chip_priv *ci)
+{
+       return 0;
+}
+#endif
+
+static void brcmf_chip_get_raminfo(struct brcmf_chip_priv *ci)
+{
+       switch (ci->pub.chip) {
+       case BCM4329_CHIP_ID:
+               ci->pub.ramsize = BCM4329_RAMSIZE;
+               break;
+       case BCM43143_CHIP_ID:
+               ci->pub.ramsize = BCM43143_RAMSIZE;
+               break;
+       case BCM43241_CHIP_ID:
+               ci->pub.ramsize = 0x90000;
+               break;
+       case BCM4330_CHIP_ID:
+               ci->pub.ramsize = 0x48000;
+               break;
+       case BCM4334_CHIP_ID:
+               ci->pub.ramsize = 0x80000;
+               break;
+       case BCM4335_CHIP_ID:
+               ci->pub.ramsize = 0xc0000;
+               ci->pub.rambase = 0x180000;
+               break;
+       case BCM43362_CHIP_ID:
+               ci->pub.ramsize = 0x3c000;
+               break;
+       case BCM4339_CHIP_ID:
+               ci->pub.ramsize = 0xc0000;
+               ci->pub.rambase = 0x180000;
+               break;
+       default:
+               brcmf_err("unknown chip: %s\n", ci->pub.name);
+               break;
+       }
+}
+
+static u32 brcmf_chip_dmp_get_desc(struct brcmf_chip_priv *ci, u32 *eromaddr,
+                                  u8 *type)
+{
+       u32 val;
+
+       /* read next descriptor */
+       val = ci->ops->read32(ci->ctx, *eromaddr);
+       *eromaddr += 4;
+
+       if (!type)
+               return val;
+
+       /* determine descriptor type */
+       *type = (val & DMP_DESC_TYPE_MSK);
+       if ((*type & ~DMP_DESC_ADDRSIZE_GT32) == DMP_DESC_ADDRESS)
+               *type = DMP_DESC_ADDRESS;
+
+       return val;
+}
+
+static int brcmf_chip_dmp_get_regaddr(struct brcmf_chip_priv *ci, u32 *eromaddr,
+                                     u32 *regbase, u32 *wrapbase)
+{
+       u8 desc;
+       u32 val;
+       u8 mpnum = 0;
+       u8 stype, sztype, wraptype;
+
+       *regbase = 0;
+       *wrapbase = 0;
+
+       val = brcmf_chip_dmp_get_desc(ci, eromaddr, &desc);
+       if (desc == DMP_DESC_MASTER_PORT) {
+               mpnum = (val & DMP_MASTER_PORT_NUM) >> DMP_MASTER_PORT_NUM_S;
+               wraptype = DMP_SLAVE_TYPE_MWRAP;
+       } else if (desc == DMP_DESC_ADDRESS) {
+               /* revert erom address */
+               *eromaddr -= 4;
+               wraptype = DMP_SLAVE_TYPE_SWRAP;
+       } else {
+               *eromaddr -= 4;
+               return -EILSEQ;
+       }
+
+       do {
+               /* locate address descriptor */
+               do {
+                       val = brcmf_chip_dmp_get_desc(ci, eromaddr, &desc);
+                       /* unexpected table end */
+                       if (desc == DMP_DESC_EOT) {
+                               *eromaddr -= 4;
+                               return -EFAULT;
+                       }
+               } while (desc != DMP_DESC_ADDRESS);
+
+               /* skip upper 32-bit address descriptor */
+               if (val & DMP_DESC_ADDRSIZE_GT32)
+                       brcmf_chip_dmp_get_desc(ci, eromaddr, NULL);
+
+               sztype = (val & DMP_SLAVE_SIZE_TYPE) >> DMP_SLAVE_SIZE_TYPE_S;
+
+               /* next size descriptor can be skipped */
+               if (sztype == DMP_SLAVE_SIZE_DESC) {
+                       val = brcmf_chip_dmp_get_desc(ci, eromaddr, NULL);
+                       /* skip upper size descriptor if present */
+                       if (val & DMP_DESC_ADDRSIZE_GT32)
+                               brcmf_chip_dmp_get_desc(ci, eromaddr, NULL);
+               }
+
+               /* only look for 4K register regions */
+               if (sztype != DMP_SLAVE_SIZE_4K)
+                       continue;
+
+               stype = (val & DMP_SLAVE_TYPE) >> DMP_SLAVE_TYPE_S;
+
+               /* only regular slave and wrapper */
+               if (*regbase == 0 && stype == DMP_SLAVE_TYPE_SLAVE)
+                       *regbase = val & DMP_SLAVE_ADDR_BASE;
+               if (*wrapbase == 0 && stype == wraptype)
+                       *wrapbase = val & DMP_SLAVE_ADDR_BASE;
+       } while (*regbase == 0 || *wrapbase == 0);
+
+       return 0;
+}
+
+static
+int brcmf_chip_dmp_erom_scan(struct brcmf_chip_priv *ci)
+{
+       struct brcmf_core *core;
+       u32 eromaddr;
+       u8 desc_type = 0;
+       u32 val;
+       u16 id;
+       u8 nmp, nsp, nmw, nsw, rev;
+       u32 base, wrap;
+       int err;
+
+       eromaddr = ci->ops->read32(ci->ctx, CORE_CC_REG(SI_ENUM_BASE, eromptr));
+
+       while (desc_type != DMP_DESC_EOT) {
+               val = brcmf_chip_dmp_get_desc(ci, &eromaddr, &desc_type);
+               if (!(val & DMP_DESC_VALID))
+                       continue;
+
+               if (desc_type == DMP_DESC_EMPTY)
+                       continue;
+
+               /* need a component descriptor */
+               if (desc_type != DMP_DESC_COMPONENT)
+                       continue;
+
+               id = (val & DMP_COMP_PARTNUM) >> DMP_COMP_PARTNUM_S;
+
+               /* next descriptor must be component as well */
+               val = brcmf_chip_dmp_get_desc(ci, &eromaddr, &desc_type);
+               if (WARN_ON((val & DMP_DESC_TYPE_MSK) != DMP_DESC_COMPONENT))
+                       return -EFAULT;
+
+               /* only look at cores with master port(s) */
+               nmp = (val & DMP_COMP_NUM_MPORT) >> DMP_COMP_NUM_MPORT_S;
+               nsp = (val & DMP_COMP_NUM_SPORT) >> DMP_COMP_NUM_SPORT_S;
+               nmw = (val & DMP_COMP_NUM_MWRAP) >> DMP_COMP_NUM_MWRAP_S;
+               nsw = (val & DMP_COMP_NUM_SWRAP) >> DMP_COMP_NUM_SWRAP_S;
+               rev = (val & DMP_COMP_REVISION) >> DMP_COMP_REVISION_S;
+
+               /* need core with ports */
+               if (nmw + nsw == 0)
+                       continue;
+
+               /* try to obtain register address info */
+               err = brcmf_chip_dmp_get_regaddr(ci, &eromaddr, &base, &wrap);
+               if (err)
+                       continue;
+
+               /* finally a core to be added */
+               core = brcmf_chip_add_core(ci, id, base, wrap);
+               if (IS_ERR(core))
+                       return PTR_ERR(core);
+
+               core->rev = rev;
+       }
+
+       return 0;
+}
+
+static int brcmf_chip_recognition(struct brcmf_chip_priv *ci)
+{
+       struct brcmf_core *core;
+       u32 regdata;
+       u32 socitype;
+
+       /* Get CC core rev
+        * Chipid is assume to be at offset 0 from SI_ENUM_BASE
+        * For different chiptypes or old sdio hosts w/o chipcommon,
+        * other ways of recognition should be added here.
+        */
+       regdata = ci->ops->read32(ci->ctx, CORE_CC_REG(SI_ENUM_BASE, chipid));
+       ci->pub.chip = regdata & CID_ID_MASK;
+       ci->pub.chiprev = (regdata & CID_REV_MASK) >> CID_REV_SHIFT;
+       socitype = (regdata & CID_TYPE_MASK) >> CID_TYPE_SHIFT;
+
+       brcmf_chip_name(ci->pub.chip, ci->pub.name, sizeof(ci->pub.name));
+       brcmf_dbg(INFO, "found %s chip: BCM%s, rev=%d\n",
+                 socitype == SOCI_SB ? "SB" : "AXI", ci->pub.name,
+                 ci->pub.chiprev);
+
+       if (socitype == SOCI_SB) {
+               if (ci->pub.chip != BCM4329_CHIP_ID) {
+                       brcmf_err("SB chip is not supported\n");
+                       return -ENODEV;
+               }
+               ci->iscoreup = brcmf_chip_sb_iscoreup;
+               ci->coredisable = brcmf_chip_sb_coredisable;
+               ci->resetcore = brcmf_chip_sb_resetcore;
+
+               core = brcmf_chip_add_core(ci, BCMA_CORE_CHIPCOMMON,
+                                          SI_ENUM_BASE, 0);
+               brcmf_chip_sb_corerev(ci, core);
+               core = brcmf_chip_add_core(ci, BCMA_CORE_SDIO_DEV,
+                                          BCM4329_CORE_BUS_BASE, 0);
+               brcmf_chip_sb_corerev(ci, core);
+               core = brcmf_chip_add_core(ci, BCMA_CORE_INTERNAL_MEM,
+                                          BCM4329_CORE_SOCRAM_BASE, 0);
+               brcmf_chip_sb_corerev(ci, core);
+               core = brcmf_chip_add_core(ci, BCMA_CORE_ARM_CM3,
+                                          BCM4329_CORE_ARM_BASE, 0);
+               brcmf_chip_sb_corerev(ci, core);
+
+               core = brcmf_chip_add_core(ci, BCMA_CORE_80211, 0x18001000, 0);
+               brcmf_chip_sb_corerev(ci, core);
+       } else if (socitype == SOCI_AI) {
+               ci->iscoreup = brcmf_chip_ai_iscoreup;
+               ci->coredisable = brcmf_chip_ai_coredisable;
+               ci->resetcore = brcmf_chip_ai_resetcore;
+
+               brcmf_chip_dmp_erom_scan(ci);
+       } else {
+               brcmf_err("chip backplane type %u is not supported\n",
+                         socitype);
+               return -ENODEV;
+       }
+
+       brcmf_chip_get_raminfo(ci);
+
+       return brcmf_chip_cores_check(ci);
+}
+
+static void brcmf_chip_disable_arm(struct brcmf_chip_priv *chip, u16 id)
+{
+       struct brcmf_core *core;
+       struct brcmf_core_priv *cr4;
+       u32 val;
+
+
+       core = brcmf_chip_get_core(&chip->pub, id);
+       if (!core)
+               return;
+
+       switch (id) {
+       case BCMA_CORE_ARM_CM3:
+               brcmf_chip_coredisable(core, 0, 0);
+               break;
+       case BCMA_CORE_ARM_CR4:
+               cr4 = container_of(core, struct brcmf_core_priv, pub);
+
+               /* clear all IOCTL bits except HALT bit */
+               val = chip->ops->read32(chip->ctx, cr4->wrapbase + BCMA_IOCTL);
+               val &= ARMCR4_BCMA_IOCTL_CPUHALT;
+               brcmf_chip_resetcore(core, val, ARMCR4_BCMA_IOCTL_CPUHALT,
+                                    ARMCR4_BCMA_IOCTL_CPUHALT);
+               break;
+       default:
+               brcmf_err("unknown id: %u\n", id);
+               break;
+       }
+}
+
+static int brcmf_chip_setup(struct brcmf_chip_priv *chip)
+{
+       struct brcmf_chip *pub;
+       struct brcmf_core_priv *cc;
+       u32 base;
+       u32 val;
+       int ret = 0;
+
+       pub = &chip->pub;
+       cc = list_first_entry(&chip->cores, struct brcmf_core_priv, list);
+       base = cc->pub.base;
+
+       /* get chipcommon capabilites */
+       pub->cc_caps = chip->ops->read32(chip->ctx,
+                                        CORE_CC_REG(base, capabilities));
+
+       /* get pmu caps & rev */
+       if (pub->cc_caps & CC_CAP_PMU) {
+               val = chip->ops->read32(chip->ctx,
+                                       CORE_CC_REG(base, pmucapabilities));
+               pub->pmurev = val & PCAP_REV_MASK;
+               pub->pmucaps = val;
+       }
+
+       brcmf_dbg(INFO, "ccrev=%d, pmurev=%d, pmucaps=0x%x\n",
+                 cc->pub.rev, pub->pmurev, pub->pmucaps);
+
+       /* execute bus core specific setup */
+       if (chip->ops->setup)
+               ret = chip->ops->setup(chip->ctx, pub);
+
+       /*
+        * Make sure any on-chip ARM is off (in case strapping is wrong),
+        * or downloaded code was already running.
+        */
+       brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CM3);
+       brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CR4);
+       return ret;
+}
+
+struct brcmf_chip *brcmf_chip_attach(void *ctx,
+                                    const struct brcmf_buscore_ops *ops)
+{
+       struct brcmf_chip_priv *chip;
+       int err = 0;
+
+       if (WARN_ON(!ops->read32))
+               err = -EINVAL;
+       if (WARN_ON(!ops->write32))
+               err = -EINVAL;
+       if (WARN_ON(!ops->prepare))
+               err = -EINVAL;
+       if (WARN_ON(!ops->exit_dl))
+               err = -EINVAL;
+       if (err < 0)
+               return ERR_PTR(-EINVAL);
+
+       chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+       if (!chip)
+               return ERR_PTR(-ENOMEM);
+
+       INIT_LIST_HEAD(&chip->cores);
+       chip->num_cores = 0;
+       chip->ops = ops;
+       chip->ctx = ctx;
+
+       err = ops->prepare(ctx);
+       if (err < 0)
+               goto fail;
+
+       err = brcmf_chip_recognition(chip);
+       if (err < 0)
+               goto fail;
+
+       err = brcmf_chip_setup(chip);
+       if (err < 0)
+               goto fail;
+
+       return &chip->pub;
+
+fail:
+       brcmf_chip_detach(&chip->pub);
+       return ERR_PTR(err);
+}
+
+void brcmf_chip_detach(struct brcmf_chip *pub)
+{
+       struct brcmf_chip_priv *chip;
+       struct brcmf_core_priv *core;
+       struct brcmf_core_priv *tmp;
+
+       chip = container_of(pub, struct brcmf_chip_priv, pub);
+       list_for_each_entry_safe(core, tmp, &chip->cores, list) {
+               list_del(&core->list);
+               kfree(core);
+       }
+       kfree(chip);
+}
+
+struct brcmf_core *brcmf_chip_get_core(struct brcmf_chip *pub, u16 coreid)
+{
+       struct brcmf_chip_priv *chip;
+       struct brcmf_core_priv *core;
+
+       chip = container_of(pub, struct brcmf_chip_priv, pub);
+       list_for_each_entry(core, &chip->cores, list)
+               if (core->pub.id == coreid)
+                       return &core->pub;
+
+       return NULL;
+}
+
+struct brcmf_core *brcmf_chip_get_chipcommon(struct brcmf_chip *pub)
+{
+       struct brcmf_chip_priv *chip;
+       struct brcmf_core_priv *cc;
+
+       chip = container_of(pub, struct brcmf_chip_priv, pub);
+       cc = list_first_entry(&chip->cores, struct brcmf_core_priv, list);
+       if (WARN_ON(!cc || cc->pub.id != BCMA_CORE_CHIPCOMMON))
+               return brcmf_chip_get_core(pub, BCMA_CORE_CHIPCOMMON);
+       return &cc->pub;
+}
+
+bool brcmf_chip_iscoreup(struct brcmf_core *pub)
+{
+       struct brcmf_core_priv *core;
+
+       core = container_of(pub, struct brcmf_core_priv, pub);
+       return core->chip->iscoreup(core);
+}
+
+void brcmf_chip_coredisable(struct brcmf_core *pub, u32 prereset, u32 reset)
+{
+       struct brcmf_core_priv *core;
+
+       core = container_of(pub, struct brcmf_core_priv, pub);
+       core->chip->coredisable(core, prereset, reset);
+}
+
+void brcmf_chip_resetcore(struct brcmf_core *pub, u32 prereset, u32 reset,
+                         u32 postreset)
+{
+       struct brcmf_core_priv *core;
+
+       core = container_of(pub, struct brcmf_core_priv, pub);
+       core->chip->resetcore(core, prereset, reset, postreset);
+}
+
+static void
+brcmf_chip_cm3_enterdl(struct brcmf_chip_priv *chip)
+{
+       struct brcmf_core *core;
+
+       brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CM3);
+       core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_80211);
+       brcmf_chip_resetcore(core, D11_BCMA_IOCTL_PHYRESET |
+                                  D11_BCMA_IOCTL_PHYCLOCKEN,
+                            D11_BCMA_IOCTL_PHYCLOCKEN,
+                            D11_BCMA_IOCTL_PHYCLOCKEN);
+       core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_INTERNAL_MEM);
+       brcmf_chip_resetcore(core, 0, 0, 0);
+}
+
+static bool brcmf_chip_cm3_exitdl(struct brcmf_chip_priv *chip)
+{
+       struct brcmf_core *core;
+
+       core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_INTERNAL_MEM);
+       if (!brcmf_chip_iscoreup(core)) {
+               brcmf_err("SOCRAM core is down after reset?\n");
+               return false;
+       }
+
+       chip->ops->exit_dl(chip->ctx, &chip->pub, 0);
+
+       core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_ARM_CM3);
+       brcmf_chip_resetcore(core, 0, 0, 0);
+
+       return true;
+}
+
+static inline void
+brcmf_chip_cr4_enterdl(struct brcmf_chip_priv *chip)
+{
+       struct brcmf_core *core;
+
+       brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CR4);
+
+       core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_80211);
+       brcmf_chip_resetcore(core, D11_BCMA_IOCTL_PHYRESET |
+                                  D11_BCMA_IOCTL_PHYCLOCKEN,
+                            D11_BCMA_IOCTL_PHYCLOCKEN,
+                            D11_BCMA_IOCTL_PHYCLOCKEN);
+}
+
+static bool brcmf_chip_cr4_exitdl(struct brcmf_chip_priv *chip, u32 rstvec)
+{
+       struct brcmf_core *core;
+
+       chip->ops->exit_dl(chip->ctx, &chip->pub, rstvec);
+
+       /* restore ARM */
+       core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_ARM_CR4);
+       brcmf_chip_resetcore(core, ARMCR4_BCMA_IOCTL_CPUHALT, 0, 0);
+
+       return true;
+}
+
+void brcmf_chip_enter_download(struct brcmf_chip *pub)
+{
+       struct brcmf_chip_priv *chip;
+       struct brcmf_core *arm;
+
+       brcmf_dbg(TRACE, "Enter\n");
+
+       chip = container_of(pub, struct brcmf_chip_priv, pub);
+       arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CR4);
+       if (arm) {
+               brcmf_chip_cr4_enterdl(chip);
+               return;
+       }
+
+       brcmf_chip_cm3_enterdl(chip);
+}
+
+bool brcmf_chip_exit_download(struct brcmf_chip *pub, u32 rstvec)
+{
+       struct brcmf_chip_priv *chip;
+       struct brcmf_core *arm;
+
+       brcmf_dbg(TRACE, "Enter\n");
+
+       chip = container_of(pub, struct brcmf_chip_priv, pub);
+       arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CR4);
+       if (arm)
+               return brcmf_chip_cr4_exitdl(chip, rstvec);
+
+       return brcmf_chip_cm3_exitdl(chip);
+}
+
+bool brcmf_chip_sr_capable(struct brcmf_chip *pub)
+{
+       u32 base, addr, reg, pmu_cc3_mask = ~0;
+       struct brcmf_chip_priv *chip;
+
+       brcmf_dbg(TRACE, "Enter\n");
+
+       /* old chips with PMU version less than 17 don't support save restore */
+       if (pub->pmurev < 17)
+               return false;
+
+       base = brcmf_chip_get_chipcommon(pub)->base;
+       chip = container_of(pub, struct brcmf_chip_priv, pub);
+
+       switch (pub->chip) {
+       case BCM43241_CHIP_ID:
+       case BCM4335_CHIP_ID:
+       case BCM4339_CHIP_ID:
+               /* read PMU chipcontrol register 3 */
+               addr = CORE_CC_REG(base, chipcontrol_addr);
+               chip->ops->write32(chip->ctx, addr, 3);
+               addr = CORE_CC_REG(base, chipcontrol_data);
+               reg = chip->ops->read32(chip->ctx, addr);
+               return (reg & pmu_cc3_mask) != 0;
+       default:
+               addr = CORE_CC_REG(base, pmucapabilities_ext);
+               reg = chip->ops->read32(chip->ctx, addr);
+               if ((reg & PCAPEXT_SR_SUPPORTED_MASK) == 0)
+                       return false;
+
+               addr = CORE_CC_REG(base, retention_ctl);
+               reg = chip->ops->read32(chip->ctx, addr);
+               return (reg & (PMU_RCTL_MACPHY_DISABLE_MASK |
+                              PMU_RCTL_LOGIC_DISABLE_MASK)) == 0;
+       }
+}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.h b/drivers/net/wireless/brcm80211/brcmfmac/chip.h
new file mode 100644 (file)
index 0000000..c32908d
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * 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 BRCMF_CHIP_H
+#define BRCMF_CHIP_H
+
+#include <linux/types.h>
+
+#define CORE_CC_REG(base, field) \
+               (base + offsetof(struct chipcregs, field))
+
+/**
+ * struct brcmf_chip - chip level information.
+ *
+ * @chip: chip identifier.
+ * @chiprev: chip revision.
+ * @cc_caps: chipcommon core capabilities.
+ * @pmucaps: PMU capabilities.
+ * @pmurev: PMU revision.
+ * @rambase: RAM base address (only applicable for ARM CR4 chips).
+ * @ramsize: amount of RAM on chip.
+ * @name: string representation of the chip identifier.
+ */
+struct brcmf_chip {
+       u32 chip;
+       u32 chiprev;
+       u32 cc_caps;
+       u32 pmucaps;
+       u32 pmurev;
+       u32 rambase;
+       u32 ramsize;
+       char name[8];
+};
+
+/**
+ * struct brcmf_core - core related information.
+ *
+ * @id: core identifier.
+ * @rev: core revision.
+ * @base: base address of core register space.
+ */
+struct brcmf_core {
+       u16 id;
+       u16 rev;
+       u32 base;
+};
+
+/**
+ * struct brcmf_buscore_ops - buscore specific callbacks.
+ *
+ * @read32: read 32-bit value over bus.
+ * @write32: write 32-bit value over bus.
+ * @prepare: prepare bus for core configuration.
+ * @setup: bus-specific core setup.
+ * @exit_dl: exit download state.
+ *     The callback should use the provided @rstvec when non-zero.
+ */
+struct brcmf_buscore_ops {
+       u32 (*read32)(void *ctx, u32 addr);
+       void (*write32)(void *ctx, u32 addr, u32 value);
+       int (*prepare)(void *ctx);
+       int (*setup)(void *ctx, struct brcmf_chip *chip);
+       void (*exit_dl)(void *ctx, struct brcmf_chip *chip, u32 rstvec);
+};
+
+struct brcmf_chip *brcmf_chip_attach(void *ctx,
+                                    const struct brcmf_buscore_ops *ops);
+void brcmf_chip_detach(struct brcmf_chip *chip);
+struct brcmf_core *brcmf_chip_get_core(struct brcmf_chip *chip, u16 coreid);
+struct brcmf_core *brcmf_chip_get_chipcommon(struct brcmf_chip *chip);
+bool brcmf_chip_iscoreup(struct brcmf_core *core);
+void brcmf_chip_coredisable(struct brcmf_core *core, u32 prereset, u32 reset);
+void brcmf_chip_resetcore(struct brcmf_core *core, u32 prereset, u32 reset,
+                         u32 postreset);
+void brcmf_chip_enter_download(struct brcmf_chip *ci);
+bool brcmf_chip_exit_download(struct brcmf_chip *ci, u32 rstvec);
+bool brcmf_chip_sr_capable(struct brcmf_chip *pub);
+
+#endif /* BRCMF_AXIDMP_H */
index 3e99189..631d5dc 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/interrupt.h>
 #include <linux/sched.h>
 #include <linux/mmc/sdio.h>
+#include <linux/mmc/sdio_ids.h>
 #include <linux/mmc/sdio_func.h>
 #include <linux/mmc/card.h>
 #include <linux/semaphore.h>
@@ -40,7 +41,7 @@
 #include <brcm_hw_ids.h>
 #include <soc.h>
 #include "sdio_host.h"
-#include "sdio_chip.h"
+#include "chip.h"
 #include "nvram.h"
 
 #define DCMD_RESP_TIMEOUT  2000        /* In milli second */
@@ -156,6 +157,33 @@ struct rte_console {
 /* manfid tuple length, include tuple, link bytes */
 #define SBSDIO_CIS_MANFID_TUPLE_LEN    6
 
+#define CORE_BUS_REG(base, field) \
+               (base + offsetof(struct sdpcmd_regs, field))
+
+/* SDIO function 1 register CHIPCLKCSR */
+/* Force ALP request to backplane */
+#define SBSDIO_FORCE_ALP               0x01
+/* Force HT request to backplane */
+#define SBSDIO_FORCE_HT                        0x02
+/* Force ILP request to backplane */
+#define SBSDIO_FORCE_ILP               0x04
+/* Make ALP ready (power up xtal) */
+#define SBSDIO_ALP_AVAIL_REQ           0x08
+/* Make HT ready (power up PLL) */
+#define SBSDIO_HT_AVAIL_REQ            0x10
+/* Squelch clock requests from HW */
+#define SBSDIO_FORCE_HW_CLKREQ_OFF     0x20
+/* Status: ALP is ready */
+#define SBSDIO_ALP_AVAIL               0x40
+/* Status: HT is ready */
+#define SBSDIO_HT_AVAIL                        0x80
+#define SBSDIO_AVBITS          (SBSDIO_HT_AVAIL | SBSDIO_ALP_AVAIL)
+#define SBSDIO_ALPAV(regval)   ((regval) & SBSDIO_AVBITS)
+#define SBSDIO_HTAV(regval)    (((regval) & SBSDIO_AVBITS) == SBSDIO_AVBITS)
+#define SBSDIO_ALPONLY(regval) (SBSDIO_ALPAV(regval) && !SBSDIO_HTAV(regval))
+#define SBSDIO_CLKAV(regval, alponly) \
+       (SBSDIO_ALPAV(regval) && (alponly ? 1 : SBSDIO_HTAV(regval)))
+
 /* intstatus */
 #define I_SMB_SW0      (1 << 0)        /* To SB Mail S/W interrupt 0 */
 #define I_SMB_SW1      (1 << 1)        /* To SB Mail S/W interrupt 1 */
@@ -494,6 +522,52 @@ enum brcmf_sdio_frmtype {
        BRCMF_SDIO_FT_SUB,
 };
 
+#define SDIOD_DRVSTR_KEY(chip, pmu)     (((chip) << 16) | (pmu))
+
+/* SDIO Pad drive strength to select value mappings */
+struct sdiod_drive_str {
+       u8 strength;    /* Pad Drive Strength in mA */
+       u8 sel;         /* Chip-specific select value */
+};
+
+/* SDIO Drive Strength to sel value table for PMU Rev 11 (1.8V) */
+static const struct sdiod_drive_str sdiod_drvstr_tab1_1v8[] = {
+       {32, 0x6},
+       {26, 0x7},
+       {22, 0x4},
+       {16, 0x5},
+       {12, 0x2},
+       {8, 0x3},
+       {4, 0x0},
+       {0, 0x1}
+};
+
+/* SDIO Drive Strength to sel value table for PMU Rev 13 (1.8v) */
+static const struct sdiod_drive_str sdiod_drive_strength_tab5_1v8[] = {
+       {6, 0x7},
+       {5, 0x6},
+       {4, 0x5},
+       {3, 0x4},
+       {2, 0x2},
+       {1, 0x1},
+       {0, 0x0}
+};
+
+/* SDIO Drive Strength to sel value table for PMU Rev 17 (1.8v) */
+static const struct sdiod_drive_str sdiod_drvstr_tab6_1v8[] = {
+       {3, 0x3},
+       {2, 0x2},
+       {1, 0x1},
+       {0, 0x0} };
+
+/* SDIO Drive Strength to sel value table for 43143 PMU Rev 17 (3.3V) */
+static const struct sdiod_drive_str sdiod_drvstr_tab2_3v3[] = {
+       {16, 0x7},
+       {12, 0x5},
+       {8,  0x3},
+       {4,  0x1}
+};
+
 #define BCM43143_FIRMWARE_NAME         "brcm/brcmfmac43143-sdio.bin"
 #define BCM43143_NVRAM_NAME            "brcm/brcmfmac43143-sdio.txt"
 #define BCM43241B0_FIRMWARE_NAME       "brcm/brcmfmac43241b0-sdio.bin"
@@ -619,27 +693,24 @@ static bool data_ok(struct brcmf_sdio *bus)
  * Reads a register in the SDIO hardware block. This block occupies a series of
  * adresses on the 32 bit backplane bus.
  */
-static int
-r_sdreg32(struct brcmf_sdio *bus, u32 *regvar, u32 offset)
+static int r_sdreg32(struct brcmf_sdio *bus, u32 *regvar, u32 offset)
 {
-       u8 idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_SDIO_DEV);
+       struct brcmf_core *core;
        int ret;
 
-       *regvar = brcmf_sdiod_regrl(bus->sdiodev,
-                                   bus->ci->c_inf[idx].base + offset, &ret);
+       core = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV);
+       *regvar = brcmf_sdiod_regrl(bus->sdiodev, core->base + offset, &ret);
 
        return ret;
 }
 
-static int
-w_sdreg32(struct brcmf_sdio *bus, u32 regval, u32 reg_offset)
+static int w_sdreg32(struct brcmf_sdio *bus, u32 regval, u32 reg_offset)
 {
-       u8 idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_SDIO_DEV);
+       struct brcmf_core *core;
        int ret;
 
-       brcmf_sdiod_regwl(bus->sdiodev,
-                         bus->ci->c_inf[idx].base + reg_offset,
-                         regval, &ret);
+       core = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV);
+       brcmf_sdiod_regwl(bus->sdiodev, core->base + reg_offset, regval, &ret);
 
        return ret;
 }
@@ -900,8 +971,8 @@ static int
 brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok)
 {
        int err = 0;
-       brcmf_dbg(TRACE, "Enter\n");
-       brcmf_dbg(SDIO, "request %s currently %s\n",
+
+       brcmf_dbg(SDIO, "Enter: request %s currently %s\n",
                  (sleep ? "SLEEP" : "WAKE"),
                  (bus->sleeping ? "SLEEP" : "WAKE"));
 
@@ -953,6 +1024,86 @@ end:
 
 }
 
+#ifdef DEBUG
+static inline bool brcmf_sdio_valid_shared_address(u32 addr)
+{
+       return !(addr == 0 || ((~addr >> 16) & 0xffff) == (addr & 0xffff));
+}
+
+static int brcmf_sdio_readshared(struct brcmf_sdio *bus,
+                                struct sdpcm_shared *sh)
+{
+       u32 addr;
+       int rv;
+       u32 shaddr = 0;
+       struct sdpcm_shared_le sh_le;
+       __le32 addr_le;
+
+       shaddr = bus->ci->rambase + bus->ramsize - 4;
+
+       /*
+        * Read last word in socram to determine
+        * address of sdpcm_shared structure
+        */
+       sdio_claim_host(bus->sdiodev->func[1]);
+       brcmf_sdio_bus_sleep(bus, false, false);
+       rv = brcmf_sdiod_ramrw(bus->sdiodev, false, shaddr, (u8 *)&addr_le, 4);
+       sdio_release_host(bus->sdiodev->func[1]);
+       if (rv < 0)
+               return rv;
+
+       addr = le32_to_cpu(addr_le);
+
+       brcmf_dbg(SDIO, "sdpcm_shared address 0x%08X\n", addr);
+
+       /*
+        * Check if addr is valid.
+        * NVRAM length at the end of memory should have been overwritten.
+        */
+       if (!brcmf_sdio_valid_shared_address(addr)) {
+                       brcmf_err("invalid sdpcm_shared address 0x%08X\n",
+                                 addr);
+                       return -EINVAL;
+       }
+
+       /* Read hndrte_shared structure */
+       rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, (u8 *)&sh_le,
+                              sizeof(struct sdpcm_shared_le));
+       if (rv < 0)
+               return rv;
+
+       /* Endianness */
+       sh->flags = le32_to_cpu(sh_le.flags);
+       sh->trap_addr = le32_to_cpu(sh_le.trap_addr);
+       sh->assert_exp_addr = le32_to_cpu(sh_le.assert_exp_addr);
+       sh->assert_file_addr = le32_to_cpu(sh_le.assert_file_addr);
+       sh->assert_line = le32_to_cpu(sh_le.assert_line);
+       sh->console_addr = le32_to_cpu(sh_le.console_addr);
+       sh->msgtrace_addr = le32_to_cpu(sh_le.msgtrace_addr);
+
+       if ((sh->flags & SDPCM_SHARED_VERSION_MASK) > SDPCM_SHARED_VERSION) {
+               brcmf_err("sdpcm shared version unsupported: dhd %d dongle %d\n",
+                         SDPCM_SHARED_VERSION,
+                         sh->flags & SDPCM_SHARED_VERSION_MASK);
+               return -EPROTO;
+       }
+
+       return 0;
+}
+
+static void brcmf_sdio_get_console_addr(struct brcmf_sdio *bus)
+{
+       struct sdpcm_shared sh;
+
+       if (brcmf_sdio_readshared(bus, &sh) == 0)
+               bus->console_addr = sh.console_addr;
+}
+#else
+static void brcmf_sdio_get_console_addr(struct brcmf_sdio *bus)
+{
+}
+#endif /* DEBUG */
+
 static u32 brcmf_sdio_hostmail(struct brcmf_sdio *bus)
 {
        u32 intstatus = 0;
@@ -996,6 +1147,12 @@ static u32 brcmf_sdio_hostmail(struct brcmf_sdio *bus)
                else
                        brcmf_dbg(SDIO, "Dongle ready, protocol version %d\n",
                                  bus->sdpcm_ver);
+
+               /*
+                * Retrieve console state address now that firmware should have
+                * updated it.
+                */
+               brcmf_sdio_get_console_addr(bus);
        }
 
        /*
@@ -2293,14 +2450,13 @@ static inline void brcmf_sdio_clrintr(struct brcmf_sdio *bus)
 
 static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus)
 {
-       u8 idx;
+       struct brcmf_core *buscore;
        u32 addr;
        unsigned long val;
        int n, ret;
 
-       idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_SDIO_DEV);
-       addr = bus->ci->c_inf[idx].base +
-              offsetof(struct sdpcmd_regs, intstatus);
+       buscore = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV);
+       addr = buscore->base + offsetof(struct sdpcmd_regs, intstatus);
 
        val = brcmf_sdiod_regrl(bus->sdiodev, addr, &ret);
        bus->sdcnt.f1regdata++;
@@ -2810,72 +2966,6 @@ brcmf_sdio_bus_txctl(struct device *dev, unsigned char *msg, uint msglen)
 }
 
 #ifdef DEBUG
-static inline bool brcmf_sdio_valid_shared_address(u32 addr)
-{
-       return !(addr == 0 || ((~addr >> 16) & 0xffff) == (addr & 0xffff));
-}
-
-static int brcmf_sdio_readshared(struct brcmf_sdio *bus,
-                                struct sdpcm_shared *sh)
-{
-       u32 addr;
-       int rv;
-       u32 shaddr = 0;
-       struct sdpcm_shared_le sh_le;
-       __le32 addr_le;
-
-       shaddr = bus->ci->rambase + bus->ramsize - 4;
-
-       /*
-        * Read last word in socram to determine
-        * address of sdpcm_shared structure
-        */
-       sdio_claim_host(bus->sdiodev->func[1]);
-       brcmf_sdio_bus_sleep(bus, false, false);
-       rv = brcmf_sdiod_ramrw(bus->sdiodev, false, shaddr, (u8 *)&addr_le, 4);
-       sdio_release_host(bus->sdiodev->func[1]);
-       if (rv < 0)
-               return rv;
-
-       addr = le32_to_cpu(addr_le);
-
-       brcmf_dbg(SDIO, "sdpcm_shared address 0x%08X\n", addr);
-
-       /*
-        * Check if addr is valid.
-        * NVRAM length at the end of memory should have been overwritten.
-        */
-       if (!brcmf_sdio_valid_shared_address(addr)) {
-                       brcmf_err("invalid sdpcm_shared address 0x%08X\n",
-                                 addr);
-                       return -EINVAL;
-       }
-
-       /* Read hndrte_shared structure */
-       rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, (u8 *)&sh_le,
-                              sizeof(struct sdpcm_shared_le));
-       if (rv < 0)
-               return rv;
-
-       /* Endianness */
-       sh->flags = le32_to_cpu(sh_le.flags);
-       sh->trap_addr = le32_to_cpu(sh_le.trap_addr);
-       sh->assert_exp_addr = le32_to_cpu(sh_le.assert_exp_addr);
-       sh->assert_file_addr = le32_to_cpu(sh_le.assert_file_addr);
-       sh->assert_line = le32_to_cpu(sh_le.assert_line);
-       sh->console_addr = le32_to_cpu(sh_le.console_addr);
-       sh->msgtrace_addr = le32_to_cpu(sh_le.msgtrace_addr);
-
-       if ((sh->flags & SDPCM_SHARED_VERSION_MASK) > SDPCM_SHARED_VERSION) {
-               brcmf_err("sdpcm shared version unsupported: dhd %d dongle %d\n",
-                         SDPCM_SHARED_VERSION,
-                         sh->flags & SDPCM_SHARED_VERSION_MASK);
-               return -EPROTO;
-       }
-
-       return 0;
-}
-
 static int brcmf_sdio_dump_console(struct brcmf_sdio *bus,
                                   struct sdpcm_shared *sh, char __user *data,
                                   size_t count)
@@ -3105,6 +3195,8 @@ static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus)
        debugfs_create_file("forensics", S_IRUGO, dentry, bus,
                            &brcmf_sdio_forensic_ops);
        brcmf_debugfs_create_sdio_count(drvr, &bus->sdcnt);
+       debugfs_create_u32("console_interval", 0644, dentry,
+                          &bus->console_interval);
 }
 #else
 static int brcmf_sdio_checkdied(struct brcmf_sdio *bus)
@@ -3223,32 +3315,17 @@ static int brcmf_sdio_download_code_file(struct brcmf_sdio *bus,
                                         const struct firmware *fw)
 {
        int err;
-       int offset;
-       int address;
-       int len;
 
        brcmf_dbg(TRACE, "Enter\n");
 
-       err = 0;
-       offset = 0;
-       address = bus->ci->rambase;
-       while (offset < fw->size) {
-               len = ((offset + MEMBLOCK) < fw->size) ? MEMBLOCK :
-                     fw->size - offset;
-               err = brcmf_sdiod_ramrw(bus->sdiodev, true, address,
-                                       (u8 *)&fw->data[offset], len);
-               if (err) {
-                       brcmf_err("error %d on writing %d membytes at 0x%08x\n",
-                                 err, len, address);
-                       return err;
-               }
-               offset += len;
-               address += len;
-       }
-       if (!err)
-               if (!brcmf_sdio_verifymemory(bus->sdiodev, bus->ci->rambase,
-                                            (u8 *)fw->data, fw->size))
-                       err = -EIO;
+       err = brcmf_sdiod_ramrw(bus->sdiodev, true, bus->ci->rambase,
+                               (u8 *)fw->data, fw->size);
+       if (err)
+               brcmf_err("error %d on writing %d membytes at 0x%08x\n",
+                         err, (int)fw->size, bus->ci->rambase);
+       else if (!brcmf_sdio_verifymemory(bus->sdiodev, bus->ci->rambase,
+                                         (u8 *)fw->data, fw->size))
+               err = -EIO;
 
        return err;
 }
@@ -3291,7 +3368,7 @@ static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus)
        brcmf_sdio_clkctl(bus, CLK_AVAIL, false);
 
        /* Keep arm in reset */
-       brcmf_sdio_chip_enter_download(bus->sdiodev, bus->ci);
+       brcmf_chip_enter_download(bus->ci);
 
        fw = brcmf_sdio_get_fw(bus, BRCMF_FIRMWARE_BIN);
        if (fw == NULL) {
@@ -3323,7 +3400,7 @@ static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus)
        }
 
        /* Take arm out of reset */
-       if (!brcmf_sdio_chip_exit_download(bus->sdiodev, bus->ci, rstvec)) {
+       if (!brcmf_chip_exit_download(bus->ci, rstvec)) {
                brcmf_err("error getting out of ARM core reset\n");
                goto err;
        }
@@ -3338,40 +3415,6 @@ err:
        return bcmerror;
 }
 
-static bool brcmf_sdio_sr_capable(struct brcmf_sdio *bus)
-{
-       u32 addr, reg, pmu_cc3_mask = ~0;
-       int err;
-
-       brcmf_dbg(TRACE, "Enter\n");
-
-       /* old chips with PMU version less than 17 don't support save restore */
-       if (bus->ci->pmurev < 17)
-               return false;
-
-       switch (bus->ci->chip) {
-       case BCM43241_CHIP_ID:
-       case BCM4335_CHIP_ID:
-       case BCM4339_CHIP_ID:
-               /* read PMU chipcontrol register 3 */
-               addr = CORE_CC_REG(bus->ci->c_inf[0].base, chipcontrol_addr);
-               brcmf_sdiod_regwl(bus->sdiodev, addr, 3, NULL);
-               addr = CORE_CC_REG(bus->ci->c_inf[0].base, chipcontrol_data);
-               reg = brcmf_sdiod_regrl(bus->sdiodev, addr, NULL);
-               return (reg & pmu_cc3_mask) != 0;
-       default:
-               addr = CORE_CC_REG(bus->ci->c_inf[0].base, pmucapabilities_ext);
-               reg = brcmf_sdiod_regrl(bus->sdiodev, addr, &err);
-               if ((reg & PCAPEXT_SR_SUPPORTED_MASK) == 0)
-                       return false;
-
-               addr = CORE_CC_REG(bus->ci->c_inf[0].base, retention_ctl);
-               reg = brcmf_sdiod_regrl(bus->sdiodev, addr, NULL);
-               return (reg & (PMU_RCTL_MACPHY_DISABLE_MASK |
-                              PMU_RCTL_LOGIC_DISABLE_MASK)) == 0;
-       }
-}
-
 static void brcmf_sdio_sr_init(struct brcmf_sdio *bus)
 {
        int err = 0;
@@ -3423,7 +3466,7 @@ static int brcmf_sdio_kso_init(struct brcmf_sdio *bus)
        brcmf_dbg(TRACE, "Enter\n");
 
        /* KSO bit added in SDIO core rev 12 */
-       if (bus->ci->c_inf[1].rev < 12)
+       if (brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV)->rev < 12)
                return 0;
 
        val = brcmf_sdiod_regrb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, &err);
@@ -3454,15 +3497,13 @@ static int brcmf_sdio_bus_preinit(struct device *dev)
        struct brcmf_sdio *bus = sdiodev->bus;
        uint pad_size;
        u32 value;
-       u8 idx;
        int err;
 
        /* the commands below use the terms tx and rx from
         * a device perspective, ie. bus:txglom affects the
         * bus transfers from device to host.
         */
-       idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_SDIO_DEV);
-       if (bus->ci->c_inf[idx].rev < 12) {
+       if (brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV)->rev < 12) {
                /* for sdio core rev < 12, disable txgloming */
                value = 0;
                err = brcmf_iovar_data_set(dev, "bus:txglom", &value,
@@ -3573,7 +3614,7 @@ static int brcmf_sdio_bus_init(struct device *dev)
                ret = -ENODEV;
        }
 
-       if (brcmf_sdio_sr_capable(bus)) {
+       if (brcmf_chip_sr_capable(bus->ci)) {
                brcmf_sdio_sr_init(bus);
        } else {
                /* Restore previous clock setting */
@@ -3722,6 +3763,170 @@ static void brcmf_sdio_dataworker(struct work_struct *work)
        }
 }
 
+static void
+brcmf_sdio_drivestrengthinit(struct brcmf_sdio_dev *sdiodev,
+                            struct brcmf_chip *ci, u32 drivestrength)
+{
+       const struct sdiod_drive_str *str_tab = NULL;
+       u32 str_mask;
+       u32 str_shift;
+       u32 base;
+       u32 i;
+       u32 drivestrength_sel = 0;
+       u32 cc_data_temp;
+       u32 addr;
+
+       if (!(ci->cc_caps & CC_CAP_PMU))
+               return;
+
+       switch (SDIOD_DRVSTR_KEY(ci->chip, ci->pmurev)) {
+       case SDIOD_DRVSTR_KEY(BCM4330_CHIP_ID, 12):
+               str_tab = sdiod_drvstr_tab1_1v8;
+               str_mask = 0x00003800;
+               str_shift = 11;
+               break;
+       case SDIOD_DRVSTR_KEY(BCM4334_CHIP_ID, 17):
+               str_tab = sdiod_drvstr_tab6_1v8;
+               str_mask = 0x00001800;
+               str_shift = 11;
+               break;
+       case SDIOD_DRVSTR_KEY(BCM43143_CHIP_ID, 17):
+               /* note: 43143 does not support tristate */
+               i = ARRAY_SIZE(sdiod_drvstr_tab2_3v3) - 1;
+               if (drivestrength >= sdiod_drvstr_tab2_3v3[i].strength) {
+                       str_tab = sdiod_drvstr_tab2_3v3;
+                       str_mask = 0x00000007;
+                       str_shift = 0;
+               } else
+                       brcmf_err("Invalid SDIO Drive strength for chip %s, strength=%d\n",
+                                 ci->name, drivestrength);
+               break;
+       case SDIOD_DRVSTR_KEY(BCM43362_CHIP_ID, 13):
+               str_tab = sdiod_drive_strength_tab5_1v8;
+               str_mask = 0x00003800;
+               str_shift = 11;
+               break;
+       default:
+               brcmf_err("No SDIO Drive strength init done for chip %s rev %d pmurev %d\n",
+                         ci->name, ci->chiprev, ci->pmurev);
+               break;
+       }
+
+       if (str_tab != NULL) {
+               for (i = 0; str_tab[i].strength != 0; i++) {
+                       if (drivestrength >= str_tab[i].strength) {
+                               drivestrength_sel = str_tab[i].sel;
+                               break;
+                       }
+               }
+               base = brcmf_chip_get_chipcommon(ci)->base;
+               addr = CORE_CC_REG(base, chipcontrol_addr);
+               brcmf_sdiod_regwl(sdiodev, addr, 1, NULL);
+               cc_data_temp = brcmf_sdiod_regrl(sdiodev, addr, NULL);
+               cc_data_temp &= ~str_mask;
+               drivestrength_sel <<= str_shift;
+               cc_data_temp |= drivestrength_sel;
+               brcmf_sdiod_regwl(sdiodev, addr, cc_data_temp, NULL);
+
+               brcmf_dbg(INFO, "SDIO: %d mA (req=%d mA) drive strength selected, set to 0x%08x\n",
+                         str_tab[i].strength, drivestrength, cc_data_temp);
+       }
+}
+
+static int brcmf_sdio_buscoreprep(void *ctx)
+{
+       struct brcmf_sdio_dev *sdiodev = ctx;
+       int err = 0;
+       u8 clkval, clkset;
+
+       /* Try forcing SDIO core to do ALPAvail request only */
+       clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ;
+       brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err);
+       if (err) {
+               brcmf_err("error writing for HT off\n");
+               return err;
+       }
+
+       /* If register supported, wait for ALPAvail and then force ALP */
+       /* This may take up to 15 milliseconds */
+       clkval = brcmf_sdiod_regrb(sdiodev,
+                                  SBSDIO_FUNC1_CHIPCLKCSR, NULL);
+
+       if ((clkval & ~SBSDIO_AVBITS) != clkset) {
+               brcmf_err("ChipClkCSR access: wrote 0x%02x read 0x%02x\n",
+                         clkset, clkval);
+               return -EACCES;
+       }
+
+       SPINWAIT(((clkval = brcmf_sdiod_regrb(sdiodev,
+                                             SBSDIO_FUNC1_CHIPCLKCSR, NULL)),
+                       !SBSDIO_ALPAV(clkval)),
+                       PMU_MAX_TRANSITION_DLY);
+       if (!SBSDIO_ALPAV(clkval)) {
+               brcmf_err("timeout on ALPAV wait, clkval 0x%02x\n",
+                         clkval);
+               return -EBUSY;
+       }
+
+       clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_FORCE_ALP;
+       brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err);
+       udelay(65);
+
+       /* Also, disable the extra SDIO pull-ups */
+       brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_SDIOPULLUP, 0, NULL);
+
+       return 0;
+}
+
+static void brcmf_sdio_buscore_exitdl(void *ctx, struct brcmf_chip *chip,
+                                     u32 rstvec)
+{
+       struct brcmf_sdio_dev *sdiodev = ctx;
+       struct brcmf_core *core;
+       u32 reg_addr;
+
+       /* clear all interrupts */
+       core = brcmf_chip_get_core(chip, BCMA_CORE_SDIO_DEV);
+       reg_addr = core->base + offsetof(struct sdpcmd_regs, intstatus);
+       brcmf_sdiod_regwl(sdiodev, reg_addr, 0xFFFFFFFF, NULL);
+
+       if (rstvec)
+               /* Write reset vector to address 0 */
+               brcmf_sdiod_ramrw(sdiodev, true, 0, (void *)&rstvec,
+                                 sizeof(rstvec));
+}
+
+static u32 brcmf_sdio_buscore_read32(void *ctx, u32 addr)
+{
+       struct brcmf_sdio_dev *sdiodev = ctx;
+       u32 val, rev;
+
+       val = brcmf_sdiod_regrl(sdiodev, addr, NULL);
+       if (sdiodev->func[0]->device == SDIO_DEVICE_ID_BROADCOM_4335_4339 &&
+           addr == CORE_CC_REG(SI_ENUM_BASE, chipid)) {
+               rev = (val & CID_REV_MASK) >> CID_REV_SHIFT;
+               if (rev >= 2) {
+                       val &= ~CID_ID_MASK;
+                       val |= BCM4339_CHIP_ID;
+               }
+       }
+       return val;
+}
+
+static void brcmf_sdio_buscore_write32(void *ctx, u32 addr, u32 val)
+{
+       struct brcmf_sdio_dev *sdiodev = ctx;
+
+       brcmf_sdiod_regwl(sdiodev, addr, val, NULL);
+}
+
+static const struct brcmf_buscore_ops brcmf_sdio_buscore_ops = {
+       .prepare = brcmf_sdio_buscoreprep,
+       .exit_dl = brcmf_sdio_buscore_exitdl,
+       .read32 = brcmf_sdio_buscore_read32,
+       .write32 = brcmf_sdio_buscore_write32,
+};
+
 static bool
 brcmf_sdio_probe_attach(struct brcmf_sdio *bus)
 {
@@ -3737,7 +3942,7 @@ brcmf_sdio_probe_attach(struct brcmf_sdio *bus)
                 brcmf_sdiod_regrl(bus->sdiodev, SI_ENUM_BASE, NULL));
 
        /*
-        * Force PLL off until brcmf_sdio_chip_attach()
+        * Force PLL off until brcmf_chip_attach()
         * programs PLL control regs
         */
 
@@ -3758,8 +3963,10 @@ brcmf_sdio_probe_attach(struct brcmf_sdio *bus)
         */
        brcmf_bus_change_state(bus->sdiodev->bus_if, BRCMF_BUS_DOWN);
 
-       if (brcmf_sdio_chip_attach(bus->sdiodev, &bus->ci)) {
-               brcmf_err("brcmf_sdio_chip_attach failed!\n");
+       bus->ci = brcmf_chip_attach(bus->sdiodev, &brcmf_sdio_buscore_ops);
+       if (IS_ERR(bus->ci)) {
+               brcmf_err("brcmf_chip_attach failed!\n");
+               bus->ci = NULL;
                goto fail;
        }
 
@@ -3772,7 +3979,7 @@ brcmf_sdio_probe_attach(struct brcmf_sdio *bus)
                drivestrength = bus->sdiodev->pdata->drive_strength;
        else
                drivestrength = DEFAULT_SDIO_DRIVE_STRENGTH;
-       brcmf_sdio_chip_drivestrengthinit(bus->sdiodev, bus->ci, drivestrength);
+       brcmf_sdio_drivestrengthinit(bus->sdiodev, bus->ci, drivestrength);
 
        /* Get info on the SOCRAM cores... */
        bus->ramsize = bus->ci->ramsize;
@@ -3795,24 +4002,18 @@ brcmf_sdio_probe_attach(struct brcmf_sdio *bus)
                goto fail;
 
        /* set PMUControl so a backplane reset does PMU state reload */
-       reg_addr = CORE_CC_REG(bus->ci->c_inf[0].base,
+       reg_addr = CORE_CC_REG(brcmf_chip_get_chipcommon(bus->ci)->base,
                               pmucontrol);
-       reg_val = brcmf_sdiod_regrl(bus->sdiodev,
-                                   reg_addr,
-                                   &err);
+       reg_val = brcmf_sdiod_regrl(bus->sdiodev, reg_addr, &err);
        if (err)
                goto fail;
 
        reg_val |= (BCMA_CC_PMU_CTL_RES_RELOAD << BCMA_CC_PMU_CTL_RES_SHIFT);
 
-       brcmf_sdiod_regwl(bus->sdiodev,
-                         reg_addr,
-                         reg_val,
-                         &err);
+       brcmf_sdiod_regwl(bus->sdiodev, reg_addr, reg_val, &err);
        if (err)
                goto fail;
 
-
        sdio_release_host(bus->sdiodev->func[1]);
 
        brcmu_pktq_init(&bus->txq, (PRIOMASK + 1), TXQLEN);
@@ -4027,14 +4228,14 @@ void brcmf_sdio_remove(struct brcmf_sdio *bus)
                /* De-register interrupt handler */
                brcmf_sdiod_intr_unregister(bus->sdiodev);
 
-               cancel_work_sync(&bus->datawork);
-               if (bus->brcmf_wq)
-                       destroy_workqueue(bus->brcmf_wq);
-
                if (bus->sdiodev->bus_if->drvr) {
                        brcmf_detach(bus->sdiodev->dev);
                }
 
+               cancel_work_sync(&bus->datawork);
+               if (bus->brcmf_wq)
+                       destroy_workqueue(bus->brcmf_wq);
+
                if (bus->ci) {
                        if (bus->sdiodev->bus_if->state == BRCMF_BUS_DOWN) {
                                sdio_claim_host(bus->sdiodev->func[1]);
@@ -4045,12 +4246,11 @@ void brcmf_sdio_remove(struct brcmf_sdio *bus)
                                 * all necessary cores.
                                 */
                                msleep(20);
-                               brcmf_sdio_chip_enter_download(bus->sdiodev,
-                                                              bus->ci);
+                               brcmf_chip_enter_download(bus->ci);
                                brcmf_sdio_clkctl(bus, CLK_NONE, false);
                                sdio_release_host(bus->sdiodev->func[1]);
                        }
-                       brcmf_sdio_chip_detach(&bus->ci);
+                       brcmf_chip_detach(bus->ci);
                }
 
                brcmu_pkt_buf_free_skb(bus->txglom_sgpad);
index 22adbe3..59a5af5 100644 (file)
@@ -124,7 +124,8 @@ brcmf_fil_cmd_int_get(struct brcmf_if *ifp, u32 cmd, u32 *data)
 }
 
 static u32
-brcmf_create_iovar(char *name, char *data, u32 datalen, char *buf, u32 buflen)
+brcmf_create_iovar(char *name, const char *data, u32 datalen,
+                  char *buf, u32 buflen)
 {
        u32 len;
 
@@ -144,7 +145,7 @@ brcmf_create_iovar(char *name, char *data, u32 datalen, char *buf, u32 buflen)
 
 
 s32
-brcmf_fil_iovar_data_set(struct brcmf_if *ifp, char *name, void *data,
+brcmf_fil_iovar_data_set(struct brcmf_if *ifp, char *name, const void *data,
                         u32 len)
 {
        struct brcmf_pub *drvr = ifp->drvr;
index 77eae86..a30be68 100644 (file)
@@ -83,7 +83,7 @@ s32 brcmf_fil_cmd_data_get(struct brcmf_if *ifp, u32 cmd, void *data, u32 len);
 s32 brcmf_fil_cmd_int_set(struct brcmf_if *ifp, u32 cmd, u32 data);
 s32 brcmf_fil_cmd_int_get(struct brcmf_if *ifp, u32 cmd, u32 *data);
 
-s32 brcmf_fil_iovar_data_set(struct brcmf_if *ifp, char *name, void *data,
+s32 brcmf_fil_iovar_data_set(struct brcmf_if *ifp, char *name, const void *data,
                             u32 len);
 s32 brcmf_fil_iovar_data_get(struct brcmf_if *ifp, char *name, void *data,
                             u32 len);
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c
deleted file mode 100644 (file)
index 82bf3c5..0000000
+++ /dev/null
@@ -1,972 +0,0 @@
-/*
- * Copyright (c) 2011 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.
- */
-/* ***** SDIO interface chip backplane handle functions ***** */
-
-#include <linux/types.h>
-#include <linux/netdevice.h>
-#include <linux/mmc/card.h>
-#include <linux/mmc/sdio_func.h>
-#include <linux/mmc/sdio_ids.h>
-#include <linux/ssb/ssb_regs.h>
-#include <linux/bcma/bcma.h>
-
-#include <chipcommon.h>
-#include <brcm_hw_ids.h>
-#include <brcmu_wifi.h>
-#include <brcmu_utils.h>
-#include <soc.h>
-#include "dhd_dbg.h"
-#include "sdio_host.h"
-#include "sdio_chip.h"
-
-/* chip core base & ramsize */
-/* bcm4329 */
-/* SDIO device core, ID 0x829 */
-#define BCM4329_CORE_BUS_BASE          0x18011000
-/* internal memory core, ID 0x80e */
-#define BCM4329_CORE_SOCRAM_BASE       0x18003000
-/* ARM Cortex M3 core, ID 0x82a */
-#define BCM4329_CORE_ARM_BASE          0x18002000
-#define BCM4329_RAMSIZE                        0x48000
-
-/* bcm43143 */
-/* SDIO device core */
-#define BCM43143_CORE_BUS_BASE         0x18002000
-/* internal memory core */
-#define BCM43143_CORE_SOCRAM_BASE      0x18004000
-/* ARM Cortex M3 core, ID 0x82a */
-#define BCM43143_CORE_ARM_BASE         0x18003000
-#define BCM43143_RAMSIZE               0x70000
-
-/* All D11 cores, ID 0x812 */
-#define BCM43xx_CORE_D11_BASE          0x18001000
-
-#define        SBCOREREV(sbidh) \
-       ((((sbidh) & SSB_IDHIGH_RCHI) >> SSB_IDHIGH_RCHI_SHIFT) | \
-         ((sbidh) & SSB_IDHIGH_RCLO))
-
-/* SOC Interconnect types (aka chip types) */
-#define SOCI_SB                0
-#define SOCI_AI                1
-
-/* EROM CompIdentB */
-#define CIB_REV_MASK           0xff000000
-#define CIB_REV_SHIFT          24
-
-/* ARM CR4 core specific control flag bits */
-#define ARMCR4_BCMA_IOCTL_CPUHALT      0x0020
-
-/* D11 core specific control flag bits */
-#define D11_BCMA_IOCTL_PHYCLOCKEN      0x0004
-#define D11_BCMA_IOCTL_PHYRESET                0x0008
-
-#define SDIOD_DRVSTR_KEY(chip, pmu)     (((chip) << 16) | (pmu))
-/* SDIO Pad drive strength to select value mappings */
-struct sdiod_drive_str {
-       u8 strength;    /* Pad Drive Strength in mA */
-       u8 sel;         /* Chip-specific select value */
-};
-/* SDIO Drive Strength to sel value table for PMU Rev 11 (1.8V) */
-static const struct sdiod_drive_str sdiod_drvstr_tab1_1v8[] = {
-       {32, 0x6},
-       {26, 0x7},
-       {22, 0x4},
-       {16, 0x5},
-       {12, 0x2},
-       {8, 0x3},
-       {4, 0x0},
-       {0, 0x1}
-};
-
-/* SDIO Drive Strength to sel value table for PMU Rev 13 (1.8v) */
-static const struct sdiod_drive_str sdiod_drive_strength_tab5_1v8[] = {
-        {6, 0x7},
-        {5, 0x6},
-        {4, 0x5},
-        {3, 0x4},
-        {2, 0x2},
-        {1, 0x1},
-        {0, 0x0}
-};
-
-/* SDIO Drive Strength to sel value table for PMU Rev 17 (1.8v) */
-static const struct sdiod_drive_str sdiod_drvstr_tab6_1v8[] = {
-       {3, 0x3},
-       {2, 0x2},
-       {1, 0x1},
-       {0, 0x0} };
-
-/* SDIO Drive Strength to sel value table for 43143 PMU Rev 17 (3.3V) */
-static const struct sdiod_drive_str sdiod_drvstr_tab2_3v3[] = {
-       {16, 0x7},
-       {12, 0x5},
-       {8,  0x3},
-       {4,  0x1}
-};
-
-u8
-brcmf_sdio_chip_getinfidx(struct brcmf_chip *ci, u16 coreid)
-{
-       u8 idx;
-
-       for (idx = 0; idx < BRCMF_MAX_CORENUM; idx++)
-               if (coreid == ci->c_inf[idx].id)
-                       return idx;
-
-       return BRCMF_MAX_CORENUM;
-}
-
-static u32
-brcmf_sdio_sb_corerev(struct brcmf_sdio_dev *sdiodev,
-                     struct brcmf_chip *ci, u16 coreid)
-{
-       u32 regdata;
-       u8 idx;
-
-       idx = brcmf_sdio_chip_getinfidx(ci, coreid);
-
-       regdata = brcmf_sdiod_regrl(sdiodev,
-                                   CORE_SB(ci->c_inf[idx].base, sbidhigh),
-                                   NULL);
-       return SBCOREREV(regdata);
-}
-
-static u32
-brcmf_sdio_ai_corerev(struct brcmf_sdio_dev *sdiodev,
-                     struct brcmf_chip *ci, u16 coreid)
-{
-       u8 idx;
-
-       idx = brcmf_sdio_chip_getinfidx(ci, coreid);
-
-       return (ci->c_inf[idx].cib & CIB_REV_MASK) >> CIB_REV_SHIFT;
-}
-
-static bool
-brcmf_sdio_sb_iscoreup(struct brcmf_sdio_dev *sdiodev,
-                      struct brcmf_chip *ci, u16 coreid)
-{
-       u32 regdata;
-       u8 idx;
-
-       idx = brcmf_sdio_chip_getinfidx(ci, coreid);
-       if (idx == BRCMF_MAX_CORENUM)
-               return false;
-
-       regdata = brcmf_sdiod_regrl(sdiodev,
-                                   CORE_SB(ci->c_inf[idx].base, sbtmstatelow),
-                                   NULL);
-       regdata &= (SSB_TMSLOW_RESET | SSB_TMSLOW_REJECT |
-                   SSB_IMSTATE_REJECT | SSB_TMSLOW_CLOCK);
-       return (SSB_TMSLOW_CLOCK == regdata);
-}
-
-static bool
-brcmf_sdio_ai_iscoreup(struct brcmf_sdio_dev *sdiodev,
-                      struct brcmf_chip *ci, u16 coreid)
-{
-       u32 regdata;
-       u8 idx;
-       bool ret;
-
-       idx = brcmf_sdio_chip_getinfidx(ci, coreid);
-       if (idx == BRCMF_MAX_CORENUM)
-               return false;
-
-       regdata = brcmf_sdiod_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL,
-                                   NULL);
-       ret = (regdata & (BCMA_IOCTL_FGC | BCMA_IOCTL_CLK)) == BCMA_IOCTL_CLK;
-
-       regdata = brcmf_sdiod_regrl(sdiodev,
-                                   ci->c_inf[idx].wrapbase+BCMA_RESET_CTL,
-                                   NULL);
-       ret = ret && ((regdata & BCMA_RESET_CTL_RESET) == 0);
-
-       return ret;
-}
-
-static void
-brcmf_sdio_sb_coredisable(struct brcmf_sdio_dev *sdiodev,
-                         struct brcmf_chip *ci, u16 coreid, u32 pre_resetbits,
-                         u32 in_resetbits)
-{
-       u32 regdata, base;
-       u8 idx;
-
-       idx = brcmf_sdio_chip_getinfidx(ci, coreid);
-       base = ci->c_inf[idx].base;
-
-       regdata = brcmf_sdiod_regrl(sdiodev, CORE_SB(base, sbtmstatelow), NULL);
-       if (regdata & SSB_TMSLOW_RESET)
-               return;
-
-       regdata = brcmf_sdiod_regrl(sdiodev, CORE_SB(base, sbtmstatelow), NULL);
-       if ((regdata & SSB_TMSLOW_CLOCK) != 0) {
-               /*
-                * set target reject and spin until busy is clear
-                * (preserve core-specific bits)
-                */
-               regdata = brcmf_sdiod_regrl(sdiodev,
-                                           CORE_SB(base, sbtmstatelow), NULL);
-               brcmf_sdiod_regwl(sdiodev, CORE_SB(base, sbtmstatelow),
-                                 regdata | SSB_TMSLOW_REJECT, NULL);
-
-               regdata = brcmf_sdiod_regrl(sdiodev,
-                                           CORE_SB(base, sbtmstatelow), NULL);
-               udelay(1);
-               SPINWAIT((brcmf_sdiod_regrl(sdiodev,
-                                           CORE_SB(base, sbtmstatehigh),
-                                           NULL) &
-                         SSB_TMSHIGH_BUSY), 100000);
-
-               regdata = brcmf_sdiod_regrl(sdiodev,
-                                           CORE_SB(base, sbtmstatehigh),
-                                           NULL);
-               if (regdata & SSB_TMSHIGH_BUSY)
-                       brcmf_err("core state still busy\n");
-
-               regdata = brcmf_sdiod_regrl(sdiodev, CORE_SB(base, sbidlow),
-                                           NULL);
-               if (regdata & SSB_IDLOW_INITIATOR) {
-                       regdata = brcmf_sdiod_regrl(sdiodev,
-                                                   CORE_SB(base, sbimstate),
-                                                   NULL);
-                       regdata |= SSB_IMSTATE_REJECT;
-                       brcmf_sdiod_regwl(sdiodev, CORE_SB(base, sbimstate),
-                                         regdata, NULL);
-                       regdata = brcmf_sdiod_regrl(sdiodev,
-                                                   CORE_SB(base, sbimstate),
-                                                   NULL);
-                       udelay(1);
-                       SPINWAIT((brcmf_sdiod_regrl(sdiodev,
-                                                   CORE_SB(base, sbimstate),
-                                                   NULL) &
-                                 SSB_IMSTATE_BUSY), 100000);
-               }
-
-               /* set reset and reject while enabling the clocks */
-               regdata = SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK |
-                         SSB_TMSLOW_REJECT | SSB_TMSLOW_RESET;
-               brcmf_sdiod_regwl(sdiodev, CORE_SB(base, sbtmstatelow),
-                                 regdata, NULL);
-               regdata = brcmf_sdiod_regrl(sdiodev,
-                                           CORE_SB(base, sbtmstatelow), NULL);
-               udelay(10);
-
-               /* clear the initiator reject bit */
-               regdata = brcmf_sdiod_regrl(sdiodev, CORE_SB(base, sbidlow),
-                                           NULL);
-               if (regdata & SSB_IDLOW_INITIATOR) {
-                       regdata = brcmf_sdiod_regrl(sdiodev,
-                                                   CORE_SB(base, sbimstate),
-                                                   NULL);
-                       regdata &= ~SSB_IMSTATE_REJECT;
-                       brcmf_sdiod_regwl(sdiodev, CORE_SB(base, sbimstate),
-                                         regdata, NULL);
-               }
-       }
-
-       /* leave reset and reject asserted */
-       brcmf_sdiod_regwl(sdiodev, CORE_SB(base, sbtmstatelow),
-                         (SSB_TMSLOW_REJECT | SSB_TMSLOW_RESET), NULL);
-       udelay(1);
-}
-
-static void
-brcmf_sdio_ai_coredisable(struct brcmf_sdio_dev *sdiodev,
-                         struct brcmf_chip *ci, u16 coreid, u32 pre_resetbits,
-                         u32 in_resetbits)
-{
-       u8 idx;
-       u32 regdata;
-       u32 wrapbase;
-
-       idx = brcmf_sdio_chip_getinfidx(ci, coreid);
-       if (idx == BRCMF_MAX_CORENUM)
-               return;
-
-       wrapbase = ci->c_inf[idx].wrapbase;
-
-       /* if core is already in reset, just return */
-       regdata = brcmf_sdiod_regrl(sdiodev, wrapbase + BCMA_RESET_CTL, NULL);
-       if ((regdata & BCMA_RESET_CTL_RESET) != 0)
-               return;
-
-       /* configure reset */
-       brcmf_sdiod_regwl(sdiodev, wrapbase + BCMA_IOCTL, pre_resetbits |
-                         BCMA_IOCTL_FGC | BCMA_IOCTL_CLK, NULL);
-       regdata = brcmf_sdiod_regrl(sdiodev, wrapbase + BCMA_IOCTL, NULL);
-
-       /* put in reset */
-       brcmf_sdiod_regwl(sdiodev, wrapbase + BCMA_RESET_CTL,
-                         BCMA_RESET_CTL_RESET, NULL);
-       usleep_range(10, 20);
-
-       /* wait till reset is 1 */
-       SPINWAIT(brcmf_sdiod_regrl(sdiodev, wrapbase + BCMA_RESET_CTL, NULL) !=
-                BCMA_RESET_CTL_RESET, 300);
-
-       /* post reset configure */
-       brcmf_sdiod_regwl(sdiodev, wrapbase + BCMA_IOCTL, pre_resetbits |
-                         BCMA_IOCTL_FGC | BCMA_IOCTL_CLK, NULL);
-       regdata = brcmf_sdiod_regrl(sdiodev, wrapbase + BCMA_IOCTL, NULL);
-}
-
-static void
-brcmf_sdio_sb_resetcore(struct brcmf_sdio_dev *sdiodev,
-                       struct brcmf_chip *ci, u16 coreid,  u32 pre_resetbits,
-                       u32 in_resetbits, u32 post_resetbits)
-{
-       u32 regdata;
-       u8 idx;
-
-       idx = brcmf_sdio_chip_getinfidx(ci, coreid);
-       if (idx == BRCMF_MAX_CORENUM)
-               return;
-
-       /*
-        * Must do the disable sequence first to work for
-        * arbitrary current core state.
-        */
-       brcmf_sdio_sb_coredisable(sdiodev, ci, coreid, pre_resetbits,
-                                 in_resetbits);
-
-       /*
-        * Now do the initialization sequence.
-        * set reset while enabling the clock and
-        * forcing them on throughout the core
-        */
-       brcmf_sdiod_regwl(sdiodev,
-                         CORE_SB(ci->c_inf[idx].base, sbtmstatelow),
-                         SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK | SSB_TMSLOW_RESET,
-                         NULL);
-       regdata = brcmf_sdiod_regrl(sdiodev,
-                                   CORE_SB(ci->c_inf[idx].base, sbtmstatelow),
-                                   NULL);
-       udelay(1);
-
-       /* clear any serror */
-       regdata = brcmf_sdiod_regrl(sdiodev,
-                                   CORE_SB(ci->c_inf[idx].base, sbtmstatehigh),
-                                   NULL);
-       if (regdata & SSB_TMSHIGH_SERR)
-               brcmf_sdiod_regwl(sdiodev,
-                                 CORE_SB(ci->c_inf[idx].base, sbtmstatehigh),
-                                 0, NULL);
-
-       regdata = brcmf_sdiod_regrl(sdiodev,
-                                   CORE_SB(ci->c_inf[idx].base, sbimstate),
-                                   NULL);
-       if (regdata & (SSB_IMSTATE_IBE | SSB_IMSTATE_TO))
-               brcmf_sdiod_regwl(sdiodev,
-                                 CORE_SB(ci->c_inf[idx].base, sbimstate),
-                                 regdata & ~(SSB_IMSTATE_IBE | SSB_IMSTATE_TO),
-                                 NULL);
-
-       /* clear reset and allow it to propagate throughout the core */
-       brcmf_sdiod_regwl(sdiodev, CORE_SB(ci->c_inf[idx].base, sbtmstatelow),
-                         SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK, NULL);
-       regdata = brcmf_sdiod_regrl(sdiodev,
-                                   CORE_SB(ci->c_inf[idx].base, sbtmstatelow),
-                                   NULL);
-       udelay(1);
-
-       /* leave clock enabled */
-       brcmf_sdiod_regwl(sdiodev, CORE_SB(ci->c_inf[idx].base, sbtmstatelow),
-                         SSB_TMSLOW_CLOCK, NULL);
-       regdata = brcmf_sdiod_regrl(sdiodev,
-                                   CORE_SB(ci->c_inf[idx].base, sbtmstatelow),
-                                   NULL);
-       udelay(1);
-}
-
-static void
-brcmf_sdio_ai_resetcore(struct brcmf_sdio_dev *sdiodev,
-                       struct brcmf_chip *ci, u16 coreid, u32 pre_resetbits,
-                       u32 in_resetbits, u32 post_resetbits)
-{
-       u8 idx;
-       u32 regdata;
-       u32 wrapbase;
-
-       idx = brcmf_sdio_chip_getinfidx(ci, coreid);
-       if (idx == BRCMF_MAX_CORENUM)
-               return;
-
-       wrapbase = ci->c_inf[idx].wrapbase;
-
-       /* must disable first to work for arbitrary current core state */
-       brcmf_sdio_ai_coredisable(sdiodev, ci, coreid, pre_resetbits,
-                                 in_resetbits);
-
-       while (brcmf_sdiod_regrl(sdiodev, wrapbase + BCMA_RESET_CTL, NULL) &
-              BCMA_RESET_CTL_RESET) {
-               brcmf_sdiod_regwl(sdiodev, wrapbase + BCMA_RESET_CTL, 0, NULL);
-               usleep_range(40, 60);
-       }
-
-       brcmf_sdiod_regwl(sdiodev, wrapbase + BCMA_IOCTL, post_resetbits |
-                         BCMA_IOCTL_CLK, NULL);
-       regdata = brcmf_sdiod_regrl(sdiodev, wrapbase + BCMA_IOCTL, NULL);
-}
-
-#ifdef DEBUG
-/* safety check for chipinfo */
-static int brcmf_sdio_chip_cichk(struct brcmf_chip *ci)
-{
-       u8 core_idx;
-
-       /* check RAM core presence for ARM CM3 core */
-       core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_ARM_CM3);
-       if (BRCMF_MAX_CORENUM != core_idx) {
-               core_idx = brcmf_sdio_chip_getinfidx(ci,
-                                                    BCMA_CORE_INTERNAL_MEM);
-               if (BRCMF_MAX_CORENUM == core_idx) {
-                       brcmf_err("RAM core not provided with ARM CM3 core\n");
-                       return -ENODEV;
-               }
-       }
-
-       /* check RAM base for ARM CR4 core */
-       core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_ARM_CR4);
-       if (BRCMF_MAX_CORENUM != core_idx) {
-               if (ci->rambase == 0) {
-                       brcmf_err("RAM base not provided with ARM CR4 core\n");
-                       return -ENOMEM;
-               }
-       }
-
-       return 0;
-}
-#else  /* DEBUG */
-static inline int brcmf_sdio_chip_cichk(struct brcmf_chip *ci)
-{
-       return 0;
-}
-#endif
-
-static int brcmf_sdio_chip_recognition(struct brcmf_sdio_dev *sdiodev,
-                                      struct brcmf_chip *ci)
-{
-       u32 regdata;
-       u32 socitype;
-
-       /* Get CC core rev
-        * Chipid is assume to be at offset 0 from SI_ENUM_BASE
-        * For different chiptypes or old sdio hosts w/o chipcommon,
-        * other ways of recognition should be added here.
-        */
-       regdata = brcmf_sdiod_regrl(sdiodev,
-                                   CORE_CC_REG(SI_ENUM_BASE, chipid),
-                                   NULL);
-       ci->chip = regdata & CID_ID_MASK;
-       ci->chiprev = (regdata & CID_REV_MASK) >> CID_REV_SHIFT;
-       if (sdiodev->func[0]->device == SDIO_DEVICE_ID_BROADCOM_4335_4339 &&
-           ci->chiprev >= 2)
-               ci->chip = BCM4339_CHIP_ID;
-       socitype = (regdata & CID_TYPE_MASK) >> CID_TYPE_SHIFT;
-
-       brcmf_dbg(INFO, "found %s chip: id=0x%x, rev=%d\n",
-                 socitype == SOCI_SB ? "SB" : "AXI", ci->chip, ci->chiprev);
-
-       if (socitype == SOCI_SB) {
-               if (ci->chip != BCM4329_CHIP_ID) {
-                       brcmf_err("SB chip is not supported\n");
-                       return -ENODEV;
-               }
-               ci->iscoreup = brcmf_sdio_sb_iscoreup;
-               ci->corerev = brcmf_sdio_sb_corerev;
-               ci->coredisable = brcmf_sdio_sb_coredisable;
-               ci->resetcore = brcmf_sdio_sb_resetcore;
-
-               ci->c_inf[0].id = BCMA_CORE_CHIPCOMMON;
-               ci->c_inf[0].base = SI_ENUM_BASE;
-               ci->c_inf[1].id = BCMA_CORE_SDIO_DEV;
-               ci->c_inf[1].base = BCM4329_CORE_BUS_BASE;
-               ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM;
-               ci->c_inf[2].base = BCM4329_CORE_SOCRAM_BASE;
-               ci->c_inf[3].id = BCMA_CORE_ARM_CM3;
-               ci->c_inf[3].base = BCM4329_CORE_ARM_BASE;
-               ci->c_inf[4].id = BCMA_CORE_80211;
-               ci->c_inf[4].base = BCM43xx_CORE_D11_BASE;
-               ci->ramsize = BCM4329_RAMSIZE;
-       } else if (socitype == SOCI_AI) {
-               ci->iscoreup = brcmf_sdio_ai_iscoreup;
-               ci->corerev = brcmf_sdio_ai_corerev;
-               ci->coredisable = brcmf_sdio_ai_coredisable;
-               ci->resetcore = brcmf_sdio_ai_resetcore;
-
-               ci->c_inf[0].id = BCMA_CORE_CHIPCOMMON;
-               ci->c_inf[0].base = SI_ENUM_BASE;
-
-               /* Address of cores for new chips should be added here */
-               switch (ci->chip) {
-               case BCM43143_CHIP_ID:
-                       ci->c_inf[0].wrapbase = ci->c_inf[0].base + 0x00100000;
-                       ci->c_inf[0].cib = 0x2b000000;
-                       ci->c_inf[1].id = BCMA_CORE_SDIO_DEV;
-                       ci->c_inf[1].base = BCM43143_CORE_BUS_BASE;
-                       ci->c_inf[1].wrapbase = ci->c_inf[1].base + 0x00100000;
-                       ci->c_inf[1].cib = 0x18000000;
-                       ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM;
-                       ci->c_inf[2].base = BCM43143_CORE_SOCRAM_BASE;
-                       ci->c_inf[2].wrapbase = ci->c_inf[2].base + 0x00100000;
-                       ci->c_inf[2].cib = 0x14000000;
-                       ci->c_inf[3].id = BCMA_CORE_ARM_CM3;
-                       ci->c_inf[3].base = BCM43143_CORE_ARM_BASE;
-                       ci->c_inf[3].wrapbase = ci->c_inf[3].base + 0x00100000;
-                       ci->c_inf[3].cib = 0x07000000;
-                       ci->c_inf[4].id = BCMA_CORE_80211;
-                       ci->c_inf[4].base = BCM43xx_CORE_D11_BASE;
-                       ci->c_inf[4].wrapbase = ci->c_inf[4].base + 0x00100000;
-                       ci->ramsize = BCM43143_RAMSIZE;
-                       break;
-               case BCM43241_CHIP_ID:
-                       ci->c_inf[0].wrapbase = 0x18100000;
-                       ci->c_inf[0].cib = 0x2a084411;
-                       ci->c_inf[1].id = BCMA_CORE_SDIO_DEV;
-                       ci->c_inf[1].base = 0x18002000;
-                       ci->c_inf[1].wrapbase = 0x18102000;
-                       ci->c_inf[1].cib = 0x0e004211;
-                       ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM;
-                       ci->c_inf[2].base = 0x18004000;
-                       ci->c_inf[2].wrapbase = 0x18104000;
-                       ci->c_inf[2].cib = 0x14080401;
-                       ci->c_inf[3].id = BCMA_CORE_ARM_CM3;
-                       ci->c_inf[3].base = 0x18003000;
-                       ci->c_inf[3].wrapbase = 0x18103000;
-                       ci->c_inf[3].cib = 0x07004211;
-                       ci->c_inf[4].id = BCMA_CORE_80211;
-                       ci->c_inf[4].base = BCM43xx_CORE_D11_BASE;
-                       ci->c_inf[4].wrapbase = ci->c_inf[4].base + 0x00100000;
-                       ci->ramsize = 0x90000;
-                       break;
-               case BCM4330_CHIP_ID:
-                       ci->c_inf[0].wrapbase = 0x18100000;
-                       ci->c_inf[0].cib = 0x27004211;
-                       ci->c_inf[1].id = BCMA_CORE_SDIO_DEV;
-                       ci->c_inf[1].base = 0x18002000;
-                       ci->c_inf[1].wrapbase = 0x18102000;
-                       ci->c_inf[1].cib = 0x07004211;
-                       ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM;
-                       ci->c_inf[2].base = 0x18004000;
-                       ci->c_inf[2].wrapbase = 0x18104000;
-                       ci->c_inf[2].cib = 0x0d080401;
-                       ci->c_inf[3].id = BCMA_CORE_ARM_CM3;
-                       ci->c_inf[3].base = 0x18003000;
-                       ci->c_inf[3].wrapbase = 0x18103000;
-                       ci->c_inf[3].cib = 0x03004211;
-                       ci->c_inf[4].id = BCMA_CORE_80211;
-                       ci->c_inf[4].base = BCM43xx_CORE_D11_BASE;
-                       ci->c_inf[4].wrapbase = ci->c_inf[4].base + 0x00100000;
-                       ci->ramsize = 0x48000;
-                       break;
-               case BCM4334_CHIP_ID:
-                       ci->c_inf[0].wrapbase = 0x18100000;
-                       ci->c_inf[0].cib = 0x29004211;
-                       ci->c_inf[1].id = BCMA_CORE_SDIO_DEV;
-                       ci->c_inf[1].base = 0x18002000;
-                       ci->c_inf[1].wrapbase = 0x18102000;
-                       ci->c_inf[1].cib = 0x0d004211;
-                       ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM;
-                       ci->c_inf[2].base = 0x18004000;
-                       ci->c_inf[2].wrapbase = 0x18104000;
-                       ci->c_inf[2].cib = 0x13080401;
-                       ci->c_inf[3].id = BCMA_CORE_ARM_CM3;
-                       ci->c_inf[3].base = 0x18003000;
-                       ci->c_inf[3].wrapbase = 0x18103000;
-                       ci->c_inf[3].cib = 0x07004211;
-                       ci->c_inf[4].id = BCMA_CORE_80211;
-                       ci->c_inf[4].base = BCM43xx_CORE_D11_BASE;
-                       ci->c_inf[4].wrapbase = ci->c_inf[4].base + 0x00100000;
-                       ci->ramsize = 0x80000;
-                       break;
-               case BCM4335_CHIP_ID:
-                       ci->c_inf[0].wrapbase = 0x18100000;
-                       ci->c_inf[0].cib = 0x2b084411;
-                       ci->c_inf[1].id = BCMA_CORE_SDIO_DEV;
-                       ci->c_inf[1].base = 0x18005000;
-                       ci->c_inf[1].wrapbase = 0x18105000;
-                       ci->c_inf[1].cib = 0x0f004211;
-                       ci->c_inf[2].id = BCMA_CORE_ARM_CR4;
-                       ci->c_inf[2].base = 0x18002000;
-                       ci->c_inf[2].wrapbase = 0x18102000;
-                       ci->c_inf[2].cib = 0x01084411;
-                       ci->c_inf[3].id = BCMA_CORE_80211;
-                       ci->c_inf[3].base = BCM43xx_CORE_D11_BASE;
-                       ci->c_inf[3].wrapbase = ci->c_inf[3].base + 0x00100000;
-                       ci->ramsize = 0xc0000;
-                       ci->rambase = 0x180000;
-                       break;
-               case BCM43362_CHIP_ID:
-                       ci->c_inf[0].wrapbase = 0x18100000;
-                       ci->c_inf[0].cib = 0x27004211;
-                       ci->c_inf[1].id = BCMA_CORE_SDIO_DEV;
-                       ci->c_inf[1].base = 0x18002000;
-                       ci->c_inf[1].wrapbase = 0x18102000;
-                       ci->c_inf[1].cib = 0x0a004211;
-                       ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM;
-                       ci->c_inf[2].base = 0x18004000;
-                       ci->c_inf[2].wrapbase = 0x18104000;
-                       ci->c_inf[2].cib = 0x08080401;
-                       ci->c_inf[3].id = BCMA_CORE_ARM_CM3;
-                       ci->c_inf[3].base = 0x18003000;
-                       ci->c_inf[3].wrapbase = 0x18103000;
-                       ci->c_inf[3].cib = 0x03004211;
-                       ci->c_inf[4].id = BCMA_CORE_80211;
-                       ci->c_inf[4].base = BCM43xx_CORE_D11_BASE;
-                       ci->c_inf[4].wrapbase = ci->c_inf[4].base + 0x00100000;
-                       ci->ramsize = 0x3C000;
-                       break;
-               case BCM4339_CHIP_ID:
-                       ci->c_inf[0].wrapbase = 0x18100000;
-                       ci->c_inf[0].cib = 0x2e084411;
-                       ci->c_inf[1].id = BCMA_CORE_SDIO_DEV;
-                       ci->c_inf[1].base = 0x18005000;
-                       ci->c_inf[1].wrapbase = 0x18105000;
-                       ci->c_inf[1].cib = 0x15004211;
-                       ci->c_inf[2].id = BCMA_CORE_ARM_CR4;
-                       ci->c_inf[2].base = 0x18002000;
-                       ci->c_inf[2].wrapbase = 0x18102000;
-                       ci->c_inf[2].cib = 0x04084411;
-                       ci->c_inf[3].id = BCMA_CORE_80211;
-                       ci->c_inf[3].base = BCM43xx_CORE_D11_BASE;
-                       ci->c_inf[3].wrapbase = ci->c_inf[3].base + 0x00100000;
-                       ci->ramsize = 0xc0000;
-                       ci->rambase = 0x180000;
-                       break;
-               default:
-                       brcmf_err("AXI chip is not supported\n");
-                       return -ENODEV;
-               }
-       } else {
-               brcmf_err("chip backplane type %u is not supported\n",
-                         socitype);
-               return -ENODEV;
-       }
-
-       return brcmf_sdio_chip_cichk(ci);
-}
-
-static int
-brcmf_sdio_chip_buscoreprep(struct brcmf_sdio_dev *sdiodev)
-{
-       int err = 0;
-       u8 clkval, clkset;
-
-       /* Try forcing SDIO core to do ALPAvail request only */
-       clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ;
-       brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err);
-       if (err) {
-               brcmf_err("error writing for HT off\n");
-               return err;
-       }
-
-       /* If register supported, wait for ALPAvail and then force ALP */
-       /* This may take up to 15 milliseconds */
-       clkval = brcmf_sdiod_regrb(sdiodev,
-                                  SBSDIO_FUNC1_CHIPCLKCSR, NULL);
-
-       if ((clkval & ~SBSDIO_AVBITS) != clkset) {
-               brcmf_err("ChipClkCSR access: wrote 0x%02x read 0x%02x\n",
-                         clkset, clkval);
-               return -EACCES;
-       }
-
-       SPINWAIT(((clkval = brcmf_sdiod_regrb(sdiodev,
-                                             SBSDIO_FUNC1_CHIPCLKCSR, NULL)),
-                       !SBSDIO_ALPAV(clkval)),
-                       PMU_MAX_TRANSITION_DLY);
-       if (!SBSDIO_ALPAV(clkval)) {
-               brcmf_err("timeout on ALPAV wait, clkval 0x%02x\n",
-                         clkval);
-               return -EBUSY;
-       }
-
-       clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_FORCE_ALP;
-       brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err);
-       udelay(65);
-
-       /* Also, disable the extra SDIO pull-ups */
-       brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_SDIOPULLUP, 0, NULL);
-
-       return 0;
-}
-
-static void
-brcmf_sdio_chip_buscoresetup(struct brcmf_sdio_dev *sdiodev,
-                            struct brcmf_chip *ci)
-{
-       u32 base = ci->c_inf[0].base;
-
-       /* get chipcommon rev */
-       ci->c_inf[0].rev = ci->corerev(sdiodev, ci, ci->c_inf[0].id);
-
-       /* get chipcommon capabilites */
-       ci->c_inf[0].caps = brcmf_sdiod_regrl(sdiodev,
-                                             CORE_CC_REG(base, capabilities),
-                                             NULL);
-
-       /* get pmu caps & rev */
-       if (ci->c_inf[0].caps & CC_CAP_PMU) {
-               ci->pmucaps =
-                       brcmf_sdiod_regrl(sdiodev,
-                                         CORE_CC_REG(base, pmucapabilities),
-                                         NULL);
-               ci->pmurev = ci->pmucaps & PCAP_REV_MASK;
-       }
-
-       ci->c_inf[1].rev = ci->corerev(sdiodev, ci, ci->c_inf[1].id);
-
-       brcmf_dbg(INFO, "ccrev=%d, pmurev=%d, buscore rev/type=%d/0x%x\n",
-                 ci->c_inf[0].rev, ci->pmurev,
-                 ci->c_inf[1].rev, ci->c_inf[1].id);
-
-       /*
-        * Make sure any on-chip ARM is off (in case strapping is wrong),
-        * or downloaded code was already running.
-        */
-       ci->coredisable(sdiodev, ci, BCMA_CORE_ARM_CM3, 0, 0);
-}
-
-int brcmf_sdio_chip_attach(struct brcmf_sdio_dev *sdiodev,
-                          struct brcmf_chip **ci_ptr)
-{
-       int ret;
-       struct brcmf_chip *ci;
-
-       brcmf_dbg(TRACE, "Enter\n");
-
-       ci = kzalloc(sizeof(*ci), GFP_ATOMIC);
-       if (!ci)
-               return -ENOMEM;
-
-       ret = brcmf_sdio_chip_buscoreprep(sdiodev);
-       if (ret != 0)
-               goto err;
-
-       ret = brcmf_sdio_chip_recognition(sdiodev, ci);
-       if (ret != 0)
-               goto err;
-
-       brcmf_sdio_chip_buscoresetup(sdiodev, ci);
-
-       brcmf_sdiod_regwl(sdiodev, CORE_CC_REG(ci->c_inf[0].base, gpiopullup),
-                         0, NULL);
-       brcmf_sdiod_regwl(sdiodev, CORE_CC_REG(ci->c_inf[0].base, gpiopulldown),
-                         0, NULL);
-
-       *ci_ptr = ci;
-       return 0;
-
-err:
-       kfree(ci);
-       return ret;
-}
-
-void
-brcmf_sdio_chip_detach(struct brcmf_chip **ci_ptr)
-{
-       brcmf_dbg(TRACE, "Enter\n");
-
-       kfree(*ci_ptr);
-       *ci_ptr = NULL;
-}
-
-static char *brcmf_sdio_chip_name(uint chipid, char *buf, uint len)
-{
-       const char *fmt;
-
-       fmt = ((chipid > 0xa000) || (chipid < 0x4000)) ? "%d" : "%x";
-       snprintf(buf, len, fmt, chipid);
-       return buf;
-}
-
-void
-brcmf_sdio_chip_drivestrengthinit(struct brcmf_sdio_dev *sdiodev,
-                                 struct brcmf_chip *ci, u32 drivestrength)
-{
-       const struct sdiod_drive_str *str_tab = NULL;
-       u32 str_mask;
-       u32 str_shift;
-       char chn[8];
-       u32 base = ci->c_inf[0].base;
-       u32 i;
-       u32 drivestrength_sel = 0;
-       u32 cc_data_temp;
-       u32 addr;
-
-       if (!(ci->c_inf[0].caps & CC_CAP_PMU))
-               return;
-
-       switch (SDIOD_DRVSTR_KEY(ci->chip, ci->pmurev)) {
-       case SDIOD_DRVSTR_KEY(BCM4330_CHIP_ID, 12):
-               str_tab = sdiod_drvstr_tab1_1v8;
-               str_mask = 0x00003800;
-               str_shift = 11;
-               break;
-       case SDIOD_DRVSTR_KEY(BCM4334_CHIP_ID, 17):
-               str_tab = sdiod_drvstr_tab6_1v8;
-               str_mask = 0x00001800;
-               str_shift = 11;
-               break;
-       case SDIOD_DRVSTR_KEY(BCM43143_CHIP_ID, 17):
-               /* note: 43143 does not support tristate */
-               i = ARRAY_SIZE(sdiod_drvstr_tab2_3v3) - 1;
-               if (drivestrength >= sdiod_drvstr_tab2_3v3[i].strength) {
-                       str_tab = sdiod_drvstr_tab2_3v3;
-                       str_mask = 0x00000007;
-                       str_shift = 0;
-               } else
-                       brcmf_err("Invalid SDIO Drive strength for chip %s, strength=%d\n",
-                                 brcmf_sdio_chip_name(ci->chip, chn, 8),
-                                 drivestrength);
-               break;
-       case SDIOD_DRVSTR_KEY(BCM43362_CHIP_ID, 13):
-               str_tab = sdiod_drive_strength_tab5_1v8;
-               str_mask = 0x00003800;
-               str_shift = 11;
-               break;
-       default:
-               brcmf_err("No SDIO Drive strength init done for chip %s rev %d pmurev %d\n",
-                         brcmf_sdio_chip_name(ci->chip, chn, 8),
-                         ci->chiprev, ci->pmurev);
-               break;
-       }
-
-       if (str_tab != NULL) {
-               for (i = 0; str_tab[i].strength != 0; i++) {
-                       if (drivestrength >= str_tab[i].strength) {
-                               drivestrength_sel = str_tab[i].sel;
-                               break;
-                       }
-               }
-               addr = CORE_CC_REG(base, chipcontrol_addr);
-               brcmf_sdiod_regwl(sdiodev, addr, 1, NULL);
-               cc_data_temp = brcmf_sdiod_regrl(sdiodev, addr, NULL);
-               cc_data_temp &= ~str_mask;
-               drivestrength_sel <<= str_shift;
-               cc_data_temp |= drivestrength_sel;
-               brcmf_sdiod_regwl(sdiodev, addr, cc_data_temp, NULL);
-
-               brcmf_dbg(INFO, "SDIO: %d mA (req=%d mA) drive strength selected, set to 0x%08x\n",
-                         str_tab[i].strength, drivestrength, cc_data_temp);
-       }
-}
-
-static void
-brcmf_sdio_chip_cm3_enterdl(struct brcmf_sdio_dev *sdiodev,
-                           struct brcmf_chip *ci)
-{
-       ci->coredisable(sdiodev, ci, BCMA_CORE_ARM_CM3, 0, 0);
-       ci->resetcore(sdiodev, ci, BCMA_CORE_80211,
-                     D11_BCMA_IOCTL_PHYRESET | D11_BCMA_IOCTL_PHYCLOCKEN,
-                     D11_BCMA_IOCTL_PHYCLOCKEN, D11_BCMA_IOCTL_PHYCLOCKEN);
-       ci->resetcore(sdiodev, ci, BCMA_CORE_INTERNAL_MEM, 0, 0, 0);
-}
-
-static bool brcmf_sdio_chip_cm3_exitdl(struct brcmf_sdio_dev *sdiodev,
-                                      struct brcmf_chip *ci)
-{
-       u8 core_idx;
-       u32 reg_addr;
-
-       if (!ci->iscoreup(sdiodev, ci, BCMA_CORE_INTERNAL_MEM)) {
-               brcmf_err("SOCRAM core is down after reset?\n");
-               return false;
-       }
-
-       /* clear all interrupts */
-       core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_SDIO_DEV);
-       reg_addr = ci->c_inf[core_idx].base;
-       reg_addr += offsetof(struct sdpcmd_regs, intstatus);
-       brcmf_sdiod_regwl(sdiodev, reg_addr, 0xFFFFFFFF, NULL);
-
-       ci->resetcore(sdiodev, ci, BCMA_CORE_ARM_CM3, 0, 0, 0);
-
-       return true;
-}
-
-static inline void
-brcmf_sdio_chip_cr4_enterdl(struct brcmf_sdio_dev *sdiodev,
-                           struct brcmf_chip *ci)
-{
-       u8 idx;
-       u32 regdata;
-       u32 wrapbase;
-       idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_ARM_CR4);
-
-       if (idx == BRCMF_MAX_CORENUM)
-               return;
-
-       wrapbase = ci->c_inf[idx].wrapbase;
-       regdata = brcmf_sdiod_regrl(sdiodev, wrapbase + BCMA_IOCTL, NULL);
-       regdata &= ARMCR4_BCMA_IOCTL_CPUHALT;
-       ci->resetcore(sdiodev, ci, BCMA_CORE_ARM_CR4, regdata,
-                     ARMCR4_BCMA_IOCTL_CPUHALT, ARMCR4_BCMA_IOCTL_CPUHALT);
-       ci->resetcore(sdiodev, ci, BCMA_CORE_80211,
-                     D11_BCMA_IOCTL_PHYRESET | D11_BCMA_IOCTL_PHYCLOCKEN,
-                     D11_BCMA_IOCTL_PHYCLOCKEN, D11_BCMA_IOCTL_PHYCLOCKEN);
-}
-
-static bool brcmf_sdio_chip_cr4_exitdl(struct brcmf_sdio_dev *sdiodev,
-                                      struct brcmf_chip *ci, u32 rstvec)
-{
-       u8 core_idx;
-       u32 reg_addr;
-
-       /* clear all interrupts */
-       core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_SDIO_DEV);
-       reg_addr = ci->c_inf[core_idx].base;
-       reg_addr += offsetof(struct sdpcmd_regs, intstatus);
-       brcmf_sdiod_regwl(sdiodev, reg_addr, 0xFFFFFFFF, NULL);
-
-       /* Write reset vector to address 0 */
-       brcmf_sdiod_ramrw(sdiodev, true, 0, (void *)&rstvec,
-                         sizeof(rstvec));
-
-       /* restore ARM */
-       ci->resetcore(sdiodev, ci, BCMA_CORE_ARM_CR4, ARMCR4_BCMA_IOCTL_CPUHALT,
-                     0, 0);
-
-       return true;
-}
-
-void brcmf_sdio_chip_enter_download(struct brcmf_sdio_dev *sdiodev,
-                                   struct brcmf_chip *ci)
-{
-       u8 arm_core_idx;
-
-       arm_core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_ARM_CM3);
-       if (BRCMF_MAX_CORENUM != arm_core_idx) {
-               brcmf_sdio_chip_cm3_enterdl(sdiodev, ci);
-               return;
-       }
-
-       brcmf_sdio_chip_cr4_enterdl(sdiodev, ci);
-}
-
-bool brcmf_sdio_chip_exit_download(struct brcmf_sdio_dev *sdiodev,
-                                  struct brcmf_chip *ci, u32 rstvec)
-{
-       u8 arm_core_idx;
-
-       arm_core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_ARM_CM3);
-       if (BRCMF_MAX_CORENUM != arm_core_idx)
-               return brcmf_sdio_chip_cm3_exitdl(sdiodev, ci);
-
-       return brcmf_sdio_chip_cr4_exitdl(sdiodev, ci, rstvec);
-}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h
deleted file mode 100644 (file)
index fb06143..0000000
+++ /dev/null
@@ -1,231 +0,0 @@
-/*
- * Copyright (c) 2011 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 _BRCMFMAC_SDIO_CHIP_H_
-#define _BRCMFMAC_SDIO_CHIP_H_
-
-/*
- * Core reg address translation.
- * Both macro's returns a 32 bits byte address on the backplane bus.
- */
-#define CORE_CC_REG(base, field) \
-               (base + offsetof(struct chipcregs, field))
-#define CORE_BUS_REG(base, field) \
-               (base + offsetof(struct sdpcmd_regs, field))
-#define CORE_SB(base, field) \
-               (base + SBCONFIGOFF + offsetof(struct sbconfig, field))
-
-/* SDIO function 1 register CHIPCLKCSR */
-/* Force ALP request to backplane */
-#define SBSDIO_FORCE_ALP               0x01
-/* Force HT request to backplane */
-#define SBSDIO_FORCE_HT                        0x02
-/* Force ILP request to backplane */
-#define SBSDIO_FORCE_ILP               0x04
-/* Make ALP ready (power up xtal) */
-#define SBSDIO_ALP_AVAIL_REQ           0x08
-/* Make HT ready (power up PLL) */
-#define SBSDIO_HT_AVAIL_REQ            0x10
-/* Squelch clock requests from HW */
-#define SBSDIO_FORCE_HW_CLKREQ_OFF     0x20
-/* Status: ALP is ready */
-#define SBSDIO_ALP_AVAIL               0x40
-/* Status: HT is ready */
-#define SBSDIO_HT_AVAIL                        0x80
-#define SBSDIO_AVBITS          (SBSDIO_HT_AVAIL | SBSDIO_ALP_AVAIL)
-#define SBSDIO_ALPAV(regval)   ((regval) & SBSDIO_AVBITS)
-#define SBSDIO_HTAV(regval)    (((regval) & SBSDIO_AVBITS) == SBSDIO_AVBITS)
-#define SBSDIO_ALPONLY(regval) (SBSDIO_ALPAV(regval) && !SBSDIO_HTAV(regval))
-#define SBSDIO_CLKAV(regval, alponly) \
-       (SBSDIO_ALPAV(regval) && (alponly ? 1 : SBSDIO_HTAV(regval)))
-
-#define BRCMF_MAX_CORENUM      6
-
-struct brcmf_core {
-       u16 id;
-       u16 rev;
-       u32 base;
-       u32 wrapbase;
-       u32 caps;
-       u32 cib;
-};
-
-struct brcmf_chip {
-       u32 chip;
-       u32 chiprev;
-       /* core info */
-       /* always put chipcommon core at 0, bus core at 1 */
-       struct brcmf_core c_inf[BRCMF_MAX_CORENUM];
-       u32 pmurev;
-       u32 pmucaps;
-       u32 ramsize;
-       u32 rambase;
-       u32 rst_vec;    /* reset vertor for ARM CR4 core */
-
-       bool (*iscoreup)(struct brcmf_sdio_dev *sdiodev, struct brcmf_chip *ci,
-                        u16 coreid);
-       u32 (*corerev)(struct brcmf_sdio_dev *sdiodev, struct brcmf_chip *ci,
-                        u16 coreid);
-       void (*coredisable)(struct brcmf_sdio_dev *sdiodev,
-                       struct brcmf_chip *ci, u16 coreid, u32 pre_resetbits,
-                       u32 in_resetbits);
-       void (*resetcore)(struct brcmf_sdio_dev *sdiodev,
-                       struct brcmf_chip *ci, u16 coreid, u32 pre_resetbits,
-                       u32 in_resetbits, u32 post_resetbits);
-};
-
-struct sbconfig {
-       u32 PAD[2];
-       u32 sbipsflag;  /* initiator port ocp slave flag */
-       u32 PAD[3];
-       u32 sbtpsflag;  /* target port ocp slave flag */
-       u32 PAD[11];
-       u32 sbtmerrloga;        /* (sonics >= 2.3) */
-       u32 PAD;
-       u32 sbtmerrlog; /* (sonics >= 2.3) */
-       u32 PAD[3];
-       u32 sbadmatch3; /* address match3 */
-       u32 PAD;
-       u32 sbadmatch2; /* address match2 */
-       u32 PAD;
-       u32 sbadmatch1; /* address match1 */
-       u32 PAD[7];
-       u32 sbimstate;  /* initiator agent state */
-       u32 sbintvec;   /* interrupt mask */
-       u32 sbtmstatelow;       /* target state */
-       u32 sbtmstatehigh;      /* target state */
-       u32 sbbwa0;             /* bandwidth allocation table0 */
-       u32 PAD;
-       u32 sbimconfiglow;      /* initiator configuration */
-       u32 sbimconfighigh;     /* initiator configuration */
-       u32 sbadmatch0; /* address match0 */
-       u32 PAD;
-       u32 sbtmconfiglow;      /* target configuration */
-       u32 sbtmconfighigh;     /* target configuration */
-       u32 sbbconfig;  /* broadcast configuration */
-       u32 PAD;
-       u32 sbbstate;   /* broadcast state */
-       u32 PAD[3];
-       u32 sbactcnfg;  /* activate configuration */
-       u32 PAD[3];
-       u32 sbflagst;   /* current sbflags */
-       u32 PAD[3];
-       u32 sbidlow;            /* identification */
-       u32 sbidhigh;   /* identification */
-};
-
-/* sdio core registers */
-struct sdpcmd_regs {
-       u32 corecontrol;                /* 0x00, rev8 */
-       u32 corestatus;                 /* rev8 */
-       u32 PAD[1];
-       u32 biststatus;                 /* rev8 */
-
-       /* PCMCIA access */
-       u16 pcmciamesportaladdr;        /* 0x010, rev8 */
-       u16 PAD[1];
-       u16 pcmciamesportalmask;        /* rev8 */
-       u16 PAD[1];
-       u16 pcmciawrframebc;            /* rev8 */
-       u16 PAD[1];
-       u16 pcmciaunderflowtimer;       /* rev8 */
-       u16 PAD[1];
-
-       /* interrupt */
-       u32 intstatus;                  /* 0x020, rev8 */
-       u32 hostintmask;                /* rev8 */
-       u32 intmask;                    /* rev8 */
-       u32 sbintstatus;                /* rev8 */
-       u32 sbintmask;                  /* rev8 */
-       u32 funcintmask;                /* rev4 */
-       u32 PAD[2];
-       u32 tosbmailbox;                /* 0x040, rev8 */
-       u32 tohostmailbox;              /* rev8 */
-       u32 tosbmailboxdata;            /* rev8 */
-       u32 tohostmailboxdata;          /* rev8 */
-
-       /* synchronized access to registers in SDIO clock domain */
-       u32 sdioaccess;                 /* 0x050, rev8 */
-       u32 PAD[3];
-
-       /* PCMCIA frame control */
-       u8 pcmciaframectrl;             /* 0x060, rev8 */
-       u8 PAD[3];
-       u8 pcmciawatermark;             /* rev8 */
-       u8 PAD[155];
-
-       /* interrupt batching control */
-       u32 intrcvlazy;                 /* 0x100, rev8 */
-       u32 PAD[3];
-
-       /* counters */
-       u32 cmd52rd;                    /* 0x110, rev8 */
-       u32 cmd52wr;                    /* rev8 */
-       u32 cmd53rd;                    /* rev8 */
-       u32 cmd53wr;                    /* rev8 */
-       u32 abort;                      /* rev8 */
-       u32 datacrcerror;               /* rev8 */
-       u32 rdoutofsync;                /* rev8 */
-       u32 wroutofsync;                /* rev8 */
-       u32 writebusy;                  /* rev8 */
-       u32 readwait;                   /* rev8 */
-       u32 readterm;                   /* rev8 */
-       u32 writeterm;                  /* rev8 */
-       u32 PAD[40];
-       u32 clockctlstatus;             /* rev8 */
-       u32 PAD[7];
-
-       u32 PAD[128];                   /* DMA engines */
-
-       /* SDIO/PCMCIA CIS region */
-       char cis[512];                  /* 0x400-0x5ff, rev6 */
-
-       /* PCMCIA function control registers */
-       char pcmciafcr[256];            /* 0x600-6ff, rev6 */
-       u16 PAD[55];
-
-       /* PCMCIA backplane access */
-       u16 backplanecsr;               /* 0x76E, rev6 */
-       u16 backplaneaddr0;             /* rev6 */
-       u16 backplaneaddr1;             /* rev6 */
-       u16 backplaneaddr2;             /* rev6 */
-       u16 backplaneaddr3;             /* rev6 */
-       u16 backplanedata0;             /* rev6 */
-       u16 backplanedata1;             /* rev6 */
-       u16 backplanedata2;             /* rev6 */
-       u16 backplanedata3;             /* rev6 */
-       u16 PAD[31];
-
-       /* sprom "size" & "blank" info */
-       u16 spromstatus;                /* 0x7BE, rev2 */
-       u32 PAD[464];
-
-       u16 PAD[0x80];
-};
-
-int brcmf_sdio_chip_attach(struct brcmf_sdio_dev *sdiodev,
-                          struct brcmf_chip **ci_ptr);
-void brcmf_sdio_chip_detach(struct brcmf_chip **ci_ptr);
-void brcmf_sdio_chip_drivestrengthinit(struct brcmf_sdio_dev *sdiodev,
-                                      struct brcmf_chip *ci,
-                                      u32 drivestrength);
-u8 brcmf_sdio_chip_getinfidx(struct brcmf_chip *ci, u16 coreid);
-void brcmf_sdio_chip_enter_download(struct brcmf_sdio_dev *sdiodev,
-                                   struct brcmf_chip *ci);
-bool brcmf_sdio_chip_exit_download(struct brcmf_sdio_dev *sdiodev,
-                                  struct brcmf_chip *ci, u32 rstvec);
-
-#endif         /* _BRCMFMAC_SDIO_CHIP_H_ */
index 092e9c8..5e53eb1 100644 (file)
@@ -182,6 +182,95 @@ struct brcmf_sdio_dev {
        uint max_segment_size;
 };
 
+/* sdio core registers */
+struct sdpcmd_regs {
+       u32 corecontrol;                /* 0x00, rev8 */
+       u32 corestatus;                 /* rev8 */
+       u32 PAD[1];
+       u32 biststatus;                 /* rev8 */
+
+       /* PCMCIA access */
+       u16 pcmciamesportaladdr;        /* 0x010, rev8 */
+       u16 PAD[1];
+       u16 pcmciamesportalmask;        /* rev8 */
+       u16 PAD[1];
+       u16 pcmciawrframebc;            /* rev8 */
+       u16 PAD[1];
+       u16 pcmciaunderflowtimer;       /* rev8 */
+       u16 PAD[1];
+
+       /* interrupt */
+       u32 intstatus;                  /* 0x020, rev8 */
+       u32 hostintmask;                /* rev8 */
+       u32 intmask;                    /* rev8 */
+       u32 sbintstatus;                /* rev8 */
+       u32 sbintmask;                  /* rev8 */
+       u32 funcintmask;                /* rev4 */
+       u32 PAD[2];
+       u32 tosbmailbox;                /* 0x040, rev8 */
+       u32 tohostmailbox;              /* rev8 */
+       u32 tosbmailboxdata;            /* rev8 */
+       u32 tohostmailboxdata;          /* rev8 */
+
+       /* synchronized access to registers in SDIO clock domain */
+       u32 sdioaccess;                 /* 0x050, rev8 */
+       u32 PAD[3];
+
+       /* PCMCIA frame control */
+       u8 pcmciaframectrl;             /* 0x060, rev8 */
+       u8 PAD[3];
+       u8 pcmciawatermark;             /* rev8 */
+       u8 PAD[155];
+
+       /* interrupt batching control */
+       u32 intrcvlazy;                 /* 0x100, rev8 */
+       u32 PAD[3];
+
+       /* counters */
+       u32 cmd52rd;                    /* 0x110, rev8 */
+       u32 cmd52wr;                    /* rev8 */
+       u32 cmd53rd;                    /* rev8 */
+       u32 cmd53wr;                    /* rev8 */
+       u32 abort;                      /* rev8 */
+       u32 datacrcerror;               /* rev8 */
+       u32 rdoutofsync;                /* rev8 */
+       u32 wroutofsync;                /* rev8 */
+       u32 writebusy;                  /* rev8 */
+       u32 readwait;                   /* rev8 */
+       u32 readterm;                   /* rev8 */
+       u32 writeterm;                  /* rev8 */
+       u32 PAD[40];
+       u32 clockctlstatus;             /* rev8 */
+       u32 PAD[7];
+
+       u32 PAD[128];                   /* DMA engines */
+
+       /* SDIO/PCMCIA CIS region */
+       char cis[512];                  /* 0x400-0x5ff, rev6 */
+
+       /* PCMCIA function control registers */
+       char pcmciafcr[256];            /* 0x600-6ff, rev6 */
+       u16 PAD[55];
+
+       /* PCMCIA backplane access */
+       u16 backplanecsr;               /* 0x76E, rev6 */
+       u16 backplaneaddr0;             /* rev6 */
+       u16 backplaneaddr1;             /* rev6 */
+       u16 backplaneaddr2;             /* rev6 */
+       u16 backplaneaddr3;             /* rev6 */
+       u16 backplanedata0;             /* rev6 */
+       u16 backplanedata1;             /* rev6 */
+       u16 backplanedata2;             /* rev6 */
+       u16 backplanedata3;             /* rev6 */
+       u16 PAD[31];
+
+       /* sprom "size" & "blank" info */
+       u16 spromstatus;                /* 0x7BE, rev2 */
+       u32 PAD[464];
+
+       u16 PAD[0x80];
+};
+
 /* Register/deregister interrupt handler. */
 int brcmf_sdiod_intr_register(struct brcmf_sdio_dev *sdiodev);
 int brcmf_sdiod_intr_unregister(struct brcmf_sdio_dev *sdiodev);
index d7718a5..a54db91 100644 (file)
@@ -351,13 +351,11 @@ u16 channel_to_chanspec(struct brcmu_d11inf *d11inf,
  * triples, returning a pointer to the substring whose first element
  * matches tag
  */
-struct brcmf_tlv *brcmf_parse_tlvs(void *buf, int buflen, uint key)
+const struct brcmf_tlv *
+brcmf_parse_tlvs(const void *buf, int buflen, uint key)
 {
-       struct brcmf_tlv *elt;
-       int totlen;
-
-       elt = (struct brcmf_tlv *)buf;
-       totlen = buflen;
+       const struct brcmf_tlv *elt = buf;
+       int totlen = buflen;
 
        /* find tagged parameter */
        while (totlen >= TLV_HDR_LEN) {
@@ -378,8 +376,8 @@ struct brcmf_tlv *brcmf_parse_tlvs(void *buf, int buflen, uint key)
  * not update the tlvs buffer pointer/length.
  */
 static bool
-brcmf_tlv_has_ie(u8 *ie, u8 **tlvs, u32 *tlvs_len,
-                u8 *oui, u32 oui_len, u8 type)
+brcmf_tlv_has_ie(const u8 *ie, const u8 **tlvs, u32 *tlvs_len,
+                const u8 *oui, u32 oui_len, u8 type)
 {
        /* If the contents match the OUI and the type */
        if (ie[TLV_LEN_OFF] >= oui_len + 1 &&
@@ -401,12 +399,12 @@ brcmf_tlv_has_ie(u8 *ie, u8 **tlvs, u32 *tlvs_len,
 }
 
 static struct brcmf_vs_tlv *
-brcmf_find_wpaie(u8 *parse, u32 len)
+brcmf_find_wpaie(const u8 *parse, u32 len)
 {
-       struct brcmf_tlv *ie;
+       const struct brcmf_tlv *ie;
 
        while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) {
-               if (brcmf_tlv_has_ie((u8 *)ie, &parse, &len,
+               if (brcmf_tlv_has_ie((const u8 *)ie, &parse, &len,
                                     WPA_OUI, TLV_OUI_LEN, WPA_OUI_TYPE))
                        return (struct brcmf_vs_tlv *)ie;
        }
@@ -414,9 +412,9 @@ brcmf_find_wpaie(u8 *parse, u32 len)
 }
 
 static struct brcmf_vs_tlv *
-brcmf_find_wpsie(u8 *parse, u32 len)
+brcmf_find_wpsie(const u8 *parse, u32 len)
 {
-       struct brcmf_tlv *ie;
+       const struct brcmf_tlv *ie;
 
        while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) {
                if (brcmf_tlv_has_ie((u8 *)ie, &parse, &len,
@@ -1562,9 +1560,9 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev,
        struct ieee80211_channel *chan = sme->channel;
        struct brcmf_join_params join_params;
        size_t join_params_size;
-       struct brcmf_tlv *rsn_ie;
-       struct brcmf_vs_tlv *wpa_ie;
-       void *ie;
+       const struct brcmf_tlv *rsn_ie;
+       const struct brcmf_vs_tlv *wpa_ie;
+       const void *ie;
        u32 ie_len;
        struct brcmf_ext_join_params_le *ext_join_params;
        u16 chanspec;
@@ -1591,7 +1589,8 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev,
                        ie_len = wpa_ie->len + TLV_HDR_LEN;
                } else {
                        /* find the RSN_IE */
-                       rsn_ie = brcmf_parse_tlvs((u8 *)sme->ie, sme->ie_len,
+                       rsn_ie = brcmf_parse_tlvs((const u8 *)sme->ie,
+                                                 sme->ie_len,
                                                  WLAN_EID_RSN);
                        if (rsn_ie) {
                                ie = rsn_ie;
@@ -1981,7 +1980,9 @@ brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
        if (!check_vif_up(ifp->vif))
                return -EIO;
 
-       if (mac_addr) {
+       if (mac_addr &&
+               (params->cipher != WLAN_CIPHER_SUITE_WEP40) &&
+               (params->cipher != WLAN_CIPHER_SUITE_WEP104)) {
                brcmf_dbg(TRACE, "Exit");
                return brcmf_add_keyext(wiphy, ndev, key_idx, mac_addr, params);
        }
@@ -2164,6 +2165,8 @@ brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev,
        s32 err = 0;
        u8 *bssid = profile->bssid;
        struct brcmf_sta_info_le sta_info_le;
+       u32 beacon_period;
+       u32 dtim_period;
 
        brcmf_dbg(TRACE, "Enter, MAC %pM\n", mac);
        if (!check_vif_up(ifp->vif))
@@ -2218,6 +2221,30 @@ brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev,
                                sinfo->signal = rssi;
                                brcmf_dbg(CONN, "RSSI %d dBm\n", rssi);
                        }
+                       err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_BCNPRD,
+                                                   &beacon_period);
+                       if (err) {
+                               brcmf_err("Could not get beacon period (%d)\n",
+                                         err);
+                               goto done;
+                       } else {
+                               sinfo->bss_param.beacon_interval =
+                                       beacon_period;
+                               brcmf_dbg(CONN, "Beacon peroid %d\n",
+                                         beacon_period);
+                       }
+                       err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_DTIMPRD,
+                                                   &dtim_period);
+                       if (err) {
+                               brcmf_err("Could not get DTIM period (%d)\n",
+                                         err);
+                               goto done;
+                       } else {
+                               sinfo->bss_param.dtim_period = dtim_period;
+                               brcmf_dbg(CONN, "DTIM peroid %d\n",
+                                         dtim_period);
+                       }
+                       sinfo->filled |= STATION_INFO_BSS_PARAM;
                }
        } else
                err = -EPERM;
@@ -2455,7 +2482,7 @@ static s32 brcmf_update_bss_info(struct brcmf_cfg80211_info *cfg,
        struct brcmf_cfg80211_profile *profile = ndev_to_prof(ifp->ndev);
        struct brcmf_bss_info_le *bi;
        struct brcmf_ssid *ssid;
-       struct brcmf_tlv *tim;
+       const struct brcmf_tlv *tim;
        u16 beacon_interval;
        u8 dtim_period;
        size_t ie_len;
@@ -3220,8 +3247,9 @@ static bool brcmf_valid_wpa_oui(u8 *oui, bool is_rsn_ie)
 }
 
 static s32
-brcmf_configure_wpaie(struct net_device *ndev, struct brcmf_vs_tlv *wpa_ie,
-                    bool is_rsn_ie)
+brcmf_configure_wpaie(struct net_device *ndev,
+                     const struct brcmf_vs_tlv *wpa_ie,
+                     bool is_rsn_ie)
 {
        struct brcmf_if *ifp = netdev_priv(ndev);
        u32 auth = 0; /* d11 open authentication */
@@ -3707,11 +3735,11 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
        s32 ie_offset;
        struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
        struct brcmf_if *ifp = netdev_priv(ndev);
-       struct brcmf_tlv *ssid_ie;
+       const struct brcmf_tlv *ssid_ie;
        struct brcmf_ssid_le ssid_le;
        s32 err = -EPERM;
-       struct brcmf_tlv *rsn_ie;
-       struct brcmf_vs_tlv *wpa_ie;
+       const struct brcmf_tlv *rsn_ie;
+       const struct brcmf_vs_tlv *wpa_ie;
        struct brcmf_join_params join_params;
        enum nl80211_iftype dev_role;
        struct brcmf_fil_bss_enable_le bss_enable;
@@ -4658,6 +4686,7 @@ brcmf_notify_connect_status(struct brcmf_if *ifp,
        struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
        struct net_device *ndev = ifp->ndev;
        struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
+       struct ieee80211_channel *chan;
        s32 err = 0;
 
        if (ifp->vif->mode == WL_MODE_AP) {
@@ -4665,9 +4694,10 @@ brcmf_notify_connect_status(struct brcmf_if *ifp,
        } else if (brcmf_is_linkup(e)) {
                brcmf_dbg(CONN, "Linkup\n");
                if (brcmf_is_ibssmode(ifp->vif)) {
+                       chan = ieee80211_get_channel(cfg->wiphy, cfg->channel);
                        memcpy(profile->bssid, e->addr, ETH_ALEN);
                        wl_inform_ibss(cfg, ndev, e->addr);
-                       cfg80211_ibss_joined(ndev, e->addr, GFP_KERNEL);
+                       cfg80211_ibss_joined(ndev, e->addr, chan, GFP_KERNEL);
                        clear_bit(BRCMF_VIF_STATUS_CONNECTING,
                                  &ifp->vif->sme_state);
                        set_bit(BRCMF_VIF_STATUS_CONNECTED,
@@ -5164,9 +5194,6 @@ static s32 brcmf_construct_reginfo(struct brcmf_cfg80211_info *cfg,
                                ieee80211_channel_to_frequency(ch.chnum, band);
                        band_chan_arr[index].hw_value = ch.chnum;
 
-                       brcmf_err("channel %d: f=%d bw=%d sb=%d\n",
-                                 ch.chnum, band_chan_arr[index].center_freq,
-                                 ch.bw, ch.sb);
                        if (ch.bw == BRCMU_CHAN_BW_40) {
                                /* assuming the order is HT20, HT40 Upper,
                                 * HT40 lower from chanspecs
index 2dc6a07..254feed 100644 (file)
@@ -491,7 +491,8 @@ void brcmf_free_vif(struct brcmf_cfg80211_vif *vif);
 s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag,
                          const u8 *vndr_ie_buf, u32 vndr_ie_len);
 s32 brcmf_vif_clear_mgmt_ies(struct brcmf_cfg80211_vif *vif);
-struct brcmf_tlv *brcmf_parse_tlvs(void *buf, int buflen, uint key);
+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);
index 9a45f6f..76b0729 100644 (file)
@@ -891,8 +891,7 @@ il3945_rs_rate_init_stub(void *il_r, struct ieee80211_supported_band *sband,
 {
 }
 
-static struct rate_control_ops rs_ops = {
-       .module = NULL,
+static const struct rate_control_ops rs_ops = {
        .name = RS_NAME,
        .tx_status = il3945_rs_tx_status,
        .get_rate = il3945_rs_get_rate,
index 4d5e332..eaaeea1 100644 (file)
@@ -2807,8 +2807,7 @@ il4965_rs_rate_init_stub(void *il_r, struct ieee80211_supported_band *sband,
 {
 }
 
-static struct rate_control_ops rs_4965_ops = {
-       .module = NULL,
+static const struct rate_control_ops rs_4965_ops = {
        .name = IL4965_RS_NAME,
        .tx_status = il4965_rs_tx_status,
        .get_rate = il4965_rs_get_rate,
index 3eb2102..74b3b4d 100644 (file)
@@ -68,6 +68,19 @@ config IWLWIFI_OPMODE_MODULAR
 comment "WARNING: iwlwifi is useless without IWLDVM or IWLMVM"
        depends on IWLWIFI && IWLDVM=n && IWLMVM=n
 
+config IWLWIFI_BCAST_FILTERING
+       bool "Enable broadcast filtering"
+       depends on IWLMVM
+       help
+         Say Y here to enable default bcast filtering configuration.
+
+         Enabling broadcast filtering will drop any incoming wireless
+         broadcast frames, except some very specific predefined
+         patterns (e.g. incoming arp requests).
+
+         If unsure, don't enable this option, as some programs might
+         expect incoming broadcasts for their normal operations.
+
 menu "Debugging Options"
        depends on IWLWIFI
 
@@ -111,6 +124,7 @@ config IWLWIFI_DEBUG_EXPERIMENTAL_UCODE
          Enable use of experimental ucode for testing and debugging.
 
 config IWLWIFI_DEVICE_TRACING
+
        bool "iwlwifi device access tracing"
        depends on IWLWIFI
        depends on EVENT_TRACING
index 1fa6442..3d32f41 100644 (file)
@@ -8,7 +8,7 @@ iwlwifi-objs            += iwl-eeprom-read.o iwl-eeprom-parse.o
 iwlwifi-objs           += iwl-phy-db.o iwl-nvm-parse.o
 iwlwifi-objs           += pcie/drv.o pcie/rx.o pcie/tx.o pcie/trans.o
 iwlwifi-$(CONFIG_IWLDVM) += iwl-1000.o iwl-2000.o iwl-5000.o iwl-6000.o
-iwlwifi-$(CONFIG_IWLMVM) += iwl-7000.o
+iwlwifi-$(CONFIG_IWLMVM) += iwl-7000.o iwl-8000.o
 
 iwlwifi-objs += $(iwlwifi-m)
 
index 0977d93..aa773a2 100644 (file)
@@ -176,46 +176,46 @@ static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta,
  * (2.4 GHz) band.
  */
 
-static s32 expected_tpt_legacy[IWL_RATE_COUNT] = {
+static const u16 expected_tpt_legacy[IWL_RATE_COUNT] = {
        7, 13, 35, 58, 40, 57, 72, 98, 121, 154, 177, 186, 0
 };
 
-static s32 expected_tpt_siso20MHz[4][IWL_RATE_COUNT] = {
+static const u16 expected_tpt_siso20MHz[4][IWL_RATE_COUNT] = {
        {0, 0, 0, 0, 42, 0,  76, 102, 124, 159, 183, 193, 202}, /* Norm */
        {0, 0, 0, 0, 46, 0,  82, 110, 132, 168, 192, 202, 210}, /* SGI */
        {0, 0, 0, 0, 47, 0,  91, 133, 171, 242, 305, 334, 362}, /* AGG */
        {0, 0, 0, 0, 52, 0, 101, 145, 187, 264, 330, 361, 390}, /* AGG+SGI */
 };
 
-static s32 expected_tpt_siso40MHz[4][IWL_RATE_COUNT] = {
+static const u16 expected_tpt_siso40MHz[4][IWL_RATE_COUNT] = {
        {0, 0, 0, 0,  77, 0, 127, 160, 184, 220, 242, 250, 257}, /* Norm */
        {0, 0, 0, 0,  83, 0, 135, 169, 193, 229, 250, 257, 264}, /* SGI */
        {0, 0, 0, 0,  94, 0, 177, 249, 313, 423, 512, 550, 586}, /* AGG */
        {0, 0, 0, 0, 104, 0, 193, 270, 338, 454, 545, 584, 620}, /* AGG+SGI */
 };
 
-static s32 expected_tpt_mimo2_20MHz[4][IWL_RATE_COUNT] = {
+static const u16 expected_tpt_mimo2_20MHz[4][IWL_RATE_COUNT] = {
        {0, 0, 0, 0,  74, 0, 123, 155, 179, 214, 236, 244, 251}, /* Norm */
        {0, 0, 0, 0,  81, 0, 131, 164, 188, 223, 243, 251, 257}, /* SGI */
        {0, 0, 0, 0,  89, 0, 167, 235, 296, 402, 488, 526, 560}, /* AGG */
        {0, 0, 0, 0,  97, 0, 182, 255, 320, 431, 520, 558, 593}, /* AGG+SGI*/
 };
 
-static s32 expected_tpt_mimo2_40MHz[4][IWL_RATE_COUNT] = {
+static const u16 expected_tpt_mimo2_40MHz[4][IWL_RATE_COUNT] = {
        {0, 0, 0, 0, 123, 0, 182, 214, 235, 264, 279, 285, 289}, /* Norm */
        {0, 0, 0, 0, 131, 0, 191, 222, 242, 270, 284, 289, 293}, /* SGI */
        {0, 0, 0, 0, 171, 0, 305, 410, 496, 634, 731, 771, 805}, /* AGG */
        {0, 0, 0, 0, 186, 0, 329, 439, 527, 667, 764, 803, 838}, /* AGG+SGI */
 };
 
-static s32 expected_tpt_mimo3_20MHz[4][IWL_RATE_COUNT] = {
+static const u16 expected_tpt_mimo3_20MHz[4][IWL_RATE_COUNT] = {
        {0, 0, 0, 0,  99, 0, 153, 186, 208, 239, 256, 263, 268}, /* Norm */
        {0, 0, 0, 0, 106, 0, 162, 194, 215, 246, 262, 268, 273}, /* SGI */
        {0, 0, 0, 0, 134, 0, 249, 346, 431, 574, 685, 732, 775}, /* AGG */
        {0, 0, 0, 0, 148, 0, 272, 376, 465, 614, 727, 775, 818}, /* AGG+SGI */
 };
 
-static s32 expected_tpt_mimo3_40MHz[4][IWL_RATE_COUNT] = {
+static const u16 expected_tpt_mimo3_40MHz[4][IWL_RATE_COUNT] = {
        {0, 0, 0, 0, 152, 0, 211, 239, 255, 279,  290,  294,  297}, /* Norm */
        {0, 0, 0, 0, 160, 0, 219, 245, 261, 284,  294,  297,  300}, /* SGI */
        {0, 0, 0, 0, 254, 0, 443, 584, 695, 868,  984, 1030, 1070}, /* AGG */
@@ -1111,7 +1111,7 @@ static void rs_set_expected_tpt_table(struct iwl_lq_sta *lq_sta,
                                      struct iwl_scale_tbl_info *tbl)
 {
        /* Used to choose among HT tables */
-       s32 (*ht_tbl_pointer)[IWL_RATE_COUNT];
+       const u16 (*ht_tbl_pointer)[IWL_RATE_COUNT];
 
        /* Check for invalid LQ type */
        if (WARN_ON_ONCE(!is_legacy(tbl->lq_type) && !is_Ht(tbl->lq_type))) {
@@ -1173,9 +1173,8 @@ static s32 rs_get_best_rate(struct iwl_priv *priv,
            &(lq_sta->lq_info[lq_sta->active_tbl]);
        s32 active_sr = active_tbl->win[index].success_ratio;
        s32 active_tpt = active_tbl->expected_tpt[index];
-
        /* expected "search" throughput */
-       s32 *tpt_tbl = tbl->expected_tpt;
+       const u16 *tpt_tbl = tbl->expected_tpt;
 
        s32 new_rate, high, low, start_hi;
        u16 high_low;
@@ -3319,8 +3318,8 @@ static void rs_rate_init_stub(void *priv_r, struct ieee80211_supported_band *sba
                              struct ieee80211_sta *sta, void *priv_sta)
 {
 }
-static struct rate_control_ops rs_ops = {
-       .module = NULL,
+
+static const struct rate_control_ops rs_ops = {
        .name = RS_NAME,
        .tx_status = rs_tx_status,
        .get_rate = rs_get_rate,
index bdd5644..f6bd25c 100644 (file)
@@ -315,7 +315,7 @@ struct iwl_scale_tbl_info {
        u8 is_dup;      /* 1 = duplicated data streams */
        u8 action;      /* change modulation; IWL_[LEGACY/SISO/MIMO]_SWITCH_* */
        u8 max_search;  /* maximun number of tables we can search */
-       s32 *expected_tpt;      /* throughput metrics; expected_tpt_G, etc. */
+       const u16 *expected_tpt;        /* throughput metrics; expected_tpt_G, etc. */
        u32 current_rate;  /* rate_n_flags, uCode API format */
        struct iwl_rate_scale_data win[IWL_RATE_COUNT]; /* rate histories */
 };
index 2a59da2..fbd262f 100644 (file)
@@ -71,8 +71,8 @@
 #define IWL3160_UCODE_API_MAX  8
 
 /* Oldest version we won't warn about */
-#define IWL7260_UCODE_API_OK   7
-#define IWL3160_UCODE_API_OK   7
+#define IWL7260_UCODE_API_OK   8
+#define IWL3160_UCODE_API_OK   8
 
 /* Lowest firmware API version supported */
 #define IWL7260_UCODE_API_MIN  7
@@ -95,6 +95,8 @@
 #define IWL7265_FW_PRE "iwlwifi-7265-"
 #define IWL7265_MODULE_FIRMWARE(api) IWL7265_FW_PRE __stringify(api) ".ucode"
 
+#define NVM_HW_SECTION_NUM_FAMILY_7000         0
+
 static const struct iwl_base_params iwl7000_base_params = {
        .eeprom_size = OTP_LOW_IMAGE_SIZE,
        .num_of_queues = IWLAGN_NUM_QUEUES,
@@ -120,7 +122,8 @@ static const struct iwl_ht_params iwl7000_ht_params = {
        .max_inst_size = IWL60_RTC_INST_SIZE,                   \
        .max_data_size = IWL60_RTC_DATA_SIZE,                   \
        .base_params = &iwl7000_base_params,                    \
-       .led_mode = IWL_LED_RF_STATE
+       .led_mode = IWL_LED_RF_STATE,                           \
+       .nvm_hw_section_num = NVM_HW_SECTION_NUM_FAMILY_7000
 
 
 const struct iwl_cfg iwl7260_2ac_cfg = {
@@ -194,6 +197,17 @@ const struct iwl_cfg iwl3160_n_cfg = {
        .host_interrupt_operation_mode = true,
 };
 
+static const struct iwl_pwr_tx_backoff iwl7265_pwr_tx_backoffs[] = {
+       {.pwr = 1600, .backoff = 0},
+       {.pwr = 1300, .backoff = 467},
+       {.pwr = 900,  .backoff = 1900},
+       {.pwr = 800, .backoff = 2630},
+       {.pwr = 700, .backoff = 3720},
+       {.pwr = 600, .backoff = 5550},
+       {.pwr = 500, .backoff = 9350},
+       {0},
+};
+
 const struct iwl_cfg iwl7265_2ac_cfg = {
        .name = "Intel(R) Dual Band Wireless AC 7265",
        .fw_name_pre = IWL7265_FW_PRE,
@@ -201,6 +215,7 @@ const struct iwl_cfg iwl7265_2ac_cfg = {
        .ht_params = &iwl7000_ht_params,
        .nvm_ver = IWL7265_NVM_VERSION,
        .nvm_calib_ver = IWL7265_TX_POWER_VERSION,
+       .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
 };
 
 const struct iwl_cfg iwl7265_2n_cfg = {
@@ -210,6 +225,7 @@ const struct iwl_cfg iwl7265_2n_cfg = {
        .ht_params = &iwl7000_ht_params,
        .nvm_ver = IWL7265_NVM_VERSION,
        .nvm_calib_ver = IWL7265_TX_POWER_VERSION,
+       .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
 };
 
 const struct iwl_cfg iwl7265_n_cfg = {
@@ -219,6 +235,7 @@ const struct iwl_cfg iwl7265_n_cfg = {
        .ht_params = &iwl7000_ht_params,
        .nvm_ver = IWL7265_NVM_VERSION,
        .nvm_calib_ver = IWL7265_TX_POWER_VERSION,
+       .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
 };
 
 MODULE_FIRMWARE(IWL7260_MODULE_FIRMWARE(IWL7260_UCODE_API_OK));
diff --git a/drivers/net/wireless/iwlwifi/iwl-8000.c b/drivers/net/wireless/iwlwifi/iwl-8000.c
new file mode 100644 (file)
index 0000000..f5bd82b
--- /dev/null
@@ -0,0 +1,132 @@
+/******************************************************************************
+ *
+ * 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) 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) 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/module.h>
+#include <linux/stringify.h>
+#include "iwl-config.h"
+#include "iwl-agn-hw.h"
+
+/* Highest firmware API version supported */
+#define IWL8000_UCODE_API_MAX  8
+
+/* Oldest version we won't warn about */
+#define IWL8000_UCODE_API_OK   8
+
+/* Lowest firmware API version supported */
+#define IWL8000_UCODE_API_MIN  8
+
+/* NVM versions */
+#define IWL8000_NVM_VERSION            0x0a1d
+#define IWL8000_TX_POWER_VERSION       0xffff /* meaningless */
+
+#define IWL8000_FW_PRE "iwlwifi-8000-"
+#define IWL8000_MODULE_FIRMWARE(api) IWL8000_FW_PRE __stringify(api) ".ucode"
+
+#define NVM_HW_SECTION_NUM_FAMILY_8000         10
+
+static const struct iwl_base_params iwl8000_base_params = {
+       .eeprom_size = OTP_LOW_IMAGE_SIZE,
+       .num_of_queues = IWLAGN_NUM_QUEUES,
+       .pll_cfg_val = 0,
+       .shadow_ram_support = true,
+       .led_compensation = 57,
+       .wd_timeout = IWL_LONG_WD_TIMEOUT,
+       .max_event_log_size = 512,
+       .shadow_reg_enable = true,
+       .pcie_l1_allowed = true,
+};
+
+static const struct iwl_ht_params iwl8000_ht_params = {
+       .ht40_bands = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ),
+};
+
+#define IWL_DEVICE_8000                                                \
+       .ucode_api_max = IWL8000_UCODE_API_MAX,                 \
+       .ucode_api_ok = IWL8000_UCODE_API_OK,                   \
+       .ucode_api_min = IWL8000_UCODE_API_MIN,                 \
+       .device_family = IWL_DEVICE_FAMILY_8000,                \
+       .max_inst_size = IWL60_RTC_INST_SIZE,                   \
+       .max_data_size = IWL60_RTC_DATA_SIZE,                   \
+       .base_params = &iwl8000_base_params,                    \
+       .led_mode = IWL_LED_RF_STATE,                           \
+       .nvm_hw_section_num = NVM_HW_SECTION_NUM_FAMILY_8000
+
+const struct iwl_cfg iwl8260_2ac_cfg = {
+       .name = "Intel(R) Dual Band Wireless AC 8260",
+       .fw_name_pre = IWL8000_FW_PRE,
+       IWL_DEVICE_8000,
+       .ht_params = &iwl8000_ht_params,
+       .nvm_ver = IWL8000_NVM_VERSION,
+       .nvm_calib_ver = IWL8000_TX_POWER_VERSION,
+};
+
+const struct iwl_cfg iwl8260_n_cfg = {
+       .name = "Intel(R) Dual Band Wireless-AC 8260",
+       .fw_name_pre = IWL8000_FW_PRE,
+       IWL_DEVICE_8000,
+       .ht_params = &iwl8000_ht_params,
+       .nvm_ver = IWL8000_NVM_VERSION,
+       .nvm_calib_ver = IWL8000_TX_POWER_VERSION,
+};
+
+MODULE_FIRMWARE(IWL8000_MODULE_FIRMWARE(IWL8000_UCODE_API_OK));
index 1ced525..13ec566 100644 (file)
@@ -84,6 +84,7 @@ enum iwl_device_family {
        IWL_DEVICE_FAMILY_6050,
        IWL_DEVICE_FAMILY_6150,
        IWL_DEVICE_FAMILY_7000,
+       IWL_DEVICE_FAMILY_8000,
 };
 
 /*
@@ -192,6 +193,15 @@ struct iwl_eeprom_params {
        bool enhanced_txpower;
 };
 
+/* Tx-backoff power threshold
+ * @pwr: The power limit in mw
+ * @backoff: The tx-backoff in uSec
+ */
+struct iwl_pwr_tx_backoff {
+       u32 pwr;
+       u32 backoff;
+};
+
 /**
  * struct iwl_cfg
  * @name: Offical name of the device
@@ -217,6 +227,9 @@ struct iwl_eeprom_params {
  * @high_temp: Is this NIC is designated to be in high temperature.
  * @host_interrupt_operation_mode: device needs host interrupt operation
  *     mode set
+ * @d0i3: device uses d0i3 instead of d3
+ * @nvm_hw_section_num: the ID of the HW NVM section
+ * @pwr_tx_backoffs: translation table between power limits and backoffs
  *
  * We enable the driver to be backward compatible wrt. hardware features.
  * API differences in uCode shouldn't be handled here but through TLVs
@@ -247,6 +260,9 @@ struct iwl_cfg {
        const bool internal_wimax_coex;
        const bool host_interrupt_operation_mode;
        bool high_temp;
+       bool d0i3;
+       u8   nvm_hw_section_num;
+       const struct iwl_pwr_tx_backoff *pwr_tx_backoffs;
 };
 
 /*
@@ -307,6 +323,8 @@ extern const struct iwl_cfg iwl3160_n_cfg;
 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;
 #endif /* CONFIG_IWLMVM */
 
 #endif /* __IWL_CONFIG_H__ */
index 9d32551..f13dec9 100644 (file)
 #define CSR_DRAM_INT_TBL_ENABLE                (1 << 31)
 #define CSR_DRAM_INIT_TBL_WRAP_CHECK   (1 << 27)
 
-/* SECURE boot registers */
-#define CSR_SECURE_BOOT_CONFIG_ADDR    (0x100)
-enum secure_boot_config_reg {
-       CSR_SECURE_BOOT_CONFIG_INSPECTOR_BURNED_IN_OTP  = 0x00000001,
-       CSR_SECURE_BOOT_CONFIG_INSPECTOR_NOT_REQ        = 0x00000002,
-};
-
-#define CSR_SECURE_BOOT_CPU1_STATUS_ADDR       (0x100)
-#define CSR_SECURE_BOOT_CPU2_STATUS_ADDR       (0x100)
-enum secure_boot_status_reg {
-       CSR_SECURE_BOOT_CPU_STATUS_VERF_STATUS          = 0x00000003,
-       CSR_SECURE_BOOT_CPU_STATUS_VERF_COMPLETED       = 0x00000002,
-       CSR_SECURE_BOOT_CPU_STATUS_VERF_SUCCESS         = 0x00000004,
-       CSR_SECURE_BOOT_CPU_STATUS_VERF_FAIL            = 0x00000008,
-       CSR_SECURE_BOOT_CPU_STATUS_SIGN_VERF_FAIL       = 0x00000010,
-};
-
-#define CSR_UCODE_LOAD_STATUS_ADDR     (0x100)
-enum secure_load_status_reg {
-       CSR_CPU_STATUS_LOADING_STARTED                  = 0x00000001,
-       CSR_CPU_STATUS_LOADING_COMPLETED                = 0x00000002,
-       CSR_CPU_STATUS_NUM_OF_LAST_COMPLETED            = 0x000000F8,
-       CSR_CPU_STATUS_NUM_OF_LAST_LOADED_BLOCK         = 0x0000FF00,
-};
-
-#define CSR_SECURE_INSPECTOR_CODE_ADDR (0x100)
-#define CSR_SECURE_INSPECTOR_DATA_ADDR (0x100)
-
-#define CSR_SECURE_TIME_OUT    (100)
-
-#define FH_TCSR_0_REG0 (0x1D00)
-
 /*
  * HBUS (Host-side Bus)
  *
index a75aac9..c8cbdbe 100644 (file)
@@ -126,6 +126,7 @@ do {                                                                \
 /* 0x00000F00 - 0x00000100 */
 #define IWL_DL_POWER           0x00000100
 #define IWL_DL_TEMP            0x00000200
+#define IWL_DL_RPM             0x00000400
 #define IWL_DL_SCAN            0x00000800
 /* 0x0000F000 - 0x00001000 */
 #define IWL_DL_ASSOC           0x00001000
@@ -189,5 +190,6 @@ do {                                                                \
 #define IWL_DEBUG_RADIO(p, f, a...)    IWL_DEBUG(p, IWL_DL_RADIO, f, ## a)
 #define IWL_DEBUG_POWER(p, f, a...)    IWL_DEBUG(p, IWL_DL_POWER, f, ## a)
 #define IWL_DEBUG_11H(p, f, a...)      IWL_DEBUG(p, IWL_DL_11H, f, ## a)
+#define IWL_DEBUG_RPM(p, f, a...)      IWL_DEBUG(p, IWL_DL_RPM, f, ## a)
 
 #endif
index 7510355..bcfdcfb 100644 (file)
@@ -128,7 +128,7 @@ struct iwl_drv {
        const struct iwl_cfg *cfg;
 
        int fw_index;                   /* firmware we're trying to load */
-       char firmware_name[25];         /* name of firmware file to load */
+       char firmware_name[32];         /* name of firmware file to load */
 
        struct completion request_firmware_complete;
 
@@ -237,7 +237,8 @@ static int iwl_request_firmware(struct iwl_drv *drv, bool first)
                return -ENOENT;
        }
 
-       sprintf(drv->firmware_name, "%s%s%s", name_pre, tag, ".ucode");
+       snprintf(drv->firmware_name, sizeof(drv->firmware_name), "%s%s.ucode",
+                name_pre, tag);
 
        IWL_DEBUG_INFO(drv, "attempting to load firmware %s'%s'\n",
                       (drv->fw_index == UCODE_EXPERIMENTAL_INDEX)
index 5f1493c..f80ba58 100644 (file)
@@ -95,6 +95,8 @@
  * @IWL_UCODE_TLV_FLAGS_P2P_PS: P2P client power save is supported (only on a
  *     single bound interface).
  * @IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD: P2P client supports uAPSD power save
+ * @IWL_UCODE_TLV_FLAGS_BCAST_FILTERING: uCode supports broadcast filtering.
+ * @IWL_UCODE_TLV_FLAGS_GO_UAPSD: AP/GO interfaces support uAPSD clients
  */
 enum iwl_ucode_tlv_flag {
        IWL_UCODE_TLV_FLAGS_PAN                 = BIT(0),
@@ -119,6 +121,8 @@ enum iwl_ucode_tlv_flag {
        IWL_UCODE_TLV_FLAGS_P2P_PS              = BIT(21),
        IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT       = BIT(24),
        IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD        = BIT(26),
+       IWL_UCODE_TLV_FLAGS_BCAST_FILTERING     = BIT(29),
+       IWL_UCODE_TLV_FLAGS_GO_UAPSD            = BIT(30),
 };
 
 /* The default calibrate table size if not specified by firmware file */
@@ -160,8 +164,7 @@ enum iwl_ucode_sec {
  * For 16.0 uCode and above, there is no differentiation between sections,
  * just an offset to the HW address.
  */
-#define IWL_UCODE_SECTION_MAX 6
-#define IWL_UCODE_FIRST_SECTION_OF_SECOND_CPU  (IWL_UCODE_SECTION_MAX/2)
+#define IWL_UCODE_SECTION_MAX 12
 
 struct iwl_ucode_capabilities {
        u32 max_probe_length;
index f98175a..07372f2 100644 (file)
@@ -130,6 +130,21 @@ void iwl_write_prph(struct iwl_trans *trans, u32 ofs, u32 val)
 }
 IWL_EXPORT_SYMBOL(iwl_write_prph);
 
+int iwl_poll_prph_bit(struct iwl_trans *trans, u32 addr,
+                     u32 bits, u32 mask, int timeout)
+{
+       int t = 0;
+
+       do {
+               if ((iwl_read_prph(trans, addr) & mask) == (bits & mask))
+                       return t;
+               udelay(IWL_POLL_INTERVAL);
+               t += IWL_POLL_INTERVAL;
+       } while (t < timeout);
+
+       return -ETIMEDOUT;
+}
+
 void iwl_set_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask)
 {
        unsigned long flags;
index c339c1b..9e81b23 100644 (file)
@@ -72,6 +72,8 @@ void iwl_write_direct32(struct iwl_trans *trans, u32 reg, u32 value);
 
 u32 iwl_read_prph(struct iwl_trans *trans, u32 ofs);
 void iwl_write_prph(struct iwl_trans *trans, u32 ofs, u32 val);
+int iwl_poll_prph_bit(struct iwl_trans *trans, u32 addr,
+                     u32 bits, u32 mask, int timeout);
 void iwl_set_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask);
 void iwl_set_bits_mask_prph(struct iwl_trans *trans, u32 ofs,
                            u32 bits, u32 mask);
index 725e954..53b9cad 100644 (file)
@@ -402,11 +402,7 @@ iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg,
        iwl_init_sbands(dev, cfg, data, nvm_sw, sku & NVM_SKU_CAP_11AC_ENABLE,
                        tx_chains, rx_chains);
 
-       data->calib_version = 255;   /* TODO:
-                                       this value will prevent some checks from
-                                       failing, we need to check if this
-                                       field is still needed, and if it does,
-                                       where is it in the NVM*/
+       data->calib_version = 255;
 
        return data;
 }
index b5be51f..5d78207 100644 (file)
@@ -131,6 +131,8 @@ struct iwl_cfg;
  * @nic_config: configure NIC, called before firmware is started.
  *     May sleep
  * @wimax_active: invoked when WiMax becomes active. May sleep
+ * @enter_d0i3: configure the fw to enter d0i3. May sleep.
+ * @exit_d0i3: configure the fw to exit d0i3. May sleep.
  */
 struct iwl_op_mode_ops {
        struct iwl_op_mode *(*start)(struct iwl_trans *trans,
@@ -148,6 +150,8 @@ struct iwl_op_mode_ops {
        void (*cmd_queue_full)(struct iwl_op_mode *op_mode);
        void (*nic_config)(struct iwl_op_mode *op_mode);
        void (*wimax_active)(struct iwl_op_mode *op_mode);
+       int (*enter_d0i3)(struct iwl_op_mode *op_mode);
+       int (*exit_d0i3)(struct iwl_op_mode *op_mode);
 };
 
 int iwl_opmode_register(const char *name, const struct iwl_op_mode_ops *ops);
@@ -155,7 +159,7 @@ void iwl_opmode_deregister(const char *name);
 
 /**
  * struct iwl_op_mode - operational mode
- * @ops - pointer to its own ops
+ * @ops: pointer to its own ops
  *
  * This holds an implementation of the mac80211 / fw API.
  */
@@ -226,4 +230,22 @@ static inline void iwl_op_mode_wimax_active(struct iwl_op_mode *op_mode)
        op_mode->ops->wimax_active(op_mode);
 }
 
+static inline int iwl_op_mode_enter_d0i3(struct iwl_op_mode *op_mode)
+{
+       might_sleep();
+
+       if (!op_mode->ops->enter_d0i3)
+               return 0;
+       return op_mode->ops->enter_d0i3(op_mode);
+}
+
+static inline int iwl_op_mode_exit_d0i3(struct iwl_op_mode *op_mode)
+{
+       might_sleep();
+
+       if (!op_mode->ops->exit_d0i3)
+               return 0;
+       return op_mode->ops->exit_d0i3(op_mode);
+}
+
 #endif /* __iwl_op_mode_h__ */
index fa77d63..b761ac4 100644 (file)
@@ -72,7 +72,7 @@
 #include "iwl-trans.h"
 
 #define CHANNEL_NUM_SIZE       4       /* num of channels in calib_ch size */
-#define IWL_NUM_PAPD_CH_GROUPS 4
+#define IWL_NUM_PAPD_CH_GROUPS 7
 #define IWL_NUM_TXP_CH_GROUPS  9
 
 struct iwl_phy_db_entry {
@@ -383,7 +383,7 @@ static int iwl_phy_db_send_all_channel_groups(
                if (!entry)
                        return -EINVAL;
 
-               if (WARN_ON_ONCE(!entry->size))
+               if (!entry->size)
                        continue;
 
                /* Send the requested PHY DB section */
index 100bd0d..9c90186 100644 (file)
 /* Device NMI register */
 #define DEVICE_SET_NMI_REG 0x00a01c30
 
+/*
+ * Device reset for family 8000
+ * write to bit 24 in order to reset the CPU
+*/
+#define RELEASE_CPU_RESET              (0x300C)
+#define RELEASE_CPU_RESET_BIT          BIT(24)
+
 /*****************************************************************************
  *                        7000/3000 series SHR DTS addresses                 *
  *****************************************************************************/
@@ -281,4 +288,43 @@ static inline unsigned int SCD_QUEUE_STATUS_BITS(unsigned int chnl)
 #define OSC_CLK                                (0xa04068)
 #define OSC_CLK_FORCE_CONTROL          (0x8)
 
+/* SECURE boot registers */
+#define LMPM_SECURE_BOOT_CONFIG_ADDR   (0x100)
+enum secure_boot_config_reg {
+       LMPM_SECURE_BOOT_CONFIG_INSPECTOR_BURNED_IN_OTP = 0x00000001,
+       LMPM_SECURE_BOOT_CONFIG_INSPECTOR_NOT_REQ       = 0x00000002,
+};
+
+#define LMPM_SECURE_BOOT_CPU1_STATUS_ADDR      (0x1E30)
+#define LMPM_SECURE_BOOT_CPU2_STATUS_ADDR      (0x1E34)
+enum secure_boot_status_reg {
+       LMPM_SECURE_BOOT_CPU_STATUS_VERF_STATUS         = 0x00000001,
+       LMPM_SECURE_BOOT_CPU_STATUS_VERF_COMPLETED      = 0x00000002,
+       LMPM_SECURE_BOOT_CPU_STATUS_VERF_SUCCESS        = 0x00000004,
+       LMPM_SECURE_BOOT_CPU_STATUS_VERF_FAIL           = 0x00000008,
+       LMPM_SECURE_BOOT_CPU_STATUS_SIGN_VERF_FAIL      = 0x00000010,
+       LMPM_SECURE_BOOT_STATUS_SUCCESS                 = 0x00000003,
+};
+
+#define CSR_UCODE_LOAD_STATUS_ADDR     (0x1E70)
+enum secure_load_status_reg {
+       LMPM_CPU_UCODE_LOADING_STARTED                  = 0x00000001,
+       LMPM_CPU_HDRS_LOADING_COMPLETED                 = 0x00000003,
+       LMPM_CPU_UCODE_LOADING_COMPLETED                = 0x00000007,
+       LMPM_CPU_STATUS_NUM_OF_LAST_COMPLETED           = 0x000000F8,
+       LMPM_CPU_STATUS_NUM_OF_LAST_LOADED_BLOCK        = 0x0000FF00,
+};
+
+#define LMPM_SECURE_INSPECTOR_CODE_ADDR        (0x1E38)
+#define LMPM_SECURE_INSPECTOR_DATA_ADDR        (0x1E3C)
+#define LMPM_SECURE_UCODE_LOAD_CPU1_HDR_ADDR   (0x1E78)
+#define LMPM_SECURE_UCODE_LOAD_CPU2_HDR_ADDR   (0x1E7C)
+
+#define LMPM_SECURE_INSPECTOR_CODE_MEM_SPACE   (0x400000)
+#define LMPM_SECURE_INSPECTOR_DATA_MEM_SPACE   (0x402000)
+#define LMPM_SECURE_CPU1_HDR_MEM_SPACE         (0x420000)
+#define LMPM_SECURE_CPU2_HDR_MEM_SPACE         (0x420400)
+
+#define LMPM_SECURE_TIME_OUT   (100)
+
 #endif                         /* __iwl_prph_h__ */
index 1f065cf..7b19274 100644 (file)
@@ -193,12 +193,23 @@ static inline u32 iwl_rx_packet_payload_len(const struct iwl_rx_packet *pkt)
  * @CMD_ASYNC: Return right away and don't wait for the response
  * @CMD_WANT_SKB: valid only with CMD_SYNC. The caller needs the buffer of the
  *     response. The caller needs to call iwl_free_resp when done.
+ * @CMD_HIGH_PRIO: The command is high priority - it goes to the front of the
+ *     command queue, but after other high priority commands. valid only
+ *     with CMD_ASYNC.
+ * @CMD_SEND_IN_IDLE: The command should be sent even when the trans is idle.
+ * @CMD_MAKE_TRANS_IDLE: The command response should mark the trans as idle.
+ * @CMD_WAKE_UP_TRANS: The command response should wake up the trans
+ *     (i.e. mark it as non-idle).
  */
 enum CMD_MODE {
        CMD_SYNC                = 0,
        CMD_ASYNC               = BIT(0),
        CMD_WANT_SKB            = BIT(1),
        CMD_SEND_IN_RFKILL      = BIT(2),
+       CMD_HIGH_PRIO           = BIT(3),
+       CMD_SEND_IN_IDLE        = BIT(4),
+       CMD_MAKE_TRANS_IDLE     = BIT(5),
+       CMD_WAKE_UP_TRANS       = BIT(6),
 };
 
 #define DEF_CMD_PAYLOAD_SIZE 320
@@ -335,6 +346,9 @@ enum iwl_d3_status {
  * @STATUS_INT_ENABLED: interrupts are enabled
  * @STATUS_RFKILL: the HW RFkill switch is in KILL position
  * @STATUS_FW_ERROR: the fw is in error state
+ * @STATUS_TRANS_GOING_IDLE: shutting down the trans, only special commands
+ *     are sent
+ * @STATUS_TRANS_IDLE: the trans is idle - general commands are not to be sent
  */
 enum iwl_trans_status {
        STATUS_SYNC_HCMD_ACTIVE,
@@ -343,6 +357,8 @@ enum iwl_trans_status {
        STATUS_INT_ENABLED,
        STATUS_RFKILL,
        STATUS_FW_ERROR,
+       STATUS_TRANS_GOING_IDLE,
+       STATUS_TRANS_IDLE,
 };
 
 /**
@@ -443,6 +459,11 @@ struct iwl_trans;
  * @release_nic_access: let the NIC go to sleep. The "flags" parameter
  *     must be the same one that was sent before to the grab_nic_access.
  * @set_bits_mask - set SRAM register according to value and mask.
+ * @ref: grab a reference to the transport/FW layers, disallowing
+ *     certain low power states
+ * @unref: release a reference previously taken with @ref. Note that
+ *     initially the reference count is 1, making an initial @unref
+ *     necessary to allow low power states.
  */
 struct iwl_trans_ops {
 
@@ -489,6 +510,8 @@ struct iwl_trans_ops {
                                   unsigned long *flags);
        void (*set_bits_mask)(struct iwl_trans *trans, u32 reg, u32 mask,
                              u32 value);
+       void (*ref)(struct iwl_trans *trans);
+       void (*unref)(struct iwl_trans *trans);
 };
 
 /**
@@ -523,6 +546,7 @@ enum iwl_trans_state {
  *     starting the firmware, used for tracing
  * @rx_mpdu_cmd_hdr_size: used for tracing, amount of data before the
  *     start of the 802.11 header in the @rx_mpdu_cmd
+ * @dflt_pwr_limit: default power limit fetched from the platform (ACPI)
  */
 struct iwl_trans {
        const struct iwl_trans_ops *ops;
@@ -551,6 +575,8 @@ struct iwl_trans {
        struct lockdep_map sync_cmd_lockdep_map;
 #endif
 
+       u64 dflt_pwr_limit;
+
        /* pointer to trans specific struct */
        /*Ensure that this pointer will always be aligned to sizeof pointer */
        char trans_specific[0] __aligned(sizeof(void *));
@@ -627,6 +653,18 @@ static inline int iwl_trans_d3_resume(struct iwl_trans *trans,
        return trans->ops->d3_resume(trans, status, test);
 }
 
+static inline void iwl_trans_ref(struct iwl_trans *trans)
+{
+       if (trans->ops->ref)
+               trans->ops->ref(trans);
+}
+
+static inline void iwl_trans_unref(struct iwl_trans *trans)
+{
+       if (trans->ops->unref)
+               trans->ops->unref(trans);
+}
+
 static inline int iwl_trans_send_cmd(struct iwl_trans *trans,
                                     struct iwl_host_cmd *cmd)
 {
index f98ec2b..41d390f 100644 (file)
@@ -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 power_legacy.o bt-coex.o
+iwlmvm-y += power.o bt-coex.o
 iwlmvm-y += led.o tt.o
 iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o debugfs-vif.o
 iwlmvm-$(CONFIG_PM_SLEEP) += d3.o
index 76cde6c..38a54a3 100644 (file)
@@ -378,7 +378,6 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm)
 
        flags = iwlwifi_mod_params.bt_coex_active ?
                        BT_COEX_NW : BT_COEX_DISABLE;
-       flags |= BT_CH_PRIMARY_EN | BT_CH_SECONDARY_EN | BT_SYNC_2_BT_DISABLE;
        bt_cmd->flags = cpu_to_le32(flags);
 
        bt_cmd->valid_bit_msk = cpu_to_le32(BT_VALID_ENABLE |
@@ -399,6 +398,9 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm)
                                            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 (mvm->cfg->bt_shared_single_ant)
                memcpy(&bt_cmd->decision_lut, iwl_single_shared_ant,
                       sizeof(iwl_single_shared_ant));
@@ -489,8 +491,7 @@ static int iwl_mvm_bt_udpate_ctrl_kill_msk(struct iwl_mvm *mvm,
        return ret;
 }
 
-static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id,
-                                      bool enable)
+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 */
@@ -500,25 +501,16 @@ static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id,
                .dataflags = { IWL_HCMD_DFL_DUP, },
                .flags = CMD_ASYNC,
        };
-
-       struct ieee80211_sta *sta;
        struct iwl_mvm_sta *mvmsta;
        int ret;
 
-       if (sta_id == IWL_MVM_STATION_COUNT)
-               return 0;
-
-       sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
-                                       lockdep_is_held(&mvm->mutex));
-
-       /* This can happen if the station has been removed right now */
-       if (IS_ERR_OR_NULL(sta))
+       mvmsta = iwl_mvm_sta_from_staid_protected(mvm, sta_id);
+       if (!mvmsta)
                return 0;
 
-       mvmsta = iwl_mvm_sta_from_mac80211(sta);
-
        /* nothing to do */
-       if (mvmsta->bt_reduced_txpower == enable)
+       if (mvmsta->bt_reduced_txpower_dbg ||
+           mvmsta->bt_reduced_txpower == enable)
                return 0;
 
        bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_ATOMIC);
@@ -552,6 +544,7 @@ struct iwl_bt_iterator_data {
        bool reduced_tx_power;
        struct ieee80211_chanctx_conf *primary;
        struct ieee80211_chanctx_conf *secondary;
+       bool primary_ll;
 };
 
 static inline
@@ -577,72 +570,113 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
        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);
 
-       if (vif->type != NL80211_IFTYPE_STATION &&
-           vif->type != NL80211_IFTYPE_AP)
-               return;
+       switch (vif->type) {
+       case NL80211_IFTYPE_STATION:
+               /* default smps_mode for BSS / P2P client is AUTOMATIC */
+               smps_mode = IEEE80211_SMPS_AUTOMATIC;
+               data->num_bss_ifaces++;
 
-       smps_mode = IEEE80211_SMPS_AUTOMATIC;
+               /*
+                * Count unassoc BSSes, relax SMSP constraints
+                * and disable reduced Tx Power
+                */
+               if (!vif->bss_conf.assoc) {
+                       iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,
+                                           smps_mode);
+                       if (iwl_mvm_bt_coex_reduced_txp(mvm,
+                                                       mvmvif->ap_sta_id,
+                                                       false))
+                               IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n");
+                       return;
+               }
+               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)) {
-               /* ... and it is an associated STATION, relax constraints */
-               if (vif->type == NL80211_IFTYPE_STATION && vif->bss_conf.assoc)
-                       iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,
-                                           smps_mode);
-               iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0);
+               /* ... relax constraints and disable rssi events */
+               iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,
+                                   smps_mode);
+               if (vif->type == NL80211_IFTYPE_STATION)
+                       iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0);
                return;
        }
 
-       /* SoftAP / GO will always be primary */
-       if (vif->type == NL80211_IFTYPE_AP) {
-               if (!mvmvif->ap_ibss_active)
-                       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;
+       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);
 
-               /* the Ack / Cts kill mask must be default if AP / GO */
-               data->reduced_tx_power = false;
+       iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, smps_mode);
 
-               if (chanctx_conf == data->primary)
-                       return;
+       /* low latency is always primary */
+       if (iwl_mvm_vif_low_latency(mvmvif)) {
+               data->primary_ll = true;
 
-               /* downgrade the current primary no matter what its type is */
                data->secondary = data->primary;
                data->primary = chanctx_conf;
-               return;
        }
 
-       data->num_bss_ifaces++;
+       if (vif->type == NL80211_IFTYPE_AP) {
+               if (!mvmvif->ap_ibss_active)
+                       return;
 
-       /* we are now a STA / P2P Client, and take associated ones only */
-       if (!vif->bss_conf.assoc)
+               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 */
+       /*
+        * 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;
 
-       if (le32_to_cpu(data->notif->bt_activity_grading) >= BT_HIGH_TRAFFIC)
-               smps_mode = IEEE80211_SMPS_STATIC;
-       else if (le32_to_cpu(data->notif->bt_activity_grading) >=
-                BT_LOW_TRAFFIC)
-               smps_mode = IEEE80211_SMPS_DYNAMIC;
-
-       IWL_DEBUG_COEX(data->mvm,
-                      "mac %d: bt_status %d bt_activity_grading %d smps_req %d\n",
-                      mvmvif->id,  data->notif->bt_status,
-                      data->notif->bt_activity_grading, smps_mode);
-
-       iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, smps_mode);
-
        /* don't reduce the Tx power if in loose scheme */
        if (iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT ||
            mvm->cfg->bt_shared_single_ant) {
index 0368576..2d133b1 100644 (file)
@@ -78,5 +78,9 @@
 #define IWL_MVM_PS_SNOOZE_INTERVAL             25
 #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_LOWLAT_SINGLE_BINDING_MAXDUR   24 /* TU */
+#define IWL_MVM_LOWLAT_DUAL_BINDING_MAXDUR     24 /* TU */
+#define IWL_MVM_BT_COEX_SYNC2SCO               1
 
 #endif /* __MVM_CONSTANTS_H */
index f36a7ee..b956e2f 100644 (file)
@@ -963,7 +963,6 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
        };
        int ret, i;
        int len __maybe_unused;
-       u8 old_aux_sta_id, old_ap_sta_id = IWL_MVM_STATION_COUNT;
 
        if (!wowlan) {
                /*
@@ -980,8 +979,6 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
 
        mutex_lock(&mvm->mutex);
 
-       old_aux_sta_id = mvm->aux_sta.sta_id;
-
        /* see if there's only a single BSS vif and it's associated */
        ieee80211_iterate_active_interfaces_atomic(
                mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
@@ -1066,16 +1063,6 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
 
        iwl_trans_stop_device(mvm->trans);
 
-       /*
-        * The D3 firmware still hardcodes the AP station ID for the
-        * BSS we're associated with as 0. Store the real STA ID here
-        * and assign 0. When we leave this function, we'll restore
-        * the original value for the resume code.
-        */
-       old_ap_sta_id = mvm_ap_sta->sta_id;
-       mvm_ap_sta->sta_id = 0;
-       mvmvif->ap_sta_id = 0;
-
        /*
         * Set the HW restart bit -- this is mostly true as we're
         * going to load new firmware and reprogram that, though
@@ -1096,16 +1083,6 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
        mvm->ptk_ivlen = 0;
        mvm->ptk_icvlen = 0;
 
-       /*
-        * The D3 firmware still hardcodes the AP station ID for the
-        * BSS we're associated with as 0. As a result, we have to move
-        * the auxiliary station to ID 1 so the ID 0 remains free for
-        * the AP station for later.
-        * We set the sta_id to 1 here, and reset it to its previous
-        * value (that we stored above) later.
-        */
-       mvm->aux_sta.sta_id = 1;
-
        ret = iwl_mvm_load_d3_fw(mvm);
        if (ret)
                goto out;
@@ -1191,11 +1168,11 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
        if (ret)
                goto out;
 
-       ret = iwl_mvm_power_update_device_mode(mvm);
+       ret = iwl_mvm_power_update_device(mvm);
        if (ret)
                goto out;
 
-       ret = iwl_mvm_power_update_mode(mvm, vif);
+       ret = iwl_mvm_power_update_mac(mvm, vif);
        if (ret)
                goto out;
 
@@ -1222,10 +1199,6 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
 
        iwl_trans_d3_suspend(mvm->trans, test);
  out:
-       mvm->aux_sta.sta_id = old_aux_sta_id;
-       mvm_ap_sta->sta_id = old_ap_sta_id;
-       mvmvif->ap_sta_id = old_ap_sta_id;
-
        if (ret < 0)
                ieee80211_restart_hw(mvm->hw);
  out_noreset:
index 0e29cd8..29b4396 100644 (file)
@@ -185,7 +185,7 @@ static ssize_t iwl_dbgfs_pm_params_write(struct ieee80211_vif *vif, char *buf,
 
        mutex_lock(&mvm->mutex);
        iwl_dbgfs_update_pm(mvm, vif, param, val);
-       ret = iwl_mvm_power_update_mode(mvm, vif);
+       ret = iwl_mvm_power_update_mac(mvm, vif);
        mutex_unlock(&mvm->mutex);
 
        return ret ?: count;
@@ -202,7 +202,7 @@ static ssize_t iwl_dbgfs_pm_params_read(struct file *file,
        int bufsz = sizeof(buf);
        int pos;
 
-       pos = iwl_mvm_power_dbgfs_read(mvm, vif, buf, bufsz);
+       pos = iwl_mvm_power_mac_dbgfs_read(mvm, vif, buf, bufsz);
 
        return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
 }
@@ -225,6 +225,29 @@ static ssize_t iwl_dbgfs_mac_params_read(struct file *file,
 
        ap_sta_id = mvmvif->ap_sta_id;
 
+       switch (ieee80211_vif_type_p2p(vif)) {
+       case NL80211_IFTYPE_ADHOC:
+               pos += scnprintf(buf+pos, bufsz-pos, "type: ibss\n");
+               break;
+       case NL80211_IFTYPE_STATION:
+               pos += scnprintf(buf+pos, bufsz-pos, "type: bss\n");
+               break;
+       case NL80211_IFTYPE_AP:
+               pos += scnprintf(buf+pos, bufsz-pos, "type: ap\n");
+               break;
+       case NL80211_IFTYPE_P2P_CLIENT:
+               pos += scnprintf(buf+pos, bufsz-pos, "type: p2p client\n");
+               break;
+       case NL80211_IFTYPE_P2P_GO:
+               pos += scnprintf(buf+pos, bufsz-pos, "type: p2p go\n");
+               break;
+       case NL80211_IFTYPE_P2P_DEVICE:
+               pos += scnprintf(buf+pos, bufsz-pos, "type: p2p dev\n");
+               break;
+       default:
+               break;
+       }
+
        pos += scnprintf(buf+pos, bufsz-pos, "mac id/color: %d / %d\n",
                         mvmvif->id, mvmvif->color);
        pos += scnprintf(buf+pos, bufsz-pos, "bssid: %pM\n",
@@ -249,9 +272,10 @@ static ssize_t iwl_dbgfs_mac_params_read(struct file *file,
                        struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
 
                        pos += scnprintf(buf+pos, bufsz-pos,
-                                        "ap_sta_id %d - reduced Tx power %d\n",
+                                        "ap_sta_id %d - reduced Tx power %d force %d\n",
                                         ap_sta_id,
-                                        mvm_sta->bt_reduced_txpower);
+                                        mvm_sta->bt_reduced_txpower,
+                                        mvm_sta->bt_reduced_txpower_dbg);
                }
        }
 
@@ -269,6 +293,36 @@ static ssize_t iwl_dbgfs_mac_params_read(struct file *file,
        return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
 }
 
+static ssize_t iwl_dbgfs_reduced_txp_write(struct ieee80211_vif *vif,
+                                          char *buf, size_t count,
+                                          loff_t *ppos)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_mvm *mvm = mvmvif->mvm;
+       struct iwl_mvm_sta *mvmsta;
+       bool reduced_tx_power;
+       int ret;
+
+       if (mvmvif->ap_sta_id >= ARRAY_SIZE(mvm->fw_id_to_mac_id))
+               return -ENOTCONN;
+
+       if (strtobool(buf, &reduced_tx_power) != 0)
+               return -EINVAL;
+
+       mutex_lock(&mvm->mutex);
+
+       mvmsta = iwl_mvm_sta_from_staid_protected(mvm, mvmvif->ap_sta_id);
+       mvmsta->bt_reduced_txpower_dbg = false;
+       ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id,
+                                         reduced_tx_power);
+       if (!ret)
+               mvmsta->bt_reduced_txpower_dbg = true;
+
+       mutex_unlock(&mvm->mutex);
+
+       return ret ? : count;
+}
+
 static void iwl_dbgfs_update_bf(struct ieee80211_vif *vif,
                                enum iwl_dbgfs_bf_mask param, int value)
 {
@@ -403,9 +457,9 @@ static ssize_t iwl_dbgfs_bf_params_write(struct ieee80211_vif *vif, char *buf,
        mutex_lock(&mvm->mutex);
        iwl_dbgfs_update_bf(vif, param, value);
        if (param == MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER && !value)
-               ret = iwl_mvm_disable_beacon_filter(mvm, vif);
+               ret = iwl_mvm_disable_beacon_filter(mvm, vif, CMD_SYNC);
        else
-               ret = iwl_mvm_enable_beacon_filter(mvm, vif);
+               ret = iwl_mvm_enable_beacon_filter(mvm, vif, CMD_SYNC);
        mutex_unlock(&mvm->mutex);
 
        return ret ?: count;
@@ -460,6 +514,41 @@ static ssize_t iwl_dbgfs_bf_params_read(struct file *file,
        return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
 }
 
+static ssize_t iwl_dbgfs_low_latency_write(struct ieee80211_vif *vif, char *buf,
+                                          size_t count, loff_t *ppos)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_mvm *mvm = mvmvif->mvm;
+       u8 value;
+       int ret;
+
+       ret = kstrtou8(buf, 0, &value);
+       if (ret)
+               return ret;
+       if (value > 1)
+               return -EINVAL;
+
+       mutex_lock(&mvm->mutex);
+       iwl_mvm_update_low_latency(mvm, vif, value);
+       mutex_unlock(&mvm->mutex);
+
+       return count;
+}
+
+static ssize_t iwl_dbgfs_low_latency_read(struct file *file,
+                                         char __user *user_buf,
+                                         size_t count, loff_t *ppos)
+{
+       struct ieee80211_vif *vif = file->private_data;
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       char buf[3];
+
+       buf[0] = mvmvif->low_latency ? '1' : '0';
+       buf[1] = '\n';
+       buf[2] = '\0';
+       return simple_read_from_buffer(user_buf, count, ppos, buf, sizeof(buf));
+}
+
 #define MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \
        _MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz, struct ieee80211_vif)
 #define MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \
@@ -473,6 +562,8 @@ static ssize_t iwl_dbgfs_bf_params_read(struct file *file,
 MVM_DEBUGFS_READ_FILE_OPS(mac_params);
 MVM_DEBUGFS_READ_WRITE_FILE_OPS(pm_params, 32);
 MVM_DEBUGFS_READ_WRITE_FILE_OPS(bf_params, 256);
+MVM_DEBUGFS_READ_WRITE_FILE_OPS(low_latency, 10);
+MVM_DEBUGFS_WRITE_FILE_OPS(reduced_txp, 10);
 
 void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
 {
@@ -496,15 +587,18 @@ void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
                return;
        }
 
-       if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM &&
+       if ((mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_PM_CMD_SUPPORT) &&
+           iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM &&
            ((vif->type == NL80211_IFTYPE_STATION && !vif->p2p) ||
             (vif->type == NL80211_IFTYPE_STATION && vif->p2p &&
              mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PS)))
                MVM_DEBUGFS_ADD_FILE_VIF(pm_params, mvmvif->dbgfs_dir, S_IWUSR |
                                         S_IRUSR);
 
-       MVM_DEBUGFS_ADD_FILE_VIF(mac_params, mvmvif->dbgfs_dir,
-                                S_IRUSR);
+       MVM_DEBUGFS_ADD_FILE_VIF(mac_params, mvmvif->dbgfs_dir, S_IRUSR);
+       MVM_DEBUGFS_ADD_FILE_VIF(reduced_txp, mvmvif->dbgfs_dir, S_IWUSR);
+       MVM_DEBUGFS_ADD_FILE_VIF(low_latency, mvmvif->dbgfs_dir,
+                                S_IRUSR | S_IWUSR);
 
        if (vif->type == NL80211_IFTYPE_STATION && !vif->p2p &&
            mvmvif == mvm->bf_allowed_vif)
index 369d4c9..6853e5e 100644 (file)
@@ -90,7 +90,7 @@ static ssize_t iwl_dbgfs_tx_flush_write(struct iwl_mvm *mvm, char *buf,
 static ssize_t iwl_dbgfs_sta_drain_write(struct iwl_mvm *mvm, char *buf,
                                         size_t count, loff_t *ppos)
 {
-       struct ieee80211_sta *sta;
+       struct iwl_mvm_sta *mvmsta;
        int sta_id, drain, ret;
 
        if (!mvm->ucode_loaded || mvm->cur_ucode != IWL_UCODE_REGULAR)
@@ -105,13 +105,12 @@ static ssize_t iwl_dbgfs_sta_drain_write(struct iwl_mvm *mvm, char *buf,
 
        mutex_lock(&mvm->mutex);
 
-       sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
-                                       lockdep_is_held(&mvm->mutex));
-       if (IS_ERR_OR_NULL(sta))
+       mvmsta = iwl_mvm_sta_from_staid_protected(mvm, sta_id);
+
+       if (!mvmsta)
                ret = -ENOENT;
        else
-               ret = iwl_mvm_drain_sta(mvm, (void *)sta->drv_priv, drain) ? :
-                       count;
+               ret = iwl_mvm_drain_sta(mvm, mvmsta, drain) ? : count;
 
        mutex_unlock(&mvm->mutex);
 
@@ -251,7 +250,7 @@ static ssize_t iwl_dbgfs_disable_power_off_write(struct iwl_mvm *mvm, char *buf,
        }
 
        mutex_lock(&mvm->mutex);
-       ret = iwl_mvm_power_update_device_mode(mvm);
+       ret = iwl_mvm_power_update_device(mvm);
        mutex_unlock(&mvm->mutex);
 
        return ret ?: count;
@@ -600,6 +599,187 @@ iwl_dbgfs_scan_ant_rxchain_write(struct iwl_mvm *mvm, char *buf,
        return count;
 }
 
+#define ADD_TEXT(...) pos += scnprintf(buf + pos, bufsz - pos, __VA_ARGS__)
+#ifdef CONFIG_IWLWIFI_BCAST_FILTERING
+static ssize_t iwl_dbgfs_bcast_filters_read(struct file *file,
+                                           char __user *user_buf,
+                                           size_t count, loff_t *ppos)
+{
+       struct iwl_mvm *mvm = file->private_data;
+       struct iwl_bcast_filter_cmd cmd;
+       const struct iwl_fw_bcast_filter *filter;
+       char *buf;
+       int bufsz = 1024;
+       int i, j, pos = 0;
+       ssize_t ret;
+
+       buf = kzalloc(bufsz, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       mutex_lock(&mvm->mutex);
+       if (!iwl_mvm_bcast_filter_build_cmd(mvm, &cmd)) {
+               ADD_TEXT("None\n");
+               mutex_unlock(&mvm->mutex);
+               goto out;
+       }
+       mutex_unlock(&mvm->mutex);
+
+       for (i = 0; cmd.filters[i].attrs[0].mask; i++) {
+               filter = &cmd.filters[i];
+
+               ADD_TEXT("Filter [%d]:\n", i);
+               ADD_TEXT("\tDiscard=%d\n", filter->discard);
+               ADD_TEXT("\tFrame Type: %s\n",
+                        filter->frame_type ? "IPv4" : "Generic");
+
+               for (j = 0; j < ARRAY_SIZE(filter->attrs); j++) {
+                       const struct iwl_fw_bcast_filter_attr *attr;
+
+                       attr = &filter->attrs[j];
+                       if (!attr->mask)
+                               break;
+
+                       ADD_TEXT("\tAttr [%d]: offset=%d (from %s), mask=0x%x, value=0x%x reserved=0x%x\n",
+                                j, attr->offset,
+                                attr->offset_type ? "IP End" :
+                                                    "Payload Start",
+                                be32_to_cpu(attr->mask),
+                                be32_to_cpu(attr->val),
+                                le16_to_cpu(attr->reserved1));
+               }
+       }
+out:
+       ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+       kfree(buf);
+       return ret;
+}
+
+static ssize_t iwl_dbgfs_bcast_filters_write(struct iwl_mvm *mvm, char *buf,
+                                            size_t count, loff_t *ppos)
+{
+       int pos, next_pos;
+       struct iwl_fw_bcast_filter filter = {};
+       struct iwl_bcast_filter_cmd cmd;
+       u32 filter_id, attr_id, mask, value;
+       int err = 0;
+
+       if (sscanf(buf, "%d %hhi %hhi %n", &filter_id, &filter.discard,
+                  &filter.frame_type, &pos) != 3)
+               return -EINVAL;
+
+       if (filter_id >= ARRAY_SIZE(mvm->dbgfs_bcast_filtering.cmd.filters) ||
+           filter.frame_type > BCAST_FILTER_FRAME_TYPE_IPV4)
+               return -EINVAL;
+
+       for (attr_id = 0; attr_id < ARRAY_SIZE(filter.attrs);
+            attr_id++) {
+               struct iwl_fw_bcast_filter_attr *attr =
+                               &filter.attrs[attr_id];
+
+               if (pos >= count)
+                       break;
+
+               if (sscanf(&buf[pos], "%hhi %hhi %i %i %n",
+                          &attr->offset, &attr->offset_type,
+                          &mask, &value, &next_pos) != 4)
+                       return -EINVAL;
+
+               attr->mask = cpu_to_be32(mask);
+               attr->val = cpu_to_be32(value);
+               if (mask)
+                       filter.num_attrs++;
+
+               pos += next_pos;
+       }
+
+       mutex_lock(&mvm->mutex);
+       memcpy(&mvm->dbgfs_bcast_filtering.cmd.filters[filter_id],
+              &filter, sizeof(filter));
+
+       /* send updated bcast filtering configuration */
+       if (mvm->dbgfs_bcast_filtering.override &&
+           iwl_mvm_bcast_filter_build_cmd(mvm, &cmd))
+               err = iwl_mvm_send_cmd_pdu(mvm, BCAST_FILTER_CMD, CMD_SYNC,
+                                          sizeof(cmd), &cmd);
+       mutex_unlock(&mvm->mutex);
+
+       return err ?: count;
+}
+
+static ssize_t iwl_dbgfs_bcast_filters_macs_read(struct file *file,
+                                                char __user *user_buf,
+                                                size_t count, loff_t *ppos)
+{
+       struct iwl_mvm *mvm = file->private_data;
+       struct iwl_bcast_filter_cmd cmd;
+       char *buf;
+       int bufsz = 1024;
+       int i, pos = 0;
+       ssize_t ret;
+
+       buf = kzalloc(bufsz, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       mutex_lock(&mvm->mutex);
+       if (!iwl_mvm_bcast_filter_build_cmd(mvm, &cmd)) {
+               ADD_TEXT("None\n");
+               mutex_unlock(&mvm->mutex);
+               goto out;
+       }
+       mutex_unlock(&mvm->mutex);
+
+       for (i = 0; i < ARRAY_SIZE(cmd.macs); i++) {
+               const struct iwl_fw_bcast_mac *mac = &cmd.macs[i];
+
+               ADD_TEXT("Mac [%d]: discard=%d attached_filters=0x%x\n",
+                        i, mac->default_discard, mac->attached_filters);
+       }
+out:
+       ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+       kfree(buf);
+       return ret;
+}
+
+static ssize_t iwl_dbgfs_bcast_filters_macs_write(struct iwl_mvm *mvm,
+                                                 char *buf, size_t count,
+                                                 loff_t *ppos)
+{
+       struct iwl_bcast_filter_cmd cmd;
+       struct iwl_fw_bcast_mac mac = {};
+       u32 mac_id, attached_filters;
+       int err = 0;
+
+       if (!mvm->bcast_filters)
+               return -ENOENT;
+
+       if (sscanf(buf, "%d %hhi %i", &mac_id, &mac.default_discard,
+                  &attached_filters) != 3)
+               return -EINVAL;
+
+       if (mac_id >= ARRAY_SIZE(cmd.macs) ||
+           mac.default_discard > 1 ||
+           attached_filters >= BIT(ARRAY_SIZE(cmd.filters)))
+               return -EINVAL;
+
+       mac.attached_filters = cpu_to_le16(attached_filters);
+
+       mutex_lock(&mvm->mutex);
+       memcpy(&mvm->dbgfs_bcast_filtering.cmd.macs[mac_id],
+              &mac, sizeof(mac));
+
+       /* send updated bcast filtering configuration */
+       if (mvm->dbgfs_bcast_filtering.override &&
+           iwl_mvm_bcast_filter_build_cmd(mvm, &cmd))
+               err = iwl_mvm_send_cmd_pdu(mvm, BCAST_FILTER_CMD, CMD_SYNC,
+                                          sizeof(cmd), &cmd);
+       mutex_unlock(&mvm->mutex);
+
+       return err ?: count;
+}
+#endif
+
 #ifdef CONFIG_PM_SLEEP
 static ssize_t iwl_dbgfs_d3_sram_write(struct iwl_mvm *mvm, char *buf,
                                       size_t count, loff_t *ppos)
@@ -658,15 +838,74 @@ static ssize_t iwl_dbgfs_d3_sram_read(struct file *file, char __user *user_buf,
 }
 #endif
 
+#define PRINT_MVM_REF(ref) do {                                        \
+       if (test_bit(ref, mvm->ref_bitmap))                     \
+               pos += scnprintf(buf + pos, bufsz - pos,        \
+                                "\t(0x%lx) %s\n",              \
+                                BIT(ref), #ref);               \
+} while (0)
+
+static ssize_t iwl_dbgfs_d0i3_refs_read(struct file *file,
+                                       char __user *user_buf,
+                                       size_t count, loff_t *ppos)
+{
+       struct iwl_mvm *mvm = file->private_data;
+       int pos = 0;
+       char buf[256];
+       const size_t bufsz = sizeof(buf);
+
+       pos += scnprintf(buf + pos, bufsz - pos, "taken mvm refs: 0x%lx\n",
+                        mvm->ref_bitmap[0]);
+
+       PRINT_MVM_REF(IWL_MVM_REF_UCODE_DOWN);
+       PRINT_MVM_REF(IWL_MVM_REF_SCAN);
+       PRINT_MVM_REF(IWL_MVM_REF_ROC);
+       PRINT_MVM_REF(IWL_MVM_REF_P2P_CLIENT);
+       PRINT_MVM_REF(IWL_MVM_REF_AP_IBSS);
+       PRINT_MVM_REF(IWL_MVM_REF_USER);
+
+       return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+static ssize_t iwl_dbgfs_d0i3_refs_write(struct iwl_mvm *mvm, char *buf,
+                                        size_t count, loff_t *ppos)
+{
+       unsigned long value;
+       int ret;
+       bool taken;
+
+       ret = kstrtoul(buf, 10, &value);
+       if (ret < 0)
+               return ret;
+
+       mutex_lock(&mvm->mutex);
+
+       taken = test_bit(IWL_MVM_REF_USER, mvm->ref_bitmap);
+       if (value == 1 && !taken)
+               iwl_mvm_ref(mvm, IWL_MVM_REF_USER);
+       else if (value == 0 && taken)
+               iwl_mvm_unref(mvm, IWL_MVM_REF_USER);
+       else
+               ret = -EINVAL;
+
+       mutex_unlock(&mvm->mutex);
+
+       if (ret < 0)
+               return ret;
+       return count;
+}
+
 #define MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \
        _MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz, struct iwl_mvm)
 #define MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \
        _MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz, struct iwl_mvm)
-#define MVM_DEBUGFS_ADD_FILE(name, parent, mode) do {                  \
-               if (!debugfs_create_file(#name, mode, parent, mvm,      \
+#define MVM_DEBUGFS_ADD_FILE_ALIAS(alias, name, parent, mode) do {     \
+               if (!debugfs_create_file(alias, mode, parent, mvm,      \
                                         &iwl_dbgfs_##name##_ops))      \
                        goto err;                                       \
        } while (0)
+#define MVM_DEBUGFS_ADD_FILE(name, parent, mode) \
+       MVM_DEBUGFS_ADD_FILE_ALIAS(#name, name, parent, mode)
 
 /* Device wide debugfs entries */
 MVM_DEBUGFS_WRITE_FILE_OPS(tx_flush, 16);
@@ -680,6 +919,12 @@ MVM_DEBUGFS_READ_FILE_OPS(fw_rx_stats);
 MVM_DEBUGFS_WRITE_FILE_OPS(fw_restart, 10);
 MVM_DEBUGFS_WRITE_FILE_OPS(fw_nmi, 10);
 MVM_DEBUGFS_READ_WRITE_FILE_OPS(scan_ant_rxchain, 8);
+MVM_DEBUGFS_READ_WRITE_FILE_OPS(d0i3_refs, 8);
+
+#ifdef CONFIG_IWLWIFI_BCAST_FILTERING
+MVM_DEBUGFS_READ_WRITE_FILE_OPS(bcast_filters, 256);
+MVM_DEBUGFS_READ_WRITE_FILE_OPS(bcast_filters_macs, 256);
+#endif
 
 #ifdef CONFIG_PM_SLEEP
 MVM_DEBUGFS_READ_WRITE_FILE_OPS(d3_sram, 8);
@@ -687,6 +932,7 @@ MVM_DEBUGFS_READ_WRITE_FILE_OPS(d3_sram, 8);
 
 int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir)
 {
+       struct dentry *bcast_dir __maybe_unused;
        char buf[100];
 
        mvm->debugfs_dir = dbgfs_dir;
@@ -705,6 +951,27 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir)
        MVM_DEBUGFS_ADD_FILE(fw_nmi, mvm->debugfs_dir, S_IWUSR);
        MVM_DEBUGFS_ADD_FILE(scan_ant_rxchain, mvm->debugfs_dir,
                             S_IWUSR | S_IRUSR);
+       MVM_DEBUGFS_ADD_FILE(d0i3_refs, mvm->debugfs_dir, S_IRUSR | S_IWUSR);
+
+#ifdef CONFIG_IWLWIFI_BCAST_FILTERING
+       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BCAST_FILTERING) {
+               bcast_dir = debugfs_create_dir("bcast_filtering",
+                                              mvm->debugfs_dir);
+               if (!bcast_dir)
+                       goto err;
+
+               if (!debugfs_create_bool("override", S_IRUSR | S_IWUSR,
+                               bcast_dir,
+                               &mvm->dbgfs_bcast_filtering.override))
+                       goto err;
+
+               MVM_DEBUGFS_ADD_FILE_ALIAS("filters", bcast_filters,
+                                          bcast_dir, S_IWUSR | S_IRUSR);
+               MVM_DEBUGFS_ADD_FILE_ALIAS("macs", bcast_filters_macs,
+                                          bcast_dir, S_IWUSR | S_IRUSR);
+       }
+#endif
+
 #ifdef CONFIG_PM_SLEEP
        MVM_DEBUGFS_ADD_FILE(d3_sram, mvm->debugfs_dir, S_IRUSR | S_IWUSR);
        MVM_DEBUGFS_ADD_FILE(d3_test, mvm->debugfs_dir, S_IRUSR);
index 1b4e54d..20b723d 100644 (file)
 
 /**
  * enum iwl_bt_coex_flags - flags for BT_COEX command
- * @BT_CH_PRIMARY_EN:
- * @BT_CH_SECONDARY_EN:
- * @BT_NOTIF_COEX_OFF:
  * @BT_COEX_MODE_POS:
  * @BT_COEX_MODE_MSK:
  * @BT_COEX_DISABLE:
  * @BT_COEX_2W:
  * @BT_COEX_3W:
  * @BT_COEX_NW:
- * @BT_USE_DEFAULTS:
- * @BT_SYNC_2_BT_DISABLE:
- * @BT_COEX_CORUNNING_TBL_EN:
+ * @BT_COEX_SYNC2SCO:
  *
  * The COEX_MODE must be set for each command. Even if it is not changed.
  */
 enum iwl_bt_coex_flags {
-       BT_CH_PRIMARY_EN                = BIT(0),
-       BT_CH_SECONDARY_EN              = BIT(1),
-       BT_NOTIF_COEX_OFF               = BIT(2),
        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_USE_DEFAULTS                 = BIT(6),
-       BT_SYNC_2_BT_DISABLE            = BIT(7),
-       BT_COEX_CORUNNING_TBL_EN        = BIT(8),
-       BT_COEX_MPLUT_TBL_EN            = BIT(9),
-       /* Bit 10 is reserved */
-       BT_COEX_WF_PRIO_BOOST_CHECK_EN  = BIT(11),
+       BT_COEX_SYNC2SCO                = BIT(7),
 };
 
 /*
index 8415ff3..5219976 100644 (file)
@@ -231,8 +231,12 @@ enum iwl_wowlan_wakeup_filters {
        IWL_WOWLAN_WAKEUP_RF_KILL_DEASSERT              = BIT(8),
        IWL_WOWLAN_WAKEUP_REMOTE_LINK_LOSS              = BIT(9),
        IWL_WOWLAN_WAKEUP_REMOTE_SIGNATURE_TABLE        = BIT(10),
-       /* BIT(11) reserved */
+       IWL_WOWLAN_WAKEUP_REMOTE_TCP_EXTERNAL           = BIT(11),
        IWL_WOWLAN_WAKEUP_REMOTE_WAKEUP_PACKET          = BIT(12),
+       IWL_WOWLAN_WAKEUP_IOAC_MAGIC_PACKET             = BIT(13),
+       IWL_WOWLAN_WAKEUP_HOST_TIMER                    = BIT(14),
+       IWL_WOWLAN_WAKEUP_RX_FRAME                      = BIT(15),
+       IWL_WOWLAN_WAKEUP_BCN_FILTERING                 = BIT(16),
 }; /* WOWLAN_WAKEUP_FILTER_API_E_VER_4 */
 
 struct iwl_wowlan_config_cmd {
index 884c087..cbbcd8e 100644 (file)
@@ -301,54 +301,65 @@ struct iwl_beacon_filter_cmd {
 
 /* Beacon filtering and beacon abort */
 #define IWL_BF_ENERGY_DELTA_DEFAULT 5
+#define IWL_BF_ENERGY_DELTA_D0I3 20
 #define IWL_BF_ENERGY_DELTA_MAX 255
 #define IWL_BF_ENERGY_DELTA_MIN 0
 
 #define IWL_BF_ROAMING_ENERGY_DELTA_DEFAULT 1
+#define IWL_BF_ROAMING_ENERGY_DELTA_D0I3 20
 #define IWL_BF_ROAMING_ENERGY_DELTA_MAX 255
 #define IWL_BF_ROAMING_ENERGY_DELTA_MIN 0
 
 #define IWL_BF_ROAMING_STATE_DEFAULT 72
+#define IWL_BF_ROAMING_STATE_D0I3 72
 #define IWL_BF_ROAMING_STATE_MAX 255
 #define IWL_BF_ROAMING_STATE_MIN 0
 
 #define IWL_BF_TEMP_THRESHOLD_DEFAULT 112
+#define IWL_BF_TEMP_THRESHOLD_D0I3 112
 #define IWL_BF_TEMP_THRESHOLD_MAX 255
 #define IWL_BF_TEMP_THRESHOLD_MIN 0
 
 #define IWL_BF_TEMP_FAST_FILTER_DEFAULT 1
+#define IWL_BF_TEMP_FAST_FILTER_D0I3 1
 #define IWL_BF_TEMP_FAST_FILTER_MAX 255
 #define IWL_BF_TEMP_FAST_FILTER_MIN 0
 
 #define IWL_BF_TEMP_SLOW_FILTER_DEFAULT 5
+#define IWL_BF_TEMP_SLOW_FILTER_D0I3 5
 #define IWL_BF_TEMP_SLOW_FILTER_MAX 255
 #define IWL_BF_TEMP_SLOW_FILTER_MIN 0
 
 #define IWL_BF_ENABLE_BEACON_FILTER_DEFAULT 1
 
 #define IWL_BF_DEBUG_FLAG_DEFAULT 0
+#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_MAX 1024
 #define IWL_BF_ESCAPE_TIMER_MIN 0
 
 #define IWL_BA_ESCAPE_TIMER_DEFAULT 6
+#define IWL_BA_ESCAPE_TIMER_D0I3 6
 #define IWL_BA_ESCAPE_TIMER_D3 9
 #define IWL_BA_ESCAPE_TIMER_MAX 1024
 #define IWL_BA_ESCAPE_TIMER_MIN 0
 
 #define IWL_BA_ENABLE_BEACON_ABORT_DEFAULT 1
 
-#define IWL_BF_CMD_CONFIG_DEFAULTS                                          \
-       .bf_energy_delta = cpu_to_le32(IWL_BF_ENERGY_DELTA_DEFAULT),         \
-       .bf_roaming_energy_delta =                                           \
-               cpu_to_le32(IWL_BF_ROAMING_ENERGY_DELTA_DEFAULT),            \
-       .bf_roaming_state = cpu_to_le32(IWL_BF_ROAMING_STATE_DEFAULT),       \
-       .bf_temp_threshold = cpu_to_le32(IWL_BF_TEMP_THRESHOLD_DEFAULT),     \
-       .bf_temp_fast_filter = cpu_to_le32(IWL_BF_TEMP_FAST_FILTER_DEFAULT), \
-       .bf_temp_slow_filter = cpu_to_le32(IWL_BF_TEMP_SLOW_FILTER_DEFAULT), \
-       .bf_debug_flag = cpu_to_le32(IWL_BF_DEBUG_FLAG_DEFAULT),             \
-       .bf_escape_timer = cpu_to_le32(IWL_BF_ESCAPE_TIMER_DEFAULT),         \
-       .ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER_DEFAULT)
+#define IWL_BF_CMD_CONFIG(mode)                                             \
+       .bf_energy_delta = cpu_to_le32(IWL_BF_ENERGY_DELTA ## mode),          \
+       .bf_roaming_energy_delta =                                            \
+               cpu_to_le32(IWL_BF_ROAMING_ENERGY_DELTA ## mode),             \
+       .bf_roaming_state = cpu_to_le32(IWL_BF_ROAMING_STATE ## mode),        \
+       .bf_temp_threshold = cpu_to_le32(IWL_BF_TEMP_THRESHOLD ## mode),      \
+       .bf_temp_fast_filter = cpu_to_le32(IWL_BF_TEMP_FAST_FILTER ## mode),  \
+       .bf_temp_slow_filter = cpu_to_le32(IWL_BF_TEMP_SLOW_FILTER ## mode),  \
+       .bf_debug_flag = cpu_to_le32(IWL_BF_DEBUG_FLAG ## mode),              \
+       .bf_escape_timer = cpu_to_le32(IWL_BF_ESCAPE_TIMER ## mode),          \
+       .ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER ## mode)
 
+#define IWL_BF_CMD_CONFIG_DEFAULTS IWL_BF_CMD_CONFIG(_DEFAULT)
+#define IWL_BF_CMD_CONFIG_D0I3 IWL_BF_CMD_CONFIG(_D0I3)
 #endif
index 1b60fdf..d636478 100644 (file)
@@ -199,11 +199,14 @@ enum iwl_sta_modify_flag {
  * @STA_SLEEP_STATE_AWAKE:
  * @STA_SLEEP_STATE_PS_POLL:
  * @STA_SLEEP_STATE_UAPSD:
+ * @STA_SLEEP_STATE_MOREDATA: set more-data bit on
+ *     (last) released frame
  */
 enum iwl_sta_sleep_flag {
-       STA_SLEEP_STATE_AWAKE   = 0,
-       STA_SLEEP_STATE_PS_POLL = BIT(0),
-       STA_SLEEP_STATE_UAPSD   = BIT(1),
+       STA_SLEEP_STATE_AWAKE           = 0,
+       STA_SLEEP_STATE_PS_POLL         = BIT(0),
+       STA_SLEEP_STATE_UAPSD           = BIT(1),
+       STA_SLEEP_STATE_MOREDATA        = BIT(2),
 };
 
 /* STA ID and color bits definitions */
@@ -318,13 +321,15 @@ struct iwl_mvm_add_sta_cmd_v5 {
 } __packed; /* ADD_STA_CMD_API_S_VER_5 */
 
 /**
- * struct iwl_mvm_add_sta_cmd_v6 - Add / modify a station
- * VER_6 of this command is quite similar to VER_5 except
+ * struct iwl_mvm_add_sta_cmd_v7 - Add / modify a station
+ * VER_7 of this command is quite similar to VER_5 except
  * exclusion of all fields related to the security key installation.
+ * It only differs from VER_6 by the "awake_acs" field that is
+ * reserved and ignored in VER_6.
  */
-struct iwl_mvm_add_sta_cmd_v6 {
+struct iwl_mvm_add_sta_cmd_v7 {
        u8 add_modify;
-       u8 reserved1;
+       u8 awake_acs;
        __le16 tid_disable_tx;
        __le32 mac_id_n_color;
        u8 addr[ETH_ALEN];      /* _STA_ID_MODIFY_INFO_API_S_VER_1 */
@@ -342,7 +347,7 @@ struct iwl_mvm_add_sta_cmd_v6 {
        __le16 assoc_id;
        __le16 beamform_flags;
        __le32 tfd_queue_msk;
-} __packed; /* ADD_STA_CMD_API_S_VER_6 */
+} __packed; /* ADD_STA_CMD_API_S_VER_7 */
 
 /**
  * struct iwl_mvm_add_sta_key_cmd - add/modify sta key
@@ -432,5 +437,15 @@ struct iwl_mvm_wep_key_cmd {
        struct iwl_mvm_wep_key wep_key[0];
 } __packed; /* SEC_CURR_WEP_KEY_CMD_API_S_VER_2 */
 
+/**
+ * struct iwl_mvm_eosp_notification - EOSP notification from firmware
+ * @remain_frame_count: # of frames remaining, non-zero if SP was cut
+ *     short by GO absence
+ * @sta_id: station ID
+ */
+struct iwl_mvm_eosp_notification {
+       __le32 remain_frame_count;
+       __le32 sta_id;
+} __packed; /* UAPSD_EOSP_NTFY_API_S_VER_1 */
 
 #endif /* __fw_api_sta_h__ */
index 989d7db..a7c88f1 100644 (file)
@@ -163,6 +163,7 @@ enum {
        TX_ANT_CONFIGURATION_CMD = 0x98,
        BT_CONFIG = 0x9b,
        STATISTICS_NOTIFICATION = 0x9d,
+       EOSP_NOTIFICATION = 0x9e,
        REDUCE_TX_POWER_CMD = 0x9f,
 
        /* RF-KILL commands and notifications */
@@ -190,6 +191,7 @@ enum {
        REPLY_DEBUG_CMD = 0xf0,
        DEBUG_LOG_MSG = 0xf7,
 
+       BCAST_FILTER_CMD = 0xcf,
        MCAST_FILTER_CMD = 0xd0,
 
        /* D3 commands/notifications */
@@ -197,6 +199,7 @@ enum {
        PROT_OFFLOAD_CONFIG_CMD = 0xd4,
        OFFLOADS_QUERY_CMD = 0xd5,
        REMOTE_WAKE_CONFIG_CMD = 0xd6,
+       D0I3_END_CMD = 0xed,
 
        /* for WoWLAN in particular */
        WOWLAN_PATTERNS = 0xe0,
@@ -303,6 +306,7 @@ struct iwl_phy_cfg_cmd {
 #define PHY_CFG_RX_CHAIN_B     BIT(13)
 #define PHY_CFG_RX_CHAIN_C     BIT(14)
 
+#define NVM_MAX_NUM_SECTIONS   11
 
 /* Target of the NVM_ACCESS_CMD */
 enum {
@@ -313,14 +317,9 @@ enum {
 
 /* Section types for NVM_ACCESS_CMD */
 enum {
-       NVM_SECTION_TYPE_HW = 0,
-       NVM_SECTION_TYPE_SW,
-       NVM_SECTION_TYPE_PAPD,
-       NVM_SECTION_TYPE_BT,
-       NVM_SECTION_TYPE_CALIBRATION,
-       NVM_SECTION_TYPE_PRODUCTION,
-       NVM_SECTION_TYPE_POST_FCS_CALIB,
-       NVM_NUM_OF_SECTIONS,
+       NVM_SECTION_TYPE_SW = 1,
+       NVM_SECTION_TYPE_CALIBRATION = 4,
+       NVM_SECTION_TYPE_PRODUCTION = 5,
 };
 
 /**
@@ -412,6 +411,35 @@ struct mvm_alive_resp {
        __le32 scd_base_ptr;            /* SRAM address for SCD */
 } __packed; /* ALIVE_RES_API_S_VER_1 */
 
+struct mvm_alive_resp_ver2 {
+       __le16 status;
+       __le16 flags;
+       u8 ucode_minor;
+       u8 ucode_major;
+       __le16 id;
+       u8 api_minor;
+       u8 api_major;
+       u8 ver_subtype;
+       u8 ver_type;
+       u8 mac;
+       u8 opt;
+       __le16 reserved2;
+       __le32 timestamp;
+       __le32 error_event_table_ptr;   /* SRAM address for error log */
+       __le32 log_event_table_ptr;     /* SRAM address for LMAC event log */
+       __le32 cpu_register_ptr;
+       __le32 dbgm_config_ptr;
+       __le32 alive_counter_ptr;
+       __le32 scd_base_ptr;            /* SRAM address for SCD */
+       __le32 st_fwrd_addr;            /* pointer to Store and forward */
+       __le32 st_fwrd_size;
+       u8 umac_minor;                  /* UMAC version: minor */
+       u8 umac_major;                  /* UMAC version: major */
+       __le16 umac_id;                 /* UMAC version: id */
+       __le32 error_info_addr;         /* SRAM address for UMAC error log */
+       __le32 dbg_print_buff_addr;
+} __packed; /* ALIVE_RES_API_S_VER_2 */
+
 /* Error response/notification */
 enum {
        FW_ERR_UNKNOWN_CMD = 0x0,
@@ -1159,6 +1187,90 @@ struct iwl_mcast_filter_cmd {
        u8 addr_list[0];
 } __packed; /* MCAST_FILTERING_CMD_API_S_VER_1 */
 
+#define MAX_BCAST_FILTERS 8
+#define MAX_BCAST_FILTER_ATTRS 2
+
+/**
+ * enum iwl_mvm_bcast_filter_attr_offset - written by fw for each Rx packet
+ * @BCAST_FILTER_OFFSET_PAYLOAD_START: offset is from payload start.
+ * @BCAST_FILTER_OFFSET_IP_END: offset is from ip header end (i.e.
+ *     start of ip payload).
+ */
+enum iwl_mvm_bcast_filter_attr_offset {
+       BCAST_FILTER_OFFSET_PAYLOAD_START = 0,
+       BCAST_FILTER_OFFSET_IP_END = 1,
+};
+
+/**
+ * struct iwl_fw_bcast_filter_attr - broadcast filter attribute
+ * @offset_type:       &enum iwl_mvm_bcast_filter_attr_offset.
+ * @offset:    starting offset of this pattern.
+ * @val:               value to match - big endian (MSB is the first
+ *             byte to match from offset pos).
+ * @mask:      mask to match (big endian).
+ */
+struct iwl_fw_bcast_filter_attr {
+       u8 offset_type;
+       u8 offset;
+       __le16 reserved1;
+       __be32 val;
+       __be32 mask;
+} __packed; /* BCAST_FILTER_ATT_S_VER_1 */
+
+/**
+ * enum iwl_mvm_bcast_filter_frame_type - filter frame type
+ * @BCAST_FILTER_FRAME_TYPE_ALL: consider all frames.
+ * @BCAST_FILTER_FRAME_TYPE_IPV4: consider only ipv4 frames
+ */
+enum iwl_mvm_bcast_filter_frame_type {
+       BCAST_FILTER_FRAME_TYPE_ALL = 0,
+       BCAST_FILTER_FRAME_TYPE_IPV4 = 1,
+};
+
+/**
+ * struct iwl_fw_bcast_filter - broadcast filter
+ * @discard: discard frame (1) or let it pass (0).
+ * @frame_type: &enum iwl_mvm_bcast_filter_frame_type.
+ * @num_attrs: number of valid attributes in this filter.
+ * @attrs: attributes of this filter. a filter is considered matched
+ *     only when all its attributes are matched (i.e. AND relationship)
+ */
+struct iwl_fw_bcast_filter {
+       u8 discard;
+       u8 frame_type;
+       u8 num_attrs;
+       u8 reserved1;
+       struct iwl_fw_bcast_filter_attr attrs[MAX_BCAST_FILTER_ATTRS];
+} __packed; /* BCAST_FILTER_S_VER_1 */
+
+/**
+ * struct iwl_fw_bcast_mac - per-mac broadcast filtering configuration.
+ * @default_discard: default action for this mac (discard (1) / pass (0)).
+ * @attached_filters: bitmap of relevant filters for this mac.
+ */
+struct iwl_fw_bcast_mac {
+       u8 default_discard;
+       u8 reserved1;
+       __le16 attached_filters;
+} __packed; /* BCAST_MAC_CONTEXT_S_VER_1 */
+
+/**
+ * struct iwl_bcast_filter_cmd - broadcast filtering configuration
+ * @disable: enable (0) / disable (1)
+ * @max_bcast_filters: max number of filters (MAX_BCAST_FILTERS)
+ * @max_macs: max number of macs (NUM_MAC_INDEX_DRIVER)
+ * @filters: broadcast filters
+ * @macs: broadcast filtering configuration per-mac
+ */
+struct iwl_bcast_filter_cmd {
+       u8 disable;
+       u8 max_bcast_filters;
+       u8 max_macs;
+       u8 reserved1;
+       struct iwl_fw_bcast_filter filters[MAX_BCAST_FILTERS];
+       struct iwl_fw_bcast_mac macs[NUM_MAC_INDEX_DRIVER];
+} __packed; /* BCAST_FILTERING_HCMD_API_S_VER_1 */
+
 struct mvm_statistics_dbg {
        __le32 burst_check;
        __le32 burst_count;
index c03d395..bae75b3 100644 (file)
@@ -110,18 +110,46 @@ static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait,
                container_of(notif_wait, struct iwl_mvm, notif_wait);
        struct iwl_mvm_alive_data *alive_data = data;
        struct mvm_alive_resp *palive;
-
-       palive = (void *)pkt->data;
-
-       mvm->error_event_table = le32_to_cpu(palive->error_event_table_ptr);
-       mvm->log_event_table = le32_to_cpu(palive->log_event_table_ptr);
-       alive_data->scd_base_addr = le32_to_cpu(palive->scd_base_ptr);
-
-       alive_data->valid = le16_to_cpu(palive->status) == IWL_ALIVE_STATUS_OK;
-       IWL_DEBUG_FW(mvm,
-                    "Alive ucode status 0x%04x revision 0x%01X 0x%01X flags 0x%01X\n",
-                    le16_to_cpu(palive->status), palive->ver_type,
-                    palive->ver_subtype, palive->flags);
+       struct mvm_alive_resp_ver2 *palive2;
+
+       if (iwl_rx_packet_payload_len(pkt) == sizeof(*palive)) {
+               palive = (void *)pkt->data;
+
+               mvm->support_umac_log = false;
+               mvm->error_event_table =
+                       le32_to_cpu(palive->error_event_table_ptr);
+               mvm->log_event_table = le32_to_cpu(palive->log_event_table_ptr);
+               alive_data->scd_base_addr = le32_to_cpu(palive->scd_base_ptr);
+
+               alive_data->valid = le16_to_cpu(palive->status) ==
+                                   IWL_ALIVE_STATUS_OK;
+               IWL_DEBUG_FW(mvm,
+                            "Alive VER1 ucode status 0x%04x revision 0x%01X 0x%01X flags 0x%01X\n",
+                            le16_to_cpu(palive->status), palive->ver_type,
+                            palive->ver_subtype, palive->flags);
+       } else {
+               palive2 = (void *)pkt->data;
+
+               mvm->support_umac_log = true;
+               mvm->error_event_table =
+                       le32_to_cpu(palive2->error_event_table_ptr);
+               mvm->log_event_table =
+                       le32_to_cpu(palive2->log_event_table_ptr);
+               alive_data->scd_base_addr = le32_to_cpu(palive2->scd_base_ptr);
+               mvm->umac_error_event_table =
+                       le32_to_cpu(palive2->error_info_addr);
+
+               alive_data->valid = le16_to_cpu(palive2->status) ==
+                                   IWL_ALIVE_STATUS_OK;
+               IWL_DEBUG_FW(mvm,
+                            "Alive VER2 ucode status 0x%04x revision 0x%01X 0x%01X flags 0x%01X\n",
+                            le16_to_cpu(palive2->status), palive2->ver_type,
+                            palive2->ver_subtype, palive2->flags);
+
+               IWL_DEBUG_FW(mvm,
+                            "UMAC version: Major - 0x%x, Minor - 0x%x\n",
+                            palive2->umac_major, palive2->umac_minor);
+       }
 
        return true;
 }
@@ -439,10 +467,23 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
                        goto error;
        }
 
-       ret = iwl_mvm_power_update_device_mode(mvm);
+       /* Initialize tx backoffs to the minimal possible */
+       iwl_mvm_tt_tx_backoff(mvm, 0);
+
+       if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_PM_CMD_SUPPORT)) {
+               ret = iwl_power_legacy_set_cam_mode(mvm);
+               if (ret)
+                       goto error;
+       }
+
+       ret = iwl_mvm_power_update_device(mvm);
        if (ret)
                goto error;
 
+       /* allow FW/transport low power modes if not during restart */
+       if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
+               iwl_mvm_unref(mvm, IWL_MVM_REF_UCODE_DOWN);
+
        IWL_DEBUG_INFO(mvm, "RT uCode started.\n");
        return 0;
  error:
index ba723d5..5c21aab 100644 (file)
@@ -90,6 +90,7 @@ static void iwl_mvm_mac_tsf_id_iter(void *_data, u8 *mac,
 {
        struct iwl_mvm_mac_iface_iterator_data *data = _data;
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       u16 min_bi;
 
        /* Skip the interface for which we are trying to assign a tsf_id  */
        if (vif == data->vif)
@@ -114,42 +115,57 @@ static void iwl_mvm_mac_tsf_id_iter(void *_data, u8 *mac,
        switch (data->vif->type) {
        case NL80211_IFTYPE_STATION:
                /*
-                * The new interface is client, so if the existing one
-                * we're iterating is an AP, and both interfaces have the
-                * same beacon interval, the same TSF should be used to
-                * avoid drift between the new client and existing AP,
-                * the existing AP will get drift updates from the new
-                * client context in this case
+                * The new interface is a client, so if the one we're iterating
+                * is an AP, and the beacon interval of the AP is a multiple or
+                * divisor of the beacon interval of the client, the same TSF
+                * should be used to avoid drift between the new client and
+                * existing AP. The existing AP will get drift updates from the
+                * new client context in this case.
                 */
-               if (vif->type == NL80211_IFTYPE_AP) {
-                       if (data->preferred_tsf == NUM_TSF_IDS &&
-                           test_bit(mvmvif->tsf_id, data->available_tsf_ids) &&
-                           (vif->bss_conf.beacon_int ==
-                            data->vif->bss_conf.beacon_int)) {
-                               data->preferred_tsf = mvmvif->tsf_id;
-                               return;
-                       }
+               if (vif->type != NL80211_IFTYPE_AP ||
+                   data->preferred_tsf != NUM_TSF_IDS ||
+                   !test_bit(mvmvif->tsf_id, data->available_tsf_ids))
+                       break;
+
+               min_bi = min(data->vif->bss_conf.beacon_int,
+                            vif->bss_conf.beacon_int);
+
+               if (!min_bi)
+                       break;
+
+               if ((data->vif->bss_conf.beacon_int -
+                    vif->bss_conf.beacon_int) % min_bi == 0) {
+                       data->preferred_tsf = mvmvif->tsf_id;
+                       return;
                }
                break;
+
        case NL80211_IFTYPE_AP:
                /*
-                * The new interface is AP/GO, so in case both interfaces
-                * have the same beacon interval, it should get drift
-                * updates from an existing client or use the same
-                * TSF as an existing GO. There's no drift between
-                * TSFs internally but if they used different TSFs
-                * then a new client MAC could update one of them
-                * and cause drift that way.
+                * The new interface is AP/GO, so if its beacon interval is a
+                * multiple or a divisor of the beacon interval of an existing
+                * interface, it should get drift updates from an existing
+                * client or use the same TSF as an existing GO. There's no
+                * drift between TSFs internally but if they used different
+                * TSFs then a new client MAC could update one of them and
+                * cause drift that way.
                 */
-               if (vif->type == NL80211_IFTYPE_STATION ||
-                   vif->type == NL80211_IFTYPE_AP) {
-                       if (data->preferred_tsf == NUM_TSF_IDS &&
-                           test_bit(mvmvif->tsf_id, data->available_tsf_ids) &&
-                           (vif->bss_conf.beacon_int ==
-                            data->vif->bss_conf.beacon_int)) {
-                               data->preferred_tsf = mvmvif->tsf_id;
-                               return;
-                       }
+               if ((vif->type != NL80211_IFTYPE_AP &&
+                    vif->type != NL80211_IFTYPE_STATION) ||
+                   data->preferred_tsf != NUM_TSF_IDS ||
+                   !test_bit(mvmvif->tsf_id, data->available_tsf_ids))
+                       break;
+
+               min_bi = min(data->vif->bss_conf.beacon_int,
+                            vif->bss_conf.beacon_int);
+
+               if (!min_bi)
+                       break;
+
+               if ((data->vif->bss_conf.beacon_int -
+                    vif->bss_conf.beacon_int) % min_bi == 0) {
+                       data->preferred_tsf = mvmvif->tsf_id;
+                       return;
                }
                break;
        default:
index c35b866..a49d1ef 100644 (file)
@@ -66,6 +66,7 @@
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/ip.h>
+#include <linux/if_arp.h>
 #include <net/mac80211.h>
 #include <net/tcp.h>
 
@@ -128,6 +129,117 @@ static const struct wiphy_wowlan_tcp_support iwl_mvm_wowlan_tcp_support = {
 };
 #endif
 
+#ifdef CONFIG_IWLWIFI_BCAST_FILTERING
+/*
+ * Use the reserved field to indicate magic values.
+ * these values will only be used internally by the driver,
+ * and won't make it to the fw (reserved will be 0).
+ * BC_FILTER_MAGIC_IP - configure the val of this attribute to
+ *     be the vif's ip address. in case there is not a single
+ *     ip address (0, or more than 1), this attribute will
+ *     be skipped.
+ * BC_FILTER_MAGIC_MAC - set the val of this attribute to
+ *     the LSB bytes of the vif's mac address
+ */
+enum {
+       BC_FILTER_MAGIC_NONE = 0,
+       BC_FILTER_MAGIC_IP,
+       BC_FILTER_MAGIC_MAC,
+};
+
+static const struct iwl_fw_bcast_filter iwl_mvm_default_bcast_filters[] = {
+       {
+               /* arp */
+               .discard = 0,
+               .frame_type = BCAST_FILTER_FRAME_TYPE_ALL,
+               .attrs = {
+                       {
+                               /* frame type - arp, hw type - ethernet */
+                               .offset_type =
+                                       BCAST_FILTER_OFFSET_PAYLOAD_START,
+                               .offset = sizeof(rfc1042_header),
+                               .val = cpu_to_be32(0x08060001),
+                               .mask = cpu_to_be32(0xffffffff),
+                       },
+                       {
+                               /* arp dest ip */
+                               .offset_type =
+                                       BCAST_FILTER_OFFSET_PAYLOAD_START,
+                               .offset = sizeof(rfc1042_header) + 2 +
+                                         sizeof(struct arphdr) +
+                                         ETH_ALEN + sizeof(__be32) +
+                                         ETH_ALEN,
+                               .mask = cpu_to_be32(0xffffffff),
+                               /* mark it as special field */
+                               .reserved1 = cpu_to_le16(BC_FILTER_MAGIC_IP),
+                       },
+               },
+       },
+       {
+               /* dhcp offer bcast */
+               .discard = 0,
+               .frame_type = BCAST_FILTER_FRAME_TYPE_IPV4,
+               .attrs = {
+                       {
+                               /* udp dest port - 68 (bootp client)*/
+                               .offset_type = BCAST_FILTER_OFFSET_IP_END,
+                               .offset = offsetof(struct udphdr, dest),
+                               .val = cpu_to_be32(0x00440000),
+                               .mask = cpu_to_be32(0xffff0000),
+                       },
+                       {
+                               /* dhcp - lsb bytes of client hw address */
+                               .offset_type = BCAST_FILTER_OFFSET_IP_END,
+                               .offset = 38,
+                               .mask = cpu_to_be32(0xffffffff),
+                               /* mark it as special field */
+                               .reserved1 = cpu_to_le16(BC_FILTER_MAGIC_MAC),
+                       },
+               },
+       },
+       /* last filter must be empty */
+       {},
+};
+#endif
+
+void iwl_mvm_ref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type)
+{
+       if (!mvm->trans->cfg->d0i3)
+               return;
+
+       IWL_DEBUG_RPM(mvm, "Take mvm reference - type %d\n", ref_type);
+       WARN_ON(test_and_set_bit(ref_type, mvm->ref_bitmap));
+       iwl_trans_ref(mvm->trans);
+}
+
+void iwl_mvm_unref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type)
+{
+       if (!mvm->trans->cfg->d0i3)
+               return;
+
+       IWL_DEBUG_RPM(mvm, "Leave mvm reference - type %d\n", ref_type);
+       WARN_ON(!test_and_clear_bit(ref_type, mvm->ref_bitmap));
+       iwl_trans_unref(mvm->trans);
+}
+
+static void
+iwl_mvm_unref_all_except(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref)
+{
+       int i;
+
+       if (!mvm->trans->cfg->d0i3)
+               return;
+
+       for_each_set_bit(i, mvm->ref_bitmap, IWL_MVM_REF_COUNT) {
+               if (ref == i)
+                       continue;
+
+               IWL_DEBUG_RPM(mvm, "Cleanup: remove mvm ref type %d\n", i);
+               clear_bit(i, mvm->ref_bitmap);
+               iwl_trans_unref(mvm->trans);
+       }
+}
+
 static void iwl_mvm_reset_phy_ctxts(struct iwl_mvm *mvm)
 {
        int i;
@@ -203,6 +315,9 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
        hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG |
                                       REGULATORY_DISABLE_BEACON_HINTS;
 
+       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_GO_UAPSD)
+               hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
+
        hw->wiphy->iface_combinations = iwl_mvm_iface_combinations;
        hw->wiphy->n_iface_combinations =
                ARRAY_SIZE(iwl_mvm_iface_combinations);
@@ -289,6 +404,11 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
        }
 #endif
 
+#ifdef CONFIG_IWLWIFI_BCAST_FILTERING
+       /* assign default bcast filtering configuration */
+       mvm->bcast_filters = iwl_mvm_default_bcast_filters;
+#endif
+
        ret = iwl_mvm_leds_init(mvm);
        if (ret)
                return ret;
@@ -305,6 +425,9 @@ static void iwl_mvm_mac_tx(struct ieee80211_hw *hw,
                           struct sk_buff *skb)
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct ieee80211_sta *sta = control->sta;
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct ieee80211_hdr *hdr = (void *)skb->data;
 
        if (iwl_mvm_is_radio_killed(mvm)) {
                IWL_DEBUG_DROP(mvm, "Dropping - RF/CT KILL\n");
@@ -315,8 +438,16 @@ static void iwl_mvm_mac_tx(struct ieee80211_hw *hw,
            !test_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status))
                goto drop;
 
-       if (control->sta) {
-               if (iwl_mvm_tx_skb(mvm, skb, control->sta))
+       /* treat non-bufferable MMPDUs as broadcast if sta is sleeping */
+       if (unlikely(info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER &&
+                    ieee80211_is_mgmt(hdr->frame_control) &&
+                    !ieee80211_is_deauth(hdr->frame_control) &&
+                    !ieee80211_is_disassoc(hdr->frame_control) &&
+                    !ieee80211_is_action(hdr->frame_control)))
+               sta = NULL;
+
+       if (sta) {
+               if (iwl_mvm_tx_skb(mvm, skb, sta))
                        goto drop;
                return;
        }
@@ -434,6 +565,7 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
                iwl_mvm_cleanup_iterator, mvm);
 
        mvm->p2p_device_vif = NULL;
+       mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT;
 
        iwl_mvm_reset_phy_ctxts(mvm);
        memset(mvm->fw_key_table, 0, sizeof(mvm->fw_key_table));
@@ -441,6 +573,10 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
 
        ieee80211_wake_queues(mvm->hw);
 
+       /* cleanup all stale references (scan, roc), but keep the
+        * ucode_down ref until reconfig is complete */
+       iwl_mvm_unref_all_except(mvm, IWL_MVM_REF_UCODE_DOWN);
+
        mvm->vif_count = 0;
        mvm->rx_ba_sessions = 0;
 }
@@ -475,6 +611,9 @@ static void iwl_mvm_mac_restart_complete(struct ieee80211_hw *hw)
                IWL_ERR(mvm, "Failed to update quotas after restart (%d)\n",
                        ret);
 
+       /* allow transport/FW low power modes */
+       iwl_mvm_unref(mvm, IWL_MVM_REF_UCODE_DOWN);
+
        mutex_unlock(&mvm->mutex);
 }
 
@@ -482,9 +621,14 @@ static void iwl_mvm_mac_stop(struct ieee80211_hw *hw)
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
 
+       flush_work(&mvm->d0i3_exit_work);
        flush_work(&mvm->async_handlers_wk);
 
        mutex_lock(&mvm->mutex);
+
+       /* disallow low power states when the FW is down */
+       iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN);
+
        /* async_handlers_wk is now blocked */
 
        /*
@@ -510,14 +654,6 @@ static void iwl_mvm_mac_stop(struct ieee80211_hw *hw)
        cancel_work_sync(&mvm->async_handlers_wk);
 }
 
-static void iwl_mvm_power_update_iterator(void *data, u8 *mac,
-                                         struct ieee80211_vif *vif)
-{
-       struct iwl_mvm *mvm = data;
-
-       iwl_mvm_power_update_mode(mvm, vif);
-}
-
 static struct iwl_mvm_phy_ctxt *iwl_mvm_get_free_phy_ctxt(struct iwl_mvm *mvm)
 {
        u16 i;
@@ -585,7 +721,8 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
            vif->type == NL80211_IFTYPE_ADHOC) {
                u32 qmask = iwl_mvm_mac_get_queues_mask(mvm, vif);
                ret = iwl_mvm_allocate_int_sta(mvm, &mvmvif->bcast_sta,
-                                              qmask);
+                                              qmask,
+                                              ieee80211_vif_type_p2p(vif));
                if (ret) {
                        IWL_ERR(mvm, "Failed to allocate bcast sta\n");
                        goto out_release;
@@ -599,10 +736,12 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
        if (ret)
                goto out_release;
 
-       iwl_mvm_power_disable(mvm, vif);
+       ret = iwl_mvm_power_update_mac(mvm, vif);
+       if (ret)
+               goto out_release;
 
        /* beacon filtering */
-       ret = iwl_mvm_disable_beacon_filter(mvm, vif);
+       ret = iwl_mvm_disable_beacon_filter(mvm, vif, CMD_SYNC);
        if (ret)
                goto out_remove_mac;
 
@@ -661,11 +800,6 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
        if (vif->type != NL80211_IFTYPE_P2P_DEVICE)
                mvm->vif_count--;
 
-       /* TODO: remove this when legacy PM will be discarded */
-       ieee80211_iterate_active_interfaces(
-               mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
-               iwl_mvm_power_update_iterator, mvm);
-
        iwl_mvm_mac_ctxt_release(mvm, vif);
  out_unlock:
        mutex_unlock(&mvm->mutex);
@@ -754,11 +888,7 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw,
        if (mvm->vif_count && vif->type != NL80211_IFTYPE_P2P_DEVICE)
                mvm->vif_count--;
 
-       /* TODO: remove this when legacy PM will be discarded */
-       ieee80211_iterate_active_interfaces(
-               mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
-               iwl_mvm_power_update_iterator, mvm);
-
+       iwl_mvm_power_update_mac(mvm, vif);
        iwl_mvm_mac_ctxt_remove(mvm, vif);
 
 out_release:
@@ -876,6 +1006,156 @@ out:
        *total_flags = 0;
 }
 
+#ifdef CONFIG_IWLWIFI_BCAST_FILTERING
+struct iwl_bcast_iter_data {
+       struct iwl_mvm *mvm;
+       struct iwl_bcast_filter_cmd *cmd;
+       u8 current_filter;
+};
+
+static void
+iwl_mvm_set_bcast_filter(struct ieee80211_vif *vif,
+                        const struct iwl_fw_bcast_filter *in_filter,
+                        struct iwl_fw_bcast_filter *out_filter)
+{
+       struct iwl_fw_bcast_filter_attr *attr;
+       int i;
+
+       memcpy(out_filter, in_filter, sizeof(*out_filter));
+
+       for (i = 0; i < ARRAY_SIZE(out_filter->attrs); i++) {
+               attr = &out_filter->attrs[i];
+
+               if (!attr->mask)
+                       break;
+
+               switch (attr->reserved1) {
+               case cpu_to_le16(BC_FILTER_MAGIC_IP):
+                       if (vif->bss_conf.arp_addr_cnt != 1) {
+                               attr->mask = 0;
+                               continue;
+                       }
+
+                       attr->val = vif->bss_conf.arp_addr_list[0];
+                       break;
+               case cpu_to_le16(BC_FILTER_MAGIC_MAC):
+                       attr->val = *(__be32 *)&vif->addr[2];
+                       break;
+               default:
+                       break;
+               }
+               attr->reserved1 = 0;
+               out_filter->num_attrs++;
+       }
+}
+
+static void iwl_mvm_bcast_filter_iterator(void *_data, u8 *mac,
+                                         struct ieee80211_vif *vif)
+{
+       struct iwl_bcast_iter_data *data = _data;
+       struct iwl_mvm *mvm = data->mvm;
+       struct iwl_bcast_filter_cmd *cmd = data->cmd;
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_fw_bcast_mac *bcast_mac;
+       int i;
+
+       if (WARN_ON(mvmvif->id >= ARRAY_SIZE(cmd->macs)))
+               return;
+
+       bcast_mac = &cmd->macs[mvmvif->id];
+
+       /* enable filtering only for associated stations */
+       if (vif->type != NL80211_IFTYPE_STATION || !vif->bss_conf.assoc)
+               return;
+
+       bcast_mac->default_discard = 1;
+
+       /* copy all configured filters */
+       for (i = 0; mvm->bcast_filters[i].attrs[0].mask; i++) {
+               /*
+                * Make sure we don't exceed our filters limit.
+                * if there is still a valid filter to be configured,
+                * be on the safe side and just allow bcast for this mac.
+                */
+               if (WARN_ON_ONCE(data->current_filter >=
+                                ARRAY_SIZE(cmd->filters))) {
+                       bcast_mac->default_discard = 0;
+                       bcast_mac->attached_filters = 0;
+                       break;
+               }
+
+               iwl_mvm_set_bcast_filter(vif,
+                                        &mvm->bcast_filters[i],
+                                        &cmd->filters[data->current_filter]);
+
+               /* skip current filter if it contains no attributes */
+               if (!cmd->filters[data->current_filter].num_attrs)
+                       continue;
+
+               /* attach the filter to current mac */
+               bcast_mac->attached_filters |=
+                               cpu_to_le16(BIT(data->current_filter));
+
+               data->current_filter++;
+       }
+}
+
+bool iwl_mvm_bcast_filter_build_cmd(struct iwl_mvm *mvm,
+                                   struct iwl_bcast_filter_cmd *cmd)
+{
+       struct iwl_bcast_iter_data iter_data = {
+               .mvm = mvm,
+               .cmd = cmd,
+       };
+
+       memset(cmd, 0, sizeof(*cmd));
+       cmd->max_bcast_filters = ARRAY_SIZE(cmd->filters);
+       cmd->max_macs = ARRAY_SIZE(cmd->macs);
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+       /* use debugfs filters/macs if override is configured */
+       if (mvm->dbgfs_bcast_filtering.override) {
+               memcpy(cmd->filters, &mvm->dbgfs_bcast_filtering.cmd.filters,
+                      sizeof(cmd->filters));
+               memcpy(cmd->macs, &mvm->dbgfs_bcast_filtering.cmd.macs,
+                      sizeof(cmd->macs));
+               return true;
+       }
+#endif
+
+       /* if no filters are configured, do nothing */
+       if (!mvm->bcast_filters)
+               return false;
+
+       /* configure and attach these filters for each associated sta vif */
+       ieee80211_iterate_active_interfaces(
+               mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+               iwl_mvm_bcast_filter_iterator, &iter_data);
+
+       return true;
+}
+static int iwl_mvm_configure_bcast_filter(struct iwl_mvm *mvm,
+                                         struct ieee80211_vif *vif)
+{
+       struct iwl_bcast_filter_cmd cmd;
+
+       if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BCAST_FILTERING))
+               return 0;
+
+       if (!iwl_mvm_bcast_filter_build_cmd(mvm, &cmd))
+               return 0;
+
+       return iwl_mvm_send_cmd_pdu(mvm, BCAST_FILTER_CMD, CMD_SYNC,
+                                   sizeof(cmd), &cmd);
+}
+#else
+static inline int iwl_mvm_configure_bcast_filter(struct iwl_mvm *mvm,
+                                                struct ieee80211_vif *vif)
+{
+       return 0;
+}
+#endif
+
 static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
                                             struct ieee80211_vif *vif,
                                             struct ieee80211_bss_conf *bss_conf,
@@ -928,6 +1208,8 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
 
                        iwl_mvm_sf_update(mvm, vif, false);
                        iwl_mvm_power_vif_assoc(mvm, vif);
+                       if (vif->p2p)
+                               iwl_mvm_ref(mvm, IWL_MVM_REF_P2P_CLIENT);
                } else if (mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) {
                        /*
                         * If update fails - SF might be running in associated
@@ -940,27 +1222,25 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
                        ret = iwl_mvm_rm_sta_id(mvm, vif, mvmvif->ap_sta_id);
                        if (ret)
                                IWL_ERR(mvm, "failed to remove AP station\n");
+
+                       if (mvm->d0i3_ap_sta_id == mvmvif->ap_sta_id)
+                               mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT;
                        mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT;
                        /* remove quota for this interface */
                        ret = iwl_mvm_update_quotas(mvm, NULL);
                        if (ret)
                                IWL_ERR(mvm, "failed to update quotas\n");
+
+                       if (vif->p2p)
+                               iwl_mvm_unref(mvm, IWL_MVM_REF_P2P_CLIENT);
                }
 
                iwl_mvm_recalc_multicast(mvm);
+               iwl_mvm_configure_bcast_filter(mvm, vif);
 
                /* reset rssi values */
                mvmvif->bf_data.ave_beacon_signal = 0;
 
-               if (!(mvm->fw->ucode_capa.flags &
-                                       IWL_UCODE_TLV_FLAGS_PM_CMD_SUPPORT)) {
-                       /* Workaround for FW bug, otherwise FW disables device
-                        * power save upon disassociation
-                        */
-                       ret = iwl_mvm_power_update_mode(mvm, vif);
-                       if (ret)
-                               IWL_ERR(mvm, "failed to update power mode\n");
-               }
                iwl_mvm_bt_coex_vif_change(mvm);
                iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_TT,
                                    IEEE80211_SMPS_AUTOMATIC);
@@ -973,7 +1253,7 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
                                          &mvmvif->time_event_data);
        } else if (changes & (BSS_CHANGED_PS | BSS_CHANGED_P2P_PS |
                              BSS_CHANGED_QOS)) {
-               ret = iwl_mvm_power_update_mode(mvm, vif);
+               ret = iwl_mvm_power_update_mac(mvm, vif);
                if (ret)
                        IWL_ERR(mvm, "failed to update power mode\n");
        }
@@ -987,10 +1267,15 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
                IWL_DEBUG_MAC80211(mvm, "cqm info_changed");
                /* reset cqm events tracking */
                mvmvif->bf_data.last_cqm_event = 0;
-               ret = iwl_mvm_update_beacon_filter(mvm, vif);
+               ret = iwl_mvm_update_beacon_filter(mvm, vif, false, CMD_SYNC);
                if (ret)
                        IWL_ERR(mvm, "failed to update CQM thresholds\n");
        }
+
+       if (changes & BSS_CHANGED_ARP_FILTER) {
+               IWL_DEBUG_MAC80211(mvm, "arp filter changed");
+               iwl_mvm_configure_bcast_filter(mvm, vif);
+       }
 }
 
 static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,
@@ -1024,8 +1309,6 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,
        if (ret)
                goto out_remove;
 
-       mvmvif->ap_ibss_active = true;
-
        /* Send the bcast station. At this stage the TBTT and DTIM time events
         * are added and applied to the scheduler */
        ret = iwl_mvm_send_bcast_sta(mvm, vif, &mvmvif->bcast_sta);
@@ -1037,7 +1320,7 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,
 
        /* power updated needs to be done before quotas */
        mvm->bound_vif_cnt++;
-       iwl_mvm_power_update_binding(mvm, vif, true);
+       iwl_mvm_power_update_mac(mvm, vif);
 
        ret = iwl_mvm_update_quotas(mvm, vif);
        if (ret)
@@ -1047,6 +1330,8 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,
        if (vif->p2p && mvm->p2p_device_vif)
                iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif);
 
+       iwl_mvm_ref(mvm, IWL_MVM_REF_AP_IBSS);
+
        iwl_mvm_bt_coex_vif_change(mvm);
 
        mutex_unlock(&mvm->mutex);
@@ -1054,7 +1339,7 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,
 
 out_quota_failed:
        mvm->bound_vif_cnt--;
-       iwl_mvm_power_update_binding(mvm, vif, false);
+       iwl_mvm_power_update_mac(mvm, vif);
        mvmvif->ap_ibss_active = false;
        iwl_mvm_send_rm_bcast_sta(mvm, &mvmvif->bcast_sta);
 out_unbind:
@@ -1080,6 +1365,8 @@ static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw,
 
        iwl_mvm_bt_coex_vif_change(mvm);
 
+       iwl_mvm_unref(mvm, IWL_MVM_REF_AP_IBSS);
+
        /* Need to update the P2P Device MAC (only GO, IBSS is single vif) */
        if (vif->p2p && mvm->p2p_device_vif)
                iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif);
@@ -1089,7 +1376,7 @@ static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw,
        iwl_mvm_binding_remove_vif(mvm, vif);
 
        mvm->bound_vif_cnt--;
-       iwl_mvm_power_update_binding(mvm, vif, false);
+       iwl_mvm_power_update_mac(mvm, vif);
 
        iwl_mvm_mac_ctxt_remove(mvm, vif);
 
@@ -1103,26 +1390,20 @@ iwl_mvm_bss_info_changed_ap_ibss(struct iwl_mvm *mvm,
                                 u32 changes)
 {
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-       enum ieee80211_bss_change ht_change = BSS_CHANGED_ERP_CTS_PROT |
-                                             BSS_CHANGED_HT |
-                                             BSS_CHANGED_BANDWIDTH;
-       int ret;
 
        /* Changes will be applied when the AP/IBSS is started */
        if (!mvmvif->ap_ibss_active)
                return;
 
-       if (changes & ht_change) {
-               ret = iwl_mvm_mac_ctxt_changed(mvm, vif);
-               if (ret)
-                       IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr);
-       }
+       if (changes & (BSS_CHANGED_ERP_CTS_PROT | BSS_CHANGED_HT |
+                      BSS_CHANGED_BANDWIDTH) &&
+           iwl_mvm_mac_ctxt_changed(mvm, vif))
+               IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr);
 
        /* Need to send a new beacon template to the FW */
-       if (changes & BSS_CHANGED_BEACON) {
-               if (iwl_mvm_mac_ctxt_beacon_changed(mvm, vif))
-                       IWL_WARN(mvm, "Failed updating beacon data\n");
-       }
+       if (changes & BSS_CHANGED_BEACON &&
+           iwl_mvm_mac_ctxt_beacon_changed(mvm, vif))
+               IWL_WARN(mvm, "Failed updating beacon data\n");
 }
 
 static void iwl_mvm_bss_info_changed(struct ieee80211_hw *hw,
@@ -1155,6 +1436,8 @@ static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw,
                               struct cfg80211_scan_request *req)
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct iwl_notification_wait wait_scan_done;
+       static const u8 scan_done_notif[] = { SCAN_OFFLOAD_COMPLETE, };
        int ret;
 
        if (req->n_channels == 0 || req->n_channels > MAX_NUM_SCAN_CHANNELS)
@@ -1162,11 +1445,38 @@ static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw,
 
        mutex_lock(&mvm->mutex);
 
-       if (mvm->scan_status == IWL_MVM_SCAN_NONE)
-               ret = iwl_mvm_scan_request(mvm, vif, req);
-       else
+       switch (mvm->scan_status) {
+       case IWL_MVM_SCAN_SCHED:
+               iwl_init_notification_wait(&mvm->notif_wait, &wait_scan_done,
+                                          scan_done_notif,
+                                          ARRAY_SIZE(scan_done_notif),
+                                          NULL, NULL);
+               iwl_mvm_sched_scan_stop(mvm);
+               ret = iwl_wait_notification(&mvm->notif_wait,
+                                           &wait_scan_done, HZ);
+               if (ret) {
+                       ret = -EBUSY;
+                       goto out;
+               }
+               /* iwl_mvm_rx_scan_offload_complete_notif() will be called
+                * soon but will not reset the scan status as it won't be
+                * IWL_MVM_SCAN_SCHED any more since we queue the next scan
+                * immediately (below)
+                */
+               break;
+       case IWL_MVM_SCAN_NONE:
+               break;
+       default:
                ret = -EBUSY;
+               goto out;
+       }
+
+       iwl_mvm_ref(mvm, IWL_MVM_REF_SCAN);
 
+       ret = iwl_mvm_scan_request(mvm, vif, req);
+       if (ret)
+               iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
+out:
        mutex_unlock(&mvm->mutex);
 
        return ret;
@@ -1186,20 +1496,32 @@ static void iwl_mvm_mac_cancel_hw_scan(struct ieee80211_hw *hw,
 
 static void
 iwl_mvm_mac_allow_buffered_frames(struct ieee80211_hw *hw,
-                                 struct ieee80211_sta *sta, u16 tid,
+                                 struct ieee80211_sta *sta, u16 tids,
                                  int num_frames,
                                  enum ieee80211_frame_release_type reason,
                                  bool more_data)
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
 
-       /* TODO: how do we tell the fw to send frames for a specific TID */
+       /* Called when we need to transmit (a) frame(s) from mac80211 */
 
-       /*
-        * The fw will send EOSP notification when the last frame will be
-        * transmitted.
-        */
-       iwl_mvm_sta_modify_sleep_tx_count(mvm, sta, reason, num_frames);
+       iwl_mvm_sta_modify_sleep_tx_count(mvm, sta, reason, num_frames,
+                                         tids, more_data, false);
+}
+
+static void
+iwl_mvm_mac_release_buffered_frames(struct ieee80211_hw *hw,
+                                   struct ieee80211_sta *sta, u16 tids,
+                                   int num_frames,
+                                   enum ieee80211_frame_release_type reason,
+                                   bool more_data)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+       /* Called when we need to transmit (a) frame(s) from agg queue */
+
+       iwl_mvm_sta_modify_sleep_tx_count(mvm, sta, reason, num_frames,
+                                         tids, more_data, true);
 }
 
 static void iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw,
@@ -1209,11 +1531,25 @@ static void iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw,
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
        struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+       int tid;
 
        switch (cmd) {
        case STA_NOTIFY_SLEEP:
                if (atomic_read(&mvm->pending_frames[mvmsta->sta_id]) > 0)
                        ieee80211_sta_block_awake(hw, sta, true);
+               spin_lock_bh(&mvmsta->lock);
+               for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) {
+                       struct iwl_mvm_tid_data *tid_data;
+
+                       tid_data = &mvmsta->tid_data[tid];
+                       if (tid_data->state != IWL_AGG_ON &&
+                           tid_data->state != IWL_EMPTYING_HW_QUEUE_DELBA)
+                               continue;
+                       if (iwl_mvm_tid_queued(tid_data) == 0)
+                               continue;
+                       ieee80211_sta_set_buffered(sta, tid, true);
+               }
+               spin_unlock_bh(&mvmsta->lock);
                /*
                 * The fw updates the STA to be asleep. Tx packets on the Tx
                 * queues to this station will not be transmitted. The fw will
@@ -1304,12 +1640,12 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
        } else if (old_state == IEEE80211_STA_ASSOC &&
                   new_state == IEEE80211_STA_AUTHORIZED) {
                /* enable beacon filtering */
-               WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif));
+               WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, CMD_SYNC));
                ret = 0;
        } else if (old_state == IEEE80211_STA_AUTHORIZED &&
                   new_state == IEEE80211_STA_ASSOC) {
                /* disable beacon filtering */
-               WARN_ON(iwl_mvm_disable_beacon_filter(mvm, vif));
+               WARN_ON(iwl_mvm_disable_beacon_filter(mvm, vif, CMD_SYNC));
                ret = 0;
        } else if (old_state == IEEE80211_STA_ASSOC &&
                   new_state == IEEE80211_STA_AUTH) {
@@ -1774,7 +2110,7 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,
         * otherwise fw will complain.
         */
        mvm->bound_vif_cnt++;
-       iwl_mvm_power_update_binding(mvm, vif, true);
+       iwl_mvm_power_update_mac(mvm, vif);
 
        /* Setting the quota at this stage is only required for monitor
         * interfaces. For the other types, the bss_info changed flow
@@ -1792,7 +2128,7 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,
  out_remove_binding:
        iwl_mvm_binding_remove_vif(mvm, vif);
        mvm->bound_vif_cnt--;
-       iwl_mvm_power_update_binding(mvm, vif, false);
+       iwl_mvm_power_update_mac(mvm, vif);
  out_unlock:
        mutex_unlock(&mvm->mutex);
        if (ret)
@@ -1825,7 +2161,7 @@ static void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw,
 
        iwl_mvm_binding_remove_vif(mvm, vif);
        mvm->bound_vif_cnt--;
-       iwl_mvm_power_update_binding(mvm, vif, false);
+       iwl_mvm_power_update_mac(mvm, vif);
 
 out_unlock:
        mvmvif->phy_ctxt = NULL;
@@ -1892,8 +2228,9 @@ static int __iwl_mvm_mac_testmode_cmd(struct iwl_mvm *mvm,
                        return -EINVAL;
 
                if (nla_get_u32(tb[IWL_MVM_TM_ATTR_BEACON_FILTER_STATE]))
-                       return iwl_mvm_enable_beacon_filter(mvm, vif);
-               return iwl_mvm_disable_beacon_filter(mvm, vif);
+                       return iwl_mvm_enable_beacon_filter(mvm, vif,
+                                                           CMD_SYNC);
+               return iwl_mvm_disable_beacon_filter(mvm, vif, CMD_SYNC);
        }
 
        return -EOPNOTSUPP;
@@ -1932,6 +2269,7 @@ struct ieee80211_ops iwl_mvm_hw_ops = {
        .sta_state = iwl_mvm_mac_sta_state,
        .sta_notify = iwl_mvm_mac_sta_notify,
        .allow_buffered_frames = iwl_mvm_mac_allow_buffered_frames,
+       .release_buffered_frames = iwl_mvm_mac_release_buffered_frames,
        .set_rts_threshold = iwl_mvm_mac_set_rts_threshold,
        .sta_rc_update = iwl_mvm_sta_rc_update,
        .conf_tx = iwl_mvm_mac_conf_tx,
index e4ead86..ebea5f2 100644 (file)
@@ -92,7 +92,6 @@ enum iwl_mvm_tx_fifo {
 };
 
 extern struct ieee80211_ops iwl_mvm_hw_ops;
-extern const struct iwl_mvm_power_ops pm_legacy_ops;
 extern const struct iwl_mvm_power_ops pm_mac_ops;
 
 /**
@@ -159,20 +158,6 @@ enum iwl_power_scheme {
                                         IEEE80211_WMM_IE_STA_QOSINFO_AC_BE)
 #define IWL_UAPSD_MAX_SP               IEEE80211_WMM_IE_STA_QOSINFO_SP_2
 
-struct iwl_mvm_power_ops {
-       int (*power_update_mode)(struct iwl_mvm *mvm,
-                                struct ieee80211_vif *vif);
-       int (*power_update_device_mode)(struct iwl_mvm *mvm);
-       int (*power_disable)(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
-       void (*power_update_binding)(struct iwl_mvm *mvm,
-                                    struct ieee80211_vif *vif, bool assign);
-#ifdef CONFIG_IWLWIFI_DEBUGFS
-       int (*power_dbgfs_read)(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
-                               char *buf, int bufsz);
-#endif
-};
-
-
 #ifdef CONFIG_IWLWIFI_DEBUGFS
 enum iwl_dbgfs_pm_mask {
        MVM_DEBUGFS_PM_KEEP_ALIVE = BIT(0),
@@ -239,6 +224,17 @@ enum iwl_mvm_smps_type_request {
        NUM_IWL_MVM_SMPS_REQ,
 };
 
+enum iwl_mvm_ref_type {
+       IWL_MVM_REF_UCODE_DOWN,
+       IWL_MVM_REF_SCAN,
+       IWL_MVM_REF_ROC,
+       IWL_MVM_REF_P2P_CLIENT,
+       IWL_MVM_REF_AP_IBSS,
+       IWL_MVM_REF_USER,
+
+       IWL_MVM_REF_COUNT,
+};
+
 /**
 * struct iwl_mvm_vif_bf_data - beacon filtering related data
 * @bf_enabled: indicates if beacon filtering is enabled
@@ -269,7 +265,9 @@ struct iwl_mvm_vif_bf_data {
  * @ap_ibss_active: indicates that AP/IBSS is configured and that the interface
  *     should get quota etc.
  * @monitor_active: indicates that monitor context is configured, and that the
- * interface should get quota etc.
+ *     interface should get quota etc.
+ * @low_latency: indicates that this interface is in low-latency mode
+ *     (VMACLowLatencyMode)
  * @queue_params: QoS params for this MAC
  * @bcast_sta: station used for broadcast packets. Used by the following
  *  vifs: P2P_DEVICE, GO and AP.
@@ -285,6 +283,7 @@ struct iwl_mvm_vif {
        bool uploaded;
        bool ap_ibss_active;
        bool monitor_active;
+       bool low_latency;
        struct iwl_mvm_vif_bf_data bf_data;
 
        u32 ap_beacon_time;
@@ -333,14 +332,13 @@ struct iwl_mvm_vif {
        struct dentry *dbgfs_slink;
        struct iwl_dbgfs_pm dbgfs_pm;
        struct iwl_dbgfs_bf dbgfs_bf;
+       struct iwl_mac_power_cmd mac_pwr_cmd;
 #endif
 
        enum ieee80211_smps_mode smps_requests[NUM_IWL_MVM_SMPS_REQ];
 
        /* FW identified misbehaving AP */
        u8 uapsd_misbehaving_bssid[ETH_ALEN];
-
-       bool pm_prevented;
 };
 
 static inline struct iwl_mvm_vif *
@@ -415,6 +413,7 @@ struct iwl_tt_params {
  * @ct_kill_exit: worker to exit thermal kill
  * @dynamic_smps: Is thermal throttling enabled dynamic_smps?
  * @tx_backoff: The current thremal throttling tx backoff in uSec.
+ * @min_backoff: The minimal tx backoff due to power restrictions
  * @params: Parameters to configure the thermal throttling algorithm.
  * @throttle: Is thermal throttling is active?
  */
@@ -422,6 +421,7 @@ struct iwl_mvm_tt_mgmt {
        struct delayed_work ct_kill_exit;
        bool dynamic_smps;
        u32 tx_backoff;
+       u32 min_backoff;
        const struct iwl_tt_params *params;
        bool throttle;
 };
@@ -457,6 +457,8 @@ struct iwl_mvm {
        bool init_ucode_complete;
        u32 error_event_table;
        u32 log_event_table;
+       u32 umac_error_event_table;
+       bool support_umac_log;
 
        u32 ampdu_ref;
 
@@ -470,7 +472,7 @@ struct iwl_mvm {
 
        struct iwl_nvm_data *nvm_data;
        /* NVM sections */
-       struct iwl_nvm_section nvm_sections[NVM_NUM_OF_SECTIONS];
+       struct iwl_nvm_section nvm_sections[NVM_MAX_NUM_SECTIONS];
 
        /* EEPROM MAC addresses */
        struct mac_address addresses[IWL_MVM_MAX_ADDRESSES];
@@ -494,6 +496,17 @@ struct iwl_mvm {
        /* rx chain antennas set through debugfs for the scan command */
        u8 scan_rx_ant;
 
+#ifdef CONFIG_IWLWIFI_BCAST_FILTERING
+       /* broadcast filters to configure for each associated station */
+       const struct iwl_fw_bcast_filter *bcast_filters;
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+       struct {
+               u32 override; /* u32 for debugfs_create_bool */
+               struct iwl_bcast_filter_cmd cmd;
+       } dbgfs_bcast_filtering;
+#endif
+#endif
+
        /* Internal station */
        struct iwl_mvm_int_sta aux_sta;
 
@@ -526,6 +539,9 @@ struct iwl_mvm {
         */
        unsigned long fw_key_table[BITS_TO_LONGS(STA_KEY_MAX_NUM)];
 
+       /* A bitmap of reference types taken by the driver. */
+       unsigned long ref_bitmap[BITS_TO_LONGS(IWL_MVM_REF_COUNT)];
+
        u8 vif_count;
 
        /* -1 for always, 0 for never, >0 for that many times */
@@ -548,6 +564,10 @@ struct iwl_mvm {
 #endif
 #endif
 
+       /* d0i3 */
+       u8 d0i3_ap_sta_id;
+       struct work_struct d0i3_exit_work;
+
        /* BT-Coex */
        u8 bt_kill_msk;
        struct iwl_bt_coex_profile_notif last_bt_notif;
@@ -557,8 +577,6 @@ struct iwl_mvm {
        struct iwl_mvm_tt_mgmt thermal_throttle;
        s32 temperature;        /* Celsius */
 
-       const struct iwl_mvm_power_ops *pm_ops;
-
 #ifdef CONFIG_NL80211_TESTMODE
        u32 noa_duration;
        struct ieee80211_vif *noa_vif;
@@ -572,7 +590,9 @@ struct iwl_mvm {
        u8 bound_vif_cnt;
 
        /* Indicate if device power save is allowed */
-       bool ps_prevented;
+       bool ps_disabled;
+       /* Indicate if device power management is allowed */
+       bool pm_disabled;
 };
 
 /* Extract MVM priv from op_mode and _hw */
@@ -595,6 +615,24 @@ static inline bool iwl_mvm_is_radio_killed(struct iwl_mvm *mvm)
               test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status);
 }
 
+static inline struct iwl_mvm_sta *
+iwl_mvm_sta_from_staid_protected(struct iwl_mvm *mvm, u8 sta_id)
+{
+       struct ieee80211_sta *sta;
+
+       if (sta_id >= ARRAY_SIZE(mvm->fw_id_to_mac_id))
+               return NULL;
+
+       sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[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 NULL;
+
+       return iwl_mvm_sta_from_mac80211(sta);
+}
+
 extern const u8 iwl_mvm_ac_to_tx_fifo[];
 
 struct iwl_rate_info {
@@ -661,6 +699,8 @@ int iwl_mvm_up(struct iwl_mvm *mvm);
 int iwl_mvm_load_d3_fw(struct iwl_mvm *mvm);
 
 int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm);
+bool iwl_mvm_bcast_filter_build_cmd(struct iwl_mvm *mvm,
+                                   struct iwl_bcast_filter_cmd *cmd);
 
 /*
  * FW notifications / CMD responses handlers
@@ -773,48 +813,19 @@ iwl_mvm_vif_dbgfs_clean(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
 /* rate scaling */
 int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq, bool init);
 
-/* power managment */
-static inline int iwl_mvm_power_update_mode(struct iwl_mvm *mvm,
-                                           struct ieee80211_vif *vif)
-{
-       return mvm->pm_ops->power_update_mode(mvm, vif);
-}
-
-static inline int iwl_mvm_power_disable(struct iwl_mvm *mvm,
-                                       struct ieee80211_vif *vif)
-{
-       return mvm->pm_ops->power_disable(mvm, vif);
-}
-
-static inline int iwl_mvm_power_update_device_mode(struct iwl_mvm *mvm)
-{
-       if (mvm->pm_ops->power_update_device_mode)
-               return mvm->pm_ops->power_update_device_mode(mvm);
-       return 0;
-}
+/* power management */
+int iwl_power_legacy_set_cam_mode(struct iwl_mvm *mvm);
 
-static inline void iwl_mvm_power_update_binding(struct iwl_mvm *mvm,
-                                               struct ieee80211_vif *vif,
-                                               bool assign)
-{
-       if (mvm->pm_ops->power_update_binding)
-               mvm->pm_ops->power_update_binding(mvm, vif, assign);
-}
+int iwl_mvm_power_update_device(struct iwl_mvm *mvm);
+int iwl_mvm_power_update_mac(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                                char *buf, int bufsz);
 
 void iwl_mvm_power_vif_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
 int iwl_mvm_power_uapsd_misbehaving_ap_notif(struct iwl_mvm *mvm,
                                             struct iwl_rx_cmd_buffer *rxb,
                                             struct iwl_device_cmd *cmd);
 
-#ifdef CONFIG_IWLWIFI_DEBUGFS
-static inline int iwl_mvm_power_dbgfs_read(struct iwl_mvm *mvm,
-                                           struct ieee80211_vif *vif,
-                                           char *buf, int bufsz)
-{
-       return mvm->pm_ops->power_dbgfs_read(mvm, vif, buf, bufsz);
-}
-#endif
-
 int iwl_mvm_leds_init(struct iwl_mvm *mvm);
 void iwl_mvm_leds_exit(struct iwl_mvm *mvm);
 
@@ -841,6 +852,10 @@ iwl_mvm_set_last_nonqos_seq(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
 }
 #endif
 
+/* D0i3 */
+void iwl_mvm_ref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type);
+void iwl_mvm_unref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type);
+
 /* BT Coex */
 int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm);
 int iwl_send_bt_init_conf(struct iwl_mvm *mvm);
@@ -854,6 +869,7 @@ u16 iwl_mvm_bt_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);
+int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id, bool enable);
 
 enum iwl_bt_kill_msk {
        BT_KILL_MSK_DEFAULT,
@@ -875,25 +891,51 @@ iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif,
                                         struct iwl_beacon_filter_cmd *cmd)
 {}
 #endif
+int iwl_mvm_update_d0i3_power_mode(struct iwl_mvm *mvm,
+                                  struct ieee80211_vif *vif,
+                                  bool enable, u32 flags);
 int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm,
-                                struct ieee80211_vif *vif);
+                                struct ieee80211_vif *vif,
+                                u32 flags);
 int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm,
-                                 struct ieee80211_vif *vif);
-int iwl_mvm_beacon_filter_send_cmd(struct iwl_mvm *mvm,
-                                  struct iwl_beacon_filter_cmd *cmd);
+                                 struct ieee80211_vif *vif,
+                                 u32 flags);
 int iwl_mvm_update_beacon_abort(struct iwl_mvm *mvm,
                                struct ieee80211_vif *vif, bool enable);
 int iwl_mvm_update_beacon_filter(struct iwl_mvm *mvm,
-                                 struct ieee80211_vif *vif);
+                                struct ieee80211_vif *vif,
+                                bool force,
+                                u32 flags);
 
 /* SMPS */
 void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                                enum iwl_mvm_smps_type_request req_type,
                                enum ieee80211_smps_mode smps_request);
 
+/* Low latency */
+int iwl_mvm_update_low_latency(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                              bool value);
+/* get VMACLowLatencyMode */
+static inline bool iwl_mvm_vif_low_latency(struct iwl_mvm_vif *mvmvif)
+{
+       /*
+        * should this consider associated/active/... state?
+        *
+        * Normally low-latency should only be active on interfaces
+        * that are active, but at least with debugfs it can also be
+        * enabled on interfaces that aren't active. However, when
+        * interface aren't active then they aren't added into the
+        * binding, so this has no real impact. For now, just return
+        * the current desired low-latency state.
+        */
+
+       return mvmvif->low_latency;
+}
+
 /* Thermal management and CT-kill */
+void iwl_mvm_tt_tx_backoff(struct iwl_mvm *mvm, u32 backoff);
 void iwl_mvm_tt_handler(struct iwl_mvm *mvm);
-void iwl_mvm_tt_initialize(struct iwl_mvm *mvm);
+void iwl_mvm_tt_initialize(struct iwl_mvm *mvm, u32 min_backoff);
 void iwl_mvm_tt_exit(struct iwl_mvm *mvm);
 void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state);
 
index 35b71af..2d5251b 100644 (file)
 #include "iwl-eeprom-read.h"
 #include "iwl-nvm-parse.h"
 
-/* list of NVM sections we are allowed/need to read */
-static const int nvm_to_read[] = {
-       NVM_SECTION_TYPE_HW,
-       NVM_SECTION_TYPE_SW,
-       NVM_SECTION_TYPE_CALIBRATION,
-       NVM_SECTION_TYPE_PRODUCTION,
-};
-
 /* Default NVM size to read */
 #define IWL_NVM_DEFAULT_CHUNK_SIZE (2*1024)
 #define IWL_MAX_NVM_SECTION_SIZE 7000
@@ -240,7 +232,7 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm)
 
        /* Checking for required sections */
        if (!mvm->nvm_sections[NVM_SECTION_TYPE_SW].data ||
-           !mvm->nvm_sections[NVM_SECTION_TYPE_HW].data) {
+           !mvm->nvm_sections[mvm->cfg->nvm_hw_section_num].data) {
                IWL_ERR(mvm, "Can't parse empty NVM sections\n");
                return NULL;
        }
@@ -248,7 +240,7 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm)
        if (WARN_ON(!mvm->cfg))
                return NULL;
 
-       hw = (const __le16 *)sections[NVM_SECTION_TYPE_HW].data;
+       hw = (const __le16 *)sections[mvm->cfg->nvm_hw_section_num].data;
        sw = (const __le16 *)sections[NVM_SECTION_TYPE_SW].data;
        calib = (const __le16 *)sections[NVM_SECTION_TYPE_CALIBRATION].data;
        return iwl_parse_nvm_data(mvm->trans->dev, mvm->cfg, hw, sw, calib,
@@ -367,7 +359,7 @@ static int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm)
                        break;
                }
 
-               if (WARN(section_id >= NVM_NUM_OF_SECTIONS,
+               if (WARN(section_id >= NVM_MAX_NUM_SECTIONS,
                         "Invalid NVM section ID %d\n", section_id)) {
                        ret = -EINVAL;
                        break;
@@ -415,6 +407,9 @@ int iwl_nvm_init(struct iwl_mvm *mvm)
        int ret, i, section;
        u8 *nvm_buffer, *temp;
 
+       if (WARN_ON_ONCE(mvm->cfg->nvm_hw_section_num >= NVM_MAX_NUM_SECTIONS))
+               return -EINVAL;
+
        /* load external NVM if configured */
        if (iwlwifi_mod_params.nvm_file) {
                /* move to External NVM flow */
@@ -422,6 +417,14 @@ int iwl_nvm_init(struct iwl_mvm *mvm)
                if (ret)
                        return ret;
        } else {
+               /* list of NVM sections we are allowed/need to read */
+               int nvm_to_read[] = {
+                       mvm->cfg->nvm_hw_section_num,
+                       NVM_SECTION_TYPE_SW,
+                       NVM_SECTION_TYPE_CALIBRATION,
+                       NVM_SECTION_TYPE_PRODUCTION,
+               };
+
                /* Read From FW NVM */
                IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from NVM\n");
 
@@ -446,10 +449,6 @@ int iwl_nvm_init(struct iwl_mvm *mvm)
 
 #ifdef CONFIG_IWLWIFI_DEBUGFS
                        switch (section) {
-                       case NVM_SECTION_TYPE_HW:
-                               mvm->nvm_hw_blob.data = temp;
-                               mvm->nvm_hw_blob.size = ret;
-                               break;
                        case NVM_SECTION_TYPE_SW:
                                mvm->nvm_sw_blob.data = temp;
                                mvm->nvm_sw_blob.size  = ret;
@@ -463,6 +462,11 @@ int iwl_nvm_init(struct iwl_mvm *mvm)
                                mvm->nvm_prod_blob.size  = ret;
                                break;
                        default:
+                               if (section == mvm->cfg->nvm_hw_section_num) {
+                                       mvm->nvm_hw_blob.data = temp;
+                                       mvm->nvm_hw_blob.size = ret;
+                                       break;
+                               }
                                WARN(1, "section: %d", section);
                        }
 #endif
index a3d43de..a46f0b8 100644 (file)
@@ -185,9 +185,10 @@ static void iwl_mvm_nic_config(struct iwl_op_mode *op_mode)
         * (PCIe power is lost before PERST# is asserted), causing ME FW
         * to lose ownership and not being able to obtain it back.
         */
-       iwl_set_bits_mask_prph(mvm->trans, APMG_PS_CTRL_REG,
-                              APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS,
-                              ~APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS);
+       if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000)
+               iwl_set_bits_mask_prph(mvm->trans, APMG_PS_CTRL_REG,
+                                      APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS,
+                                      ~APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS);
 }
 
 struct iwl_rx_handlers {
@@ -222,10 +223,12 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = {
 
        RX_HANDLER(TIME_EVENT_NOTIFICATION, iwl_mvm_rx_time_event_notif, false),
 
+       RX_HANDLER(EOSP_NOTIFICATION, iwl_mvm_rx_eosp_notif, false),
+
        RX_HANDLER(SCAN_REQUEST_CMD, iwl_mvm_rx_scan_response, false),
        RX_HANDLER(SCAN_COMPLETE_NOTIFICATION, iwl_mvm_rx_scan_complete, false),
        RX_HANDLER(SCAN_OFFLOAD_COMPLETE,
-                  iwl_mvm_rx_scan_offload_complete_notif, false),
+                  iwl_mvm_rx_scan_offload_complete_notif, true),
        RX_HANDLER(MATCH_FOUND_NOTIFICATION, iwl_mvm_rx_sched_scan_results,
                   false),
 
@@ -284,9 +287,11 @@ static const char *iwl_mvm_cmd_strings[REPLY_MAX] = {
        CMD(BEACON_NOTIFICATION),
        CMD(BEACON_TEMPLATE_CMD),
        CMD(STATISTICS_NOTIFICATION),
+       CMD(EOSP_NOTIFICATION),
        CMD(REDUCE_TX_POWER_CMD),
        CMD(TX_ANT_CONFIGURATION_CMD),
        CMD(D3_CONFIG_CMD),
+       CMD(D0I3_END_CMD),
        CMD(PROT_OFFLOAD_CONFIG_CMD),
        CMD(OFFLOADS_QUERY_CMD),
        CMD(REMOTE_WAKE_CONFIG_CMD),
@@ -309,6 +314,7 @@ static const char *iwl_mvm_cmd_strings[REPLY_MAX] = {
        CMD(BT_PROFILE_NOTIFICATION),
        CMD(BT_CONFIG),
        CMD(MCAST_FILTER_CMD),
+       CMD(BCAST_FILTER_CMD),
        CMD(REPLY_SF_CFG_CMD),
        CMD(REPLY_BEACON_FILTERING_CMD),
        CMD(REPLY_THERMAL_MNG_BACKOFF),
@@ -320,6 +326,24 @@ static const char *iwl_mvm_cmd_strings[REPLY_MAX] = {
 
 /* this forward declaration can avoid to export the function */
 static void iwl_mvm_async_handlers_wk(struct work_struct *wk);
+static void iwl_mvm_d0i3_exit_work(struct work_struct *wk);
+
+static u32 calc_min_backoff(struct iwl_trans *trans, const struct iwl_cfg *cfg)
+{
+       const struct iwl_pwr_tx_backoff *pwr_tx_backoff = cfg->pwr_tx_backoffs;
+
+       if (!pwr_tx_backoff)
+               return 0;
+
+       while (pwr_tx_backoff->pwr) {
+               if (trans->dflt_pwr_limit >= pwr_tx_backoff->pwr)
+                       return pwr_tx_backoff->backoff;
+
+               pwr_tx_backoff++;
+       }
+
+       return 0;
+}
 
 static struct iwl_op_mode *
 iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
@@ -333,6 +357,14 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
                TX_CMD,
        };
        int err, scan_size;
+       u32 min_backoff;
+
+       /*
+        * We use IWL_MVM_STATION_COUNT to check the validity of the station
+        * index all over the driver - check that its value corresponds to the
+        * array size.
+        */
+       BUILD_BUG_ON(ARRAY_SIZE(mvm->fw_id_to_mac_id) != IWL_MVM_STATION_COUNT);
 
        /********************************
         * 1. Allocating and configuring HW data
@@ -373,6 +405,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
        INIT_WORK(&mvm->async_handlers_wk, iwl_mvm_async_handlers_wk);
        INIT_WORK(&mvm->roc_done_wk, iwl_mvm_roc_done_wk);
        INIT_WORK(&mvm->sta_drained_wk, iwl_mvm_sta_drained_wk);
+       INIT_WORK(&mvm->d0i3_exit_work, iwl_mvm_d0i3_exit_work);
 
        SET_IEEE80211_DEV(mvm->hw, mvm->trans->dev);
 
@@ -421,7 +454,8 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
        IWL_INFO(mvm, "Detected %s, REV=0x%X\n",
                 mvm->cfg->name, mvm->trans->hw_rev);
 
-       iwl_mvm_tt_initialize(mvm);
+       min_backoff = calc_min_backoff(trans, cfg);
+       iwl_mvm_tt_initialize(mvm, min_backoff);
 
        /*
         * If the NVM exists in an external file,
@@ -462,13 +496,11 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
        if (err)
                goto out_unregister;
 
-       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_PM_CMD_SUPPORT)
-               mvm->pm_ops = &pm_mac_ops;
-       else
-               mvm->pm_ops = &pm_legacy_ops;
-
        memset(&mvm->rx_stats, 0, sizeof(struct mvm_statistics_rx));
 
+       /* rpm starts with a taken ref. only set the appropriate bit here. */
+       set_bit(IWL_MVM_REF_UCODE_DOWN, mvm->ref_bitmap);
+
        return op_mode;
 
  out_unregister:
@@ -508,7 +540,7 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode)
        mvm->phy_db = NULL;
 
        iwl_free_nvm_data(mvm->nvm_data);
-       for (i = 0; i < NVM_NUM_OF_SECTIONS; i++)
+       for (i = 0; i < NVM_MAX_NUM_SECTIONS; i++)
                kfree(mvm->nvm_sections[i].data);
 
        ieee80211_free_hw(mvm->hw);
@@ -702,6 +734,29 @@ static void iwl_mvm_nic_restart(struct iwl_mvm *mvm)
 {
        iwl_abort_notification_waits(&mvm->notif_wait);
 
+       /*
+        * This is a bit racy, but worst case we tell mac80211 about
+        * a stopped/aborted scan when that was already done which
+        * is not a problem. It is necessary to abort any os scan
+        * here because mac80211 requires having the scan cleared
+        * before restarting.
+        * We'll reset the scan_status to NONE in restart cleanup in
+        * the next start() call from mac80211. If restart isn't called
+        * (no fw restart) scan status will stay busy.
+        */
+       switch (mvm->scan_status) {
+       case IWL_MVM_SCAN_NONE:
+               break;
+       case IWL_MVM_SCAN_OS:
+               ieee80211_scan_completed(mvm->hw, true);
+               break;
+       case IWL_MVM_SCAN_SCHED:
+               /* Sched scan will be restarted by mac80211 in restart_hw. */
+               if (!mvm->restart_fw)
+                       ieee80211_sched_scan_stopped(mvm->hw);
+               break;
+       }
+
        /*
         * If we're restarting already, don't cycle restarts.
         * If INIT fw asserted, it will likely fail again.
@@ -733,25 +788,8 @@ static void iwl_mvm_nic_restart(struct iwl_mvm *mvm)
                INIT_WORK(&reprobe->work, iwl_mvm_reprobe_wk);
                schedule_work(&reprobe->work);
        } else if (mvm->cur_ucode == IWL_UCODE_REGULAR && mvm->restart_fw) {
-               /*
-                * This is a bit racy, but worst case we tell mac80211 about
-                * a stopped/aborted (sched) scan when that was already done
-                * which is not a problem. It is necessary to abort any scan
-                * here because mac80211 requires having the scan cleared
-                * before restarting.
-                * We'll reset the scan_status to NONE in restart cleanup in
-                * the next start() call from mac80211.
-                */
-               switch (mvm->scan_status) {
-               case IWL_MVM_SCAN_NONE:
-                       break;
-               case IWL_MVM_SCAN_OS:
-                       ieee80211_scan_completed(mvm->hw, true);
-                       break;
-               case IWL_MVM_SCAN_SCHED:
-                       ieee80211_sched_scan_stopped(mvm->hw);
-                       break;
-               }
+               /* don't let the transport/FW power down */
+               iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN);
 
                if (mvm->restart_fw > 0)
                        mvm->restart_fw--;
@@ -778,6 +816,163 @@ static void iwl_mvm_cmd_queue_full(struct iwl_op_mode *op_mode)
        iwl_mvm_nic_restart(mvm);
 }
 
+struct iwl_d0i3_iter_data {
+       struct iwl_mvm *mvm;
+       u8 ap_sta_id;
+       u8 vif_count;
+};
+
+static void iwl_mvm_enter_d0i3_iterator(void *_data, u8 *mac,
+                                       struct ieee80211_vif *vif)
+{
+       struct iwl_d0i3_iter_data *data = _data;
+       struct iwl_mvm *mvm = data->mvm;
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       u32 flags = CMD_ASYNC | CMD_HIGH_PRIO | CMD_SEND_IN_IDLE;
+
+       IWL_DEBUG_RPM(mvm, "entering D0i3 - vif %pM\n", vif->addr);
+       if (vif->type != NL80211_IFTYPE_STATION ||
+           !vif->bss_conf.assoc)
+               return;
+
+       iwl_mvm_update_d0i3_power_mode(mvm, vif, true, flags);
+
+       /*
+        * on init/association, mvm already configures POWER_TABLE_CMD
+        * and REPLY_MCAST_FILTER_CMD, so currently don't
+        * reconfigure them (we might want to use different
+        * params later on, though).
+        */
+       data->ap_sta_id = mvmvif->ap_sta_id;
+       data->vif_count++;
+}
+
+static int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode)
+{
+       struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+       u32 flags = CMD_ASYNC | CMD_HIGH_PRIO | CMD_SEND_IN_IDLE;
+       int ret;
+       struct iwl_d0i3_iter_data d0i3_iter_data = {
+               .mvm = mvm,
+       };
+       struct iwl_wowlan_config_cmd wowlan_config_cmd = {
+               .wakeup_filter = cpu_to_le32(IWL_WOWLAN_WAKEUP_RX_FRAME |
+                                            IWL_WOWLAN_WAKEUP_BEACON_MISS |
+                                            IWL_WOWLAN_WAKEUP_LINK_CHANGE |
+                                            IWL_WOWLAN_WAKEUP_BCN_FILTERING),
+       };
+       struct iwl_d3_manager_config d3_cfg_cmd = {
+               .min_sleep_time = cpu_to_le32(1000),
+       };
+
+       IWL_DEBUG_RPM(mvm, "MVM entering D0i3\n");
+
+       ieee80211_iterate_active_interfaces_atomic(mvm->hw,
+                                                  IEEE80211_IFACE_ITER_NORMAL,
+                                                  iwl_mvm_enter_d0i3_iterator,
+                                                  &d0i3_iter_data);
+       if (d0i3_iter_data.vif_count == 1) {
+               mvm->d0i3_ap_sta_id = d0i3_iter_data.ap_sta_id;
+       } else {
+               WARN_ON_ONCE(d0i3_iter_data.vif_count > 1);
+               mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT;
+       }
+
+       ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, flags,
+                                  sizeof(wowlan_config_cmd),
+                                  &wowlan_config_cmd);
+       if (ret)
+               return ret;
+
+       return iwl_mvm_send_cmd_pdu(mvm, D3_CONFIG_CMD,
+                                   flags | CMD_MAKE_TRANS_IDLE,
+                                   sizeof(d3_cfg_cmd), &d3_cfg_cmd);
+}
+
+static void iwl_mvm_exit_d0i3_iterator(void *_data, u8 *mac,
+                                      struct ieee80211_vif *vif)
+{
+       struct iwl_mvm *mvm = _data;
+       u32 flags = CMD_ASYNC | CMD_HIGH_PRIO;
+
+       IWL_DEBUG_RPM(mvm, "exiting D0i3 - vif %pM\n", vif->addr);
+       if (vif->type != NL80211_IFTYPE_STATION ||
+           !vif->bss_conf.assoc)
+               return;
+
+       iwl_mvm_update_d0i3_power_mode(mvm, vif, false, flags);
+}
+
+static void iwl_mvm_d0i3_disconnect_iter(void *data, u8 *mac,
+                                        struct ieee80211_vif *vif)
+{
+       struct iwl_mvm *mvm = data;
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       if (vif->type == NL80211_IFTYPE_STATION && vif->bss_conf.assoc &&
+           mvm->d0i3_ap_sta_id == mvmvif->ap_sta_id)
+               ieee80211_connection_loss(vif);
+}
+
+static void iwl_mvm_d0i3_exit_work(struct work_struct *wk)
+{
+       struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm, d0i3_exit_work);
+       struct iwl_host_cmd get_status_cmd = {
+               .id = WOWLAN_GET_STATUSES,
+               .flags = CMD_SYNC | CMD_HIGH_PRIO | CMD_WANT_SKB,
+       };
+       struct iwl_wowlan_status_v6 *status;
+       int ret;
+       u32 disconnection_reasons, wakeup_reasons;
+
+       mutex_lock(&mvm->mutex);
+       ret = iwl_mvm_send_cmd(mvm, &get_status_cmd);
+       if (ret)
+               goto out;
+
+       if (!get_status_cmd.resp_pkt)
+               goto out;
+
+       status = (void *)get_status_cmd.resp_pkt->data;
+       wakeup_reasons = le32_to_cpu(status->wakeup_reasons);
+
+       IWL_DEBUG_RPM(mvm, "wakeup reasons: 0x%x\n", wakeup_reasons);
+
+       disconnection_reasons =
+               IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON |
+               IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH;
+       if (wakeup_reasons & disconnection_reasons)
+               ieee80211_iterate_active_interfaces(
+                       mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+                       iwl_mvm_d0i3_disconnect_iter, mvm);
+
+       iwl_free_resp(&get_status_cmd);
+out:
+       mutex_unlock(&mvm->mutex);
+}
+
+static int iwl_mvm_exit_d0i3(struct iwl_op_mode *op_mode)
+{
+       struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+       u32 flags = CMD_ASYNC | CMD_HIGH_PRIO | CMD_SEND_IN_IDLE |
+                   CMD_WAKE_UP_TRANS;
+       int ret;
+
+       IWL_DEBUG_RPM(mvm, "MVM exiting D0i3\n");
+
+       ret = iwl_mvm_send_cmd_pdu(mvm, D0I3_END_CMD, flags, 0, NULL);
+       if (ret)
+               goto out;
+
+       ieee80211_iterate_active_interfaces_atomic(mvm->hw,
+                                                  IEEE80211_IFACE_ITER_NORMAL,
+                                                  iwl_mvm_exit_d0i3_iterator,
+                                                  mvm);
+out:
+       schedule_work(&mvm->d0i3_exit_work);
+       return ret;
+}
+
 static const struct iwl_op_mode_ops iwl_mvm_ops = {
        .start = iwl_op_mode_mvm_start,
        .stop = iwl_op_mode_mvm_stop,
@@ -789,4 +984,6 @@ static const struct iwl_op_mode_ops iwl_mvm_ops = {
        .nic_error = iwl_mvm_nic_error,
        .cmd_queue_full = iwl_mvm_cmd_queue_full,
        .nic_config = iwl_mvm_nic_config,
+       .enter_d0i3 = iwl_mvm_enter_d0i3,
+       .exit_d0i3 = iwl_mvm_exit_d0i3,
 };
index d9eab3b..4da1ea4 100644 (file)
 
 #define POWER_KEEP_ALIVE_PERIOD_SEC    25
 
+static
 int iwl_mvm_beacon_filter_send_cmd(struct iwl_mvm *mvm,
-                                  struct iwl_beacon_filter_cmd *cmd)
+                                  struct iwl_beacon_filter_cmd *cmd,
+                                  u32 flags)
 {
-       int ret;
-
-       ret = iwl_mvm_send_cmd_pdu(mvm, REPLY_BEACON_FILTERING_CMD, CMD_SYNC,
-                                  sizeof(struct iwl_beacon_filter_cmd), cmd);
-
-       if (!ret) {
-               IWL_DEBUG_POWER(mvm, "ba_enable_beacon_abort is: %d\n",
-                               le32_to_cpu(cmd->ba_enable_beacon_abort));
-               IWL_DEBUG_POWER(mvm, "ba_escape_timer is: %d\n",
-                               le32_to_cpu(cmd->ba_escape_timer));
-               IWL_DEBUG_POWER(mvm, "bf_debug_flag is: %d\n",
-                               le32_to_cpu(cmd->bf_debug_flag));
-               IWL_DEBUG_POWER(mvm, "bf_enable_beacon_filter is: %d\n",
-                               le32_to_cpu(cmd->bf_enable_beacon_filter));
-               IWL_DEBUG_POWER(mvm, "bf_energy_delta is: %d\n",
-                               le32_to_cpu(cmd->bf_energy_delta));
-               IWL_DEBUG_POWER(mvm, "bf_escape_timer is: %d\n",
-                               le32_to_cpu(cmd->bf_escape_timer));
-               IWL_DEBUG_POWER(mvm, "bf_roaming_energy_delta is: %d\n",
-                               le32_to_cpu(cmd->bf_roaming_energy_delta));
-               IWL_DEBUG_POWER(mvm, "bf_roaming_state is: %d\n",
-                               le32_to_cpu(cmd->bf_roaming_state));
-               IWL_DEBUG_POWER(mvm, "bf_temp_threshold is: %d\n",
-                               le32_to_cpu(cmd->bf_temp_threshold));
-               IWL_DEBUG_POWER(mvm, "bf_temp_fast_filter is: %d\n",
-                               le32_to_cpu(cmd->bf_temp_fast_filter));
-               IWL_DEBUG_POWER(mvm, "bf_temp_slow_filter is: %d\n",
-                               le32_to_cpu(cmd->bf_temp_slow_filter));
-       }
-       return ret;
+       IWL_DEBUG_POWER(mvm, "ba_enable_beacon_abort is: %d\n",
+                       le32_to_cpu(cmd->ba_enable_beacon_abort));
+       IWL_DEBUG_POWER(mvm, "ba_escape_timer is: %d\n",
+                       le32_to_cpu(cmd->ba_escape_timer));
+       IWL_DEBUG_POWER(mvm, "bf_debug_flag is: %d\n",
+                       le32_to_cpu(cmd->bf_debug_flag));
+       IWL_DEBUG_POWER(mvm, "bf_enable_beacon_filter is: %d\n",
+                       le32_to_cpu(cmd->bf_enable_beacon_filter));
+       IWL_DEBUG_POWER(mvm, "bf_energy_delta is: %d\n",
+                       le32_to_cpu(cmd->bf_energy_delta));
+       IWL_DEBUG_POWER(mvm, "bf_escape_timer is: %d\n",
+                       le32_to_cpu(cmd->bf_escape_timer));
+       IWL_DEBUG_POWER(mvm, "bf_roaming_energy_delta is: %d\n",
+                       le32_to_cpu(cmd->bf_roaming_energy_delta));
+       IWL_DEBUG_POWER(mvm, "bf_roaming_state is: %d\n",
+                       le32_to_cpu(cmd->bf_roaming_state));
+       IWL_DEBUG_POWER(mvm, "bf_temp_threshold is: %d\n",
+                       le32_to_cpu(cmd->bf_temp_threshold));
+       IWL_DEBUG_POWER(mvm, "bf_temp_fast_filter is: %d\n",
+                       le32_to_cpu(cmd->bf_temp_fast_filter));
+       IWL_DEBUG_POWER(mvm, "bf_temp_slow_filter is: %d\n",
+                       le32_to_cpu(cmd->bf_temp_slow_filter));
+
+       return iwl_mvm_send_cmd_pdu(mvm, REPLY_BEACON_FILTERING_CMD, flags,
+                                   sizeof(struct iwl_beacon_filter_cmd), cmd);
 }
 
 static
@@ -145,7 +142,7 @@ int iwl_mvm_update_beacon_abort(struct iwl_mvm *mvm,
        mvmvif->bf_data.ba_enabled = enable;
        iwl_mvm_beacon_filter_set_cqm_params(mvm, vif, &cmd);
        iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd);
-       return iwl_mvm_beacon_filter_send_cmd(mvm, &cmd);
+       return iwl_mvm_beacon_filter_send_cmd(mvm, &cmd, CMD_SYNC);
 }
 
 static void iwl_mvm_power_log(struct iwl_mvm *mvm,
@@ -301,8 +298,7 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
        keep_alive = DIV_ROUND_UP(keep_alive, MSEC_PER_SEC);
        cmd->keep_alive_seconds = cpu_to_le16(keep_alive);
 
-       if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM ||
-           mvm->ps_prevented)
+       if (mvm->ps_disabled)
                return;
 
        cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);
@@ -312,7 +308,8 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
            mvmvif->dbgfs_pm.disable_power_off)
                cmd->flags &= cpu_to_le16(~POWER_FLAGS_POWER_SAVE_ENA_MSK);
 #endif
-       if (!vif->bss_conf.ps || mvmvif->pm_prevented)
+       if (!vif->bss_conf.ps || iwl_mvm_vif_low_latency(mvmvif) ||
+           mvm->pm_disabled)
                return;
 
        cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK);
@@ -419,11 +416,9 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
 #endif /* CONFIG_IWLWIFI_DEBUGFS */
 }
 
-static int iwl_mvm_power_mac_update_mode(struct iwl_mvm *mvm,
+static int iwl_mvm_power_send_cmd(struct iwl_mvm *mvm,
                                         struct ieee80211_vif *vif)
 {
-       int ret;
-       bool ba_enable;
        struct iwl_mac_power_cmd cmd = {};
 
        if (vif->type != NL80211_IFTYPE_STATION)
@@ -435,56 +430,30 @@ static int iwl_mvm_power_mac_update_mode(struct iwl_mvm *mvm,
 
        iwl_mvm_power_build_cmd(mvm, vif, &cmd);
        iwl_mvm_power_log(mvm, &cmd);
-
-       ret = iwl_mvm_send_cmd_pdu(mvm, MAC_PM_POWER_TABLE, CMD_SYNC,
-                                  sizeof(cmd), &cmd);
-       if (ret)
-               return ret;
-
-       ba_enable = !!(cmd.flags &
-                      cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK));
-
-       return iwl_mvm_update_beacon_abort(mvm, vif, ba_enable);
-}
-
-static int iwl_mvm_power_mac_disable(struct iwl_mvm *mvm,
-                                    struct ieee80211_vif *vif)
-{
-       struct iwl_mac_power_cmd cmd = {};
-       struct iwl_mvm_vif *mvmvif __maybe_unused =
-               iwl_mvm_vif_from_mac80211(vif);
-
-       if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
-               return 0;
-
-       cmd.id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
-                                                          mvmvif->color));
-
-       if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM)
-               cmd.flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);
-
 #ifdef CONFIG_IWLWIFI_DEBUGFS
-       if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_DISABLE_POWER_OFF &&
-           mvmvif->dbgfs_pm.disable_power_off)
-               cmd.flags &= cpu_to_le16(~POWER_FLAGS_POWER_SAVE_ENA_MSK);
+       memcpy(&iwl_mvm_vif_from_mac80211(vif)->mac_pwr_cmd, &cmd, sizeof(cmd));
 #endif
-       iwl_mvm_power_log(mvm, &cmd);
 
-       return iwl_mvm_send_cmd_pdu(mvm, MAC_PM_POWER_TABLE, CMD_ASYNC,
+       return iwl_mvm_send_cmd_pdu(mvm, MAC_PM_POWER_TABLE, CMD_SYNC,
                                    sizeof(cmd), &cmd);
 }
 
-static int _iwl_mvm_power_update_device(struct iwl_mvm *mvm, bool force_disable)
+int iwl_mvm_power_update_device(struct iwl_mvm *mvm)
 {
        struct iwl_device_power_cmd cmd = {
                .flags = cpu_to_le16(DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK),
        };
 
+       if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_PM_CMD_SUPPORT))
+               return 0;
+
        if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_DEVICE_PS_CMD))
                return 0;
 
-       if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM ||
-           force_disable)
+       if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM)
+               mvm->ps_disabled = true;
+
+       if (mvm->ps_disabled)
                cmd.flags |= cpu_to_le16(DEVICE_POWER_FLAGS_CAM_MSK);
 
 #ifdef CONFIG_IWLWIFI_DEBUGFS
@@ -501,11 +470,6 @@ static int _iwl_mvm_power_update_device(struct iwl_mvm *mvm, bool force_disable)
                                    &cmd);
 }
 
-static int iwl_mvm_power_update_device(struct iwl_mvm *mvm)
-{
-       return _iwl_mvm_power_update_device(mvm, false);
-}
-
 void iwl_mvm_power_vif_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
 {
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
@@ -544,44 +508,137 @@ int iwl_mvm_power_uapsd_misbehaving_ap_notif(struct iwl_mvm *mvm,
        return 0;
 }
 
-static void iwl_mvm_power_binding_iterator(void *_data, u8 *mac,
-                                          struct ieee80211_vif *vif)
+struct iwl_power_constraint {
+       struct ieee80211_vif *bf_vif;
+       struct ieee80211_vif *bss_vif;
+       bool pm_disabled;
+       bool ps_disabled;
+};
+
+static void iwl_mvm_power_iterator(void *_data, u8 *mac,
+                                  struct ieee80211_vif *vif)
 {
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-       struct iwl_mvm *mvm = _data;
-       int ret;
+       struct iwl_power_constraint *power_iterator = _data;
+
+       switch (ieee80211_vif_type_p2p(vif)) {
+       case NL80211_IFTYPE_P2P_DEVICE:
+               break;
+
+       case NL80211_IFTYPE_P2P_GO:
+       case NL80211_IFTYPE_AP:
+               /* no BSS power mgmt if we have an active AP */
+               if (mvmvif->ap_ibss_active)
+                       power_iterator->pm_disabled = true;
+               break;
+
+       case NL80211_IFTYPE_MONITOR:
+               /* no BSS power mgmt and no device power save */
+               power_iterator->pm_disabled = true;
+               power_iterator->ps_disabled = true;
+               break;
+
+       case NL80211_IFTYPE_P2P_CLIENT:
+               /* no BSS power mgmt if we have a P2P client*/
+               power_iterator->pm_disabled = true;
+               break;
+
+       case NL80211_IFTYPE_STATION:
+               /* we should have only one BSS vif */
+               WARN_ON(power_iterator->bss_vif);
+               power_iterator->bss_vif = vif;
+
+               if (mvmvif->bf_data.bf_enabled &&
+                   !WARN_ON(power_iterator->bf_vif))
+                       power_iterator->bf_vif = vif;
+               break;
+
+       default:
+               break;
+       }
+}
+
+static void
+iwl_mvm_power_get_global_constraint(struct iwl_mvm *mvm,
+                                   struct iwl_power_constraint *constraint)
+{
+       lockdep_assert_held(&mvm->mutex);
+
+       if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM) {
+               constraint->pm_disabled = true;
+               constraint->ps_disabled = true;
+       }
 
-       mvmvif->pm_prevented = (mvm->bound_vif_cnt <= 1) ? false : true;
+       ieee80211_iterate_active_interfaces_atomic(mvm->hw,
+                                           IEEE80211_IFACE_ITER_NORMAL,
+                                           iwl_mvm_power_iterator, constraint);
 
-       ret = iwl_mvm_power_mac_update_mode(mvm, vif);
-       WARN_ONCE(ret, "Failed to update power parameters on a specific vif\n");
+       /* TODO: remove this and determine this variable in the iterator */
+       if (mvm->bound_vif_cnt > 1)
+               constraint->pm_disabled = true;
 }
 
-static void _iwl_mvm_power_update_binding(struct iwl_mvm *mvm,
-                                         struct ieee80211_vif *vif,
-                                         bool assign)
+int iwl_mvm_power_update_mac(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
 {
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_power_constraint constraint = {};
+       bool ba_enable;
+       int ret;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_PM_CMD_SUPPORT))
+               return 0;
+
+       iwl_mvm_power_get_global_constraint(mvm, &constraint);
+       mvm->ps_disabled = constraint.ps_disabled;
+       mvm->pm_disabled = constraint.pm_disabled;
+
+       /* don't update device power state unless we add / remove monitor */
        if (vif->type == NL80211_IFTYPE_MONITOR) {
-               int ret = _iwl_mvm_power_update_device(mvm, assign);
-               mvm->ps_prevented = assign;
-               WARN_ONCE(ret, "Failed to update power device state\n");
+               ret = iwl_mvm_power_update_device(mvm);
+               if (ret)
+                       return ret;
        }
 
-       ieee80211_iterate_active_interfaces(mvm->hw,
-                                           IEEE80211_IFACE_ITER_NORMAL,
-                                           iwl_mvm_power_binding_iterator,
-                                           mvm);
+       ret = iwl_mvm_power_send_cmd(mvm, vif);
+       if (ret)
+               return ret;
+
+       if (constraint.bss_vif && vif != constraint.bss_vif) {
+               ret = iwl_mvm_power_send_cmd(mvm, constraint.bss_vif);
+               if (ret)
+                       return ret;
+       }
+
+       if (!constraint.bf_vif)
+               return 0;
+
+       vif = constraint.bf_vif;
+       mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       ba_enable = !(constraint.pm_disabled || constraint.ps_disabled ||
+                     !vif->bss_conf.ps || iwl_mvm_vif_low_latency(mvmvif));
+
+       return iwl_mvm_update_beacon_abort(mvm, constraint.bf_vif, ba_enable);
 }
 
 #ifdef CONFIG_IWLWIFI_DEBUGFS
-static int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm,
-                                       struct ieee80211_vif *vif, char *buf,
-                                       int bufsz)
+int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm,
+                                struct ieee80211_vif *vif, char *buf,
+                                int bufsz)
 {
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        struct iwl_mac_power_cmd cmd = {};
        int pos = 0;
 
-       iwl_mvm_power_build_cmd(mvm, vif, &cmd);
+       if (WARN_ON(!(mvm->fw->ucode_capa.flags &
+                     IWL_UCODE_TLV_FLAGS_PM_CMD_SUPPORT)))
+               return 0;
+
+       mutex_lock(&mvm->mutex);
+       memcpy(&cmd, &mvmvif->mac_pwr_cmd, sizeof(cmd));
+       mutex_unlock(&mvm->mutex);
 
        if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_DEVICE_PS_CMD))
                pos += scnprintf(buf+pos, bufsz-pos, "disable_power_off = %d\n",
@@ -685,32 +742,46 @@ iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif,
 }
 #endif
 
-int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm,
-                                struct ieee80211_vif *vif)
+static int _iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm,
+                                        struct ieee80211_vif *vif,
+                                        struct iwl_beacon_filter_cmd *cmd,
+                                        u32 cmd_flags,
+                                        bool d0i3)
 {
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-       struct iwl_beacon_filter_cmd cmd = {
-               IWL_BF_CMD_CONFIG_DEFAULTS,
-               .bf_enable_beacon_filter = cpu_to_le32(1),
-       };
        int ret;
 
        if (mvmvif != mvm->bf_allowed_vif ||
            vif->type != NL80211_IFTYPE_STATION || vif->p2p)
                return 0;
 
-       iwl_mvm_beacon_filter_set_cqm_params(mvm, vif, &cmd);
-       iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd);
-       ret = iwl_mvm_beacon_filter_send_cmd(mvm, &cmd);
+       iwl_mvm_beacon_filter_set_cqm_params(mvm, vif, cmd);
+       if (!d0i3)
+               iwl_mvm_beacon_filter_debugfs_parameters(vif, cmd);
+       ret = iwl_mvm_beacon_filter_send_cmd(mvm, cmd, cmd_flags);
 
-       if (!ret)
+       /* don't change bf_enabled in case of temporary d0i3 configuration */
+       if (!ret && !d0i3)
                mvmvif->bf_data.bf_enabled = true;
 
        return ret;
 }
 
+int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm,
+                                struct ieee80211_vif *vif,
+                                u32 flags)
+{
+       struct iwl_beacon_filter_cmd cmd = {
+               IWL_BF_CMD_CONFIG_DEFAULTS,
+               .bf_enable_beacon_filter = cpu_to_le32(1),
+       };
+
+       return _iwl_mvm_enable_beacon_filter(mvm, vif, &cmd, flags, false);
+}
+
 int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm,
-                                 struct ieee80211_vif *vif)
+                                 struct ieee80211_vif *vif,
+                                 u32 flags)
 {
        struct iwl_beacon_filter_cmd cmd = {};
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
@@ -720,7 +791,7 @@ int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm,
            vif->type != NL80211_IFTYPE_STATION || vif->p2p)
                return 0;
 
-       ret = iwl_mvm_beacon_filter_send_cmd(mvm, &cmd);
+       ret = iwl_mvm_beacon_filter_send_cmd(mvm, &cmd, flags);
 
        if (!ret)
                mvmvif->bf_data.bf_enabled = false;
@@ -728,23 +799,89 @@ int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm,
        return ret;
 }
 
-int iwl_mvm_update_beacon_filter(struct iwl_mvm *mvm,
-                                struct ieee80211_vif *vif)
+int iwl_mvm_update_d0i3_power_mode(struct iwl_mvm *mvm,
+                                  struct ieee80211_vif *vif,
+                                  bool enable, u32 flags)
 {
+       int ret;
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_mac_power_cmd cmd = {};
 
-       if (!mvmvif->bf_data.bf_enabled)
+       if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
                return 0;
 
-       return iwl_mvm_enable_beacon_filter(mvm, vif);
-}
+       if (!vif->bss_conf.assoc)
+               return 0;
 
-const struct iwl_mvm_power_ops pm_mac_ops = {
-       .power_update_mode = iwl_mvm_power_mac_update_mode,
-       .power_update_device_mode = iwl_mvm_power_update_device,
-       .power_disable = iwl_mvm_power_mac_disable,
-       .power_update_binding = _iwl_mvm_power_update_binding,
+       iwl_mvm_power_build_cmd(mvm, vif, &cmd);
+       if (enable) {
+               /* configure skip over dtim up to 300 msec */
+               int dtimper = mvm->hw->conf.ps_dtim_period ?: 1;
+               int dtimper_msec = dtimper * vif->bss_conf.beacon_int;
+
+               if (WARN_ON(!dtimper_msec))
+                       return 0;
+
+               cmd.flags |=
+                       cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
+               cmd.skip_dtim_periods = 300 / dtimper_msec;
+       }
+       iwl_mvm_power_log(mvm, &cmd);
 #ifdef CONFIG_IWLWIFI_DEBUGFS
-       .power_dbgfs_read = iwl_mvm_power_mac_dbgfs_read,
+       memcpy(&mvmvif->mac_pwr_cmd, &cmd, sizeof(cmd));
 #endif
-};
+       ret = iwl_mvm_send_cmd_pdu(mvm, MAC_PM_POWER_TABLE, flags,
+                                  sizeof(cmd), &cmd);
+       if (ret)
+               return ret;
+
+       /* configure beacon filtering */
+       if (mvmvif != mvm->bf_allowed_vif)
+               return 0;
+
+       if (enable) {
+               struct iwl_beacon_filter_cmd cmd_bf = {
+                       IWL_BF_CMD_CONFIG_D0I3,
+                       .bf_enable_beacon_filter = cpu_to_le32(1),
+               };
+               ret = _iwl_mvm_enable_beacon_filter(mvm, vif, &cmd_bf,
+                                                   flags, true);
+       } else {
+               if (mvmvif->bf_data.bf_enabled)
+                       ret = iwl_mvm_enable_beacon_filter(mvm, vif, flags);
+               else
+                       ret = iwl_mvm_disable_beacon_filter(mvm, vif, flags);
+       }
+
+       return ret;
+}
+
+int iwl_mvm_update_beacon_filter(struct iwl_mvm *mvm,
+                                struct ieee80211_vif *vif,
+                                bool force,
+                                u32 flags)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       if (mvmvif != mvm->bf_allowed_vif)
+               return 0;
+
+       if (!mvmvif->bf_data.bf_enabled) {
+               /* disable beacon filtering explicitly if force is true */
+               if (force)
+                       return iwl_mvm_disable_beacon_filter(mvm, vif, flags);
+               return 0;
+       }
+
+       return iwl_mvm_enable_beacon_filter(mvm, vif, flags);
+}
+
+int iwl_power_legacy_set_cam_mode(struct iwl_mvm *mvm)
+{
+       struct iwl_powertable_cmd cmd = {
+               .keep_alive_seconds = POWER_KEEP_ALIVE_PERIOD_SEC,
+       };
+
+       return iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_SYNC,
+                                   sizeof(cmd), &cmd);
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/power_legacy.c b/drivers/net/wireless/iwlwifi/mvm/power_legacy.c
deleted file mode 100644 (file)
index ef712ae..0000000
+++ /dev/null
@@ -1,319 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license.  When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2012 - 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) 2012 - 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/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-
-#include <net/mac80211.h>
-
-#include "iwl-debug.h"
-#include "mvm.h"
-#include "iwl-modparams.h"
-#include "fw-api-power.h"
-
-#define POWER_KEEP_ALIVE_PERIOD_SEC    25
-
-static void iwl_mvm_power_log(struct iwl_mvm *mvm,
-                             struct iwl_powertable_cmd *cmd)
-{
-       IWL_DEBUG_POWER(mvm,
-                       "Sending power table command for power level %d, flags = 0x%X\n",
-                       iwlmvm_mod_params.power_scheme,
-                       le16_to_cpu(cmd->flags));
-       IWL_DEBUG_POWER(mvm, "Keep alive = %u sec\n", cmd->keep_alive_seconds);
-
-       if (cmd->flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)) {
-               IWL_DEBUG_POWER(mvm, "Rx timeout = %u usec\n",
-                               le32_to_cpu(cmd->rx_data_timeout));
-               IWL_DEBUG_POWER(mvm, "Tx timeout = %u usec\n",
-                               le32_to_cpu(cmd->tx_data_timeout));
-               if (cmd->flags & cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK))
-                       IWL_DEBUG_POWER(mvm, "DTIM periods to skip = %u\n",
-                                       le32_to_cpu(cmd->skip_dtim_periods));
-               if (cmd->flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK))
-                       IWL_DEBUG_POWER(mvm, "LP RX RSSI threshold = %u\n",
-                                       le32_to_cpu(cmd->lprx_rssi_threshold));
-       }
-}
-
-static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
-                                   struct ieee80211_vif *vif,
-                                   struct iwl_powertable_cmd *cmd)
-{
-       struct ieee80211_hw *hw = mvm->hw;
-       struct ieee80211_chanctx_conf *chanctx_conf;
-       struct ieee80211_channel *chan;
-       int dtimper, dtimper_msec;
-       int keep_alive;
-       bool radar_detect = false;
-       struct iwl_mvm_vif *mvmvif __maybe_unused =
-               iwl_mvm_vif_from_mac80211(vif);
-
-       /*
-        * Regardless of power management state the driver must set
-        * keep alive period. FW will use it for sending keep alive NDPs
-        * immediately after association.
-        */
-       cmd->keep_alive_seconds = POWER_KEEP_ALIVE_PERIOD_SEC;
-
-       if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM)
-               return;
-
-       cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);
-       if (!vif->bss_conf.assoc)
-               cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK);
-
-#ifdef CONFIG_IWLWIFI_DEBUGFS
-       if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_DISABLE_POWER_OFF &&
-           mvmvif->dbgfs_pm.disable_power_off)
-               cmd->flags &= cpu_to_le16(~POWER_FLAGS_POWER_SAVE_ENA_MSK);
-#endif
-       if (!vif->bss_conf.ps)
-               return;
-
-       cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK);
-
-       if (vif->bss_conf.beacon_rate &&
-           (vif->bss_conf.beacon_rate->bitrate == 10 ||
-            vif->bss_conf.beacon_rate->bitrate == 60)) {
-               cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK);
-               cmd->lprx_rssi_threshold =
-                       cpu_to_le32(POWER_LPRX_RSSI_THRESHOLD);
-       }
-
-       dtimper = hw->conf.ps_dtim_period ?: 1;
-
-       /* Check if radar detection is required on current channel */
-       rcu_read_lock();
-       chanctx_conf = rcu_dereference(vif->chanctx_conf);
-       WARN_ON(!chanctx_conf);
-       if (chanctx_conf) {
-               chan = chanctx_conf->def.chan;
-               radar_detect = chan->flags & IEEE80211_CHAN_RADAR;
-       }
-       rcu_read_unlock();
-
-       /* Check skip over DTIM conditions */
-       if (!radar_detect && (dtimper <= 10) &&
-           (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_LP ||
-            mvm->cur_ucode == IWL_UCODE_WOWLAN)) {
-               cmd->flags |= cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
-               cmd->skip_dtim_periods = cpu_to_le32(3);
-       }
-
-       /* Check that keep alive period is at least 3 * DTIM */
-       dtimper_msec = dtimper * vif->bss_conf.beacon_int;
-       keep_alive = max_t(int, 3 * dtimper_msec,
-                          MSEC_PER_SEC * cmd->keep_alive_seconds);
-       keep_alive = DIV_ROUND_UP(keep_alive, MSEC_PER_SEC);
-       cmd->keep_alive_seconds = keep_alive;
-
-       if (mvm->cur_ucode != IWL_UCODE_WOWLAN) {
-               cmd->rx_data_timeout = cpu_to_le32(100 * USEC_PER_MSEC);
-               cmd->tx_data_timeout = cpu_to_le32(100 * USEC_PER_MSEC);
-       } else {
-               cmd->rx_data_timeout = cpu_to_le32(10 * USEC_PER_MSEC);
-               cmd->tx_data_timeout = cpu_to_le32(10 * USEC_PER_MSEC);
-       }
-
-#ifdef CONFIG_IWLWIFI_DEBUGFS
-       if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_KEEP_ALIVE)
-               cmd->keep_alive_seconds = mvmvif->dbgfs_pm.keep_alive_seconds;
-       if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_OVER_DTIM) {
-               if (mvmvif->dbgfs_pm.skip_over_dtim)
-                       cmd->flags |=
-                               cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
-               else
-                       cmd->flags &=
-                               cpu_to_le16(~POWER_FLAGS_SKIP_OVER_DTIM_MSK);
-       }
-       if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_RX_DATA_TIMEOUT)
-               cmd->rx_data_timeout =
-                       cpu_to_le32(mvmvif->dbgfs_pm.rx_data_timeout);
-       if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_TX_DATA_TIMEOUT)
-               cmd->tx_data_timeout =
-                       cpu_to_le32(mvmvif->dbgfs_pm.tx_data_timeout);
-       if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS)
-               cmd->skip_dtim_periods =
-                       cpu_to_le32(mvmvif->dbgfs_pm.skip_dtim_periods);
-       if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_LPRX_ENA) {
-               if (mvmvif->dbgfs_pm.lprx_ena)
-                       cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK);
-               else
-                       cmd->flags &= cpu_to_le16(~POWER_FLAGS_LPRX_ENA_MSK);
-       }
-       if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD)
-               cmd->lprx_rssi_threshold =
-                       cpu_to_le32(mvmvif->dbgfs_pm.lprx_rssi_threshold);
-#endif /* CONFIG_IWLWIFI_DEBUGFS */
-}
-
-static int iwl_mvm_power_legacy_update_mode(struct iwl_mvm *mvm,
-                                           struct ieee80211_vif *vif)
-{
-       int ret;
-       bool ba_enable;
-       struct iwl_powertable_cmd cmd = {};
-
-       if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
-               return 0;
-
-       /*
-        * TODO: The following vif_count verification is temporary condition.
-        * Avoid power mode update if more than one interface is currently
-        * active. Remove this condition when FW will support power management
-        * on multiple MACs.
-        */
-       IWL_DEBUG_POWER(mvm, "Currently %d interfaces active\n",
-                       mvm->vif_count);
-       if (mvm->vif_count > 1)
-               return 0;
-
-       iwl_mvm_power_build_cmd(mvm, vif, &cmd);
-       iwl_mvm_power_log(mvm, &cmd);
-
-       ret = iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_SYNC,
-                                  sizeof(cmd), &cmd);
-       if (ret)
-               return ret;
-
-       ba_enable = !!(cmd.flags &
-                      cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK));
-
-       return iwl_mvm_update_beacon_abort(mvm, vif, ba_enable);
-}
-
-static int iwl_mvm_power_legacy_disable(struct iwl_mvm *mvm,
-                                       struct ieee80211_vif *vif)
-{
-       struct iwl_powertable_cmd cmd = {};
-       struct iwl_mvm_vif *mvmvif __maybe_unused =
-               iwl_mvm_vif_from_mac80211(vif);
-
-       if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
-               return 0;
-
-       if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM)
-               cmd.flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);
-
-#ifdef CONFIG_IWLWIFI_DEBUGFS
-       if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_DISABLE_POWER_OFF &&
-           mvmvif->dbgfs_pm.disable_power_off)
-               cmd.flags &= cpu_to_le16(~POWER_FLAGS_POWER_SAVE_ENA_MSK);
-#endif
-       iwl_mvm_power_log(mvm, &cmd);
-
-       return iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_ASYNC,
-                                   sizeof(cmd), &cmd);
-}
-
-#ifdef CONFIG_IWLWIFI_DEBUGFS
-static int iwl_mvm_power_legacy_dbgfs_read(struct iwl_mvm *mvm,
-                                          struct ieee80211_vif *vif, char *buf,
-                                          int bufsz)
-{
-       struct iwl_powertable_cmd cmd = {};
-       int pos = 0;
-
-       iwl_mvm_power_build_cmd(mvm, vif, &cmd);
-
-       pos += scnprintf(buf+pos, bufsz-pos, "disable_power_off = %d\n",
-                        (cmd.flags &
-                        cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK)) ?
-                        0 : 1);
-       pos += scnprintf(buf+pos, bufsz-pos, "skip_dtim_periods = %d\n",
-                        le32_to_cpu(cmd.skip_dtim_periods));
-       pos += scnprintf(buf+pos, bufsz-pos, "power_scheme = %d\n",
-                        iwlmvm_mod_params.power_scheme);
-       pos += scnprintf(buf+pos, bufsz-pos, "flags = 0x%x\n",
-                        le16_to_cpu(cmd.flags));
-       pos += scnprintf(buf+pos, bufsz-pos, "keep_alive = %d\n",
-                        cmd.keep_alive_seconds);
-
-       if (cmd.flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)) {
-               pos += scnprintf(buf+pos, bufsz-pos, "skip_over_dtim = %d\n",
-                                (cmd.flags &
-                                cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK)) ?
-                                1 : 0);
-               pos += scnprintf(buf+pos, bufsz-pos, "rx_data_timeout = %d\n",
-                                le32_to_cpu(cmd.rx_data_timeout));
-               pos += scnprintf(buf+pos, bufsz-pos, "tx_data_timeout = %d\n",
-                                le32_to_cpu(cmd.tx_data_timeout));
-               if (cmd.flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK))
-                       pos += scnprintf(buf+pos, bufsz-pos,
-                                        "lprx_rssi_threshold = %d\n",
-                                        le32_to_cpu(cmd.lprx_rssi_threshold));
-       }
-       return pos;
-}
-#endif
-
-const struct iwl_mvm_power_ops pm_legacy_ops = {
-       .power_update_mode = iwl_mvm_power_legacy_update_mode,
-       .power_disable = iwl_mvm_power_legacy_disable,
-#ifdef CONFIG_IWLWIFI_DEBUGFS
-       .power_dbgfs_read = iwl_mvm_power_legacy_dbgfs_read,
-#endif
-};
index ce5db6c..06d8429 100644 (file)
 #include "fw-api.h"
 #include "mvm.h"
 
+#define QUOTA_100      IWL_MVM_MAX_QUOTA
+#define QUOTA_LOWLAT_MIN ((QUOTA_100 * IWL_MVM_LOWLAT_QUOTA_MIN_PERCENT) / 100)
+
 struct iwl_mvm_quota_iterator_data {
        int n_interfaces[MAX_BINDINGS];
        int colors[MAX_BINDINGS];
+       int low_latency[MAX_BINDINGS];
+       int n_low_latency_bindings;
        struct ieee80211_vif *new_vif;
 };
 
@@ -107,22 +112,29 @@ static void iwl_mvm_quota_iterator(void *_data, u8 *mac,
        switch (vif->type) {
        case NL80211_IFTYPE_STATION:
                if (vif->bss_conf.assoc)
-                       data->n_interfaces[id]++;
-               break;
+                       break;
+               return;
        case NL80211_IFTYPE_AP:
        case NL80211_IFTYPE_ADHOC:
                if (mvmvif->ap_ibss_active)
-                       data->n_interfaces[id]++;
-               break;
+                       break;
+               return;
        case NL80211_IFTYPE_MONITOR:
                if (mvmvif->monitor_active)
-                       data->n_interfaces[id]++;
-               break;
+                       break;
+               return;
        case NL80211_IFTYPE_P2P_DEVICE:
-               break;
+               return;
        default:
                WARN_ON_ONCE(1);
-               break;
+               return;
+       }
+
+       data->n_interfaces[id]++;
+
+       if (iwl_mvm_vif_low_latency(mvmvif) && !data->low_latency[id]) {
+               data->n_low_latency_bindings++;
+               data->low_latency[id] = true;
        }
 }
 
@@ -162,12 +174,13 @@ static void iwl_mvm_adjust_quota_for_noa(struct iwl_mvm *mvm,
 int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif)
 {
        struct iwl_time_quota_cmd cmd = {};
-       int i, idx, ret, num_active_macs, quota, quota_rem;
+       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,
        };
+       u32 ll_max_duration;
 
        lockdep_assert_held(&mvm->mutex);
 
@@ -186,6 +199,21 @@ int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif)
                iwl_mvm_quota_iterator(&data, newvif->addr, newvif);
        }
 
+       switch (data.n_low_latency_bindings) {
+       case 0: /* no low latency - use default */
+               ll_max_duration = 0;
+               break;
+       case 1: /* SingleBindingLowLatencyMode */
+               ll_max_duration = IWL_MVM_LOWLAT_SINGLE_BINDING_MAXDUR;
+               break;
+       case 2: /* DualBindingLowLatencyMode */
+               ll_max_duration = IWL_MVM_LOWLAT_DUAL_BINDING_MAXDUR;
+               break;
+       default: /* MultiBindingLowLatencyMode */
+               ll_max_duration = 0;
+               break;
+       }
+
        /*
         * The FW's scheduling session consists of
         * IWL_MVM_MAX_QUOTA fragments. Divide these fragments
@@ -197,11 +225,39 @@ int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif)
                num_active_macs += data.n_interfaces[i];
        }
 
-       quota = 0;
-       quota_rem = 0;
-       if (num_active_macs) {
-               quota = IWL_MVM_MAX_QUOTA / num_active_macs;
-               quota_rem = IWL_MVM_MAX_QUOTA % num_active_macs;
+       n_non_lowlat = num_active_macs;
+
+       if (data.n_low_latency_bindings == 1) {
+               for (i = 0; i < MAX_BINDINGS; i++) {
+                       if (data.low_latency[i]) {
+                               n_non_lowlat -= data.n_interfaces[i];
+                               break;
+                       }
+               }
+       }
+
+       if (data.n_low_latency_bindings == 1 && n_non_lowlat) {
+               /*
+                * Reserve quota for the low latency binding in case that
+                * there are several data bindings but only a single
+                * low latency one. Split the rest of the quota equally
+                * between the other data interfaces.
+                */
+               quota = (QUOTA_100 - QUOTA_LOWLAT_MIN) / n_non_lowlat;
+               quota_rem = QUOTA_100 - n_non_lowlat * quota -
+                           QUOTA_LOWLAT_MIN;
+       } else if (num_active_macs) {
+               /*
+                * There are 0 or more than 1 low latency bindings, or all the
+                * data interfaces belong to the single low latency binding.
+                * Split the quota equally between the data interfaces.
+                */
+               quota = QUOTA_100 / num_active_macs;
+               quota_rem = QUOTA_100 % num_active_macs;
+       } else {
+               /* values don't really matter - won't be used */
+               quota = 0;
+               quota_rem = 0;
        }
 
        for (idx = 0, i = 0; i < MAX_BINDINGS; i++) {
@@ -211,19 +267,42 @@ int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif)
                cmd.quotas[idx].id_and_color =
                        cpu_to_le32(FW_CMD_ID_AND_COLOR(i, data.colors[i]));
 
-               if (data.n_interfaces[i] <= 0) {
+               if (data.n_interfaces[i] <= 0)
                        cmd.quotas[idx].quota = cpu_to_le32(0);
-                       cmd.quotas[idx].max_duration = cpu_to_le32(0);
-               } else {
+               else if (data.n_low_latency_bindings == 1 && n_non_lowlat &&
+                        data.low_latency[i])
+                       /*
+                        * There is more than one binding, but only one of the
+                        * bindings is in low latency. For this case, allocate
+                        * the minimal required quota for the low latency
+                        * binding.
+                        */
+                       cmd.quotas[idx].quota = cpu_to_le32(QUOTA_LOWLAT_MIN);
+
+               else
                        cmd.quotas[idx].quota =
                                cpu_to_le32(quota * data.n_interfaces[i]);
+
+               WARN_ONCE(le32_to_cpu(cmd.quotas[idx].quota) > QUOTA_100,
+                         "Binding=%d, quota=%u > max=%u\n",
+                         idx, le32_to_cpu(cmd.quotas[idx].quota), QUOTA_100);
+
+               if (data.n_interfaces[i] && !data.low_latency[i])
+                       cmd.quotas[idx].max_duration =
+                               cpu_to_le32(ll_max_duration);
+               else
                        cmd.quotas[idx].max_duration = cpu_to_le32(0);
-               }
+
                idx++;
        }
 
-       /* Give the remainder of the session to the first binding */
-       le32_add_cpu(&cmd.quotas[0].quota, quota_rem);
+       /* Give the remainder of the session to the first data binding */
+       for (i = 0; i < MAX_BINDINGS; i++) {
+               if (le32_to_cpu(cmd.quotas[i].quota) != 0) {
+                       le32_add_cpu(&cmd.quotas[i].quota, quota_rem);
+                       break;
+               }
+       }
 
        iwl_mvm_adjust_quota_for_noa(mvm, &cmd);
 
index 6abf74e..32bb807 100644 (file)
@@ -380,49 +380,49 @@ static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search);
  * (2.4 GHz) band.
  */
 
-static s32 expected_tpt_legacy[IWL_RATE_COUNT] = {
+static const u16 expected_tpt_legacy[IWL_RATE_COUNT] = {
        7, 13, 35, 58, 40, 57, 72, 98, 121, 154, 177, 186, 0, 0, 0
 };
 
 /* Expected TpT tables. 4 indexes:
  * 0 - NGI, 1 - SGI, 2 - AGG+NGI, 3 - AGG+SGI
  */
-static s32 expected_tpt_siso_20MHz[4][IWL_RATE_COUNT] = {
+static const u16 expected_tpt_siso_20MHz[4][IWL_RATE_COUNT] = {
        {0, 0, 0, 0, 42, 0,  76, 102, 124, 159, 183, 193, 202, 216, 0},
        {0, 0, 0, 0, 46, 0,  82, 110, 132, 168, 192, 202, 210, 225, 0},
        {0, 0, 0, 0, 49, 0,  97, 145, 192, 285, 375, 420, 464, 551, 0},
        {0, 0, 0, 0, 54, 0, 108, 160, 213, 315, 415, 465, 513, 608, 0},
 };
 
-static s32 expected_tpt_siso_40MHz[4][IWL_RATE_COUNT] = {
+static const u16 expected_tpt_siso_40MHz[4][IWL_RATE_COUNT] = {
        {0, 0, 0, 0,  77, 0, 127, 160, 184, 220, 242, 250,  257,  269,  275},
        {0, 0, 0, 0,  83, 0, 135, 169, 193, 229, 250, 257,  264,  275,  280},
        {0, 0, 0, 0, 101, 0, 199, 295, 389, 570, 744, 828,  911, 1070, 1173},
        {0, 0, 0, 0, 112, 0, 220, 326, 429, 629, 819, 912, 1000, 1173, 1284},
 };
 
-static s32 expected_tpt_siso_80MHz[4][IWL_RATE_COUNT] = {
+static const u16 expected_tpt_siso_80MHz[4][IWL_RATE_COUNT] = {
        {0, 0, 0, 0, 130, 0, 191, 223, 244,  273,  288,  294,  298,  305,  308},
        {0, 0, 0, 0, 138, 0, 200, 231, 251,  279,  293,  298,  302,  308,  312},
        {0, 0, 0, 0, 217, 0, 429, 634, 834, 1220, 1585, 1760, 1931, 2258, 2466},
        {0, 0, 0, 0, 241, 0, 475, 701, 921, 1343, 1741, 1931, 2117, 2468, 2691},
 };
 
-static s32 expected_tpt_mimo2_20MHz[4][IWL_RATE_COUNT] = {
+static const u16 expected_tpt_mimo2_20MHz[4][IWL_RATE_COUNT] = {
        {0, 0, 0, 0,  74, 0, 123, 155, 179, 213, 235, 243, 250,  261, 0},
        {0, 0, 0, 0,  81, 0, 131, 164, 187, 221, 242, 250, 256,  267, 0},
        {0, 0, 0, 0,  98, 0, 193, 286, 375, 550, 718, 799, 878, 1032, 0},
        {0, 0, 0, 0, 109, 0, 214, 316, 414, 607, 790, 879, 965, 1132, 0},
 };
 
-static s32 expected_tpt_mimo2_40MHz[4][IWL_RATE_COUNT] = {
+static const u16 expected_tpt_mimo2_40MHz[4][IWL_RATE_COUNT] = {
        {0, 0, 0, 0, 123, 0, 182, 214, 235,  264,  279,  285,  289,  296,  300},
        {0, 0, 0, 0, 131, 0, 191, 222, 242,  270,  284,  289,  293,  300,  303},
        {0, 0, 0, 0, 200, 0, 390, 571, 741, 1067, 1365, 1505, 1640, 1894, 2053},
        {0, 0, 0, 0, 221, 0, 430, 630, 816, 1169, 1490, 1641, 1784, 2053, 2221},
 };
 
-static s32 expected_tpt_mimo2_80MHz[4][IWL_RATE_COUNT] = {
+static const u16 expected_tpt_mimo2_80MHz[4][IWL_RATE_COUNT] = {
        {0, 0, 0, 0, 182, 0, 240,  264,  278,  299,  308,  311,  313,  317,  319},
        {0, 0, 0, 0, 190, 0, 247,  269,  282,  302,  310,  313,  315,  319,  320},
        {0, 0, 0, 0, 428, 0, 833, 1215, 1577, 2254, 2863, 3147, 3418, 3913, 4219},
@@ -905,7 +905,7 @@ static void rs_get_lower_rate_down_column(struct iwl_lq_sta *lq_sta,
 
                rate->bw = RATE_MCS_CHAN_WIDTH_20;
 
-               WARN_ON_ONCE(rate->index < IWL_RATE_MCS_0_INDEX &&
+               WARN_ON_ONCE(rate->index < IWL_RATE_MCS_0_INDEX ||
                             rate->index > IWL_RATE_MCS_9_INDEX);
 
                rate->index = rs_ht_to_legacy[rate->index];
@@ -1169,12 +1169,12 @@ static void rs_set_stay_in_table(struct iwl_mvm *mvm, u8 is_legacy,
        lq_sta->visited_columns = 0;
 }
 
-static s32 *rs_get_expected_tpt_table(struct iwl_lq_sta *lq_sta,
+static const u16 *rs_get_expected_tpt_table(struct iwl_lq_sta *lq_sta,
                                      const struct rs_tx_column *column,
                                      u32 bw)
 {
        /* Used to choose among HT tables */
-       s32 (*ht_tbl_pointer)[IWL_RATE_COUNT];
+       const u16 (*ht_tbl_pointer)[IWL_RATE_COUNT];
 
        if (WARN_ON_ONCE(column->mode != RS_LEGACY &&
                         column->mode != RS_SISO &&
@@ -1262,9 +1262,8 @@ static s32 rs_get_best_rate(struct iwl_mvm *mvm,
            &(lq_sta->lq_info[lq_sta->active_tbl]);
        s32 active_sr = active_tbl->win[index].success_ratio;
        s32 active_tpt = active_tbl->expected_tpt[index];
-
        /* expected "search" throughput */
-       s32 *tpt_tbl = tbl->expected_tpt;
+       const u16 *tpt_tbl = tbl->expected_tpt;
 
        s32 new_rate, high, low, start_hi;
        u16 high_low;
@@ -1479,7 +1478,7 @@ static enum rs_column rs_get_next_column(struct iwl_mvm *mvm,
        const struct rs_tx_column *next_col;
        allow_column_func_t allow_func;
        u8 valid_ants = iwl_fw_valid_tx_ant(mvm->fw);
-       s32 *expected_tpt_tbl;
+       const u16 *expected_tpt_tbl;
        s32 tpt, max_expected_tpt;
 
        for (i = 0; i < MAX_NEXT_COLUMNS; i++) {
@@ -2815,8 +2814,8 @@ static void rs_rate_init_stub(void *mvm_r,
                              struct ieee80211_sta *sta, void *mvm_sta)
 {
 }
-static struct rate_control_ops rs_mvm_ops = {
-       .module = NULL,
+
+static const struct rate_control_ops rs_mvm_ops = {
        .name = RS_NAME,
        .tx_status = rs_tx_status,
        .get_rate = rs_get_rate,
index 7bc6404..3332b39 100644 (file)
@@ -277,7 +277,7 @@ enum rs_column {
 struct iwl_scale_tbl_info {
        struct rs_rate rate;
        enum rs_column column;
-       s32 *expected_tpt;      /* throughput metrics; expected_tpt_G, etc. */
+       const u16 *expected_tpt;        /* throughput metrics; expected_tpt_G, etc. */
        struct iwl_rate_scale_data win[IWL_RATE_COUNT]; /* rate histories */
 };
 
index a85b60f..fa3c139 100644 (file)
@@ -129,22 +129,16 @@ static void iwl_mvm_calc_rssi(struct iwl_mvm *mvm,
                              struct ieee80211_rx_status *rx_status)
 {
        int rssi_a, rssi_b, rssi_a_dbm, rssi_b_dbm, max_rssi_dbm;
-       int rssi_all_band_a, rssi_all_band_b;
-       u32 agc_a, agc_b, max_agc;
+       u32 agc_a, agc_b;
        u32 val;
 
        val = le32_to_cpu(phy_info->non_cfg_phy[IWL_RX_INFO_AGC_IDX]);
        agc_a = (val & IWL_OFDM_AGC_A_MSK) >> IWL_OFDM_AGC_A_POS;
        agc_b = (val & IWL_OFDM_AGC_B_MSK) >> IWL_OFDM_AGC_B_POS;
-       max_agc = max_t(u32, agc_a, agc_b);
 
        val = le32_to_cpu(phy_info->non_cfg_phy[IWL_RX_INFO_RSSI_AB_IDX]);
        rssi_a = (val & IWL_OFDM_RSSI_INBAND_A_MSK) >> IWL_OFDM_RSSI_A_POS;
        rssi_b = (val & IWL_OFDM_RSSI_INBAND_B_MSK) >> IWL_OFDM_RSSI_B_POS;
-       rssi_all_band_a = (val & IWL_OFDM_RSSI_ALLBAND_A_MSK) >>
-                               IWL_OFDM_RSSI_ALLBAND_A_POS;
-       rssi_all_band_b = (val & IWL_OFDM_RSSI_ALLBAND_B_MSK) >>
-                               IWL_OFDM_RSSI_ALLBAND_B_POS;
 
        /*
         * dBm = rssi dB - agc dB - constant.
@@ -364,10 +358,10 @@ int iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
                rx_status.flag |= RX_FLAG_40MHZ;
                break;
        case RATE_MCS_CHAN_WIDTH_80:
-               rx_status.flag |= RX_FLAG_80MHZ;
+               rx_status.vht_flag |= RX_VHT_FLAG_80MHZ;
                break;
        case RATE_MCS_CHAN_WIDTH_160:
-               rx_status.flag |= RX_FLAG_160MHZ;
+               rx_status.vht_flag |= RX_VHT_FLAG_160MHZ;
                break;
        }
        if (rate_n_flags & RATE_MCS_SGI_MSK)
index 742afc4..eba55cc 100644 (file)
@@ -408,6 +408,8 @@ int iwl_mvm_rx_scan_complete(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
        mvm->scan_status = IWL_MVM_SCAN_NONE;
        ieee80211_scan_completed(mvm->hw, notif->status != SCAN_COMP_STATUS_OK);
 
+       iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
+
        return 0;
 }
 
@@ -476,6 +478,7 @@ void iwl_mvm_cancel_scan(struct iwl_mvm *mvm)
 
        if (iwl_mvm_is_radio_killed(mvm)) {
                ieee80211_scan_completed(mvm->hw, true);
+               iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
                mvm->scan_status = IWL_MVM_SCAN_NONE;
                return;
        }
@@ -488,7 +491,7 @@ void iwl_mvm_cancel_scan(struct iwl_mvm *mvm)
        ret = iwl_mvm_send_cmd_pdu(mvm, SCAN_ABORT_CMD, CMD_SYNC, 0, NULL);
        if (ret) {
                IWL_ERR(mvm, "Couldn't send SCAN_ABORT_CMD: %d\n", ret);
-               /* mac80211's state will be cleaned in the fw_restart flow */
+               /* mac80211's state will be cleaned in the nic_restart flow */
                goto out_remove_notif;
        }
 
@@ -509,11 +512,16 @@ int iwl_mvm_rx_scan_offload_complete_notif(struct iwl_mvm *mvm,
        struct iwl_rx_packet *pkt = rxb_addr(rxb);
        struct iwl_scan_offload_complete *scan_notif = (void *)pkt->data;
 
+       /* scan status must be locked for proper checking */
+       lockdep_assert_held(&mvm->mutex);
+
        IWL_DEBUG_SCAN(mvm, "Scheduled scan completed, status %s\n",
                       scan_notif->status == IWL_SCAN_OFFLOAD_COMPLETED ?
                       "completed" : "aborted");
 
-       mvm->scan_status = IWL_MVM_SCAN_NONE;
+       /* might already be something else again, don't reset if so */
+       if (mvm->scan_status == IWL_MVM_SCAN_SCHED)
+               mvm->scan_status = IWL_MVM_SCAN_NONE;
        ieee80211_sched_scan_stopped(mvm->hw);
 
        return 0;
@@ -596,6 +604,9 @@ static void iwl_scan_offload_build_ssid(struct cfg80211_sched_scan_request *req,
         * config match list.
         */
        for (i = 0; i < req->n_match_sets && i < PROBE_OPTION_MAX; i++) {
+               /* 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,
index 3397f59..2677d1c 100644 (file)
 #include "sta.h"
 #include "rs.h"
 
-static void iwl_mvm_add_sta_cmd_v6_to_v5(struct iwl_mvm_add_sta_cmd_v6 *cmd_v6,
+static void iwl_mvm_add_sta_cmd_v7_to_v5(struct iwl_mvm_add_sta_cmd_v7 *cmd_v7,
                                         struct iwl_mvm_add_sta_cmd_v5 *cmd_v5)
 {
        memset(cmd_v5, 0, sizeof(*cmd_v5));
 
-       cmd_v5->add_modify = cmd_v6->add_modify;
-       cmd_v5->tid_disable_tx = cmd_v6->tid_disable_tx;
-       cmd_v5->mac_id_n_color = cmd_v6->mac_id_n_color;
-       memcpy(cmd_v5->addr, cmd_v6->addr, ETH_ALEN);
-       cmd_v5->sta_id = cmd_v6->sta_id;
-       cmd_v5->modify_mask = cmd_v6->modify_mask;
-       cmd_v5->station_flags = cmd_v6->station_flags;
-       cmd_v5->station_flags_msk = cmd_v6->station_flags_msk;
-       cmd_v5->add_immediate_ba_tid = cmd_v6->add_immediate_ba_tid;
-       cmd_v5->remove_immediate_ba_tid = cmd_v6->remove_immediate_ba_tid;
-       cmd_v5->add_immediate_ba_ssn = cmd_v6->add_immediate_ba_ssn;
-       cmd_v5->sleep_tx_count = cmd_v6->sleep_tx_count;
-       cmd_v5->sleep_state_flags = cmd_v6->sleep_state_flags;
-       cmd_v5->assoc_id = cmd_v6->assoc_id;
-       cmd_v5->beamform_flags = cmd_v6->beamform_flags;
-       cmd_v5->tfd_queue_msk = cmd_v6->tfd_queue_msk;
+       cmd_v5->add_modify = cmd_v7->add_modify;
+       cmd_v5->tid_disable_tx = cmd_v7->tid_disable_tx;
+       cmd_v5->mac_id_n_color = cmd_v7->mac_id_n_color;
+       memcpy(cmd_v5->addr, cmd_v7->addr, ETH_ALEN);
+       cmd_v5->sta_id = cmd_v7->sta_id;
+       cmd_v5->modify_mask = cmd_v7->modify_mask;
+       cmd_v5->station_flags = cmd_v7->station_flags;
+       cmd_v5->station_flags_msk = cmd_v7->station_flags_msk;
+       cmd_v5->add_immediate_ba_tid = cmd_v7->add_immediate_ba_tid;
+       cmd_v5->remove_immediate_ba_tid = cmd_v7->remove_immediate_ba_tid;
+       cmd_v5->add_immediate_ba_ssn = cmd_v7->add_immediate_ba_ssn;
+       cmd_v5->sleep_tx_count = cmd_v7->sleep_tx_count;
+       cmd_v5->sleep_state_flags = cmd_v7->sleep_state_flags;
+       cmd_v5->assoc_id = cmd_v7->assoc_id;
+       cmd_v5->beamform_flags = cmd_v7->beamform_flags;
+       cmd_v5->tfd_queue_msk = cmd_v7->tfd_queue_msk;
 }
 
 static void
@@ -110,7 +110,7 @@ iwl_mvm_add_sta_key_to_add_sta_cmd_v5(struct iwl_mvm_add_sta_key_cmd *key_cmd,
 }
 
 static int iwl_mvm_send_add_sta_cmd_status(struct iwl_mvm *mvm,
-                                          struct iwl_mvm_add_sta_cmd_v6 *cmd,
+                                          struct iwl_mvm_add_sta_cmd_v7 *cmd,
                                           int *status)
 {
        struct iwl_mvm_add_sta_cmd_v5 cmd_v5;
@@ -119,14 +119,14 @@ static int iwl_mvm_send_add_sta_cmd_status(struct iwl_mvm *mvm,
                return iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(*cmd),
                                                   cmd, status);
 
-       iwl_mvm_add_sta_cmd_v6_to_v5(cmd, &cmd_v5);
+       iwl_mvm_add_sta_cmd_v7_to_v5(cmd, &cmd_v5);
 
        return iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd_v5),
                                           &cmd_v5, status);
 }
 
 static int iwl_mvm_send_add_sta_cmd(struct iwl_mvm *mvm, u32 flags,
-                                   struct iwl_mvm_add_sta_cmd_v6 *cmd)
+                                   struct iwl_mvm_add_sta_cmd_v7 *cmd)
 {
        struct iwl_mvm_add_sta_cmd_v5 cmd_v5;
 
@@ -134,7 +134,7 @@ static int iwl_mvm_send_add_sta_cmd(struct iwl_mvm *mvm, u32 flags,
                return iwl_mvm_send_cmd_pdu(mvm, ADD_STA, flags,
                                            sizeof(*cmd), cmd);
 
-       iwl_mvm_add_sta_cmd_v6_to_v5(cmd, &cmd_v5);
+       iwl_mvm_add_sta_cmd_v7_to_v5(cmd, &cmd_v5);
 
        return iwl_mvm_send_cmd_pdu(mvm, ADD_STA, flags, sizeof(cmd_v5),
                                    &cmd_v5);
@@ -175,19 +175,30 @@ static int iwl_mvm_send_add_sta_key_cmd(struct iwl_mvm *mvm,
                                    &sta_cmd);
 }
 
-static int iwl_mvm_find_free_sta_id(struct iwl_mvm *mvm)
+static int iwl_mvm_find_free_sta_id(struct iwl_mvm *mvm,
+                                   enum nl80211_iftype iftype)
 {
        int sta_id;
+       u32 reserved_ids = 0;
 
+       BUILD_BUG_ON(IWL_MVM_STATION_COUNT > 32);
        WARN_ON_ONCE(test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status));
 
        lockdep_assert_held(&mvm->mutex);
 
+       /* d0i3/d3 assumes the AP's sta_id (of sta vif) is 0. reserve it. */
+       if (iftype != NL80211_IFTYPE_STATION)
+               reserved_ids = BIT(0);
+
        /* Don't take rcu_read_lock() since we are protected by mvm->mutex */
-       for (sta_id = 0; sta_id < IWL_MVM_STATION_COUNT; sta_id++)
+       for (sta_id = 0; sta_id < IWL_MVM_STATION_COUNT; sta_id++) {
+               if (BIT(sta_id) & reserved_ids)
+                       continue;
+
                if (!rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
                                               lockdep_is_held(&mvm->mutex)))
                        return sta_id;
+       }
        return IWL_MVM_STATION_COUNT;
 }
 
@@ -196,7 +207,7 @@ int iwl_mvm_sta_send_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
                           bool update)
 {
        struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
-       struct iwl_mvm_add_sta_cmd_v6 add_sta_cmd;
+       struct iwl_mvm_add_sta_cmd_v7 add_sta_cmd;
        int ret;
        u32 status;
        u32 agg_size = 0, mpdu_dens = 0;
@@ -312,7 +323,8 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm,
        lockdep_assert_held(&mvm->mutex);
 
        if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
-               sta_id = iwl_mvm_find_free_sta_id(mvm);
+               sta_id = iwl_mvm_find_free_sta_id(mvm,
+                                                 ieee80211_vif_type_p2p(vif));
        else
                sta_id = mvm_sta->sta_id;
 
@@ -368,7 +380,7 @@ int iwl_mvm_update_sta(struct iwl_mvm *mvm,
 int iwl_mvm_drain_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
                      bool drain)
 {
-       struct iwl_mvm_add_sta_cmd_v6 cmd = {};
+       struct iwl_mvm_add_sta_cmd_v7 cmd = {};
        int ret;
        u32 status;
 
@@ -522,6 +534,10 @@ int iwl_mvm_rm_sta(struct iwl_mvm *mvm,
 
                /* unassoc - go ahead - remove the AP STA now */
                mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT;
+
+               /* clear d0i3_ap_sta_id if no longer relevant */
+               if (mvm->d0i3_ap_sta_id == mvm_sta->sta_id)
+                       mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT;
        }
 
        /*
@@ -560,10 +576,10 @@ int iwl_mvm_rm_sta_id(struct iwl_mvm *mvm,
 }
 
 int iwl_mvm_allocate_int_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *sta,
-                            u32 qmask)
+                            u32 qmask, enum nl80211_iftype iftype)
 {
        if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) {
-               sta->sta_id = iwl_mvm_find_free_sta_id(mvm);
+               sta->sta_id = iwl_mvm_find_free_sta_id(mvm, iftype);
                if (WARN_ON_ONCE(sta->sta_id == IWL_MVM_STATION_COUNT))
                        return -ENOSPC;
        }
@@ -587,13 +603,13 @@ static int iwl_mvm_add_int_sta_common(struct iwl_mvm *mvm,
                                      const u8 *addr,
                                      u16 mac_id, u16 color)
 {
-       struct iwl_mvm_add_sta_cmd_v6 cmd;
+       struct iwl_mvm_add_sta_cmd_v7 cmd;
        int ret;
        u32 status;
 
        lockdep_assert_held(&mvm->mutex);
 
-       memset(&cmd, 0, sizeof(struct iwl_mvm_add_sta_cmd_v6));
+       memset(&cmd, 0, sizeof(struct iwl_mvm_add_sta_cmd_v7));
        cmd.sta_id = sta->sta_id;
        cmd.mac_id_n_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mac_id,
                                                             color));
@@ -627,7 +643,8 @@ int iwl_mvm_add_aux_sta(struct iwl_mvm *mvm)
        lockdep_assert_held(&mvm->mutex);
 
        /* Add the aux station, but without any queues */
-       ret = iwl_mvm_allocate_int_sta(mvm, &mvm->aux_sta, 0);
+       ret = iwl_mvm_allocate_int_sta(mvm, &mvm->aux_sta, 0,
+                                      NL80211_IFTYPE_UNSPECIFIED);
        if (ret)
                return ret;
 
@@ -699,7 +716,8 @@ int iwl_mvm_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
        lockdep_assert_held(&mvm->mutex);
 
        qmask = iwl_mvm_mac_get_queues_mask(mvm, vif);
-       ret = iwl_mvm_allocate_int_sta(mvm, bsta, qmask);
+       ret = iwl_mvm_allocate_int_sta(mvm, bsta, qmask,
+                                      ieee80211_vif_type_p2p(vif));
        if (ret)
                return ret;
 
@@ -735,7 +753,7 @@ int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
                       int tid, u16 ssn, bool start)
 {
        struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
-       struct iwl_mvm_add_sta_cmd_v6 cmd = {};
+       struct iwl_mvm_add_sta_cmd_v7 cmd = {};
        int ret;
        u32 status;
 
@@ -794,7 +812,7 @@ static int iwl_mvm_sta_tx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
                              int tid, u8 queue, bool start)
 {
        struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
-       struct iwl_mvm_add_sta_cmd_v6 cmd = {};
+       struct iwl_mvm_add_sta_cmd_v7 cmd = {};
        int ret;
        u32 status;
 
@@ -833,7 +851,7 @@ static int iwl_mvm_sta_tx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
        return ret;
 }
 
-static const u8 tid_to_ac[] = {
+static const u8 tid_to_mac80211_ac[] = {
        IEEE80211_AC_BE,
        IEEE80211_AC_BK,
        IEEE80211_AC_BK,
@@ -844,6 +862,17 @@ static const u8 tid_to_ac[] = {
        IEEE80211_AC_VO,
 };
 
+static const u8 tid_to_ucode_ac[] = {
+       AC_BE,
+       AC_BK,
+       AC_BK,
+       AC_BE,
+       AC_VI,
+       AC_VI,
+       AC_VO,
+       AC_VO,
+};
+
 int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                             struct ieee80211_sta *sta, u16 tid, u16 *ssn)
 {
@@ -874,7 +903,7 @@ int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
        }
 
        /* the new tx queue is still connected to the same mac80211 queue */
-       mvm->queue_to_mac80211[txq_id] = vif->hw_queue[tid_to_ac[tid]];
+       mvm->queue_to_mac80211[txq_id] = vif->hw_queue[tid_to_mac80211_ac[tid]];
 
        spin_lock_bh(&mvmsta->lock);
        tid_data = &mvmsta->tid_data[tid];
@@ -916,7 +945,7 @@ int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
        tid_data->ssn = 0xffff;
        spin_unlock_bh(&mvmsta->lock);
 
-       fifo = iwl_mvm_ac_to_tx_fifo[tid_to_ac[tid]];
+       fifo = iwl_mvm_ac_to_tx_fifo[tid_to_mac80211_ac[tid]];
 
        ret = iwl_mvm_sta_tx_agg(mvm, sta, tid, queue, true);
        if (ret)
@@ -1411,7 +1440,7 @@ void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm,
                                struct ieee80211_sta *sta)
 {
        struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
-       struct iwl_mvm_add_sta_cmd_v6 cmd = {
+       struct iwl_mvm_add_sta_cmd_v7 cmd = {
                .add_modify = STA_MODE_MODIFY,
                .sta_id = mvmsta->sta_id,
                .station_flags_msk = cpu_to_le32(STA_FLG_PS),
@@ -1427,28 +1456,102 @@ void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm,
 void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm,
                                       struct ieee80211_sta *sta,
                                       enum ieee80211_frame_release_type reason,
-                                      u16 cnt)
+                                      u16 cnt, u16 tids, bool more_data,
+                                      bool agg)
 {
-       u16 sleep_state_flags =
-               (reason == IEEE80211_FRAME_RELEASE_UAPSD) ?
-                       STA_SLEEP_STATE_UAPSD : STA_SLEEP_STATE_PS_POLL;
        struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
-       struct iwl_mvm_add_sta_cmd_v6 cmd = {
+       struct iwl_mvm_add_sta_cmd_v7 cmd = {
                .add_modify = STA_MODE_MODIFY,
                .sta_id = mvmsta->sta_id,
                .modify_mask = STA_MODIFY_SLEEPING_STA_TX_COUNT,
                .sleep_tx_count = cpu_to_le16(cnt),
                .mac_id_n_color = cpu_to_le32(mvmsta->mac_id_n_color),
-               /*
-                * Same modify mask for sleep_tx_count and sleep_state_flags so
-                * we must set the sleep_state_flags too.
-                */
-               .sleep_state_flags = cpu_to_le16(sleep_state_flags),
        };
-       int ret;
+       int tid, ret;
+       unsigned long _tids = tids;
+
+       /* convert TIDs to ACs - we don't support TSPEC so that's OK
+        * Note that this field is reserved and unused by firmware not
+        * supporting GO uAPSD, so it's safe to always do this.
+        */
+       for_each_set_bit(tid, &_tids, IWL_MAX_TID_COUNT)
+               cmd.awake_acs |= BIT(tid_to_ucode_ac[tid]);
+
+       /* If we're releasing frames from aggregation queues then check if the
+        * all queues combined that we're releasing frames from have
+        *  - more frames than the service period, in which case more_data
+        *    needs to be set
+        *  - fewer than 'cnt' frames, in which case we need to adjust the
+        *    firmware command (but do that unconditionally)
+        */
+       if (agg) {
+               int remaining = cnt;
+
+               spin_lock_bh(&mvmsta->lock);
+               for_each_set_bit(tid, &_tids, IWL_MAX_TID_COUNT) {
+                       struct iwl_mvm_tid_data *tid_data;
+                       u16 n_queued;
+
+                       tid_data = &mvmsta->tid_data[tid];
+                       if (WARN(tid_data->state != IWL_AGG_ON &&
+                                tid_data->state != IWL_EMPTYING_HW_QUEUE_DELBA,
+                                "TID %d state is %d\n",
+                                tid, tid_data->state)) {
+                               spin_unlock_bh(&mvmsta->lock);
+                               ieee80211_sta_eosp(sta);
+                               return;
+                       }
+
+                       n_queued = iwl_mvm_tid_queued(tid_data);
+                       if (n_queued > remaining) {
+                               more_data = true;
+                               remaining = 0;
+                               break;
+                       }
+                       remaining -= n_queued;
+               }
+               spin_unlock_bh(&mvmsta->lock);
+
+               cmd.sleep_tx_count = cpu_to_le16(cnt - remaining);
+               if (WARN_ON(cnt - remaining == 0)) {
+                       ieee80211_sta_eosp(sta);
+                       return;
+               }
+       }
+
+       /* Note: this is ignored by firmware not supporting GO uAPSD */
+       if (more_data)
+               cmd.sleep_state_flags |= cpu_to_le16(STA_SLEEP_STATE_MOREDATA);
+
+       if (reason == IEEE80211_FRAME_RELEASE_PSPOLL) {
+               mvmsta->next_status_eosp = true;
+               cmd.sleep_state_flags |= cpu_to_le16(STA_SLEEP_STATE_PS_POLL);
+       } else {
+               cmd.sleep_state_flags |= cpu_to_le16(STA_SLEEP_STATE_UAPSD);
+       }
 
-       /* TODO: somehow the fw doesn't seem to take PS_POLL into account */
        ret = iwl_mvm_send_add_sta_cmd(mvm, CMD_ASYNC, &cmd);
        if (ret)
                IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret);
 }
+
+int iwl_mvm_rx_eosp_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_mvm_eosp_notification *notif = (void *)pkt->data;
+       struct ieee80211_sta *sta;
+       u32 sta_id = le32_to_cpu(notif->sta_id);
+
+       if (WARN_ON_ONCE(sta_id >= IWL_MVM_STATION_COUNT))
+               return 0;
+
+       rcu_read_lock();
+       sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);
+       if (!IS_ERR_OR_NULL(sta))
+               ieee80211_sta_eosp(sta);
+       rcu_read_unlock();
+
+       return 0;
+}
index 4968d02..2ed84c4 100644 (file)
@@ -195,24 +195,33 @@ struct iwl_mvm;
 /**
  * DOC: AP mode - PS
  *
- * When a station is asleep, the fw will set it as "asleep". All the
- * non-aggregation frames to that station will be dropped by the fw
- * (%TX_STATUS_FAIL_DEST_PS failure code).
+ * When a station is asleep, the fw will set it as "asleep". All frames on
+ * shared queues (i.e. non-aggregation queues) to that station will be dropped
+ * by the fw (%TX_STATUS_FAIL_DEST_PS failure code).
+ *
  * AMPDUs are in a separate queue that is stopped by the fw. We just need to
- * let mac80211 know how many frames we have in these queues so that it can
+ * let mac80211 know when there are frames in these queues so that it can
  * properly handle trigger frames.
- * When the a trigger frame is received, mac80211 tells the driver to send
- * frames from the AMPDU queues or AC queue depending on which queue are
- * delivery-enabled and what TID has frames to transmit (Note that mac80211 has
- * all the knowledege since all the non-agg frames are buffered / filtered, and
- * the driver tells mac80211 about agg frames). The driver needs to tell the fw
- * to let frames out even if the station is asleep. This is done by
- * %iwl_mvm_sta_modify_sleep_tx_count.
- * When we receive a frame from that station with PM bit unset, the
- * driver needs to let the fw know that this station isn't alseep any more.
- * This is done by %iwl_mvm_sta_modify_ps_wake.
- *
- * TODO - EOSP handling
+ *
+ * When a trigger frame is received, mac80211 tells the driver to send frames
+ * from the AMPDU queues or sends frames to non-aggregation queues itself,
+ * depending on which ACs are delivery-enabled and what TID has frames to
+ * transmit. Note that mac80211 has all the knowledege since all the non-agg
+ * frames are buffered / filtered, and the driver tells mac80211 about agg
+ * frames). The driver needs to tell the fw to let frames out even if the
+ * station is asleep. This is done by %iwl_mvm_sta_modify_sleep_tx_count.
+ *
+ * When we receive a frame from that station with PM bit unset, the driver
+ * needs to let the fw know that this station isn't asleep any more. This is
+ * done by %iwl_mvm_sta_modify_ps_wake in response to mac80211 signalling the
+ * station's wakeup.
+ *
+ * For a GO, the Service Period might be cut short due to an absence period
+ * of the GO. In this (and all other cases) the firmware notifies us with the
+ * EOSP_NOTIFICATION, and we notify mac80211 of that. Further frames that we
+ * already sent to the device will be rejected again.
+ *
+ * See also "AP support for powersaving clients" in mac80211.h.
  */
 
 /**
@@ -261,6 +270,12 @@ struct iwl_mvm_tid_data {
        u16 ssn;
 };
 
+static inline u16 iwl_mvm_tid_queued(struct iwl_mvm_tid_data *tid_data)
+{
+       return ieee80211_sn_sub(IEEE80211_SEQ_TO_SN(tid_data->seq_number),
+                               tid_data->next_reclaimed);
+}
+
 /**
  * struct iwl_mvm_sta - representation of a station in the driver
  * @sta_id: the index of the station in the fw (will be replaced by id_n_color)
@@ -269,7 +284,11 @@ struct iwl_mvm_tid_data {
  * @tid_disable_agg: bitmap: if bit(tid) is set, the fw won't send ampdus for
  *     tid.
  * @max_agg_bufsize: the maximal size of the AGG buffer for this station
+ * @bt_reduced_txpower_dbg: debug mode in which %bt_reduced_txpower is forced
+ *     by debugfs.
  * @bt_reduced_txpower: is reduced tx power enabled for this station
+ * @next_status_eosp: the next reclaimed packet is a PS-Poll response and
+ *     we need to signal the EOSP
  * @lock: lock to protect the whole struct. Since %tid_data is access from Tx
  * and from Tx response flow, it needs a spinlock.
  * @tid_data: per tid data. Look at %iwl_mvm_tid_data.
@@ -287,7 +306,9 @@ struct iwl_mvm_sta {
        u32 mac_id_n_color;
        u16 tid_disable_agg;
        u8 max_agg_bufsize;
+       bool bt_reduced_txpower_dbg;
        bool bt_reduced_txpower;
+       bool next_status_eosp;
        spinlock_t lock;
        struct iwl_mvm_tid_data tid_data[IWL_MAX_TID_COUNT];
        struct iwl_lq_sta lq_sta;
@@ -345,6 +366,10 @@ void iwl_mvm_update_tkip_key(struct iwl_mvm *mvm,
                             struct ieee80211_sta *sta, u32 iv32,
                             u16 *phase1key);
 
+int iwl_mvm_rx_eosp_notif(struct iwl_mvm *mvm,
+                         struct iwl_rx_cmd_buffer *rxb,
+                         struct iwl_device_cmd *cmd);
+
 /* AMPDU */
 int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
                       int tid, u16 ssn, bool start);
@@ -359,7 +384,7 @@ int iwl_mvm_sta_tx_agg_flush(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
 
 int iwl_mvm_add_aux_sta(struct iwl_mvm *mvm);
 int iwl_mvm_allocate_int_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *sta,
-                            u32 qmask);
+                            u32 qmask, enum nl80211_iftype iftype);
 void iwl_mvm_dealloc_int_sta(struct iwl_mvm *mvm,
                             struct iwl_mvm_int_sta *sta);
 int iwl_mvm_send_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
@@ -375,7 +400,8 @@ void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm,
 void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm,
                                       struct ieee80211_sta *sta,
                                       enum ieee80211_frame_release_type reason,
-                                      u16 cnt);
+                                      u16 cnt, u16 tids, bool more_data,
+                                      bool agg);
 int iwl_mvm_drain_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
                      bool drain);
 
index b4c2aba..e145dd4 100644 (file)
@@ -126,6 +126,7 @@ static void iwl_mvm_roc_finished(struct iwl_mvm *mvm)
         * in iwl_mvm_te_handle_notif).
         */
        clear_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status);
+       iwl_mvm_unref(mvm, IWL_MVM_REF_ROC);
 
        /*
         * Of course, our status bit is just as racy as mac80211, so in
@@ -210,6 +211,7 @@ static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm,
 
                if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) {
                        set_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status);
+                       iwl_mvm_ref(mvm, IWL_MVM_REF_ROC);
                        ieee80211_ready_on_channel(mvm->hw);
                }
        } else {
index 3afa6b6..7a99fa3 100644 (file)
@@ -403,7 +403,7 @@ static void iwl_mvm_tt_tx_protection(struct iwl_mvm *mvm, bool enable)
        }
 }
 
-static void iwl_mvm_tt_tx_backoff(struct iwl_mvm *mvm, u32 backoff)
+void iwl_mvm_tt_tx_backoff(struct iwl_mvm *mvm, u32 backoff)
 {
        struct iwl_host_cmd cmd = {
                .id = REPLY_THERMAL_MNG_BACKOFF,
@@ -412,6 +412,8 @@ static void iwl_mvm_tt_tx_backoff(struct iwl_mvm *mvm, u32 backoff)
                .flags = CMD_SYNC,
        };
 
+       backoff = max(backoff, mvm->thermal_throttle.min_backoff);
+
        if (iwl_mvm_send_cmd(mvm, &cmd) == 0) {
                IWL_DEBUG_TEMP(mvm, "Set Thermal Tx backoff to: %u\n",
                               backoff);
@@ -534,7 +536,7 @@ static const struct iwl_tt_params iwl7000_high_temp_tt_params = {
        .support_tx_backoff = true,
 };
 
-void iwl_mvm_tt_initialize(struct iwl_mvm *mvm)
+void iwl_mvm_tt_initialize(struct iwl_mvm *mvm, u32 min_backoff)
 {
        struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle;
 
@@ -546,6 +548,7 @@ void iwl_mvm_tt_initialize(struct iwl_mvm *mvm)
                tt->params = &iwl7000_tt_params;
 
        tt->throttle = false;
+       tt->min_backoff = min_backoff;
        INIT_DELAYED_WORK(&tt->ct_kill_exit, check_exit_ctkill);
 }
 
index 4df12fa..74d60bf 100644 (file)
@@ -377,6 +377,13 @@ int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb,
        tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload;
        /* From now on, we cannot access info->control */
 
+       /*
+        * we handle that entirely ourselves -- for uAPSD the firmware
+        * will always send a notification, and for PS-Poll responses
+        * we'll notify mac80211 when getting frame status
+        */
+       info->flags &= ~IEEE80211_TX_STATUS_EOSP;
+
        spin_lock(&mvmsta->lock);
 
        if (ieee80211_is_data_qos(fc) && !ieee80211_is_qos_nullfunc(fc)) {
@@ -437,6 +444,17 @@ static void iwl_mvm_check_ratid_empty(struct iwl_mvm *mvm,
 
        lockdep_assert_held(&mvmsta->lock);
 
+       if ((tid_data->state == IWL_AGG_ON ||
+            tid_data->state == IWL_EMPTYING_HW_QUEUE_DELBA) &&
+           iwl_mvm_tid_queued(tid_data) == 0) {
+               /*
+                * Now that this aggregation queue is empty tell mac80211 so it
+                * knows we no longer have frames buffered for the station on
+                * this TID (for the TIM bitmap calculation.)
+                */
+               ieee80211_sta_set_buffered(sta, tid, false);
+       }
+
        if (tid_data->ssn != tid_data->next_reclaimed)
                return;
 
@@ -680,6 +698,11 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
                        iwl_mvm_check_ratid_empty(mvm, sta, tid);
                        spin_unlock_bh(&mvmsta->lock);
                }
+
+               if (mvmsta->next_status_eosp) {
+                       mvmsta->next_status_eosp = false;
+                       ieee80211_sta_eosp(sta);
+               }
        } else {
                mvmsta = NULL;
        }
index 86989df..1493f79 100644 (file)
@@ -376,9 +376,67 @@ struct iwl_error_event_table {
        u32 flow_handler;       /* FH read/write pointers, RX credit */
 } __packed;
 
+/*
+ * UMAC error struct - relevant starting from family 8000 chip.
+ * Note: This structure is read from the device with IO accesses,
+ * and the reading already does the endian conversion. As it is
+ * read with u32-sized accesses, any members with a different size
+ * need to be ordered correctly though!
+ */
+struct iwl_umac_error_event_table {
+       u32 valid;              /* (nonzero) valid, (0) log is empty */
+       u32 error_id;           /* type of error */
+       u32 pc;                 /* program counter */
+       u32 blink1;             /* branch link */
+       u32 blink2;             /* branch link */
+       u32 ilink1;             /* interrupt link */
+       u32 ilink2;             /* interrupt link */
+       u32 data1;              /* error-specific data */
+       u32 data2;              /* error-specific data */
+       u32 line;               /* source code line of error */
+       u32 umac_ver;           /* umac version */
+} __packed;
+
 #define ERROR_START_OFFSET  (1 * sizeof(u32))
 #define ERROR_ELEM_SIZE     (7 * sizeof(u32))
 
+static void iwl_mvm_dump_umac_error_log(struct iwl_mvm *mvm)
+{
+       struct iwl_trans *trans = mvm->trans;
+       struct iwl_umac_error_event_table table;
+       u32 base;
+
+       base = mvm->umac_error_event_table;
+
+       if (base < 0x800000 || base >= 0x80C000) {
+               IWL_ERR(mvm,
+                       "Not valid error log pointer 0x%08X for %s uCode\n",
+                       base,
+                       (mvm->cur_ucode == IWL_UCODE_INIT)
+                                       ? "Init" : "RT");
+               return;
+       }
+
+       iwl_trans_read_mem_bytes(trans, base, &table, sizeof(table));
+
+       if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) {
+               IWL_ERR(trans, "Start IWL Error Log Dump:\n");
+               IWL_ERR(trans, "Status: 0x%08lX, count: %d\n",
+                       mvm->status, table.valid);
+       }
+
+       IWL_ERR(mvm, "0x%08X | %-28s\n", table.error_id,
+               desc_lookup(table.error_id));
+       IWL_ERR(mvm, "0x%08X | umac uPc\n", table.pc);
+       IWL_ERR(mvm, "0x%08X | umac branchlink1\n", table.blink1);
+       IWL_ERR(mvm, "0x%08X | umac branchlink2\n", table.blink2);
+       IWL_ERR(mvm, "0x%08X | umac interruptlink1\n", table.ilink1);
+       IWL_ERR(mvm, "0x%08X | umac interruptlink2\n", table.ilink2);
+       IWL_ERR(mvm, "0x%08X | umac data1\n", table.data1);
+       IWL_ERR(mvm, "0x%08X | umac data2\n", table.data2);
+       IWL_ERR(mvm, "0x%08X | umac version\n", table.umac_ver);
+}
+
 void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm)
 {
        struct iwl_trans *trans = mvm->trans;
@@ -394,7 +452,7 @@ void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm)
                        base = mvm->fw->inst_errlog_ptr;
        }
 
-       if (base < 0x800000 || base >= 0x80C000) {
+       if (base < 0x800000) {
                IWL_ERR(mvm,
                        "Not valid error log pointer 0x%08X for %s uCode\n",
                        base,
@@ -453,13 +511,17 @@ void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm)
        IWL_ERR(mvm, "0x%08X | lmpm_pmg_sel\n", table.lmpm_pmg_sel);
        IWL_ERR(mvm, "0x%08X | timestamp\n", table.u_timestamp);
        IWL_ERR(mvm, "0x%08X | flow_handler\n", table.flow_handler);
+
+       if (mvm->support_umac_log)
+               iwl_mvm_dump_umac_error_log(mvm);
 }
 
 void iwl_mvm_dump_sram(struct iwl_mvm *mvm)
 {
        const struct fw_img *img;
        int ofs, len = 0;
-       u8 *buf;
+       int i;
+       __le32 *buf;
 
        if (!mvm->ucode_loaded)
                return;
@@ -473,7 +535,12 @@ void iwl_mvm_dump_sram(struct iwl_mvm *mvm)
                return;
 
        iwl_trans_read_mem_bytes(mvm->trans, ofs, buf, len);
-       iwl_print_hex_error(mvm->trans, buf, len);
+       len = len >> 2;
+       for (i = 0; i < len; i++) {
+               IWL_ERR(mvm, "0x%08X\n", le32_to_cpu(buf[i]));
+               /* Add a small delay to let syslog catch up */
+               udelay(10);
+       }
 
        kfree(buf);
 }
@@ -516,7 +583,7 @@ void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                         enum ieee80211_smps_mode smps_request)
 {
        struct iwl_mvm_vif *mvmvif;
-       enum ieee80211_smps_mode smps_mode = IEEE80211_SMPS_AUTOMATIC;
+       enum ieee80211_smps_mode smps_mode;
        int i;
 
        lockdep_assert_held(&mvm->mutex);
@@ -525,6 +592,11 @@ void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
        if (num_of_ant(iwl_fw_valid_rx_ant(mvm->fw)) == 1)
                return;
 
+       if (vif->type == NL80211_IFTYPE_AP)
+               smps_mode = IEEE80211_SMPS_OFF;
+       else
+               smps_mode = IEEE80211_SMPS_AUTOMATIC;
+
        mvmvif = iwl_mvm_vif_from_mac80211(vif);
        mvmvif->smps_requests[req_type] = smps_request;
        for (i = 0; i < NUM_IWL_MVM_SMPS_REQ; i++) {
@@ -538,3 +610,22 @@ void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
 
        ieee80211_request_smps(vif, smps_mode);
 }
+
+int iwl_mvm_update_low_latency(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                              bool value)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       int res;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       mvmvif->low_latency = value;
+
+       res = iwl_mvm_update_quotas(mvm, NULL);
+       if (res)
+               return res;
+
+       iwl_mvm_bt_coex_vif_change(mvm);
+
+       return iwl_mvm_power_update_mac(mvm, vif);
+}
index f47bcbe..0f52e96 100644 (file)
@@ -66,6 +66,7 @@
 #include <linux/module.h>
 #include <linux/pci.h>
 #include <linux/pci-aspm.h>
+#include <linux/acpi.h>
 
 #include "iwl-trans.h"
 #include "iwl-drv.h"
@@ -390,12 +391,91 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = {
        {IWL_PCI_DEVICE(0x095A, 0x5590, iwl7265_2ac_cfg)},
        {IWL_PCI_DEVICE(0x095B, 0x5290, iwl7265_2ac_cfg)},
        {IWL_PCI_DEVICE(0x095A, 0x5490, iwl7265_2ac_cfg)},
+
+/* 8000 Series */
+       {IWL_PCI_DEVICE(0x24F3, 0x0010, iwl8260_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x24F4, 0x0030, iwl8260_2ac_cfg)},
 #endif /* CONFIG_IWLMVM */
 
        {0}
 };
 MODULE_DEVICE_TABLE(pci, iwl_hw_card_ids);
 
+#ifdef CONFIG_ACPI
+#define SPL_METHOD             "SPLC"
+#define SPL_DOMAINTYPE_MODULE  BIT(0)
+#define SPL_DOMAINTYPE_WIFI    BIT(1)
+#define SPL_DOMAINTYPE_WIGIG   BIT(2)
+#define SPL_DOMAINTYPE_RFEM    BIT(3)
+
+static u64 splx_get_pwr_limit(struct iwl_trans *trans, union acpi_object *splx)
+{
+       union acpi_object *limits, *domain_type, *power_limit;
+
+       if (splx->type != ACPI_TYPE_PACKAGE ||
+           splx->package.count != 2 ||
+           splx->package.elements[0].type != ACPI_TYPE_INTEGER ||
+           splx->package.elements[0].integer.value != 0) {
+               IWL_ERR(trans, "Unsupported splx structure");
+               return 0;
+       }
+
+       limits = &splx->package.elements[1];
+       if (limits->type != ACPI_TYPE_PACKAGE ||
+           limits->package.count < 2 ||
+           limits->package.elements[0].type != ACPI_TYPE_INTEGER ||
+           limits->package.elements[1].type != ACPI_TYPE_INTEGER) {
+               IWL_ERR(trans, "Invalid limits element");
+               return 0;
+       }
+
+       domain_type = &limits->package.elements[0];
+       power_limit = &limits->package.elements[1];
+       if (!(domain_type->integer.value & SPL_DOMAINTYPE_WIFI)) {
+               IWL_DEBUG_INFO(trans, "WiFi power is not limited");
+               return 0;
+       }
+
+       return power_limit->integer.value;
+}
+
+static void set_dflt_pwr_limit(struct iwl_trans *trans, struct pci_dev *pdev)
+{
+       acpi_handle pxsx_handle;
+       acpi_handle handle;
+       struct acpi_buffer splx = {ACPI_ALLOCATE_BUFFER, NULL};
+       acpi_status status;
+
+       pxsx_handle = ACPI_HANDLE(&pdev->dev);
+       if (!pxsx_handle) {
+               IWL_ERR(trans, "Could not retrieve root port ACPI handle");
+               return;
+       }
+
+       /* Get the method's handle */
+       status = acpi_get_handle(pxsx_handle, (acpi_string)SPL_METHOD, &handle);
+       if (ACPI_FAILURE(status)) {
+               IWL_DEBUG_INFO(trans, "SPL method not found");
+               return;
+       }
+
+       /* Call SPLC with no arguments */
+       status = acpi_evaluate_object(handle, NULL, NULL, &splx);
+       if (ACPI_FAILURE(status)) {
+               IWL_ERR(trans, "SPLC invocation failed (0x%x)", status);
+               return;
+       }
+
+       trans->dflt_pwr_limit = splx_get_pwr_limit(trans, splx.pointer);
+       IWL_DEBUG_INFO(trans, "Default power limit set to %lld",
+                      trans->dflt_pwr_limit);
+       kfree(splx.pointer);
+}
+
+#else /* CONFIG_ACPI */
+static void set_dflt_pwr_limit(struct iwl_trans *trans, struct pci_dev *pdev) {}
+#endif
+
 /* PCI registers */
 #define PCI_CFG_RETRY_TIMEOUT  0x041
 
@@ -420,6 +500,8 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
                goto out_free_trans;
        }
 
+       set_dflt_pwr_limit(iwl_trans, pdev);
+
        /* register transport layer debugfs here */
        ret = iwl_trans_dbgfs_register(iwl_trans, iwl_trans->dbgfs_dir);
        if (ret)
index 08c23d4..41f684d 100644 (file)
@@ -802,10 +802,9 @@ static void iwl_pcie_irq_handle_error(struct iwl_trans *trans)
 
 static u32 iwl_pcie_int_cause_non_ict(struct iwl_trans *trans)
 {
-       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
        u32 inta;
 
-       lockdep_assert_held(&trans_pcie->irq_lock);
+       lockdep_assert_held(&IWL_TRANS_GET_PCIE_TRANS(trans)->irq_lock);
 
        trace_iwlwifi_dev_irq(trans->dev);
 
index f950780..84d4712 100644 (file)
@@ -89,6 +89,7 @@ static void iwl_pcie_set_pwr(struct iwl_trans *trans, bool vaux)
 
 /* PCI registers */
 #define PCI_CFG_RETRY_TIMEOUT  0x041
+#define CPU1_CPU2_SEPARATOR_SECTION    0xFFFFCCCC
 
 static void iwl_pcie_apm_config(struct iwl_trans *trans)
 {
@@ -132,8 +133,9 @@ static int iwl_pcie_apm_init(struct iwl_trans *trans)
         */
 
        /* Disable L0S exit timer (platform NMI Work/Around) */
-       iwl_set_bit(trans, CSR_GIO_CHICKEN_BITS,
-                   CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER);
+       if (trans->cfg->device_family != IWL_DEVICE_FAMILY_8000)
+               iwl_set_bit(trans, CSR_GIO_CHICKEN_BITS,
+                           CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER);
 
        /*
         * Disable L0s without affecting L1;
@@ -203,19 +205,23 @@ static int iwl_pcie_apm_init(struct iwl_trans *trans)
        /*
         * Enable DMA clock and wait for it to stabilize.
         *
-        * Write to "CLK_EN_REG"; "1" bits enable clocks, while "0" bits
-        * do not disable clocks.  This preserves any hardware bits already
-        * set by default in "CLK_CTRL_REG" after reset.
+        * Write to "CLK_EN_REG"; "1" bits enable clocks, while "0"
+        * bits do not disable clocks.  This preserves any hardware
+        * bits already set by default in "CLK_CTRL_REG" after reset.
         */
-       iwl_write_prph(trans, APMG_CLK_EN_REG, APMG_CLK_VAL_DMA_CLK_RQT);
-       udelay(20);
+       if (trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) {
+               iwl_write_prph(trans, APMG_CLK_EN_REG,
+                              APMG_CLK_VAL_DMA_CLK_RQT);
+               udelay(20);
 
-       /* Disable L1-Active */
-       iwl_set_bits_prph(trans, APMG_PCIDEV_STT_REG,
-                         APMG_PCIDEV_STT_VAL_L1_ACT_DIS);
+               /* Disable L1-Active */
+               iwl_set_bits_prph(trans, APMG_PCIDEV_STT_REG,
+                                 APMG_PCIDEV_STT_VAL_L1_ACT_DIS);
 
-       /* Clear the interrupt in APMG if the NIC is in RFKILL */
-       iwl_write_prph(trans, APMG_RTC_INT_STT_REG, APMG_RTC_INT_STT_RFKILL);
+               /* Clear the interrupt in APMG if the NIC is in RFKILL */
+               iwl_write_prph(trans, APMG_RTC_INT_STT_REG,
+                              APMG_RTC_INT_STT_RFKILL);
+       }
 
        set_bit(STATUS_DEVICE_ENABLED, &trans->status);
 
@@ -273,7 +279,8 @@ static int iwl_pcie_nic_init(struct iwl_trans *trans)
 
        spin_unlock(&trans_pcie->irq_lock);
 
-       iwl_pcie_set_pwr(trans, false);
+       if (trans->cfg->device_family != IWL_DEVICE_FAMILY_8000)
+               iwl_pcie_set_pwr(trans, false);
 
        iwl_op_mode_nic_config(trans->op_mode);
 
@@ -435,78 +442,106 @@ static int iwl_pcie_load_section(struct iwl_trans *trans, u8 section_num,
        return ret;
 }
 
-static int iwl_pcie_secure_set(struct iwl_trans *trans, int cpu)
+static int iwl_pcie_load_cpu_secured_sections(struct iwl_trans *trans,
+                                             const struct fw_img *image,
+                                             int cpu,
+                                             int *first_ucode_section)
 {
        int shift_param;
-       u32 address;
-       int ret = 0;
+       int i, ret = 0;
+       u32 last_read_idx = 0;
 
        if (cpu == 1) {
                shift_param = 0;
-               address = CSR_SECURE_BOOT_CPU1_STATUS_ADDR;
+               *first_ucode_section = 0;
        } else {
                shift_param = 16;
-               address = CSR_SECURE_BOOT_CPU2_STATUS_ADDR;
+               (*first_ucode_section)++;
        }
 
-       /* set CPU to started */
-       iwl_trans_set_bits_mask(trans,
-                               CSR_UCODE_LOAD_STATUS_ADDR,
-                               CSR_CPU_STATUS_LOADING_STARTED << shift_param,
-                               1);
-
-       /* set last complete descriptor number */
-       iwl_trans_set_bits_mask(trans,
-                               CSR_UCODE_LOAD_STATUS_ADDR,
-                               CSR_CPU_STATUS_NUM_OF_LAST_COMPLETED
-                               << shift_param,
-                               1);
-
-       /* set last loaded block */
-       iwl_trans_set_bits_mask(trans,
-                               CSR_UCODE_LOAD_STATUS_ADDR,
-                               CSR_CPU_STATUS_NUM_OF_LAST_LOADED_BLOCK
-                               << shift_param,
-                               1);
+       for (i = *first_ucode_section; i < IWL_UCODE_SECTION_MAX; i++) {
+               last_read_idx = i;
+
+               if (!image->sec[i].data ||
+                   image->sec[i].offset == CPU1_CPU2_SEPARATOR_SECTION) {
+                       IWL_DEBUG_FW(trans,
+                                    "Break since Data not valid or Empty section, sec = %d\n",
+                                    i);
+                       break;
+               }
+
+               if (i == (*first_ucode_section) + 1)
+                       /* set CPU to started */
+                       iwl_set_bits_prph(trans,
+                                         CSR_UCODE_LOAD_STATUS_ADDR,
+                                         LMPM_CPU_HDRS_LOADING_COMPLETED
+                                         << shift_param);
 
+               ret = iwl_pcie_load_section(trans, i, &image->sec[i]);
+               if (ret)
+                       return ret;
+       }
        /* image loading complete */
-       iwl_trans_set_bits_mask(trans,
-                               CSR_UCODE_LOAD_STATUS_ADDR,
-                               CSR_CPU_STATUS_LOADING_COMPLETED
-                               << shift_param,
-                               1);
-
-       /* set FH_TCSR_0_REG  */
-       iwl_trans_set_bits_mask(trans, FH_TCSR_0_REG0, 0x00400000, 1);
-
-       /* verify image verification started  */
-       ret = iwl_poll_bit(trans, address,
-                          CSR_SECURE_BOOT_CPU_STATUS_VERF_STATUS,
-                          CSR_SECURE_BOOT_CPU_STATUS_VERF_STATUS,
-                          CSR_SECURE_TIME_OUT);
-       if (ret < 0) {
-               IWL_ERR(trans, "secure boot process didn't start\n");
-               return ret;
+       iwl_set_bits_prph(trans,
+                         CSR_UCODE_LOAD_STATUS_ADDR,
+                         LMPM_CPU_UCODE_LOADING_COMPLETED << shift_param);
+
+       *first_ucode_section = last_read_idx;
+
+       return 0;
+}
+
+static int iwl_pcie_load_cpu_sections(struct iwl_trans *trans,
+                                     const struct fw_img *image,
+                                     int cpu,
+                                     int *first_ucode_section)
+{
+       int shift_param;
+       int i, ret = 0;
+       u32 last_read_idx = 0;
+
+       if (cpu == 1) {
+               shift_param = 0;
+               *first_ucode_section = 0;
+       } else {
+               shift_param = 16;
+               (*first_ucode_section)++;
        }
 
-       /* wait for image verification to complete  */
-       ret = iwl_poll_bit(trans, address,
-                          CSR_SECURE_BOOT_CPU_STATUS_VERF_COMPLETED,
-                          CSR_SECURE_BOOT_CPU_STATUS_VERF_COMPLETED,
-                          CSR_SECURE_TIME_OUT);
+       for (i = *first_ucode_section; i < IWL_UCODE_SECTION_MAX; i++) {
+               last_read_idx = i;
 
-       if (ret < 0) {
-               IWL_ERR(trans, "Time out on secure boot process\n");
-               return ret;
+               if (!image->sec[i].data ||
+                   image->sec[i].offset == CPU1_CPU2_SEPARATOR_SECTION) {
+                       IWL_DEBUG_FW(trans,
+                                    "Break since Data not valid or Empty section, sec = %d\n",
+                                    i);
+                       break;
+               }
+
+               ret = iwl_pcie_load_section(trans, i, &image->sec[i]);
+               if (ret)
+                       return ret;
        }
 
+       if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
+               iwl_set_bits_prph(trans,
+                                 CSR_UCODE_LOAD_STATUS_ADDR,
+                                 (LMPM_CPU_UCODE_LOADING_COMPLETED |
+                                  LMPM_CPU_HDRS_LOADING_COMPLETED |
+                                  LMPM_CPU_UCODE_LOADING_STARTED) <<
+                                       shift_param);
+
+       *first_ucode_section = last_read_idx;
+
        return 0;
 }
 
 static int iwl_pcie_load_given_ucode(struct iwl_trans *trans,
                                const struct fw_img *image)
 {
-       int i, ret = 0;
+       int ret = 0;
+       int first_ucode_section;
 
        IWL_DEBUG_FW(trans,
                     "working with %s image\n",
@@ -518,53 +553,68 @@ static int iwl_pcie_load_given_ucode(struct iwl_trans *trans,
        /* configure the ucode to be ready to get the secured image */
        if (image->is_secure) {
                /* set secure boot inspector addresses */
-               iwl_write32(trans, CSR_SECURE_INSPECTOR_CODE_ADDR, 0);
-               iwl_write32(trans, CSR_SECURE_INSPECTOR_DATA_ADDR, 0);
-
-               /* release CPU1 reset if secure inspector image burned in OTP */
-               iwl_write32(trans, CSR_RESET, 0);
-       }
-
-       /* load to FW the binary sections of CPU1 */
-       IWL_DEBUG_INFO(trans, "Loading CPU1\n");
-       for (i = 0;
-            i < IWL_UCODE_FIRST_SECTION_OF_SECOND_CPU;
-            i++) {
-               if (!image->sec[i].data)
-                       break;
-               ret = iwl_pcie_load_section(trans, i, &image->sec[i]);
+               iwl_write_prph(trans,
+                              LMPM_SECURE_INSPECTOR_CODE_ADDR,
+                              LMPM_SECURE_INSPECTOR_CODE_MEM_SPACE);
+
+               iwl_write_prph(trans,
+                              LMPM_SECURE_INSPECTOR_DATA_ADDR,
+                              LMPM_SECURE_INSPECTOR_DATA_MEM_SPACE);
+
+               /* set CPU1 header address */
+               iwl_write_prph(trans,
+                              LMPM_SECURE_UCODE_LOAD_CPU1_HDR_ADDR,
+                              LMPM_SECURE_CPU1_HDR_MEM_SPACE);
+
+               /* load to FW the binary Secured sections of CPU1 */
+               ret = iwl_pcie_load_cpu_secured_sections(trans, image, 1,
+                                                        &first_ucode_section);
                if (ret)
                        return ret;
-       }
 
-       /* configure the ucode to start secure process on CPU1 */
-       if (image->is_secure) {
-               /* config CPU1 to start secure protocol */
-               ret = iwl_pcie_secure_set(trans, 1);
+       } else {
+               /* load to FW the binary Non secured sections of CPU1 */
+               ret = iwl_pcie_load_cpu_sections(trans, image, 1,
+                                                &first_ucode_section);
                if (ret)
                        return ret;
-       } else {
-               /* Remove all resets to allow NIC to operate */
-               iwl_write32(trans, CSR_RESET, 0);
        }
 
        if (image->is_dual_cpus) {
+               /* set CPU2 header address */
+               iwl_write_prph(trans,
+                              LMPM_SECURE_UCODE_LOAD_CPU2_HDR_ADDR,
+                              LMPM_SECURE_CPU2_HDR_MEM_SPACE);
+
                /* load to FW the binary sections of CPU2 */
-               IWL_DEBUG_INFO(trans, "working w/ DUAL CPUs - Loading CPU2\n");
-               for (i = IWL_UCODE_FIRST_SECTION_OF_SECOND_CPU;
-                       i < IWL_UCODE_SECTION_MAX; i++) {
-                       if (!image->sec[i].data)
-                               break;
-                       ret = iwl_pcie_load_section(trans, i, &image->sec[i]);
-                       if (ret)
-                               return ret;
-               }
+               if (image->is_secure)
+                       ret = iwl_pcie_load_cpu_secured_sections(
+                                                       trans, image, 2,
+                                                       &first_ucode_section);
+               else
+                       ret = iwl_pcie_load_cpu_sections(trans, image, 2,
+                                                        &first_ucode_section);
+               if (ret)
+                       return ret;
+       }
 
-               if (image->is_secure) {
-                       /* set CPU2 for secure protocol */
-                       ret = iwl_pcie_secure_set(trans, 2);
-                       if (ret)
-                               return ret;
+       /* release CPU reset */
+       if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
+               iwl_write_prph(trans, RELEASE_CPU_RESET, RELEASE_CPU_RESET_BIT);
+       else
+               iwl_write32(trans, CSR_RESET, 0);
+
+       if (image->is_secure) {
+               /* wait for image verification to complete  */
+               ret = iwl_poll_prph_bit(trans,
+                                       LMPM_SECURE_BOOT_CPU1_STATUS_ADDR,
+                                       LMPM_SECURE_BOOT_STATUS_SUCCESS,
+                                       LMPM_SECURE_BOOT_STATUS_SUCCESS,
+                                       LMPM_SECURE_TIME_OUT);
+
+               if (ret < 0) {
+                       IWL_ERR(trans, "Time out on secure boot process\n");
+                       return ret;
                }
        }
 
@@ -1407,16 +1457,15 @@ static ssize_t iwl_dbgfs_fh_reg_read(struct file *file,
 {
        struct iwl_trans *trans = file->private_data;
        char *buf = NULL;
-       int pos = 0;
-       ssize_t ret = -EFAULT;
-
-       ret = pos = iwl_dump_fh(trans, &buf);
-       if (buf) {
-               ret = simple_read_from_buffer(user_buf,
-                                             count, ppos, buf, pos);
-               kfree(buf);
-       }
+       ssize_t ret;
 
+       ret = iwl_dump_fh(trans, &buf);
+       if (ret < 0)
+               return ret;
+       if (!buf)
+               return -EINVAL;
+       ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+       kfree(buf);
        return ret;
 }
 
index 3d54900..2541264 100644 (file)
@@ -705,8 +705,9 @@ void iwl_pcie_tx_start(struct iwl_trans *trans, u32 scd_base_addr)
                           reg_val | FH_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN);
 
        /* Enable L1-Active */
-       iwl_clear_bits_prph(trans, APMG_PCIDEV_STT_REG,
-                           APMG_PCIDEV_STT_VAL_L1_ACT_DIS);
+       if (trans->cfg->device_family != IWL_DEVICE_FAMILY_8000)
+               iwl_clear_bits_prph(trans, APMG_PCIDEV_STT_REG,
+                                   APMG_PCIDEV_STT_VAL_L1_ACT_DIS);
 }
 
 void iwl_trans_pcie_tx_reset(struct iwl_trans *trans)
index 32f7500..2d72a6b 100644 (file)
@@ -1766,7 +1766,8 @@ static void lbs_join_post(struct lbs_private *priv,
        memcpy(priv->wdev->ssid, params->ssid, params->ssid_len);
        priv->wdev->ssid_len = params->ssid_len;
 
-       cfg80211_ibss_joined(priv->dev, bssid, GFP_KERNEL);
+       cfg80211_ibss_joined(priv->dev, bssid, params->chandef.chan,
+                            GFP_KERNEL);
 
        /* TODO: consider doing this at MACREG_INT_CODE_LINK_SENSED time */
        priv->connect_status = LBS_CONNECTED;
index 69d4c31..f7e3562 100644 (file)
@@ -57,6 +57,10 @@ static bool rctbl = false;
 module_param(rctbl, bool, 0444);
 MODULE_PARM_DESC(rctbl, "Handle rate control table");
 
+static bool support_p2p_device = true;
+module_param(support_p2p_device, bool, 0444);
+MODULE_PARM_DESC(support_p2p_device, "Support P2P-Device interface type");
+
 /**
  * enum hwsim_regtest - the type of regulatory tests we offer
  *
@@ -335,7 +339,8 @@ static const struct ieee80211_iface_limit hwsim_if_limits[] = {
 #endif
                                 BIT(NL80211_IFTYPE_AP) |
                                 BIT(NL80211_IFTYPE_P2P_GO) },
-       { .max = 1, .types = BIT(NL80211_IFTYPE_P2P_DEVICE) },
+       /* must be last, see hwsim_if_comb */
+       { .max = 1, .types = BIT(NL80211_IFTYPE_P2P_DEVICE) }
 };
 
 static const struct ieee80211_iface_limit hwsim_if_dfs_limits[] = {
@@ -343,6 +348,27 @@ static const struct ieee80211_iface_limit hwsim_if_dfs_limits[] = {
 };
 
 static const struct ieee80211_iface_combination hwsim_if_comb[] = {
+       {
+               .limits = hwsim_if_limits,
+               /* remove the last entry which is P2P_DEVICE */
+               .n_limits = ARRAY_SIZE(hwsim_if_limits) - 1,
+               .max_interfaces = 2048,
+               .num_different_channels = 1,
+       },
+       {
+               .limits = hwsim_if_dfs_limits,
+               .n_limits = ARRAY_SIZE(hwsim_if_dfs_limits),
+               .max_interfaces = 8,
+               .num_different_channels = 1,
+               .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) |
+                                      BIT(NL80211_CHAN_WIDTH_20) |
+                                      BIT(NL80211_CHAN_WIDTH_40) |
+                                      BIT(NL80211_CHAN_WIDTH_80) |
+                                      BIT(NL80211_CHAN_WIDTH_160),
+       }
+};
+
+static const struct ieee80211_iface_combination hwsim_if_comb_p2p_dev[] = {
        {
                .limits = hwsim_if_limits,
                .n_limits = ARRAY_SIZE(hwsim_if_limits),
@@ -451,7 +477,7 @@ static struct genl_family hwsim_genl_family = {
 
 /* MAC80211_HWSIM netlink policy */
 
-static struct nla_policy hwsim_genl_policy[HWSIM_ATTR_MAX + 1] = {
+static const struct nla_policy hwsim_genl_policy[HWSIM_ATTR_MAX + 1] = {
        [HWSIM_ATTR_ADDR_RECEIVER] = { .type = NLA_UNSPEC, .len = ETH_ALEN },
        [HWSIM_ATTR_ADDR_TRANSMITTER] = { .type = NLA_UNSPEC, .len = ETH_ALEN },
        [HWSIM_ATTR_FRAME] = { .type = NLA_BINARY,
@@ -468,6 +494,7 @@ static struct nla_policy hwsim_genl_policy[HWSIM_ATTR_MAX + 1] = {
        [HWSIM_ATTR_REG_HINT_ALPHA2] = { .type = NLA_STRING, .len = 2 },
        [HWSIM_ATTR_REG_CUSTOM_REG] = { .type = NLA_U32 },
        [HWSIM_ATTR_REG_STRICT_REG] = { .type = NLA_FLAG },
+       [HWSIM_ATTR_SUPPORT_P2P_DEVICE] = { .type = NLA_FLAG },
 };
 
 static void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
@@ -1035,32 +1062,6 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw,
                        ack = true;
 
                rx_status.mactime = now + data2->tsf_offset;
-#if 0
-               /*
-                * Don't enable this code by default as the OUI 00:00:00
-                * is registered to Xerox so we shouldn't use it here, it
-                * might find its way into pcap files.
-                * Note that this code requires the headroom in the SKB
-                * that was allocated earlier.
-                */
-               rx_status.vendor_radiotap_oui[0] = 0x00;
-               rx_status.vendor_radiotap_oui[1] = 0x00;
-               rx_status.vendor_radiotap_oui[2] = 0x00;
-               rx_status.vendor_radiotap_subns = 127;
-               /*
-                * Radiotap vendor namespaces can (and should) also be
-                * split into fields by using the standard radiotap
-                * presence bitmap mechanism. Use just BIT(0) here for
-                * the presence bitmap.
-                */
-               rx_status.vendor_radiotap_bitmap = BIT(0);
-               /* We have 8 bytes of (dummy) data */
-               rx_status.vendor_radiotap_len = 8;
-               /* For testing, also require it to be aligned */
-               rx_status.vendor_radiotap_align = 8;
-               /* push the data */
-               memcpy(skb_push(nskb, 8), "ABCDEFGH", 8);
-#endif
 
                memcpy(IEEE80211_SKB_RXCB(nskb), &rx_status, sizeof(rx_status));
                ieee80211_rx_irqsafe(data2->hw, nskb);
@@ -1275,6 +1276,9 @@ static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac,
 
        mac80211_hwsim_tx_frame(hw, skb,
                                rcu_dereference(vif->chanctx_conf)->def.chan);
+
+       if (vif->csa_active && ieee80211_csa_is_complete(vif))
+               ieee80211_csa_finish(vif);
 }
 
 static enum hrtimer_restart
@@ -1936,7 +1940,7 @@ static struct ieee80211_ops mac80211_hwsim_mchan_ops;
 
 static int mac80211_hwsim_create_radio(int channels, const char *reg_alpha2,
                                       const struct ieee80211_regdomain *regd,
-                                      bool reg_strict)
+                                      bool reg_strict, bool p2p_device)
 {
        int err;
        u8 addr[ETH_ALEN];
@@ -2000,8 +2004,15 @@ static int mac80211_hwsim_create_radio(int channels, const char *reg_alpha2,
                /* For channels > 1 DFS is not allowed */
                hw->wiphy->n_iface_combinations = 1;
                hw->wiphy->iface_combinations = &data->if_combination;
-               data->if_combination = hwsim_if_comb[0];
                data->if_combination.num_different_channels = data->channels;
+               if (p2p_device)
+                       data->if_combination = hwsim_if_comb_p2p_dev[0];
+               else
+                       data->if_combination = hwsim_if_comb[0];
+       } else if (p2p_device) {
+               hw->wiphy->iface_combinations = hwsim_if_comb_p2p_dev;
+               hw->wiphy->n_iface_combinations =
+                       ARRAY_SIZE(hwsim_if_comb_p2p_dev);
        } else {
                hw->wiphy->iface_combinations = hwsim_if_comb;
                hw->wiphy->n_iface_combinations = ARRAY_SIZE(hwsim_if_comb);
@@ -2017,8 +2028,10 @@ static int mac80211_hwsim_create_radio(int channels, const char *reg_alpha2,
                                     BIT(NL80211_IFTYPE_P2P_CLIENT) |
                                     BIT(NL80211_IFTYPE_P2P_GO) |
                                     BIT(NL80211_IFTYPE_ADHOC) |
-                                    BIT(NL80211_IFTYPE_MESH_POINT) |
-                                    BIT(NL80211_IFTYPE_P2P_DEVICE);
+                                    BIT(NL80211_IFTYPE_MESH_POINT);
+
+       if (p2p_device)
+               hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_DEVICE);
 
        hw->flags = IEEE80211_HW_MFP_CAPABLE |
                    IEEE80211_HW_SIGNAL_DBM |
@@ -2027,13 +2040,15 @@ static int mac80211_hwsim_create_radio(int channels, const char *reg_alpha2,
                    IEEE80211_HW_AMPDU_AGGREGATION |
                    IEEE80211_HW_WANT_MONITOR_VIF |
                    IEEE80211_HW_QUEUE_CONTROL |
-                   IEEE80211_HW_SUPPORTS_HT_CCK_RATES;
+                   IEEE80211_HW_SUPPORTS_HT_CCK_RATES |
+                   IEEE80211_HW_CHANCTX_STA_CSA;
        if (rctbl)
                hw->flags |= IEEE80211_HW_SUPPORTS_RC_TABLE;
 
        hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS |
                            WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
-                           WIPHY_FLAG_AP_UAPSD;
+                           WIPHY_FLAG_AP_UAPSD |
+                           WIPHY_FLAG_HAS_CHANNEL_SWITCH;
        hw->wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR;
 
        /* ask mac80211 to reserve space for magic */
@@ -2407,6 +2422,7 @@ static int hwsim_create_radio_nl(struct sk_buff *msg, struct genl_info *info)
        const char *alpha2 = NULL;
        const struct ieee80211_regdomain *regd = NULL;
        bool reg_strict = info->attrs[HWSIM_ATTR_REG_STRICT_REG];
+       bool p2p_device = info->attrs[HWSIM_ATTR_SUPPORT_P2P_DEVICE];
 
        if (info->attrs[HWSIM_ATTR_CHANNELS])
                chans = nla_get_u32(info->attrs[HWSIM_ATTR_CHANNELS]);
@@ -2422,7 +2438,8 @@ static int hwsim_create_radio_nl(struct sk_buff *msg, struct genl_info *info)
                regd = hwsim_world_regdom_custom[idx];
        }
 
-       return mac80211_hwsim_create_radio(chans, alpha2, regd, reg_strict);
+       return mac80211_hwsim_create_radio(chans, alpha2, regd, reg_strict,
+                                          p2p_device);
 }
 
 static int hwsim_destroy_radio_nl(struct sk_buff *msg, struct genl_info *info)
@@ -2640,7 +2657,8 @@ static int __init init_mac80211_hwsim(void)
                }
 
                err = mac80211_hwsim_create_radio(channels, reg_alpha2,
-                                                 regd, reg_strict);
+                                                 regd, reg_strict,
+                                                 support_p2p_device);
                if (err < 0)
                        goto out_free_radios;
        }
index 2747cce..6e72996 100644 (file)
@@ -107,6 +107,7 @@ enum {
  *     (nla string, length 2)
  * @HWSIM_ATTR_REG_CUSTOM_REG: custom regulatory domain index (u32 attribute)
  * @HWSIM_ATTR_REG_STRICT_REG: request REGULATORY_STRICT_REG (flag attribute)
+ * @HWSIM_ATTR_SUPPORT_P2P_DEVICE: support P2P Device virtual interface (flag)
  * @__HWSIM_ATTR_MAX: enum limit
  */
 
@@ -126,6 +127,7 @@ enum {
        HWSIM_ATTR_REG_HINT_ALPHA2,
        HWSIM_ATTR_REG_CUSTOM_REG,
        HWSIM_ATTR_REG_STRICT_REG,
+       HWSIM_ATTR_SUPPORT_P2P_DEVICE,
        __HWSIM_ATTR_MAX,
 };
 #define HWSIM_ATTR_MAX (__HWSIM_ATTR_MAX - 1)
index 5e0eec4..bb43251 100644 (file)
 #include "main.h"
 #include "11ac.h"
 
+/* Tables of the MCS map to the highest data rate (in Mbps) supported
+ * for long GI.
+ */
+static const u16 max_rate_lgi_80MHZ[8][3] = {
+       {0x124, 0x15F, 0x186},  /* NSS = 1 */
+       {0x249, 0x2BE, 0x30C},  /* NSS = 2 */
+       {0x36D, 0x41D, 0x492},  /* NSS = 3 */
+       {0x492, 0x57C, 0x618},  /* NSS = 4 */
+       {0x5B6, 0x6DB, 0x79E},  /* NSS = 5 */
+       {0x6DB, 0x83A, 0x0},    /* NSS = 6 */
+       {0x7FF, 0x999, 0xAAA},  /* NSS = 7 */
+       {0x924, 0xAF8, 0xC30}   /* NSS = 8 */
+};
+
+static const u16 max_rate_lgi_160MHZ[8][3] = {
+       {0x249, 0x2BE, 0x30C},   /* NSS = 1 */
+       {0x492, 0x57C, 0x618},   /* NSS = 2 */
+       {0x6DB, 0x83A, 0x0},     /* NSS = 3 */
+       {0x924, 0xAF8, 0xC30},   /* NSS = 4 */
+       {0xB6D, 0xDB6, 0xF3C},   /* NSS = 5 */
+       {0xDB6, 0x1074, 0x1248}, /* NSS = 6 */
+       {0xFFF, 0x1332, 0x1554}, /* NSS = 7 */
+       {0x1248, 0x15F0, 0x1860} /* NSS = 8 */
+};
+
 /* This function converts the 2-bit MCS map to the highest long GI
  * VHT data rate.
  */
@@ -30,33 +55,10 @@ static u16
 mwifiex_convert_mcsmap_to_maxrate(struct mwifiex_private *priv,
                                  u8 bands, u16 mcs_map)
 {
-       u8 i, nss, max_mcs;
+       u8 i, nss, mcs;
        u16 max_rate = 0;
        u32 usr_vht_cap_info = 0;
        struct mwifiex_adapter *adapter = priv->adapter;
-       /* tables of the MCS map to the highest data rate (in Mbps)
-        * supported for long GI
-        */
-       u16 max_rate_lgi_80MHZ[8][3] = {
-               {0x124, 0x15F, 0x186},  /* NSS = 1 */
-               {0x249, 0x2BE, 0x30C},  /* NSS = 2 */
-               {0x36D, 0x41D, 0x492},  /* NSS = 3 */
-               {0x492, 0x57C, 0x618},  /* NSS = 4 */
-               {0x5B6, 0x6DB, 0x79E},  /* NSS = 5 */
-               {0x6DB, 0x83A, 0x0},    /* NSS = 6 */
-               {0x7FF, 0x999, 0xAAA},  /* NSS = 7 */
-               {0x924, 0xAF8, 0xC30}   /* NSS = 8 */
-       };
-       u16 max_rate_lgi_160MHZ[8][3] = {
-               {0x249, 0x2BE, 0x30C},   /* NSS = 1 */
-               {0x492, 0x57C, 0x618},   /* NSS = 2 */
-               {0x6DB, 0x83A, 0x0},     /* NSS = 3 */
-               {0x924, 0xAF8, 0xC30},   /* NSS = 4 */
-               {0xB6D, 0xDB6, 0xF3C},   /* NSS = 5 */
-               {0xDB6, 0x1074, 0x1248}, /* NSS = 6 */
-               {0xFFF, 0x1332, 0x1554}, /* NSS = 7 */
-               {0x1248, 0x15F0, 0x1860} /* NSS = 8 */
-       };
 
        if (bands & BAND_AAC)
                usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_a;
@@ -64,29 +66,29 @@ mwifiex_convert_mcsmap_to_maxrate(struct mwifiex_private *priv,
                usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_bg;
 
        /* find the max NSS supported */
-       nss = 0;
-       for (i = 0; i < 8; i++) {
-               max_mcs = (mcs_map >> (2 * i)) & 0x3;
-               if (max_mcs < 3)
+       nss = 1;
+       for (i = 1; i <= 8; i++) {
+               mcs = GET_VHTNSSMCS(mcs_map, i);
+               if (mcs < IEEE80211_VHT_MCS_NOT_SUPPORTED)
                        nss = i;
        }
-       max_mcs = (mcs_map >> (2 * nss)) & 0x3;
+       mcs = GET_VHTNSSMCS(mcs_map, nss);
 
-       /* if max_mcs is 3, nss must be 0 (SS = 1). Thus, max mcs is MCS 9 */
-       if (max_mcs >= 3)
-               max_mcs = 2;
+       /* if mcs is 3, nss must be 1 (NSS = 1). Default mcs to MCS 0~9 */
+       if (mcs == IEEE80211_VHT_MCS_NOT_SUPPORTED)
+               mcs = IEEE80211_VHT_MCS_SUPPORT_0_9;
 
        if (GET_VHTCAP_CHWDSET(usr_vht_cap_info)) {
                /* support 160 MHz */
-               max_rate = max_rate_lgi_160MHZ[nss][max_mcs];
+               max_rate = max_rate_lgi_160MHZ[nss - 1][mcs];
                if (!max_rate)
                        /* MCS9 is not supported in NSS6 */
-                       max_rate = max_rate_lgi_160MHZ[nss][max_mcs - 1];
+                       max_rate = max_rate_lgi_160MHZ[nss - 1][mcs - 1];
        } else {
-               max_rate = max_rate_lgi_80MHZ[nss][max_mcs];
+               max_rate = max_rate_lgi_80MHZ[nss - 1][mcs];
                if (!max_rate)
                        /* MCS9 is not supported in NSS3 */
-                       max_rate = max_rate_lgi_80MHZ[nss][max_mcs - 1];
+                       max_rate = max_rate_lgi_80MHZ[nss - 1][mcs - 1];
        }
 
        return max_rate;
@@ -94,21 +96,20 @@ mwifiex_convert_mcsmap_to_maxrate(struct mwifiex_private *priv,
 
 static void
 mwifiex_fill_vht_cap_info(struct mwifiex_private *priv,
-                         struct mwifiex_ie_types_vhtcap *vht_cap, u8 bands)
+                         struct ieee80211_vht_cap *vht_cap, u8 bands)
 {
        struct mwifiex_adapter *adapter = priv->adapter;
 
        if (bands & BAND_A)
-               vht_cap->vht_cap.vht_cap_info =
+               vht_cap->vht_cap_info =
                                cpu_to_le32(adapter->usr_dot_11ac_dev_cap_a);
        else
-               vht_cap->vht_cap.vht_cap_info =
+               vht_cap->vht_cap_info =
                                cpu_to_le32(adapter->usr_dot_11ac_dev_cap_bg);
 }
 
-static void
-mwifiex_fill_vht_cap_tlv(struct mwifiex_private *priv,
-                        struct mwifiex_ie_types_vhtcap *vht_cap, u8 bands)
+void mwifiex_fill_vht_cap_tlv(struct mwifiex_private *priv,
+                             struct ieee80211_vht_cap *vht_cap, u8 bands)
 {
        struct mwifiex_adapter *adapter = priv->adapter;
        u16 mcs_map_user, mcs_map_resp, mcs_map_result;
@@ -119,46 +120,48 @@ mwifiex_fill_vht_cap_tlv(struct mwifiex_private *priv,
 
        /* rx MCS Set: find the minimum of the user rx mcs and ap rx mcs */
        mcs_map_user = GET_DEVRXMCSMAP(adapter->usr_dot_11ac_mcs_support);
-       mcs_map_resp = le16_to_cpu(vht_cap->vht_cap.supp_mcs.rx_mcs_map);
+       mcs_map_resp = le16_to_cpu(vht_cap->supp_mcs.rx_mcs_map);
        mcs_map_result = 0;
 
        for (nss = 1; nss <= 8; nss++) {
                mcs_user = GET_VHTNSSMCS(mcs_map_user, nss);
                mcs_resp = GET_VHTNSSMCS(mcs_map_resp, nss);
 
-               if ((mcs_user == NO_NSS_SUPPORT) ||
-                   (mcs_resp == NO_NSS_SUPPORT))
-                       SET_VHTNSSMCS(mcs_map_result, nss, NO_NSS_SUPPORT);
+               if ((mcs_user == IEEE80211_VHT_MCS_NOT_SUPPORTED) ||
+                   (mcs_resp == IEEE80211_VHT_MCS_NOT_SUPPORTED))
+                       SET_VHTNSSMCS(mcs_map_result, nss,
+                                     IEEE80211_VHT_MCS_NOT_SUPPORTED);
                else
                        SET_VHTNSSMCS(mcs_map_result, nss,
                                      min(mcs_user, mcs_resp));
        }
 
-       vht_cap->vht_cap.supp_mcs.rx_mcs_map = cpu_to_le16(mcs_map_result);
+       vht_cap->supp_mcs.rx_mcs_map = cpu_to_le16(mcs_map_result);
 
        tmp = mwifiex_convert_mcsmap_to_maxrate(priv, bands, mcs_map_result);
-       vht_cap->vht_cap.supp_mcs.rx_highest = cpu_to_le16(tmp);
+       vht_cap->supp_mcs.rx_highest = cpu_to_le16(tmp);
 
        /* tx MCS Set: find the minimum of the user tx mcs and ap tx mcs */
        mcs_map_user = GET_DEVTXMCSMAP(adapter->usr_dot_11ac_mcs_support);
-       mcs_map_resp = le16_to_cpu(vht_cap->vht_cap.supp_mcs.tx_mcs_map);
+       mcs_map_resp = le16_to_cpu(vht_cap->supp_mcs.tx_mcs_map);
        mcs_map_result = 0;
 
        for (nss = 1; nss <= 8; nss++) {
                mcs_user = GET_VHTNSSMCS(mcs_map_user, nss);
                mcs_resp = GET_VHTNSSMCS(mcs_map_resp, nss);
-               if ((mcs_user == NO_NSS_SUPPORT) ||
-                   (mcs_resp == NO_NSS_SUPPORT))
-                       SET_VHTNSSMCS(mcs_map_result, nss, NO_NSS_SUPPORT);
+               if ((mcs_user == IEEE80211_VHT_MCS_NOT_SUPPORTED) ||
+                   (mcs_resp == IEEE80211_VHT_MCS_NOT_SUPPORTED))
+                       SET_VHTNSSMCS(mcs_map_result, nss,
+                                     IEEE80211_VHT_MCS_NOT_SUPPORTED);
                else
                        SET_VHTNSSMCS(mcs_map_result, nss,
                                      min(mcs_user, mcs_resp));
        }
 
-       vht_cap->vht_cap.supp_mcs.tx_mcs_map = cpu_to_le16(mcs_map_result);
+       vht_cap->supp_mcs.tx_mcs_map = cpu_to_le16(mcs_map_result);
 
        tmp = mwifiex_convert_mcsmap_to_maxrate(priv, bands, mcs_map_result);
-       vht_cap->vht_cap.supp_mcs.tx_highest = cpu_to_le16(tmp);
+       vht_cap->supp_mcs.tx_highest = cpu_to_le16(tmp);
 
        return;
 }
@@ -193,7 +196,8 @@ int mwifiex_cmd_append_11ac_tlv(struct mwifiex_private *priv,
                       sizeof(struct ieee_types_header),
                       le16_to_cpu(vht_cap->header.len));
 
-               mwifiex_fill_vht_cap_tlv(priv, vht_cap, bss_desc->bss_band);
+               mwifiex_fill_vht_cap_tlv(priv, &vht_cap->vht_cap,
+                                        bss_desc->bss_band);
                *buffer += sizeof(*vht_cap);
                ret_len += sizeof(*vht_cap);
        }
@@ -300,3 +304,81 @@ void mwifiex_set_11ac_ba_params(struct mwifiex_private *priv)
 
        return;
 }
+
+bool mwifiex_is_bss_in_11ac_mode(struct mwifiex_private *priv)
+{
+       struct mwifiex_bssdescriptor *bss_desc;
+       struct ieee80211_vht_operation *vht_oper;
+
+       bss_desc = &priv->curr_bss_params.bss_descriptor;
+       vht_oper = bss_desc->bcn_vht_oper;
+
+       if (!bss_desc->bcn_vht_cap || !vht_oper)
+               return false;
+
+       if (vht_oper->chan_width == IEEE80211_VHT_CHANWIDTH_USE_HT)
+               return false;
+
+       return true;
+}
+
+u8 mwifiex_get_center_freq_index(struct mwifiex_private *priv, u8 band,
+                                u32 pri_chan, u8 chan_bw)
+{
+       u8 center_freq_idx = 0;
+
+       if (band & BAND_AAC) {
+               switch (pri_chan) {
+               case 36:
+               case 40:
+               case 44:
+               case 48:
+                       if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ)
+                               center_freq_idx = 42;
+                       break;
+               case 52:
+               case 56:
+               case 60:
+               case 64:
+                       if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ)
+                               center_freq_idx = 58;
+                       else if (chan_bw == IEEE80211_VHT_CHANWIDTH_160MHZ)
+                               center_freq_idx = 50;
+                       break;
+               case 100:
+               case 104:
+               case 108:
+               case 112:
+                       if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ)
+                               center_freq_idx = 106;
+                       break;
+               case 116:
+               case 120:
+               case 124:
+               case 128:
+                       if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ)
+                               center_freq_idx = 122;
+                       else if (chan_bw == IEEE80211_VHT_CHANWIDTH_160MHZ)
+                               center_freq_idx = 114;
+                       break;
+               case 132:
+               case 136:
+               case 140:
+               case 144:
+                       if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ)
+                               center_freq_idx = 138;
+                       break;
+               case 149:
+               case 153:
+               case 157:
+               case 161:
+                       if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ)
+                               center_freq_idx = 155;
+                       break;
+               default:
+                       center_freq_idx = 42;
+               }
+       }
+
+       return center_freq_idx;
+}
index 7c2c69b..0b02cb6 100644 (file)
@@ -40,4 +40,6 @@ int mwifiex_cmd_append_11ac_tlv(struct mwifiex_private *priv,
 int mwifiex_cmd_11ac_cfg(struct mwifiex_private *priv,
                         struct host_cmd_ds_command *cmd, u16 cmd_action,
                         struct mwifiex_11ac_vht_cfg *cfg);
+void mwifiex_fill_vht_cap_tlv(struct mwifiex_private *priv,
+                             struct ieee80211_vht_cap *vht_cap, u8 bands);
 #endif /* _MWIFIEX_11AC_H_ */
index 6261f8c..37677af 100644 (file)
  *
  * RD responder bit to set to clear in the extended capability header.
  */
-void
-mwifiex_fill_cap_info(struct mwifiex_private *priv, u8 radio_type,
-                     struct mwifiex_ie_types_htcap *ht_cap)
+int mwifiex_fill_cap_info(struct mwifiex_private *priv, u8 radio_type,
+                         struct ieee80211_ht_cap *ht_cap)
 {
-       uint16_t ht_ext_cap = le16_to_cpu(ht_cap->ht_cap.extended_ht_cap_info);
+       uint16_t ht_ext_cap = le16_to_cpu(ht_cap->extended_ht_cap_info);
        struct ieee80211_supported_band *sband =
                                        priv->wdev->wiphy->bands[radio_type];
 
-       ht_cap->ht_cap.ampdu_params_info =
+       if (WARN_ON_ONCE(!sband)) {
+               dev_err(priv->adapter->dev, "Invalid radio type!\n");
+               return -EINVAL;
+       }
+
+       ht_cap->ampdu_params_info =
                (sband->ht_cap.ampdu_factor &
                 IEEE80211_HT_AMPDU_PARM_FACTOR) |
                ((sband->ht_cap.ampdu_density <<
                 IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT) &
                 IEEE80211_HT_AMPDU_PARM_DENSITY);
 
-       memcpy((u8 *) &ht_cap->ht_cap.mcs, &sband->ht_cap.mcs,
+       memcpy((u8 *)&ht_cap->mcs, &sband->ht_cap.mcs,
               sizeof(sband->ht_cap.mcs));
 
        if (priv->bss_mode == NL80211_IFTYPE_STATION ||
@@ -57,13 +61,18 @@ mwifiex_fill_cap_info(struct mwifiex_private *priv, u8 radio_type,
             (priv->adapter->sec_chan_offset !=
                                        IEEE80211_HT_PARAM_CHA_SEC_NONE)))
                /* Set MCS32 for infra mode or ad-hoc mode with 40MHz support */
-               SETHT_MCS32(ht_cap->ht_cap.mcs.rx_mask);
+               SETHT_MCS32(ht_cap->mcs.rx_mask);
 
        /* Clear RD responder bit */
        ht_ext_cap &= ~IEEE80211_HT_EXT_CAP_RD_RESPONDER;
 
-       ht_cap->ht_cap.cap_info = cpu_to_le16(sband->ht_cap.cap);
-       ht_cap->ht_cap.extended_ht_cap_info = cpu_to_le16(ht_ext_cap);
+       ht_cap->cap_info = cpu_to_le16(sband->ht_cap.cap);
+       ht_cap->extended_ht_cap_info = cpu_to_le16(ht_ext_cap);
+
+       if (ISSUPP_BEAMFORMING(priv->adapter->hw_dot_11n_dev_cap))
+               ht_cap->tx_BF_cap_info = cpu_to_le32(MWIFIEX_DEF_11N_TX_BF_CAP);
+
+       return 0;
 }
 
 /*
@@ -312,7 +321,7 @@ mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv,
                       sizeof(struct ieee_types_header),
                       le16_to_cpu(ht_cap->header.len));
 
-               mwifiex_fill_cap_info(priv, radio_type, ht_cap);
+               mwifiex_fill_cap_info(priv, radio_type, &ht_cap->ht_cap);
 
                *buffer += sizeof(struct mwifiex_ie_types_htcap);
                ret_len += sizeof(struct mwifiex_ie_types_htcap);
@@ -528,16 +537,32 @@ void mwifiex_create_ba_tbl(struct mwifiex_private *priv, u8 *ra, int tid,
 int mwifiex_send_addba(struct mwifiex_private *priv, int tid, u8 *peer_mac)
 {
        struct host_cmd_ds_11n_addba_req add_ba_req;
+       struct mwifiex_sta_node *sta_ptr;
+       u32 tx_win_size = priv->add_ba_param.tx_win_size;
        static u8 dialog_tok;
        int ret;
 
        dev_dbg(priv->adapter->dev, "cmd: %s: tid %d\n", __func__, tid);
 
+       if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) &&
+           ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) &&
+           priv->adapter->is_hw_11ac_capable &&
+           memcmp(priv->cfg_bssid, peer_mac, ETH_ALEN)) {
+               sta_ptr = mwifiex_get_sta_entry(priv, peer_mac);
+               if (!sta_ptr) {
+                       dev_warn(priv->adapter->dev,
+                                "BA setup with unknown TDLS peer %pM!\n",
+                               peer_mac);
+                       return -1;
+               }
+               if (sta_ptr->is_11ac_enabled)
+                       tx_win_size = MWIFIEX_11AC_STA_AMPDU_DEF_TXWINSIZE;
+       }
+
        add_ba_req.block_ack_param_set = cpu_to_le16(
                (u16) ((tid << BLOCKACKPARAM_TID_POS) |
-                        (priv->add_ba_param.
-                         tx_win_size << BLOCKACKPARAM_WINSIZE_POS) |
-                        IMMEDIATE_BLOCK_ACK));
+                      tx_win_size << BLOCKACKPARAM_WINSIZE_POS |
+                      IMMEDIATE_BLOCK_ACK));
        add_ba_req.block_ack_tmo = cpu_to_le16((u16)priv->add_ba_param.timeout);
 
        ++dialog_tok;
index 375db01..12bb6ac 100644 (file)
@@ -34,8 +34,8 @@ int mwifiex_cmd_11n_cfg(struct mwifiex_private *priv,
 int mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv,
                               struct mwifiex_bssdescriptor *bss_desc,
                               u8 **buffer);
-void mwifiex_fill_cap_info(struct mwifiex_private *, u8 radio_type,
-                          struct mwifiex_ie_types_htcap *);
+int mwifiex_fill_cap_info(struct mwifiex_private *, u8 radio_type,
+                         struct ieee80211_ht_cap *);
 int mwifiex_set_get_11n_htcap_cfg(struct mwifiex_private *priv,
                                  u16 action, int *htcap_cfg);
 void mwifiex_11n_delete_tx_ba_stream_tbl_entry(struct mwifiex_private *priv,
@@ -64,14 +64,32 @@ int mwifiex_cmd_amsdu_aggr_ctrl(struct host_cmd_ds_command *cmd,
                                struct mwifiex_ds_11n_amsdu_aggr_ctrl *aa_ctrl);
 void mwifiex_del_tx_ba_stream_tbl_by_ra(struct mwifiex_private *priv, u8 *ra);
 
-/*
- * This function checks whether AMPDU is allowed or not for a particular TID.
- */
 static inline u8
-mwifiex_is_ampdu_allowed(struct mwifiex_private *priv, int tid)
+mwifiex_is_station_ampdu_allowed(struct mwifiex_private *priv,
+                                struct mwifiex_ra_list_tbl *ptr, int tid)
 {
-       return ((priv->aggr_prio_tbl[tid].ampdu_ap != BA_STREAM_NOT_ALLOWED)
-               ? true : false);
+       struct mwifiex_sta_node *node = mwifiex_get_sta_entry(priv, ptr->ra);
+
+       if (unlikely(!node))
+               return false;
+
+       return (node->ampdu_sta[tid] != BA_STREAM_NOT_ALLOWED) ? true : false;
+}
+
+/* This function checks whether AMPDU is allowed or not for a particular TID. */
+static inline u8
+mwifiex_is_ampdu_allowed(struct mwifiex_private *priv,
+                        struct mwifiex_ra_list_tbl *ptr, int tid)
+{
+       if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) {
+               return mwifiex_is_station_ampdu_allowed(priv, ptr, tid);
+       } else {
+               if (ptr->tdls_link)
+                       return mwifiex_is_station_ampdu_allowed(priv, ptr, tid);
+
+               return (priv->aggr_prio_tbl[tid].ampdu_ap !=
+                       BA_STREAM_NOT_ALLOWED) ? true : false;
+       }
 }
 
 /*
@@ -165,4 +183,14 @@ static inline int mwifiex_is_sta_11n_enabled(struct mwifiex_private *priv,
 
        return node->is_11n_enabled;
 }
+
+static inline u8
+mwifiex_tdls_peer_11n_enabled(struct mwifiex_private *priv, u8 *ra)
+{
+       struct mwifiex_sta_node *node = mwifiex_get_sta_entry(priv, ra);
+       if (node)
+               return node->is_11n_enabled;
+
+       return false;
+}
 #endif /* !_MWIFIEX_11N_H_ */
index ada809f..b361257 100644 (file)
@@ -290,7 +290,11 @@ mwifiex_11n_create_rx_reorder_tbl(struct mwifiex_private *priv, u8 *ta,
                                last_seq = node->rx_seq[tid];
                }
        } else {
-               last_seq = priv->rx_seq[tid];
+               node = mwifiex_get_sta_entry(priv, ta);
+               if (node)
+                       last_seq = node->rx_seq[tid];
+               else
+                       last_seq = priv->rx_seq[tid];
        }
 
        if (last_seq != MWIFIEX_DEF_11N_RX_SEQ_NUM &&
@@ -358,10 +362,28 @@ int mwifiex_cmd_11n_addba_rsp_gen(struct mwifiex_private *priv,
                                  *cmd_addba_req)
 {
        struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = &cmd->params.add_ba_rsp;
+       struct mwifiex_sta_node *sta_ptr;
+       u32 rx_win_size = priv->add_ba_param.rx_win_size;
        u8 tid;
        int win_size;
        uint16_t block_ack_param_set;
 
+       if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) &&
+           ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) &&
+           priv->adapter->is_hw_11ac_capable &&
+           memcmp(priv->cfg_bssid, cmd_addba_req->peer_mac_addr, ETH_ALEN)) {
+               sta_ptr = mwifiex_get_sta_entry(priv,
+                                               cmd_addba_req->peer_mac_addr);
+               if (!sta_ptr) {
+                       dev_warn(priv->adapter->dev,
+                                "BA setup with unknown TDLS peer %pM!\n",
+                                cmd_addba_req->peer_mac_addr);
+                       return -1;
+               }
+               if (sta_ptr->is_11ac_enabled)
+                       rx_win_size = MWIFIEX_11AC_STA_AMPDU_DEF_RXWINSIZE;
+       }
+
        cmd->command = cpu_to_le16(HostCmd_CMD_11N_ADDBA_RSP);
        cmd->size = cpu_to_le16(sizeof(*add_ba_rsp) + S_DS_GEN);
 
@@ -378,8 +400,7 @@ int mwifiex_cmd_11n_addba_rsp_gen(struct mwifiex_private *priv,
        block_ack_param_set &= ~IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK;
        /* We donot support AMSDU inside AMPDU, hence reset the bit */
        block_ack_param_set &= ~BLOCKACKPARAM_AMSDU_SUPP_MASK;
-       block_ack_param_set |= (priv->add_ba_param.rx_win_size <<
-                                            BLOCKACKPARAM_WINSIZE_POS);
+       block_ack_param_set |= rx_win_size << BLOCKACKPARAM_WINSIZE_POS;
        add_ba_rsp->block_ack_param_set = cpu_to_le16(block_ack_param_set);
        win_size = (le16_to_cpu(add_ba_rsp->block_ack_param_set)
                                        & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK)
index a42a506..2aa208f 100644 (file)
@@ -41,6 +41,7 @@ mwifiex-y += uap_txrx.o
 mwifiex-y += cfg80211.o
 mwifiex-y += ethtool.o
 mwifiex-y += 11h.o
+mwifiex-y += tdls.o
 mwifiex-$(CONFIG_DEBUG_FS) += debugfs.o
 obj-$(CONFIG_MWIFIEX) += mwifiex.o
 
index 8bfc07c..436ba43 100644 (file)
@@ -1416,9 +1416,6 @@ static int mwifiex_cfg80211_start_ap(struct wiphy *wiphy,
 
                if (params->chandef.width > NL80211_CHAN_WIDTH_20_NOHT)
                        config_bands |= BAND_GN;
-
-               if (params->chandef.width > NL80211_CHAN_WIDTH_40)
-                       config_bands |= BAND_GAC;
        } else {
                bss_cfg->band_cfg = BAND_CONFIG_A;
                config_bands = BAND_A;
@@ -1583,8 +1580,9 @@ static int mwifiex_cfg80211_inform_ibss_bss(struct mwifiex_private *priv)
  * the function notifies the CFG802.11 subsystem of the new BSS connection.
  */
 static int
-mwifiex_cfg80211_assoc(struct mwifiex_private *priv, size_t ssid_len, u8 *ssid,
-                      u8 *bssid, int mode, struct ieee80211_channel *channel,
+mwifiex_cfg80211_assoc(struct mwifiex_private *priv, size_t ssid_len,
+                      const u8 *ssid, const u8 *bssid, int mode,
+                      struct ieee80211_channel *channel,
                       struct cfg80211_connect_params *sme, bool privacy)
 {
        struct cfg80211_ssid req_ssid;
@@ -1881,7 +1879,8 @@ mwifiex_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev,
                                     params->privacy);
 done:
        if (!ret) {
-               cfg80211_ibss_joined(priv->netdev, priv->cfg_bssid, GFP_KERNEL);
+               cfg80211_ibss_joined(priv->netdev, priv->cfg_bssid,
+                                    params->chandef.chan, GFP_KERNEL);
                dev_dbg(priv->adapter->dev,
                        "info: joined/created adhoc network with bssid"
                        " %pM successfully\n", priv->cfg_bssid);
@@ -2595,6 +2594,170 @@ static int mwifiex_cfg80211_set_coalesce(struct wiphy *wiphy,
                                     HostCmd_ACT_GEN_SET, 0, &coalesce_cfg);
 }
 
+/* cfg80211 ops handler for tdls_mgmt.
+ * Function prepares TDLS action frame packets and forwards them to FW
+ */
+static int
+mwifiex_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
+                          u8 *peer, u8 action_code, u8 dialog_token,
+                          u16 status_code, const u8 *extra_ies,
+                          size_t extra_ies_len)
+{
+       struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+       int ret;
+
+       if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS))
+               return -ENOTSUPP;
+
+       /* make sure we are in station mode and connected */
+       if (!(priv->bss_type == MWIFIEX_BSS_TYPE_STA && priv->media_connected))
+               return -ENOTSUPP;
+
+       switch (action_code) {
+       case WLAN_TDLS_SETUP_REQUEST:
+               dev_dbg(priv->adapter->dev,
+                       "Send TDLS Setup Request to %pM status_code=%d\n", peer,
+                        status_code);
+               ret = mwifiex_send_tdls_data_frame(priv, peer, action_code,
+                                                  dialog_token, status_code,
+                                                  extra_ies, extra_ies_len);
+               break;
+       case WLAN_TDLS_SETUP_RESPONSE:
+               dev_dbg(priv->adapter->dev,
+                       "Send TDLS Setup Response to %pM status_code=%d\n",
+                       peer, status_code);
+               ret = mwifiex_send_tdls_data_frame(priv, peer, action_code,
+                                                  dialog_token, status_code,
+                                                  extra_ies, extra_ies_len);
+               break;
+       case WLAN_TDLS_SETUP_CONFIRM:
+               dev_dbg(priv->adapter->dev,
+                       "Send TDLS Confirm to %pM status_code=%d\n", peer,
+                       status_code);
+               ret = mwifiex_send_tdls_data_frame(priv, peer, action_code,
+                                                  dialog_token, status_code,
+                                                  extra_ies, extra_ies_len);
+               break;
+       case WLAN_TDLS_TEARDOWN:
+               dev_dbg(priv->adapter->dev, "Send TDLS Tear down to %pM\n",
+                       peer);
+               ret = mwifiex_send_tdls_data_frame(priv, peer, action_code,
+                                                  dialog_token, status_code,
+                                                  extra_ies, extra_ies_len);
+               break;
+       case WLAN_TDLS_DISCOVERY_REQUEST:
+               dev_dbg(priv->adapter->dev,
+                       "Send TDLS Discovery Request to %pM\n", peer);
+               ret = mwifiex_send_tdls_data_frame(priv, peer, action_code,
+                                                  dialog_token, status_code,
+                                                  extra_ies, extra_ies_len);
+               break;
+       case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
+               dev_dbg(priv->adapter->dev,
+                       "Send TDLS Discovery Response to %pM\n", peer);
+               ret = mwifiex_send_tdls_action_frame(priv, peer, action_code,
+                                                  dialog_token, status_code,
+                                                  extra_ies, extra_ies_len);
+               break;
+       default:
+               dev_warn(priv->adapter->dev,
+                        "Unknown TDLS mgmt/action frame %pM\n", peer);
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+static int
+mwifiex_cfg80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
+                          u8 *peer, enum nl80211_tdls_operation action)
+{
+       struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+
+       if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) ||
+           !(wiphy->flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP))
+               return -ENOTSUPP;
+
+       /* make sure we are in station mode and connected */
+       if (!(priv->bss_type == MWIFIEX_BSS_TYPE_STA && priv->media_connected))
+               return -ENOTSUPP;
+
+       dev_dbg(priv->adapter->dev,
+               "TDLS peer=%pM, oper=%d\n", peer, action);
+
+       switch (action) {
+       case NL80211_TDLS_ENABLE_LINK:
+               action = MWIFIEX_TDLS_ENABLE_LINK;
+               break;
+       case NL80211_TDLS_DISABLE_LINK:
+               action = MWIFIEX_TDLS_DISABLE_LINK;
+               break;
+       case NL80211_TDLS_TEARDOWN:
+               /* shouldn't happen!*/
+               dev_warn(priv->adapter->dev,
+                        "tdls_oper: teardown from driver not supported\n");
+               return -EINVAL;
+       case NL80211_TDLS_SETUP:
+               /* shouldn't happen!*/
+               dev_warn(priv->adapter->dev,
+                        "tdls_oper: setup from driver not supported\n");
+               return -EINVAL;
+       case NL80211_TDLS_DISCOVERY_REQ:
+               /* shouldn't happen!*/
+               dev_warn(priv->adapter->dev,
+                        "tdls_oper: discovery from driver not supported\n");
+               return -EINVAL;
+       default:
+               dev_err(priv->adapter->dev,
+                       "tdls_oper: operation not supported\n");
+               return -ENOTSUPP;
+       }
+
+       return mwifiex_tdls_oper(priv, peer, action);
+}
+
+static int
+mwifiex_cfg80211_add_station(struct wiphy *wiphy,
+                            struct net_device *dev,
+                            u8 *mac, struct station_parameters *params)
+{
+       struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+
+       if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)))
+               return -ENOTSUPP;
+
+       /* make sure we are in station mode and connected */
+       if ((priv->bss_type != MWIFIEX_BSS_TYPE_STA) || !priv->media_connected)
+               return -ENOTSUPP;
+
+       return mwifiex_tdls_oper(priv, mac, MWIFIEX_TDLS_CREATE_LINK);
+}
+
+static int
+mwifiex_cfg80211_change_station(struct wiphy *wiphy,
+                               struct net_device *dev,
+                               u8 *mac, struct station_parameters *params)
+{
+       int ret;
+       struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+
+       /* we support change_station handler only for TDLS peers*/
+       if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)))
+               return -ENOTSUPP;
+
+       /* make sure we are in station mode and connected */
+       if ((priv->bss_type != MWIFIEX_BSS_TYPE_STA) || !priv->media_connected)
+               return -ENOTSUPP;
+
+       priv->sta_params = params;
+
+       ret = mwifiex_tdls_oper(priv, mac, MWIFIEX_TDLS_CONFIG_LINK);
+       priv->sta_params = NULL;
+
+       return ret;
+}
+
 /* station cfg80211 operations */
 static struct cfg80211_ops mwifiex_cfg80211_ops = {
        .add_virtual_intf = mwifiex_add_virtual_intf,
@@ -2630,6 +2793,10 @@ static struct cfg80211_ops mwifiex_cfg80211_ops = {
        .set_wakeup = mwifiex_cfg80211_set_wakeup,
 #endif
        .set_coalesce = mwifiex_cfg80211_set_coalesce,
+       .tdls_mgmt = mwifiex_cfg80211_tdls_mgmt,
+       .tdls_oper = mwifiex_cfg80211_tdls_oper,
+       .add_station = mwifiex_cfg80211_add_station,
+       .change_station = mwifiex_cfg80211_change_station,
 };
 
 #ifdef CONFIG_PM
@@ -2715,6 +2882,11 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter)
                        WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD |
                        WIPHY_FLAG_AP_UAPSD |
                        WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
+
+       if (ISSUPP_TDLS_ENABLED(adapter->fw_cap_info))
+               wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS |
+                               WIPHY_FLAG_TDLS_EXTERNAL_SETUP;
+
        wiphy->regulatory_flags |=
                        REGULATORY_CUSTOM_REG |
                        REGULATORY_STRICT_REG;
index 9eefacb..2c3226b 100644 (file)
@@ -71,6 +71,95 @@ u16 region_code_index[MWIFIEX_MAX_REGION_CODE] = { 0x10, 0x20, 0x30,
 
 static u8 supported_rates_n[N_SUPPORTED_RATES] = { 0x02, 0x04, 0 };
 
+/* For every mcs_rate line, the first 8 bytes are for stream 1x1,
+ * and all 16 bytes are for stream 2x2.
+ */
+static const u16 mcs_rate[4][16] = {
+       /* LGI 40M */
+       { 0x1b, 0x36, 0x51, 0x6c, 0xa2, 0xd8, 0xf3, 0x10e,
+         0x36, 0x6c, 0xa2, 0xd8, 0x144, 0x1b0, 0x1e6, 0x21c },
+
+       /* SGI 40M */
+       { 0x1e, 0x3c, 0x5a, 0x78, 0xb4, 0xf0, 0x10e, 0x12c,
+         0x3c, 0x78, 0xb4, 0xf0, 0x168, 0x1e0, 0x21c, 0x258 },
+
+       /* LGI 20M */
+       { 0x0d, 0x1a, 0x27, 0x34, 0x4e, 0x68, 0x75, 0x82,
+         0x1a, 0x34, 0x4e, 0x68, 0x9c, 0xd0, 0xea, 0x104 },
+
+       /* SGI 20M */
+       { 0x0e, 0x1c, 0x2b, 0x39, 0x56, 0x73, 0x82, 0x90,
+         0x1c, 0x39, 0x56, 0x73, 0xad, 0xe7, 0x104, 0x120 }
+};
+
+/* AC rates */
+static const u16 ac_mcs_rate_nss1[8][10] = {
+       /* LG 160M */
+       { 0x75, 0xEA, 0x15F, 0x1D4, 0x2BE, 0x3A8, 0x41D,
+         0x492, 0x57C, 0x618 },
+
+       /* SG 160M */
+       { 0x82, 0x104, 0x186, 0x208, 0x30C, 0x410, 0x492,
+         0x514, 0x618, 0x6C6 },
+
+       /* LG 80M */
+       { 0x3B, 0x75, 0xB0, 0xEA, 0x15F, 0x1D4, 0x20F,
+         0x249, 0x2BE, 0x30C },
+
+       /* SG 80M */
+       { 0x41, 0x82, 0xC3, 0x104, 0x186, 0x208, 0x249,
+         0x28A, 0x30C, 0x363 },
+
+       /* LG 40M */
+       { 0x1B, 0x36, 0x51, 0x6C, 0xA2, 0xD8, 0xF3,
+         0x10E, 0x144, 0x168 },
+
+       /* SG 40M */
+       { 0x1E, 0x3C, 0x5A, 0x78, 0xB4, 0xF0, 0x10E,
+         0x12C, 0x168, 0x190 },
+
+       /* LG 20M */
+       { 0xD, 0x1A, 0x27, 0x34, 0x4E, 0x68, 0x75, 0x82, 0x9C, 0x00 },
+
+       /* SG 20M */
+       { 0xF, 0x1D, 0x2C, 0x3A, 0x57, 0x74, 0x82, 0x91, 0xAE, 0x00 },
+};
+
+/* NSS2 note: the value in the table is 2 multiplier of the actual rate */
+static const u16 ac_mcs_rate_nss2[8][10] = {
+       /* LG 160M */
+       { 0xEA, 0x1D4, 0x2BE, 0x3A8, 0x57C, 0x750, 0x83A,
+         0x924, 0xAF8, 0xC30 },
+
+       /* SG 160M */
+       { 0x104, 0x208, 0x30C, 0x410, 0x618, 0x820, 0x924,
+         0xA28, 0xC30, 0xD8B },
+
+       /* LG 80M */
+       { 0x75, 0xEA, 0x15F, 0x1D4, 0x2BE, 0x3A8, 0x41D,
+         0x492, 0x57C, 0x618 },
+
+       /* SG 80M */
+       { 0x82, 0x104, 0x186, 0x208, 0x30C, 0x410, 0x492,
+         0x514, 0x618, 0x6C6 },
+
+       /* LG 40M */
+       { 0x36, 0x6C, 0xA2, 0xD8, 0x144, 0x1B0, 0x1E6,
+         0x21C, 0x288, 0x2D0 },
+
+       /* SG 40M */
+       { 0x3C, 0x78, 0xB4, 0xF0, 0x168, 0x1E0, 0x21C,
+         0x258, 0x2D0, 0x320 },
+
+       /* LG 20M */
+       { 0x1A, 0x34, 0x4A, 0x68, 0x9C, 0xD0, 0xEA, 0x104,
+         0x138, 0x00 },
+
+       /* SG 20M */
+       { 0x1D, 0x3A, 0x57, 0x74, 0xAE, 0xE6, 0x104, 0x121,
+         0x15B, 0x00 },
+};
+
 struct region_code_mapping {
        u8 code;
        u8 region[IEEE80211_COUNTRY_STRING_LEN];
@@ -109,95 +198,6 @@ u8 *mwifiex_11d_code_2_region(u8 code)
 u32 mwifiex_index_to_acs_data_rate(struct mwifiex_private *priv,
                                   u8 index, u8 ht_info)
 {
-       /*
-        * For every mcs_rate line, the first 8 bytes are for stream 1x1,
-        * and all 16 bytes are for stream 2x2.
-        */
-       u16  mcs_rate[4][16] = {
-               /* LGI 40M */
-               { 0x1b, 0x36, 0x51, 0x6c, 0xa2, 0xd8, 0xf3, 0x10e,
-                 0x36, 0x6c, 0xa2, 0xd8, 0x144, 0x1b0, 0x1e6, 0x21c },
-
-               /* SGI 40M */
-               { 0x1e, 0x3c, 0x5a, 0x78, 0xb4, 0xf0, 0x10e, 0x12c,
-                 0x3c, 0x78, 0xb4, 0xf0, 0x168, 0x1e0, 0x21c, 0x258 },
-
-               /* LGI 20M */
-               { 0x0d, 0x1a, 0x27, 0x34, 0x4e, 0x68, 0x75, 0x82,
-                 0x1a, 0x34, 0x4e, 0x68, 0x9c, 0xd0, 0xea, 0x104 },
-
-               /* SGI 20M */
-               { 0x0e, 0x1c, 0x2b, 0x39, 0x56, 0x73, 0x82, 0x90,
-                 0x1c, 0x39, 0x56, 0x73, 0xad, 0xe7, 0x104, 0x120 }
-       };
-       /* AC rates */
-       u16 ac_mcs_rate_nss1[8][10] = {
-               /* LG 160M */
-               { 0x75, 0xEA, 0x15F, 0x1D4, 0x2BE, 0x3A8, 0x41D,
-                 0x492, 0x57C, 0x618 },
-
-               /* SG 160M */
-               { 0x82, 0x104, 0x186, 0x208, 0x30C, 0x410, 0x492,
-                 0x514, 0x618, 0x6C6 },
-
-               /* LG 80M */
-               { 0x3B, 0x75, 0xB0, 0xEA, 0x15F, 0x1D4, 0x20F,
-                 0x249, 0x2BE, 0x30C },
-
-               /* SG 80M */
-               { 0x41, 0x82, 0xC3, 0x104, 0x186, 0x208, 0x249,
-                 0x28A, 0x30C, 0x363 },
-
-               /* LG 40M */
-               { 0x1B, 0x36, 0x51, 0x6C, 0xA2, 0xD8, 0xF3,
-                 0x10E, 0x144, 0x168 },
-
-               /* SG 40M */
-               { 0x1E, 0x3C, 0x5A, 0x78, 0xB4, 0xF0, 0x10E,
-                 0x12C, 0x168, 0x190 },
-
-               /* LG 20M */
-               { 0xD, 0x1A, 0x27, 0x34, 0x4E, 0x68, 0x75, 0x82, 0x9C, 0x00 },
-
-               /* SG 20M */
-               { 0xF, 0x1D, 0x2C, 0x3A, 0x57, 0x74, 0x82, 0x91, 0xAE, 0x00 },
-       };
-       /* NSS2 note: the value in the table is 2 multiplier of the actual
-        * rate
-        */
-       u16 ac_mcs_rate_nss2[8][10] = {
-               /* LG 160M */
-               { 0xEA, 0x1D4, 0x2BE, 0x3A8, 0x57C, 0x750, 0x83A,
-                 0x924, 0xAF8, 0xC30 },
-
-               /* SG 160M */
-               { 0x104, 0x208, 0x30C, 0x410, 0x618, 0x820, 0x924,
-                 0xA28, 0xC30, 0xD8B },
-
-               /* LG 80M */
-               { 0x75, 0xEA, 0x15F, 0x1D4, 0x2BE, 0x3A8, 0x41D,
-                 0x492, 0x57C, 0x618 },
-
-               /* SG 80M */
-               { 0x82, 0x104, 0x186, 0x208, 0x30C, 0x410, 0x492,
-                 0x514, 0x618, 0x6C6 },
-
-               /* LG 40M */
-               { 0x36, 0x6C, 0xA2, 0xD8, 0x144, 0x1B0, 0x1E6,
-                 0x21C, 0x288, 0x2D0 },
-
-               /* SG 40M */
-               { 0x3C, 0x78, 0xB4, 0xF0, 0x168, 0x1E0, 0x21C,
-                 0x258, 0x2D0, 0x320 },
-
-               /* LG 20M */
-               { 0x1A, 0x34, 0x4A, 0x68, 0x9C, 0xD0, 0xEA, 0x104,
-                 0x138, 0x00 },
-
-               /* SG 20M */
-               { 0x1D, 0x3A, 0x57, 0x74, 0xAE, 0xE6, 0x104, 0x121,
-                 0x15B, 0x00 },
-       };
        u32 rate = 0;
        u8 mcs_index = 0;
        u8 bw = 0;
@@ -252,26 +252,6 @@ u32 mwifiex_index_to_acs_data_rate(struct mwifiex_private *priv,
 u32 mwifiex_index_to_data_rate(struct mwifiex_private *priv,
                               u8 index, u8 ht_info)
 {
-       /* For every mcs_rate line, the first 8 bytes are for stream 1x1,
-        * and all 16 bytes are for stream 2x2.
-        */
-       u16  mcs_rate[4][16] = {
-               /* LGI 40M */
-               { 0x1b, 0x36, 0x51, 0x6c, 0xa2, 0xd8, 0xf3, 0x10e,
-                 0x36, 0x6c, 0xa2, 0xd8, 0x144, 0x1b0, 0x1e6, 0x21c },
-
-               /* SGI 40M */
-               { 0x1e, 0x3c, 0x5a, 0x78, 0xb4, 0xf0, 0x10e, 0x12c,
-                 0x3c, 0x78, 0xb4, 0xf0, 0x168, 0x1e0, 0x21c, 0x258 },
-
-               /* LGI 20M */
-               { 0x0d, 0x1a, 0x27, 0x34, 0x4e, 0x68, 0x75, 0x82,
-                 0x1a, 0x34, 0x4e, 0x68, 0x9c, 0xd0, 0xea, 0x104 },
-
-               /* SGI 20M */
-               { 0x0e, 0x1c, 0x2b, 0x39, 0x56, 0x73, 0x82, 0x90,
-                 0x1c, 0x39, 0x56, 0x73, 0xad, 0xe7, 0x104, 0x120 }
-       };
        u32 mcs_num_supp =
                (priv->adapter->hw_dev_mcs_support == HT_STREAM_2X2) ? 16 : 8;
        u32 rate;
@@ -458,7 +438,6 @@ u32 mwifiex_get_supported_rates(struct mwifiex_private *priv, u8 *rates)
                        break;
                case BAND_G:
                case BAND_G | BAND_GN:
-               case BAND_G | BAND_GN | BAND_GAC:
                        dev_dbg(adapter->dev, "info: infra band=%d "
                                "supported_rates_g\n", adapter->config_bands);
                        k = mwifiex_copy_rates(rates, k, supported_rates_g,
@@ -469,10 +448,7 @@ u32 mwifiex_get_supported_rates(struct mwifiex_private *priv, u8 *rates)
                case BAND_A | BAND_B:
                case BAND_A | BAND_B | BAND_G | BAND_GN | BAND_AN:
                case BAND_A | BAND_B | BAND_G | BAND_GN | BAND_AN | BAND_AAC:
-               case BAND_A | BAND_B | BAND_G | BAND_GN | BAND_AN |
-                    BAND_AAC | BAND_GAC:
                case BAND_B | BAND_G | BAND_GN:
-               case BAND_B | BAND_G | BAND_GN | BAND_GAC:
                        dev_dbg(adapter->dev, "info: infra band=%d "
                                "supported_rates_bg\n", adapter->config_bands);
                        k = mwifiex_copy_rates(rates, k, supported_rates_bg,
@@ -496,7 +472,6 @@ u32 mwifiex_get_supported_rates(struct mwifiex_private *priv, u8 *rates)
                                               sizeof(supported_rates_a));
                        break;
                case BAND_GN:
-               case BAND_GN | BAND_GAC:
                        dev_dbg(adapter->dev, "info: infra band=%d "
                                "supported_rates_n\n", adapter->config_bands);
                        k = mwifiex_copy_rates(rates, k, supported_rates_n,
index 1ddc8b2..2154460 100644 (file)
@@ -595,7 +595,8 @@ int mwifiex_send_cmd_async(struct mwifiex_private *priv, uint16_t cmd_no,
        }
 
        /* Send command */
-       if (cmd_no == HostCmd_CMD_802_11_SCAN) {
+       if (cmd_no == HostCmd_CMD_802_11_SCAN ||
+           cmd_no == HostCmd_CMD_802_11_SCAN_EXT) {
                mwifiex_queue_scan_cmd(priv, cmd_node);
        } else {
                mwifiex_insert_cmd_to_pending_q(adapter, cmd_node, true);
@@ -1454,7 +1455,10 @@ int mwifiex_ret_get_hw_spec(struct mwifiex_private *priv,
 {
        struct host_cmd_ds_get_hw_spec *hw_spec = &resp->params.hw_spec;
        struct mwifiex_adapter *adapter = priv->adapter;
-       int i;
+       struct mwifiex_ie_types_header *tlv;
+       struct hw_spec_fw_api_rev *api_rev;
+       u16 resp_size, api_id;
+       int i, left_len, parsed_len = 0;
 
        adapter->fw_cap_info = le32_to_cpu(hw_spec->fw_cap_info);
 
@@ -1498,8 +1502,10 @@ int mwifiex_ret_get_hw_spec(struct mwifiex_private *priv,
                /* Copy 11AC cap */
                adapter->hw_dot_11ac_dev_cap =
                                        le32_to_cpu(hw_spec->dot_11ac_dev_cap);
-               adapter->usr_dot_11ac_dev_cap_bg = adapter->hw_dot_11ac_dev_cap;
-               adapter->usr_dot_11ac_dev_cap_a = adapter->hw_dot_11ac_dev_cap;
+               adapter->usr_dot_11ac_dev_cap_bg = adapter->hw_dot_11ac_dev_cap
+                                       & ~MWIFIEX_DEF_11AC_CAP_BF_RESET_MASK;
+               adapter->usr_dot_11ac_dev_cap_a = adapter->hw_dot_11ac_dev_cap
+                                       & ~MWIFIEX_DEF_11AC_CAP_BF_RESET_MASK;
 
                /* Copy 11AC mcs */
                adapter->hw_dot_11ac_mcs_support =
@@ -1510,6 +1516,46 @@ int mwifiex_ret_get_hw_spec(struct mwifiex_private *priv,
                adapter->is_hw_11ac_capable = false;
        }
 
+       resp_size = le16_to_cpu(resp->size) - S_DS_GEN;
+       if (resp_size > sizeof(struct host_cmd_ds_get_hw_spec)) {
+               /* we have variable HW SPEC information */
+               left_len = resp_size - sizeof(struct host_cmd_ds_get_hw_spec);
+               while (left_len > sizeof(struct mwifiex_ie_types_header)) {
+                       tlv = (void *)&hw_spec->tlvs + parsed_len;
+                       switch (le16_to_cpu(tlv->type)) {
+                       case TLV_TYPE_FW_API_REV:
+                               api_rev = (struct hw_spec_fw_api_rev *)tlv;
+                               api_id = le16_to_cpu(api_rev->api_id);
+                               switch (api_id) {
+                               case KEY_API_VER_ID:
+                                       adapter->fw_key_api_major_ver =
+                                                       api_rev->major_ver;
+                                       adapter->fw_key_api_minor_ver =
+                                                       api_rev->minor_ver;
+                                       dev_dbg(adapter->dev,
+                                               "fw_key_api v%d.%d\n",
+                                               adapter->fw_key_api_major_ver,
+                                               adapter->fw_key_api_minor_ver);
+                                       break;
+                               default:
+                                       dev_warn(adapter->dev,
+                                                "Unknown FW api_id: %d\n",
+                                                api_id);
+                                       break;
+                               }
+                               break;
+                       default:
+                               dev_warn(adapter->dev,
+                                        "Unknown GET_HW_SPEC TLV type: %#x\n",
+                                        le16_to_cpu(tlv->type));
+                               break;
+                       }
+                       parsed_len += le16_to_cpu(tlv->len) +
+                                     sizeof(struct mwifiex_ie_types_header);
+                       left_len -= parsed_len;
+               }
+       }
+
        dev_dbg(adapter->dev, "info: GET_HW_SPEC: fw_release_number- %#x\n",
                adapter->fw_release_number);
        dev_dbg(adapter->dev, "info: GET_HW_SPEC: permanent addr: %pM\n",
index 3a21bd0..e7b3e16 100644 (file)
 
 #define MWIFIEX_BUF_FLAG_REQUEUED_PKT      BIT(0)
 #define MWIFIEX_BUF_FLAG_BRIDGED_PKT      BIT(1)
+#define MWIFIEX_BUF_FLAG_TDLS_PKT         BIT(2)
 
 #define MWIFIEX_BRIDGED_PKTS_THR_HIGH      1024
 #define MWIFIEX_BRIDGED_PKTS_THR_LOW        128
 
+#define MWIFIEX_TDLS_DISABLE_LINK             0x00
+#define MWIFIEX_TDLS_ENABLE_LINK              0x01
+#define MWIFIEX_TDLS_CREATE_LINK              0x02
+#define MWIFIEX_TDLS_CONFIG_LINK              0x03
+
 enum mwifiex_bss_type {
        MWIFIEX_BSS_TYPE_STA = 0,
        MWIFIEX_BSS_TYPE_UAP = 1,
@@ -92,6 +98,23 @@ enum mwifiex_bss_role {
        MWIFIEX_BSS_ROLE_ANY = 0xff,
 };
 
+enum mwifiex_tdls_status {
+       TDLS_NOT_SETUP = 0,
+       TDLS_SETUP_INPROGRESS,
+       TDLS_SETUP_COMPLETE,
+       TDLS_SETUP_FAILURE,
+       TDLS_LINK_TEARDOWN,
+};
+
+enum mwifiex_tdls_error_code {
+       TDLS_ERR_NO_ERROR = 0,
+       TDLS_ERR_INTERNAL_ERROR,
+       TDLS_ERR_MAX_LINKS_EST,
+       TDLS_ERR_LINK_EXISTS,
+       TDLS_ERR_LINK_NONEXISTENT,
+       TDLS_ERR_PEER_STA_UNREACHABLE = 25,
+};
+
 #define BSS_ROLE_BIT_MASK    BIT(0)
 
 #define GET_BSS_ROLE(priv)   ((priv)->bss_role & BSS_ROLE_BIT_MASK)
index 5fa932d..aa8abef 100644 (file)
@@ -50,21 +50,23 @@ struct tx_packet_hdr {
 #define HOSTCMD_SUPPORTED_RATES         14
 #define N_SUPPORTED_RATES               3
 #define ALL_802_11_BANDS           (BAND_A | BAND_B | BAND_G | BAND_GN | \
-                                   BAND_AN | BAND_GAC | BAND_AAC)
+                                   BAND_AN | BAND_AAC)
 
 #define FW_MULTI_BANDS_SUPPORT  (BIT(8) | BIT(9) | BIT(10) | BIT(11) | \
-                                BIT(12) | BIT(13))
+                                BIT(13))
 #define IS_SUPPORT_MULTI_BANDS(adapter)        \
        (adapter->fw_cap_info & FW_MULTI_BANDS_SUPPORT)
 
-/* shift bit 12 and bit 13 in fw_cap_info from the firmware to bit 13 and 14
- * for 11ac so that bit 11 is for GN, bit 12 for AN, bit 13 for GAC, and bit
- * bit 14 for AAC, in order to be compatible with the band capability
- * defined in the driver after right shift of 8 bits.
+/* bit 13: 11ac BAND_AAC
+ * bit 12: reserved for lab testing, will be reused for BAND_AN
+ * bit 11: 11n  BAND_GN
+ * bit 10: 11a  BAND_A
+ * bit 9: 11g   BAND_G
+ * bit 8: 11b   BAND_B
+ * Map these bits to band capability by right shifting 8 bits.
  */
 #define GET_FW_DEFAULT_BANDS(adapter)  \
-           (((((adapter->fw_cap_info & 0x3000) << 1) | \
-              (adapter->fw_cap_info & ~0xF000)) >> 8) & \
+           (((adapter->fw_cap_info & 0x2f00) >> 8) & \
             ALL_802_11_BANDS)
 
 #define HostCmd_WEP_KEY_INDEX_MASK              0x3fff
@@ -77,12 +79,21 @@ enum KEY_TYPE_ID {
        KEY_TYPE_ID_WAPI,
        KEY_TYPE_ID_AES_CMAC,
 };
+
+#define WPA_PN_SIZE            8
+#define KEY_PARAMS_FIXED_LEN   10
+#define KEY_INDEX_MASK         0xf
+#define FW_KEY_API_VER_MAJOR_V2        2
+
 #define KEY_MCAST      BIT(0)
 #define KEY_UNICAST    BIT(1)
 #define KEY_ENABLED    BIT(2)
+#define KEY_DEFAULT    BIT(3)
+#define KEY_TX_KEY     BIT(4)
+#define KEY_RX_KEY     BIT(5)
 #define KEY_IGTK       BIT(10)
 
-#define WAPI_KEY_LEN                   50
+#define WAPI_KEY_LEN                   (WLAN_KEY_LEN_SMS4 + PN_LEN + 2)
 
 #define MAX_POLL_TRIES                 100
 #define MAX_FIRMWARE_POLL_TRIES                        100
@@ -130,6 +141,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
 #define TLV_TYPE_RSSI_HIGH          (PROPRIETARY_TLV_BASE_ID + 22)
 #define TLV_TYPE_AUTH_TYPE          (PROPRIETARY_TLV_BASE_ID + 31)
 #define TLV_TYPE_STA_MAC_ADDR       (PROPRIETARY_TLV_BASE_ID + 32)
+#define TLV_TYPE_BSSID              (PROPRIETARY_TLV_BASE_ID + 35)
 #define TLV_TYPE_CHANNELBANDLIST    (PROPRIETARY_TLV_BASE_ID + 42)
 #define TLV_TYPE_UAP_BEACON_PERIOD  (PROPRIETARY_TLV_BASE_ID + 44)
 #define TLV_TYPE_UAP_DTIM_PERIOD    (PROPRIETARY_TLV_BASE_ID + 45)
@@ -144,6 +156,8 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
 #define TLV_TYPE_RATE_DROP_CONTROL  (PROPRIETARY_TLV_BASE_ID + 82)
 #define TLV_TYPE_RATE_SCOPE         (PROPRIETARY_TLV_BASE_ID + 83)
 #define TLV_TYPE_POWER_GROUP        (PROPRIETARY_TLV_BASE_ID + 84)
+#define TLV_TYPE_BSS_SCAN_RSP       (PROPRIETARY_TLV_BASE_ID + 86)
+#define TLV_TYPE_BSS_SCAN_INFO      (PROPRIETARY_TLV_BASE_ID + 87)
 #define TLV_TYPE_UAP_RETRY_LIMIT    (PROPRIETARY_TLV_BASE_ID + 93)
 #define TLV_TYPE_WAPI_IE            (PROPRIETARY_TLV_BASE_ID + 94)
 #define TLV_TYPE_UAP_MGMT_FRAME     (PROPRIETARY_TLV_BASE_ID + 104)
@@ -154,6 +168,8 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
 #define TLV_TYPE_PWK_CIPHER         (PROPRIETARY_TLV_BASE_ID + 145)
 #define TLV_TYPE_GWK_CIPHER         (PROPRIETARY_TLV_BASE_ID + 146)
 #define TLV_TYPE_COALESCE_RULE      (PROPRIETARY_TLV_BASE_ID + 154)
+#define TLV_TYPE_KEY_PARAM_V2       (PROPRIETARY_TLV_BASE_ID + 156)
+#define TLV_TYPE_FW_API_REV         (PROPRIETARY_TLV_BASE_ID + 199)
 
 #define MWIFIEX_TX_DATA_BUF_SIZE_2K        2048
 
@@ -176,11 +192,14 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
 #define MWIFIEX_TX_DATA_BUF_SIZE_8K        8192
 
 #define ISSUPP_11NENABLED(FwCapInfo) (FwCapInfo & BIT(11))
+#define ISSUPP_TDLS_ENABLED(FwCapInfo) (FwCapInfo & BIT(14))
 
 #define MWIFIEX_DEF_HT_CAP     (IEEE80211_HT_CAP_DSSSCCK40 | \
                                 (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) | \
                                 IEEE80211_HT_CAP_SM_PS)
 
+#define MWIFIEX_DEF_11N_TX_BF_CAP      0x09E1E008
+
 #define MWIFIEX_DEF_AMPDU      IEEE80211_HT_AMPDU_PARM_FACTOR
 
 /* dev_cap bitmap
@@ -204,6 +223,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
 #define ISSUPP_GREENFIELD(Dot11nDevCap) (Dot11nDevCap & BIT(29))
 #define ISENABLED_40MHZ_INTOLERANT(Dot11nDevCap) (Dot11nDevCap & BIT(8))
 #define ISSUPP_RXLDPC(Dot11nDevCap) (Dot11nDevCap & BIT(22))
+#define ISSUPP_BEAMFORMING(Dot11nDevCap) (Dot11nDevCap & BIT(30))
 
 /* httxcfg bitmap
  * 0           reserved
@@ -226,17 +246,24 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
 
 /* HW_SPEC fw_cap_info */
 
-#define ISSUPP_11ACENABLED(fw_cap_info) (fw_cap_info & (BIT(12)|BIT(13)))
+#define ISSUPP_11ACENABLED(fw_cap_info) (fw_cap_info & BIT(13))
 
 #define GET_VHTCAP_CHWDSET(vht_cap_info)    ((vht_cap_info >> 2) & 0x3)
 #define GET_VHTNSSMCS(mcs_mapset, nss) ((mcs_mapset >> (2 * (nss - 1))) & 0x3)
 #define SET_VHTNSSMCS(mcs_mapset, nss, value) (mcs_mapset |= (value & 0x3) << \
                                              (2 * (nss - 1)))
-#define NO_NSS_SUPPORT         0x3
-
 #define GET_DEVTXMCSMAP(dev_mcs_map)      (dev_mcs_map >> 16)
 #define GET_DEVRXMCSMAP(dev_mcs_map)      (dev_mcs_map & 0xFFFF)
 
+/* Clear SU Beanformer, MU beanformer, MU beanformee and
+ * sounding dimensions bits
+ */
+#define MWIFIEX_DEF_11AC_CAP_BF_RESET_MASK \
+                       (IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE | \
+                        IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE | \
+                        IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE | \
+                        IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK)
+
 #define MOD_CLASS_HR_DSSS       0x03
 #define MOD_CLASS_OFDM          0x07
 #define MOD_CLASS_HT            0x08
@@ -295,10 +322,12 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
 #define HostCmd_CMD_CAU_REG_ACCESS                    0x00ed
 #define HostCmd_CMD_SET_BSS_MODE                      0x00f7
 #define HostCmd_CMD_PCIE_DESC_DETAILS                 0x00fa
+#define HostCmd_CMD_802_11_SCAN_EXT                   0x0107
 #define HostCmd_CMD_COALESCE_CFG                      0x010a
 #define HostCmd_CMD_MGMT_FRAME_REG                    0x010c
 #define HostCmd_CMD_REMAIN_ON_CHAN                    0x010d
 #define HostCmd_CMD_11AC_CFG                         0x0112
+#define HostCmd_CMD_TDLS_OPER                         0x0122
 
 #define PROTOCOL_NO_SECURITY        0x01
 #define PROTOCOL_STATIC_WEP         0x02
@@ -440,6 +469,7 @@ enum P2P_MODES {
 #define EVENT_UAP_MIC_COUNTERMEASURES   0x0000004c
 #define EVENT_HOSTWAKE_STAIE           0x0000004d
 #define EVENT_CHANNEL_SWITCH_ANN        0x00000050
+#define EVENT_EXT_SCAN_REPORT           0x00000058
 #define EVENT_REMAIN_ON_CHAN_EXPIRED    0x0000005f
 
 #define EVENT_ID_MASK                   0xffff
@@ -468,6 +498,10 @@ enum P2P_MODES {
 #define MWIFIEX_CRITERIA_UNICAST       BIT(1)
 #define MWIFIEX_CRITERIA_MULTICAST     BIT(3)
 
+#define ACT_TDLS_DELETE            0x00
+#define ACT_TDLS_CREATE            0x01
+#define ACT_TDLS_CONFIG            0x02
+
 struct mwifiex_ie_types_header {
        __le16 type;
        __le16 len;
@@ -480,6 +514,7 @@ struct mwifiex_ie_types_data {
 
 #define MWIFIEX_TxPD_POWER_MGMT_NULL_PACKET 0x01
 #define MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET 0x08
+#define MWIFIEX_TXPD_FLAGS_TDLS_PACKET      0x10
 
 struct txpd {
        u8 bss_type;
@@ -676,6 +711,56 @@ struct mwifiex_cmac_param {
        u8 key[WLAN_KEY_LEN_AES_CMAC];
 } __packed;
 
+struct mwifiex_wep_param {
+       __le16 key_len;
+       u8 key[WLAN_KEY_LEN_WEP104];
+} __packed;
+
+struct mwifiex_tkip_param {
+       u8 pn[WPA_PN_SIZE];
+       __le16 key_len;
+       u8 key[WLAN_KEY_LEN_TKIP];
+} __packed;
+
+struct mwifiex_aes_param {
+       u8 pn[WPA_PN_SIZE];
+       __le16 key_len;
+       u8 key[WLAN_KEY_LEN_CCMP];
+} __packed;
+
+struct mwifiex_wapi_param {
+       u8 pn[PN_LEN];
+       __le16 key_len;
+       u8 key[WLAN_KEY_LEN_SMS4];
+} __packed;
+
+struct mwifiex_cmac_aes_param {
+       u8 ipn[IGTK_PN_LEN];
+       __le16 key_len;
+       u8 key[WLAN_KEY_LEN_AES_CMAC];
+} __packed;
+
+struct mwifiex_ie_type_key_param_set_v2 {
+       __le16 type;
+       __le16 len;
+       u8 mac_addr[ETH_ALEN];
+       u8 key_idx;
+       u8 key_type;
+       __le16 key_info;
+       union {
+               struct mwifiex_wep_param wep;
+               struct mwifiex_tkip_param tkip;
+               struct mwifiex_aes_param aes;
+               struct mwifiex_wapi_param wapi;
+               struct mwifiex_cmac_aes_param cmac_aes;
+       } key_params;
+} __packed;
+
+struct host_cmd_ds_802_11_key_material_v2 {
+       __le16 action;
+       struct mwifiex_ie_type_key_param_set_v2 key_param_set;
+} __packed;
+
 struct host_cmd_ds_802_11_key_material {
        __le16 action;
        struct mwifiex_ie_type_key_param_set key_param_set;
@@ -727,6 +812,17 @@ struct host_cmd_ds_802_11_ps_mode_enh {
        } params;
 } __packed;
 
+enum FW_API_VER_ID {
+       KEY_API_VER_ID = 1,
+};
+
+struct hw_spec_fw_api_rev {
+       struct mwifiex_ie_types_header header;
+       __le16 api_id;
+       u8 major_ver;
+       u8 minor_ver;
+} __packed;
+
 struct host_cmd_ds_get_hw_spec {
        __le16 hw_if_version;
        __le16 version;
@@ -748,6 +844,7 @@ struct host_cmd_ds_get_hw_spec {
        __le32 reserved_6;
        __le32 dot_11ac_dev_cap;
        __le32 dot_11ac_mcs_support;
+       u8 tlvs[0];
 } __packed;
 
 struct host_cmd_ds_802_11_rssi_info {
@@ -1047,14 +1144,28 @@ struct host_cmd_ds_rf_ant_siso {
        __le16 ant_mode;
 };
 
-struct mwifiex_bcn_param {
-       u8 bssid[ETH_ALEN];
-       u8 rssi;
+struct host_cmd_ds_tdls_oper {
+       __le16 tdls_action;
+       __le16 reason;
+       u8 peer_mac[ETH_ALEN];
+} __packed;
+
+struct mwifiex_fixed_bcn_param {
        __le64 timestamp;
        __le16 beacon_period;
        __le16 cap_info_bitmap;
 } __packed;
 
+struct mwifiex_event_scan_result {
+       __le16 event_id;
+       u8 bss_index;
+       u8 bss_type;
+       u8 more_event;
+       u8 reserved[3];
+       __le16 buf_size;
+       u8 num_of_set;
+} __packed;
+
 #define MWIFIEX_USER_SCAN_CHAN_MAX             50
 
 #define MWIFIEX_MAX_SSID_LIST_LENGTH         10
@@ -1124,6 +1235,28 @@ struct host_cmd_ds_802_11_scan_rsp {
        u8 bss_desc_and_tlv_buffer[1];
 } __packed;
 
+struct host_cmd_ds_802_11_scan_ext {
+       u32   reserved;
+       u8    tlv_buffer[1];
+} __packed;
+
+struct mwifiex_ie_types_bss_scan_rsp {
+       struct mwifiex_ie_types_header header;
+       u8 bssid[ETH_ALEN];
+       u8 frame_body[1];
+} __packed;
+
+struct mwifiex_ie_types_bss_scan_info {
+       struct mwifiex_ie_types_header header;
+       __le16 rssi;
+       __le16 anpi;
+       u8 cca_busy_fraction;
+       u8 radio_type;
+       u8 channel;
+       u8 reserved;
+       __le64 tsf;
+} __packed;
+
 struct host_cmd_ds_802_11_bg_scan_query {
        u8 flush;
 } __packed;
@@ -1296,6 +1429,11 @@ struct mwifiex_ie_types_vhtcap {
        struct ieee80211_vht_cap vht_cap;
 } __packed;
 
+struct mwifiex_ie_types_aid {
+       struct mwifiex_ie_types_header header;
+       __le16 aid;
+} __packed;
+
 struct mwifiex_ie_types_oper_mode_ntf {
        struct mwifiex_ie_types_header header;
        u8 oper_mode;
@@ -1331,6 +1469,11 @@ struct mwifiex_ie_types_extcap {
        u8 ext_capab[0];
 } __packed;
 
+struct mwifiex_ie_types_qos_info {
+       struct mwifiex_ie_types_header header;
+       u8 qos_info;
+} __packed;
+
 struct host_cmd_ds_mac_reg_access {
        __le16 action;
        __le16 offset;
@@ -1441,6 +1584,11 @@ struct host_cmd_tlv_rates {
        u8 rates[0];
 } __packed;
 
+struct mwifiex_ie_types_bssid_list {
+       struct mwifiex_ie_types_header header;
+       u8 bssid[ETH_ALEN];
+} __packed;
+
 struct host_cmd_tlv_bcast_ssid {
        struct mwifiex_ie_types_header header;
        u8 bcast_ctl;
@@ -1634,6 +1782,7 @@ struct host_cmd_ds_command {
                struct host_cmd_ds_802_11_ps_mode_enh psmode_enh;
                struct host_cmd_ds_802_11_hs_cfg_enh opt_hs_cfg;
                struct host_cmd_ds_802_11_scan scan;
+               struct host_cmd_ds_802_11_scan_ext ext_scan;
                struct host_cmd_ds_802_11_scan_rsp scan_resp;
                struct host_cmd_ds_802_11_bg_scan_query bg_scan_query;
                struct host_cmd_ds_802_11_bg_scan_query_rsp bg_scan_query_resp;
@@ -1653,6 +1802,7 @@ struct host_cmd_ds_command {
                struct host_cmd_ds_11n_cfg htcfg;
                struct host_cmd_ds_wmm_get_status get_wmm_status;
                struct host_cmd_ds_802_11_key_material key_material;
+               struct host_cmd_ds_802_11_key_material_v2 key_material_v2;
                struct host_cmd_ds_version_ext verext;
                struct host_cmd_ds_mgmt_frame_reg reg_mask;
                struct host_cmd_ds_remain_on_chan roc_cfg;
@@ -1671,6 +1821,7 @@ struct host_cmd_ds_command {
                struct host_cmd_ds_sta_deauth sta_deauth;
                struct host_cmd_11ac_vht_cfg vht_cfg;
                struct host_cmd_ds_coalesce_cfg coalesce_cfg;
+               struct host_cmd_ds_tdls_oper tdls_oper;
        } params;
 } __packed;
 
index 1d0a817..a4cd2cb 100644 (file)
@@ -137,6 +137,7 @@ int mwifiex_init_priv(struct mwifiex_private *priv)
        priv->csa_expire_time = 0;
        priv->del_list_idx = 0;
        priv->hs2_enabled = false;
+       memcpy(priv->tos_to_tid_inv, tos_to_tid_inv, MAX_NUM_TID);
 
        return mwifiex_add_bss_prio_tbl(priv);
 }
@@ -281,6 +282,9 @@ static void mwifiex_init_adapter(struct mwifiex_adapter *adapter)
        adapter->arp_filter_size = 0;
        adapter->max_mgmt_ie_index = MAX_MGMT_IE_INDEX;
        adapter->empty_tx_q_cnt = 0;
+       adapter->ext_scan = true;
+       adapter->fw_key_api_major_ver = 0;
+       adapter->fw_key_api_minor_ver = 0;
 }
 
 /*
@@ -450,6 +454,7 @@ int mwifiex_init_lock_list(struct mwifiex_adapter *adapter)
                INIT_LIST_HEAD(&priv->tx_ba_stream_tbl_ptr);
                INIT_LIST_HEAD(&priv->rx_reorder_tbl_ptr);
                INIT_LIST_HEAD(&priv->sta_list);
+               skb_queue_head_init(&priv->tdls_txq);
 
                spin_lock_init(&priv->tx_ba_stream_tbl_lock);
                spin_lock_init(&priv->rx_reorder_tbl_lock);
index 00a95f4..5974642 100644 (file)
@@ -60,8 +60,7 @@ enum {
        BAND_A = 4,
        BAND_GN = 8,
        BAND_AN = 16,
-       BAND_GAC = 32,
-       BAND_AAC = 64,
+       BAND_AAC = 32,
 };
 
 #define MWIFIEX_WPA_PASSHPHRASE_LEN 64
@@ -86,6 +85,10 @@ struct wep_key {
 #define BAND_CONFIG_A           0x01
 #define MWIFIEX_SUPPORTED_RATES                 14
 #define MWIFIEX_SUPPORTED_RATES_EXT             32
+#define MWIFIEX_TDLS_SUPPORTED_RATES           8
+#define MWIFIEX_TDLS_DEF_QOS_CAPAB             0xf
+#define MWIFIEX_PRIO_BK                                2
+#define MWIFIEX_PRIO_VI                                5
 
 struct mwifiex_uap_bss_param {
        u8 channel;
@@ -233,7 +236,10 @@ struct mwifiex_ds_encrypt_key {
        u8 mac_addr[ETH_ALEN];
        u32 is_wapi_key;
        u8 pn[PN_LEN];          /* packet number */
+       u8 pn_len;
        u8 is_igtk_key;
+       u8 is_current_wep_key;
+       u8 is_rx_seq_valid;
 };
 
 struct mwifiex_power_cfg {
@@ -432,4 +438,16 @@ struct mwifiex_ds_coalesce_cfg {
        struct mwifiex_coalesce_rule rule[MWIFIEX_COALESCE_MAX_RULES];
 };
 
+struct mwifiex_ds_tdls_oper {
+       u16 tdls_action;
+       u8 peer_mac[ETH_ALEN];
+       u16 capability;
+       u8 qos_info;
+       u8 *ext_capab;
+       u8 ext_capab_len;
+       u8 *supp_rates;
+       u8 supp_rates_len;
+       u8 *ht_capab;
+};
+
 #endif /* !_MWIFIEX_IOCTL_H_ */
index 4e4686e..34472ea 100644 (file)
@@ -515,8 +515,7 @@ int mwifiex_cmd_802_11_associate(struct mwifiex_private *priv,
 
        if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info) &&
            !bss_desc->disable_11n && !bss_desc->disable_11ac &&
-           (priv->adapter->config_bands & BAND_GAC ||
-            priv->adapter->config_bands & BAND_AAC))
+           priv->adapter->config_bands & BAND_AAC)
                mwifiex_cmd_append_11ac_tlv(priv, bss_desc, &pos);
 
        /* Append vendor specific IE TLV */
@@ -983,7 +982,7 @@ mwifiex_cmd_802_11_ad_hoc_start(struct mwifiex_private *priv,
                       cpu_to_le16(sizeof(struct ieee80211_ht_cap));
                radio_type = mwifiex_band_to_radio_type(
                                        priv->adapter->config_bands);
-               mwifiex_fill_cap_info(priv, radio_type, ht_cap);
+               mwifiex_fill_cap_info(priv, radio_type, &ht_cap->ht_cap);
 
                if (adapter->sec_chan_offset ==
                                        IEEE80211_HT_PARAM_CHA_SEC_NONE) {
@@ -1300,8 +1299,7 @@ int mwifiex_associate(struct mwifiex_private *priv,
 
        if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info) &&
            !bss_desc->disable_11n && !bss_desc->disable_11ac &&
-           (priv->adapter->config_bands & BAND_GAC ||
-            priv->adapter->config_bands & BAND_AAC))
+           priv->adapter->config_bands & BAND_AAC)
                mwifiex_set_11ac_ba_params(priv);
        else
                mwifiex_set_ba_params(priv);
@@ -1335,8 +1333,7 @@ mwifiex_adhoc_start(struct mwifiex_private *priv,
                priv->curr_bss_params.band);
 
        if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info) &&
-           (priv->adapter->config_bands & BAND_GAC ||
-            priv->adapter->config_bands & BAND_AAC))
+           priv->adapter->config_bands & BAND_AAC)
                mwifiex_set_11ac_ba_params(priv);
        else
                mwifiex_set_ba_params(priv);
@@ -1376,8 +1373,7 @@ int mwifiex_adhoc_join(struct mwifiex_private *priv,
 
        if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info) &&
            !bss_desc->disable_11n && !bss_desc->disable_11ac &&
-           (priv->adapter->config_bands & BAND_GAC ||
-            priv->adapter->config_bands & BAND_AAC))
+           priv->adapter->config_bands & BAND_AAC)
                mwifiex_set_11ac_ba_params(priv);
        else
                mwifiex_set_ba_params(priv);
index d8ad554..407f8ea 100644 (file)
@@ -59,7 +59,7 @@ enum {
 
 #define MWIFIEX_UPLD_SIZE               (2312)
 
-#define MAX_EVENT_SIZE                  1024
+#define MAX_EVENT_SIZE                  2048
 
 #define ARP_FILTER_MAX_BUF_SIZE         68
 
@@ -210,6 +210,7 @@ struct mwifiex_ra_list_tbl {
        u16 ba_pkt_count;
        u8 ba_packet_thr;
        u16 total_pkt_count;
+       bool tdls_link;
 };
 
 struct mwifiex_tid_tbl {
@@ -262,6 +263,31 @@ struct ieee_types_generic {
        u8 data[IEEE_MAX_IE_SIZE - sizeof(struct ieee_types_header)];
 } __packed;
 
+struct ieee_types_bss_co_2040 {
+       struct ieee_types_header ieee_hdr;
+       u8 bss_2040co;
+} __packed;
+
+struct ieee_types_extcap {
+       struct ieee_types_header ieee_hdr;
+       u8 ext_capab[8];
+} __packed;
+
+struct ieee_types_vht_cap {
+       struct ieee_types_header ieee_hdr;
+       struct ieee80211_vht_cap vhtcap;
+} __packed;
+
+struct ieee_types_vht_oper {
+       struct ieee_types_header ieee_hdr;
+       struct ieee80211_vht_operation vhtoper;
+} __packed;
+
+struct ieee_types_aid {
+       struct ieee_types_header ieee_hdr;
+       u16 aid;
+} __packed;
+
 struct mwifiex_bssdescriptor {
        u8 mac_address[ETH_ALEN];
        struct cfg80211_ssid ssid;
@@ -443,6 +469,7 @@ struct mwifiex_private {
        u8 wpa_ie_len;
        u8 wpa_is_gtk_set;
        struct host_cmd_ds_802_11_key_material aes_key;
+       struct host_cmd_ds_802_11_key_material_v2 aes_key_v2;
        u8 wapi_ie[256];
        u8 wapi_ie_len;
        u8 *wps_ie;
@@ -461,6 +488,7 @@ struct mwifiex_private {
        struct mwifiex_tx_aggr aggr_prio_tbl[MAX_NUM_TID];
        struct mwifiex_add_ba_param add_ba_param;
        u16 rx_seq[MAX_NUM_TID];
+       u8 tos_to_tid_inv[MAX_NUM_TID];
        struct list_head rx_reorder_tbl_ptr;
        /* spin lock for rx_reorder_tbl_ptr queue */
        spinlock_t rx_reorder_tbl_lock;
@@ -518,6 +546,8 @@ struct mwifiex_private {
        unsigned long csa_expire_time;
        u8 del_list_idx;
        bool hs2_enabled;
+       struct station_parameters *sta_params;
+       struct sk_buff_head tdls_txq;
 };
 
 enum mwifiex_ba_status {
@@ -583,17 +613,35 @@ struct mwifiex_bss_priv {
        u64 fw_tsf;
 };
 
-/* This is AP specific structure which stores information
- * about associated STA
+struct mwifiex_tdls_capab {
+       __le16 capab;
+       u8 rates[32];
+       u8 rates_len;
+       u8 qos_info;
+       u8 coex_2040;
+       u16 aid;
+       struct ieee80211_ht_cap ht_capb;
+       struct ieee80211_ht_operation ht_oper;
+       struct ieee_types_extcap extcap;
+       struct ieee_types_generic rsn_ie;
+       struct ieee80211_vht_cap vhtcap;
+       struct ieee80211_vht_operation vhtoper;
+};
+
+/* This is AP/TDLS specific structure which stores information
+ * about associated/peer STA
  */
 struct mwifiex_sta_node {
        struct list_head list;
        u8 mac_addr[ETH_ALEN];
        u8 is_wmm_enabled;
        u8 is_11n_enabled;
+       u8 is_11ac_enabled;
        u8 ampdu_sta[MAX_NUM_TID];
        u16 rx_seq[MAX_NUM_TID];
        u16 max_amsdu;
+       u8 tdls_status;
+       struct mwifiex_tdls_capab tdls_cap;
 };
 
 struct mwifiex_if_ops {
@@ -753,6 +801,8 @@ struct mwifiex_adapter {
        atomic_t is_tx_received;
        atomic_t pending_bridged_pkts;
        struct semaphore *card_sem;
+       bool ext_scan;
+       u8 fw_key_api_major_ver, fw_key_api_minor_ver;
 };
 
 int mwifiex_init_lock_list(struct mwifiex_adapter *adapter);
@@ -938,6 +988,12 @@ mwifiex_set_wmm_params(struct mwifiex_private *priv,
                       struct cfg80211_ap_settings *params);
 void mwifiex_set_ba_params(struct mwifiex_private *priv);
 void mwifiex_set_11ac_ba_params(struct mwifiex_private *priv);
+int mwifiex_cmd_802_11_scan_ext(struct mwifiex_private *priv,
+                               struct host_cmd_ds_command *cmd,
+                               void *data_buf);
+int mwifiex_ret_802_11_scan_ext(struct mwifiex_private *priv);
+int mwifiex_handle_event_ext_scan_report(struct mwifiex_private *priv,
+                                        void *buf);
 
 /*
  * This function checks if the queuing is RA based or not.
@@ -1078,7 +1134,7 @@ int mwifiex_set_encode(struct mwifiex_private *priv, struct key_params *kp,
                       const u8 *key, int key_len, u8 key_index,
                       const u8 *mac_addr, int disable);
 
-int mwifiex_set_gen_ie(struct mwifiex_private *priv, u8 *ie, int ie_len);
+int mwifiex_set_gen_ie(struct mwifiex_private *priv, const u8 *ie, int ie_len);
 
 int mwifiex_get_ver_ext(struct mwifiex_private *priv);
 
@@ -1159,6 +1215,32 @@ void mwifiex_dnld_txpwr_table(struct mwifiex_private *priv);
 
 extern const struct ethtool_ops mwifiex_ethtool_ops;
 
+void mwifiex_del_all_sta_list(struct mwifiex_private *priv);
+void mwifiex_del_sta_entry(struct mwifiex_private *priv, u8 *mac);
+void
+mwifiex_set_sta_ht_cap(struct mwifiex_private *priv, const u8 *ies,
+                      int ies_len, struct mwifiex_sta_node *node);
+struct mwifiex_sta_node *
+mwifiex_add_sta_entry(struct mwifiex_private *priv, u8 *mac);
+struct mwifiex_sta_node *
+mwifiex_get_sta_entry(struct mwifiex_private *priv, u8 *mac);
+int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv, u8 *peer,
+                                u8 action_code, u8 dialog_token,
+                                u16 status_code, const u8 *extra_ies,
+                                size_t extra_ies_len);
+int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv,
+                                u8 *peer, u8 action_code, u8 dialog_token,
+                                u16 status_code, const u8 *extra_ies,
+                                size_t extra_ies_len);
+void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv,
+                                      u8 *buf, int len);
+int mwifiex_tdls_oper(struct mwifiex_private *priv, u8 *peer, u8 action);
+int mwifiex_get_tdls_link_status(struct mwifiex_private *priv, u8 *mac);
+void mwifiex_disable_all_tdls_links(struct mwifiex_private *priv);
+bool mwifiex_is_bss_in_11ac_mode(struct mwifiex_private *priv);
+u8 mwifiex_get_center_freq_index(struct mwifiex_private *priv, u8 band,
+                                u32 pri_chan, u8 chan_bw);
+
 #ifdef CONFIG_DEBUG_FS
 void mwifiex_debugfs_init(void);
 void mwifiex_debugfs_remove(void);
index 03688aa..d11d4ac 100644 (file)
@@ -39,20 +39,31 @@ static struct semaphore add_remove_card_sem;
 
 static int
 mwifiex_map_pci_memory(struct mwifiex_adapter *adapter, struct sk_buff *skb,
-                      int size, int flags)
+                      size_t size, int flags)
 {
        struct pcie_service_card *card = adapter->card;
-       dma_addr_t buf_pa;
+       struct mwifiex_dma_mapping mapping;
 
-       buf_pa = pci_map_single(card->dev, skb->data, size, flags);
-       if (pci_dma_mapping_error(card->dev, buf_pa)) {
+       mapping.addr = pci_map_single(card->dev, skb->data, size, flags);
+       if (pci_dma_mapping_error(card->dev, mapping.addr)) {
                dev_err(adapter->dev, "failed to map pci memory!\n");
                return -1;
        }
-       memcpy(skb->cb, &buf_pa, sizeof(dma_addr_t));
+       mapping.len = size;
+       memcpy(skb->cb, &mapping, sizeof(mapping));
        return 0;
 }
 
+static void mwifiex_unmap_pci_memory(struct mwifiex_adapter *adapter,
+                                    struct sk_buff *skb, int flags)
+{
+       struct pcie_service_card *card = adapter->card;
+       struct mwifiex_dma_mapping mapping;
+
+       MWIFIEX_SKB_PACB(skb, &mapping);
+       pci_unmap_single(card->dev, mapping.addr, mapping.len, flags);
+}
+
 /*
  * This function reads sleep cookie and checks if FW is ready
  */
@@ -456,7 +467,7 @@ static int mwifiex_init_rxq_ring(struct mwifiex_adapter *adapter)
                                           PCI_DMA_FROMDEVICE))
                        return -1;
 
-               MWIFIEX_SKB_PACB(skb, &buf_pa);
+               buf_pa = MWIFIEX_SKB_DMA_ADDR(skb);
 
                dev_dbg(adapter->dev,
                        "info: RX ring: skb=%p len=%d data=%p buf_pa=%#x:%x\n",
@@ -513,7 +524,7 @@ static int mwifiex_pcie_init_evt_ring(struct mwifiex_adapter *adapter)
                                           PCI_DMA_FROMDEVICE))
                        return -1;
 
-               MWIFIEX_SKB_PACB(skb, &buf_pa);
+               buf_pa = MWIFIEX_SKB_DMA_ADDR(skb);
 
                dev_dbg(adapter->dev,
                        "info: EVT ring: skb=%p len=%d data=%p buf_pa=%#x:%x\n",
@@ -549,8 +560,8 @@ static void mwifiex_cleanup_txq_ring(struct mwifiex_adapter *adapter)
                        desc2 = card->txbd_ring[i];
                        if (card->tx_buf_list[i]) {
                                skb = card->tx_buf_list[i];
-                               pci_unmap_single(card->dev, desc2->paddr,
-                                                skb->len, PCI_DMA_TODEVICE);
+                               mwifiex_unmap_pci_memory(adapter, skb,
+                                                        PCI_DMA_TODEVICE);
                                dev_kfree_skb_any(skb);
                        }
                        memset(desc2, 0, sizeof(*desc2));
@@ -558,8 +569,8 @@ static void mwifiex_cleanup_txq_ring(struct mwifiex_adapter *adapter)
                        desc = card->txbd_ring[i];
                        if (card->tx_buf_list[i]) {
                                skb = card->tx_buf_list[i];
-                               pci_unmap_single(card->dev, desc->paddr,
-                                                skb->len, PCI_DMA_TODEVICE);
+                               mwifiex_unmap_pci_memory(adapter, skb,
+                                                        PCI_DMA_TODEVICE);
                                dev_kfree_skb_any(skb);
                        }
                        memset(desc, 0, sizeof(*desc));
@@ -587,8 +598,8 @@ static void mwifiex_cleanup_rxq_ring(struct mwifiex_adapter *adapter)
                        desc2 = card->rxbd_ring[i];
                        if (card->rx_buf_list[i]) {
                                skb = card->rx_buf_list[i];
-                               pci_unmap_single(card->dev, desc2->paddr,
-                                                skb->len, PCI_DMA_FROMDEVICE);
+                               mwifiex_unmap_pci_memory(adapter, skb,
+                                                        PCI_DMA_FROMDEVICE);
                                dev_kfree_skb_any(skb);
                        }
                        memset(desc2, 0, sizeof(*desc2));
@@ -596,8 +607,8 @@ static void mwifiex_cleanup_rxq_ring(struct mwifiex_adapter *adapter)
                        desc = card->rxbd_ring[i];
                        if (card->rx_buf_list[i]) {
                                skb = card->rx_buf_list[i];
-                               pci_unmap_single(card->dev, desc->paddr,
-                                                skb->len, PCI_DMA_FROMDEVICE);
+                               mwifiex_unmap_pci_memory(adapter, skb,
+                                                        PCI_DMA_FROMDEVICE);
                                dev_kfree_skb_any(skb);
                        }
                        memset(desc, 0, sizeof(*desc));
@@ -622,8 +633,8 @@ static void mwifiex_cleanup_evt_ring(struct mwifiex_adapter *adapter)
                desc = card->evtbd_ring[i];
                if (card->evt_buf_list[i]) {
                        skb = card->evt_buf_list[i];
-                       pci_unmap_single(card->dev, desc->paddr, MAX_EVENT_SIZE,
-                                        PCI_DMA_FROMDEVICE);
+                       mwifiex_unmap_pci_memory(adapter, skb,
+                                                PCI_DMA_FROMDEVICE);
                        dev_kfree_skb_any(skb);
                }
                card->evt_buf_list[i] = NULL;
@@ -861,7 +872,6 @@ static int mwifiex_pcie_alloc_cmdrsp_buf(struct mwifiex_adapter *adapter)
 static int mwifiex_pcie_delete_cmdrsp_buf(struct mwifiex_adapter *adapter)
 {
        struct pcie_service_card *card;
-       dma_addr_t buf_pa;
 
        if (!adapter)
                return 0;
@@ -869,16 +879,14 @@ static int mwifiex_pcie_delete_cmdrsp_buf(struct mwifiex_adapter *adapter)
        card = adapter->card;
 
        if (card && card->cmdrsp_buf) {
-               MWIFIEX_SKB_PACB(card->cmdrsp_buf, &buf_pa);
-               pci_unmap_single(card->dev, buf_pa, MWIFIEX_UPLD_SIZE,
-                                PCI_DMA_FROMDEVICE);
+               mwifiex_unmap_pci_memory(adapter, card->cmdrsp_buf,
+                                        PCI_DMA_FROMDEVICE);
                dev_kfree_skb_any(card->cmdrsp_buf);
        }
 
        if (card && card->cmd_buf) {
-               MWIFIEX_SKB_PACB(card->cmd_buf, &buf_pa);
-               pci_unmap_single(card->dev, buf_pa, card->cmd_buf->len,
-                                PCI_DMA_TODEVICE);
+               mwifiex_unmap_pci_memory(adapter, card->cmd_buf,
+                                        PCI_DMA_TODEVICE);
        }
        return 0;
 }
@@ -956,7 +964,6 @@ static int mwifiex_clean_pcie_ring_buf(struct mwifiex_adapter *adapter)
 static int mwifiex_pcie_send_data_complete(struct mwifiex_adapter *adapter)
 {
        struct sk_buff *skb;
-       dma_addr_t buf_pa;
        u32 wrdoneidx, rdptr, num_tx_buffs, unmap_count = 0;
        struct mwifiex_pcie_buf_desc *desc;
        struct mwifiex_pfu_buf_desc *desc2;
@@ -986,13 +993,13 @@ static int mwifiex_pcie_send_data_complete(struct mwifiex_adapter *adapter)
                            reg->tx_start_ptr;
 
                skb = card->tx_buf_list[wrdoneidx];
+
                if (skb) {
                        dev_dbg(adapter->dev,
                                "SEND COMP: Detach skb %p at txbd_rdidx=%d\n",
                                skb, wrdoneidx);
-                       MWIFIEX_SKB_PACB(skb, &buf_pa);
-                       pci_unmap_single(card->dev, buf_pa, skb->len,
-                                        PCI_DMA_TODEVICE);
+                       mwifiex_unmap_pci_memory(adapter, skb,
+                                                PCI_DMA_TODEVICE);
 
                        unmap_count++;
 
@@ -1082,12 +1089,12 @@ mwifiex_pcie_send_data(struct mwifiex_adapter *adapter, struct sk_buff *skb,
                tmp = (__le16 *)&payload[2];
                *tmp = cpu_to_le16(MWIFIEX_TYPE_DATA);
 
-               if (mwifiex_map_pci_memory(adapter, skb, skb->len ,
+               if (mwifiex_map_pci_memory(adapter, skb, skb->len,
                                           PCI_DMA_TODEVICE))
                        return -1;
 
                wrindx = (card->txbd_wrptr & reg->tx_mask) >> reg->tx_start_ptr;
-               MWIFIEX_SKB_PACB(skb, &buf_pa);
+               buf_pa = MWIFIEX_SKB_DMA_ADDR(skb);
                card->tx_buf_list[wrindx] = skb;
 
                if (reg->pfu_enabled) {
@@ -1162,8 +1169,7 @@ mwifiex_pcie_send_data(struct mwifiex_adapter *adapter, struct sk_buff *skb,
 
        return -EINPROGRESS;
 done_unmap:
-       MWIFIEX_SKB_PACB(skb, &buf_pa);
-       pci_unmap_single(card->dev, buf_pa, skb->len, PCI_DMA_TODEVICE);
+       mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE);
        card->tx_buf_list[wrindx] = NULL;
        if (reg->pfu_enabled)
                memset(desc2, 0, sizeof(*desc2));
@@ -1211,9 +1217,7 @@ static int mwifiex_pcie_process_recv_data(struct mwifiex_adapter *adapter)
                rd_index = card->rxbd_rdptr & reg->rx_mask;
                skb_data = card->rx_buf_list[rd_index];
 
-               MWIFIEX_SKB_PACB(skb_data, &buf_pa);
-               pci_unmap_single(card->dev, buf_pa, MWIFIEX_RX_DATA_BUF_SIZE,
-                                PCI_DMA_FROMDEVICE);
+               mwifiex_unmap_pci_memory(adapter, skb_data, PCI_DMA_FROMDEVICE);
                card->rx_buf_list[rd_index] = NULL;
 
                /* Get data length from interface header -
@@ -1240,7 +1244,7 @@ static int mwifiex_pcie_process_recv_data(struct mwifiex_adapter *adapter)
                                           PCI_DMA_FROMDEVICE))
                        return -1;
 
-               MWIFIEX_SKB_PACB(skb_tmp, &buf_pa);
+               buf_pa = MWIFIEX_SKB_DMA_ADDR(skb_tmp);
 
                dev_dbg(adapter->dev,
                        "RECV DATA: Attach new sk_buff %p at rxbd_rdidx=%d\n",
@@ -1316,7 +1320,7 @@ mwifiex_pcie_send_boot_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb)
        if (mwifiex_map_pci_memory(adapter, skb, skb->len , PCI_DMA_TODEVICE))
                return -1;
 
-       MWIFIEX_SKB_PACB(skb, &buf_pa);
+       buf_pa = MWIFIEX_SKB_DMA_ADDR(skb);
 
        /* Write the lower 32bits of the physical address to low command
         * address scratch register
@@ -1325,8 +1329,7 @@ mwifiex_pcie_send_boot_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb)
                dev_err(adapter->dev,
                        "%s: failed to write download command to boot code.\n",
                        __func__);
-               pci_unmap_single(card->dev, buf_pa, MWIFIEX_UPLD_SIZE,
-                                PCI_DMA_TODEVICE);
+               mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE);
                return -1;
        }
 
@@ -1338,8 +1341,7 @@ mwifiex_pcie_send_boot_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb)
                dev_err(adapter->dev,
                        "%s: failed to write download command to boot code.\n",
                        __func__);
-               pci_unmap_single(card->dev, buf_pa, MWIFIEX_UPLD_SIZE,
-                                PCI_DMA_TODEVICE);
+               mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE);
                return -1;
        }
 
@@ -1348,8 +1350,7 @@ mwifiex_pcie_send_boot_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb)
                dev_err(adapter->dev,
                        "%s: failed to write command len to cmd_size scratch reg\n",
                        __func__);
-               pci_unmap_single(card->dev, buf_pa, MWIFIEX_UPLD_SIZE,
-                                PCI_DMA_TODEVICE);
+               mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE);
                return -1;
        }
 
@@ -1358,8 +1359,7 @@ mwifiex_pcie_send_boot_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb)
                              CPU_INTR_DOOR_BELL)) {
                dev_err(adapter->dev,
                        "%s: failed to assert door-bell intr\n", __func__);
-               pci_unmap_single(card->dev, buf_pa,
-                                MWIFIEX_UPLD_SIZE, PCI_DMA_TODEVICE);
+               mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE);
                return -1;
        }
 
@@ -1433,7 +1433,7 @@ mwifiex_pcie_send_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb)
        */
 
        if (card->cmdrsp_buf) {
-               MWIFIEX_SKB_PACB(card->cmdrsp_buf, &cmdrsp_buf_pa);
+               cmdrsp_buf_pa = MWIFIEX_SKB_DMA_ADDR(card->cmdrsp_buf);
                /* Write the lower 32bits of the cmdrsp buffer physical
                   address */
                if (mwifiex_write_reg(adapter, reg->cmdrsp_addr_lo,
@@ -1454,7 +1454,7 @@ mwifiex_pcie_send_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb)
                }
        }
 
-       MWIFIEX_SKB_PACB(card->cmd_buf, &cmd_buf_pa);
+       cmd_buf_pa = MWIFIEX_SKB_DMA_ADDR(card->cmd_buf);
        /* Write the lower 32bits of the physical address to reg->cmd_addr_lo */
        if (mwifiex_write_reg(adapter, reg->cmd_addr_lo,
                              (u32)cmd_buf_pa)) {
@@ -1508,13 +1508,17 @@ static int mwifiex_pcie_process_cmd_complete(struct mwifiex_adapter *adapter)
        int count = 0;
        u16 rx_len;
        __le16 pkt_len;
-       dma_addr_t buf_pa;
 
        dev_dbg(adapter->dev, "info: Rx CMD Response\n");
 
-       MWIFIEX_SKB_PACB(skb, &buf_pa);
-       pci_unmap_single(card->dev, buf_pa, MWIFIEX_UPLD_SIZE,
-                        PCI_DMA_FROMDEVICE);
+       mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_FROMDEVICE);
+
+       /* Unmap the command as a response has been received. */
+       if (card->cmd_buf) {
+               mwifiex_unmap_pci_memory(adapter, card->cmd_buf,
+                                        PCI_DMA_TODEVICE);
+               card->cmd_buf = NULL;
+       }
 
        pkt_len = *((__le16 *)skb->data);
        rx_len = le16_to_cpu(pkt_len);
@@ -1538,8 +1542,6 @@ static int mwifiex_pcie_process_cmd_complete(struct mwifiex_adapter *adapter)
                if (mwifiex_map_pci_memory(adapter, skb, MWIFIEX_UPLD_SIZE,
                                           PCI_DMA_FROMDEVICE))
                        return -1;
-
-               MWIFIEX_SKB_PACB(skb, &buf_pa);
        } else if (mwifiex_pcie_ok_to_access_hw(adapter)) {
                adapter->curr_cmd->resp_skb = skb;
                adapter->cmd_resp_received = true;
@@ -1574,8 +1576,6 @@ static int mwifiex_pcie_cmdrsp_complete(struct mwifiex_adapter *adapter,
                                        struct sk_buff *skb)
 {
        struct pcie_service_card *card = adapter->card;
-       dma_addr_t buf_pa;
-       struct sk_buff *skb_tmp;
 
        if (skb) {
                card->cmdrsp_buf = skb;
@@ -1585,14 +1585,6 @@ static int mwifiex_pcie_cmdrsp_complete(struct mwifiex_adapter *adapter,
                        return -1;
        }
 
-       skb_tmp = card->cmd_buf;
-       if (skb_tmp) {
-               MWIFIEX_SKB_PACB(skb_tmp, &buf_pa);
-               pci_unmap_single(card->dev, buf_pa, skb_tmp->len,
-                                PCI_DMA_FROMDEVICE);
-               card->cmd_buf = NULL;
-       }
-
        return 0;
 }
 
@@ -1605,7 +1597,6 @@ static int mwifiex_pcie_process_event_ready(struct mwifiex_adapter *adapter)
        const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
        u32 rdptr = card->evtbd_rdptr & MWIFIEX_EVTBD_MASK;
        u32 wrptr, event;
-       dma_addr_t buf_pa;
        struct mwifiex_evt_buf_desc *desc;
 
        if (!mwifiex_pcie_ok_to_access_hw(adapter))
@@ -1641,9 +1632,7 @@ static int mwifiex_pcie_process_event_ready(struct mwifiex_adapter *adapter)
 
                dev_dbg(adapter->dev, "info: Read Index: %d\n", rdptr);
                skb_cmd = card->evt_buf_list[rdptr];
-               MWIFIEX_SKB_PACB(skb_cmd, &buf_pa);
-               pci_unmap_single(card->dev, buf_pa, MAX_EVENT_SIZE,
-                                PCI_DMA_FROMDEVICE);
+               mwifiex_unmap_pci_memory(adapter, skb_cmd, PCI_DMA_FROMDEVICE);
 
                /* Take the pointer and set it to event pointer in adapter
                   and will return back after event handling callback */
@@ -1689,7 +1678,6 @@ static int mwifiex_pcie_event_complete(struct mwifiex_adapter *adapter,
        int ret = 0;
        u32 rdptr = card->evtbd_rdptr & MWIFIEX_EVTBD_MASK;
        u32 wrptr;
-       dma_addr_t buf_pa;
        struct mwifiex_evt_buf_desc *desc;
 
        if (!skb)
@@ -1714,11 +1702,9 @@ static int mwifiex_pcie_event_complete(struct mwifiex_adapter *adapter,
                                           MAX_EVENT_SIZE,
                                           PCI_DMA_FROMDEVICE))
                        return -1;
-               MWIFIEX_SKB_PACB(skb, &buf_pa);
                card->evt_buf_list[rdptr] = skb;
-               MWIFIEX_SKB_PACB(skb, &buf_pa);
                desc = card->evtbd_ring[rdptr];
-               desc->paddr = buf_pa;
+               desc->paddr = MWIFIEX_SKB_DMA_ADDR(skb);
                desc->len = (u16)skb->len;
                desc->flags = 0;
                skb = NULL;
@@ -1768,7 +1754,6 @@ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter,
        struct sk_buff *skb;
        u32 txlen, tx_blocks = 0, tries, len;
        u32 block_retry_cnt = 0;
-       dma_addr_t buf_pa;
        struct pcie_service_card *card = adapter->card;
        const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
 
@@ -1866,8 +1851,6 @@ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter,
                        goto done;
                }
 
-               MWIFIEX_SKB_PACB(skb, &buf_pa);
-
                /* Wait for the command done interrupt */
                do {
                        if (mwifiex_read_reg(adapter, PCIE_CPU_INT_STATUS,
@@ -1875,16 +1858,15 @@ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter,
                                dev_err(adapter->dev, "%s: Failed to read "
                                        "interrupt status during fw dnld.\n",
                                        __func__);
-                               pci_unmap_single(card->dev, buf_pa, skb->len,
-                                                PCI_DMA_TODEVICE);
+                               mwifiex_unmap_pci_memory(adapter, skb,
+                                                        PCI_DMA_TODEVICE);
                                ret = -1;
                                goto done;
                        }
                } while ((ireg_intr & CPU_INTR_DOOR_BELL) ==
                         CPU_INTR_DOOR_BELL);
 
-               pci_unmap_single(card->dev, buf_pa, skb->len,
-                                PCI_DMA_TODEVICE);
+               mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE);
 
                offset += txlen;
        } while (true);
index 0a8a26e..92adbb1 100644 (file)
@@ -595,7 +595,7 @@ mwifiex_scan_channel_list(struct mwifiex_private *priv,
        struct mwifiex_chan_scan_param_set *tmp_chan_list;
        struct mwifiex_chan_scan_param_set *start_chan;
 
-       u32 tlv_idx, rates_size;
+       u32 tlv_idx, rates_size, cmd_no;
        u32 total_scan_time;
        u32 done_early;
        u8 radio_type;
@@ -733,9 +733,13 @@ mwifiex_scan_channel_list(struct mwifiex_private *priv,
 
                /* Send the scan command to the firmware with the specified
                   cfg */
-               ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_802_11_SCAN,
-                                            HostCmd_ACT_GEN_SET, 0,
-                                            scan_cfg_out);
+               if (priv->adapter->ext_scan)
+                       cmd_no = HostCmd_CMD_802_11_SCAN_EXT;
+               else
+                       cmd_no = HostCmd_CMD_802_11_SCAN;
+
+               ret = mwifiex_send_cmd_async(priv, cmd_no, HostCmd_ACT_GEN_SET,
+                                            0, scan_cfg_out);
 
                /* rate IE is updated per scan command but same starting
                 * pointer is used each time so that rate IE from earlier
@@ -786,6 +790,7 @@ mwifiex_config_scan(struct mwifiex_private *priv,
        struct mwifiex_adapter *adapter = priv->adapter;
        struct mwifiex_ie_types_num_probes *num_probes_tlv;
        struct mwifiex_ie_types_wildcard_ssid_params *wildcard_ssid_tlv;
+       struct mwifiex_ie_types_bssid_list *bssid_tlv;
        u8 *tlv_pos;
        u32 num_probes;
        u32 ssid_len;
@@ -848,6 +853,17 @@ mwifiex_config_scan(struct mwifiex_private *priv,
                       user_scan_in->specific_bssid,
                       sizeof(scan_cfg_out->specific_bssid));
 
+               if (adapter->ext_scan &&
+                   !is_zero_ether_addr(scan_cfg_out->specific_bssid)) {
+                       bssid_tlv =
+                               (struct mwifiex_ie_types_bssid_list *)tlv_pos;
+                       bssid_tlv->header.type = cpu_to_le16(TLV_TYPE_BSSID);
+                       bssid_tlv->header.len = cpu_to_le16(ETH_ALEN);
+                       memcpy(bssid_tlv->bssid, user_scan_in->specific_bssid,
+                              ETH_ALEN);
+                       tlv_pos += sizeof(struct mwifiex_ie_types_bssid_list);
+               }
+
                for (i = 0; i < user_scan_in->num_ssids; i++) {
                        ssid_len = user_scan_in->ssid_list[i].ssid_len;
 
@@ -941,7 +957,7 @@ mwifiex_config_scan(struct mwifiex_private *priv,
                                cpu_to_le16(sizeof(struct ieee80211_ht_cap));
                radio_type =
                        mwifiex_band_to_radio_type(priv->adapter->config_bands);
-               mwifiex_fill_cap_info(priv, radio_type, ht_cap);
+               mwifiex_fill_cap_info(priv, radio_type, &ht_cap->ht_cap);
                tlv_pos += sizeof(struct mwifiex_ie_types_htcap);
        }
 
@@ -1576,6 +1592,228 @@ done:
        return 0;
 }
 
+static int
+mwifiex_parse_single_response_buf(struct mwifiex_private *priv, u8 **bss_info,
+                                 u32 *bytes_left, u64 fw_tsf, u8 *radio_type,
+                                 bool ext_scan, s32 rssi_val)
+{
+       struct mwifiex_adapter *adapter = priv->adapter;
+       struct mwifiex_chan_freq_power *cfp;
+       struct cfg80211_bss *bss;
+       u8 bssid[ETH_ALEN];
+       s32 rssi;
+       const u8 *ie_buf;
+       size_t ie_len;
+       u16 channel = 0;
+       u16 beacon_size = 0;
+       u32 curr_bcn_bytes;
+       u32 freq;
+       u16 beacon_period;
+       u16 cap_info_bitmap;
+       u8 *current_ptr;
+       u64 timestamp;
+       struct mwifiex_fixed_bcn_param *bcn_param;
+       struct mwifiex_bss_priv *bss_priv;
+
+       if (*bytes_left >= sizeof(beacon_size)) {
+               /* Extract & convert beacon size from command buffer */
+               memcpy(&beacon_size, *bss_info, sizeof(beacon_size));
+               *bytes_left -= sizeof(beacon_size);
+               *bss_info += sizeof(beacon_size);
+       }
+
+       if (!beacon_size || beacon_size > *bytes_left) {
+               *bss_info += *bytes_left;
+               *bytes_left = 0;
+               return -EFAULT;
+       }
+
+       /* Initialize the current working beacon pointer for this BSS
+        * iteration
+        */
+       current_ptr = *bss_info;
+
+       /* Advance the return beacon pointer past the current beacon */
+       *bss_info += beacon_size;
+       *bytes_left -= beacon_size;
+
+       curr_bcn_bytes = beacon_size;
+
+       /* First 5 fields are bssid, RSSI(for legacy scan only),
+        * time stamp, beacon interval, and capability information
+        */
+       if (curr_bcn_bytes < ETH_ALEN + sizeof(u8) +
+           sizeof(struct mwifiex_fixed_bcn_param)) {
+               dev_err(adapter->dev, "InterpretIE: not enough bytes left\n");
+               return -EFAULT;
+       }
+
+       memcpy(bssid, current_ptr, ETH_ALEN);
+       current_ptr += ETH_ALEN;
+       curr_bcn_bytes -= ETH_ALEN;
+
+       if (!ext_scan) {
+               rssi = (s32) *(u8 *)current_ptr;
+               rssi = (-rssi) * 100;           /* Convert dBm to mBm */
+               current_ptr += sizeof(u8);
+               curr_bcn_bytes -= sizeof(u8);
+               dev_dbg(adapter->dev, "info: InterpretIE: RSSI=%d\n", rssi);
+       } else {
+               rssi = rssi_val;
+       }
+
+       bcn_param = (struct mwifiex_fixed_bcn_param *)current_ptr;
+       current_ptr += sizeof(*bcn_param);
+       curr_bcn_bytes -= sizeof(*bcn_param);
+
+       timestamp = le64_to_cpu(bcn_param->timestamp);
+       beacon_period = le16_to_cpu(bcn_param->beacon_period);
+
+       cap_info_bitmap = le16_to_cpu(bcn_param->cap_info_bitmap);
+       dev_dbg(adapter->dev, "info: InterpretIE: capabilities=0x%X\n",
+               cap_info_bitmap);
+
+       /* Rest of the current buffer are IE's */
+       ie_buf = current_ptr;
+       ie_len = curr_bcn_bytes;
+       dev_dbg(adapter->dev, "info: InterpretIE: IELength for this AP = %d\n",
+               curr_bcn_bytes);
+
+       while (curr_bcn_bytes >= sizeof(struct ieee_types_header)) {
+               u8 element_id, element_len;
+
+               element_id = *current_ptr;
+               element_len = *(current_ptr + 1);
+               if (curr_bcn_bytes < element_len +
+                               sizeof(struct ieee_types_header)) {
+                       dev_err(adapter->dev,
+                               "%s: bytes left < IE length\n", __func__);
+                       return -EFAULT;
+               }
+               if (element_id == WLAN_EID_DS_PARAMS) {
+                       channel = *(current_ptr +
+                                   sizeof(struct ieee_types_header));
+                       break;
+               }
+
+               current_ptr += element_len + sizeof(struct ieee_types_header);
+               curr_bcn_bytes -= element_len +
+                                       sizeof(struct ieee_types_header);
+       }
+
+       if (channel) {
+               struct ieee80211_channel *chan;
+               u8 band;
+
+               /* Skip entry if on csa closed channel */
+               if (channel == priv->csa_chan) {
+                       dev_dbg(adapter->dev,
+                               "Dropping entry on csa closed channel\n");
+                       return 0;
+               }
+
+               band = BAND_G;
+               if (radio_type)
+                       band = mwifiex_radio_type_to_band(*radio_type &
+                                                         (BIT(0) | BIT(1)));
+
+               cfp = mwifiex_get_cfp(priv, band, channel, 0);
+
+               freq = cfp ? cfp->freq : 0;
+
+               chan = ieee80211_get_channel(priv->wdev->wiphy, freq);
+
+               if (chan && !(chan->flags & IEEE80211_CHAN_DISABLED)) {
+                       bss = cfg80211_inform_bss(priv->wdev->wiphy,
+                                           chan, bssid, timestamp,
+                                           cap_info_bitmap, beacon_period,
+                                           ie_buf, ie_len, rssi, GFP_KERNEL);
+                       bss_priv = (struct mwifiex_bss_priv *)bss->priv;
+                       bss_priv->band = band;
+                       bss_priv->fw_tsf = fw_tsf;
+                       if (priv->media_connected &&
+                           !memcmp(bssid, priv->curr_bss_params.bss_descriptor
+                                   .mac_address, ETH_ALEN))
+                               mwifiex_update_curr_bss_params(priv, bss);
+                       cfg80211_put_bss(priv->wdev->wiphy, bss);
+               }
+       } else {
+               dev_dbg(adapter->dev, "missing BSS channel IE\n");
+       }
+
+       return 0;
+}
+
+static void mwifiex_check_next_scan_command(struct mwifiex_private *priv)
+{
+       struct mwifiex_adapter *adapter = priv->adapter;
+       struct cmd_ctrl_node *cmd_node;
+       unsigned long flags;
+
+       spin_lock_irqsave(&adapter->scan_pending_q_lock, flags);
+       if (list_empty(&adapter->scan_pending_q)) {
+               spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags);
+               spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags);
+               adapter->scan_processing = false;
+               spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags);
+
+               /* Need to indicate IOCTL complete */
+               if (adapter->curr_cmd->wait_q_enabled) {
+                       adapter->cmd_wait_q.status = 0;
+                       if (!priv->scan_request) {
+                               dev_dbg(adapter->dev,
+                                       "complete internal scan\n");
+                               mwifiex_complete_cmd(adapter,
+                                                    adapter->curr_cmd);
+                       }
+               }
+               if (priv->report_scan_result)
+                       priv->report_scan_result = false;
+
+               if (priv->scan_request) {
+                       dev_dbg(adapter->dev, "info: notifying scan done\n");
+                       cfg80211_scan_done(priv->scan_request, 0);
+                       priv->scan_request = NULL;
+               } else {
+                       priv->scan_aborting = false;
+                       dev_dbg(adapter->dev, "info: scan already aborted\n");
+               }
+       } else {
+               if ((priv->scan_aborting && !priv->scan_request) ||
+                   priv->scan_block) {
+                       spin_unlock_irqrestore(&adapter->scan_pending_q_lock,
+                                              flags);
+                       adapter->scan_delay_cnt = MWIFIEX_MAX_SCAN_DELAY_CNT;
+                       mod_timer(&priv->scan_delay_timer, jiffies);
+                       dev_dbg(priv->adapter->dev,
+                               "info: %s: triggerring scan abort\n", __func__);
+               } else if (!mwifiex_wmm_lists_empty(adapter) &&
+                          (priv->scan_request && (priv->scan_request->flags &
+                                           NL80211_SCAN_FLAG_LOW_PRIORITY))) {
+                       spin_unlock_irqrestore(&adapter->scan_pending_q_lock,
+                                              flags);
+                       adapter->scan_delay_cnt = 1;
+                       mod_timer(&priv->scan_delay_timer, jiffies +
+                                 msecs_to_jiffies(MWIFIEX_SCAN_DELAY_MSEC));
+                       dev_dbg(priv->adapter->dev,
+                               "info: %s: deferring scan\n", __func__);
+               } else {
+                       /* Get scan command from scan_pending_q and put to
+                        * cmd_pending_q
+                        */
+                       cmd_node = list_first_entry(&adapter->scan_pending_q,
+                                                   struct cmd_ctrl_node, list);
+                       list_del(&cmd_node->list);
+                       spin_unlock_irqrestore(&adapter->scan_pending_q_lock,
+                                              flags);
+                       mwifiex_insert_cmd_to_pending_q(adapter, cmd_node,
+                                                       true);
+               }
+       }
+
+       return;
+}
+
 /*
  * This function handles the command response of scan.
  *
@@ -1600,7 +1838,6 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv,
 {
        int ret = 0;
        struct mwifiex_adapter *adapter = priv->adapter;
-       struct cmd_ctrl_node *cmd_node;
        struct host_cmd_ds_802_11_scan_rsp *scan_rsp;
        struct mwifiex_ie_types_data *tlv_data;
        struct mwifiex_ie_types_tsf_timestamp *tsf_tlv;
@@ -1609,12 +1846,11 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv,
        u32 bytes_left;
        u32 idx;
        u32 tlv_buf_size;
-       struct mwifiex_chan_freq_power *cfp;
        struct mwifiex_ie_types_chan_band_list_param_set *chan_band_tlv;
        struct chan_band_param_set *chan_band;
        u8 is_bgscan_resp;
-       unsigned long flags;
-       struct cfg80211_bss *bss;
+       __le64 fw_tsf = 0;
+       u8 *radio_type;
 
        is_bgscan_resp = (le16_to_cpu(resp->command)
                          == HostCmd_CMD_802_11_BG_SCAN_QUERY);
@@ -1676,220 +1912,194 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv,
                                             &chan_band_tlv);
 
        for (idx = 0; idx < scan_rsp->number_of_sets && bytes_left; idx++) {
-               u8 bssid[ETH_ALEN];
-               s32 rssi;
-               const u8 *ie_buf;
-               size_t ie_len;
-               u16 channel = 0;
-               __le64 fw_tsf = 0;
-               u16 beacon_size = 0;
-               u32 curr_bcn_bytes;
-               u32 freq;
-               u16 beacon_period;
-               u16 cap_info_bitmap;
-               u8 *current_ptr;
-               u64 timestamp;
-               struct mwifiex_bcn_param *bcn_param;
-               struct mwifiex_bss_priv *bss_priv;
-
-               if (bytes_left >= sizeof(beacon_size)) {
-                       /* Extract & convert beacon size from command buffer */
-                       memcpy(&beacon_size, bss_info, sizeof(beacon_size));
-                       bytes_left -= sizeof(beacon_size);
-                       bss_info += sizeof(beacon_size);
-               }
+               /*
+                * If the TSF TLV was appended to the scan results, save this
+                * entry's TSF value in the fw_tsf field. It is the firmware's
+                * TSF value at the time the beacon or probe response was
+                * received.
+                */
+               if (tsf_tlv)
+                       memcpy(&fw_tsf, &tsf_tlv->tsf_data[idx * TSF_DATA_SIZE],
+                              sizeof(fw_tsf));
 
-               if (!beacon_size || beacon_size > bytes_left) {
-                       bss_info += bytes_left;
-                       bytes_left = 0;
-                       ret = -1;
-                       goto check_next_scan;
+               if (chan_band_tlv) {
+                       chan_band = &chan_band_tlv->chan_band_param[idx];
+                       radio_type = &chan_band->radio_type;
+               } else {
+                       radio_type = NULL;
                }
 
-               /* Initialize the current working beacon pointer for this BSS
-                * iteration */
-               current_ptr = bss_info;
+               ret = mwifiex_parse_single_response_buf(priv, &bss_info,
+                                                       &bytes_left,
+                                                       le64_to_cpu(fw_tsf),
+                                                       radio_type, false, 0);
+               if (ret)
+                       goto check_next_scan;
+       }
 
-               /* Advance the return beacon pointer past the current beacon */
-               bss_info += beacon_size;
-               bytes_left -= beacon_size;
+check_next_scan:
+       mwifiex_check_next_scan_command(priv);
+       return ret;
+}
 
-               curr_bcn_bytes = beacon_size;
+/*
+ * This function prepares an extended scan command to be sent to the firmware
+ *
+ * This uses the scan command configuration sent to the command processing
+ * module in command preparation stage to configure a extended scan command
+ * structure to send to firmware.
+ */
+int mwifiex_cmd_802_11_scan_ext(struct mwifiex_private *priv,
+                               struct host_cmd_ds_command *cmd,
+                               void *data_buf)
+{
+       struct host_cmd_ds_802_11_scan_ext *ext_scan = &cmd->params.ext_scan;
+       struct mwifiex_scan_cmd_config *scan_cfg = data_buf;
 
-               /*
-                * First 5 fields are bssid, RSSI, time stamp, beacon interval,
-                *   and capability information
-                */
-               if (curr_bcn_bytes < sizeof(struct mwifiex_bcn_param)) {
-                       dev_err(adapter->dev,
-                               "InterpretIE: not enough bytes left\n");
-                       continue;
-               }
-               bcn_param = (struct mwifiex_bcn_param *)current_ptr;
-               current_ptr += sizeof(*bcn_param);
-               curr_bcn_bytes -= sizeof(*bcn_param);
+       memcpy(ext_scan->tlv_buffer, scan_cfg->tlv_buf, scan_cfg->tlv_buf_len);
 
-               memcpy(bssid, bcn_param->bssid, ETH_ALEN);
+       cmd->command = cpu_to_le16(HostCmd_CMD_802_11_SCAN_EXT);
 
-               rssi = (s32) bcn_param->rssi;
-               rssi = (-rssi) * 100;           /* Convert dBm to mBm */
-               dev_dbg(adapter->dev, "info: InterpretIE: RSSI=%d\n", rssi);
+       /* Size is equal to the sizeof(fixed portions) + the TLV len + header */
+       cmd->size = cpu_to_le16((u16)(sizeof(ext_scan->reserved)
+                                     + scan_cfg->tlv_buf_len + S_DS_GEN));
 
-               timestamp = le64_to_cpu(bcn_param->timestamp);
-               beacon_period = le16_to_cpu(bcn_param->beacon_period);
+       return 0;
+}
 
-               cap_info_bitmap = le16_to_cpu(bcn_param->cap_info_bitmap);
-               dev_dbg(adapter->dev, "info: InterpretIE: capabilities=0x%X\n",
-                       cap_info_bitmap);
+/* This function handles the command response of extended scan */
+int mwifiex_ret_802_11_scan_ext(struct mwifiex_private *priv)
+{
+       dev_dbg(priv->adapter->dev, "info: EXT scan returns successfully\n");
+       return 0;
+}
 
-               /* Rest of the current buffer are IE's */
-               ie_buf = current_ptr;
-               ie_len = curr_bcn_bytes;
-               dev_dbg(adapter->dev,
-                       "info: InterpretIE: IELength for this AP = %d\n",
-                       curr_bcn_bytes);
+/* This function This function handles the event extended scan report. It
+ * parses extended scan results and informs to cfg80211 stack.
+ */
+int mwifiex_handle_event_ext_scan_report(struct mwifiex_private *priv,
+                                        void *buf)
+{
+       int ret = 0;
+       struct mwifiex_adapter *adapter = priv->adapter;
+       u8 *bss_info;
+       u32 bytes_left, bytes_left_for_tlv, idx;
+       u16 type, len;
+       struct mwifiex_ie_types_data *tlv;
+       struct mwifiex_ie_types_bss_scan_rsp *scan_rsp_tlv;
+       struct mwifiex_ie_types_bss_scan_info *scan_info_tlv;
+       u8 *radio_type;
+       u64 fw_tsf = 0;
+       s32 rssi = 0;
+       struct mwifiex_event_scan_result *event_scan = buf;
+       u8 num_of_set = event_scan->num_of_set;
+       u8 *scan_resp = buf + sizeof(struct mwifiex_event_scan_result);
+       u16 scan_resp_size = le16_to_cpu(event_scan->buf_size);
+
+       if (num_of_set > MWIFIEX_MAX_AP) {
+               dev_err(adapter->dev,
+                       "EXT_SCAN: Invalid number of AP returned (%d)!!\n",
+                       num_of_set);
+               ret = -1;
+               goto check_next_scan;
+       }
 
-               while (curr_bcn_bytes >= sizeof(struct ieee_types_header)) {
-                       u8 element_id, element_len;
+       bytes_left = scan_resp_size;
+       dev_dbg(adapter->dev,
+               "EXT_SCAN: size %d, returned %d APs...",
+               scan_resp_size, num_of_set);
 
-                       element_id = *current_ptr;
-                       element_len = *(current_ptr + 1);
-                       if (curr_bcn_bytes < element_len +
-                                       sizeof(struct ieee_types_header)) {
-                               dev_err(priv->adapter->dev,
-                                       "%s: bytes left < IE length\n",
-                                       __func__);
-                               goto check_next_scan;
-                       }
-                       if (element_id == WLAN_EID_DS_PARAMS) {
-                               channel = *(current_ptr + sizeof(struct ieee_types_header));
-                               break;
-                       }
+       tlv = (struct mwifiex_ie_types_data *)scan_resp;
 
-                       current_ptr += element_len +
-                                       sizeof(struct ieee_types_header);
-                       curr_bcn_bytes -= element_len +
-                                       sizeof(struct ieee_types_header);
+       for (idx = 0; idx < num_of_set && bytes_left; idx++) {
+               type = le16_to_cpu(tlv->header.type);
+               len = le16_to_cpu(tlv->header.len);
+               if (bytes_left < sizeof(struct mwifiex_ie_types_header) + len) {
+                       dev_err(adapter->dev, "EXT_SCAN: Error bytes left < TLV length\n");
+                       break;
                }
+               scan_rsp_tlv = NULL;
+               scan_info_tlv = NULL;
+               bytes_left_for_tlv = bytes_left;
 
-               /*
-                * If the TSF TLV was appended to the scan results, save this
-                * entry's TSF value in the fw_tsf field. It is the firmware's
-                * TSF value at the time the beacon or probe response was
-                * received.
+               /* BSS response TLV with beacon or probe response buffer
+                * at the initial position of each descriptor
                 */
-               if (tsf_tlv)
-                       memcpy(&fw_tsf, &tsf_tlv->tsf_data[idx * TSF_DATA_SIZE],
-                              sizeof(fw_tsf));
-
-               if (channel) {
-                       struct ieee80211_channel *chan;
-                       u8 band;
+               if (type != TLV_TYPE_BSS_SCAN_RSP)
+                       break;
 
-                       /* Skip entry if on csa closed channel */
-                       if (channel == priv->csa_chan) {
-                               dev_dbg(adapter->dev,
-                                       "Dropping entry on csa closed channel\n");
+               bss_info = (u8 *)tlv;
+               scan_rsp_tlv = (struct mwifiex_ie_types_bss_scan_rsp *)tlv;
+               tlv = (struct mwifiex_ie_types_data *)(tlv->data + len);
+               bytes_left_for_tlv -=
+                               (len + sizeof(struct mwifiex_ie_types_header));
+
+               while (bytes_left_for_tlv >=
+                      sizeof(struct mwifiex_ie_types_header) &&
+                      le16_to_cpu(tlv->header.type) != TLV_TYPE_BSS_SCAN_RSP) {
+                       type = le16_to_cpu(tlv->header.type);
+                       len = le16_to_cpu(tlv->header.len);
+                       if (bytes_left_for_tlv <
+                           sizeof(struct mwifiex_ie_types_header) + len) {
+                               dev_err(adapter->dev,
+                                       "EXT_SCAN: Error in processing TLV, bytes left < TLV length\n");
+                               scan_rsp_tlv = NULL;
+                               bytes_left_for_tlv = 0;
                                continue;
                        }
-
-                       band = BAND_G;
-                       if (chan_band_tlv) {
-                               chan_band =
-                                       &chan_band_tlv->chan_band_param[idx];
-                               band = mwifiex_radio_type_to_band(
-                                               chan_band->radio_type
-                                               & (BIT(0) | BIT(1)));
-                       }
-
-                       cfp = mwifiex_get_cfp(priv, band, channel, 0);
-
-                       freq = cfp ? cfp->freq : 0;
-
-                       chan = ieee80211_get_channel(priv->wdev->wiphy, freq);
-
-                       if (chan && !(chan->flags & IEEE80211_CHAN_DISABLED)) {
-                               bss = cfg80211_inform_bss(priv->wdev->wiphy,
-                                             chan, bssid, timestamp,
-                                             cap_info_bitmap, beacon_period,
-                                             ie_buf, ie_len, rssi, GFP_KERNEL);
-                               bss_priv = (struct mwifiex_bss_priv *)bss->priv;
-                               bss_priv->band = band;
-                               bss_priv->fw_tsf = le64_to_cpu(fw_tsf);
-                               if (priv->media_connected &&
-                                   !memcmp(bssid,
-                                           priv->curr_bss_params.bss_descriptor
-                                           .mac_address, ETH_ALEN))
-                                       mwifiex_update_curr_bss_params(priv,
-                                                                      bss);
-                               cfg80211_put_bss(priv->wdev->wiphy, bss);
+                       switch (type) {
+                       case TLV_TYPE_BSS_SCAN_INFO:
+                               scan_info_tlv =
+                                 (struct mwifiex_ie_types_bss_scan_info *)tlv;
+                               if (len !=
+                                sizeof(struct mwifiex_ie_types_bss_scan_info) -
+                                sizeof(struct mwifiex_ie_types_header)) {
+                                       bytes_left_for_tlv = 0;
+                                       continue;
+                               }
+                               break;
+                       default:
+                               break;
                        }
-               } else {
-                       dev_dbg(adapter->dev, "missing BSS channel IE\n");
+                       tlv = (struct mwifiex_ie_types_data *)(tlv->data + len);
+                       bytes_left -=
+                               (len + sizeof(struct mwifiex_ie_types_header));
+                       bytes_left_for_tlv -=
+                               (len + sizeof(struct mwifiex_ie_types_header));
                }
-       }
 
-check_next_scan:
-       spin_lock_irqsave(&adapter->scan_pending_q_lock, flags);
-       if (list_empty(&adapter->scan_pending_q)) {
-               spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags);
-               spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags);
-               adapter->scan_processing = false;
-               spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags);
+               if (!scan_rsp_tlv)
+                       break;
 
-               /* Need to indicate IOCTL complete */
-               if (adapter->curr_cmd->wait_q_enabled) {
-                       adapter->cmd_wait_q.status = 0;
-                       if (!priv->scan_request) {
-                               dev_dbg(adapter->dev,
-                                       "complete internal scan\n");
-                               mwifiex_complete_cmd(adapter,
-                                                    adapter->curr_cmd);
-                       }
-               }
-               if (priv->report_scan_result)
-                       priv->report_scan_result = false;
+               /* Advance pointer to the beacon buffer length and
+                * update the bytes count so that the function
+                * wlan_interpret_bss_desc_with_ie() can handle the
+                * scan buffer withut any change
+                */
+               bss_info += sizeof(u16);
+               bytes_left -= sizeof(u16);
 
-               if (priv->scan_request) {
-                       dev_dbg(adapter->dev, "info: notifying scan done\n");
-                       cfg80211_scan_done(priv->scan_request, 0);
-                       priv->scan_request = NULL;
-               } else {
-                       priv->scan_aborting = false;
-                       dev_dbg(adapter->dev, "info: scan already aborted\n");
-               }
-       } else {
-               if ((priv->scan_aborting && !priv->scan_request) ||
-                   priv->scan_block) {
-                       spin_unlock_irqrestore(&adapter->scan_pending_q_lock,
-                                              flags);
-                       adapter->scan_delay_cnt = MWIFIEX_MAX_SCAN_DELAY_CNT;
-                       mod_timer(&priv->scan_delay_timer, jiffies);
-                       dev_dbg(priv->adapter->dev,
-                               "info: %s: triggerring scan abort\n", __func__);
-               } else if (!mwifiex_wmm_lists_empty(adapter) &&
-                          (priv->scan_request && (priv->scan_request->flags &
-                                           NL80211_SCAN_FLAG_LOW_PRIORITY))) {
-                       spin_unlock_irqrestore(&adapter->scan_pending_q_lock,
-                                              flags);
-                       adapter->scan_delay_cnt = 1;
-                       mod_timer(&priv->scan_delay_timer, jiffies +
-                                 msecs_to_jiffies(MWIFIEX_SCAN_DELAY_MSEC));
-                       dev_dbg(priv->adapter->dev,
-                               "info: %s: deferring scan\n", __func__);
+               if (scan_info_tlv) {
+                       rssi = (s32)(s16)(le16_to_cpu(scan_info_tlv->rssi));
+                       rssi *= 100;           /* Convert dBm to mBm */
+                       dev_dbg(adapter->dev,
+                               "info: InterpretIE: RSSI=%d\n", rssi);
+                       fw_tsf = le64_to_cpu(scan_info_tlv->tsf);
+                       radio_type = &scan_info_tlv->radio_type;
                } else {
-                       /* Get scan command from scan_pending_q and put to
-                          cmd_pending_q */
-                       cmd_node = list_first_entry(&adapter->scan_pending_q,
-                                                   struct cmd_ctrl_node, list);
-                       list_del(&cmd_node->list);
-                       spin_unlock_irqrestore(&adapter->scan_pending_q_lock,
-                                              flags);
-                       mwifiex_insert_cmd_to_pending_q(adapter, cmd_node,
-                                                       true);
+                       radio_type = NULL;
                }
+               ret = mwifiex_parse_single_response_buf(priv, &bss_info,
+                                                       &bytes_left, fw_tsf,
+                                                       radio_type, true, rssi);
+               if (ret)
+                       goto check_next_scan;
        }
 
+check_next_scan:
+       if (!event_scan->more_event)
+               mwifiex_check_next_scan_command(priv);
+
        return ret;
 }
 
index 9208a88..5aa3d39 100644 (file)
@@ -532,8 +532,228 @@ mwifiex_set_keyparamset_wep(struct mwifiex_private *priv,
        return 0;
 }
 
+/* This function populates key material v2 command
+ * to set network key for AES & CMAC AES.
+ */
+static int mwifiex_set_aes_key_v2(struct mwifiex_private *priv,
+                                 struct host_cmd_ds_command *cmd,
+                                 struct mwifiex_ds_encrypt_key *enc_key,
+                                 struct host_cmd_ds_802_11_key_material_v2 *km)
+{
+       struct mwifiex_adapter *adapter = priv->adapter;
+       u16 size, len = KEY_PARAMS_FIXED_LEN;
+
+       if (enc_key->is_igtk_key) {
+               dev_dbg(adapter->dev, "%s: Set CMAC AES Key\n", __func__);
+               if (enc_key->is_rx_seq_valid)
+                       memcpy(km->key_param_set.key_params.cmac_aes.ipn,
+                              enc_key->pn, enc_key->pn_len);
+               km->key_param_set.key_info &= cpu_to_le16(~KEY_MCAST);
+               km->key_param_set.key_info |= cpu_to_le16(KEY_IGTK);
+               km->key_param_set.key_type = KEY_TYPE_ID_AES_CMAC;
+               km->key_param_set.key_params.cmac_aes.key_len =
+                                         cpu_to_le16(enc_key->key_len);
+               memcpy(km->key_param_set.key_params.cmac_aes.key,
+                      enc_key->key_material, enc_key->key_len);
+               len += sizeof(struct mwifiex_cmac_aes_param);
+       } else {
+               dev_dbg(adapter->dev, "%s: Set AES Key\n", __func__);
+               if (enc_key->is_rx_seq_valid)
+                       memcpy(km->key_param_set.key_params.aes.pn,
+                              enc_key->pn, enc_key->pn_len);
+               km->key_param_set.key_type = KEY_TYPE_ID_AES;
+               km->key_param_set.key_params.aes.key_len =
+                                         cpu_to_le16(enc_key->key_len);
+               memcpy(km->key_param_set.key_params.aes.key,
+                      enc_key->key_material, enc_key->key_len);
+               len += sizeof(struct mwifiex_aes_param);
+       }
+
+       km->key_param_set.len = cpu_to_le16(len);
+       size = len + sizeof(struct mwifiex_ie_types_header) +
+              sizeof(km->action) + S_DS_GEN;
+       cmd->size = cpu_to_le16(size);
+
+       return 0;
+}
+
+/* This function prepares command to set/get/reset network key(s).
+ * This function prepares key material command for V2 format.
+ * Preparation includes -
+ *      - Setting command ID, action and proper size
+ *      - Setting WEP keys, WAPI keys or WPA keys along with required
+ *        encryption (TKIP, AES) (as required)
+ *      - Ensuring correct endian-ness
+ */
+static int
+mwifiex_cmd_802_11_key_material_v2(struct mwifiex_private *priv,
+                                  struct host_cmd_ds_command *cmd,
+                                  u16 cmd_action, u32 cmd_oid,
+                                  struct mwifiex_ds_encrypt_key *enc_key)
+{
+       struct mwifiex_adapter *adapter = priv->adapter;
+       u8 *mac = enc_key->mac_addr;
+       u16 key_info, len = KEY_PARAMS_FIXED_LEN;
+       struct host_cmd_ds_802_11_key_material_v2 *km =
+                                               &cmd->params.key_material_v2;
+
+       cmd->command = cpu_to_le16(HostCmd_CMD_802_11_KEY_MATERIAL);
+       km->action = cpu_to_le16(cmd_action);
+
+       if (cmd_action == HostCmd_ACT_GEN_GET) {
+               dev_dbg(adapter->dev, "%s: Get key\n", __func__);
+               km->key_param_set.key_idx =
+                                       enc_key->key_index & KEY_INDEX_MASK;
+               km->key_param_set.type = cpu_to_le16(TLV_TYPE_KEY_PARAM_V2);
+               km->key_param_set.len = cpu_to_le16(KEY_PARAMS_FIXED_LEN);
+               memcpy(km->key_param_set.mac_addr, mac, ETH_ALEN);
+
+               if (enc_key->key_index & MWIFIEX_KEY_INDEX_UNICAST)
+                       key_info = KEY_UNICAST;
+               else
+                       key_info = KEY_MCAST;
+
+               if (enc_key->is_igtk_key)
+                       key_info |= KEY_IGTK;
+
+               km->key_param_set.key_info = cpu_to_le16(key_info);
+
+               cmd->size = cpu_to_le16(sizeof(struct mwifiex_ie_types_header) +
+                                       S_DS_GEN + KEY_PARAMS_FIXED_LEN +
+                                       sizeof(km->action));
+               return 0;
+       }
+
+       memset(&km->key_param_set, 0,
+              sizeof(struct mwifiex_ie_type_key_param_set_v2));
+
+       if (enc_key->key_disable) {
+               dev_dbg(adapter->dev, "%s: Remove key\n", __func__);
+               km->action = cpu_to_le16(HostCmd_ACT_GEN_REMOVE);
+               km->key_param_set.type = cpu_to_le16(TLV_TYPE_KEY_PARAM_V2);
+               km->key_param_set.len = cpu_to_le16(KEY_PARAMS_FIXED_LEN);
+               km->key_param_set.key_idx = enc_key->key_index & KEY_INDEX_MASK;
+               key_info = KEY_MCAST | KEY_UNICAST;
+               km->key_param_set.key_info = cpu_to_le16(key_info);
+               memcpy(km->key_param_set.mac_addr, mac, ETH_ALEN);
+               cmd->size = cpu_to_le16(sizeof(struct mwifiex_ie_types_header) +
+                                       S_DS_GEN + KEY_PARAMS_FIXED_LEN +
+                                       sizeof(km->action));
+               return 0;
+       }
+
+       km->action = cpu_to_le16(HostCmd_ACT_GEN_SET);
+       km->key_param_set.key_idx = enc_key->key_index & KEY_INDEX_MASK;
+       km->key_param_set.type = cpu_to_le16(TLV_TYPE_KEY_PARAM_V2);
+       key_info = KEY_ENABLED;
+       memcpy(km->key_param_set.mac_addr, mac, ETH_ALEN);
+
+       if (enc_key->key_len <= WLAN_KEY_LEN_WEP104) {
+               dev_dbg(adapter->dev, "%s: Set WEP Key\n", __func__);
+               len += sizeof(struct mwifiex_wep_param);
+               km->key_param_set.len = cpu_to_le16(len);
+               km->key_param_set.key_type = KEY_TYPE_ID_WEP;
+
+               if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) {
+                               key_info |= KEY_MCAST | KEY_UNICAST;
+               } else {
+                       if (enc_key->is_current_wep_key) {
+                               key_info |= KEY_MCAST | KEY_UNICAST;
+                               if (km->key_param_set.key_idx ==
+                                   (priv->wep_key_curr_index & KEY_INDEX_MASK))
+                                       key_info |= KEY_DEFAULT;
+                       } else {
+                               if (mac) {
+                                       if (is_broadcast_ether_addr(mac))
+                                               key_info |= KEY_MCAST;
+                                       else
+                                               key_info |= KEY_UNICAST |
+                                                           KEY_DEFAULT;
+                               } else {
+                                       key_info |= KEY_MCAST;
+                               }
+                       }
+               }
+               km->key_param_set.key_info = cpu_to_le16(key_info);
+
+               km->key_param_set.key_params.wep.key_len =
+                                                 cpu_to_le16(enc_key->key_len);
+               memcpy(km->key_param_set.key_params.wep.key,
+                      enc_key->key_material, enc_key->key_len);
+
+               cmd->size = cpu_to_le16(sizeof(struct mwifiex_ie_types_header) +
+                                       len + sizeof(km->action) + S_DS_GEN);
+               return 0;
+       }
+
+       if (is_broadcast_ether_addr(mac))
+               key_info |= KEY_MCAST | KEY_RX_KEY;
+       else
+               key_info |= KEY_UNICAST | KEY_TX_KEY | KEY_RX_KEY;
+
+       if (enc_key->is_wapi_key) {
+               dev_dbg(adapter->dev, "%s: Set WAPI Key\n", __func__);
+               km->key_param_set.key_type = KEY_TYPE_ID_WAPI;
+               memcpy(km->key_param_set.key_params.wapi.pn, enc_key->pn,
+                      PN_LEN);
+               km->key_param_set.key_params.wapi.key_len =
+                                               cpu_to_le16(enc_key->key_len);
+               memcpy(km->key_param_set.key_params.wapi.key,
+                      enc_key->key_material, enc_key->key_len);
+               if (is_broadcast_ether_addr(mac))
+                       priv->sec_info.wapi_key_on = true;
+
+               if (!priv->sec_info.wapi_key_on)
+                       key_info |= KEY_DEFAULT;
+               km->key_param_set.key_info = cpu_to_le16(key_info);
+
+               len += sizeof(struct mwifiex_wapi_param);
+               km->key_param_set.len = cpu_to_le16(len);
+               cmd->size = cpu_to_le16(sizeof(struct mwifiex_ie_types_header) +
+                                       len + sizeof(km->action) + S_DS_GEN);
+               return 0;
+       }
+
+       if (priv->bss_mode == NL80211_IFTYPE_ADHOC) {
+               key_info |= KEY_DEFAULT;
+               /* Enable unicast bit for WPA-NONE/ADHOC_AES */
+               if (!priv->sec_info.wpa2_enabled &&
+                   !is_broadcast_ether_addr(mac))
+                       key_info |= KEY_UNICAST;
+       } else {
+               /* Enable default key for WPA/WPA2 */
+               if (!priv->wpa_is_gtk_set)
+                       key_info |= KEY_DEFAULT;
+       }
+
+       km->key_param_set.key_info = cpu_to_le16(key_info);
+
+       if (enc_key->key_len == WLAN_KEY_LEN_CCMP)
+               return mwifiex_set_aes_key_v2(priv, cmd, enc_key, km);
+
+       if (enc_key->key_len == WLAN_KEY_LEN_TKIP) {
+               dev_dbg(adapter->dev, "%s: Set TKIP Key\n", __func__);
+               if (enc_key->is_rx_seq_valid)
+                       memcpy(km->key_param_set.key_params.tkip.pn,
+                              enc_key->pn, enc_key->pn_len);
+               km->key_param_set.key_type = KEY_TYPE_ID_TKIP;
+               km->key_param_set.key_params.tkip.key_len =
+                                               cpu_to_le16(enc_key->key_len);
+               memcpy(km->key_param_set.key_params.tkip.key,
+                      enc_key->key_material, enc_key->key_len);
+
+               len += sizeof(struct mwifiex_tkip_param);
+               km->key_param_set.len = cpu_to_le16(len);
+               cmd->size = cpu_to_le16(sizeof(struct mwifiex_ie_types_header) +
+                                       len + sizeof(km->action) + S_DS_GEN);
+       }
+
+       return 0;
+}
+
 /*
  * This function prepares command to set/get/reset network key(s).
+ * This function prepares key material command for V1 format.
  *
  * Preparation includes -
  *      - Setting command ID, action and proper size
@@ -542,10 +762,10 @@ mwifiex_set_keyparamset_wep(struct mwifiex_private *priv,
  *      - Ensuring correct endian-ness
  */
 static int
-mwifiex_cmd_802_11_key_material(struct mwifiex_private *priv,
-                               struct host_cmd_ds_command *cmd,
-                               u16 cmd_action, u32 cmd_oid,
-                               struct mwifiex_ds_encrypt_key *enc_key)
+mwifiex_cmd_802_11_key_material_v1(struct mwifiex_private *priv,
+                                  struct host_cmd_ds_command *cmd,
+                                  u16 cmd_action, u32 cmd_oid,
+                                  struct mwifiex_ds_encrypt_key *enc_key)
 {
        struct host_cmd_ds_802_11_key_material *key_material =
                &cmd->params.key_material;
@@ -724,6 +944,24 @@ mwifiex_cmd_802_11_key_material(struct mwifiex_private *priv,
        return ret;
 }
 
+/* Wrapper function for setting network key depending upon FW KEY API version */
+static int
+mwifiex_cmd_802_11_key_material(struct mwifiex_private *priv,
+                               struct host_cmd_ds_command *cmd,
+                               u16 cmd_action, u32 cmd_oid,
+                               struct mwifiex_ds_encrypt_key *enc_key)
+{
+       if (priv->adapter->fw_key_api_major_ver == FW_KEY_API_VER_MAJOR_V2)
+               return mwifiex_cmd_802_11_key_material_v2(priv, cmd,
+                                                         cmd_action, cmd_oid,
+                                                         enc_key);
+
+       else
+               return mwifiex_cmd_802_11_key_material_v1(priv, cmd,
+                                                         cmd_action, cmd_oid,
+                                                         enc_key);
+}
+
 /*
  * This function prepares command to set/get 11d domain information.
  *
@@ -1280,6 +1518,127 @@ mwifiex_cmd_coalesce_cfg(struct mwifiex_private *priv,
        return 0;
 }
 
+static int
+mwifiex_cmd_tdls_oper(struct mwifiex_private *priv,
+                     struct host_cmd_ds_command *cmd,
+                     void *data_buf)
+{
+       struct host_cmd_ds_tdls_oper *tdls_oper = &cmd->params.tdls_oper;
+       struct mwifiex_ds_tdls_oper *oper = data_buf;
+       struct mwifiex_sta_node *sta_ptr;
+       struct host_cmd_tlv_rates *tlv_rates;
+       struct mwifiex_ie_types_htcap *ht_capab;
+       struct mwifiex_ie_types_qos_info *wmm_qos_info;
+       struct mwifiex_ie_types_extcap *extcap;
+       struct mwifiex_ie_types_vhtcap *vht_capab;
+       struct mwifiex_ie_types_aid *aid;
+       u8 *pos, qos_info;
+       u16 config_len = 0;
+       struct station_parameters *params = priv->sta_params;
+
+       cmd->command = cpu_to_le16(HostCmd_CMD_TDLS_OPER);
+       cmd->size = cpu_to_le16(S_DS_GEN);
+       le16_add_cpu(&cmd->size, sizeof(struct host_cmd_ds_tdls_oper));
+
+       tdls_oper->reason = 0;
+       memcpy(tdls_oper->peer_mac, oper->peer_mac, ETH_ALEN);
+       sta_ptr = mwifiex_get_sta_entry(priv, oper->peer_mac);
+
+       pos = (u8 *)tdls_oper + sizeof(struct host_cmd_ds_tdls_oper);
+
+       switch (oper->tdls_action) {
+       case MWIFIEX_TDLS_DISABLE_LINK:
+               tdls_oper->tdls_action = cpu_to_le16(ACT_TDLS_DELETE);
+               break;
+       case MWIFIEX_TDLS_CREATE_LINK:
+               tdls_oper->tdls_action = cpu_to_le16(ACT_TDLS_CREATE);
+               break;
+       case MWIFIEX_TDLS_CONFIG_LINK:
+               tdls_oper->tdls_action = cpu_to_le16(ACT_TDLS_CONFIG);
+
+               if (!params) {
+                       dev_err(priv->adapter->dev,
+                               "TDLS config params not available for %pM\n",
+                               oper->peer_mac);
+                       return -ENODATA;
+               }
+
+               *(__le16 *)pos = cpu_to_le16(params->capability);
+               config_len += sizeof(params->capability);
+
+               qos_info = params->uapsd_queues | (params->max_sp << 5);
+               wmm_qos_info = (struct mwifiex_ie_types_qos_info *)(pos +
+                                                                   config_len);
+               wmm_qos_info->header.type = cpu_to_le16(WLAN_EID_QOS_CAPA);
+               wmm_qos_info->header.len = cpu_to_le16(sizeof(qos_info));
+               wmm_qos_info->qos_info = qos_info;
+               config_len += sizeof(struct mwifiex_ie_types_qos_info);
+
+               if (params->ht_capa) {
+                       ht_capab = (struct mwifiex_ie_types_htcap *)(pos +
+                                                                   config_len);
+                       ht_capab->header.type =
+                                           cpu_to_le16(WLAN_EID_HT_CAPABILITY);
+                       ht_capab->header.len =
+                                  cpu_to_le16(sizeof(struct ieee80211_ht_cap));
+                       memcpy(&ht_capab->ht_cap, params->ht_capa,
+                              sizeof(struct ieee80211_ht_cap));
+                       config_len += sizeof(struct mwifiex_ie_types_htcap);
+               }
+
+               if (params->supported_rates && params->supported_rates_len) {
+                       tlv_rates = (struct host_cmd_tlv_rates *)(pos +
+                                                                 config_len);
+                       tlv_rates->header.type =
+                                              cpu_to_le16(WLAN_EID_SUPP_RATES);
+                       tlv_rates->header.len =
+                                      cpu_to_le16(params->supported_rates_len);
+                       memcpy(tlv_rates->rates, params->supported_rates,
+                              params->supported_rates_len);
+                       config_len += sizeof(struct host_cmd_tlv_rates) +
+                                     params->supported_rates_len;
+               }
+
+               if (params->ext_capab && params->ext_capab_len) {
+                       extcap = (struct mwifiex_ie_types_extcap *)(pos +
+                                                                   config_len);
+                       extcap->header.type =
+                                          cpu_to_le16(WLAN_EID_EXT_CAPABILITY);
+                       extcap->header.len = cpu_to_le16(params->ext_capab_len);
+                       memcpy(extcap->ext_capab, params->ext_capab,
+                              params->ext_capab_len);
+                       config_len += sizeof(struct mwifiex_ie_types_extcap) +
+                                     params->ext_capab_len;
+               }
+               if (params->vht_capa) {
+                       vht_capab = (struct mwifiex_ie_types_vhtcap *)(pos +
+                                                                   config_len);
+                       vht_capab->header.type =
+                                          cpu_to_le16(WLAN_EID_VHT_CAPABILITY);
+                       vht_capab->header.len =
+                                 cpu_to_le16(sizeof(struct ieee80211_vht_cap));
+                       memcpy(&vht_capab->vht_cap, params->vht_capa,
+                              sizeof(struct ieee80211_vht_cap));
+                       config_len += sizeof(struct mwifiex_ie_types_vhtcap);
+               }
+               if (params->aid) {
+                       aid = (struct mwifiex_ie_types_aid *)(pos + config_len);
+                       aid->header.type = cpu_to_le16(WLAN_EID_AID);
+                       aid->header.len = cpu_to_le16(sizeof(params->aid));
+                       aid->aid = cpu_to_le16(params->aid);
+                       config_len += sizeof(struct mwifiex_ie_types_aid);
+               }
+
+               break;
+       default:
+               dev_err(priv->adapter->dev, "Unknown TDLS operation\n");
+               return -ENOTSUPP;
+       }
+
+       le16_add_cpu(&cmd->size, config_len);
+
+       return 0;
+}
 /*
  * This function prepares the commands before sending them to the firmware.
  *
@@ -1472,6 +1831,9 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no,
                ret = mwifiex_cmd_ibss_coalescing_status(cmd_ptr, cmd_action,
                                                         data_buf);
                break;
+       case HostCmd_CMD_802_11_SCAN_EXT:
+               ret = mwifiex_cmd_802_11_scan_ext(priv, cmd_ptr, data_buf);
+               break;
        case HostCmd_CMD_MAC_REG_ACCESS:
        case HostCmd_CMD_BBP_REG_ACCESS:
        case HostCmd_CMD_RF_REG_ACCESS:
@@ -1507,6 +1869,9 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no,
                ret = mwifiex_cmd_coalesce_cfg(priv, cmd_ptr, cmd_action,
                                               data_buf);
                break;
+       case HostCmd_CMD_TDLS_OPER:
+               ret = mwifiex_cmd_tdls_oper(priv, cmd_ptr, data_buf);
+               break;
        default:
                dev_err(priv->adapter->dev,
                        "PREP_CMD: unknown cmd- %#x\n", cmd_no);
index 24523e4..1c5e188 100644 (file)
@@ -69,6 +69,7 @@ mwifiex_process_cmdresp_error(struct mwifiex_private *priv,
 
                break;
        case HostCmd_CMD_802_11_SCAN:
+       case HostCmd_CMD_802_11_SCAN_EXT:
                /* Cancel all pending scan command */
                spin_lock_irqsave(&adapter->scan_pending_q_lock, flags);
                list_for_each_entry_safe(cmd_node, tmp_node,
@@ -561,13 +562,13 @@ static int mwifiex_ret_802_11_ad_hoc_stop(struct mwifiex_private *priv,
 }
 
 /*
- * This function handles the command response of set/get key material.
+ * This function handles the command response of set/get v1 key material.
  *
  * Handling includes updating the driver parameters to reflect the
  * changes.
  */
-static int mwifiex_ret_802_11_key_material(struct mwifiex_private *priv,
-                                          struct host_cmd_ds_command *resp)
+static int mwifiex_ret_802_11_key_material_v1(struct mwifiex_private *priv,
+                                             struct host_cmd_ds_command *resp)
 {
        struct host_cmd_ds_802_11_key_material *key =
                                                &resp->params.key_material;
@@ -589,6 +590,51 @@ static int mwifiex_ret_802_11_key_material(struct mwifiex_private *priv,
        return 0;
 }
 
+/*
+ * This function handles the command response of set/get v2 key material.
+ *
+ * Handling includes updating the driver parameters to reflect the
+ * changes.
+ */
+static int mwifiex_ret_802_11_key_material_v2(struct mwifiex_private *priv,
+                                             struct host_cmd_ds_command *resp)
+{
+       struct host_cmd_ds_802_11_key_material_v2 *key_v2;
+       __le16 len;
+
+       key_v2 = &resp->params.key_material_v2;
+       if (le16_to_cpu(key_v2->action) == HostCmd_ACT_GEN_SET) {
+               if ((le16_to_cpu(key_v2->key_param_set.key_info) & KEY_MCAST)) {
+                       dev_dbg(priv->adapter->dev, "info: key: GTK is set\n");
+                       priv->wpa_is_gtk_set = true;
+                       priv->scan_block = false;
+               }
+       }
+
+       if (key_v2->key_param_set.key_type != KEY_TYPE_ID_AES)
+               return 0;
+
+       memset(priv->aes_key_v2.key_param_set.key_params.aes.key, 0,
+              WLAN_KEY_LEN_CCMP);
+       priv->aes_key_v2.key_param_set.key_params.aes.key_len =
+                               key_v2->key_param_set.key_params.aes.key_len;
+       len = priv->aes_key_v2.key_param_set.key_params.aes.key_len;
+       memcpy(priv->aes_key_v2.key_param_set.key_params.aes.key,
+              key_v2->key_param_set.key_params.aes.key, le16_to_cpu(len));
+
+       return 0;
+}
+
+/* Wrapper function for processing response of key material command */
+static int mwifiex_ret_802_11_key_material(struct mwifiex_private *priv,
+                                          struct host_cmd_ds_command *resp)
+{
+       if (priv->adapter->fw_key_api_major_ver == FW_KEY_API_VER_MAJOR_V2)
+               return mwifiex_ret_802_11_key_material_v2(priv, resp);
+       else
+               return mwifiex_ret_802_11_key_material_v1(priv, resp);
+}
+
 /*
  * This function handles the command response of get 11d domain information.
  */
@@ -800,7 +846,60 @@ static int mwifiex_ret_ibss_coalescing_status(struct mwifiex_private *priv,
 
        return 0;
 }
+static int mwifiex_ret_tdls_oper(struct mwifiex_private *priv,
+                                struct host_cmd_ds_command *resp)
+{
+       struct host_cmd_ds_tdls_oper *cmd_tdls_oper = &resp->params.tdls_oper;
+       u16 reason = le16_to_cpu(cmd_tdls_oper->reason);
+       u16 action = le16_to_cpu(cmd_tdls_oper->tdls_action);
+       struct mwifiex_sta_node *node =
+                          mwifiex_get_sta_entry(priv, cmd_tdls_oper->peer_mac);
 
+       switch (action) {
+       case ACT_TDLS_DELETE:
+               if (reason)
+                       dev_err(priv->adapter->dev,
+                               "TDLS link delete for %pM failed: reason %d\n",
+                               cmd_tdls_oper->peer_mac, reason);
+               else
+                       dev_dbg(priv->adapter->dev,
+                               "TDLS link config for %pM successful\n",
+                               cmd_tdls_oper->peer_mac);
+               break;
+       case ACT_TDLS_CREATE:
+               if (reason) {
+                       dev_err(priv->adapter->dev,
+                               "TDLS link creation for %pM failed: reason %d",
+                               cmd_tdls_oper->peer_mac, reason);
+                       if (node && reason != TDLS_ERR_LINK_EXISTS)
+                               node->tdls_status = TDLS_SETUP_FAILURE;
+               } else {
+                       dev_dbg(priv->adapter->dev,
+                               "TDLS link creation for %pM successful",
+                               cmd_tdls_oper->peer_mac);
+               }
+               break;
+       case ACT_TDLS_CONFIG:
+               if (reason) {
+                       dev_err(priv->adapter->dev,
+                               "TDLS link config for %pM failed, reason %d\n",
+                               cmd_tdls_oper->peer_mac, reason);
+                       if (node)
+                               node->tdls_status = TDLS_SETUP_FAILURE;
+               } else {
+                       dev_dbg(priv->adapter->dev,
+                               "TDLS link config for %pM successful\n",
+                               cmd_tdls_oper->peer_mac);
+               }
+               break;
+       default:
+               dev_err(priv->adapter->dev,
+                       "Unknown TDLS command action respnse %d", action);
+               return -1;
+       }
+
+       return 0;
+}
 /*
  * This function handles the command response for subscribe event command.
  */
@@ -871,6 +970,10 @@ int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no,
                ret = mwifiex_ret_802_11_scan(priv, resp);
                adapter->curr_cmd->wait_q_enabled = false;
                break;
+       case HostCmd_CMD_802_11_SCAN_EXT:
+               ret = mwifiex_ret_802_11_scan_ext(priv);
+               adapter->curr_cmd->wait_q_enabled = false;
+               break;
        case HostCmd_CMD_802_11_BG_SCAN_QUERY:
                ret = mwifiex_ret_802_11_scan(priv, resp);
                dev_dbg(adapter->dev,
@@ -999,6 +1102,9 @@ int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no,
                break;
        case HostCmd_CMD_COALESCE_CFG:
                break;
+       case HostCmd_CMD_TDLS_OPER:
+               ret = mwifiex_ret_tdls_oper(priv, resp);
+               break;
        default:
                dev_err(adapter->dev, "CMD_RESP: unknown cmd response %#x\n",
                        resp->command);
index 8c351f7..92ff7b3 100644 (file)
@@ -54,6 +54,10 @@ mwifiex_reset_connect_state(struct mwifiex_private *priv, u16 reason_code)
 
        priv->scan_block = false;
 
+       if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) &&
+           ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info))
+               mwifiex_disable_all_tdls_links(priv);
+
        /* Free Tx and Rx packets, report disconnect to upper layer */
        mwifiex_clean_txrx(priv);
 
@@ -331,6 +335,14 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv)
                dev_dbg(adapter->dev, "event: PORT RELEASE\n");
                break;
 
+       case EVENT_EXT_SCAN_REPORT:
+               dev_dbg(adapter->dev, "event: EXT_SCAN Report\n");
+               if (adapter->ext_scan)
+                       ret = mwifiex_handle_event_ext_scan_report(priv,
+                                               adapter->event_skb->data);
+
+               break;
+
        case EVENT_WMM_STATUS_CHANGE:
                dev_dbg(adapter->dev, "event: WMM status changed\n");
                ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_WMM_GET_STATUS,
index c5cb2ed..b393d55 100644 (file)
@@ -290,7 +290,7 @@ int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss,
 
                if (mwifiex_band_to_radio_type(bss_desc->bss_band) ==
                                                HostCmd_SCAN_RADIO_TYPE_BG)
-                       config_bands = BAND_B | BAND_G | BAND_GN | BAND_GAC;
+                       config_bands = BAND_B | BAND_G | BAND_GN;
                else
                        config_bands = BAND_A | BAND_AN | BAND_AAC;
 
@@ -865,6 +865,7 @@ static int mwifiex_sec_ioctl_set_wapi_key(struct mwifiex_private *priv,
 static int mwifiex_sec_ioctl_set_wep_key(struct mwifiex_private *priv,
                              struct mwifiex_ds_encrypt_key *encrypt_key)
 {
+       struct mwifiex_adapter *adapter = priv->adapter;
        int ret;
        struct mwifiex_wep_key *wep_key;
        int index;
@@ -879,10 +880,17 @@ static int mwifiex_sec_ioctl_set_wep_key(struct mwifiex_private *priv,
                /* Copy the required key as the current key */
                wep_key = &priv->wep_key[index];
                if (!wep_key->key_length) {
-                       dev_err(priv->adapter->dev,
+                       dev_err(adapter->dev,
                                "key not set, so cannot enable it\n");
                        return -1;
                }
+
+               if (adapter->fw_key_api_major_ver == FW_KEY_API_VER_MAJOR_V2) {
+                       memcpy(encrypt_key->key_material,
+                              wep_key->key_material, wep_key->key_length);
+                       encrypt_key->key_len = wep_key->key_length;
+               }
+
                priv->wep_key_curr_index = (u16) index;
                priv->sec_info.wep_enabled = 1;
        } else {
@@ -897,13 +905,25 @@ static int mwifiex_sec_ioctl_set_wep_key(struct mwifiex_private *priv,
                priv->sec_info.wep_enabled = 1;
        }
        if (wep_key->key_length) {
+               void *enc_key;
+
+               if (encrypt_key->key_disable)
+                       memset(&priv->wep_key[index], 0,
+                              sizeof(struct mwifiex_wep_key));
+
+               if (adapter->fw_key_api_major_ver == FW_KEY_API_VER_MAJOR_V2)
+                       enc_key = encrypt_key;
+               else
+                       enc_key = NULL;
+
                /* Send request to firmware */
                ret = mwifiex_send_cmd_async(priv,
                                             HostCmd_CMD_802_11_KEY_MATERIAL,
-                                            HostCmd_ACT_GEN_SET, 0, NULL);
+                                            HostCmd_ACT_GEN_SET, 0, enc_key);
                if (ret)
                        return ret;
        }
+
        if (priv->sec_info.wep_enabled)
                priv->curr_pkt_filter |= HostCmd_ACT_MAC_WEP_ENABLE;
        else
@@ -1044,19 +1064,27 @@ int mwifiex_set_encode(struct mwifiex_private *priv, struct key_params *kp,
 
        memset(&encrypt_key, 0, sizeof(struct mwifiex_ds_encrypt_key));
        encrypt_key.key_len = key_len;
+       encrypt_key.key_index = key_index;
 
        if (kp && kp->cipher == WLAN_CIPHER_SUITE_AES_CMAC)
                encrypt_key.is_igtk_key = true;
 
        if (!disable) {
-               encrypt_key.key_index = key_index;
                if (key_len)
                        memcpy(encrypt_key.key_material, key, key_len);
+               else
+                       encrypt_key.is_current_wep_key = true;
+
                if (mac_addr)
                        memcpy(encrypt_key.mac_addr, mac_addr, ETH_ALEN);
-               if (kp && kp->seq && kp->seq_len)
+               if (kp && kp->seq && kp->seq_len) {
                        memcpy(encrypt_key.pn, kp->seq, kp->seq_len);
+                       encrypt_key.pn_len = kp->seq_len;
+                       encrypt_key.is_rx_seq_valid = true;
+               }
        } else {
+               if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP)
+                       return 0;
                encrypt_key.key_disable = true;
                if (mac_addr)
                        memcpy(encrypt_key.mac_addr, mac_addr, ETH_ALEN);
@@ -1391,7 +1419,7 @@ static int mwifiex_misc_ioctl_gen_ie(struct mwifiex_private *priv,
  * with requisite parameters and calls the IOCTL handler.
  */
 int
-mwifiex_set_gen_ie(struct mwifiex_private *priv, u8 *ie, int ie_len)
+mwifiex_set_gen_ie(struct mwifiex_private *priv, const u8 *ie, int ie_len)
 {
        struct mwifiex_ds_misc_gen_ie gen_ie;
 
index 4651d67..b6aa958 100644 (file)
@@ -88,11 +88,14 @@ int mwifiex_process_rx_packet(struct mwifiex_private *priv,
        struct rxpd *local_rx_pd;
        int hdr_chop;
        struct ethhdr *eth;
+       u16 rx_pkt_off, rx_pkt_len;
+       u8 *offset;
 
        local_rx_pd = (struct rxpd *) (skb->data);
 
-       rx_pkt_hdr = (void *)local_rx_pd +
-                    le16_to_cpu(local_rx_pd->rx_pkt_offset);
+       rx_pkt_off = le16_to_cpu(local_rx_pd->rx_pkt_offset);
+       rx_pkt_len = le16_to_cpu(local_rx_pd->rx_pkt_length);
+       rx_pkt_hdr = (void *)local_rx_pd + rx_pkt_off;
 
        if ((!memcmp(&rx_pkt_hdr->rfc1042_hdr, bridge_tunnel_header,
                     sizeof(bridge_tunnel_header))) ||
@@ -142,6 +145,12 @@ int mwifiex_process_rx_packet(struct mwifiex_private *priv,
                return 0;
        }
 
+       if (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) &&
+           ntohs(rx_pkt_hdr->eth803_hdr.h_proto) == ETH_P_TDLS) {
+               offset = (u8 *)local_rx_pd + rx_pkt_off;
+               mwifiex_process_tdls_action_frame(priv, offset, rx_pkt_len);
+       }
+
        priv->rxpd_rate = local_rx_pd->rx_rate;
 
        priv->rxpd_htinfo = local_rx_pd->ht_info;
index 354d64c..1236a5d 100644 (file)
@@ -95,6 +95,9 @@ void *mwifiex_process_sta_txpd(struct mwifiex_private *priv,
                }
        }
 
+       if (tx_info->flags & MWIFIEX_BUF_FLAG_TDLS_PKT)
+               local_tx_pd->flags |= MWIFIEX_TXPD_FLAGS_TDLS_PACKET;
+
        /* Offset of actual data */
        pkt_offset = sizeof(struct txpd) + pad;
        if (pkt_type == PKT_TYPE_MGMT) {
diff --git a/drivers/net/wireless/mwifiex/tdls.c b/drivers/net/wireless/mwifiex/tdls.c
new file mode 100644 (file)
index 0000000..5efd456
--- /dev/null
@@ -0,0 +1,1044 @@
+/* Marvell Wireless LAN device driver: TDLS handling
+ *
+ * Copyright (C) 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
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available on the worldwide web at
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+#include "main.h"
+#include "wmm.h"
+#include "11n.h"
+#include "11n_rxreorder.h"
+#include "11ac.h"
+
+#define TDLS_REQ_FIX_LEN      6
+#define TDLS_RESP_FIX_LEN     8
+#define TDLS_CONFIRM_FIX_LEN  6
+
+static void
+mwifiex_restore_tdls_packets(struct mwifiex_private *priv, u8 *mac, u8 status)
+{
+       struct mwifiex_ra_list_tbl *ra_list;
+       struct list_head *tid_list;
+       struct sk_buff *skb, *tmp;
+       struct mwifiex_txinfo *tx_info;
+       unsigned long flags;
+       u32 tid;
+       u8 tid_down;
+
+       dev_dbg(priv->adapter->dev, "%s: %pM\n", __func__, mac);
+       spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags);
+
+       skb_queue_walk_safe(&priv->tdls_txq, skb, tmp) {
+               if (!ether_addr_equal(mac, skb->data))
+                       continue;
+
+               __skb_unlink(skb, &priv->tdls_txq);
+               tx_info = MWIFIEX_SKB_TXCB(skb);
+               tid = skb->priority;
+               tid_down = mwifiex_wmm_downgrade_tid(priv, tid);
+
+               if (status == TDLS_SETUP_COMPLETE) {
+                       ra_list = mwifiex_wmm_get_queue_raptr(priv, tid, mac);
+                       ra_list->tdls_link = true;
+                       tx_info->flags |= MWIFIEX_BUF_FLAG_TDLS_PKT;
+               } else {
+                       tid_list = &priv->wmm.tid_tbl_ptr[tid_down].ra_list;
+                       if (!list_empty(tid_list))
+                               ra_list = list_first_entry(tid_list,
+                                             struct mwifiex_ra_list_tbl, list);
+                       else
+                               ra_list = NULL;
+                       tx_info->flags &= ~MWIFIEX_BUF_FLAG_TDLS_PKT;
+               }
+
+               if (!ra_list) {
+                       mwifiex_write_data_complete(priv->adapter, skb, 0, -1);
+                       continue;
+               }
+
+               skb_queue_tail(&ra_list->skb_head, skb);
+
+               ra_list->ba_pkt_count++;
+               ra_list->total_pkt_count++;
+
+               if (atomic_read(&priv->wmm.highest_queued_prio) <
+                                                      tos_to_tid_inv[tid_down])
+                       atomic_set(&priv->wmm.highest_queued_prio,
+                                  tos_to_tid_inv[tid_down]);
+
+               atomic_inc(&priv->wmm.tx_pkts_queued);
+       }
+
+       spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags);
+       return;
+}
+
+static void mwifiex_hold_tdls_packets(struct mwifiex_private *priv, u8 *mac)
+{
+       struct mwifiex_ra_list_tbl *ra_list;
+       struct list_head *ra_list_head;
+       struct sk_buff *skb, *tmp;
+       unsigned long flags;
+       int i;
+
+       dev_dbg(priv->adapter->dev, "%s: %pM\n", __func__, mac);
+       spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags);
+
+       for (i = 0; i < MAX_NUM_TID; i++) {
+               if (!list_empty(&priv->wmm.tid_tbl_ptr[i].ra_list)) {
+                       ra_list_head = &priv->wmm.tid_tbl_ptr[i].ra_list;
+                       list_for_each_entry(ra_list, ra_list_head, list) {
+                               skb_queue_walk_safe(&ra_list->skb_head, skb,
+                                                   tmp) {
+                                       if (!ether_addr_equal(mac, skb->data))
+                                               continue;
+                                       __skb_unlink(skb, &ra_list->skb_head);
+                                       atomic_dec(&priv->wmm.tx_pkts_queued);
+                                       ra_list->total_pkt_count--;
+                                       skb_queue_tail(&priv->tdls_txq, skb);
+                               }
+                       }
+               }
+       }
+
+       spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags);
+       return;
+}
+
+/* This function appends rate TLV to scan config command. */
+static int
+mwifiex_tdls_append_rates_ie(struct mwifiex_private *priv,
+                            struct sk_buff *skb)
+{
+       u8 rates[MWIFIEX_SUPPORTED_RATES], *pos;
+       u16 rates_size, supp_rates_size, ext_rates_size;
+
+       memset(rates, 0, sizeof(rates));
+       rates_size = mwifiex_get_supported_rates(priv, rates);
+
+       supp_rates_size = min_t(u16, rates_size, MWIFIEX_TDLS_SUPPORTED_RATES);
+
+       if (skb_tailroom(skb) < rates_size + 4) {
+               dev_err(priv->adapter->dev,
+                       "Insuffient space while adding rates\n");
+               return -ENOMEM;
+       }
+
+       pos = skb_put(skb, supp_rates_size + 2);
+       *pos++ = WLAN_EID_SUPP_RATES;
+       *pos++ = supp_rates_size;
+       memcpy(pos, rates, supp_rates_size);
+
+       if (rates_size > MWIFIEX_TDLS_SUPPORTED_RATES) {
+               ext_rates_size = rates_size - MWIFIEX_TDLS_SUPPORTED_RATES;
+               pos = skb_put(skb, ext_rates_size + 2);
+               *pos++ = WLAN_EID_EXT_SUPP_RATES;
+               *pos++ = ext_rates_size;
+               memcpy(pos, rates + MWIFIEX_TDLS_SUPPORTED_RATES,
+                      ext_rates_size);
+       }
+
+       return 0;
+}
+
+static void mwifiex_tdls_add_aid(struct mwifiex_private *priv,
+                               struct sk_buff *skb)
+{
+       struct ieee_types_assoc_rsp *assoc_rsp;
+       u8 *pos;
+
+       assoc_rsp = (struct ieee_types_assoc_rsp *)&priv->assoc_rsp_buf;
+       pos = (void *)skb_put(skb, 4);
+       *pos++ = WLAN_EID_AID;
+       *pos++ = 2;
+       *pos++ = le16_to_cpu(assoc_rsp->a_id);
+
+       return;
+}
+
+static int mwifiex_tdls_add_vht_capab(struct mwifiex_private *priv,
+                                     struct sk_buff *skb)
+{
+       struct ieee80211_vht_cap vht_cap;
+       u8 *pos;
+
+       pos = (void *)skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2);
+       *pos++ = WLAN_EID_VHT_CAPABILITY;
+       *pos++ = sizeof(struct ieee80211_vht_cap);
+
+       memset(&vht_cap, 0, sizeof(struct ieee80211_vht_cap));
+
+       mwifiex_fill_vht_cap_tlv(priv, &vht_cap, priv->curr_bss_params.band);
+       memcpy(pos, &vht_cap, sizeof(struct ieee80211_ht_cap));
+
+       return 0;
+}
+
+static int mwifiex_tdls_add_vht_oper(struct mwifiex_private *priv,
+                                    u8 *mac, struct sk_buff *skb)
+{
+       struct mwifiex_bssdescriptor *bss_desc;
+       struct ieee80211_vht_operation *vht_oper;
+       struct ieee80211_vht_cap *vht_cap, *ap_vht_cap = NULL;
+       struct mwifiex_sta_node *sta_ptr;
+       struct mwifiex_adapter *adapter = priv->adapter;
+       u8 supp_chwd_set, peer_supp_chwd_set;
+       u8 *pos, ap_supp_chwd_set, chan_bw;
+       u16 mcs_map_user, mcs_map_resp, mcs_map_result;
+       u16 mcs_user, mcs_resp, nss;
+       u32 usr_vht_cap_info;
+
+       bss_desc = &priv->curr_bss_params.bss_descriptor;
+
+       sta_ptr = mwifiex_get_sta_entry(priv, mac);
+       if (unlikely(!sta_ptr)) {
+               dev_warn(adapter->dev, "TDLS peer station not found in list\n");
+               return -1;
+       }
+
+       if (!mwifiex_is_bss_in_11ac_mode(priv)) {
+               if (sta_ptr->tdls_cap.extcap.ext_capab[7] &
+                  WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED) {
+                       dev_dbg(adapter->dev,
+                               "TDLS peer doesn't support wider bandwitdh\n");
+                       return 0;
+               }
+       } else {
+               ap_vht_cap = bss_desc->bcn_vht_cap;
+       }
+
+       pos = (void *)skb_put(skb, sizeof(struct ieee80211_vht_operation) + 2);
+       *pos++ = WLAN_EID_VHT_OPERATION;
+       *pos++ = sizeof(struct ieee80211_vht_operation);
+       vht_oper = (struct ieee80211_vht_operation *)pos;
+
+       if (bss_desc->bss_band & BAND_A)
+               usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_a;
+       else
+               usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_bg;
+
+       /* find the minmum bandwith between AP/TDLS peers */
+       vht_cap = &sta_ptr->tdls_cap.vhtcap;
+       supp_chwd_set = GET_VHTCAP_CHWDSET(usr_vht_cap_info);
+       peer_supp_chwd_set =
+                        GET_VHTCAP_CHWDSET(le32_to_cpu(vht_cap->vht_cap_info));
+       supp_chwd_set = min_t(u8, supp_chwd_set, peer_supp_chwd_set);
+
+       /* We need check AP's bandwidth when TDLS_WIDER_BANDWIDTH is off */
+
+       if (ap_vht_cap && sta_ptr->tdls_cap.extcap.ext_capab[7] &
+           WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED) {
+               ap_supp_chwd_set =
+                     GET_VHTCAP_CHWDSET(le32_to_cpu(ap_vht_cap->vht_cap_info));
+               supp_chwd_set = min_t(u8, supp_chwd_set, ap_supp_chwd_set);
+       }
+
+       switch (supp_chwd_set) {
+       case IEEE80211_VHT_CHANWIDTH_80MHZ:
+               vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ;
+               break;
+       case IEEE80211_VHT_CHANWIDTH_160MHZ:
+               vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_160MHZ;
+               break;
+       case IEEE80211_VHT_CHANWIDTH_80P80MHZ:
+               vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80P80MHZ;
+               break;
+       default:
+               vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_USE_HT;
+               break;
+       }
+
+       mcs_map_user = GET_DEVRXMCSMAP(adapter->usr_dot_11ac_mcs_support);
+       mcs_map_resp = le16_to_cpu(vht_cap->supp_mcs.rx_mcs_map);
+       mcs_map_result = 0;
+
+       for (nss = 1; nss <= 8; nss++) {
+               mcs_user = GET_VHTNSSMCS(mcs_map_user, nss);
+               mcs_resp = GET_VHTNSSMCS(mcs_map_resp, nss);
+
+               if ((mcs_user == IEEE80211_VHT_MCS_NOT_SUPPORTED) ||
+                   (mcs_resp == IEEE80211_VHT_MCS_NOT_SUPPORTED))
+                       SET_VHTNSSMCS(mcs_map_result, nss,
+                                     IEEE80211_VHT_MCS_NOT_SUPPORTED);
+               else
+                       SET_VHTNSSMCS(mcs_map_result, nss,
+                                     min_t(u16, mcs_user, mcs_resp));
+       }
+
+       vht_oper->basic_mcs_set = cpu_to_le16(mcs_map_result);
+
+       switch (vht_oper->chan_width) {
+       case IEEE80211_VHT_CHANWIDTH_80MHZ:
+               chan_bw = IEEE80211_VHT_CHANWIDTH_80MHZ;
+               break;
+       case IEEE80211_VHT_CHANWIDTH_160MHZ:
+               chan_bw = IEEE80211_VHT_CHANWIDTH_160MHZ;
+               break;
+       case IEEE80211_VHT_CHANWIDTH_80P80MHZ:
+               chan_bw = IEEE80211_VHT_CHANWIDTH_80MHZ;
+               break;
+       default:
+               chan_bw = IEEE80211_VHT_CHANWIDTH_USE_HT;
+               break;
+       }
+       vht_oper->center_freq_seg1_idx =
+                       mwifiex_get_center_freq_index(priv, BAND_AAC,
+                                                     bss_desc->channel,
+                                                     chan_bw);
+
+       return 0;
+}
+
+static void mwifiex_tdls_add_ext_capab(struct mwifiex_private *priv,
+                                      struct sk_buff *skb)
+{
+       struct ieee_types_extcap *extcap;
+
+       extcap = (void *)skb_put(skb, sizeof(struct ieee_types_extcap));
+       extcap->ieee_hdr.element_id = WLAN_EID_EXT_CAPABILITY;
+       extcap->ieee_hdr.len = 8;
+       memset(extcap->ext_capab, 0, 8);
+       extcap->ext_capab[4] |= WLAN_EXT_CAPA5_TDLS_ENABLED;
+
+       if (priv->adapter->is_hw_11ac_capable)
+               extcap->ext_capab[7] |= WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED;
+}
+
+static void mwifiex_tdls_add_qos_capab(struct sk_buff *skb)
+{
+       u8 *pos = (void *)skb_put(skb, 3);
+
+       *pos++ = WLAN_EID_QOS_CAPA;
+       *pos++ = 1;
+       *pos++ = MWIFIEX_TDLS_DEF_QOS_CAPAB;
+}
+
+static int mwifiex_prep_tdls_encap_data(struct mwifiex_private *priv,
+                            u8 *peer, u8 action_code, u8 dialog_token,
+                            u16 status_code, struct sk_buff *skb)
+{
+       struct ieee80211_tdls_data *tf;
+       int ret;
+       u16 capab;
+       struct ieee80211_ht_cap *ht_cap;
+       u8 radio, *pos;
+
+       capab = priv->curr_bss_params.bss_descriptor.cap_info_bitmap;
+
+       tf = (void *)skb_put(skb, offsetof(struct ieee80211_tdls_data, u));
+       memcpy(tf->da, peer, ETH_ALEN);
+       memcpy(tf->sa, priv->curr_addr, ETH_ALEN);
+       tf->ether_type = cpu_to_be16(ETH_P_TDLS);
+       tf->payload_type = WLAN_TDLS_SNAP_RFTYPE;
+
+       switch (action_code) {
+       case WLAN_TDLS_SETUP_REQUEST:
+               tf->category = WLAN_CATEGORY_TDLS;
+               tf->action_code = WLAN_TDLS_SETUP_REQUEST;
+               skb_put(skb, sizeof(tf->u.setup_req));
+               tf->u.setup_req.dialog_token = dialog_token;
+               tf->u.setup_req.capability = cpu_to_le16(capab);
+               ret = mwifiex_tdls_append_rates_ie(priv, skb);
+               if (ret) {
+                       dev_kfree_skb_any(skb);
+                       return ret;
+               }
+
+               pos = (void *)skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2);
+               *pos++ = WLAN_EID_HT_CAPABILITY;
+               *pos++ = sizeof(struct ieee80211_ht_cap);
+               ht_cap = (void *)pos;
+               radio = mwifiex_band_to_radio_type(priv->curr_bss_params.band);
+               ret = mwifiex_fill_cap_info(priv, radio, ht_cap);
+               if (ret) {
+                       dev_kfree_skb_any(skb);
+                       return ret;
+               }
+
+               if (priv->adapter->is_hw_11ac_capable) {
+                       ret = mwifiex_tdls_add_vht_capab(priv, skb);
+                       if (ret) {
+                               dev_kfree_skb_any(skb);
+                               return ret;
+                       }
+                       mwifiex_tdls_add_aid(priv, skb);
+               }
+
+               mwifiex_tdls_add_ext_capab(priv, skb);
+               mwifiex_tdls_add_qos_capab(skb);
+               break;
+
+       case WLAN_TDLS_SETUP_RESPONSE:
+               tf->category = WLAN_CATEGORY_TDLS;
+               tf->action_code = WLAN_TDLS_SETUP_RESPONSE;
+               skb_put(skb, sizeof(tf->u.setup_resp));
+               tf->u.setup_resp.status_code = cpu_to_le16(status_code);
+               tf->u.setup_resp.dialog_token = dialog_token;
+               tf->u.setup_resp.capability = cpu_to_le16(capab);
+               ret = mwifiex_tdls_append_rates_ie(priv, skb);
+               if (ret) {
+                       dev_kfree_skb_any(skb);
+                       return ret;
+               }
+
+               pos = (void *)skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2);
+               *pos++ = WLAN_EID_HT_CAPABILITY;
+               *pos++ = sizeof(struct ieee80211_ht_cap);
+               ht_cap = (void *)pos;
+               radio = mwifiex_band_to_radio_type(priv->curr_bss_params.band);
+               ret = mwifiex_fill_cap_info(priv, radio, ht_cap);
+               if (ret) {
+                       dev_kfree_skb_any(skb);
+                       return ret;
+               }
+
+               if (priv->adapter->is_hw_11ac_capable) {
+                       ret = mwifiex_tdls_add_vht_capab(priv, skb);
+                       if (ret) {
+                               dev_kfree_skb_any(skb);
+                               return ret;
+                       }
+                       mwifiex_tdls_add_aid(priv, skb);
+               }
+
+               mwifiex_tdls_add_ext_capab(priv, skb);
+               mwifiex_tdls_add_qos_capab(skb);
+               break;
+
+       case WLAN_TDLS_SETUP_CONFIRM:
+               tf->category = WLAN_CATEGORY_TDLS;
+               tf->action_code = WLAN_TDLS_SETUP_CONFIRM;
+               skb_put(skb, sizeof(tf->u.setup_cfm));
+               tf->u.setup_cfm.status_code = cpu_to_le16(status_code);
+               tf->u.setup_cfm.dialog_token = dialog_token;
+               if (priv->adapter->is_hw_11ac_capable) {
+                       ret = mwifiex_tdls_add_vht_oper(priv, peer, skb);
+                       if (ret) {
+                               dev_kfree_skb_any(skb);
+                               return ret;
+                       }
+               }
+               break;
+
+       case WLAN_TDLS_TEARDOWN:
+               tf->category = WLAN_CATEGORY_TDLS;
+               tf->action_code = WLAN_TDLS_TEARDOWN;
+               skb_put(skb, sizeof(tf->u.teardown));
+               tf->u.teardown.reason_code = cpu_to_le16(status_code);
+               break;
+
+       case WLAN_TDLS_DISCOVERY_REQUEST:
+               tf->category = WLAN_CATEGORY_TDLS;
+               tf->action_code = WLAN_TDLS_DISCOVERY_REQUEST;
+               skb_put(skb, sizeof(tf->u.discover_req));
+               tf->u.discover_req.dialog_token = dialog_token;
+               break;
+       default:
+               dev_err(priv->adapter->dev, "Unknown TDLS frame type.\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static void
+mwifiex_tdls_add_link_ie(struct sk_buff *skb, u8 *src_addr, u8 *peer, u8 *bssid)
+{
+       struct ieee80211_tdls_lnkie *lnkid;
+
+       lnkid = (void *)skb_put(skb, sizeof(struct ieee80211_tdls_lnkie));
+       lnkid->ie_type = WLAN_EID_LINK_ID;
+       lnkid->ie_len = sizeof(struct ieee80211_tdls_lnkie) -
+                       sizeof(struct ieee_types_header);
+
+       memcpy(lnkid->bssid, bssid, ETH_ALEN);
+       memcpy(lnkid->init_sta, src_addr, ETH_ALEN);
+       memcpy(lnkid->resp_sta, peer, ETH_ALEN);
+}
+
+int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv,
+                                u8 *peer, u8 action_code, u8 dialog_token,
+                                u16 status_code, const u8 *extra_ies,
+                                size_t extra_ies_len)
+{
+       struct sk_buff *skb;
+       struct mwifiex_txinfo *tx_info;
+       struct timeval tv;
+       int ret;
+       u16 skb_len;
+
+       skb_len = MWIFIEX_MIN_DATA_HEADER_LEN +
+                 max(sizeof(struct ieee80211_mgmt),
+                     sizeof(struct ieee80211_tdls_data)) +
+                 MWIFIEX_MGMT_FRAME_HEADER_SIZE +
+                 MWIFIEX_SUPPORTED_RATES +
+                 3 + /* Qos Info */
+                 sizeof(struct ieee_types_extcap) +
+                 sizeof(struct ieee80211_ht_cap) +
+                 sizeof(struct ieee_types_bss_co_2040) +
+                 sizeof(struct ieee80211_ht_operation) +
+                 sizeof(struct ieee80211_tdls_lnkie) +
+                 extra_ies_len;
+
+       if (priv->adapter->is_hw_11ac_capable)
+               skb_len += sizeof(struct ieee_types_vht_cap) +
+                          sizeof(struct ieee_types_vht_oper) +
+                          sizeof(struct ieee_types_aid);
+
+       skb = dev_alloc_skb(skb_len);
+       if (!skb) {
+               dev_err(priv->adapter->dev,
+                       "allocate skb failed for management frame\n");
+               return -ENOMEM;
+       }
+       skb_reserve(skb, MWIFIEX_MIN_DATA_HEADER_LEN);
+
+       switch (action_code) {
+       case WLAN_TDLS_SETUP_REQUEST:
+       case WLAN_TDLS_SETUP_CONFIRM:
+       case WLAN_TDLS_TEARDOWN:
+       case WLAN_TDLS_DISCOVERY_REQUEST:
+               ret = mwifiex_prep_tdls_encap_data(priv, peer, action_code,
+                                                  dialog_token, status_code,
+                                                  skb);
+               if (ret) {
+                       dev_kfree_skb_any(skb);
+                       return ret;
+               }
+               if (extra_ies_len)
+                       memcpy(skb_put(skb, extra_ies_len), extra_ies,
+                              extra_ies_len);
+               mwifiex_tdls_add_link_ie(skb, priv->curr_addr, peer,
+                                        priv->cfg_bssid);
+               break;
+       case WLAN_TDLS_SETUP_RESPONSE:
+               ret = mwifiex_prep_tdls_encap_data(priv, peer, action_code,
+                                                  dialog_token, status_code,
+                                                  skb);
+               if (ret) {
+                       dev_kfree_skb_any(skb);
+                       return ret;
+               }
+               if (extra_ies_len)
+                       memcpy(skb_put(skb, extra_ies_len), extra_ies,
+                              extra_ies_len);
+               mwifiex_tdls_add_link_ie(skb, peer, priv->curr_addr,
+                                        priv->cfg_bssid);
+               break;
+       }
+
+       switch (action_code) {
+       case WLAN_TDLS_SETUP_REQUEST:
+       case WLAN_TDLS_SETUP_RESPONSE:
+               skb->priority = MWIFIEX_PRIO_BK;
+               break;
+       default:
+               skb->priority = MWIFIEX_PRIO_VI;
+               break;
+       }
+
+       tx_info = MWIFIEX_SKB_TXCB(skb);
+       tx_info->bss_num = priv->bss_num;
+       tx_info->bss_type = priv->bss_type;
+
+       do_gettimeofday(&tv);
+       skb->tstamp = timeval_to_ktime(tv);
+       mwifiex_queue_tx_pkt(priv, skb);
+
+       return 0;
+}
+
+static int
+mwifiex_construct_tdls_action_frame(struct mwifiex_private *priv, u8 *peer,
+                                   u8 action_code, u8 dialog_token,
+                                   u16 status_code, struct sk_buff *skb)
+{
+       struct ieee80211_mgmt *mgmt;
+       u8 bc_addr[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+       int ret;
+       u16 capab;
+       struct ieee80211_ht_cap *ht_cap;
+       u8 radio, *pos;
+
+       capab = priv->curr_bss_params.bss_descriptor.cap_info_bitmap;
+
+       mgmt = (void *)skb_put(skb, offsetof(struct ieee80211_mgmt, u));
+
+       memset(mgmt, 0, 24);
+       memcpy(mgmt->da, peer, ETH_ALEN);
+       memcpy(mgmt->sa, priv->curr_addr, ETH_ALEN);
+       memcpy(mgmt->bssid, priv->cfg_bssid, ETH_ALEN);
+       mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+                                         IEEE80211_STYPE_ACTION);
+
+       /* add address 4 */
+       pos = skb_put(skb, ETH_ALEN);
+
+       switch (action_code) {
+       case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
+               skb_put(skb, sizeof(mgmt->u.action.u.tdls_discover_resp) + 1);
+               mgmt->u.action.category = WLAN_CATEGORY_PUBLIC;
+               mgmt->u.action.u.tdls_discover_resp.action_code =
+                                             WLAN_PUB_ACTION_TDLS_DISCOVER_RES;
+               mgmt->u.action.u.tdls_discover_resp.dialog_token =
+                                                                  dialog_token;
+               mgmt->u.action.u.tdls_discover_resp.capability =
+                                                            cpu_to_le16(capab);
+               /* move back for addr4 */
+               memmove(pos + ETH_ALEN, &mgmt->u.action.category,
+                       sizeof(mgmt->u.action.u.tdls_discover_resp));
+               /* init address 4 */
+               memcpy(pos, bc_addr, ETH_ALEN);
+
+               ret = mwifiex_tdls_append_rates_ie(priv, skb);
+               if (ret) {
+                       dev_kfree_skb_any(skb);
+                       return ret;
+               }
+
+               pos = (void *)skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2);
+               *pos++ = WLAN_EID_HT_CAPABILITY;
+               *pos++ = sizeof(struct ieee80211_ht_cap);
+               ht_cap = (void *)pos;
+               radio = mwifiex_band_to_radio_type(priv->curr_bss_params.band);
+               ret = mwifiex_fill_cap_info(priv, radio, ht_cap);
+               if (ret) {
+                       dev_kfree_skb_any(skb);
+                       return ret;
+               }
+
+               if (priv->adapter->is_hw_11ac_capable) {
+                       ret = mwifiex_tdls_add_vht_capab(priv, skb);
+                       if (ret) {
+                               dev_kfree_skb_any(skb);
+                               return ret;
+                       }
+                       mwifiex_tdls_add_aid(priv, skb);
+               }
+
+               mwifiex_tdls_add_ext_capab(priv, skb);
+               mwifiex_tdls_add_qos_capab(skb);
+               break;
+       default:
+               dev_err(priv->adapter->dev, "Unknown TDLS action frame type\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv,
+                                u8 *peer, u8 action_code, u8 dialog_token,
+                                u16 status_code, const u8 *extra_ies,
+                                size_t extra_ies_len)
+{
+       struct sk_buff *skb;
+       struct mwifiex_txinfo *tx_info;
+       struct timeval tv;
+       u8 *pos;
+       u32 pkt_type, tx_control;
+       u16 pkt_len, skb_len;
+
+       skb_len = MWIFIEX_MIN_DATA_HEADER_LEN +
+                 max(sizeof(struct ieee80211_mgmt),
+                     sizeof(struct ieee80211_tdls_data)) +
+                 MWIFIEX_MGMT_FRAME_HEADER_SIZE +
+                 MWIFIEX_SUPPORTED_RATES +
+                 sizeof(struct ieee_types_extcap) +
+                 sizeof(struct ieee80211_ht_cap) +
+                 sizeof(struct ieee_types_bss_co_2040) +
+                 sizeof(struct ieee80211_ht_operation) +
+                 sizeof(struct ieee80211_tdls_lnkie) +
+                 extra_ies_len +
+                 3 + /* Qos Info */
+                 ETH_ALEN; /* Address4 */
+
+       if (priv->adapter->is_hw_11ac_capable)
+               skb_len += sizeof(struct ieee_types_vht_cap) +
+                          sizeof(struct ieee_types_vht_oper) +
+                          sizeof(struct ieee_types_aid);
+
+       skb = dev_alloc_skb(skb_len);
+       if (!skb) {
+               dev_err(priv->adapter->dev,
+                       "allocate skb failed for management frame\n");
+               return -ENOMEM;
+       }
+
+       skb_reserve(skb, MWIFIEX_MIN_DATA_HEADER_LEN);
+
+       pkt_type = PKT_TYPE_MGMT;
+       tx_control = 0;
+       pos = skb_put(skb, MWIFIEX_MGMT_FRAME_HEADER_SIZE + sizeof(pkt_len));
+       memset(pos, 0, MWIFIEX_MGMT_FRAME_HEADER_SIZE + sizeof(pkt_len));
+       memcpy(pos, &pkt_type, sizeof(pkt_type));
+       memcpy(pos + sizeof(pkt_type), &tx_control, sizeof(tx_control));
+
+       if (mwifiex_construct_tdls_action_frame(priv, peer, action_code,
+                                               dialog_token, status_code,
+                                               skb)) {
+               dev_kfree_skb_any(skb);
+               return -EINVAL;
+       }
+
+       if (extra_ies_len)
+               memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len);
+
+       /* the TDLS link IE is always added last we are the responder */
+
+       mwifiex_tdls_add_link_ie(skb, peer, priv->curr_addr,
+                                priv->cfg_bssid);
+
+       skb->priority = MWIFIEX_PRIO_VI;
+
+       tx_info = MWIFIEX_SKB_TXCB(skb);
+       tx_info->bss_num = priv->bss_num;
+       tx_info->bss_type = priv->bss_type;
+       tx_info->flags |= MWIFIEX_BUF_FLAG_TDLS_PKT;
+
+       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);
+       mwifiex_queue_tx_pkt(priv, skb);
+
+       return 0;
+}
+
+/* This function process tdls action frame from peer.
+ * Peer capabilities are stored into station node structure.
+ */
+void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv,
+                                      u8 *buf, int len)
+{
+       struct mwifiex_sta_node *sta_ptr;
+       u8 *peer, *pos, *end;
+       u8 i, action, basic;
+       int ie_len = 0;
+
+       if (len < (sizeof(struct ethhdr) + 3))
+               return;
+       if (*(u8 *)(buf + sizeof(struct ethhdr)) != WLAN_TDLS_SNAP_RFTYPE)
+               return;
+       if (*(u8 *)(buf + sizeof(struct ethhdr) + 1) != WLAN_CATEGORY_TDLS)
+               return;
+
+       peer = buf + ETH_ALEN;
+       action = *(u8 *)(buf + sizeof(struct ethhdr) + 2);
+
+       /* just handle TDLS setup request/response/confirm */
+       if (action > WLAN_TDLS_SETUP_CONFIRM)
+               return;
+
+       dev_dbg(priv->adapter->dev,
+               "rx:tdls action: peer=%pM, action=%d\n", peer, action);
+
+       sta_ptr = mwifiex_add_sta_entry(priv, peer);
+       if (!sta_ptr)
+               return;
+
+       switch (action) {
+       case WLAN_TDLS_SETUP_REQUEST:
+               if (len < (sizeof(struct ethhdr) + TDLS_REQ_FIX_LEN))
+                       return;
+
+               pos = buf + sizeof(struct ethhdr) + 4;
+               /* payload 1+ category 1 + action 1 + dialog 1 */
+               sta_ptr->tdls_cap.capab = cpu_to_le16(*(u16 *)pos);
+               ie_len = len - sizeof(struct ethhdr) - TDLS_REQ_FIX_LEN;
+               pos += 2;
+               break;
+
+       case WLAN_TDLS_SETUP_RESPONSE:
+               if (len < (sizeof(struct ethhdr) + TDLS_RESP_FIX_LEN))
+                       return;
+               /* payload 1+ category 1 + action 1 + dialog 1 + status code 2*/
+               pos = buf + sizeof(struct ethhdr) + 6;
+               sta_ptr->tdls_cap.capab = cpu_to_le16(*(u16 *)pos);
+               ie_len = len - sizeof(struct ethhdr) - TDLS_RESP_FIX_LEN;
+               pos += 2;
+               break;
+
+       case WLAN_TDLS_SETUP_CONFIRM:
+               if (len < (sizeof(struct ethhdr) + TDLS_CONFIRM_FIX_LEN))
+                       return;
+               pos = buf + sizeof(struct ethhdr) + TDLS_CONFIRM_FIX_LEN;
+               ie_len = len - sizeof(struct ethhdr) - TDLS_CONFIRM_FIX_LEN;
+               break;
+       default:
+               dev_warn(priv->adapter->dev, "Unknown TDLS frame type.\n");
+               return;
+       }
+
+       for (end = pos + ie_len; pos + 1 < end; pos += 2 + pos[1]) {
+               if (pos + 2 + pos[1] > end)
+                       break;
+
+               switch (*pos) {
+               case WLAN_EID_SUPP_RATES:
+                       sta_ptr->tdls_cap.rates_len = pos[1];
+                       for (i = 0; i < pos[1]; i++)
+                               sta_ptr->tdls_cap.rates[i] = pos[i + 2];
+                       break;
+
+               case WLAN_EID_EXT_SUPP_RATES:
+                       basic = sta_ptr->tdls_cap.rates_len;
+                       for (i = 0; i < pos[1]; i++)
+                               sta_ptr->tdls_cap.rates[basic + i] = pos[i + 2];
+                       sta_ptr->tdls_cap.rates_len += pos[1];
+                       break;
+               case WLAN_EID_HT_CAPABILITY:
+                       memcpy((u8 *)&sta_ptr->tdls_cap.ht_capb, pos,
+                              sizeof(struct ieee80211_ht_cap));
+                       sta_ptr->is_11n_enabled = 1;
+                       break;
+               case WLAN_EID_HT_OPERATION:
+                       memcpy(&sta_ptr->tdls_cap.ht_oper, pos,
+                              sizeof(struct ieee80211_ht_operation));
+                       break;
+               case WLAN_EID_BSS_COEX_2040:
+                       sta_ptr->tdls_cap.coex_2040 = pos[2];
+                       break;
+               case WLAN_EID_EXT_CAPABILITY:
+                       memcpy((u8 *)&sta_ptr->tdls_cap.extcap, pos,
+                              sizeof(struct ieee_types_header) +
+                              min_t(u8, pos[1], 8));
+                       break;
+               case WLAN_EID_RSN:
+                       memcpy((u8 *)&sta_ptr->tdls_cap.rsn_ie, pos,
+                              sizeof(struct ieee_types_header) + pos[1]);
+                       break;
+               case WLAN_EID_QOS_CAPA:
+                       sta_ptr->tdls_cap.qos_info = pos[2];
+                       break;
+               case WLAN_EID_VHT_OPERATION:
+                       if (priv->adapter->is_hw_11ac_capable)
+                               memcpy(&sta_ptr->tdls_cap.vhtoper, pos,
+                                      sizeof(struct ieee80211_vht_operation));
+                       break;
+               case WLAN_EID_VHT_CAPABILITY:
+                       if (priv->adapter->is_hw_11ac_capable) {
+                               memcpy((u8 *)&sta_ptr->tdls_cap.vhtcap, pos,
+                                      sizeof(struct ieee80211_vht_cap));
+                               sta_ptr->is_11ac_enabled = 1;
+                       }
+                       break;
+               case WLAN_EID_AID:
+                       if (priv->adapter->is_hw_11ac_capable)
+                               sta_ptr->tdls_cap.aid =
+                                             le16_to_cpu(*(__le16 *)(pos + 2));
+               default:
+                       break;
+               }
+       }
+
+       return;
+}
+
+static int
+mwifiex_tdls_process_config_link(struct mwifiex_private *priv, u8 *peer)
+{
+       struct mwifiex_sta_node *sta_ptr;
+       struct mwifiex_ds_tdls_oper tdls_oper;
+
+       memset(&tdls_oper, 0, sizeof(struct mwifiex_ds_tdls_oper));
+       sta_ptr = mwifiex_get_sta_entry(priv, peer);
+
+       if (!sta_ptr || sta_ptr->tdls_status == TDLS_SETUP_FAILURE) {
+               dev_err(priv->adapter->dev,
+                       "link absent for peer %pM; cannot config\n", peer);
+               return -EINVAL;
+       }
+
+       memcpy(&tdls_oper.peer_mac, peer, ETH_ALEN);
+       tdls_oper.tdls_action = MWIFIEX_TDLS_CONFIG_LINK;
+       return mwifiex_send_cmd_sync(priv, HostCmd_CMD_TDLS_OPER,
+                                    HostCmd_ACT_GEN_SET, 0, &tdls_oper);
+}
+
+static int
+mwifiex_tdls_process_create_link(struct mwifiex_private *priv, u8 *peer)
+{
+       struct mwifiex_sta_node *sta_ptr;
+       struct mwifiex_ds_tdls_oper tdls_oper;
+
+       memset(&tdls_oper, 0, sizeof(struct mwifiex_ds_tdls_oper));
+       sta_ptr = mwifiex_get_sta_entry(priv, peer);
+
+       if (sta_ptr && sta_ptr->tdls_status == TDLS_SETUP_INPROGRESS) {
+               dev_dbg(priv->adapter->dev,
+                       "Setup already in progress for peer %pM\n", peer);
+               return 0;
+       }
+
+       sta_ptr = mwifiex_add_sta_entry(priv, peer);
+       if (!sta_ptr)
+               return -ENOMEM;
+
+       sta_ptr->tdls_status = TDLS_SETUP_INPROGRESS;
+       mwifiex_hold_tdls_packets(priv, peer);
+       memcpy(&tdls_oper.peer_mac, peer, ETH_ALEN);
+       tdls_oper.tdls_action = MWIFIEX_TDLS_CREATE_LINK;
+       return mwifiex_send_cmd_sync(priv, HostCmd_CMD_TDLS_OPER,
+                                    HostCmd_ACT_GEN_SET, 0, &tdls_oper);
+}
+
+static int
+mwifiex_tdls_process_disable_link(struct mwifiex_private *priv, u8 *peer)
+{
+       struct mwifiex_sta_node *sta_ptr;
+       struct mwifiex_ds_tdls_oper tdls_oper;
+       unsigned long flags;
+
+       memset(&tdls_oper, 0, sizeof(struct mwifiex_ds_tdls_oper));
+       sta_ptr = mwifiex_get_sta_entry(priv, peer);
+
+       if (sta_ptr) {
+               if (sta_ptr->is_11n_enabled) {
+                       mwifiex_11n_cleanup_reorder_tbl(priv);
+                       spin_lock_irqsave(&priv->wmm.ra_list_spinlock,
+                                         flags);
+                       mwifiex_11n_delete_all_tx_ba_stream_tbl(priv);
+                       spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock,
+                                              flags);
+               }
+               mwifiex_del_sta_entry(priv, peer);
+       }
+
+       mwifiex_restore_tdls_packets(priv, peer, TDLS_LINK_TEARDOWN);
+       memcpy(&tdls_oper.peer_mac, peer, ETH_ALEN);
+       tdls_oper.tdls_action = MWIFIEX_TDLS_DISABLE_LINK;
+       return mwifiex_send_cmd_sync(priv, HostCmd_CMD_TDLS_OPER,
+                                    HostCmd_ACT_GEN_SET, 0, &tdls_oper);
+}
+
+static int
+mwifiex_tdls_process_enable_link(struct mwifiex_private *priv, u8 *peer)
+{
+       struct mwifiex_sta_node *sta_ptr;
+       struct ieee80211_mcs_info mcs;
+       unsigned long flags;
+       int i;
+
+       sta_ptr = mwifiex_get_sta_entry(priv, peer);
+
+       if (sta_ptr && (sta_ptr->tdls_status != TDLS_SETUP_FAILURE)) {
+               dev_dbg(priv->adapter->dev,
+                       "tdls: enable link %pM success\n", peer);
+
+               sta_ptr->tdls_status = TDLS_SETUP_COMPLETE;
+
+               mcs = sta_ptr->tdls_cap.ht_capb.mcs;
+               if (mcs.rx_mask[0] != 0xff)
+                       sta_ptr->is_11n_enabled = true;
+               if (sta_ptr->is_11n_enabled) {
+                       if (le16_to_cpu(sta_ptr->tdls_cap.ht_capb.cap_info) &
+                           IEEE80211_HT_CAP_MAX_AMSDU)
+                               sta_ptr->max_amsdu =
+                                       MWIFIEX_TX_DATA_BUF_SIZE_8K;
+                       else
+                               sta_ptr->max_amsdu =
+                                       MWIFIEX_TX_DATA_BUF_SIZE_4K;
+
+                       for (i = 0; i < MAX_NUM_TID; i++)
+                               sta_ptr->ampdu_sta[i] =
+                                             priv->aggr_prio_tbl[i].ampdu_user;
+               } else {
+                       for (i = 0; i < MAX_NUM_TID; i++)
+                               sta_ptr->ampdu_sta[i] = BA_STREAM_NOT_ALLOWED;
+               }
+
+               memset(sta_ptr->rx_seq, 0xff, sizeof(sta_ptr->rx_seq));
+               mwifiex_restore_tdls_packets(priv, peer, TDLS_SETUP_COMPLETE);
+       } else {
+               dev_dbg(priv->adapter->dev,
+                       "tdls: enable link %pM failed\n", peer);
+               if (sta_ptr) {
+                       mwifiex_11n_cleanup_reorder_tbl(priv);
+                       spin_lock_irqsave(&priv->wmm.ra_list_spinlock,
+                                         flags);
+                       mwifiex_11n_delete_all_tx_ba_stream_tbl(priv);
+                       spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock,
+                                              flags);
+                       mwifiex_del_sta_entry(priv, peer);
+               }
+               mwifiex_restore_tdls_packets(priv, peer, TDLS_LINK_TEARDOWN);
+
+               return -1;
+       }
+
+       return 0;
+}
+
+int mwifiex_tdls_oper(struct mwifiex_private *priv, u8 *peer, u8 action)
+{
+       switch (action) {
+       case MWIFIEX_TDLS_ENABLE_LINK:
+               return mwifiex_tdls_process_enable_link(priv, peer);
+       case MWIFIEX_TDLS_DISABLE_LINK:
+               return mwifiex_tdls_process_disable_link(priv, peer);
+       case MWIFIEX_TDLS_CREATE_LINK:
+               return mwifiex_tdls_process_create_link(priv, peer);
+       case MWIFIEX_TDLS_CONFIG_LINK:
+               return mwifiex_tdls_process_config_link(priv, peer);
+       }
+       return 0;
+}
+
+int mwifiex_get_tdls_link_status(struct mwifiex_private *priv, u8 *mac)
+{
+       struct mwifiex_sta_node *sta_ptr;
+
+       sta_ptr = mwifiex_get_sta_entry(priv, mac);
+       if (sta_ptr)
+               return sta_ptr->tdls_status;
+
+       return TDLS_NOT_SETUP;
+}
+
+void mwifiex_disable_all_tdls_links(struct mwifiex_private *priv)
+{
+       struct mwifiex_sta_node *sta_ptr;
+       struct mwifiex_ds_tdls_oper tdls_oper;
+       unsigned long flags;
+
+       if (list_empty(&priv->sta_list))
+               return;
+
+       list_for_each_entry(sta_ptr, &priv->sta_list, list) {
+               memset(&tdls_oper, 0, sizeof(struct mwifiex_ds_tdls_oper));
+
+               if (sta_ptr->is_11n_enabled) {
+                       mwifiex_11n_cleanup_reorder_tbl(priv);
+                       spin_lock_irqsave(&priv->wmm.ra_list_spinlock,
+                                         flags);
+                       mwifiex_11n_delete_all_tx_ba_stream_tbl(priv);
+                       spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock,
+                                              flags);
+               }
+
+               mwifiex_restore_tdls_packets(priv, sta_ptr->mac_addr,
+                                            TDLS_LINK_TEARDOWN);
+               memcpy(&tdls_oper.peer_mac, sta_ptr->mac_addr, ETH_ALEN);
+               tdls_oper.tdls_action = MWIFIEX_TDLS_DISABLE_LINK;
+               if (mwifiex_send_cmd_async(priv, HostCmd_CMD_TDLS_OPER,
+                                          HostCmd_ACT_GEN_SET, 0, &tdls_oper))
+                       dev_warn(priv->adapter->dev,
+                                "Disable link failed for TDLS peer %pM",
+                                sta_ptr->mac_addr);
+       }
+
+       mwifiex_del_all_sta_list(priv);
+}
index 7180665..2d47ba7 100644 (file)
 #include "main.h"
 #include "11n.h"
 
-/*
- * This function will return the pointer to station entry in station list
- * table which matches specified mac address.
- * This function should be called after acquiring RA list spinlock.
- * NULL is returned if station entry is not found in associated STA list.
- */
-struct mwifiex_sta_node *
-mwifiex_get_sta_entry(struct mwifiex_private *priv, u8 *mac)
-{
-       struct mwifiex_sta_node *node;
-
-       if (!mac)
-               return NULL;
-
-       list_for_each_entry(node, &priv->sta_list, list) {
-               if (!memcmp(node->mac_addr, mac, ETH_ALEN))
-                       return node;
-       }
 
-       return NULL;
-}
-
-/*
- * This function will add a sta_node entry to associated station list
- * table with the given mac address.
- * If entry exist already, existing entry is returned.
- * If received mac address is NULL, NULL is returned.
- */
-static struct mwifiex_sta_node *
-mwifiex_add_sta_entry(struct mwifiex_private *priv, u8 *mac)
-{
-       struct mwifiex_sta_node *node;
-       unsigned long flags;
 
-       if (!mac)
-               return NULL;
-
-       spin_lock_irqsave(&priv->sta_list_spinlock, flags);
-       node = mwifiex_get_sta_entry(priv, mac);
-       if (node)
-               goto done;
-
-       node = kzalloc(sizeof(struct mwifiex_sta_node), GFP_ATOMIC);
-       if (!node)
-               goto done;
-
-       memcpy(node->mac_addr, mac, ETH_ALEN);
-       list_add_tail(&node->list, &priv->sta_list);
-
-done:
-       spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
-       return node;
-}
-
-/*
- * This function will search for HT IE in association request IEs
- * and set station HT parameters accordingly.
- */
-static void
-mwifiex_set_sta_ht_cap(struct mwifiex_private *priv, const u8 *ies,
-                      int ies_len, struct mwifiex_sta_node *node)
-{
-       const struct ieee80211_ht_cap *ht_cap;
-
-       if (!ies)
-               return;
-
-       ht_cap = (void *)cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, ies, ies_len);
-       if (ht_cap) {
-               node->is_11n_enabled = 1;
-               node->max_amsdu = le16_to_cpu(ht_cap->cap_info) &
-                                 IEEE80211_HT_CAP_MAX_AMSDU ?
-                                 MWIFIEX_TX_DATA_BUF_SIZE_8K :
-                                 MWIFIEX_TX_DATA_BUF_SIZE_4K;
-       } else {
-               node->is_11n_enabled = 0;
-       }
-
-       return;
-}
-
-/*
- * This function will delete a station entry from station list
- */
-static void mwifiex_del_sta_entry(struct mwifiex_private *priv, u8 *mac)
-{
-       struct mwifiex_sta_node *node;
-       unsigned long flags;
-
-       spin_lock_irqsave(&priv->sta_list_spinlock, flags);
-
-       node = mwifiex_get_sta_entry(priv, mac);
-       if (node) {
-               list_del(&node->list);
-               kfree(node);
-       }
-
-       spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
-       return;
-}
-
-/*
- * This function will delete all stations from associated station list.
- */
-static void mwifiex_del_all_sta_list(struct mwifiex_private *priv)
-{
-       struct mwifiex_sta_node *node, *tmp;
-       unsigned long flags;
-
-       spin_lock_irqsave(&priv->sta_list_spinlock, flags);
-
-       list_for_each_entry_safe(node, tmp, &priv->sta_list, list) {
-               list_del(&node->list);
-               kfree(node);
-       }
-
-       INIT_LIST_HEAD(&priv->sta_list);
-       spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
-       return;
-}
 
 /*
  * This function handles AP interface specific events generated by firmware.
index 9b82e22..8d37bfc 100644 (file)
@@ -252,3 +252,117 @@ int mwifiex_complete_cmd(struct mwifiex_adapter *adapter,
 
        return 0;
 }
+
+/* This function will return the pointer to station entry in station list
+ * table which matches specified mac address.
+ * This function should be called after acquiring RA list spinlock.
+ * NULL is returned if station entry is not found in associated STA list.
+ */
+struct mwifiex_sta_node *
+mwifiex_get_sta_entry(struct mwifiex_private *priv, u8 *mac)
+{
+       struct mwifiex_sta_node *node;
+
+       if (!mac)
+               return NULL;
+
+       list_for_each_entry(node, &priv->sta_list, list) {
+               if (!memcmp(node->mac_addr, mac, ETH_ALEN))
+                       return node;
+       }
+
+       return NULL;
+}
+
+/* This function will add a sta_node entry to associated station list
+ * table with the given mac address.
+ * If entry exist already, existing entry is returned.
+ * If received mac address is NULL, NULL is returned.
+ */
+struct mwifiex_sta_node *
+mwifiex_add_sta_entry(struct mwifiex_private *priv, u8 *mac)
+{
+       struct mwifiex_sta_node *node;
+       unsigned long flags;
+
+       if (!mac)
+               return NULL;
+
+       spin_lock_irqsave(&priv->sta_list_spinlock, flags);
+       node = mwifiex_get_sta_entry(priv, mac);
+       if (node)
+               goto done;
+
+       node = kzalloc(sizeof(*node), GFP_ATOMIC);
+       if (!node)
+               goto done;
+
+       memcpy(node->mac_addr, mac, ETH_ALEN);
+       list_add_tail(&node->list, &priv->sta_list);
+
+done:
+       spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
+       return node;
+}
+
+/* This function will search for HT IE in association request IEs
+ * and set station HT parameters accordingly.
+ */
+void
+mwifiex_set_sta_ht_cap(struct mwifiex_private *priv, const u8 *ies,
+                      int ies_len, struct mwifiex_sta_node *node)
+{
+       const struct ieee80211_ht_cap *ht_cap;
+
+       if (!ies)
+               return;
+
+       ht_cap = (void *)cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, ies, ies_len);
+       if (ht_cap) {
+               node->is_11n_enabled = 1;
+               node->max_amsdu = le16_to_cpu(ht_cap->cap_info) &
+                                 IEEE80211_HT_CAP_MAX_AMSDU ?
+                                 MWIFIEX_TX_DATA_BUF_SIZE_8K :
+                                 MWIFIEX_TX_DATA_BUF_SIZE_4K;
+       } else {
+               node->is_11n_enabled = 0;
+       }
+
+       return;
+}
+
+/* This function will delete a station entry from station list */
+void mwifiex_del_sta_entry(struct mwifiex_private *priv, u8 *mac)
+{
+       struct mwifiex_sta_node *node;
+       unsigned long flags;
+
+       spin_lock_irqsave(&priv->sta_list_spinlock, flags);
+
+       node = mwifiex_get_sta_entry(priv, mac);
+       if (node) {
+               list_del(&node->list);
+               kfree(node);
+       }
+
+       spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
+       return;
+}
+
+/* This function will delete all stations from associated station list. */
+void mwifiex_del_all_sta_list(struct mwifiex_private *priv)
+{
+       struct mwifiex_sta_node *node, *tmp;
+       unsigned long flags;
+
+       spin_lock_irqsave(&priv->sta_list_spinlock, flags);
+
+       list_for_each_entry_safe(node, tmp, &priv->sta_list, list) {
+               list_del(&node->list);
+               kfree(node);
+       }
+
+       INIT_LIST_HEAD(&priv->sta_list);
+       spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
+       return;
+}
index cb2d058..ddae570 100644 (file)
@@ -30,8 +30,24 @@ static inline struct mwifiex_txinfo *MWIFIEX_SKB_TXCB(struct sk_buff *skb)
        return (struct mwifiex_txinfo *)(skb->cb + sizeof(dma_addr_t));
 }
 
-static inline void MWIFIEX_SKB_PACB(struct sk_buff *skb, dma_addr_t *buf_pa)
+struct mwifiex_dma_mapping {
+       dma_addr_t addr;
+       size_t len;
+};
+
+static inline void MWIFIEX_SKB_PACB(struct sk_buff *skb,
+                                       struct mwifiex_dma_mapping *mapping)
 {
-       memcpy(buf_pa, skb->cb, sizeof(dma_addr_t));
+       memcpy(mapping, skb->cb, sizeof(*mapping));
 }
+
+static inline dma_addr_t MWIFIEX_SKB_DMA_ADDR(struct sk_buff *skb)
+{
+       struct mwifiex_dma_mapping mapping;
+
+       MWIFIEX_SKB_PACB(skb, &mapping);
+
+       return mapping.addr;
+}
+
 #endif /* !_MWIFIEX_UTIL_H_ */
index 13eaeed..e0ba011 100644 (file)
@@ -64,21 +64,6 @@ static u8 tos_to_tid[] = {
        0x07                    /* 1 1 1 AC_VO */
 };
 
-/*
- * This table inverses the tos_to_tid operation to get a priority
- * which is in sequential order, and can be compared.
- * Use this to compare the priority of two different TIDs.
- */
-static u8 tos_to_tid_inv[] = {
-       0x02,  /* from tos_to_tid[2] = 0 */
-       0x00,  /* from tos_to_tid[0] = 1 */
-       0x01,  /* from tos_to_tid[1] = 2 */
-       0x03,
-       0x04,
-       0x05,
-       0x06,
-       0x07};
-
 static u8 ac_to_tid[4][2] = { {1, 2}, {0, 3}, {4, 5}, {6, 7} };
 
 /*
@@ -175,8 +160,15 @@ mwifiex_ralist_add(struct mwifiex_private *priv, u8 *ra)
                        break;
 
                ra_list->is_11n_enabled = 0;
+               ra_list->tdls_link = false;
                if (!mwifiex_queuing_ra_based(priv)) {
-                       ra_list->is_11n_enabled = IS_11N_ENABLED(priv);
+                       if (mwifiex_get_tdls_link_status(priv, ra) ==
+                           TDLS_SETUP_COMPLETE) {
+                               ra_list->is_11n_enabled =
+                                       mwifiex_tdls_peer_11n_enabled(priv, ra);
+                       } else {
+                               ra_list->is_11n_enabled = IS_11N_ENABLED(priv);
+                       }
                } else {
                        ra_list->is_11n_enabled =
                                      mwifiex_is_sta_11n_enabled(priv, node);
@@ -213,8 +205,9 @@ static void mwifiex_wmm_default_queue_priorities(struct mwifiex_private *priv)
  * This function map ACs to TIDs.
  */
 static void
-mwifiex_wmm_queue_priorities_tid(struct mwifiex_wmm_desc *wmm)
+mwifiex_wmm_queue_priorities_tid(struct mwifiex_private *priv)
 {
+       struct mwifiex_wmm_desc *wmm = &priv->wmm;
        u8 *queue_priority = wmm->queue_priority;
        int i;
 
@@ -224,7 +217,7 @@ mwifiex_wmm_queue_priorities_tid(struct mwifiex_wmm_desc *wmm)
        }
 
        for (i = 0; i < MAX_NUM_TID; ++i)
-               tos_to_tid_inv[tos_to_tid[i]] = (u8)i;
+               priv->tos_to_tid_inv[tos_to_tid[i]] = (u8)i;
 
        atomic_set(&wmm->highest_queued_prio, HIGH_PRIO_TID);
 }
@@ -285,7 +278,7 @@ mwifiex_wmm_setup_queue_priorities(struct mwifiex_private *priv,
                }
        }
 
-       mwifiex_wmm_queue_priorities_tid(&priv->wmm);
+       mwifiex_wmm_queue_priorities_tid(priv);
 }
 
 /*
@@ -388,8 +381,7 @@ mwifiex_wmm_convert_tos_to_ac(struct mwifiex_adapter *adapter, u32 tos)
  * AP is disabled (due to call admission control (ACM bit). Mapping
  * of TID to AC is taken care of internally.
  */
-static u8
-mwifiex_wmm_downgrade_tid(struct mwifiex_private *priv, u32 tid)
+u8 mwifiex_wmm_downgrade_tid(struct mwifiex_private *priv, u32 tid)
 {
        enum mwifiex_wmm_ac_e ac, ac_down;
        u8 new_tid;
@@ -421,9 +413,11 @@ mwifiex_wmm_init(struct mwifiex_adapter *adapter)
                        continue;
 
                for (i = 0; i < MAX_NUM_TID; ++i) {
-                       priv->aggr_prio_tbl[i].amsdu = tos_to_tid_inv[i];
-                       priv->aggr_prio_tbl[i].ampdu_ap = tos_to_tid_inv[i];
-                       priv->aggr_prio_tbl[i].ampdu_user = tos_to_tid_inv[i];
+                       priv->aggr_prio_tbl[i].amsdu = priv->tos_to_tid_inv[i];
+                       priv->aggr_prio_tbl[i].ampdu_ap =
+                                                       priv->tos_to_tid_inv[i];
+                       priv->aggr_prio_tbl[i].ampdu_user =
+                                                       priv->tos_to_tid_inv[i];
                }
 
                priv->aggr_prio_tbl[6].amsdu
@@ -546,6 +540,7 @@ void
 mwifiex_clean_txrx(struct mwifiex_private *priv)
 {
        unsigned long flags;
+       struct sk_buff *skb, *tmp;
 
        mwifiex_11n_cleanup_reorder_tbl(priv);
        spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags);
@@ -562,6 +557,9 @@ mwifiex_clean_txrx(struct mwifiex_private *priv)
        if (priv->adapter->if_ops.clean_pcie_ring)
                priv->adapter->if_ops.clean_pcie_ring(priv->adapter);
        spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags);
+
+       skb_queue_walk_safe(&priv->tdls_txq, skb, tmp)
+               mwifiex_write_data_complete(priv->adapter, skb, 0, -1);
 }
 
 /*
@@ -590,7 +588,7 @@ mwifiex_wmm_get_ralist_node(struct mwifiex_private *priv, u8 tid,
  * If no such node is found, a new node is added first and then
  * retrieved.
  */
-static struct mwifiex_ra_list_tbl *
+struct mwifiex_ra_list_tbl *
 mwifiex_wmm_get_queue_raptr(struct mwifiex_private *priv, u8 tid, u8 *ra_addr)
 {
        struct mwifiex_ra_list_tbl *ra_list;
@@ -640,6 +638,21 @@ mwifiex_wmm_add_buf_txqueue(struct mwifiex_private *priv,
        struct mwifiex_ra_list_tbl *ra_list;
        u8 ra[ETH_ALEN], tid_down;
        unsigned long flags;
+       struct list_head list_head;
+       int tdls_status = TDLS_NOT_SETUP;
+       struct ethhdr *eth_hdr = (struct ethhdr *)skb->data;
+       struct mwifiex_txinfo *tx_info = MWIFIEX_SKB_TXCB(skb);
+
+       memcpy(ra, eth_hdr->h_dest, ETH_ALEN);
+
+       if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA &&
+           ISSUPP_TDLS_ENABLED(adapter->fw_cap_info)) {
+               if (ntohs(eth_hdr->h_proto) == ETH_P_TDLS)
+                       dev_dbg(adapter->dev,
+                               "TDLS setup packet for %pM. Don't block\n", ra);
+               else
+                       tdls_status = mwifiex_get_tdls_link_status(priv, ra);
+       }
 
        if (!priv->media_connected && !mwifiex_is_skb_mgmt_frame(skb)) {
                dev_dbg(adapter->dev, "data: drop packet in disconnect\n");
@@ -658,12 +671,27 @@ mwifiex_wmm_add_buf_txqueue(struct mwifiex_private *priv,
           have only 1 raptr for a tid in case of infra */
        if (!mwifiex_queuing_ra_based(priv) &&
            !mwifiex_is_skb_mgmt_frame(skb)) {
-               if (!list_empty(&priv->wmm.tid_tbl_ptr[tid_down].ra_list))
-                       ra_list = list_first_entry(
-                               &priv->wmm.tid_tbl_ptr[tid_down].ra_list,
-                               struct mwifiex_ra_list_tbl, list);
-               else
-                       ra_list = NULL;
+               switch (tdls_status) {
+               case TDLS_SETUP_COMPLETE:
+                       ra_list = mwifiex_wmm_get_queue_raptr(priv, tid_down,
+                                                             ra);
+                       tx_info->flags |= MWIFIEX_BUF_FLAG_TDLS_PKT;
+                       break;
+               case TDLS_SETUP_INPROGRESS:
+                       skb_queue_tail(&priv->tdls_txq, skb);
+                       spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock,
+                                              flags);
+                       return;
+               default:
+                       list_head = priv->wmm.tid_tbl_ptr[tid_down].ra_list;
+                       if (!list_empty(&list_head))
+                               ra_list = list_first_entry(
+                                       &list_head, struct mwifiex_ra_list_tbl,
+                                       list);
+                       else
+                               ra_list = NULL;
+                       break;
+               }
        } else {
                memcpy(ra, skb->data, ETH_ALEN);
                if (ra[0] & 0x01 || mwifiex_is_skb_mgmt_frame(skb))
@@ -683,9 +711,9 @@ mwifiex_wmm_add_buf_txqueue(struct mwifiex_private *priv,
        ra_list->total_pkt_count++;
 
        if (atomic_read(&priv->wmm.highest_queued_prio) <
-                                               tos_to_tid_inv[tid_down])
+                                               priv->tos_to_tid_inv[tid_down])
                atomic_set(&priv->wmm.highest_queued_prio,
-                          tos_to_tid_inv[tid_down]);
+                          priv->tos_to_tid_inv[tid_down]);
 
        atomic_inc(&priv->wmm.tx_pkts_queued);
 
@@ -1226,7 +1254,7 @@ mwifiex_dequeue_tx_packet(struct mwifiex_adapter *adapter)
                /* ra_list_spinlock has been freed in
                   mwifiex_send_single_packet() */
        } else {
-               if (mwifiex_is_ampdu_allowed(priv, tid) &&
+               if (mwifiex_is_ampdu_allowed(priv, ptr, tid) &&
                    ptr->ba_pkt_count > ptr->ba_packet_thr) {
                        if (mwifiex_space_avail_for_new_ba_stream(adapter)) {
                                mwifiex_create_ba_tbl(priv, ptr->ra, tid,
index 0f129d4..83e4208 100644 (file)
@@ -33,6 +33,21 @@ enum ieee_types_wmm_ecw_bitmasks {
 
 static const u16 mwifiex_1d_to_wmm_queue[8] = { 1, 0, 0, 1, 2, 2, 3, 3 };
 
+/*
+ * This table inverses the tos_to_tid operation to get a priority
+ * which is in sequential order, and can be compared.
+ * Use this to compare the priority of two different TIDs.
+ */
+static const u8 tos_to_tid_inv[] = {
+       0x02,  /* from tos_to_tid[2] = 0 */
+       0x00,  /* from tos_to_tid[0] = 1 */
+       0x01,  /* from tos_to_tid[1] = 2 */
+       0x03,
+       0x04,
+       0x05,
+       0x06,
+       0x07};
+
 /*
  * This function retrieves the TID of the given RA list.
  */
@@ -107,5 +122,8 @@ void mwifiex_wmm_setup_queue_priorities(struct mwifiex_private *priv,
 void mwifiex_wmm_setup_ac_downgrade(struct mwifiex_private *priv);
 int mwifiex_ret_wmm_get_status(struct mwifiex_private *priv,
                               const struct host_cmd_ds_command *resp);
+struct mwifiex_ra_list_tbl *
+mwifiex_wmm_get_queue_raptr(struct mwifiex_private *priv, u8 tid, u8 *ra_addr);
+u8 mwifiex_wmm_downgrade_tid(struct mwifiex_private *priv, u32 tid);
 
 #endif /* !_MWIFIEX_WMM_H_ */
index 5028557..2e89a86 100644 (file)
@@ -2835,7 +2835,9 @@ static void rndis_wlan_do_link_up_work(struct usbnet *usbdev)
                                        bssid, req_ie, req_ie_len,
                                        resp_ie, resp_ie_len, GFP_KERNEL);
        } else if (priv->infra_mode == NDIS_80211_INFRA_ADHOC)
-               cfg80211_ibss_joined(usbdev->net, bssid, GFP_KERNEL);
+               cfg80211_ibss_joined(usbdev->net, bssid,
+                                    get_current_channel(usbdev, NULL),
+                                    GFP_KERNEL);
 
        kfree(info);
 
index caddc1b..14a90dd 100644 (file)
@@ -125,9 +125,9 @@ static inline bool rt2800usb_entry_txstatus_timeout(struct queue_entry *entry)
 
        tout = time_after(jiffies, entry->last_action + msecs_to_jiffies(100));
        if (unlikely(tout))
-               rt2x00_warn(entry->queue->rt2x00dev,
-                           "TX status timeout for entry %d in queue %d\n",
-                           entry->entry_idx, entry->queue->qid);
+               rt2x00_dbg(entry->queue->rt2x00dev,
+                          "TX status timeout for entry %d in queue %d\n",
+                          entry->entry_idx, entry->queue->qid);
        return tout;
 
 }
@@ -566,8 +566,8 @@ static void rt2800usb_txdone(struct rt2x00_dev *rt2x00dev)
                queue = rt2x00queue_get_tx_queue(rt2x00dev, qid);
 
                if (unlikely(rt2x00queue_empty(queue))) {
-                       rt2x00_warn(rt2x00dev, "Got TX status for an empty queue %u, dropping\n",
-                                   qid);
+                       rt2x00_dbg(rt2x00dev, "Got TX status for an empty queue %u, dropping\n",
+                                  qid);
                        break;
                }
 
index 3867d14..7980ab1 100644 (file)
@@ -619,13 +619,13 @@ static int rtl8180_start(struct ieee80211_hw *dev)
 
        if (priv->r8185) {
                reg = rtl818x_ioread8(priv, &priv->map->CW_CONF);
-               reg &= ~RTL818X_CW_CONF_PERPACKET_CW_SHIFT;
-               reg |= RTL818X_CW_CONF_PERPACKET_RETRY_SHIFT;
+               reg &= ~RTL818X_CW_CONF_PERPACKET_CW;
+               reg |= RTL818X_CW_CONF_PERPACKET_RETRY;
                rtl818x_iowrite8(priv, &priv->map->CW_CONF, reg);
 
                reg = rtl818x_ioread8(priv, &priv->map->TX_AGC_CTL);
-               reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_GAIN_SHIFT;
-               reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL_SHIFT;
+               reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_GAIN;
+               reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL;
                reg |=  RTL818X_TX_AGC_CTL_FEEDBACK_ANT;
                rtl818x_iowrite8(priv, &priv->map->TX_AGC_CTL, reg);
 
index fd78df8..c981bcf 100644 (file)
@@ -785,7 +785,7 @@ static int rtl8187b_init_hw(struct ieee80211_hw *dev)
        rtl818x_iowrite16(priv, (__le16 *)0xFF34, 0x0FFF);
 
        reg = rtl818x_ioread8(priv, &priv->map->CW_CONF);
-       reg |= RTL818X_CW_CONF_PERPACKET_RETRY_SHIFT;
+       reg |= RTL818X_CW_CONF_PERPACKET_RETRY;
        rtl818x_iowrite8(priv, &priv->map->CW_CONF, reg);
 
        /* Auto Rate Fallback Register (ARFR): 1M-54M setting */
@@ -943,8 +943,8 @@ static int rtl8187_start(struct ieee80211_hw *dev)
                rtl818x_iowrite32(priv, &priv->map->RX_CONF, reg);
 
                reg = rtl818x_ioread8(priv, &priv->map->TX_AGC_CTL);
-               reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_GAIN_SHIFT;
-               reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL_SHIFT;
+               reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_GAIN;
+               reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL;
                reg &= ~RTL818X_TX_AGC_CTL_FEEDBACK_ANT;
                rtl818x_iowrite8(priv, &priv->map->TX_AGC_CTL, reg);
 
@@ -986,13 +986,13 @@ static int rtl8187_start(struct ieee80211_hw *dev)
        rtl818x_iowrite32(priv, &priv->map->RX_CONF, reg);
 
        reg = rtl818x_ioread8(priv, &priv->map->CW_CONF);
-       reg &= ~RTL818X_CW_CONF_PERPACKET_CW_SHIFT;
-       reg |= RTL818X_CW_CONF_PERPACKET_RETRY_SHIFT;
+       reg &= ~RTL818X_CW_CONF_PERPACKET_CW;
+       reg |= RTL818X_CW_CONF_PERPACKET_RETRY;
        rtl818x_iowrite8(priv, &priv->map->CW_CONF, reg);
 
        reg = rtl818x_ioread8(priv, &priv->map->TX_AGC_CTL);
-       reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_GAIN_SHIFT;
-       reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL_SHIFT;
+       reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_GAIN;
+       reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL;
        reg &= ~RTL818X_TX_AGC_CTL_FEEDBACK_ANT;
        rtl818x_iowrite8(priv, &priv->map->TX_AGC_CTL, reg);
 
index ce23dfd..fa7f7f6 100644 (file)
@@ -144,9 +144,9 @@ struct rtl818x_csr {
        __le32  HSSI_PARA;
        u8      reserved_13[4];
        u8      TX_AGC_CTL;
-#define RTL818X_TX_AGC_CTL_PERPACKET_GAIN_SHIFT                (1 << 0)
-#define RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL_SHIFT      (1 << 1)
-#define RTL818X_TX_AGC_CTL_FEEDBACK_ANT                        (1 << 2)
+#define RTL818X_TX_AGC_CTL_PERPACKET_GAIN      (1 << 0)
+#define RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL    (1 << 1)
+#define RTL818X_TX_AGC_CTL_FEEDBACK_ANT                (1 << 2)
        u8      TX_GAIN_CCK;
        u8      TX_GAIN_OFDM;
        u8      TX_ANTENNA;
@@ -158,8 +158,8 @@ struct rtl818x_csr {
        u8      SLOT;
        u8      reserved_16[5];
        u8      CW_CONF;
-#define RTL818X_CW_CONF_PERPACKET_CW_SHIFT     (1 << 0)
-#define RTL818X_CW_CONF_PERPACKET_RETRY_SHIFT  (1 << 1)
+#define RTL818X_CW_CONF_PERPACKET_CW   (1 << 0)
+#define RTL818X_CW_CONF_PERPACKET_RETRY        (1 << 1)
        u8      CW_VAL;
        u8      RATE_FALLBACK;
 #define RTL818X_RATE_FALLBACK_ENABLE   (1 << 7)
index a98acef..ee28a1a 100644 (file)
@@ -260,8 +260,7 @@ static void rtl_rate_free_sta(void *rtlpriv,
        kfree(rate_priv);
 }
 
-static struct rate_control_ops rtl_rate_ops = {
-       .module = NULL,
+static const struct rate_control_ops rtl_rate_ops = {
        .name = "rtl_rc",
        .alloc = rtl_rate_alloc,
        .free = rtl_rate_free,
index aece6c9..27ace30 100644 (file)
@@ -452,7 +452,7 @@ bool rtl88ee_rx_query_desc(struct ieee80211_hw *hw,
                        /* During testing, hdr was NULL */
                        return false;
                }
-               if ((ieee80211_is_robust_mgmt_frame(hdr)) &&
+               if ((_ieee80211_is_robust_mgmt_frame(hdr)) &&
                    (ieee80211_has_protected(hdr->frame_control)))
                        rx_status->flag &= ~RX_FLAG_DECRYPTED;
                else
index 52abf0a..114858d 100644 (file)
@@ -393,7 +393,7 @@ bool rtl92ce_rx_query_desc(struct ieee80211_hw *hw,
                        /* In testing, hdr was NULL here */
                        return false;
                }
-               if ((ieee80211_is_robust_mgmt_frame(hdr)) &&
+               if ((_ieee80211_is_robust_mgmt_frame(hdr)) &&
                    (ieee80211_has_protected(hdr->frame_control)))
                        rx_status->flag &= ~RX_FLAG_DECRYPTED;
                else
index 27efbcd..163a681 100644 (file)
@@ -310,7 +310,7 @@ bool rtl92se_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats,
                        /* during testing, hdr was NULL here */
                        return false;
                }
-               if ((ieee80211_is_robust_mgmt_frame(hdr)) &&
+               if ((_ieee80211_is_robust_mgmt_frame(hdr)) &&
                        (ieee80211_has_protected(hdr->frame_control)))
                        rx_status->flag &= ~RX_FLAG_DECRYPTED;
                else
index 50b7be3..721162c 100644 (file)
@@ -334,7 +334,7 @@ bool rtl8723ae_rx_query_desc(struct ieee80211_hw *hw,
                        /* during testing, hdr could be NULL here */
                        return false;
                }
-               if ((ieee80211_is_robust_mgmt_frame(hdr)) &&
+               if ((_ieee80211_is_robust_mgmt_frame(hdr)) &&
                        (ieee80211_has_protected(hdr->frame_control)))
                        rx_status->flag &= ~RX_FLAG_DECRYPTED;
                else
index be7129b..d50dfac 100644 (file)
@@ -1378,7 +1378,7 @@ static u32 wl12xx_get_rx_packet_len(struct wl1271 *wl, void *rx_data,
 
 static int wl12xx_tx_delayed_compl(struct wl1271 *wl)
 {
-       if (wl->fw_status_1->tx_results_counter ==
+       if (wl->fw_status->tx_results_counter ==
            (wl->tx_results_count & 0xff))
                return 0;
 
@@ -1438,6 +1438,37 @@ out:
        return ret;
 }
 
+static void wl12xx_convert_fw_status(struct wl1271 *wl, void *raw_fw_status,
+                                    struct wl_fw_status *fw_status)
+{
+       struct wl12xx_fw_status *int_fw_status = raw_fw_status;
+
+       fw_status->intr = le32_to_cpu(int_fw_status->intr);
+       fw_status->fw_rx_counter = int_fw_status->fw_rx_counter;
+       fw_status->drv_rx_counter = int_fw_status->drv_rx_counter;
+       fw_status->tx_results_counter = int_fw_status->tx_results_counter;
+       fw_status->rx_pkt_descs = int_fw_status->rx_pkt_descs;
+
+       fw_status->fw_localtime = le32_to_cpu(int_fw_status->fw_localtime);
+       fw_status->link_ps_bitmap = le32_to_cpu(int_fw_status->link_ps_bitmap);
+       fw_status->link_fast_bitmap =
+                       le32_to_cpu(int_fw_status->link_fast_bitmap);
+       fw_status->total_released_blks =
+                       le32_to_cpu(int_fw_status->total_released_blks);
+       fw_status->tx_total = le32_to_cpu(int_fw_status->tx_total);
+
+       fw_status->counters.tx_released_pkts =
+                       int_fw_status->counters.tx_released_pkts;
+       fw_status->counters.tx_lnk_free_pkts =
+                       int_fw_status->counters.tx_lnk_free_pkts;
+       fw_status->counters.tx_voice_released_blks =
+                       int_fw_status->counters.tx_voice_released_blks;
+       fw_status->counters.tx_last_rate =
+                       int_fw_status->counters.tx_last_rate;
+
+       fw_status->log_start_addr = le32_to_cpu(int_fw_status->log_start_addr);
+}
+
 static u32 wl12xx_sta_get_ap_rate_mask(struct wl1271 *wl,
                                       struct wl12xx_vif *wlvif)
 {
@@ -1677,6 +1708,7 @@ static struct wlcore_ops wl12xx_ops = {
        .tx_delayed_compl       = wl12xx_tx_delayed_compl,
        .hw_init                = wl12xx_hw_init,
        .init_vif               = NULL,
+       .convert_fw_status      = wl12xx_convert_fw_status,
        .sta_get_ap_rate_mask   = wl12xx_sta_get_ap_rate_mask,
        .get_pg_ver             = wl12xx_get_pg_ver,
        .get_mac                = wl12xx_get_mac,
@@ -1711,22 +1743,53 @@ static struct ieee80211_sta_ht_cap wl12xx_ht_cap = {
                },
 };
 
+static const struct ieee80211_iface_limit wl12xx_iface_limits[] = {
+       {
+               .max = 3,
+               .types = BIT(NL80211_IFTYPE_STATION),
+       },
+       {
+               .max = 1,
+               .types = BIT(NL80211_IFTYPE_AP) |
+                        BIT(NL80211_IFTYPE_P2P_GO) |
+                        BIT(NL80211_IFTYPE_P2P_CLIENT),
+       },
+};
+
+static const struct ieee80211_iface_combination
+wl12xx_iface_combinations[] = {
+       {
+               .max_interfaces = 3,
+               .limits = wl12xx_iface_limits,
+               .n_limits = ARRAY_SIZE(wl12xx_iface_limits),
+               .num_different_channels = 1,
+       },
+};
+
 static int wl12xx_setup(struct wl1271 *wl)
 {
        struct wl12xx_priv *priv = wl->priv;
        struct wlcore_platdev_data *pdev_data = dev_get_platdata(&wl->pdev->dev);
        struct wl12xx_platform_data *pdata = pdev_data->pdata;
 
+       BUILD_BUG_ON(WL12XX_MAX_LINKS > WLCORE_MAX_LINKS);
+       BUILD_BUG_ON(WL12XX_MAX_AP_STATIONS > WL12XX_MAX_LINKS);
+
        wl->rtable = wl12xx_rtable;
        wl->num_tx_desc = WL12XX_NUM_TX_DESCRIPTORS;
        wl->num_rx_desc = WL12XX_NUM_RX_DESCRIPTORS;
-       wl->num_channels = 1;
+       wl->num_links = WL12XX_MAX_LINKS;
+       wl->max_ap_stations = WL12XX_MAX_AP_STATIONS;
+       wl->iface_combinations = wl12xx_iface_combinations;
+       wl->n_iface_combinations = ARRAY_SIZE(wl12xx_iface_combinations);
        wl->num_mac_addr = WL12XX_NUM_MAC_ADDRESSES;
        wl->band_rate_to_idx = wl12xx_band_rate_to_idx;
        wl->hw_tx_rate_tbl_size = WL12XX_CONF_HW_RXTX_RATE_MAX;
        wl->hw_min_ht_rate = WL12XX_CONF_HW_RXTX_RATE_MCS0;
+       wl->fw_status_len = sizeof(struct wl12xx_fw_status);
        wl->fw_status_priv_len = 0;
        wl->stats.fw_stats_len = sizeof(struct wl12xx_acx_statistics);
+       wl->ofdm_only_ap = true;
        wlcore_set_ht_cap(wl, IEEE80211_BAND_2GHZ, &wl12xx_ht_cap);
        wlcore_set_ht_cap(wl, IEEE80211_BAND_5GHZ, &wl12xx_ht_cap);
        wl12xx_conf_init(wl);
index 9e5484a..75c9265 100644 (file)
@@ -65,6 +65,9 @@
 
 #define WL12XX_RX_BA_MAX_SESSIONS 3
 
+#define WL12XX_MAX_AP_STATIONS 8
+#define WL12XX_MAX_LINKS 12
+
 struct wl127x_rx_mem_pool_addr {
        u32 addr;
        u32 addr_extra;
@@ -79,4 +82,54 @@ struct wl12xx_priv {
        struct wl127x_rx_mem_pool_addr *rx_mem_addr;
 };
 
+struct wl12xx_fw_packet_counters {
+       /* Cumulative counter of released packets per AC */
+       u8 tx_released_pkts[NUM_TX_QUEUES];
+
+       /* Cumulative counter of freed packets per HLID */
+       u8 tx_lnk_free_pkts[WL12XX_MAX_LINKS];
+
+       /* Cumulative counter of released Voice memory blocks */
+       u8 tx_voice_released_blks;
+
+       /* Tx rate of the last transmitted packet */
+       u8 tx_last_rate;
+
+       u8 padding[2];
+} __packed;
+
+/* FW status registers */
+struct wl12xx_fw_status {
+       __le32 intr;
+       u8  fw_rx_counter;
+       u8  drv_rx_counter;
+       u8  reserved;
+       u8  tx_results_counter;
+       __le32 rx_pkt_descs[WL12XX_NUM_RX_DESCRIPTORS];
+
+       __le32 fw_localtime;
+
+       /*
+        * A bitmap (where each bit represents a single HLID)
+        * to indicate if the station is in PS mode.
+        */
+       __le32 link_ps_bitmap;
+
+       /*
+        * A bitmap (where each bit represents a single HLID) to indicate
+        * if the station is in Fast mode
+        */
+       __le32 link_fast_bitmap;
+
+       /* Cumulative counter of total released mem blocks since FW-reset */
+       __le32 total_released_blks;
+
+       /* Size (in Memory Blocks) of TX pool */
+       __le32 tx_total;
+
+       struct wl12xx_fw_packet_counters counters;
+
+       __le32 log_start_addr;
+} __packed;
+
 #endif /* __WL12XX_PRIV_H__ */
index ec37b16..de5b4fa 100644 (file)
@@ -648,7 +648,7 @@ static const struct wl18xx_clk_cfg wl18xx_clk_table[NUM_CLOCK_CONFIGS] = {
 };
 
 /* TODO: maybe move to a new header file? */
-#define WL18XX_FW_NAME "ti-connectivity/wl18xx-fw-2.bin"
+#define WL18XX_FW_NAME "ti-connectivity/wl18xx-fw-3.bin"
 
 static int wl18xx_identify_chip(struct wl1271 *wl)
 {
@@ -1133,6 +1133,39 @@ static int wl18xx_hw_init(struct wl1271 *wl)
        return ret;
 }
 
+static void wl18xx_convert_fw_status(struct wl1271 *wl, void *raw_fw_status,
+                                    struct wl_fw_status *fw_status)
+{
+       struct wl18xx_fw_status *int_fw_status = raw_fw_status;
+
+       fw_status->intr = le32_to_cpu(int_fw_status->intr);
+       fw_status->fw_rx_counter = int_fw_status->fw_rx_counter;
+       fw_status->drv_rx_counter = int_fw_status->drv_rx_counter;
+       fw_status->tx_results_counter = int_fw_status->tx_results_counter;
+       fw_status->rx_pkt_descs = int_fw_status->rx_pkt_descs;
+
+       fw_status->fw_localtime = le32_to_cpu(int_fw_status->fw_localtime);
+       fw_status->link_ps_bitmap = le32_to_cpu(int_fw_status->link_ps_bitmap);
+       fw_status->link_fast_bitmap =
+                       le32_to_cpu(int_fw_status->link_fast_bitmap);
+       fw_status->total_released_blks =
+                       le32_to_cpu(int_fw_status->total_released_blks);
+       fw_status->tx_total = le32_to_cpu(int_fw_status->tx_total);
+
+       fw_status->counters.tx_released_pkts =
+                       int_fw_status->counters.tx_released_pkts;
+       fw_status->counters.tx_lnk_free_pkts =
+                       int_fw_status->counters.tx_lnk_free_pkts;
+       fw_status->counters.tx_voice_released_blks =
+                       int_fw_status->counters.tx_voice_released_blks;
+       fw_status->counters.tx_last_rate =
+                       int_fw_status->counters.tx_last_rate;
+
+       fw_status->log_start_addr = le32_to_cpu(int_fw_status->log_start_addr);
+
+       fw_status->priv = &int_fw_status->priv;
+}
+
 static void wl18xx_set_tx_desc_csum(struct wl1271 *wl,
                                    struct wl1271_tx_hw_descr *desc,
                                    struct sk_buff *skb)
@@ -1572,7 +1605,7 @@ static bool wl18xx_lnk_high_prio(struct wl1271 *wl, u8 hlid,
 {
        u8 thold;
        struct wl18xx_fw_status_priv *status_priv =
-               (struct wl18xx_fw_status_priv *)wl->fw_status_2->priv;
+               (struct wl18xx_fw_status_priv *)wl->fw_status->priv;
        u32 suspend_bitmap = le32_to_cpu(status_priv->link_suspend_bitmap);
 
        /* suspended links are never high priority */
@@ -1594,7 +1627,7 @@ static bool wl18xx_lnk_low_prio(struct wl1271 *wl, u8 hlid,
 {
        u8 thold;
        struct wl18xx_fw_status_priv *status_priv =
-               (struct wl18xx_fw_status_priv *)wl->fw_status_2->priv;
+               (struct wl18xx_fw_status_priv *)wl->fw_status->priv;
        u32 suspend_bitmap = le32_to_cpu(status_priv->link_suspend_bitmap);
 
        if (test_bit(hlid, (unsigned long *)&suspend_bitmap))
@@ -1632,6 +1665,7 @@ static struct wlcore_ops wl18xx_ops = {
        .tx_immediate_compl = wl18xx_tx_immediate_completion,
        .tx_delayed_compl = NULL,
        .hw_init        = wl18xx_hw_init,
+       .convert_fw_status = wl18xx_convert_fw_status,
        .set_tx_desc_csum = wl18xx_set_tx_desc_csum,
        .get_pg_ver     = wl18xx_get_pg_ver,
        .set_rx_csum = wl18xx_set_rx_csum,
@@ -1713,19 +1747,62 @@ static struct ieee80211_sta_ht_cap wl18xx_mimo_ht_cap_2ghz = {
                },
 };
 
+static const struct ieee80211_iface_limit wl18xx_iface_limits[] = {
+       {
+               .max = 3,
+               .types = BIT(NL80211_IFTYPE_STATION),
+       },
+       {
+               .max = 1,
+               .types = BIT(NL80211_IFTYPE_AP) |
+                        BIT(NL80211_IFTYPE_P2P_GO) |
+                        BIT(NL80211_IFTYPE_P2P_CLIENT),
+       },
+};
+
+static const struct ieee80211_iface_limit wl18xx_iface_ap_limits[] = {
+       {
+               .max = 2,
+               .types = BIT(NL80211_IFTYPE_AP),
+       },
+};
+
+static const struct ieee80211_iface_combination
+wl18xx_iface_combinations[] = {
+       {
+               .max_interfaces = 3,
+               .limits = wl18xx_iface_limits,
+               .n_limits = ARRAY_SIZE(wl18xx_iface_limits),
+               .num_different_channels = 2,
+       },
+       {
+               .max_interfaces = 2,
+               .limits = wl18xx_iface_ap_limits,
+               .n_limits = ARRAY_SIZE(wl18xx_iface_ap_limits),
+               .num_different_channels = 1,
+       }
+};
+
 static int wl18xx_setup(struct wl1271 *wl)
 {
        struct wl18xx_priv *priv = wl->priv;
        int ret;
 
+       BUILD_BUG_ON(WL18XX_MAX_LINKS > WLCORE_MAX_LINKS);
+       BUILD_BUG_ON(WL18XX_MAX_AP_STATIONS > WL18XX_MAX_LINKS);
+
        wl->rtable = wl18xx_rtable;
        wl->num_tx_desc = WL18XX_NUM_TX_DESCRIPTORS;
        wl->num_rx_desc = WL18XX_NUM_RX_DESCRIPTORS;
-       wl->num_channels = 2;
+       wl->num_links = WL18XX_MAX_LINKS;
+       wl->max_ap_stations = WL18XX_MAX_AP_STATIONS;
+       wl->iface_combinations = wl18xx_iface_combinations;
+       wl->n_iface_combinations = ARRAY_SIZE(wl18xx_iface_combinations);
        wl->num_mac_addr = WL18XX_NUM_MAC_ADDRESSES;
        wl->band_rate_to_idx = wl18xx_band_rate_to_idx;
        wl->hw_tx_rate_tbl_size = WL18XX_CONF_HW_RXTX_RATE_MAX;
        wl->hw_min_ht_rate = WL18XX_CONF_HW_RXTX_RATE_MCS0;
+       wl->fw_status_len = sizeof(struct wl18xx_fw_status);
        wl->fw_status_priv_len = sizeof(struct wl18xx_fw_status_priv);
        wl->stats.fw_stats_len = sizeof(struct wl18xx_acx_statistics);
        wl->static_data_priv_len = sizeof(struct wl18xx_static_data_priv);
index 57c6943..be1ebd5 100644 (file)
@@ -32,7 +32,7 @@ static
 void wl18xx_get_last_tx_rate(struct wl1271 *wl, struct ieee80211_vif *vif,
                             struct ieee80211_tx_rate *rate)
 {
-       u8 fw_rate = wl->fw_status_2->counters.tx_last_rate;
+       u8 fw_rate = wl->fw_status->counters.tx_last_rate;
 
        if (fw_rate > CONF_HW_RATE_INDEX_MAX) {
                wl1271_error("last Tx rate invalid: %d", fw_rate);
@@ -139,7 +139,7 @@ static void wl18xx_tx_complete_packet(struct wl1271 *wl, u8 tx_stat_byte)
 void wl18xx_tx_immediate_complete(struct wl1271 *wl)
 {
        struct wl18xx_fw_status_priv *status_priv =
-               (struct wl18xx_fw_status_priv *)wl->fw_status_2->priv;
+               (struct wl18xx_fw_status_priv *)wl->fw_status->priv;
        struct wl18xx_priv *priv = wl->priv;
        u8 i;
 
index 9204e07..eb7cfe8 100644 (file)
 
 /* minimum FW required for driver */
 #define WL18XX_CHIP_VER                8
-#define WL18XX_IFTYPE_VER      5
+#define WL18XX_IFTYPE_VER      8
 #define WL18XX_MAJOR_VER       WLCORE_FW_VER_IGNORE
 #define WL18XX_SUBTYPE_VER     WLCORE_FW_VER_IGNORE
-#define WL18XX_MINOR_VER       39
+#define WL18XX_MINOR_VER       13
 
 #define WL18XX_CMD_MAX_SIZE          740
 
 
 #define WL18XX_NUM_MAC_ADDRESSES 3
 
-#define WL18XX_RX_BA_MAX_SESSIONS 5
+#define WL18XX_RX_BA_MAX_SESSIONS 13
+
+#define WL18XX_MAX_AP_STATIONS 10
+#define WL18XX_MAX_LINKS 16
 
 struct wl18xx_priv {
        /* buffer for sending commands to FW */
@@ -109,6 +112,59 @@ struct wl18xx_fw_status_priv {
        u8 padding[3];
 };
 
+struct wl18xx_fw_packet_counters {
+       /* Cumulative counter of released packets per AC */
+       u8 tx_released_pkts[NUM_TX_QUEUES];
+
+       /* Cumulative counter of freed packets per HLID */
+       u8 tx_lnk_free_pkts[WL18XX_MAX_LINKS];
+
+       /* Cumulative counter of released Voice memory blocks */
+       u8 tx_voice_released_blks;
+
+       /* Tx rate of the last transmitted packet */
+       u8 tx_last_rate;
+
+       u8 padding[2];
+} __packed;
+
+/* FW status registers */
+struct wl18xx_fw_status {
+       __le32 intr;
+       u8  fw_rx_counter;
+       u8  drv_rx_counter;
+       u8  reserved;
+       u8  tx_results_counter;
+       __le32 rx_pkt_descs[WL18XX_NUM_RX_DESCRIPTORS];
+
+       __le32 fw_localtime;
+
+       /*
+        * A bitmap (where each bit represents a single HLID)
+        * to indicate if the station is in PS mode.
+        */
+       __le32 link_ps_bitmap;
+
+       /*
+        * A bitmap (where each bit represents a single HLID) to indicate
+        * if the station is in Fast mode
+        */
+       __le32 link_fast_bitmap;
+
+       /* Cumulative counter of total released mem blocks since FW-reset */
+       __le32 total_released_blks;
+
+       /* Size (in Memory Blocks) of TX pool */
+       __le32 tx_total;
+
+       struct wl18xx_fw_packet_counters counters;
+
+       __le32 log_start_addr;
+
+       /* Private status to be used by the lower drivers */
+       struct wl18xx_fw_status_priv priv;
+} __packed;
+
 #define WL18XX_PHY_VERSION_MAX_LEN 20
 
 struct wl18xx_static_data_priv {
index ec83675..b924cea 100644 (file)
@@ -358,7 +358,8 @@ int wl1271_acx_beacon_filter_opt(struct wl1271 *wl, struct wl12xx_vif *wlvif,
        struct acx_beacon_filter_option *beacon_filter = NULL;
        int ret = 0;
 
-       wl1271_debug(DEBUG_ACX, "acx beacon filter opt");
+       wl1271_debug(DEBUG_ACX, "acx beacon filter opt enable=%d",
+                    enable_filter);
 
        if (enable_filter &&
            wl->conf.conn.bcn_filt_mode == CONF_BCN_FILT_MODE_DISABLED)
@@ -1591,7 +1592,8 @@ out:
        return ret;
 }
 
-int wl1271_acx_set_inconnection_sta(struct wl1271 *wl, u8 *addr)
+int wl1271_acx_set_inconnection_sta(struct wl1271 *wl,
+                                   struct wl12xx_vif *wlvif, u8 *addr)
 {
        struct wl1271_acx_inconnection_sta *acx = NULL;
        int ret;
@@ -1603,6 +1605,7 @@ int wl1271_acx_set_inconnection_sta(struct wl1271 *wl, u8 *addr)
                return -ENOMEM;
 
        memcpy(acx->addr, addr, ETH_ALEN);
+       acx->role_id = wlvif->role_id;
 
        ret = wl1271_cmd_configure(wl, ACX_UPDATE_INCONNECTION_STA_LIST,
                                   acx, sizeof(*acx));
index 6dcfad9..954d57e 100644 (file)
@@ -824,7 +824,8 @@ struct wl1271_acx_inconnection_sta {
        struct acx_header header;
 
        u8 addr[ETH_ALEN];
-       u8 padding1[2];
+       u8 role_id;
+       u8 padding;
 } __packed;
 
 /*
@@ -1118,7 +1119,8 @@ int wl1271_acx_ps_rx_streaming(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                               bool enable);
 int wl1271_acx_ap_max_tx_retry(struct wl1271 *wl, struct wl12xx_vif *wlvif);
 int wl12xx_acx_config_ps(struct wl1271 *wl, struct wl12xx_vif *wlvif);
-int wl1271_acx_set_inconnection_sta(struct wl1271 *wl, u8 *addr);
+int wl1271_acx_set_inconnection_sta(struct wl1271 *wl,
+                                   struct wl12xx_vif *wlvif, u8 *addr);
 int wl1271_acx_fm_coex(struct wl1271 *wl);
 int wl12xx_acx_set_rate_mgmt_params(struct wl1271 *wl);
 int wl12xx_acx_config_hangover(struct wl1271 *wl);
index 9b2ecf5..40dc30f 100644 (file)
@@ -60,8 +60,8 @@ static int __wlcore_cmd_send(struct wl1271 *wl, u16 id, void *buf,
        u16 status;
        u16 poll_count = 0;
 
-       if (WARN_ON(wl->state == WLCORE_STATE_RESTARTING &&
-                   id != CMD_STOP_FWLOGGER))
+       if (unlikely(wl->state == WLCORE_STATE_RESTARTING &&
+                    id != CMD_STOP_FWLOGGER))
                return -EIO;
 
        cmd = buf;
@@ -312,8 +312,8 @@ static int wlcore_get_new_session_id(struct wl1271 *wl, u8 hlid)
 int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid)
 {
        unsigned long flags;
-       u8 link = find_first_zero_bit(wl->links_map, WL12XX_MAX_LINKS);
-       if (link >= WL12XX_MAX_LINKS)
+       u8 link = find_first_zero_bit(wl->links_map, wl->num_links);
+       if (link >= wl->num_links)
                return -EBUSY;
 
        wl->session_ids[link] = wlcore_get_new_session_id(wl, link);
@@ -324,9 +324,14 @@ int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid)
        __set_bit(link, wlvif->links_map);
        spin_unlock_irqrestore(&wl->wl_lock, flags);
 
-       /* take the last "freed packets" value from the current FW status */
-       wl->links[link].prev_freed_pkts =
-                       wl->fw_status_2->counters.tx_lnk_free_pkts[link];
+       /*
+        * take the last "freed packets" value from the current FW status.
+        * on recovery, we might not have fw_status yet, and
+        * tx_lnk_free_pkts will be NULL. check for it.
+        */
+       if (wl->fw_status->counters.tx_lnk_free_pkts)
+               wl->links[link].prev_freed_pkts =
+                       wl->fw_status->counters.tx_lnk_free_pkts[link];
        wl->links[link].wlvif = wlvif;
 
        /*
@@ -1527,6 +1532,7 @@ int wl12xx_cmd_add_peer(struct wl1271 *wl, struct wl12xx_vif *wlvif,
        cmd->sp_len = sta->max_sp;
        cmd->wmm = sta->wme ? 1 : 0;
        cmd->session_id = wl->session_ids[hlid];
+       cmd->role_id = wlvif->role_id;
 
        for (i = 0; i < NUM_ACCESS_CATEGORIES_COPY; i++)
                if (sta->wme && (sta->uapsd_queues & BIT(i)))
@@ -1563,7 +1569,8 @@ out:
        return ret;
 }
 
-int wl12xx_cmd_remove_peer(struct wl1271 *wl, u8 hlid)
+int wl12xx_cmd_remove_peer(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                          u8 hlid)
 {
        struct wl12xx_cmd_remove_peer *cmd;
        int ret;
@@ -1581,6 +1588,7 @@ int wl12xx_cmd_remove_peer(struct wl1271 *wl, u8 hlid)
        /* We never send a deauth, mac80211 is in charge of this */
        cmd->reason_opcode = 0;
        cmd->send_deauth_flag = 0;
+       cmd->role_id = wlvif->role_id;
 
        ret = wl1271_cmd_send(wl, CMD_REMOVE_PEER, cmd, sizeof(*cmd), 0);
        if (ret < 0) {
index 323d4a8..b084830 100644 (file)
@@ -88,7 +88,8 @@ int wl12xx_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 role_id,
 int wl12xx_croc(struct wl1271 *wl, u8 role_id);
 int wl12xx_cmd_add_peer(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                        struct ieee80211_sta *sta, u8 hlid);
-int wl12xx_cmd_remove_peer(struct wl1271 *wl, u8 hlid);
+int wl12xx_cmd_remove_peer(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                          u8 hlid);
 void wlcore_set_pending_regdomain_ch(struct wl1271 *wl, u16 channel,
                                     enum ieee80211_band band);
 int wlcore_cmd_regdomain_config_locked(struct wl1271 *wl);
@@ -206,7 +207,7 @@ enum cmd_templ {
 #define WL1271_COMMAND_TIMEOUT     2000
 #define WL1271_CMD_TEMPL_DFLT_SIZE 252
 #define WL1271_CMD_TEMPL_MAX_SIZE  512
-#define WL1271_EVENT_TIMEOUT       1500
+#define WL1271_EVENT_TIMEOUT       5000
 
 struct wl1271_cmd_header {
        __le16 id;
@@ -594,6 +595,8 @@ struct wl12xx_cmd_add_peer {
        u8 sp_len;
        u8 wmm;
        u8 session_id;
+       u8 role_id;
+       u8 padding[3];
 } __packed;
 
 struct wl12xx_cmd_remove_peer {
@@ -602,7 +605,7 @@ struct wl12xx_cmd_remove_peer {
        u8 hlid;
        u8 reason_opcode;
        u8 send_deauth_flag;
-       u8 padding1;
+       u8 role_id;
 } __packed;
 
 /*
index 8d3b349..1f9a360 100644 (file)
@@ -67,7 +67,7 @@ static void wl1271_stop_ba_event(struct wl1271 *wl, struct wl12xx_vif *wlvif)
                u8 hlid;
                struct wl1271_link *lnk;
                for_each_set_bit(hlid, wlvif->ap.sta_hlid_map,
-                                WL12XX_MAX_LINKS) {
+                                wl->num_links) {
                        lnk = &wl->links[hlid];
                        if (!lnk->ba_bitmap)
                                continue;
@@ -172,7 +172,7 @@ static void wlcore_disconnect_sta(struct wl1271 *wl, unsigned long sta_bitmap)
        const u8 *addr;
        int h;
 
-       for_each_set_bit(h, &sta_bitmap, WL12XX_MAX_LINKS) {
+       for_each_set_bit(h, &sta_bitmap, wl->num_links) {
                bool found = false;
                /* find the ap vif connected to this sta */
                wl12xx_for_each_wlvif_ap(wl, wlvif) {
index 51f8d63..1555ff9 100644 (file)
@@ -106,6 +106,15 @@ wlcore_hw_init_vif(struct wl1271 *wl, struct wl12xx_vif *wlvif)
        return 0;
 }
 
+static inline void
+wlcore_hw_convert_fw_status(struct wl1271 *wl, void *raw_fw_status,
+                           struct wl_fw_status *fw_status)
+{
+       BUG_ON(!wl->ops->convert_fw_status);
+
+       wl->ops->convert_fw_status(wl, raw_fw_status, fw_status);
+}
+
 static inline u32
 wlcore_hw_sta_get_ap_rate_mask(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 {
index 7699f9d..199e941 100644 (file)
@@ -287,8 +287,8 @@ static int wl1271_init_sta_beacon_filter(struct wl1271 *wl,
        if (ret < 0)
                return ret;
 
-       /* enable beacon filtering */
-       ret = wl1271_acx_beacon_filter_opt(wl, wlvif, true);
+       /* disable beacon filtering until we get the first beacon */
+       ret = wl1271_acx_beacon_filter_opt(wl, wlvif, false);
        if (ret < 0)
                return ret;
 
@@ -462,7 +462,7 @@ int wl1271_init_ap_rates(struct wl1271 *wl, struct wl12xx_vif *wlvif)
         * If the basic rates contain OFDM rates, use OFDM only
         * rates for unicast TX as well. Else use all supported rates.
         */
-       if ((wlvif->basic_rate_set & CONF_TX_OFDM_RATES))
+       if (wl->ofdm_only_ap && (wlvif->basic_rate_set & CONF_TX_OFDM_RATES))
                supported_rates = CONF_TX_OFDM_RATES;
        else
                supported_rates = CONF_TX_ENABLED_RATES;
index 07e3d6a..0305729 100644 (file)
@@ -60,7 +60,9 @@ static inline int __must_check wlcore_raw_write(struct wl1271 *wl, int addr,
 {
        int ret;
 
-       if (test_bit(WL1271_FLAG_IO_FAILED, &wl->flags))
+       if (test_bit(WL1271_FLAG_IO_FAILED, &wl->flags) ||
+           WARN_ON((test_bit(WL1271_FLAG_IN_ELP, &wl->flags) &&
+                    addr != HW_ACCESS_ELP_CTRL_REG)))
                return -EIO;
 
        ret = wl->if_ops->write(wl->dev, addr, buf, len, fixed);
@@ -76,7 +78,9 @@ static inline int __must_check wlcore_raw_read(struct wl1271 *wl, int addr,
 {
        int ret;
 
-       if (test_bit(WL1271_FLAG_IO_FAILED, &wl->flags))
+       if (test_bit(WL1271_FLAG_IO_FAILED, &wl->flags) ||
+           WARN_ON((test_bit(WL1271_FLAG_IN_ELP, &wl->flags) &&
+                    addr != HW_ACCESS_ELP_CTRL_REG)))
                return -EIO;
 
        ret = wl->if_ops->read(wl->dev, addr, buf, len, fixed);
index b46b311..7aae5b3 100644 (file)
@@ -345,24 +345,24 @@ static void wl12xx_irq_ps_regulate_link(struct wl1271 *wl,
         * Start high-level PS if the STA is asleep with enough blocks in FW.
         * Make an exception if this is the only connected link. In this
         * case FW-memory congestion is less of a problem.
-        * Note that a single connected STA means 3 active links, since we must
-        * account for the global and broadcast AP links. The "fw_ps" check
-        * assures us the third link is a STA connected to the AP. Otherwise
-        * the FW would not set the PSM bit.
+        * Note that a single connected STA means 2*ap_count + 1 active links,
+        * since we must account for the global and broadcast AP links
+        * for each AP. The "fw_ps" check assures us the other link is a STA
+        * connected to the AP. Otherwise the FW would not set the PSM bit.
         */
-       else if (wl->active_link_count > 3 && fw_ps &&
+       else if (wl->active_link_count > (wl->ap_count*2 + 1) && fw_ps &&
                 tx_pkts >= WL1271_PS_STA_MAX_PACKETS)
                wl12xx_ps_link_start(wl, wlvif, hlid, true);
 }
 
 static void wl12xx_irq_update_links_status(struct wl1271 *wl,
                                           struct wl12xx_vif *wlvif,
-                                          struct wl_fw_status_2 *status)
+                                          struct wl_fw_status *status)
 {
        u32 cur_fw_ps_map;
        u8 hlid;
 
-       cur_fw_ps_map = le32_to_cpu(status->link_ps_bitmap);
+       cur_fw_ps_map = status->link_ps_bitmap;
        if (wl->ap_fw_ps_map != cur_fw_ps_map) {
                wl1271_debug(DEBUG_PSM,
                             "link ps prev 0x%x cur 0x%x changed 0x%x",
@@ -372,77 +372,73 @@ static void wl12xx_irq_update_links_status(struct wl1271 *wl,
                wl->ap_fw_ps_map = cur_fw_ps_map;
        }
 
-       for_each_set_bit(hlid, wlvif->ap.sta_hlid_map, WL12XX_MAX_LINKS)
+       for_each_set_bit(hlid, wlvif->ap.sta_hlid_map, wl->num_links)
                wl12xx_irq_ps_regulate_link(wl, wlvif, hlid,
                                            wl->links[hlid].allocated_pkts);
 }
 
-static int wlcore_fw_status(struct wl1271 *wl,
-                           struct wl_fw_status_1 *status_1,
-                           struct wl_fw_status_2 *status_2)
+static int wlcore_fw_status(struct wl1271 *wl, struct wl_fw_status *status)
 {
        struct wl12xx_vif *wlvif;
        struct timespec ts;
        u32 old_tx_blk_count = wl->tx_blocks_available;
        int avail, freed_blocks;
        int i;
-       size_t status_len;
        int ret;
        struct wl1271_link *lnk;
 
-       status_len = WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc) +
-               sizeof(*status_2) + wl->fw_status_priv_len;
-
-       ret = wlcore_raw_read_data(wl, REG_RAW_FW_STATUS_ADDR, status_1,
-                                  status_len, false);
+       ret = wlcore_raw_read_data(wl, REG_RAW_FW_STATUS_ADDR,
+                                  wl->raw_fw_status,
+                                  wl->fw_status_len, false);
        if (ret < 0)
                return ret;
 
+       wlcore_hw_convert_fw_status(wl, wl->raw_fw_status, wl->fw_status);
+
        wl1271_debug(DEBUG_IRQ, "intr: 0x%x (fw_rx_counter = %d, "
                     "drv_rx_counter = %d, tx_results_counter = %d)",
-                    status_1->intr,
-                    status_1->fw_rx_counter,
-                    status_1->drv_rx_counter,
-                    status_1->tx_results_counter);
+                    status->intr,
+                    status->fw_rx_counter,
+                    status->drv_rx_counter,
+                    status->tx_results_counter);
 
        for (i = 0; i < NUM_TX_QUEUES; i++) {
                /* prevent wrap-around in freed-packets counter */
                wl->tx_allocated_pkts[i] -=
-                               (status_2->counters.tx_released_pkts[i] -
+                               (status->counters.tx_released_pkts[i] -
                                wl->tx_pkts_freed[i]) & 0xff;
 
-               wl->tx_pkts_freed[i] = status_2->counters.tx_released_pkts[i];
+               wl->tx_pkts_freed[i] = status->counters.tx_released_pkts[i];
        }
 
 
-       for_each_set_bit(i, wl->links_map, WL12XX_MAX_LINKS) {
+       for_each_set_bit(i, wl->links_map, wl->num_links) {
                u8 diff;
                lnk = &wl->links[i];
 
                /* prevent wrap-around in freed-packets counter */
-               diff = (status_2->counters.tx_lnk_free_pkts[i] -
+               diff = (status->counters.tx_lnk_free_pkts[i] -
                       lnk->prev_freed_pkts) & 0xff;
 
                if (diff == 0)
                        continue;
 
                lnk->allocated_pkts -= diff;
-               lnk->prev_freed_pkts = status_2->counters.tx_lnk_free_pkts[i];
+               lnk->prev_freed_pkts = status->counters.tx_lnk_free_pkts[i];
 
                /* accumulate the prev_freed_pkts counter */
                lnk->total_freed_pkts += diff;
        }
 
        /* prevent wrap-around in total blocks counter */
-       if (likely(wl->tx_blocks_freed <=
-                  le32_to_cpu(status_2->total_released_blks)))
-               freed_blocks = le32_to_cpu(status_2->total_released_blks) -
+       if (likely(wl->tx_blocks_freed <= status->total_released_blks))
+               freed_blocks = status->total_released_blks -
                               wl->tx_blocks_freed;
        else
                freed_blocks = 0x100000000LL - wl->tx_blocks_freed +
-                              le32_to_cpu(status_2->total_released_blks);
+                              status->total_released_blks;
 
-       wl->tx_blocks_freed = le32_to_cpu(status_2->total_released_blks);
+       wl->tx_blocks_freed = status->total_released_blks;
 
        wl->tx_allocated_blocks -= freed_blocks;
 
@@ -458,7 +454,7 @@ static int wlcore_fw_status(struct wl1271 *wl,
                        cancel_delayed_work(&wl->tx_watchdog_work);
        }
 
-       avail = le32_to_cpu(status_2->tx_total) - wl->tx_allocated_blocks;
+       avail = status->tx_total - wl->tx_allocated_blocks;
 
        /*
         * The FW might change the total number of TX memblocks before
@@ -477,15 +473,15 @@ static int wlcore_fw_status(struct wl1271 *wl,
 
        /* for AP update num of allocated TX blocks per link and ps status */
        wl12xx_for_each_wlvif_ap(wl, wlvif) {
-               wl12xx_irq_update_links_status(wl, wlvif, status_2);
+               wl12xx_irq_update_links_status(wl, wlvif, status);
        }
 
        /* update the host-chipset time offset */
        getnstimeofday(&ts);
        wl->time_offset = (timespec_to_ns(&ts) >> 10) -
-               (s64)le32_to_cpu(status_2->fw_localtime);
+               (s64)(status->fw_localtime);
 
-       wl->fw_fast_lnk_map = le32_to_cpu(status_2->link_fast_bitmap);
+       wl->fw_fast_lnk_map = status->link_fast_bitmap;
 
        return 0;
 }
@@ -549,13 +545,13 @@ static int wlcore_irq_locked(struct wl1271 *wl)
                clear_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags);
                smp_mb__after_clear_bit();
 
-               ret = wlcore_fw_status(wl, wl->fw_status_1, wl->fw_status_2);
+               ret = wlcore_fw_status(wl, wl->fw_status);
                if (ret < 0)
                        goto out;
 
                wlcore_hw_tx_immediate_compl(wl);
 
-               intr = le32_to_cpu(wl->fw_status_1->intr);
+               intr = wl->fw_status->intr;
                intr &= WLCORE_ALL_INTR_MASK;
                if (!intr) {
                        done = true;
@@ -584,7 +580,7 @@ static int wlcore_irq_locked(struct wl1271 *wl)
                if (likely(intr & WL1271_ACX_INTR_DATA)) {
                        wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_DATA");
 
-                       ret = wlcore_rx(wl, wl->fw_status_1);
+                       ret = wlcore_rx(wl, wl->fw_status);
                        if (ret < 0)
                                goto out;
 
@@ -786,10 +782,11 @@ out:
 
 void wl12xx_queue_recovery_work(struct wl1271 *wl)
 {
-       WARN_ON(!test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags));
-
        /* Avoid a recursive recovery */
        if (wl->state == WLCORE_STATE_ON) {
+               WARN_ON(!test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY,
+                                 &wl->flags));
+
                wl->state = WLCORE_STATE_RESTARTING;
                set_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags);
                wl1271_ps_elp_wakeup(wl);
@@ -843,11 +840,11 @@ static void wl12xx_read_fwlog_panic(struct wl1271 *wl)
                wl12xx_cmd_stop_fwlog(wl);
 
        /* Read the first memory block address */
-       ret = wlcore_fw_status(wl, wl->fw_status_1, wl->fw_status_2);
+       ret = wlcore_fw_status(wl, wl->fw_status);
        if (ret < 0)
                goto out;
 
-       addr = le32_to_cpu(wl->fw_status_2->log_start_addr);
+       addr = wl->fw_status->log_start_addr;
        if (!addr)
                goto out;
 
@@ -990,23 +987,23 @@ static int wlcore_fw_wakeup(struct wl1271 *wl)
 
 static int wl1271_setup(struct wl1271 *wl)
 {
-       wl->fw_status_1 = kzalloc(WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc) +
-                                 sizeof(*wl->fw_status_2) +
-                                 wl->fw_status_priv_len, GFP_KERNEL);
-       if (!wl->fw_status_1)
-               return -ENOMEM;
+       wl->raw_fw_status = kzalloc(wl->fw_status_len, GFP_KERNEL);
+       if (!wl->raw_fw_status)
+               goto err;
 
-       wl->fw_status_2 = (struct wl_fw_status_2 *)
-                               (((u8 *) wl->fw_status_1) +
-                               WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc));
+       wl->fw_status = kzalloc(sizeof(*wl->fw_status), GFP_KERNEL);
+       if (!wl->fw_status)
+               goto err;
 
        wl->tx_res_if = kzalloc(sizeof(*wl->tx_res_if), GFP_KERNEL);
-       if (!wl->tx_res_if) {
-               kfree(wl->fw_status_1);
-               return -ENOMEM;
-       }
+       if (!wl->tx_res_if)
+               goto err;
 
        return 0;
+err:
+       kfree(wl->fw_status);
+       kfree(wl->raw_fw_status);
+       return -ENOMEM;
 }
 
 static int wl12xx_set_power_on(struct wl1271 *wl)
@@ -1767,6 +1764,12 @@ static int wl1271_op_suspend(struct ieee80211_hw *hw,
        flush_work(&wl->tx_work);
        flush_delayed_work(&wl->elp_work);
 
+       /*
+        * Cancel the watchdog even if above tx_flush failed. We will detect
+        * it on resume anyway.
+        */
+       cancel_delayed_work(&wl->tx_watchdog_work);
+
        return 0;
 }
 
@@ -1824,6 +1827,13 @@ static int wl1271_op_resume(struct ieee80211_hw *hw)
 
 out:
        wl->wow_enabled = false;
+
+       /*
+        * Set a flag to re-init the watchdog on the first Tx after resume.
+        * That way we avoid possible conditions where Tx-complete interrupts
+        * fail to arrive and we perform a spurious recovery.
+        */
+       set_bit(WL1271_FLAG_REINIT_TX_WDOG, &wl->flags);
        mutex_unlock(&wl->mutex);
 
        return 0;
@@ -1914,6 +1924,7 @@ static void wlcore_op_stop_locked(struct wl1271 *wl)
        memset(wl->links_map, 0, sizeof(wl->links_map));
        memset(wl->roc_map, 0, sizeof(wl->roc_map));
        memset(wl->session_ids, 0, sizeof(wl->session_ids));
+       memset(wl->rx_filter_enabled, 0, sizeof(wl->rx_filter_enabled));
        wl->active_sta_count = 0;
        wl->active_link_count = 0;
 
@@ -1938,9 +1949,10 @@ static void wlcore_op_stop_locked(struct wl1271 *wl)
 
        wl1271_debugfs_reset(wl);
 
-       kfree(wl->fw_status_1);
-       wl->fw_status_1 = NULL;
-       wl->fw_status_2 = NULL;
+       kfree(wl->raw_fw_status);
+       wl->raw_fw_status = NULL;
+       kfree(wl->fw_status);
+       wl->fw_status = NULL;
        kfree(wl->tx_res_if);
        wl->tx_res_if = NULL;
        kfree(wl->target_mem_map);
@@ -2571,10 +2583,8 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl,
                ieee80211_scan_completed(wl->hw, true);
        }
 
-       if (wl->sched_vif == wlvif) {
-               ieee80211_sched_scan_stopped(wl->hw);
+       if (wl->sched_vif == wlvif)
                wl->sched_vif = NULL;
-       }
 
        if (wl->roc_vif == vif) {
                wl->roc_vif = NULL;
@@ -2931,6 +2941,11 @@ static int wlcore_unset_assoc(struct wl1271 *wl, struct wl12xx_vif *wlvif)
                ret = wl1271_acx_keep_alive_mode(wl, wlvif, false);
                if (ret < 0)
                        return ret;
+
+               /* disable beacon filtering */
+               ret = wl1271_acx_beacon_filter_opt(wl, wlvif, false);
+               if (ret < 0)
+                       return ret;
        }
 
        if (test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags)) {
@@ -3463,6 +3478,10 @@ static void wl1271_op_set_default_key_idx(struct ieee80211_hw *hw,
        wl1271_debug(DEBUG_MAC80211, "mac80211 set default key idx %d",
                     key_idx);
 
+       /* we don't handle unsetting of default key */
+       if (key_idx == -1)
+               return;
+
        mutex_lock(&wl->mutex);
 
        if (unlikely(wl->state != WLCORE_STATE_ON)) {
@@ -4298,6 +4317,13 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
                }
        }
 
+       if ((changed & BSS_CHANGED_BEACON_INFO) && bss_conf->dtim_period) {
+               /* enable beacon filtering */
+               ret = wl1271_acx_beacon_filter_opt(wl, wlvif, true);
+               if (ret < 0)
+                       goto out;
+       }
+
        ret = wl1271_bss_erp_info_changed(wl, vif, bss_conf, changed);
        if (ret < 0)
                goto out;
@@ -4651,7 +4677,7 @@ static int wl1271_allocate_sta(struct wl1271 *wl,
        int ret;
 
 
-       if (wl->active_sta_count >= AP_MAX_STATIONS) {
+       if (wl->active_sta_count >= wl->max_ap_stations) {
                wl1271_warning("could not allocate HLID - too much stations");
                return -EBUSY;
        }
@@ -4754,7 +4780,7 @@ static int wl12xx_sta_remove(struct wl1271 *wl,
        if (WARN_ON(!test_bit(id, wlvif->ap.sta_hlid_map)))
                return -EINVAL;
 
-       ret = wl12xx_cmd_remove_peer(wl, wl_sta->hlid);
+       ret = wl12xx_cmd_remove_peer(wl, wlvif, wl_sta->hlid);
        if (ret < 0)
                return ret;
 
@@ -5679,28 +5705,6 @@ static void wl1271_unregister_hw(struct wl1271 *wl)
 
 }
 
-static const struct ieee80211_iface_limit wlcore_iface_limits[] = {
-       {
-               .max = 3,
-               .types = BIT(NL80211_IFTYPE_STATION),
-       },
-       {
-               .max = 1,
-               .types = BIT(NL80211_IFTYPE_AP) |
-                        BIT(NL80211_IFTYPE_P2P_GO) |
-                        BIT(NL80211_IFTYPE_P2P_CLIENT),
-       },
-};
-
-static struct ieee80211_iface_combination
-wlcore_iface_combinations[] = {
-       {
-         .max_interfaces = 3,
-         .limits = wlcore_iface_limits,
-         .n_limits = ARRAY_SIZE(wlcore_iface_limits),
-       },
-};
-
 static int wl1271_init_ieee80211(struct wl1271 *wl)
 {
        int i;
@@ -5733,7 +5737,8 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
                IEEE80211_HW_AP_LINK_PS |
                IEEE80211_HW_AMPDU_AGGREGATION |
                IEEE80211_HW_TX_AMPDU_SETUP_IN_HW |
-               IEEE80211_HW_QUEUE_CONTROL;
+               IEEE80211_HW_QUEUE_CONTROL |
+               IEEE80211_HW_CHANCTX_STA_CSA;
 
        wl->hw->wiphy->cipher_suites = cipher_suites;
        wl->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
@@ -5821,10 +5826,8 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
                NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P;
 
        /* allowed interface combinations */
-       wlcore_iface_combinations[0].num_different_channels = wl->num_channels;
-       wl->hw->wiphy->iface_combinations = wlcore_iface_combinations;
-       wl->hw->wiphy->n_iface_combinations =
-               ARRAY_SIZE(wlcore_iface_combinations);
+       wl->hw->wiphy->iface_combinations = wl->iface_combinations;
+       wl->hw->wiphy->n_iface_combinations = wl->n_iface_combinations;
 
        SET_IEEE80211_DEV(wl->hw, wl->dev);
 
@@ -5844,8 +5847,6 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size,
        int i, j, ret;
        unsigned int order;
 
-       BUILD_BUG_ON(AP_MAX_STATIONS > WL12XX_MAX_LINKS);
-
        hw = ieee80211_alloc_hw(sizeof(*wl), &wl1271_ops);
        if (!hw) {
                wl1271_error("could not alloc ieee80211_hw");
@@ -5867,8 +5868,12 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size,
 
        wl->hw = hw;
 
+       /*
+        * wl->num_links is not configured yet, so just use WLCORE_MAX_LINKS.
+        * we don't allocate any additional resource here, so that's fine.
+        */
        for (i = 0; i < NUM_TX_QUEUES; i++)
-               for (j = 0; j < WL12XX_MAX_LINKS; j++)
+               for (j = 0; j < WLCORE_MAX_LINKS; j++)
                        skb_queue_head_init(&wl->links[j].tx_queue[i]);
 
        skb_queue_head_init(&wl->deferred_rx_queue);
@@ -6011,7 +6016,8 @@ int wlcore_free_hw(struct wl1271 *wl)
        kfree(wl->nvs);
        wl->nvs = NULL;
 
-       kfree(wl->fw_status_1);
+       kfree(wl->raw_fw_status);
+       kfree(wl->fw_status);
        kfree(wl->tx_res_if);
        destroy_workqueue(wl->freezable_wq);
 
index 26bfc36..b52516e 100644 (file)
@@ -280,7 +280,11 @@ void wl12xx_ps_link_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
        struct ieee80211_sta *sta;
        struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
 
-       if (test_bit(hlid, &wl->ap_ps_map))
+       if (WARN_ON_ONCE(wlvif->bss_type != BSS_TYPE_AP_BSS))
+               return;
+
+       if (!test_bit(hlid, wlvif->ap.sta_hlid_map) ||
+           test_bit(hlid, &wl->ap_ps_map))
                return;
 
        wl1271_debug(DEBUG_PSM, "start mac80211 PSM on hlid %d pkts %d "
index 6791a1a..e125974 100644 (file)
@@ -203,9 +203,9 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length,
        return is_data;
 }
 
-int wlcore_rx(struct wl1271 *wl, struct wl_fw_status_1 *status)
+int wlcore_rx(struct wl1271 *wl, struct wl_fw_status *status)
 {
-       unsigned long active_hlids[BITS_TO_LONGS(WL12XX_MAX_LINKS)] = {0};
+       unsigned long active_hlids[BITS_TO_LONGS(WLCORE_MAX_LINKS)] = {0};
        u32 buf_size;
        u32 fw_rx_counter = status->fw_rx_counter % wl->num_rx_desc;
        u32 drv_rx_counter = wl->rx_counter % wl->num_rx_desc;
@@ -263,12 +263,12 @@ int wlcore_rx(struct wl1271 *wl, struct wl_fw_status_1 *status)
                                                  wl->aggr_buf + pkt_offset,
                                                  pkt_len, rx_align,
                                                  &hlid) == 1) {
-                               if (hlid < WL12XX_MAX_LINKS)
+                               if (hlid < wl->num_links)
                                        __set_bit(hlid, active_hlids);
                                else
                                        WARN(1,
-                                            "hlid exceeded WL12XX_MAX_LINKS "
-                                            "(%d)\n", hlid);
+                                            "hlid (%d) exceeded MAX_LINKS\n",
+                                            hlid);
                        }
 
                        wl->rx_counter++;
@@ -302,7 +302,7 @@ int wl1271_rx_filter_enable(struct wl1271 *wl,
 {
        int ret;
 
-       if (wl->rx_filter_enabled[index] == enable) {
+       if (!!test_bit(index, wl->rx_filter_enabled) == enable) {
                wl1271_warning("Request to enable an already "
                             "enabled rx filter %d", index);
                return 0;
@@ -316,7 +316,10 @@ int wl1271_rx_filter_enable(struct wl1271 *wl,
                return ret;
        }
 
-       wl->rx_filter_enabled[index] = enable;
+       if (enable)
+               __set_bit(index, wl->rx_filter_enabled);
+       else
+               __clear_bit(index, wl->rx_filter_enabled);
 
        return 0;
 }
@@ -326,7 +329,7 @@ int wl1271_rx_filter_clear_all(struct wl1271 *wl)
        int i, ret = 0;
 
        for (i = 0; i < WL1271_MAX_RX_FILTERS; i++) {
-               if (!wl->rx_filter_enabled[i])
+               if (!test_bit(i, wl->rx_filter_enabled))
                        continue;
                ret = wl1271_rx_filter_enable(wl, i, 0, NULL);
                if (ret)
index 3363f60..a3b1618 100644 (file)
@@ -142,7 +142,7 @@ struct wl1271_rx_descriptor {
        u8  reserved;
 } __packed;
 
-int wlcore_rx(struct wl1271 *wl, struct wl_fw_status_1 *status);
+int wlcore_rx(struct wl1271 *wl, struct wl_fw_status *status);
 u8 wl1271_rate_to_idx(int rate, enum ieee80211_band band);
 int wl1271_rx_filter_enable(struct wl1271 *wl,
                            int index, bool enable,
index 87cd707..40b4311 100644 (file)
@@ -101,7 +101,7 @@ static void wl1271_tx_ap_update_inconnection_sta(struct wl1271 *wl,
         * authentication response. this way it won't get de-authed by FW
         * when transmitting too soon.
         */
-       wl1271_acx_set_inconnection_sta(wl, hdr->addr1);
+       wl1271_acx_set_inconnection_sta(wl, wlvif, hdr->addr1);
 
        /*
         * ROC for 1 second on the AP channel for completing the connection.
@@ -134,12 +134,12 @@ static void wl1271_tx_regulate_link(struct wl1271 *wl,
         * into high-level PS and clean out its TX queues.
         * Make an exception if this is the only connected link. In this
         * case FW-memory congestion is less of a problem.
-        * Note that a single connected STA means 3 active links, since we must
-        * account for the global and broadcast AP links. The "fw_ps" check
-        * assures us the third link is a STA connected to the AP. Otherwise
-        * the FW would not set the PSM bit.
+        * Note that a single connected STA means 2*ap_count + 1 active links,
+        * since we must account for the global and broadcast AP links
+        * for each AP. The "fw_ps" check assures us the other link is a STA
+        * connected to the AP. Otherwise the FW would not set the PSM bit.
         */
-       if (wl->active_link_count > 3 && fw_ps &&
+       if (wl->active_link_count > (wl->ap_count*2 + 1) && fw_ps &&
            tx_pkts >= WL1271_PS_STA_MAX_PACKETS)
                wl12xx_ps_link_start(wl, wlvif, hlid, true);
 }
@@ -234,8 +234,13 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                wl->tx_blocks_available -= total_blocks;
                wl->tx_allocated_blocks += total_blocks;
 
-               /* If the FW was empty before, arm the Tx watchdog */
-               if (wl->tx_allocated_blocks == total_blocks)
+               /*
+                * If the FW was empty before, arm the Tx watchdog. Also do
+                * this on the first Tx after resume, as we always cancel the
+                * watchdog on suspend.
+                */
+               if (wl->tx_allocated_blocks == total_blocks ||
+                   test_and_clear_bit(WL1271_FLAG_REINIT_TX_WDOG, &wl->flags))
                        wl12xx_rearm_tx_watchdog_locked(wl);
 
                ac = wl1271_tx_get_queue(skb_get_queue_mapping(skb));
@@ -357,6 +362,10 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct wl12xx_vif *wlvif,
            ieee80211_has_protected(frame_control))
                tx_attr |= TX_HW_ATTR_HOST_ENCRYPT;
 
+       /* send EAPOL frames as voice */
+       if (control->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO)
+               tx_attr |= TX_HW_ATTR_EAPOL_FRAME;
+
        desc->tx_attr = cpu_to_le16(tx_attr);
 
        wlcore_hw_set_tx_desc_csum(wl, desc, skb);
@@ -560,11 +569,11 @@ static struct sk_buff *wlcore_vif_dequeue_high_prio(struct wl1271 *wl,
        int i, h, start_hlid;
 
        /* start from the link after the last one */
-       start_hlid = (wlvif->last_tx_hlid + 1) % WL12XX_MAX_LINKS;
+       start_hlid = (wlvif->last_tx_hlid + 1) % wl->num_links;
 
        /* dequeue according to AC, round robin on each link */
-       for (i = 0; i < WL12XX_MAX_LINKS; i++) {
-               h = (start_hlid + i) % WL12XX_MAX_LINKS;
+       for (i = 0; i < wl->num_links; i++) {
+               h = (start_hlid + i) % wl->num_links;
 
                /* only consider connected stations */
                if (!test_bit(h, wlvif->links_map))
@@ -688,8 +697,8 @@ static void wl1271_skb_queue_head(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                skb_queue_head(&wl->links[hlid].tx_queue[q], skb);
 
                /* make sure we dequeue the same packet next time */
-               wlvif->last_tx_hlid = (hlid + WL12XX_MAX_LINKS - 1) %
-                                     WL12XX_MAX_LINKS;
+               wlvif->last_tx_hlid = (hlid + wl->num_links - 1) %
+                                     wl->num_links;
        }
 
        spin_lock_irqsave(&wl->wl_lock, flags);
@@ -722,7 +731,7 @@ void wl12xx_rearm_rx_streaming(struct wl1271 *wl, unsigned long *active_hlids)
        timeout = wl->conf.rx_streaming.duration;
        wl12xx_for_each_wlvif_sta(wl, wlvif) {
                bool found = false;
-               for_each_set_bit(hlid, active_hlids, WL12XX_MAX_LINKS) {
+               for_each_set_bit(hlid, active_hlids, wl->num_links) {
                        if (test_bit(hlid, wlvif->links_map)) {
                                found  = true;
                                break;
@@ -759,7 +768,7 @@ int wlcore_tx_work_locked(struct wl1271 *wl)
        struct wl1271_tx_hw_descr *desc;
        u32 buf_offset = 0, last_len = 0;
        bool sent_packets = false;
-       unsigned long active_hlids[BITS_TO_LONGS(WL12XX_MAX_LINKS)] = {0};
+       unsigned long active_hlids[BITS_TO_LONGS(WLCORE_MAX_LINKS)] = {0};
        int ret = 0;
        int bus_ret = 0;
        u8 hlid;
@@ -1061,7 +1070,7 @@ void wl12xx_tx_reset_wlvif(struct wl1271 *wl, struct wl12xx_vif *wlvif)
        int i;
 
        /* TX failure */
-       for_each_set_bit(i, wlvif->links_map, WL12XX_MAX_LINKS) {
+       for_each_set_bit(i, wlvif->links_map, wl->num_links) {
                if (wlvif->bss_type == BSS_TYPE_AP_BSS &&
                    i != wlvif->ap.bcast_hlid && i != wlvif->ap.global_hlid) {
                        /* this calls wl12xx_free_link */
@@ -1085,7 +1094,7 @@ void wl12xx_tx_reset(struct wl1271 *wl)
 
        /* only reset the queues if something bad happened */
        if (wl1271_tx_total_queue_count(wl) != 0) {
-               for (i = 0; i < WL12XX_MAX_LINKS; i++)
+               for (i = 0; i < wl->num_links; i++)
                        wl1271_tx_reset_link_queues(wl, i);
 
                for (i = 0; i < NUM_TX_QUEUES; i++)
@@ -1178,7 +1187,7 @@ void wl1271_tx_flush(struct wl1271 *wl)
                       WL1271_TX_FLUSH_TIMEOUT / 1000);
 
        /* forcibly flush all Tx buffers on our queues */
-       for (i = 0; i < WL12XX_MAX_LINKS; i++)
+       for (i = 0; i < wl->num_links; i++)
                wl1271_tx_reset_link_queues(wl, i);
 
 out_wake:
index 35489c3..79cb3ff 100644 (file)
@@ -37,6 +37,7 @@
 #define TX_HW_ATTR_TX_CMPLT_REQ          BIT(12)
 #define TX_HW_ATTR_TX_DUMMY_REQ          BIT(13)
 #define TX_HW_ATTR_HOST_ENCRYPT          BIT(14)
+#define TX_HW_ATTR_EAPOL_FRAME           BIT(15)
 
 #define TX_HW_ATTR_OFST_SAVE_RETRIES     0
 #define TX_HW_ATTR_OFST_HEADER_PAD       1
index 06efc12..95a5450 100644 (file)
@@ -73,6 +73,8 @@ struct wlcore_ops {
        void (*tx_immediate_compl)(struct wl1271 *wl);
        int (*hw_init)(struct wl1271 *wl);
        int (*init_vif)(struct wl1271 *wl, struct wl12xx_vif *wlvif);
+       void (*convert_fw_status)(struct wl1271 *wl, void *raw_fw_status,
+                                 struct wl_fw_status *fw_status);
        u32 (*sta_get_ap_rate_mask)(struct wl1271 *wl,
                                    struct wl12xx_vif *wlvif);
        int (*get_pg_ver)(struct wl1271 *wl, s8 *ver);
@@ -220,7 +222,7 @@ struct wl1271 {
        int channel;
        u8 system_hlid;
 
-       unsigned long links_map[BITS_TO_LONGS(WL12XX_MAX_LINKS)];
+       unsigned long links_map[BITS_TO_LONGS(WLCORE_MAX_LINKS)];
        unsigned long roles_map[BITS_TO_LONGS(WL12XX_MAX_ROLES)];
        unsigned long roc_map[BITS_TO_LONGS(WL12XX_MAX_ROLES)];
        unsigned long rate_policies_map[
@@ -228,7 +230,7 @@ struct wl1271 {
        unsigned long klv_templates_map[
                        BITS_TO_LONGS(WLCORE_MAX_KLV_TEMPLATES)];
 
-       u8 session_ids[WL12XX_MAX_LINKS];
+       u8 session_ids[WLCORE_MAX_LINKS];
 
        struct list_head wlvif_list;
 
@@ -346,8 +348,8 @@ struct wl1271 {
        u32 buffer_cmd;
        u32 buffer_busyword[WL1271_BUSY_WORD_CNT];
 
-       struct wl_fw_status_1 *fw_status_1;
-       struct wl_fw_status_2 *fw_status_2;
+       void *raw_fw_status;
+       struct wl_fw_status *fw_status;
        struct wl1271_tx_hw_res_if *tx_res_if;
 
        /* Current chipset configuration */
@@ -376,7 +378,7 @@ struct wl1271 {
         * AP-mode - links indexed by HLID. The global and broadcast links
         * are always active.
         */
-       struct wl1271_link links[WL12XX_MAX_LINKS];
+       struct wl1271_link links[WLCORE_MAX_LINKS];
 
        /* number of currently active links */
        int active_link_count;
@@ -405,6 +407,9 @@ struct wl1271 {
        /* AP-mode - number of currently connected stations */
        int active_sta_count;
 
+       /* Flag determining whether AP should broadcast OFDM-only rates */
+       bool ofdm_only_ap;
+
        /* last wlvif we transmitted from */
        struct wl12xx_vif *last_wlvif;
 
@@ -434,6 +439,10 @@ struct wl1271 {
        u32 num_tx_desc;
        /* number of RX descriptors the HW supports. */
        u32 num_rx_desc;
+       /* number of links the HW supports */
+       u8 num_links;
+       /* max stations a single AP can support */
+       u8 max_ap_stations;
 
        /* translate HW Tx rates to standard rate-indices */
        const u8 **band_rate_to_idx;
@@ -448,10 +457,11 @@ struct wl1271 {
        struct ieee80211_sta_ht_cap ht_cap[WLCORE_NUM_BANDS];
 
        /* size of the private FW status data */
+       size_t fw_status_len;
        size_t fw_status_priv_len;
 
        /* RX Data filter rule state - enabled/disabled */
-       bool rx_filter_enabled[WL1271_MAX_RX_FILTERS];
+       unsigned long rx_filter_enabled[BITS_TO_LONGS(WL1271_MAX_RX_FILTERS)];
 
        /* size of the private static data */
        size_t static_data_priv_len;
@@ -476,8 +486,9 @@ struct wl1271 {
 
        struct completion nvs_loading_complete;
 
-       /* number of concurrent channels the HW supports */
-       u32 num_channels;
+       /* interface combinations supported by the hw */
+       const struct ieee80211_iface_combination *iface_combinations;
+       u8 n_iface_combinations;
 };
 
 int wlcore_probe(struct wl1271 *wl, struct platform_device *pdev);
index ce7261c..756e890 100644 (file)
 #define WL1271_DEFAULT_DTIM_PERIOD 1
 
 #define WL12XX_MAX_ROLES           4
-#define WL12XX_MAX_LINKS           12
 #define WL12XX_INVALID_ROLE_ID     0xff
 #define WL12XX_INVALID_LINK_ID     0xff
 
+/*
+ * max number of links allowed by all HWs.
+ * this is NOT the actual max links supported by the current hw.
+ */
+#define WLCORE_MAX_LINKS 16
+
 /* the driver supports the 2.4Ghz and 5Ghz bands */
 #define WLCORE_NUM_BANDS           2
 
@@ -118,72 +123,58 @@ struct wl1271_chip {
 
 #define NUM_TX_QUEUES              4
 
-#define AP_MAX_STATIONS            8
-
-struct wl_fw_packet_counters {
-       /* Cumulative counter of released packets per AC */
-       u8 tx_released_pkts[NUM_TX_QUEUES];
-
-       /* Cumulative counter of freed packets per HLID */
-       u8 tx_lnk_free_pkts[WL12XX_MAX_LINKS];
-
-       /* Cumulative counter of released Voice memory blocks */
-       u8 tx_voice_released_blks;
-
-       /* Tx rate of the last transmitted packet */
-       u8 tx_last_rate;
-
-       u8 padding[2];
-} __packed;
-
-/* FW status registers */
-struct wl_fw_status_1 {
-       __le32 intr;
+struct wl_fw_status {
+       u32 intr;
        u8  fw_rx_counter;
        u8  drv_rx_counter;
-       u8  reserved;
        u8  tx_results_counter;
-       __le32 rx_pkt_descs[0];
-} __packed;
-
-/*
- * Each HW arch has a different number of Rx descriptors.
- * The length of the status depends on it, since it holds an array
- * of descriptors.
- */
-#define WLCORE_FW_STATUS_1_LEN(num_rx_desc) \
-               (sizeof(struct wl_fw_status_1) + \
-               (sizeof(((struct wl_fw_status_1 *)0)->rx_pkt_descs[0])) * \
-               num_rx_desc)
+       __le32 *rx_pkt_descs;
 
-struct wl_fw_status_2 {
-       __le32 fw_localtime;
+       u32 fw_localtime;
 
        /*
         * A bitmap (where each bit represents a single HLID)
         * to indicate if the station is in PS mode.
         */
-       __le32 link_ps_bitmap;
+       u32 link_ps_bitmap;
 
        /*
         * A bitmap (where each bit represents a single HLID) to indicate
         * if the station is in Fast mode
         */
-       __le32 link_fast_bitmap;
+       u32 link_fast_bitmap;
 
        /* Cumulative counter of total released mem blocks since FW-reset */
-       __le32 total_released_blks;
+       u32 total_released_blks;
 
        /* Size (in Memory Blocks) of TX pool */
-       __le32 tx_total;
+       u32 tx_total;
+
+       struct {
+               /*
+                * Cumulative counter of released packets per AC
+                * (length of the array is NUM_TX_QUEUES)
+                */
+               u8 *tx_released_pkts;
 
-       struct wl_fw_packet_counters counters;
+               /*
+                * Cumulative counter of freed packets per HLID
+                * (length of the array is wl->num_links)
+                */
+               u8 *tx_lnk_free_pkts;
+
+               /* Cumulative counter of released Voice memory blocks */
+               u8 tx_voice_released_blks;
 
-       __le32 log_start_addr;
+               /* Tx rate of the last transmitted packet */
+               u8 tx_last_rate;
+       } counters;
+
+       u32 log_start_addr;
 
        /* Private status to be used by the lower drivers */
-       u8 priv[0];
-} __packed;
+       void *priv;
+};
 
 #define WL1271_MAX_CHANNELS 64
 struct wl1271_scan {
@@ -240,6 +231,7 @@ enum wl12xx_flags {
        WL1271_FLAG_VIF_CHANGE_IN_PROGRESS,
        WL1271_FLAG_INTENDED_FW_RECOVERY,
        WL1271_FLAG_IO_FAILED,
+       WL1271_FLAG_REINIT_TX_WDOG,
 };
 
 enum wl12xx_vif_flags {
@@ -368,7 +360,7 @@ struct wl12xx_vif {
 
                        /* HLIDs bitmap of associated stations */
                        unsigned long sta_hlid_map[BITS_TO_LONGS(
-                                                       WL12XX_MAX_LINKS)];
+                                                       WLCORE_MAX_LINKS)];
 
                        /* recoreded keys - set here before AP startup */
                        struct wl1271_ap_key *recorded_keys[MAX_NUM_KEYS];
@@ -385,7 +377,7 @@ struct wl12xx_vif {
        /* counters of packets per AC, across all links in the vif */
        int tx_queue_count[NUM_TX_QUEUES];
 
-       unsigned long links_map[BITS_TO_LONGS(WL12XX_MAX_LINKS)];
+       unsigned long links_map[BITS_TO_LONGS(WLCORE_MAX_LINKS)];
 
        u8 ssid[IEEE80211_MAX_SSID_LEN + 1];
        u8 ssid_len;
index d387f13..0cc32c6 100644 (file)
@@ -286,7 +286,6 @@ static void rtl_rate_free_sta(void *rtlpriv,
 }
 
 static struct rate_control_ops rtl_rate_ops = {
-       .module = NULL,
        .name = "rtl_rc",
        .alloc = rtl_rate_alloc,
        .free = rtl_rate_free,
index e526a8c..5f34935 100644 (file)
@@ -596,6 +596,20 @@ static inline int ieee80211_is_qos_nullfunc(__le16 fc)
               cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_QOS_NULLFUNC);
 }
 
+/**
+ * ieee80211_is_bufferable_mmpdu - check if frame is bufferable MMPDU
+ * @fc: frame control field in little-endian byteorder
+ */
+static inline bool ieee80211_is_bufferable_mmpdu(__le16 fc)
+{
+       /* IEEE 802.11-2012, definition of "bufferable management frame";
+        * note that this ignores the IBSS special case. */
+       return ieee80211_is_mgmt(fc) &&
+              (ieee80211_is_action(fc) ||
+               ieee80211_is_disassoc(fc) ||
+               ieee80211_is_deauth(fc));
+}
+
 /**
  * ieee80211_is_first_frag - check if IEEE80211_SCTL_FRAG is not set
  * @seq_ctrl: frame sequence control bytes in little-endian byteorder
@@ -1636,51 +1650,22 @@ enum ieee80211_reasoncode {
 enum ieee80211_eid {
        WLAN_EID_SSID = 0,
        WLAN_EID_SUPP_RATES = 1,
-       WLAN_EID_FH_PARAMS = 2,
+       WLAN_EID_FH_PARAMS = 2, /* reserved now */
        WLAN_EID_DS_PARAMS = 3,
        WLAN_EID_CF_PARAMS = 4,
        WLAN_EID_TIM = 5,
        WLAN_EID_IBSS_PARAMS = 6,
-       WLAN_EID_CHALLENGE = 16,
-
        WLAN_EID_COUNTRY = 7,
        WLAN_EID_HP_PARAMS = 8,
        WLAN_EID_HP_TABLE = 9,
        WLAN_EID_REQUEST = 10,
-
        WLAN_EID_QBSS_LOAD = 11,
        WLAN_EID_EDCA_PARAM_SET = 12,
        WLAN_EID_TSPEC = 13,
        WLAN_EID_TCLAS = 14,
        WLAN_EID_SCHEDULE = 15,
-       WLAN_EID_TS_DELAY = 43,
-       WLAN_EID_TCLAS_PROCESSING = 44,
-       WLAN_EID_QOS_CAPA = 46,
-       /* 802.11z */
-       WLAN_EID_LINK_ID = 101,
-       /* 802.11s */
-       WLAN_EID_MESH_CONFIG = 113,
-       WLAN_EID_MESH_ID = 114,
-       WLAN_EID_LINK_METRIC_REPORT = 115,
-       WLAN_EID_CONGESTION_NOTIFICATION = 116,
-       WLAN_EID_PEER_MGMT = 117,
-       WLAN_EID_CHAN_SWITCH_PARAM = 118,
-       WLAN_EID_MESH_AWAKE_WINDOW = 119,
-       WLAN_EID_BEACON_TIMING = 120,
-       WLAN_EID_MCCAOP_SETUP_REQ = 121,
-       WLAN_EID_MCCAOP_SETUP_RESP = 122,
-       WLAN_EID_MCCAOP_ADVERT = 123,
-       WLAN_EID_MCCAOP_TEARDOWN = 124,
-       WLAN_EID_GANN = 125,
-       WLAN_EID_RANN = 126,
-       WLAN_EID_PREQ = 130,
-       WLAN_EID_PREP = 131,
-       WLAN_EID_PERR = 132,
-       WLAN_EID_PXU = 137,
-       WLAN_EID_PXUC = 138,
-       WLAN_EID_AUTH_MESH_PEER_EXCH = 139,
-       WLAN_EID_MIC = 140,
-
+       WLAN_EID_CHALLENGE = 16,
+       /* 17-31 reserved for challenge text extension */
        WLAN_EID_PWR_CONSTRAINT = 32,
        WLAN_EID_PWR_CAPABILITY = 33,
        WLAN_EID_TPC_REQUEST = 34,
@@ -1691,66 +1676,114 @@ enum ieee80211_eid {
        WLAN_EID_MEASURE_REPORT = 39,
        WLAN_EID_QUIET = 40,
        WLAN_EID_IBSS_DFS = 41,
-
        WLAN_EID_ERP_INFO = 42,
-       WLAN_EID_EXT_SUPP_RATES = 50,
-
+       WLAN_EID_TS_DELAY = 43,
+       WLAN_EID_TCLAS_PROCESSING = 44,
        WLAN_EID_HT_CAPABILITY = 45,
-       WLAN_EID_HT_OPERATION = 61,
-       WLAN_EID_SECONDARY_CHANNEL_OFFSET = 62,
-
+       WLAN_EID_QOS_CAPA = 46,
+       /* 47 reserved for Broadcom */
        WLAN_EID_RSN = 48,
-       WLAN_EID_MMIE = 76,
-       WLAN_EID_VENDOR_SPECIFIC = 221,
-       WLAN_EID_QOS_PARAMETER = 222,
-
+       WLAN_EID_802_15_COEX = 49,
+       WLAN_EID_EXT_SUPP_RATES = 50,
        WLAN_EID_AP_CHAN_REPORT = 51,
        WLAN_EID_NEIGHBOR_REPORT = 52,
        WLAN_EID_RCPI = 53,
+       WLAN_EID_MOBILITY_DOMAIN = 54,
+       WLAN_EID_FAST_BSS_TRANSITION = 55,
+       WLAN_EID_TIMEOUT_INTERVAL = 56,
+       WLAN_EID_RIC_DATA = 57,
+       WLAN_EID_DSE_REGISTERED_LOCATION = 58,
+       WLAN_EID_SUPPORTED_REGULATORY_CLASSES = 59,
+       WLAN_EID_EXT_CHANSWITCH_ANN = 60,
+       WLAN_EID_HT_OPERATION = 61,
+       WLAN_EID_SECONDARY_CHANNEL_OFFSET = 62,
        WLAN_EID_BSS_AVG_ACCESS_DELAY = 63,
        WLAN_EID_ANTENNA_INFO = 64,
        WLAN_EID_RSNI = 65,
        WLAN_EID_MEASUREMENT_PILOT_TX_INFO = 66,
        WLAN_EID_BSS_AVAILABLE_CAPACITY = 67,
        WLAN_EID_BSS_AC_ACCESS_DELAY = 68,
+       WLAN_EID_TIME_ADVERTISEMENT = 69,
        WLAN_EID_RRM_ENABLED_CAPABILITIES = 70,
        WLAN_EID_MULTIPLE_BSSID = 71,
        WLAN_EID_BSS_COEX_2040 = 72,
        WLAN_EID_OVERLAP_BSS_SCAN_PARAM = 74,
-       WLAN_EID_EXT_CAPABILITY = 127,
-
-       WLAN_EID_MOBILITY_DOMAIN = 54,
-       WLAN_EID_FAST_BSS_TRANSITION = 55,
-       WLAN_EID_TIMEOUT_INTERVAL = 56,
-       WLAN_EID_RIC_DATA = 57,
        WLAN_EID_RIC_DESCRIPTOR = 75,
-
-       WLAN_EID_DSE_REGISTERED_LOCATION = 58,
-       WLAN_EID_SUPPORTED_REGULATORY_CLASSES = 59,
-       WLAN_EID_EXT_CHANSWITCH_ANN = 60,
-
-       WLAN_EID_VHT_CAPABILITY = 191,
-       WLAN_EID_VHT_OPERATION = 192,
-       WLAN_EID_OPMODE_NOTIF = 199,
-       WLAN_EID_WIDE_BW_CHANNEL_SWITCH = 194,
-       WLAN_EID_CHANNEL_SWITCH_WRAPPER = 196,
-       WLAN_EID_EXTENDED_BSS_LOAD = 193,
-       WLAN_EID_VHT_TX_POWER_ENVELOPE = 195,
-       WLAN_EID_AID = 197,
-       WLAN_EID_QUIET_CHANNEL = 198,
-
-       /* 802.11ad */
+       WLAN_EID_MMIE = 76,
+       WLAN_EID_ASSOC_COMEBACK_TIME = 77,
+       WLAN_EID_EVENT_REQUEST = 78,
+       WLAN_EID_EVENT_REPORT = 79,
+       WLAN_EID_DIAGNOSTIC_REQUEST = 80,
+       WLAN_EID_DIAGNOSTIC_REPORT = 81,
+       WLAN_EID_LOCATION_PARAMS = 82,
        WLAN_EID_NON_TX_BSSID_CAP =  83,
+       WLAN_EID_SSID_LIST = 84,
+       WLAN_EID_MULTI_BSSID_IDX = 85,
+       WLAN_EID_FMS_DESCRIPTOR = 86,
+       WLAN_EID_FMS_REQUEST = 87,
+       WLAN_EID_FMS_RESPONSE = 88,
+       WLAN_EID_QOS_TRAFFIC_CAPA = 89,
+       WLAN_EID_BSS_MAX_IDLE_PERIOD = 90,
+       WLAN_EID_TSF_REQUEST = 91,
+       WLAN_EID_TSF_RESPOSNE = 92,
+       WLAN_EID_WNM_SLEEP_MODE = 93,
+       WLAN_EID_TIM_BCAST_REQ = 94,
+       WLAN_EID_TIM_BCAST_RESP = 95,
+       WLAN_EID_COLL_IF_REPORT = 96,
+       WLAN_EID_CHANNEL_USAGE = 97,
+       WLAN_EID_TIME_ZONE = 98,
+       WLAN_EID_DMS_REQUEST = 99,
+       WLAN_EID_DMS_RESPONSE = 100,
+       WLAN_EID_LINK_ID = 101,
+       WLAN_EID_WAKEUP_SCHEDUL = 102,
+       /* 103 reserved */
+       WLAN_EID_CHAN_SWITCH_TIMING = 104,
+       WLAN_EID_PTI_CONTROL = 105,
+       WLAN_EID_PU_BUFFER_STATUS = 106,
+       WLAN_EID_INTERWORKING = 107,
+       WLAN_EID_ADVERTISEMENT_PROTOCOL = 108,
+       WLAN_EID_EXPEDITED_BW_REQ = 109,
+       WLAN_EID_QOS_MAP_SET = 110,
+       WLAN_EID_ROAMING_CONSORTIUM = 111,
+       WLAN_EID_EMERGENCY_ALERT = 112,
+       WLAN_EID_MESH_CONFIG = 113,
+       WLAN_EID_MESH_ID = 114,
+       WLAN_EID_LINK_METRIC_REPORT = 115,
+       WLAN_EID_CONGESTION_NOTIFICATION = 116,
+       WLAN_EID_PEER_MGMT = 117,
+       WLAN_EID_CHAN_SWITCH_PARAM = 118,
+       WLAN_EID_MESH_AWAKE_WINDOW = 119,
+       WLAN_EID_BEACON_TIMING = 120,
+       WLAN_EID_MCCAOP_SETUP_REQ = 121,
+       WLAN_EID_MCCAOP_SETUP_RESP = 122,
+       WLAN_EID_MCCAOP_ADVERT = 123,
+       WLAN_EID_MCCAOP_TEARDOWN = 124,
+       WLAN_EID_GANN = 125,
+       WLAN_EID_RANN = 126,
+       WLAN_EID_EXT_CAPABILITY = 127,
+       /* 128, 129 reserved for Agere */
+       WLAN_EID_PREQ = 130,
+       WLAN_EID_PREP = 131,
+       WLAN_EID_PERR = 132,
+       /* 133-136 reserved for Cisco */
+       WLAN_EID_PXU = 137,
+       WLAN_EID_PXUC = 138,
+       WLAN_EID_AUTH_MESH_PEER_EXCH = 139,
+       WLAN_EID_MIC = 140,
+       WLAN_EID_DESTINATION_URI = 141,
+       WLAN_EID_UAPSD_COEX = 142,
        WLAN_EID_WAKEUP_SCHEDULE = 143,
        WLAN_EID_EXT_SCHEDULE = 144,
        WLAN_EID_STA_AVAILABILITY = 145,
        WLAN_EID_DMG_TSPEC = 146,
        WLAN_EID_DMG_AT = 147,
        WLAN_EID_DMG_CAP = 148,
+       /* 149-150 reserved for Cisco */
        WLAN_EID_DMG_OPERATION = 151,
        WLAN_EID_DMG_BSS_PARAM_CHANGE = 152,
        WLAN_EID_DMG_BEAM_REFINEMENT = 153,
        WLAN_EID_CHANNEL_MEASURE_FEEDBACK = 154,
+       /* 155-156 reserved for Cisco */
        WLAN_EID_AWAKE_WINDOW = 157,
        WLAN_EID_MULTI_BAND = 158,
        WLAN_EID_ADDBA_EXT = 159,
@@ -1767,11 +1800,34 @@ enum ieee80211_eid {
        WLAN_EID_MULTIPLE_MAC_ADDR = 170,
        WLAN_EID_U_PID = 171,
        WLAN_EID_DMG_LINK_ADAPT_ACK = 172,
+       /* 173 reserved for Symbol */
+       WLAN_EID_MCCAOP_ADV_OVERVIEW = 174,
        WLAN_EID_QUIET_PERIOD_REQ = 175,
+       /* 176 reserved for Symbol */
        WLAN_EID_QUIET_PERIOD_RESP = 177,
+       /* 178-179 reserved for Symbol */
+       /* 180 reserved for ISO/IEC 20011 */
        WLAN_EID_EPAC_POLICY = 182,
        WLAN_EID_CLISTER_TIME_OFF = 183,
+       WLAN_EID_INTER_AC_PRIO = 184,
+       WLAN_EID_SCS_DESCRIPTOR = 185,
+       WLAN_EID_QLOAD_REPORT = 186,
+       WLAN_EID_HCCA_TXOP_UPDATE_COUNT = 187,
+       WLAN_EID_HL_STREAM_ID = 188,
+       WLAN_EID_GCR_GROUP_ADDR = 189,
        WLAN_EID_ANTENNA_SECTOR_ID_PATTERN = 190,
+       WLAN_EID_VHT_CAPABILITY = 191,
+       WLAN_EID_VHT_OPERATION = 192,
+       WLAN_EID_EXTENDED_BSS_LOAD = 193,
+       WLAN_EID_WIDE_BW_CHANNEL_SWITCH = 194,
+       WLAN_EID_VHT_TX_POWER_ENVELOPE = 195,
+       WLAN_EID_CHANNEL_SWITCH_WRAPPER = 196,
+       WLAN_EID_AID = 197,
+       WLAN_EID_QUIET_CHANNEL = 198,
+       WLAN_EID_OPMODE_NOTIF = 199,
+
+       WLAN_EID_VENDOR_SPECIFIC = 221,
+       WLAN_EID_QOS_PARAMETER = 222,
 };
 
 /* Action category code */
@@ -2192,10 +2248,10 @@ static inline u8 *ieee80211_get_DA(struct ieee80211_hdr *hdr)
 }
 
 /**
- * ieee80211_is_robust_mgmt_frame - check if frame is a robust management frame
+ * _ieee80211_is_robust_mgmt_frame - check if frame is a robust management frame
  * @hdr: the frame (buffer must include at least the first octet of payload)
  */
-static inline bool ieee80211_is_robust_mgmt_frame(struct ieee80211_hdr *hdr)
+static inline bool _ieee80211_is_robust_mgmt_frame(struct ieee80211_hdr *hdr)
 {
        if (ieee80211_is_disassoc(hdr->frame_control) ||
            ieee80211_is_deauth(hdr->frame_control))
@@ -2223,6 +2279,17 @@ static inline bool ieee80211_is_robust_mgmt_frame(struct ieee80211_hdr *hdr)
        return false;
 }
 
+/**
+ * ieee80211_is_robust_mgmt_frame - check if skb contains a robust mgmt frame
+ * @skb: the skb containing the frame, length will be checked
+ */
+static inline bool ieee80211_is_robust_mgmt_frame(struct sk_buff *skb)
+{
+       if (skb->len < 25)
+               return false;
+       return _ieee80211_is_robust_mgmt_frame((void *)skb->data);
+}
+
 /**
  * ieee80211_is_public_action - check if frame is a public action frame
  * @hdr: the frame
index b1f84b0..9f90554 100644 (file)
@@ -1394,10 +1394,12 @@ struct cfg80211_scan_request {
 /**
  * struct cfg80211_match_set - sets of attributes to match
  *
- * @ssid: SSID to be matched
+ * @ssid: SSID to be matched; may be zero-length for no match (RSSI only)
+ * @rssi_thold: don't report scan results below this threshold (in s32 dBm)
  */
 struct cfg80211_match_set {
        struct cfg80211_ssid ssid;
+       s32 rssi_thold;
 };
 
 /**
@@ -1420,7 +1422,8 @@ struct cfg80211_match_set {
  * @dev: the interface
  * @scan_start: start time of the scheduled scan
  * @channels: channels to scan
- * @rssi_thold: don't report scan results below this threshold (in s32 dBm)
+ * @min_rssi_thold: for drivers only supporting a single threshold, this
+ *     contains the minimum over all matchsets
  */
 struct cfg80211_sched_scan_request {
        struct cfg80211_ssid *ssids;
@@ -1433,7 +1436,7 @@ struct cfg80211_sched_scan_request {
        u32 flags;
        struct cfg80211_match_set *match_sets;
        int n_match_sets;
-       s32 rssi_thold;
+       s32 min_rssi_thold;
 
        /* internal */
        struct wiphy *wiphy;
@@ -1701,8 +1704,14 @@ struct cfg80211_ibss_params {
  *
  * @channel: The channel to use or %NULL if not specified (auto-select based
  *     on scan results)
+ * @channel_hint: The channel of the recommended BSS for initial connection or
+ *     %NULL if not specified
  * @bssid: The AP BSSID or %NULL if not specified (auto-select based on scan
  *     results)
+ * @bssid_hint: The recommended AP BSSID for initial connection to the BSS or
+ *     %NULL if not specified. Unlike the @bssid parameter, the driver is
+ *     allowed to ignore this @bssid_hint if it has knowledge of a better BSS
+ *     to use.
  * @ssid: SSID
  * @ssid_len: Length of ssid in octets
  * @auth_type: Authentication type (algorithm)
@@ -1725,11 +1734,13 @@ struct cfg80211_ibss_params {
  */
 struct cfg80211_connect_params {
        struct ieee80211_channel *channel;
-       u8 *bssid;
-       u8 *ssid;
+       struct ieee80211_channel *channel_hint;
+       const u8 *bssid;
+       const u8 *bssid_hint;
+       const u8 *ssid;
        size_t ssid_len;
        enum nl80211_auth_type auth_type;
-       u8 *ie;
+       const u8 *ie;
        size_t ie_len;
        bool privacy;
        enum nl80211_mfp mfp;
@@ -1768,6 +1779,7 @@ struct cfg80211_bitrate_mask {
                u32 legacy;
                u8 ht_mcs[IEEE80211_HT_MCS_MASK_LEN];
                u16 vht_mcs[NL80211_VHT_NSS_MAX];
+               enum nl80211_txrate_gi gi;
        } control[IEEE80211_NUM_BANDS];
 };
 /**
@@ -2875,6 +2887,11 @@ struct wiphy_vendor_command {
  * @n_vendor_commands: number of vendor commands
  * @vendor_events: array of vendor events supported by the hardware
  * @n_vendor_events: number of vendor events
+ *
+ * @max_ap_assoc_sta: maximum number of associated stations supported in AP mode
+ *     (including P2P GO) or 0 to indicate no such limit is advertised. The
+ *     driver is allowed to advertise a theoretical limit that it can reach in
+ *     some cases, but may not always reach.
  */
 struct wiphy {
        /* assign these fields before you register the wiphy */
@@ -2990,6 +3007,8 @@ struct wiphy {
        const struct nl80211_vendor_cmd_info *vendor_events;
        int n_vendor_commands, n_vendor_events;
 
+       u16 max_ap_assoc_sta;
+
        char priv[0] __aligned(NETDEV_ALIGN);
 };
 
@@ -3127,8 +3146,8 @@ struct cfg80211_cached_keys;
  * @identifier: (private) Identifier used in nl80211 to identify this
  *     wireless device if it has no netdev
  * @current_bss: (private) Used by the internal configuration code
- * @channel: (private) Used by the internal configuration code to track
- *     the user-set AP, monitor and WDS channel
+ * @chandef: (private) Used by the internal configuration code to track
+ *     the user-set channel definition.
  * @preset_chandef: (private) Used by the internal configuration code to
  *     track the channel to be used for AP later
  * @bssid: (private) Used by the internal configuration code
@@ -3192,9 +3211,7 @@ struct wireless_dev {
 
        struct cfg80211_internal_bss *current_bss; /* associated / joined */
        struct cfg80211_chan_def preset_chandef;
-
-       /* for AP and mesh channel tracking */
-       struct ieee80211_channel *channel;
+       struct cfg80211_chan_def chandef;
 
        bool ibss_fixed;
        bool ibss_dfs_possible;
@@ -3876,6 +3893,7 @@ void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr,
  *
  * @dev: network device
  * @bssid: the BSSID of the IBSS joined
+ * @channel: the channel of the IBSS joined
  * @gfp: allocation flags
  *
  * This function notifies cfg80211 that the device joined an IBSS or
@@ -3885,7 +3903,8 @@ void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr,
  * with the locally generated beacon -- this guarantees that there is
  * always a scan result for this IBSS. cfg80211 will handle the rest.
  */
-void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp);
+void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,
+                         struct ieee80211_channel *channel, gfp_t gfp);
 
 /**
  * cfg80211_notify_new_candidate - notify cfg80211 of a new mesh peer candidate
index 8b5b714..b0fd947 100644 (file)
@@ -316,6 +316,10 @@ enum ieee80211_radiotap_type {
 #define IEEE80211_RADIOTAP_VHT_FLAG_LDPC_EXTRA_OFDM_SYM                0x10
 #define IEEE80211_RADIOTAP_VHT_FLAG_BEAMFORMED                 0x20
 
+#define IEEE80211_RADIOTAP_CODING_LDPC_USER0                   0x01
+#define IEEE80211_RADIOTAP_CODING_LDPC_USER1                   0x02
+#define IEEE80211_RADIOTAP_CODING_LDPC_USER2                   0x04
+#define IEEE80211_RADIOTAP_CODING_LDPC_USER3                   0x08
 
 /* helpers */
 static inline int ieee80211_get_radiotap_len(unsigned char *data)
index f4ab2fb..4f0f29d 100644 (file)
@@ -808,9 +808,6 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info)
  * @RX_FLAG_HT: HT MCS was used and rate_idx is MCS index
  * @RX_FLAG_VHT: VHT MCS was used and rate_index is MCS index
  * @RX_FLAG_40MHZ: HT40 (40 MHz) was used
- * @RX_FLAG_80MHZ: 80 MHz was used
- * @RX_FLAG_80P80MHZ: 80+80 MHz was used
- * @RX_FLAG_160MHZ: 160 MHz was used
  * @RX_FLAG_SHORT_GI: Short guard interval was used
  * @RX_FLAG_NO_SIGNAL_VAL: The signal strength value is not present.
  *     Valid only for data frames (mainly A-MPDU)
@@ -830,6 +827,7 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info)
  *     on this subframe
  * @RX_FLAG_AMPDU_DELIM_CRC_KNOWN: The delimiter CRC field is known (the CRC
  *     is stored in the @ampdu_delimiter_crc field)
+ * @RX_FLAG_LDPC: LDPC was used
  * @RX_FLAG_STBC_MASK: STBC 2 bit bitmask. 1 - Nss=1, 2 - Nss=2, 3 - Nss=3
  * @RX_FLAG_10MHZ: 10 MHz (half channel) was used
  * @RX_FLAG_5MHZ: 5 MHz (quarter channel) was used
@@ -866,9 +864,7 @@ enum mac80211_rx_flags {
        RX_FLAG_AMPDU_DELIM_CRC_KNOWN   = BIT(20),
        RX_FLAG_MACTIME_END             = BIT(21),
        RX_FLAG_VHT                     = BIT(22),
-       RX_FLAG_80MHZ                   = BIT(23),
-       RX_FLAG_80P80MHZ                = BIT(24),
-       RX_FLAG_160MHZ                  = BIT(25),
+       RX_FLAG_LDPC                    = BIT(23),
        RX_FLAG_STBC_MASK               = BIT(26) | BIT(27),
        RX_FLAG_10MHZ                   = BIT(28),
        RX_FLAG_5MHZ                    = BIT(29),
@@ -877,6 +873,21 @@ enum mac80211_rx_flags {
 
 #define RX_FLAG_STBC_SHIFT             26
 
+/**
+ * enum mac80211_rx_vht_flags - receive VHT flags
+ *
+ * These flags are used with the @vht_flag member of
+ *     &struct ieee80211_rx_status.
+ * @RX_VHT_FLAG_80MHZ: 80 MHz was used
+ * @RX_VHT_FLAG_80P80MHZ: 80+80 MHz was used
+ * @RX_VHT_FLAG_160MHZ: 160 MHz was used
+ */
+enum mac80211_rx_vht_flags {
+       RX_VHT_FLAG_80MHZ               = BIT(0),
+       RX_VHT_FLAG_80P80MHZ            = BIT(1),
+       RX_VHT_FLAG_160MHZ              = BIT(2),
+};
+
 /**
  * struct ieee80211_rx_status - receive status
  *
@@ -902,26 +913,19 @@ enum mac80211_rx_flags {
  *     HT or VHT is used (%RX_FLAG_HT/%RX_FLAG_VHT)
  * @vht_nss: number of streams (VHT only)
  * @flag: %RX_FLAG_*
+ * @vht_flag: %RX_VHT_FLAG_*
  * @rx_flags: internal RX flags for mac80211
  * @ampdu_reference: A-MPDU reference number, must be a different value for
  *     each A-MPDU but the same for each subframe within one A-MPDU
  * @ampdu_delimiter_crc: A-MPDU delimiter CRC
- * @vendor_radiotap_bitmap: radiotap vendor namespace presence bitmap
- * @vendor_radiotap_len: radiotap vendor namespace length
- * @vendor_radiotap_align: radiotap vendor namespace alignment. Note
- *     that the actual data must be at the start of the SKB data
- *     already.
- * @vendor_radiotap_oui: radiotap vendor namespace OUI
- * @vendor_radiotap_subns: radiotap vendor sub namespace
  */
 struct ieee80211_rx_status {
        u64 mactime;
        u32 device_timestamp;
        u32 ampdu_reference;
        u32 flag;
-       u32 vendor_radiotap_bitmap;
-       u16 vendor_radiotap_len;
        u16 freq;
+       u8 vht_flag;
        u8 rate_idx;
        u8 vht_nss;
        u8 rx_flags;
@@ -931,9 +935,6 @@ struct ieee80211_rx_status {
        u8 chains;
        s8 chain_signal[IEEE80211_MAX_CHAINS];
        u8 ampdu_delimiter_crc;
-       u8 vendor_radiotap_align;
-       u8 vendor_radiotap_oui[3];
-       u8 vendor_radiotap_subns;
 };
 
 /**
@@ -2750,11 +2751,13 @@ enum ieee80211_roc_type {
  * @channel_switch_beacon: Starts a channel switch to a new channel.
  *     Beacons are modified to include CSA or ECSA IEs before calling this
  *     function. The corresponding count fields in these IEs must be
- *     decremented, and when they reach zero the driver must call
+ *     decremented, and when they reach 1 the driver must call
  *     ieee80211_csa_finish(). Drivers which use ieee80211_beacon_get()
  *     get the csa counter decremented by mac80211, but must check if it is
- *     zero using ieee80211_csa_is_complete() after the beacon has been
+ *     1 using ieee80211_csa_is_complete() after the beacon has been
  *     transmitted and then call ieee80211_csa_finish().
+ *     If the CSA count starts as zero or 1, this function will not be called,
+ *     since there won't be any time to beacon before the switch anyway.
  *
  * @join_ibss: Join an IBSS (on an IBSS interface); this is called after all
  *     information in bss_conf is set up and the beacon can be retrieved. A
@@ -3452,13 +3455,13 @@ static inline struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
  * @vif: &struct ieee80211_vif pointer from the add_interface callback.
  *
  * After a channel switch announcement was scheduled and the counter in this
- * announcement hit zero, this function must be called by the driver to
+ * announcement hits 1, this function must be called by the driver to
  * notify mac80211 that the channel can be changed.
  */
 void ieee80211_csa_finish(struct ieee80211_vif *vif);
 
 /**
- * ieee80211_csa_is_complete - find out if counters reached zero
+ * ieee80211_csa_is_complete - find out if counters reached 1
  * @vif: &struct ieee80211_vif pointer from the add_interface callback.
  *
  * This function returns whether the channel switch counters reached zero.
@@ -4451,7 +4454,6 @@ struct ieee80211_tx_rate_control {
 };
 
 struct rate_control_ops {
-       struct module *module;
        const char *name;
        void *(*alloc)(struct ieee80211_hw *hw, struct dentry *debugfsdir);
        void (*free)(void *priv);
@@ -4553,8 +4555,8 @@ int rate_control_set_rates(struct ieee80211_hw *hw,
                           struct ieee80211_sta *pubsta,
                           struct ieee80211_sta_rates *rates);
 
-int ieee80211_rate_control_register(struct rate_control_ops *ops);
-void ieee80211_rate_control_unregister(struct rate_control_ops *ops);
+int ieee80211_rate_control_register(const struct rate_control_ops *ops);
+void ieee80211_rate_control_unregister(const struct rate_control_ops *ops);
 
 static inline bool
 conf_is_ht20(struct ieee80211_conf *conf)
index 91054fd..a12e6ca 100644 (file)
  *     %NL80211_ATTR_SSID attribute, and can optionally specify the association
  *     IEs in %NL80211_ATTR_IE, %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_USE_MFP,
  *     %NL80211_ATTR_MAC, %NL80211_ATTR_WIPHY_FREQ, %NL80211_ATTR_CONTROL_PORT,
- *     %NL80211_ATTR_CONTROL_PORT_ETHERTYPE and
- *     %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT.
+ *     %NL80211_ATTR_CONTROL_PORT_ETHERTYPE,
+ *     %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT, %NL80211_ATTR_MAC_HINT, and
+ *     %NL80211_ATTR_WIPHY_FREQ_HINT.
+ *     If included, %NL80211_ATTR_MAC and %NL80211_ATTR_WIPHY_FREQ are
+ *     restrictions on BSS selection, i.e., they effectively prevent roaming
+ *     within the ESS. %NL80211_ATTR_MAC_HINT and %NL80211_ATTR_WIPHY_FREQ_HINT
+ *     can be included to provide a recommendation of the initial BSS while
+ *     allowing the driver to roam to other BSSes within the ESS and also to
+ *     ignore this recommendation if the indicated BSS is not ideal. Only one
+ *     set of BSSID,frequency parameters is used (i.e., either the enforcing
+ *     %NL80211_ATTR_MAC,%NL80211_ATTR_WIPHY_FREQ or the less strict
+ *     %NL80211_ATTR_MAC_HINT and %NL80211_ATTR_WIPHY_FREQ_HINT).
  *     Background scan period can optionally be
  *     specified in %NL80211_ATTR_BG_SCAN_PERIOD,
  *     if not specified default background scan configuration
@@ -1555,6 +1565,16 @@ enum nl80211_commands {
  *     data is in the format defined for the payload of the QoS Map Set element
  *     in IEEE Std 802.11-2012, 8.4.2.97.
  *
+ * @NL80211_ATTR_MAC_HINT: MAC address recommendation as initial BSS
+ * @NL80211_ATTR_WIPHY_FREQ_HINT: frequency of the recommended initial BSS
+ *
+ * @NL80211_ATTR_MAX_AP_ASSOC_STA: Device attribute that indicates how many
+ *     associated stations are supported in AP mode (including P2P GO); u32.
+ *     Since drivers may not have a fixed limit on the maximum number (e.g.,
+ *     other concurrent operations may affect this), drivers are allowed to
+ *     advertise values that cannot always be met. In such cases, an attempt
+ *     to add a new station entry with @NL80211_CMD_NEW_STATION may fail.
+ *
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
  */
@@ -1883,6 +1903,11 @@ enum nl80211_attrs {
 
        NL80211_ATTR_QOS_MAP,
 
+       NL80211_ATTR_MAC_HINT,
+       NL80211_ATTR_WIPHY_FREQ_HINT,
+
+       NL80211_ATTR_MAX_AP_ASSOC_STA,
+
        /* add attributes here, update the policy in nl80211.c */
 
        __NL80211_ATTR_AFTER_LAST,
@@ -2412,7 +2437,10 @@ enum nl80211_reg_type {
  *     in KHz. This is not a center a frequency but an actual regulatory
  *     band edge.
  * @NL80211_ATTR_FREQ_RANGE_MAX_BW: maximum allowed bandwidth for this
- *     frequency range, in KHz.
+ *     frequency range, in KHz. If not present or 0, maximum available
+ *     bandwidth should be calculated base on contiguous rules and wider
+ *     channels will be allowed to cross multiple contiguous/overlapping
+ *     frequency ranges.
  * @NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN: the maximum allowed antenna gain
  *     for a given frequency range. The value is in mBi (100 * dBi).
  *     If you don't have one then don't send this.
@@ -2442,9 +2470,15 @@ enum nl80211_reg_rule_attr {
  * enum nl80211_sched_scan_match_attr - scheduled scan match attributes
  * @__NL80211_SCHED_SCAN_MATCH_ATTR_INVALID: attribute number 0 is reserved
  * @NL80211_SCHED_SCAN_MATCH_ATTR_SSID: SSID to be used for matching,
- * only report BSS with matching SSID.
+ *     only report BSS with matching SSID.
  * @NL80211_SCHED_SCAN_MATCH_ATTR_RSSI: RSSI threshold (in dBm) for reporting a
- *     BSS in scan results. Filtering is turned off if not specified.
+ *     BSS in scan results. Filtering is turned off if not specified. Note that
+ *     if this attribute is in a match set of its own, then it is treated as
+ *     the default value for all matchsets with an SSID, rather than being a
+ *     matchset of its own without an RSSI filter. This is due to problems with
+ *     how this API was implemented in the past. Also, due to the same problem,
+ *     the only way to create a matchset with only an RSSI filter (with this
+ *     attribute) is if there's only a single matchset with the RSSI attribute.
  * @NL80211_SCHED_SCAN_MATCH_ATTR_MAX: highest scheduled scan filter
  *     attribute number currently defined
  * @__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST: internal use
@@ -3131,6 +3165,7 @@ enum nl80211_key_attributes {
  *     in an array of MCS numbers.
  * @NL80211_TXRATE_VHT: VHT rates allowed for TX rate selection,
  *     see &struct nl80211_txrate_vht
+ * @NL80211_TXRATE_GI: configure GI, see &enum nl80211_txrate_gi
  * @__NL80211_TXRATE_AFTER_LAST: internal
  * @NL80211_TXRATE_MAX: highest TX rate attribute
  */
@@ -3139,6 +3174,7 @@ enum nl80211_tx_rate_attributes {
        NL80211_TXRATE_LEGACY,
        NL80211_TXRATE_HT,
        NL80211_TXRATE_VHT,
+       NL80211_TXRATE_GI,
 
        /* keep last */
        __NL80211_TXRATE_AFTER_LAST,
@@ -3156,6 +3192,12 @@ struct nl80211_txrate_vht {
        __u16 mcs[NL80211_VHT_NSS_MAX];
 };
 
+enum nl80211_txrate_gi {
+       NL80211_TXRATE_DEFAULT_GI,
+       NL80211_TXRATE_FORCE_SGI,
+       NL80211_TXRATE_FORCE_LGI,
+};
+
 /**
  * enum nl80211_band - Frequency band
  * @NL80211_BAND_2GHZ: 2.4 GHz ISM band
index 13b7683..ce9633a 100644 (file)
@@ -107,7 +107,7 @@ static void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata,
        mgmt->u.action.u.addba_req.start_seq_num =
                                        cpu_to_le16(start_seq_num << 4);
 
-       ieee80211_tx_skb_tid(sdata, skb, tid);
+       ieee80211_tx_skb(sdata, skb);
 }
 
 void ieee80211_send_bar(struct ieee80211_vif *vif, u8 *ra, u16 tid, u16 ssn)
index 453e974..363d19b 100644 (file)
@@ -451,11 +451,11 @@ void sta_set_rate_info_rx(struct sta_info *sta, struct rate_info *rinfo)
                rinfo->flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
        if (sta->last_rx_rate_flag & RX_FLAG_SHORT_GI)
                rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI;
-       if (sta->last_rx_rate_flag & RX_FLAG_80MHZ)
+       if (sta->last_rx_rate_vht_flag & RX_VHT_FLAG_80MHZ)
                rinfo->flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH;
-       if (sta->last_rx_rate_flag & RX_FLAG_80P80MHZ)
+       if (sta->last_rx_rate_vht_flag & RX_VHT_FLAG_80P80MHZ)
                rinfo->flags |= RATE_INFO_FLAGS_80P80_MHZ_WIDTH;
-       if (sta->last_rx_rate_flag & RX_FLAG_160MHZ)
+       if (sta->last_rx_rate_vht_flag & RX_VHT_FLAG_160MHZ)
                rinfo->flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH;
 }
 
@@ -970,9 +970,9 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
        /* TODO: make hostapd tell us what it wants */
        sdata->smps_mode = IEEE80211_SMPS_OFF;
        sdata->needed_rx_chains = sdata->local->rx_chains;
-       sdata->radar_required = params->radar_required;
 
        mutex_lock(&local->mtx);
+       sdata->radar_required = params->radar_required;
        err = ieee80211_vif_use_channel(sdata, &params->chandef,
                                        IEEE80211_CHANCTX_SHARED);
        mutex_unlock(&local->mtx);
@@ -1056,6 +1056,7 @@ static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev,
        int err;
 
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       sdata_assert_lock(sdata);
 
        /* don't allow changing the beacon while CSA is in place - offset
         * of channel switch counter may change
@@ -1083,6 +1084,8 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
        struct probe_resp *old_probe_resp;
        struct cfg80211_chan_def chandef;
 
+       sdata_assert_lock(sdata);
+
        old_beacon = sdata_dereference(sdata->u.ap.beacon, sdata);
        if (!old_beacon)
                return -ENOENT;
@@ -1343,6 +1346,18 @@ static int sta_apply_parameters(struct ieee80211_local *local,
                ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
                                                    params->vht_capa, sta);
 
+       if (params->opmode_notif_used) {
+               enum ieee80211_band band =
+                       ieee80211_get_sdata_band(sdata);
+
+               /* returned value is only needed for rc update, but the
+                * rc isn't initialized here yet, so ignore it
+                */
+               __ieee80211_vht_handle_opmode(sdata, sta,
+                                             params->opmode_notif,
+                                             band, false);
+       }
+
        if (ieee80211_vif_is_mesh(&sdata->vif)) {
 #ifdef CONFIG_MAC80211_MESH
                u32 changed = 0;
@@ -2630,6 +2645,18 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
        if (!roc)
                return -ENOMEM;
 
+       /*
+        * If the duration is zero, then the driver
+        * wouldn't actually do anything. Set it to
+        * 10 for now.
+        *
+        * TODO: cancel the off-channel operation
+        *       when we get the SKB's TX status and
+        *       the wait time was zero before.
+        */
+       if (!duration)
+               duration = 10;
+
        roc->chan = channel;
        roc->duration = duration;
        roc->req_duration = duration;
@@ -2671,18 +2698,6 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
 
        /* otherwise actually kick it off here (for error handling) */
 
-       /*
-        * If the duration is zero, then the driver
-        * wouldn't actually do anything. Set it to
-        * 10 for now.
-        *
-        * TODO: cancel the off-channel operation
-        *       when we get the SKB's TX status and
-        *       the wait time was zero before.
-        */
-       if (!duration)
-               duration = 10;
-
        ret = drv_remain_on_channel(local, sdata, channel, duration, type);
        if (ret) {
                kfree(roc);
@@ -2990,69 +3005,88 @@ cfg80211_beacon_dup(struct cfg80211_beacon_data *beacon)
        return new_beacon;
 }
 
-void ieee80211_csa_finalize_work(struct work_struct *work)
+void ieee80211_csa_finish(struct ieee80211_vif *vif)
+{
+       struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+
+       ieee80211_queue_work(&sdata->local->hw,
+                            &sdata->csa_finalize_work);
+}
+EXPORT_SYMBOL(ieee80211_csa_finish);
+
+static void ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata)
 {
-       struct ieee80211_sub_if_data *sdata =
-               container_of(work, struct ieee80211_sub_if_data,
-                            csa_finalize_work);
        struct ieee80211_local *local = sdata->local;
        int err, changed = 0;
 
-       sdata_lock(sdata);
-       /* AP might have been stopped while waiting for the lock. */
-       if (!sdata->vif.csa_active)
-               goto unlock;
-
-       if (!ieee80211_sdata_running(sdata))
-               goto unlock;
+       sdata_assert_lock(sdata);
 
-       sdata->radar_required = sdata->csa_radar_required;
        mutex_lock(&local->mtx);
+       sdata->radar_required = sdata->csa_radar_required;
        err = ieee80211_vif_change_channel(sdata, &changed);
        mutex_unlock(&local->mtx);
        if (WARN_ON(err < 0))
-               goto unlock;
+               return;
 
        if (!local->use_chanctx) {
                local->_oper_chandef = sdata->csa_chandef;
                ieee80211_hw_config(local, 0);
        }
 
-       ieee80211_bss_info_change_notify(sdata, changed);
-
        sdata->vif.csa_active = false;
        switch (sdata->vif.type) {
        case NL80211_IFTYPE_AP:
                err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon);
-               if (err < 0)
-                       goto unlock;
-
-               changed |= err;
                kfree(sdata->u.ap.next_beacon);
                sdata->u.ap.next_beacon = NULL;
 
-               ieee80211_bss_info_change_notify(sdata, err);
+               if (err < 0)
+                       return;
+               changed |= err;
                break;
        case NL80211_IFTYPE_ADHOC:
-               ieee80211_ibss_finish_csa(sdata);
+               err = ieee80211_ibss_finish_csa(sdata);
+               if (err < 0)
+                       return;
+               changed |= err;
                break;
 #ifdef CONFIG_MAC80211_MESH
        case NL80211_IFTYPE_MESH_POINT:
                err = ieee80211_mesh_finish_csa(sdata);
                if (err < 0)
-                       goto unlock;
+                       return;
+               changed |= err;
                break;
 #endif
        default:
                WARN_ON(1);
-               goto unlock;
+               return;
        }
 
+       ieee80211_bss_info_change_notify(sdata, changed);
+
        ieee80211_wake_queues_by_reason(&sdata->local->hw,
                                        IEEE80211_MAX_QUEUE_MAP,
                                        IEEE80211_QUEUE_STOP_REASON_CSA);
 
        cfg80211_ch_switch_notify(sdata->dev, &sdata->csa_chandef);
+}
+
+void ieee80211_csa_finalize_work(struct work_struct *work)
+{
+       struct ieee80211_sub_if_data *sdata =
+               container_of(work, struct ieee80211_sub_if_data,
+                            csa_finalize_work);
+
+       sdata_lock(sdata);
+       /* AP might have been stopped while waiting for the lock. */
+       if (!sdata->vif.csa_active)
+               goto unlock;
+
+       if (!ieee80211_sdata_running(sdata))
+               goto unlock;
+
+       ieee80211_csa_finalize(sdata);
 
 unlock:
        sdata_unlock(sdata);
@@ -3066,9 +3100,9 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
        struct ieee80211_chanctx_conf *chanctx_conf;
        struct ieee80211_chanctx *chanctx;
        struct ieee80211_if_mesh __maybe_unused *ifmsh;
-       int err, num_chanctx;
+       int err, num_chanctx, changed = 0;
 
-       lockdep_assert_held(&sdata->wdev.mtx);
+       sdata_assert_lock(sdata);
 
        if (!list_empty(&local->roc_list) || local->scanning)
                return -EBUSY;
@@ -3107,19 +3141,40 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
 
        switch (sdata->vif.type) {
        case NL80211_IFTYPE_AP:
-               sdata->csa_counter_offset_beacon =
-                       params->counter_offset_beacon;
-               sdata->csa_counter_offset_presp = params->counter_offset_presp;
                sdata->u.ap.next_beacon =
                        cfg80211_beacon_dup(&params->beacon_after);
                if (!sdata->u.ap.next_beacon)
                        return -ENOMEM;
 
+               /*
+                * With a count of 0, we don't have to wait for any
+                * TBTT before switching, so complete the CSA
+                * immediately.  In theory, with a count == 1 we
+                * should delay the switch until just before the next
+                * TBTT, but that would complicate things so we switch
+                * immediately too.  If we would delay the switch
+                * until the next TBTT, we would have to set the probe
+                * response here.
+                *
+                * TODO: A channel switch with count <= 1 without
+                * sending a CSA action frame is kind of useless,
+                * because the clients won't know we're changing
+                * channels.  The action frame must be implemented
+                * either here or in the userspace.
+                */
+               if (params->count <= 1)
+                       break;
+
+               sdata->csa_counter_offset_beacon =
+                       params->counter_offset_beacon;
+               sdata->csa_counter_offset_presp = params->counter_offset_presp;
                err = ieee80211_assign_beacon(sdata, &params->beacon_csa);
                if (err < 0) {
                        kfree(sdata->u.ap.next_beacon);
                        return err;
                }
+               changed |= err;
+
                break;
        case NL80211_IFTYPE_ADHOC:
                if (!sdata->vif.bss_conf.ibss_joined)
@@ -3147,17 +3202,21 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
                    params->chandef.chan->band)
                        return -EINVAL;
 
-               err = ieee80211_ibss_csa_beacon(sdata, params);
-               if (err < 0)
-                       return err;
+               /* see comments in the NL80211_IFTYPE_AP block */
+               if (params->count > 1) {
+                       err = ieee80211_ibss_csa_beacon(sdata, params);
+                       if (err < 0)
+                               return err;
+                       changed |= err;
+               }
+
+               ieee80211_send_action_csa(sdata, params);
+
                break;
 #ifdef CONFIG_MAC80211_MESH
        case NL80211_IFTYPE_MESH_POINT:
                ifmsh = &sdata->u.mesh;
 
-               if (!ifmsh->mesh_id)
-                       return -EINVAL;
-
                if (params->chandef.width != sdata->vif.bss_conf.chandef.width)
                        return -EINVAL;
 
@@ -3166,17 +3225,27 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
                    params->chandef.chan->band)
                        return -EINVAL;
 
-               ifmsh->chsw_init = true;
-               if (!ifmsh->pre_value)
-                       ifmsh->pre_value = 1;
-               else
-                       ifmsh->pre_value++;
+               if (ifmsh->csa_role == IEEE80211_MESH_CSA_ROLE_NONE) {
+                       ifmsh->csa_role = IEEE80211_MESH_CSA_ROLE_INIT;
+                       if (!ifmsh->pre_value)
+                               ifmsh->pre_value = 1;
+                       else
+                               ifmsh->pre_value++;
+               }
 
-               err = ieee80211_mesh_csa_beacon(sdata, params, true);
-               if (err < 0) {
-                       ifmsh->chsw_init = false;
-                       return err;
+               /* see comments in the NL80211_IFTYPE_AP block */
+               if (params->count > 1) {
+                       err = ieee80211_mesh_csa_beacon(sdata, params);
+                       if (err < 0) {
+                               ifmsh->csa_role = IEEE80211_MESH_CSA_ROLE_NONE;
+                               return err;
+                       }
+                       changed |= err;
                }
+
+               if (ifmsh->csa_role == IEEE80211_MESH_CSA_ROLE_INIT)
+                       ieee80211_send_action_csa(sdata, params);
+
                break;
 #endif
        default:
@@ -3193,8 +3262,13 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
        sdata->csa_chandef = params->chandef;
        sdata->vif.csa_active = true;
 
-       ieee80211_bss_info_change_notify(sdata, err);
-       drv_channel_switch_beacon(sdata, &params->chandef);
+       if (changed) {
+               ieee80211_bss_info_change_notify(sdata, changed);
+               drv_channel_switch_beacon(sdata, &params->chandef);
+       } else {
+               /* if the beacon didn't change, we can finalize immediately */
+               ieee80211_csa_finalize(sdata);
+       }
 
        return 0;
 }
@@ -3865,7 +3939,7 @@ static int ieee80211_set_qos_map(struct wiphy *wiphy,
        return 0;
 }
 
-struct cfg80211_ops mac80211_config_ops = {
+const struct cfg80211_ops mac80211_config_ops = {
        .add_virtual_intf = ieee80211_add_iface,
        .del_virtual_intf = ieee80211_del_iface,
        .change_virtual_intf = ieee80211_change_iface,
index 7d7879f..2d51f62 100644 (file)
@@ -4,6 +4,6 @@
 #ifndef __CFG_H
 #define __CFG_H
 
-extern struct cfg80211_ops mac80211_config_ops;
+extern const struct cfg80211_ops mac80211_config_ops;
 
 #endif /* __CFG_H */
index f43613a..42c6592 100644 (file)
@@ -196,6 +196,8 @@ static bool ieee80211_is_radar_required(struct ieee80211_local *local)
 {
        struct ieee80211_sub_if_data *sdata;
 
+       lockdep_assert_held(&local->mtx);
+
        rcu_read_lock();
        list_for_each_entry_rcu(sdata, &local->interfaces, list) {
                if (sdata->radar_required) {
index 80194b5..2ecb4de 100644 (file)
@@ -195,7 +195,7 @@ static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf,
 static ssize_t sta_agg_status_write(struct file *file, const char __user *userbuf,
                                    size_t count, loff_t *ppos)
 {
-       char _buf[12], *buf = _buf;
+       char _buf[12] = {}, *buf = _buf;
        struct sta_info *sta = file->private_data;
        bool start, tx;
        unsigned long tid;
index 70dd013..afbe2b2 100644 (file)
@@ -375,7 +375,7 @@ void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata,
        mgmt->u.action.u.delba.params = cpu_to_le16(params);
        mgmt->u.action.u.delba.reason_code = cpu_to_le16(reason_code);
 
-       ieee80211_tx_skb_tid(sdata, skb, tid);
+       ieee80211_tx_skb(sdata, skb);
 }
 
 void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata,
index 2796a19..4453e27 100644 (file)
@@ -220,7 +220,6 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
 {
        struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
        struct ieee80211_local *local = sdata->local;
-       struct ieee80211_supported_band *sband;
        struct ieee80211_mgmt *mgmt;
        struct cfg80211_bss *bss;
        u32 bss_change;
@@ -294,7 +293,6 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
        }
 
        mutex_lock(&local->mtx);
-       ieee80211_vif_release_channel(sdata);
        if (ieee80211_vif_use_channel(sdata, &chandef,
                                      ifibss->fixed_channel ?
                                        IEEE80211_CHANCTX_SHARED :
@@ -303,12 +301,11 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
                mutex_unlock(&local->mtx);
                return;
        }
+       sdata->radar_required = radar_required;
        mutex_unlock(&local->mtx);
 
        memcpy(ifibss->bssid, bssid, ETH_ALEN);
 
-       sband = local->hw.wiphy->bands[chan->band];
-
        presp = ieee80211_ibss_build_presp(sdata, beacon_int, basic_rates,
                                           capability, tsf, &chandef,
                                           &have_higher_than_11mbit, NULL);
@@ -318,7 +315,6 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
        rcu_assign_pointer(ifibss->presp, presp);
        mgmt = (void *)presp->head;
 
-       sdata->radar_required = radar_required;
        sdata->vif.bss_conf.enable_beacon = true;
        sdata->vif.bss_conf.beacon_int = beacon_int;
        sdata->vif.bss_conf.basic_rates = basic_rates;
@@ -386,7 +382,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
                                              presp->head_len, 0, GFP_KERNEL);
        cfg80211_put_bss(local->hw.wiphy, bss);
        netif_carrier_on(sdata->dev);
-       cfg80211_ibss_joined(sdata->dev, ifibss->bssid, GFP_KERNEL);
+       cfg80211_ibss_joined(sdata->dev, ifibss->bssid, chan, GFP_KERNEL);
 }
 
 static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
@@ -521,12 +517,6 @@ int ieee80211_ibss_csa_beacon(struct ieee80211_sub_if_data *sdata,
        if (old_presp)
                kfree_rcu(old_presp, rcu_head);
 
-       /* it might not send the beacon for a while. send an action frame
-        * immediately to announce the channel switch.
-        */
-       if (csa_settings)
-               ieee80211_send_action_csa(sdata, csa_settings);
-
        return BSS_CHANGED_BEACON;
  out:
        return ret;
@@ -536,7 +526,7 @@ int ieee80211_ibss_finish_csa(struct ieee80211_sub_if_data *sdata)
 {
        struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
        struct cfg80211_bss *cbss;
-       int err;
+       int err, changed = 0;
        u16 capability;
 
        sdata_assert_lock(sdata);
@@ -568,10 +558,9 @@ int ieee80211_ibss_finish_csa(struct ieee80211_sub_if_data *sdata)
        if (err < 0)
                return err;
 
-       if (err)
-               ieee80211_bss_info_change_notify(sdata, err);
+       changed |= err;
 
-       return 0;
+       return changed;
 }
 
 void ieee80211_ibss_stop(struct ieee80211_sub_if_data *sdata)
@@ -799,6 +788,8 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
        int err;
        u32 sta_flags;
 
+       sdata_assert_lock(sdata);
+
        sta_flags = IEEE80211_STA_DISABLE_VHT;
        switch (ifibss->chandef.width) {
        case NL80211_CHAN_WIDTH_5:
@@ -1468,6 +1459,11 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata,
        memcpy(((struct ieee80211_mgmt *) skb->data)->da, mgmt->sa, ETH_ALEN);
        ibss_dbg(sdata, "Sending ProbeResp to %pM\n", mgmt->sa);
        IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
+
+       /* avoid excessive retries for probe request to wildcard SSIDs */
+       if (pos[1] == 0)
+               IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_NO_ACK;
+
        ieee80211_tx_skb(sdata, skb);
 }
 
index 3701930..0014b53 100644 (file)
@@ -616,7 +616,11 @@ struct ieee80211_if_mesh {
        struct ps_data ps;
        /* Channel Switching Support */
        struct mesh_csa_settings __rcu *csa;
-       bool chsw_init;
+       enum {
+               IEEE80211_MESH_CSA_ROLE_NONE,
+               IEEE80211_MESH_CSA_ROLE_INIT,
+               IEEE80211_MESH_CSA_ROLE_REPEATER,
+       } csa_role;
        u8 chsw_ttl;
        u16 pre_value;
 
@@ -1408,8 +1412,7 @@ void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata);
 void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
                                   struct sk_buff *skb);
 int ieee80211_mesh_csa_beacon(struct ieee80211_sub_if_data *sdata,
-                             struct cfg80211_csa_settings *csa_settings,
-                             bool csa_action);
+                             struct cfg80211_csa_settings *csa_settings);
 int ieee80211_mesh_finish_csa(struct ieee80211_sub_if_data *sdata);
 
 /* scan/BSS handling */
@@ -1553,6 +1556,9 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
                                    struct sta_info *sta);
 enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta);
 void ieee80211_sta_set_rx_nss(struct sta_info *sta);
+u32 __ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
+                                  struct sta_info *sta, u8 opmode,
+                                  enum ieee80211_band band, bool nss_only);
 void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
                                 struct sta_info *sta, u8 opmode,
                                 enum ieee80211_band band, bool nss_only);
@@ -1605,7 +1611,7 @@ static inline int __ieee80211_resume(struct ieee80211_hw *hw)
 }
 
 /* utility functions/constants */
-extern void *mac80211_wiphy_privid; /* for wiphy privid */
+extern const void *const mac80211_wiphy_privid; /* for wiphy privid */
 u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len,
                        enum nl80211_iftype type);
 int ieee80211_frame_duration(enum ieee80211_band band, size_t len,
index ce1c443..088111a 100644 (file)
@@ -833,7 +833,9 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
        cancel_work_sync(&local->dynamic_ps_enable_work);
 
        cancel_work_sync(&sdata->recalc_smps);
+       sdata_lock(sdata);
        sdata->vif.csa_active = false;
+       sdata_unlock(sdata);
        cancel_work_sync(&sdata->csa_finalize_work);
 
        cancel_delayed_work_sync(&sdata->dfs_cac_timer_work);
index d767cfb..1f7d842 100644 (file)
@@ -893,10 +893,15 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
        /* mac80211 supports control port protocol changing */
        local->hw.wiphy->flags |= WIPHY_FLAG_CONTROL_PORT_PROTOCOL;
 
-       if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM)
+       if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM) {
                local->hw.wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
-       else if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC)
+       } else if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC) {
                local->hw.wiphy->signal_type = CFG80211_SIGNAL_TYPE_UNSPEC;
+               if (hw->max_signal <= 0) {
+                       result = -EINVAL;
+                       goto fail_wiphy_register;
+               }
+       }
 
        WARN((local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)
             && (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK),
index 5b919ca..f70e9cd 100644 (file)
@@ -688,7 +688,7 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
                *pos++ = csa->settings.count;
                *pos++ = WLAN_EID_CHAN_SWITCH_PARAM;
                *pos++ = 6;
-               if (ifmsh->chsw_init) {
+               if (ifmsh->csa_role == IEEE80211_MESH_CSA_ROLE_INIT) {
                        *pos++ = ifmsh->mshcfg.dot11MeshTTL;
                        *pos |= WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR;
                } else {
@@ -859,18 +859,12 @@ ieee80211_mesh_process_chnswitch(struct ieee80211_sub_if_data *sdata,
 {
        struct cfg80211_csa_settings params;
        struct ieee80211_csa_ie csa_ie;
-       struct ieee80211_chanctx_conf *chanctx_conf;
-       struct ieee80211_chanctx *chanctx;
        struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
        enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
-       int err, num_chanctx;
+       int err;
        u32 sta_flags;
 
-       if (sdata->vif.csa_active)
-               return true;
-
-       if (!ifmsh->mesh_id)
-               return false;
+       sdata_assert_lock(sdata);
 
        sta_flags = IEEE80211_STA_DISABLE_VHT;
        switch (sdata->vif.bss_conf.chandef.width) {
@@ -896,10 +890,6 @@ ieee80211_mesh_process_chnswitch(struct ieee80211_sub_if_data *sdata,
        params.chandef = csa_ie.chandef;
        params.count = csa_ie.count;
 
-       if (sdata->vif.bss_conf.chandef.chan->band !=
-           params.chandef.chan->band)
-               return false;
-
        if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, &params.chandef,
                                     IEEE80211_CHAN_DISABLED)) {
                sdata_info(sdata,
@@ -922,24 +912,12 @@ ieee80211_mesh_process_chnswitch(struct ieee80211_sub_if_data *sdata,
                return false;
        }
 
-       rcu_read_lock();
-       chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
-       if (!chanctx_conf)
-               goto failed_chswitch;
-
-       /* don't handle for multi-VIF cases */
-       chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf);
-       if (chanctx->refcount > 1)
-               goto failed_chswitch;
-
-       num_chanctx = 0;
-       list_for_each_entry_rcu(chanctx, &sdata->local->chanctx_list, list)
-               num_chanctx++;
-
-       if (num_chanctx > 1)
-               goto failed_chswitch;
-
-       rcu_read_unlock();
+       if (cfg80211_chandef_identical(&params.chandef,
+                                      &sdata->vif.bss_conf.chandef)) {
+               mcsa_dbg(sdata,
+                        "received csa with an identical chandef, ignoring\n");
+               return true;
+       }
 
        mcsa_dbg(sdata,
                 "received channel switch announcement to go to channel %d MHz\n",
@@ -953,30 +931,16 @@ ieee80211_mesh_process_chnswitch(struct ieee80211_sub_if_data *sdata,
                ifmsh->pre_value = csa_ie.pre_value;
        }
 
-       if (ifmsh->chsw_ttl < ifmsh->mshcfg.dot11MeshTTL) {
-               if (ieee80211_mesh_csa_beacon(sdata, &params, false) < 0)
-                       return false;
-       } else {
+       if (ifmsh->chsw_ttl >= ifmsh->mshcfg.dot11MeshTTL)
                return false;
-       }
 
-       sdata->csa_radar_required = params.radar_required;
+       ifmsh->csa_role = IEEE80211_MESH_CSA_ROLE_REPEATER;
 
-       if (params.block_tx)
-               ieee80211_stop_queues_by_reason(&sdata->local->hw,
-                               IEEE80211_MAX_QUEUE_MAP,
-                               IEEE80211_QUEUE_STOP_REASON_CSA);
-
-       sdata->csa_chandef = params.chandef;
-       sdata->vif.csa_active = true;
-
-       ieee80211_bss_info_change_notify(sdata, err);
-       drv_channel_switch_beacon(sdata, &params.chandef);
+       if (ieee80211_channel_switch(sdata->local->hw.wiphy, sdata->dev,
+                                    &params) < 0)
+               return false;
 
        return true;
-failed_chswitch:
-       rcu_read_unlock();
-       return false;
 }
 
 static void
@@ -1086,7 +1050,8 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
                ifmsh->sync_ops->rx_bcn_presp(sdata,
                        stype, mgmt, &elems, rx_status);
 
-       if (!ifmsh->chsw_init)
+       if (ifmsh->csa_role != IEEE80211_MESH_CSA_ROLE_INIT &&
+           !sdata->vif.csa_active)
                ieee80211_mesh_process_chnswitch(sdata, &elems, true);
 }
 
@@ -1095,29 +1060,30 @@ int ieee80211_mesh_finish_csa(struct ieee80211_sub_if_data *sdata)
        struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
        struct mesh_csa_settings *tmp_csa_settings;
        int ret = 0;
+       int changed = 0;
 
        /* Reset the TTL value and Initiator flag */
-       ifmsh->chsw_init = false;
+       ifmsh->csa_role = IEEE80211_MESH_CSA_ROLE_NONE;
        ifmsh->chsw_ttl = 0;
 
        /* Remove the CSA and MCSP elements from the beacon */
        tmp_csa_settings = rcu_dereference(ifmsh->csa);
        rcu_assign_pointer(ifmsh->csa, NULL);
-       kfree_rcu(tmp_csa_settings, rcu_head);
+       if (tmp_csa_settings)
+               kfree_rcu(tmp_csa_settings, rcu_head);
        ret = ieee80211_mesh_rebuild_beacon(sdata);
        if (ret)
                return -EINVAL;
 
-       ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON);
+       changed |= BSS_CHANGED_BEACON;
 
        mcsa_dbg(sdata, "complete switching to center freq %d MHz",
                 sdata->vif.bss_conf.chandef.chan->center_freq);
-       return 0;
+       return changed;
 }
 
 int ieee80211_mesh_csa_beacon(struct ieee80211_sub_if_data *sdata,
-                             struct cfg80211_csa_settings *csa_settings,
-                             bool csa_action)
+                             struct cfg80211_csa_settings *csa_settings)
 {
        struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
        struct mesh_csa_settings *tmp_csa_settings;
@@ -1141,12 +1107,7 @@ int ieee80211_mesh_csa_beacon(struct ieee80211_sub_if_data *sdata,
                return ret;
        }
 
-       ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON);
-
-       if (csa_action)
-               ieee80211_send_action_csa(sdata, csa_settings);
-
-       return 0;
+       return BSS_CHANGED_BEACON;
 }
 
 static int mesh_fwd_csa_frame(struct ieee80211_sub_if_data *sdata,
@@ -1210,7 +1171,8 @@ static void mesh_rx_csa_frame(struct ieee80211_sub_if_data *sdata,
 
        ifmsh->pre_value = pre_value;
 
-       if (!ieee80211_mesh_process_chnswitch(sdata, &elems, false)) {
+       if (!sdata->vif.csa_active &&
+           !ieee80211_mesh_process_chnswitch(sdata, &elems, false)) {
                mcsa_dbg(sdata, "Failed to process CSA action frame");
                return;
        }
@@ -1257,7 +1219,7 @@ void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
        sdata_lock(sdata);
 
        /* mesh already went down */
-       if (!sdata->wdev.mesh_id_len)
+       if (!sdata->u.mesh.mesh_id_len)
                goto out;
 
        rx_status = IEEE80211_SKB_RXCB(skb);
@@ -1310,7 +1272,7 @@ void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata)
        sdata_lock(sdata);
 
        /* mesh already went down */
-       if (!sdata->wdev.mesh_id_len)
+       if (!sdata->u.mesh.mesh_id_len)
                goto out;
 
        if (ifmsh->preq_queue_len &&
@@ -1365,7 +1327,7 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata)
        mesh_rmc_init(sdata);
        ifmsh->last_preq = jiffies;
        ifmsh->next_perr = jiffies;
-       ifmsh->chsw_init = false;
+       ifmsh->csa_role = IEEE80211_MESH_CSA_ROLE_NONE;
        /* Allocate all mesh structures when creating the first mesh interface. */
        if (!mesh_allocated)
                ieee80211s_init();
index fc1d824..6160483 100644 (file)
@@ -508,6 +508,7 @@ static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata,
        u8 *pos;
        u32 cap;
        struct ieee80211_sta_vht_cap vht_cap;
+       u32 mask, ap_bf_sts, our_bf_sts;
 
        BUILD_BUG_ON(sizeof(vht_cap) != sizeof(sband->vht_cap));
 
@@ -535,6 +536,16 @@ static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata,
                        cpu_to_le32(IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE)))
                cap &= ~IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
 
+       mask = IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK;
+
+       ap_bf_sts = le32_to_cpu(ap_vht_cap->vht_cap_info) & mask;
+       our_bf_sts = cap & mask;
+
+       if (ap_bf_sts < our_bf_sts) {
+               cap &= ~mask;
+               cap |= ap_bf_sts;
+       }
+
        /* reserve and fill IE */
        pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2);
        ieee80211_ie_build_vht_cap(pos, &vht_cap, cap);
@@ -745,6 +756,34 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
                ieee80211_add_ht_ie(sdata, skb, assoc_data->ap_ht_param,
                                    sband, chan, sdata->smps_mode);
 
+       /* if present, add any custom IEs that go before VHT */
+       if (assoc_data->ie_len) {
+               static const u8 before_vht[] = {
+                       WLAN_EID_SSID,
+                       WLAN_EID_SUPP_RATES,
+                       WLAN_EID_EXT_SUPP_RATES,
+                       WLAN_EID_PWR_CAPABILITY,
+                       WLAN_EID_SUPPORTED_CHANNELS,
+                       WLAN_EID_RSN,
+                       WLAN_EID_QOS_CAPA,
+                       WLAN_EID_RRM_ENABLED_CAPABILITIES,
+                       WLAN_EID_MOBILITY_DOMAIN,
+                       WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
+                       WLAN_EID_HT_CAPABILITY,
+                       WLAN_EID_BSS_COEX_2040,
+                       WLAN_EID_EXT_CAPABILITY,
+                       WLAN_EID_QOS_TRAFFIC_CAPA,
+                       WLAN_EID_TIM_BCAST_REQ,
+                       WLAN_EID_INTERWORKING,
+               };
+               noffset = ieee80211_ie_split(assoc_data->ie, assoc_data->ie_len,
+                                            before_vht, ARRAY_SIZE(before_vht),
+                                            offset);
+               pos = skb_put(skb, noffset - offset);
+               memcpy(pos, assoc_data->ie + offset, noffset - offset);
+               offset = noffset;
+       }
+
        if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT))
                ieee80211_add_vht_ie(sdata, skb, sband,
                                     &assoc_data->ap_vht_cap);
@@ -1001,7 +1040,6 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
        }
 
        ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED;
-       sdata->vif.csa_active = true;
 
        mutex_lock(&local->chanctx_mtx);
        if (local->use_chanctx) {
@@ -1039,6 +1077,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
        mutex_unlock(&local->chanctx_mtx);
 
        sdata->csa_chandef = csa_ie.chandef;
+       sdata->vif.csa_active = true;
 
        if (csa_ie.mode)
                ieee80211_stop_queues_by_reason(&local->hw,
index 22b223f..8fdadfd 100644 (file)
 
 #include <linux/kernel.h>
 #include <linux/rtnetlink.h>
-#include <linux/slab.h>
 #include <linux/module.h>
+#include <linux/slab.h>
 #include "rate.h"
 #include "ieee80211_i.h"
 #include "debugfs.h"
 
 struct rate_control_alg {
        struct list_head list;
-       struct rate_control_ops *ops;
+       const struct rate_control_ops *ops;
 };
 
 static LIST_HEAD(rate_ctrl_algs);
@@ -29,7 +29,7 @@ module_param(ieee80211_default_rc_algo, charp, 0644);
 MODULE_PARM_DESC(ieee80211_default_rc_algo,
                 "Default rate control algorithm for mac80211 to use");
 
-int ieee80211_rate_control_register(struct rate_control_ops *ops)
+int ieee80211_rate_control_register(const struct rate_control_ops *ops)
 {
        struct rate_control_alg *alg;
 
@@ -60,7 +60,7 @@ int ieee80211_rate_control_register(struct rate_control_ops *ops)
 }
 EXPORT_SYMBOL(ieee80211_rate_control_register);
 
-void ieee80211_rate_control_unregister(struct rate_control_ops *ops)
+void ieee80211_rate_control_unregister(const struct rate_control_ops *ops)
 {
        struct rate_control_alg *alg;
 
@@ -76,32 +76,31 @@ void ieee80211_rate_control_unregister(struct rate_control_ops *ops)
 }
 EXPORT_SYMBOL(ieee80211_rate_control_unregister);
 
-static struct rate_control_ops *
+static const struct rate_control_ops *
 ieee80211_try_rate_control_ops_get(const char *name)
 {
        struct rate_control_alg *alg;
-       struct rate_control_ops *ops = NULL;
+       const struct rate_control_ops *ops = NULL;
 
        if (!name)
                return NULL;
 
        mutex_lock(&rate_ctrl_mutex);
        list_for_each_entry(alg, &rate_ctrl_algs, list) {
-               if (!strcmp(alg->ops->name, name))
-                       if (try_module_get(alg->ops->module)) {
-                               ops = alg->ops;
-                               break;
-                       }
+               if (!strcmp(alg->ops->name, name)) {
+                       ops = alg->ops;
+                       break;
+               }
        }
        mutex_unlock(&rate_ctrl_mutex);
        return ops;
 }
 
 /* Get the rate control algorithm. */
-static struct rate_control_ops *
+static const struct rate_control_ops *
 ieee80211_rate_control_ops_get(const char *name)
 {
-       struct rate_control_ops *ops;
+       const struct rate_control_ops *ops;
        const char *alg_name;
 
        kparam_block_sysfs_write(ieee80211_default_rc_algo);
@@ -111,10 +110,6 @@ ieee80211_rate_control_ops_get(const char *name)
                alg_name = name;
 
        ops = ieee80211_try_rate_control_ops_get(alg_name);
-       if (!ops) {
-               request_module("rc80211_%s", alg_name);
-               ops = ieee80211_try_rate_control_ops_get(alg_name);
-       }
        if (!ops && name)
                /* try default if specific alg requested but not found */
                ops = ieee80211_try_rate_control_ops_get(ieee80211_default_rc_algo);
@@ -127,11 +122,6 @@ ieee80211_rate_control_ops_get(const char *name)
        return ops;
 }
 
-static void ieee80211_rate_control_ops_put(struct rate_control_ops *ops)
-{
-       module_put(ops->module);
-}
-
 #ifdef CONFIG_MAC80211_DEBUGFS
 static ssize_t rcname_read(struct file *file, char __user *userbuf,
                           size_t count, loff_t *ppos)
@@ -158,11 +148,11 @@ static struct rate_control_ref *rate_control_alloc(const char *name,
 
        ref = kmalloc(sizeof(struct rate_control_ref), GFP_KERNEL);
        if (!ref)
-               goto fail_ref;
+               return NULL;
        ref->local = local;
        ref->ops = ieee80211_rate_control_ops_get(name);
        if (!ref->ops)
-               goto fail_ops;
+               goto free;
 
 #ifdef CONFIG_MAC80211_DEBUGFS
        debugfsdir = debugfs_create_dir("rc", local->hw.wiphy->debugfsdir);
@@ -172,14 +162,11 @@ static struct rate_control_ref *rate_control_alloc(const char *name,
 
        ref->priv = ref->ops->alloc(&local->hw, debugfsdir);
        if (!ref->priv)
-               goto fail_priv;
+               goto free;
        return ref;
 
-fail_priv:
-       ieee80211_rate_control_ops_put(ref->ops);
-fail_ops:
+free:
        kfree(ref);
-fail_ref:
        return NULL;
 }
 
@@ -192,7 +179,6 @@ static void rate_control_free(struct rate_control_ref *ctrl_ref)
        ctrl_ref->local->debugfs.rcdir = NULL;
 #endif
 
-       ieee80211_rate_control_ops_put(ctrl_ref->ops);
        kfree(ctrl_ref);
 }
 
index b95e16c..9aa2a11 100644 (file)
@@ -21,7 +21,7 @@
 
 struct rate_control_ref {
        struct ieee80211_local *local;
-       struct rate_control_ops *ops;
+       const struct rate_control_ops *ops;
        void *priv;
 };
 
index f3d88b0..26fd94f 100644 (file)
@@ -657,7 +657,7 @@ minstrel_free(void *priv)
        kfree(priv);
 }
 
-struct rate_control_ops mac80211_minstrel = {
+const struct rate_control_ops mac80211_minstrel = {
        .name = "minstrel",
        .tx_status = minstrel_tx_status,
        .get_rate = minstrel_get_rate,
index f4301f4..046d1bd 100644 (file)
@@ -123,7 +123,7 @@ struct minstrel_debugfs_info {
        char buf[];
 };
 
-extern struct rate_control_ops mac80211_minstrel;
+extern const struct rate_control_ops mac80211_minstrel;
 void minstrel_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir);
 void minstrel_remove_sta_debugfs(void *priv, void *priv_sta);
 
index c1b5b73..bccaf85 100644 (file)
@@ -124,7 +124,7 @@ const struct mcs_group minstrel_mcs_groups[] = {
 
 #define MINSTREL_CCK_GROUP     (ARRAY_SIZE(minstrel_mcs_groups) - 1)
 
-static u8 sample_table[SAMPLE_COLUMNS][MCS_GROUP_RATES];
+static u8 sample_table[SAMPLE_COLUMNS][MCS_GROUP_RATES] __read_mostly;
 
 static void
 minstrel_ht_update_rates(struct minstrel_priv *mp, struct minstrel_ht_sta *mi);
@@ -1031,7 +1031,7 @@ minstrel_ht_free(void *priv)
        mac80211_minstrel.free(priv);
 }
 
-static struct rate_control_ops mac80211_minstrel_ht = {
+static const struct rate_control_ops mac80211_minstrel_ht = {
        .name = "minstrel_ht",
        .tx_status = minstrel_ht_tx_status,
        .get_rate = minstrel_ht_get_rate,
@@ -1048,8 +1048,7 @@ static struct rate_control_ops mac80211_minstrel_ht = {
 };
 
 
-static void
-init_sample_table(void)
+static void __init init_sample_table(void)
 {
        int col, i, new_idx;
        u8 rnd[MCS_GROUP_RATES];
index 958fad0..d0da2a7 100644 (file)
@@ -452,7 +452,7 @@ static void rate_control_pid_free_sta(void *priv, struct ieee80211_sta *sta,
        kfree(priv_sta);
 }
 
-static struct rate_control_ops mac80211_rcpid = {
+static const struct rate_control_ops mac80211_rcpid = {
        .name = "pid",
        .tx_status = rate_control_pid_tx_status,
        .get_rate = rate_control_pid_get_rate,
index c24ca0d..5930621 100644 (file)
@@ -40,8 +40,6 @@
 static struct sk_buff *remove_monitor_info(struct ieee80211_local *local,
                                           struct sk_buff *skb)
 {
-       struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
-
        if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS) {
                if (likely(skb->len > FCS_LEN))
                        __pskb_trim(skb, skb->len - FCS_LEN);
@@ -53,9 +51,6 @@ static struct sk_buff *remove_monitor_info(struct ieee80211_local *local,
                }
        }
 
-       if (status->vendor_radiotap_len)
-               __pskb_pull(skb, status->vendor_radiotap_len);
-
        return skb;
 }
 
@@ -64,14 +59,13 @@ static inline int should_drop_frame(struct sk_buff *skb, int present_fcs_len)
        struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
        struct ieee80211_hdr *hdr;
 
-       hdr = (void *)(skb->data + status->vendor_radiotap_len);
+       hdr = (void *)(skb->data);
 
        if (status->flag & (RX_FLAG_FAILED_FCS_CRC |
                            RX_FLAG_FAILED_PLCP_CRC |
                            RX_FLAG_AMPDU_IS_ZEROLEN))
                return 1;
-       if (unlikely(skb->len < 16 + present_fcs_len +
-                               status->vendor_radiotap_len))
+       if (unlikely(skb->len < 16 + present_fcs_len))
                return 1;
        if (ieee80211_is_ctl(hdr->frame_control) &&
            !ieee80211_is_pspoll(hdr->frame_control) &&
@@ -90,8 +84,6 @@ ieee80211_rx_radiotap_space(struct ieee80211_local *local,
        len = sizeof(struct ieee80211_radiotap_header) + 8;
 
        /* allocate extra bitmaps */
-       if (status->vendor_radiotap_len)
-               len += 4;
        if (status->chains)
                len += 4 * hweight8(status->chains);
 
@@ -127,18 +119,6 @@ ieee80211_rx_radiotap_space(struct ieee80211_local *local,
                len += 2 * hweight8(status->chains);
        }
 
-       if (status->vendor_radiotap_len) {
-               if (WARN_ON_ONCE(status->vendor_radiotap_align == 0))
-                       status->vendor_radiotap_align = 1;
-               /* align standard part of vendor namespace */
-               len = ALIGN(len, 2);
-               /* allocate standard part of vendor namespace */
-               len += 6;
-               /* align vendor-defined part */
-               len = ALIGN(len, status->vendor_radiotap_align);
-               /* vendor-defined part is already in skb */
-       }
-
        return len;
 }
 
@@ -172,7 +152,7 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
        it_present = &rthdr->it_present;
 
        /* radiotap header, set always present flags */
-       rthdr->it_len = cpu_to_le16(rtap_len + status->vendor_radiotap_len);
+       rthdr->it_len = cpu_to_le16(rtap_len);
        it_present_val = BIT(IEEE80211_RADIOTAP_FLAGS) |
                         BIT(IEEE80211_RADIOTAP_CHANNEL) |
                         BIT(IEEE80211_RADIOTAP_RX_FLAGS);
@@ -190,14 +170,6 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
                                 BIT(IEEE80211_RADIOTAP_DBM_ANTSIGNAL);
        }
 
-       if (status->vendor_radiotap_len) {
-               it_present_val |= BIT(IEEE80211_RADIOTAP_VENDOR_NAMESPACE) |
-                                 BIT(IEEE80211_RADIOTAP_EXT);
-               put_unaligned_le32(it_present_val, it_present);
-               it_present++;
-               it_present_val = status->vendor_radiotap_bitmap;
-       }
-
        put_unaligned_le32(it_present_val, it_present);
 
        pos = (void *)(it_present + 1);
@@ -307,6 +279,8 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
                        *pos |= IEEE80211_RADIOTAP_MCS_BW_40;
                if (status->flag & RX_FLAG_HT_GF)
                        *pos |= IEEE80211_RADIOTAP_MCS_FMT_GF;
+               if (status->flag & RX_FLAG_LDPC)
+                       *pos |= IEEE80211_RADIOTAP_MCS_FEC_LDPC;
                stbc = (status->flag & RX_FLAG_STBC_MASK) >> RX_FLAG_STBC_SHIFT;
                *pos |= stbc << IEEE80211_RADIOTAP_MCS_STBC_SHIFT;
                pos++;
@@ -349,20 +323,23 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
 
                rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_VHT);
                /* known field - how to handle 80+80? */
-               if (status->flag & RX_FLAG_80P80MHZ)
+               if (status->vht_flag & RX_VHT_FLAG_80P80MHZ)
                        known &= ~IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH;
                put_unaligned_le16(known, pos);
                pos += 2;
                /* flags */
                if (status->flag & RX_FLAG_SHORT_GI)
                        *pos |= IEEE80211_RADIOTAP_VHT_FLAG_SGI;
+               /* in VHT, STBC is binary */
+               if (status->flag & RX_FLAG_STBC_MASK)
+                       *pos |= IEEE80211_RADIOTAP_VHT_FLAG_STBC;
                pos++;
                /* bandwidth */
-               if (status->flag & RX_FLAG_80MHZ)
+               if (status->vht_flag & RX_VHT_FLAG_80MHZ)
                        *pos++ = 4;
-               else if (status->flag & RX_FLAG_80P80MHZ)
+               else if (status->vht_flag & RX_VHT_FLAG_80P80MHZ)
                        *pos++ = 0; /* marked not known above */
-               else if (status->flag & RX_FLAG_160MHZ)
+               else if (status->vht_flag & RX_VHT_FLAG_160MHZ)
                        *pos++ = 11;
                else if (status->flag & RX_FLAG_40MHZ)
                        *pos++ = 1;
@@ -372,6 +349,8 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
                *pos = (status->rate_idx << 4) | status->vht_nss;
                pos += 4;
                /* coding field */
+               if (status->flag & RX_FLAG_LDPC)
+                       *pos |= IEEE80211_RADIOTAP_CODING_LDPC_USER0;
                pos++;
                /* group ID */
                pos++;
@@ -383,21 +362,6 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
                *pos++ = status->chain_signal[chain];
                *pos++ = chain;
        }
-
-       if (status->vendor_radiotap_len) {
-               /* ensure 2 byte alignment for the vendor field as required */
-               if ((pos - (u8 *)rthdr) & 1)
-                       *pos++ = 0;
-               *pos++ = status->vendor_radiotap_oui[0];
-               *pos++ = status->vendor_radiotap_oui[1];
-               *pos++ = status->vendor_radiotap_oui[2];
-               *pos++ = status->vendor_radiotap_subns;
-               put_unaligned_le16(status->vendor_radiotap_len, pos);
-               pos += 2;
-               /* align the actual payload as requested */
-               while ((pos - (u8 *)rthdr) & (status->vendor_radiotap_align - 1))
-                       *pos++ = 0;
-       }
 }
 
 /*
@@ -428,8 +392,8 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
        if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS)
                present_fcs_len = FCS_LEN;
 
-       /* ensure hdr->frame_control and vendor radiotap data are in skb head */
-       if (!pskb_may_pull(origskb, 2 + status->vendor_radiotap_len)) {
+       /* ensure hdr->frame_control is in skb head */
+       if (!pskb_may_pull(origskb, 2)) {
                dev_kfree_skb(origskb);
                return NULL;
        }
@@ -599,10 +563,10 @@ static int ieee80211_is_unicast_robust_mgmt_frame(struct sk_buff *skb)
 {
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
 
-       if (skb->len < 24 || is_multicast_ether_addr(hdr->addr1))
+       if (is_multicast_ether_addr(hdr->addr1))
                return 0;
 
-       return ieee80211_is_robust_mgmt_frame(hdr);
+       return ieee80211_is_robust_mgmt_frame(skb);
 }
 
 
@@ -610,10 +574,10 @@ static int ieee80211_is_multicast_robust_mgmt_frame(struct sk_buff *skb)
 {
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
 
-       if (skb->len < 24 || !is_multicast_ether_addr(hdr->addr1))
+       if (!is_multicast_ether_addr(hdr->addr1))
                return 0;
 
-       return ieee80211_is_robust_mgmt_frame(hdr);
+       return ieee80211_is_robust_mgmt_frame(skb);
 }
 
 
@@ -626,7 +590,7 @@ static int ieee80211_get_mmie_keyidx(struct sk_buff *skb)
        if (skb->len < 24 + sizeof(*mmie) || !is_multicast_ether_addr(hdr->da))
                return -1;
 
-       if (!ieee80211_is_robust_mgmt_frame((struct ieee80211_hdr *) hdr))
+       if (!ieee80211_is_robust_mgmt_frame(skb))
                return -1; /* not a robust management frame */
 
        mmie = (struct ieee80211_mmie *)
@@ -1261,6 +1225,7 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
                        if (ieee80211_is_data(hdr->frame_control)) {
                                sta->last_rx_rate_idx = status->rate_idx;
                                sta->last_rx_rate_flag = status->flag;
+                               sta->last_rx_rate_vht_flag = status->vht_flag;
                                sta->last_rx_rate_vht_nss = status->vht_nss;
                        }
                }
@@ -1311,18 +1276,15 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
            !ieee80211_has_morefrags(hdr->frame_control) &&
            !(status->rx_flags & IEEE80211_RX_DEFERRED_RELEASE) &&
            (rx->sdata->vif.type == NL80211_IFTYPE_AP ||
-            rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN)) {
+            rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) &&
+           /* PM bit is only checked in frames where it isn't reserved,
+            * in AP mode it's reserved in non-bufferable management frames
+            * (cf. IEEE 802.11-2012 8.2.4.1.7 Power Management field)
+            */
+           (!ieee80211_is_mgmt(hdr->frame_control) ||
+            ieee80211_is_bufferable_mmpdu(hdr->frame_control))) {
                if (test_sta_flag(sta, WLAN_STA_PS_STA)) {
-                       /*
-                        * Ignore doze->wake transitions that are
-                        * indicated by non-data frames, the standard
-                        * is unclear here, but for example going to
-                        * PS mode and then scanning would cause a
-                        * doze->wake transition for the probe request,
-                        * and that is clearly undesirable.
-                        */
-                       if (ieee80211_is_data(hdr->frame_control) &&
-                           !ieee80211_has_pm(hdr->frame_control))
+                       if (!ieee80211_has_pm(hdr->frame_control))
                                sta_ps_end(sta);
                } else {
                        if (ieee80211_has_pm(hdr->frame_control))
@@ -1845,8 +1807,7 @@ static int ieee80211_drop_unencrypted_mgmt(struct ieee80211_rx_data *rx)
                 * having configured keys.
                 */
                if (unlikely(ieee80211_is_action(fc) && !rx->key &&
-                            ieee80211_is_robust_mgmt_frame(
-                                    (struct ieee80211_hdr *) rx->skb->data)))
+                            ieee80211_is_robust_mgmt_frame(rx->skb)))
                        return -EACCES;
        }
 
index d77ff70..d4d85de 100644 (file)
@@ -261,6 +261,7 @@ struct ieee80211_tx_latency_stat {
  *     "the" transmit rate
  * @last_rx_rate_idx: rx status rate index of the last data packet
  * @last_rx_rate_flag: rx status flag of the last data packet
+ * @last_rx_rate_vht_flag: rx status vht flag of the last data packet
  * @last_rx_rate_vht_nss: rx status nss of last data packet
  * @lock: used for locking all fields that require locking, see comments
  *     in the header file.
@@ -397,6 +398,7 @@ struct sta_info {
        struct ieee80211_tx_rate last_tx_rate;
        int last_rx_rate_idx;
        u32 last_rx_rate_flag;
+       u32 last_rx_rate_vht_flag;
        u8 last_rx_rate_vht_nss;
        u16 tid_seq[IEEE80211_QOS_CTL_TID_MASK + 1];
 
index 1ee85c4..e6e574a 100644 (file)
@@ -479,7 +479,7 @@ static void ieee80211_tx_latency_end_msrmnt(struct ieee80211_local *local,
        u32 msrmnt;
        u16 tid;
        u8 *qc;
-       int i, bin_range_count, bin_count;
+       int i, bin_range_count;
        u32 *bin_ranges;
        __le16 fc;
        struct ieee80211_tx_latency_stat *tx_lat;
@@ -522,7 +522,6 @@ static void ieee80211_tx_latency_end_msrmnt(struct ieee80211_local *local,
        /* count how many Tx frames transmitted with the appropriate latency */
        bin_range_count = tx_latency->n_ranges;
        bin_ranges = tx_latency->ranges;
-       bin_count = tx_lat->bin_count;
 
        for (i = 0; i < bin_range_count; i++) {
                if (msrmnt <= bin_ranges[i]) {
index 97a02d3..722151f 100644 (file)
@@ -452,8 +452,7 @@ static int ieee80211_use_mfp(__le16 fc, struct sta_info *sta,
        if (sta == NULL || !test_sta_flag(sta, WLAN_STA_MFP))
                return 0;
 
-       if (!ieee80211_is_robust_mgmt_frame((struct ieee80211_hdr *)
-                                           skb->data))
+       if (!ieee80211_is_robust_mgmt_frame(skb))
                return 0;
 
        return 1;
@@ -523,11 +522,8 @@ ieee80211_tx_h_ps_buf(struct ieee80211_tx_data *tx)
        if (unlikely(tx->flags & IEEE80211_TX_PS_BUFFERED))
                return TX_CONTINUE;
 
-       /* only deauth, disassoc and action are bufferable MMPDUs */
        if (ieee80211_is_mgmt(hdr->frame_control) &&
-           !ieee80211_is_deauth(hdr->frame_control) &&
-           !ieee80211_is_disassoc(hdr->frame_control) &&
-           !ieee80211_is_action(hdr->frame_control)) {
+           !ieee80211_is_bufferable_mmpdu(hdr->frame_control)) {
                if (tx->flags & IEEE80211_TX_UNICAST)
                        info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER;
                return TX_CONTINUE;
@@ -567,7 +563,7 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)
                tx->key = key;
        else if (ieee80211_is_mgmt(hdr->frame_control) &&
                 is_multicast_ether_addr(hdr->addr1) &&
-                ieee80211_is_robust_mgmt_frame(hdr) &&
+                ieee80211_is_robust_mgmt_frame(tx->skb) &&
                 (key = rcu_dereference(tx->sdata->default_mgmt_key)))
                tx->key = key;
        else if (is_multicast_ether_addr(hdr->addr1) &&
@@ -582,12 +578,12 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)
                tx->key = NULL;
        else if (tx->skb->protocol == tx->sdata->control_port_protocol)
                tx->key = NULL;
-       else if (ieee80211_is_robust_mgmt_frame(hdr) &&
+       else if (ieee80211_is_robust_mgmt_frame(tx->skb) &&
                 !(ieee80211_is_action(hdr->frame_control) &&
                   tx->sta && test_sta_flag(tx->sta, WLAN_STA_MFP)))
                tx->key = NULL;
        else if (ieee80211_is_mgmt(hdr->frame_control) &&
-                !ieee80211_is_robust_mgmt_frame(hdr))
+                !ieee80211_is_robust_mgmt_frame(tx->skb))
                tx->key = NULL;
        else {
                I802_DEBUG_INC(tx->local->tx_handlers_drop_unencrypted);
@@ -2402,15 +2398,6 @@ static int ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,
        return 0;
 }
 
-void ieee80211_csa_finish(struct ieee80211_vif *vif)
-{
-       struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
-
-       ieee80211_queue_work(&sdata->local->hw,
-                            &sdata->csa_finalize_work);
-}
-EXPORT_SYMBOL(ieee80211_csa_finish);
-
 static void ieee80211_update_csa(struct ieee80211_sub_if_data *sdata,
                                 struct beacon_data *beacon)
 {
@@ -2439,8 +2426,12 @@ static void ieee80211_update_csa(struct ieee80211_sub_if_data *sdata,
        if (WARN_ON(counter_offset_beacon >= beacon_data_len))
                return;
 
-       /* warn if the driver did not check for/react to csa completeness */
-       if (WARN_ON(beacon_data[counter_offset_beacon] == 0))
+       /* Warn if the driver did not check for/react to csa
+        * completeness.  A beacon with CSA counter set to 0 should
+        * never occur, because a counter of 1 means switch just
+        * before the next beacon.
+        */
+       if (WARN_ON(beacon_data[counter_offset_beacon] == 1))
                return;
 
        beacon_data[counter_offset_beacon]--;
@@ -2506,7 +2497,7 @@ bool ieee80211_csa_is_complete(struct ieee80211_vif *vif)
        if (WARN_ON(counter_beacon > beacon_data_len))
                goto out;
 
-       if (beacon_data[counter_beacon] == 0)
+       if (beacon_data[counter_beacon] == 1)
                ret = true;
  out:
        rcu_read_unlock();
index 676dc09..d842af5 100644 (file)
@@ -34,7 +34,7 @@
 #include "wep.h"
 
 /* privid for wiphys to determine whether they belong to us or not */
-void *mac80211_wiphy_privid = &mac80211_wiphy_privid;
+const void *const mac80211_wiphy_privid = &mac80211_wiphy_privid;
 
 struct ieee80211_hw *wiphy_to_ieee80211_hw(struct wiphy *wiphy)
 {
@@ -1281,13 +1281,32 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
         * that calculates local->scan_ies_len.
         */
 
-       /* add any remaining custom IEs */
+       /* insert custom IEs that go before VHT */
        if (ie && ie_len) {
-               noffset = ie_len;
+               static const u8 before_vht[] = {
+                       WLAN_EID_SSID,
+                       WLAN_EID_SUPP_RATES,
+                       WLAN_EID_REQUEST,
+                       WLAN_EID_EXT_SUPP_RATES,
+                       WLAN_EID_DS_PARAMS,
+                       WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
+                       WLAN_EID_HT_CAPABILITY,
+                       WLAN_EID_BSS_COEX_2040,
+                       WLAN_EID_EXT_CAPABILITY,
+                       WLAN_EID_SSID_LIST,
+                       WLAN_EID_CHANNEL_USAGE,
+                       WLAN_EID_INTERWORKING,
+                       /* mesh ID can't happen here */
+                       /* 60 GHz can't happen here right now */
+               };
+               noffset = ieee80211_ie_split(ie, ie_len,
+                                            before_vht, ARRAY_SIZE(before_vht),
+                                            offset);
                if (end - pos < noffset - offset)
                        goto out_err;
                memcpy(pos, ie + offset, noffset - offset);
                pos += noffset - offset;
+               offset = noffset;
        }
 
        if (sband->vht_cap.vht_supported) {
@@ -1297,6 +1316,15 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
                                                 sband->vht_cap.cap);
        }
 
+       /* add any remaining custom IEs */
+       if (ie && ie_len) {
+               noffset = ie_len;
+               if (end - pos < noffset - offset)
+                       goto out_err;
+               memcpy(pos, ie + offset, noffset - offset);
+               pos += noffset - offset;
+       }
+
        return pos - buffer;
  out_err:
        WARN_ONCE(1, "not enough space for preq IEs\n");
@@ -1374,7 +1402,6 @@ u32 ieee80211_sta_get_rates(struct ieee80211_sub_if_data *sdata,
                            enum ieee80211_band band, u32 *basic_rates)
 {
        struct ieee80211_supported_band *sband;
-       struct ieee80211_rate *bitrates;
        size_t num_rates;
        u32 supp_rates, rate_flags;
        int i, j, shift;
@@ -1386,7 +1413,6 @@ u32 ieee80211_sta_get_rates(struct ieee80211_sub_if_data *sdata,
        if (WARN_ON(!sband))
                return 1;
 
-       bitrates = sband->bitrates;
        num_rates = sband->n_bitrates;
        supp_rates = 0;
        for (i = 0; i < elems->supp_rates_len +
@@ -2272,11 +2298,11 @@ u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local,
                ri.nss = status->vht_nss;
                if (status->flag & RX_FLAG_40MHZ)
                        ri.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
-               if (status->flag & RX_FLAG_80MHZ)
+               if (status->vht_flag & RX_VHT_FLAG_80MHZ)
                        ri.flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH;
-               if (status->flag & RX_FLAG_80P80MHZ)
+               if (status->vht_flag & RX_VHT_FLAG_80P80MHZ)
                        ri.flags |= RATE_INFO_FLAGS_80P80_MHZ_WIDTH;
-               if (status->flag & RX_FLAG_160MHZ)
+               if (status->vht_flag & RX_VHT_FLAG_160MHZ)
                        ri.flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH;
                if (status->flag & RX_FLAG_SHORT_GI)
                        ri.flags |= RATE_INFO_FLAGS_SHORT_GI;
index d75f35c..e9e36a2 100644 (file)
@@ -349,9 +349,9 @@ void ieee80211_sta_set_rx_nss(struct sta_info *sta)
        sta->sta.rx_nss = max_t(u8, 1, ht_rx_nss);
 }
 
-void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
-                                struct sta_info *sta, u8 opmode,
-                                enum ieee80211_band band, bool nss_only)
+u32 __ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
+                                 struct sta_info *sta, u8 opmode,
+                                 enum ieee80211_band band, bool nss_only)
 {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_supported_band *sband;
@@ -363,7 +363,7 @@ void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
 
        /* ignore - no support for BF yet */
        if (opmode & IEEE80211_OPMODE_NOTIF_RX_NSS_TYPE_BF)
-               return;
+               return 0;
 
        nss = opmode & IEEE80211_OPMODE_NOTIF_RX_NSS_MASK;
        nss >>= IEEE80211_OPMODE_NOTIF_RX_NSS_SHIFT;
@@ -375,7 +375,7 @@ void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
        }
 
        if (nss_only)
-               goto change;
+               return changed;
 
        switch (opmode & IEEE80211_OPMODE_NOTIF_CHANWIDTH_MASK) {
        case IEEE80211_OPMODE_NOTIF_CHANWIDTH_20MHZ:
@@ -398,7 +398,19 @@ void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
                changed |= IEEE80211_RC_BW_CHANGED;
        }
 
- change:
-       if (changed)
+       return changed;
+}
+
+void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
+                                struct sta_info *sta, u8 opmode,
+                                enum ieee80211_band band, bool nss_only)
+{
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band];
+
+       u32 changed = __ieee80211_vht_handle_opmode(sdata, sta, opmode,
+                                                   band, nss_only);
+
+       if (changed > 0)
                rate_control_rate_update(local, sband, sta, changed);
 }
index 21448d6..b8600e3 100644 (file)
@@ -301,8 +301,7 @@ ieee80211_crypto_tkip_decrypt(struct ieee80211_rx_data *rx)
 }
 
 
-static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *b_0, u8 *aad,
-                               int encrypted)
+static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *b_0, u8 *aad)
 {
        __le16 mask_fc;
        int a4_included, mgmt;
@@ -456,7 +455,7 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
                return 0;
 
        pos += IEEE80211_CCMP_HDR_LEN;
-       ccmp_special_blocks(skb, pn, b_0, aad, 0);
+       ccmp_special_blocks(skb, pn, b_0, aad);
        ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, b_0, aad, pos, len,
                                  skb_put(skb, IEEE80211_CCMP_MIC_LEN));
 
@@ -495,7 +494,7 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)
        hdrlen = ieee80211_hdrlen(hdr->frame_control);
 
        if (!ieee80211_is_data(hdr->frame_control) &&
-           !ieee80211_is_robust_mgmt_frame(hdr))
+           !ieee80211_is_robust_mgmt_frame(skb))
                return RX_CONTINUE;
 
        data_len = skb->len - hdrlen - IEEE80211_CCMP_HDR_LEN -
@@ -524,7 +523,7 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)
                u8 aad[2 * AES_BLOCK_SIZE];
                u8 b_0[AES_BLOCK_SIZE];
                /* hardware didn't decrypt/verify MIC */
-               ccmp_special_blocks(skb, pn, b_0, aad, 1);
+               ccmp_special_blocks(skb, pn, b_0, aad);
 
                if (ieee80211_aes_ccm_decrypt(
                            key->u.ccmp.tfm, b_0, aad,
index ed7e0b4..b3b16c0 100644 (file)
@@ -789,7 +789,8 @@ void rfkill_resume_polling(struct rfkill *rfkill)
        if (!rfkill->ops->poll)
                return;
 
-       schedule_work(&rfkill->poll_work.work);
+       queue_delayed_work(system_power_efficient_wq,
+                          &rfkill->poll_work, 0);
 }
 EXPORT_SYMBOL(rfkill_resume_polling);
 
@@ -894,7 +895,8 @@ static void rfkill_poll(struct work_struct *work)
         */
        rfkill->ops->poll(rfkill, rfkill->data);
 
-       schedule_delayed_work(&rfkill->poll_work,
+       queue_delayed_work(system_power_efficient_wq,
+               &rfkill->poll_work,
                round_jiffies_relative(POLL_INTERVAL));
 }
 
@@ -958,7 +960,8 @@ int __must_check rfkill_register(struct rfkill *rfkill)
        INIT_WORK(&rfkill->sync_work, rfkill_sync_work);
 
        if (rfkill->ops->poll)
-               schedule_delayed_work(&rfkill->poll_work,
+               queue_delayed_work(system_power_efficient_wq,
+                       &rfkill->poll_work,
                        round_jiffies_relative(POLL_INTERVAL));
 
        if (!rfkill->persistent || rfkill_epo_lock_active) {
index 11ee4ed..68602be 100644 (file)
@@ -27,9 +27,10 @@ static int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
        err = rdev_stop_ap(rdev, dev);
        if (!err) {
                wdev->beacon_interval = 0;
-               wdev->channel = NULL;
+               memset(&wdev->chandef, 0, sizeof(wdev->chandef));
                wdev->ssid_len = 0;
                rdev_set_qos_map(rdev, dev, NULL);
+               nl80211_send_ap_stopped(wdev);
        }
 
        return err;
index a04b884..2d4268c 100644 (file)
@@ -642,7 +642,8 @@ int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev,
 void
 cfg80211_get_chan_state(struct wireless_dev *wdev,
                        struct ieee80211_channel **chan,
-                       enum cfg80211_chan_mode *chanmode)
+                       enum cfg80211_chan_mode *chanmode,
+                       u8 *radar_detect)
 {
        *chan = NULL;
        *chanmode = CHAN_MODE_UNDEFINED;
@@ -660,6 +661,11 @@ cfg80211_get_chan_state(struct wireless_dev *wdev,
                                     !wdev->ibss_dfs_possible)
                                  ? CHAN_MODE_SHARED
                                  : CHAN_MODE_EXCLUSIVE;
+
+                       /* consider worst-case - IBSS can try to return to the
+                        * original user-specified channel as creator */
+                       if (wdev->ibss_dfs_possible)
+                               *radar_detect |= BIT(wdev->chandef.width);
                        return;
                }
                break;
@@ -674,17 +680,26 @@ cfg80211_get_chan_state(struct wireless_dev *wdev,
        case NL80211_IFTYPE_AP:
        case NL80211_IFTYPE_P2P_GO:
                if (wdev->cac_started) {
-                       *chan = wdev->channel;
+                       *chan = wdev->chandef.chan;
                        *chanmode = CHAN_MODE_SHARED;
+                       *radar_detect |= BIT(wdev->chandef.width);
                } else if (wdev->beacon_interval) {
-                       *chan = wdev->channel;
+                       *chan = wdev->chandef.chan;
                        *chanmode = CHAN_MODE_SHARED;
+
+                       if (cfg80211_chandef_dfs_required(wdev->wiphy,
+                                                         &wdev->chandef))
+                               *radar_detect |= BIT(wdev->chandef.width);
                }
                return;
        case NL80211_IFTYPE_MESH_POINT:
                if (wdev->mesh_id_len) {
-                       *chan = wdev->channel;
+                       *chan = wdev->chandef.chan;
                        *chanmode = CHAN_MODE_SHARED;
+
+                       if (cfg80211_chandef_dfs_required(wdev->wiphy,
+                                                         &wdev->chandef))
+                               *radar_detect |= BIT(wdev->chandef.width);
                }
                return;
        case NL80211_IFTYPE_MONITOR:
index 010892b..76ae6a6 100644 (file)
@@ -737,7 +737,7 @@ void cfg80211_unregister_wdev(struct wireless_dev *wdev)
 }
 EXPORT_SYMBOL(cfg80211_unregister_wdev);
 
-static struct device_type wiphy_type = {
+static const struct device_type wiphy_type = {
        .name   = "wlan",
 };
 
index f1d193b..4068300 100644 (file)
@@ -211,6 +211,7 @@ struct cfg80211_event {
                } dc;
                struct {
                        u8 bssid[ETH_ALEN];
+                       struct ieee80211_channel *channel;
                } ij;
        };
 };
@@ -258,7 +259,8 @@ int __cfg80211_leave_ibss(struct cfg80211_registered_device *rdev,
                          struct net_device *dev, bool nowext);
 int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev,
                        struct net_device *dev, bool nowext);
-void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid);
+void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,
+                           struct ieee80211_channel *channel);
 int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev,
                            struct wireless_dev *wdev);
 
@@ -443,7 +445,8 @@ static inline unsigned int elapsed_jiffies_msecs(unsigned long start)
 void
 cfg80211_get_chan_state(struct wireless_dev *wdev,
                        struct ieee80211_channel **chan,
-                       enum cfg80211_chan_mode *chanmode);
+                       enum cfg80211_chan_mode *chanmode,
+                       u8 *radar_detect);
 
 int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev,
                                 struct cfg80211_chan_def *chandef);
index f911c5f..1470b90 100644 (file)
@@ -14,7 +14,8 @@
 #include "rdev-ops.h"
 
 
-void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid)
+void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,
+                           struct ieee80211_channel *channel)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct cfg80211_bss *bss;
@@ -28,8 +29,7 @@ void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid)
        if (!wdev->ssid_len)
                return;
 
-       bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid,
-                              wdev->ssid, wdev->ssid_len,
+       bss = cfg80211_get_bss(wdev->wiphy, channel, bssid, NULL, 0,
                               WLAN_CAPABILITY_IBSS, WLAN_CAPABILITY_IBSS);
 
        if (WARN_ON(!bss))
@@ -54,21 +54,26 @@ void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid)
 #endif
 }
 
-void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp)
+void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,
+                         struct ieee80211_channel *channel, gfp_t gfp)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
        struct cfg80211_event *ev;
        unsigned long flags;
 
-       trace_cfg80211_ibss_joined(dev, bssid);
+       trace_cfg80211_ibss_joined(dev, bssid, channel);
+
+       if (WARN_ON(!channel))
+               return;
 
        ev = kzalloc(sizeof(*ev), gfp);
        if (!ev)
                return;
 
        ev->type = EVENT_IBSS_JOINED;
-       memcpy(ev->cr.bssid, bssid, ETH_ALEN);
+       memcpy(ev->ij.bssid, bssid, ETH_ALEN);
+       ev->ij.channel = channel;
 
        spin_lock_irqsave(&wdev->event_lock, flags);
        list_add_tail(&ev->list, &wdev->event_list);
@@ -117,6 +122,7 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
 
        wdev->ibss_fixed = params->channel_fixed;
        wdev->ibss_dfs_possible = params->userspace_handles_dfs;
+       wdev->chandef = params->chandef;
 #ifdef CONFIG_CFG80211_WEXT
        wdev->wext.ibss.chandef = params->chandef;
 #endif
@@ -200,6 +206,7 @@ static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext)
 
        wdev->current_bss = NULL;
        wdev->ssid_len = 0;
+       memset(&wdev->chandef, 0, sizeof(wdev->chandef));
 #ifdef CONFIG_CFG80211_WEXT
        if (!nowext)
                wdev->wext.ibss.ssid_len = 0;
index 8858624..d42a3fc 100644 (file)
@@ -195,7 +195,7 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
        if (!err) {
                memcpy(wdev->ssid, setup->mesh_id, setup->mesh_id_len);
                wdev->mesh_id_len = setup->mesh_id_len;
-               wdev->channel = setup->chandef.chan;
+               wdev->chandef = setup->chandef;
        }
 
        return err;
@@ -244,7 +244,7 @@ int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev,
                err = rdev_libertas_set_mesh_channel(rdev, wdev->netdev,
                                                     chandef->chan);
                if (!err)
-                       wdev->channel = chandef->chan;
+                       wdev->chandef = *chandef;
 
                return err;
        }
@@ -276,7 +276,7 @@ static int __cfg80211_leave_mesh(struct cfg80211_registered_device *rdev,
        err = rdev_leave_mesh(rdev, dev);
        if (!err) {
                wdev->mesh_id_len = 0;
-               wdev->channel = NULL;
+               memset(&wdev->chandef, 0, sizeof(wdev->chandef));
                rdev_set_qos_map(rdev, dev, NULL);
        }
 
index 52cca05..d47c9d1 100644 (file)
@@ -772,7 +772,7 @@ void cfg80211_cac_event(struct net_device *netdev,
        if (WARN_ON(!wdev->cac_started))
                return;
 
-       if (WARN_ON(!wdev->channel))
+       if (WARN_ON(!wdev->chandef.chan))
                return;
 
        switch (event) {
index 4fe2e6e..8e6b6a2 100644 (file)
@@ -382,6 +382,8 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
        [NL80211_ATTR_VENDOR_DATA] = { .type = NLA_BINARY },
        [NL80211_ATTR_QOS_MAP] = { .type = NLA_BINARY,
                                   .len = IEEE80211_QOS_MAP_LEN_MAX },
+       [NL80211_ATTR_MAC_HINT] = { .len = ETH_ALEN },
+       [NL80211_ATTR_WIPHY_FREQ_HINT] = { .type = NLA_U32 },
 };
 
 /* policy for the key attributes */
@@ -855,6 +857,19 @@ static int nl80211_key_allowed(struct wireless_dev *wdev)
        return 0;
 }
 
+static struct ieee80211_channel *nl80211_get_valid_chan(struct wiphy *wiphy,
+                                                       struct nlattr *tb)
+{
+       struct ieee80211_channel *chan;
+
+       if (tb == NULL)
+               return NULL;
+       chan = ieee80211_get_channel(wiphy, nla_get_u32(tb));
+       if (!chan || chan->flags & IEEE80211_CHAN_DISABLED)
+               return NULL;
+       return chan;
+}
+
 static int nl80211_put_iftypes(struct sk_buff *msg, u32 attr, u16 ifmodes)
 {
        struct nlattr *nl_modes = nla_nest_start(msg, attr);
@@ -1586,6 +1601,12 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
                    (nla_put_flag(msg, NL80211_ATTR_SUPPORT_5_MHZ) ||
                     nla_put_flag(msg, NL80211_ATTR_SUPPORT_10_MHZ)))
                        goto nla_put_failure;
+
+               if (dev->wiphy.max_ap_assoc_sta &&
+                   nla_put_u32(msg, NL80211_ATTR_MAX_AP_ASSOC_STA,
+                               dev->wiphy.max_ap_assoc_sta))
+                       goto nla_put_failure;
+
                state->split_start++;
                break;
        case 11:
@@ -2035,10 +2056,12 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
                nla_for_each_nested(nl_txq_params,
                                    info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS],
                                    rem_txq_params) {
-                       nla_parse(tb, NL80211_TXQ_ATTR_MAX,
-                                 nla_data(nl_txq_params),
-                                 nla_len(nl_txq_params),
-                                 txq_params_policy);
+                       result = nla_parse(tb, NL80211_TXQ_ATTR_MAX,
+                                          nla_data(nl_txq_params),
+                                          nla_len(nl_txq_params),
+                                          txq_params_policy);
+                       if (result)
+                               return result;
                        result = parse_txq_params(tb, &txq_params);
                        if (result)
                                return result;
@@ -3259,7 +3282,7 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
        if (!err) {
                wdev->preset_chandef = params.chandef;
                wdev->beacon_interval = params.beacon_interval;
-               wdev->channel = params.chandef.chan;
+               wdev->chandef = params.chandef;
                wdev->ssid_len = params.ssid_len;
                memcpy(wdev->ssid, params.ssid, wdev->ssid_len);
        }
@@ -3902,8 +3925,8 @@ static struct net_device *get_vlan(struct genl_info *info,
        return ERR_PTR(ret);
 }
 
-static struct nla_policy
-nl80211_sta_wme_policy[NL80211_STA_WME_MAX + 1] __read_mostly = {
+static const struct nla_policy
+nl80211_sta_wme_policy[NL80211_STA_WME_MAX + 1] = {
        [NL80211_STA_WME_UAPSD_QUEUES] = { .type = NLA_U8 },
        [NL80211_STA_WME_MAX_SP] = { .type = NLA_U8 },
 };
@@ -4604,8 +4627,6 @@ static int parse_reg_rule(struct nlattr *tb[],
                return -EINVAL;
        if (!tb[NL80211_ATTR_FREQ_RANGE_END])
                return -EINVAL;
-       if (!tb[NL80211_ATTR_FREQ_RANGE_MAX_BW])
-               return -EINVAL;
        if (!tb[NL80211_ATTR_POWER_RULE_MAX_EIRP])
                return -EINVAL;
 
@@ -4615,8 +4636,9 @@ static int parse_reg_rule(struct nlattr *tb[],
                nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]);
        freq_range->end_freq_khz =
                nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]);
-       freq_range->max_bandwidth_khz =
-               nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]);
+       if (tb[NL80211_ATTR_FREQ_RANGE_MAX_BW])
+               freq_range->max_bandwidth_khz =
+                       nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]);
 
        power_rule->max_eirp =
                nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_EIRP]);
@@ -5086,6 +5108,7 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
                const struct ieee80211_reg_rule *reg_rule;
                const struct ieee80211_freq_range *freq_range;
                const struct ieee80211_power_rule *power_rule;
+               unsigned int max_bandwidth_khz;
 
                reg_rule = &regdom->reg_rules[i];
                freq_range = &reg_rule->freq_range;
@@ -5095,6 +5118,11 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
                if (!nl_reg_rule)
                        goto nla_put_failure_rcu;
 
+               max_bandwidth_khz = freq_range->max_bandwidth_khz;
+               if (!max_bandwidth_khz)
+                       max_bandwidth_khz = reg_get_max_bandwidth(regdom,
+                                                                 reg_rule);
+
                if (nla_put_u32(msg, NL80211_ATTR_REG_RULE_FLAGS,
                                reg_rule->flags) ||
                    nla_put_u32(msg, NL80211_ATTR_FREQ_RANGE_START,
@@ -5102,7 +5130,7 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
                    nla_put_u32(msg, NL80211_ATTR_FREQ_RANGE_END,
                                freq_range->end_freq_khz) ||
                    nla_put_u32(msg, NL80211_ATTR_FREQ_RANGE_MAX_BW,
-                               freq_range->max_bandwidth_khz) ||
+                               max_bandwidth_khz) ||
                    nla_put_u32(msg, NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN,
                                power_rule->max_antenna_gain) ||
                    nla_put_u32(msg, NL80211_ATTR_POWER_RULE_MAX_EIRP,
@@ -5178,9 +5206,11 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
 
        nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
                            rem_reg_rules) {
-               nla_parse(tb, NL80211_REG_RULE_ATTR_MAX,
-                         nla_data(nl_reg_rule), nla_len(nl_reg_rule),
-                         reg_rule_policy);
+               r = nla_parse(tb, NL80211_REG_RULE_ATTR_MAX,
+                             nla_data(nl_reg_rule), nla_len(nl_reg_rule),
+                             reg_rule_policy);
+               if (r)
+                       goto bad_reg;
                r = parse_reg_rule(tb, &rd->reg_rules[rule_idx]);
                if (r)
                        goto bad_reg;
@@ -5443,6 +5473,7 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
        enum ieee80211_band band;
        size_t ie_len;
        struct nlattr *tb[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1];
+       s32 default_match_rssi = NL80211_SCAN_RSSI_THOLD_OFF;
 
        if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
            !rdev->ops->sched_scan_start)
@@ -5477,11 +5508,40 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
        if (n_ssids > wiphy->max_sched_scan_ssids)
                return -EINVAL;
 
-       if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH])
+       /*
+        * First, count the number of 'real' matchsets. Due to an issue with
+        * the old implementation, matchsets containing only the RSSI attribute
+        * (NL80211_SCHED_SCAN_MATCH_ATTR_RSSI) are considered as the 'default'
+        * RSSI for all matchsets, rather than their own matchset for reporting
+        * all APs with a strong RSSI. This is needed to be compatible with
+        * older userspace that treated a matchset with only the RSSI as the
+        * global RSSI for all other matchsets - if there are other matchsets.
+        */
+       if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) {
                nla_for_each_nested(attr,
                                    info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH],
-                                   tmp)
-                       n_match_sets++;
+                                   tmp) {
+                       struct nlattr *rssi;
+
+                       err = nla_parse(tb, NL80211_SCHED_SCAN_MATCH_ATTR_MAX,
+                                       nla_data(attr), nla_len(attr),
+                                       nl80211_match_policy);
+                       if (err)
+                               return err;
+                       /* add other standalone attributes here */
+                       if (tb[NL80211_SCHED_SCAN_MATCH_ATTR_SSID]) {
+                               n_match_sets++;
+                               continue;
+                       }
+                       rssi = tb[NL80211_SCHED_SCAN_MATCH_ATTR_RSSI];
+                       if (rssi)
+                               default_match_rssi = nla_get_s32(rssi);
+               }
+       }
+
+       /* However, if there's no other matchset, add the RSSI one */
+       if (!n_match_sets && default_match_rssi != NL80211_SCAN_RSSI_THOLD_OFF)
+               n_match_sets = 1;
 
        if (n_match_sets > wiphy->max_match_sets)
                return -EINVAL;
@@ -5602,11 +5662,22 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
                                    tmp) {
                        struct nlattr *ssid, *rssi;
 
-                       nla_parse(tb, NL80211_SCHED_SCAN_MATCH_ATTR_MAX,
-                                 nla_data(attr), nla_len(attr),
-                                 nl80211_match_policy);
+                       err = nla_parse(tb, NL80211_SCHED_SCAN_MATCH_ATTR_MAX,
+                                       nla_data(attr), nla_len(attr),
+                                       nl80211_match_policy);
+                       if (err)
+                               goto out_free;
                        ssid = tb[NL80211_SCHED_SCAN_MATCH_ATTR_SSID];
                        if (ssid) {
+                               if (WARN_ON(i >= n_match_sets)) {
+                                       /* this indicates a programming error,
+                                        * the loop above should have verified
+                                        * things properly
+                                        */
+                                       err = -EINVAL;
+                                       goto out_free;
+                               }
+
                                if (nla_len(ssid) > IEEE80211_MAX_SSID_LEN) {
                                        err = -EINVAL;
                                        goto out_free;
@@ -5615,15 +5686,28 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
                                       nla_data(ssid), nla_len(ssid));
                                request->match_sets[i].ssid.ssid_len =
                                        nla_len(ssid);
+                               /* special attribute - old implemenation w/a */
+                               request->match_sets[i].rssi_thold =
+                                       default_match_rssi;
+                               rssi = tb[NL80211_SCHED_SCAN_MATCH_ATTR_RSSI];
+                               if (rssi)
+                                       request->match_sets[i].rssi_thold =
+                                               nla_get_s32(rssi);
                        }
-                       rssi = tb[NL80211_SCHED_SCAN_MATCH_ATTR_RSSI];
-                       if (rssi)
-                               request->rssi_thold = nla_get_u32(rssi);
-                       else
-                               request->rssi_thold =
-                                                  NL80211_SCAN_RSSI_THOLD_OFF;
                        i++;
                }
+
+               /* there was no other matchset, so the RSSI one is alone */
+               if (i == 0)
+                       request->match_sets[0].rssi_thold = default_match_rssi;
+
+               request->min_rssi_thold = INT_MAX;
+               for (i = 0; i < n_match_sets; i++)
+                       request->min_rssi_thold =
+                               min(request->match_sets[i].rssi_thold,
+                                   request->min_rssi_thold);
+       } else {
+               request->min_rssi_thold = NL80211_SCAN_RSSI_THOLD_OFF;
        }
 
        if (info->attrs[NL80211_ATTR_IE]) {
@@ -5719,7 +5803,7 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
 
        err = rdev->ops->start_radar_detection(&rdev->wiphy, dev, &chandef);
        if (!err) {
-               wdev->channel = chandef.chan;
+               wdev->chandef = chandef;
                wdev->cac_started = true;
                wdev->cac_start_time = jiffies;
        }
@@ -5751,10 +5835,15 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
 
                /* useless if AP is not running */
                if (!wdev->beacon_interval)
-                       return -EINVAL;
+                       return -ENOTCONN;
                break;
        case NL80211_IFTYPE_ADHOC:
+               if (!wdev->ssid_len)
+                       return -ENOTCONN;
+               break;
        case NL80211_IFTYPE_MESH_POINT:
+               if (!wdev->mesh_id_len)
+                       return -ENOTCONN;
                break;
        default:
                return -EOPNOTSUPP;
@@ -6192,9 +6281,9 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
                return -EOPNOTSUPP;
 
        bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
-       chan = ieee80211_get_channel(&rdev->wiphy,
-               nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
-       if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED))
+       chan = nl80211_get_valid_chan(&rdev->wiphy,
+                                     info->attrs[NL80211_ATTR_WIPHY_FREQ]);
+       if (!chan)
                return -EINVAL;
 
        ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
@@ -6347,9 +6436,9 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
 
        bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
 
-       chan = ieee80211_get_channel(&rdev->wiphy,
-               nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
-       if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED))
+       chan = nl80211_get_valid_chan(&rdev->wiphy,
+                                     info->attrs[NL80211_ATTR_WIPHY_FREQ]);
+       if (!chan)
                return -EINVAL;
 
        ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
@@ -6985,6 +7074,9 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
 
        if (info->attrs[NL80211_ATTR_MAC])
                connect.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
+       else if (info->attrs[NL80211_ATTR_MAC_HINT])
+               connect.bssid_hint =
+                       nla_data(info->attrs[NL80211_ATTR_MAC_HINT]);
        connect.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
        connect.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
 
@@ -7003,11 +7095,14 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
        }
 
        if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
-               connect.channel =
-                       ieee80211_get_channel(wiphy,
-                           nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
-               if (!connect.channel ||
-                   connect.channel->flags & IEEE80211_CHAN_DISABLED)
+               connect.channel = nl80211_get_valid_chan(
+                       wiphy, info->attrs[NL80211_ATTR_WIPHY_FREQ]);
+               if (!connect.channel)
+                       return -EINVAL;
+       } else if (info->attrs[NL80211_ATTR_WIPHY_FREQ_HINT]) {
+               connect.channel_hint = nl80211_get_valid_chan(
+                       wiphy, info->attrs[NL80211_ATTR_WIPHY_FREQ_HINT]);
+               if (!connect.channel_hint)
                        return -EINVAL;
        }
 
@@ -7421,6 +7516,7 @@ static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = {
        [NL80211_TXRATE_HT] = { .type = NLA_BINARY,
                                .len = NL80211_MAX_SUPP_HT_RATES },
        [NL80211_TXRATE_VHT] = { .len = sizeof(struct nl80211_txrate_vht)},
+       [NL80211_TXRATE_GI] = { .type = NLA_U8 },
 };
 
 static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
@@ -7467,16 +7563,19 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
         * directly to the enum ieee80211_band values used in cfg80211.
         */
        BUILD_BUG_ON(NL80211_MAX_SUPP_HT_RATES > IEEE80211_HT_MCS_MASK_LEN * 8);
-       nla_for_each_nested(tx_rates, info->attrs[NL80211_ATTR_TX_RATES], rem)
-       {
+       nla_for_each_nested(tx_rates, info->attrs[NL80211_ATTR_TX_RATES], rem) {
                enum ieee80211_band band = nla_type(tx_rates);
+               int err;
+
                if (band < 0 || band >= IEEE80211_NUM_BANDS)
                        return -EINVAL;
                sband = rdev->wiphy.bands[band];
                if (sband == NULL)
                        return -EINVAL;
-               nla_parse(tb, NL80211_TXRATE_MAX, nla_data(tx_rates),
-                         nla_len(tx_rates), nl80211_txattr_policy);
+               err = nla_parse(tb, NL80211_TXRATE_MAX, nla_data(tx_rates),
+                               nla_len(tx_rates), nl80211_txattr_policy);
+               if (err)
+                       return err;
                if (tb[NL80211_TXRATE_LEGACY]) {
                        mask.control[band].legacy = rateset_to_mask(
                                sband,
@@ -7501,6 +7600,12 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
                                        mask.control[band].vht_mcs))
                                return -EINVAL;
                }
+               if (tb[NL80211_TXRATE_GI]) {
+                       mask.control[band].gi =
+                               nla_get_u8(tb[NL80211_TXRATE_GI]);
+                       if (mask.control[band].gi > NL80211_TXRATE_FORCE_LGI)
+                               return -EINVAL;
+               }
 
                if (mask.control[band].legacy == 0) {
                        /* don't allow empty legacy rates if HT or VHT
@@ -7777,8 +7882,8 @@ static int nl80211_get_power_save(struct sk_buff *skb, struct genl_info *info)
        return err;
 }
 
-static struct nla_policy
-nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] __read_mostly = {
+static const struct nla_policy
+nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] = {
        [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 },
        [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U32 },
        [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 },
@@ -11107,7 +11212,8 @@ void cfg80211_ch_switch_notify(struct net_device *dev,
                    wdev->iftype != NL80211_IFTYPE_MESH_POINT))
                return;
 
-       wdev->channel = chandef->chan;
+       wdev->chandef = *chandef;
+       wdev->preset_chandef = *chandef;
        nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL);
 }
 EXPORT_SYMBOL(cfg80211_ch_switch_notify);
@@ -11621,6 +11727,35 @@ void cfg80211_crit_proto_stopped(struct wireless_dev *wdev, gfp_t gfp)
 }
 EXPORT_SYMBOL(cfg80211_crit_proto_stopped);
 
+void nl80211_send_ap_stopped(struct wireless_dev *wdev)
+{
+       struct wiphy *wiphy = wdev->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct sk_buff *msg;
+       void *hdr;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg)
+               return;
+
+       hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_STOP_AP);
+       if (!hdr)
+               goto out;
+
+       if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+           nla_put_u32(msg, NL80211_ATTR_IFINDEX, wdev->netdev->ifindex) ||
+           nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev)))
+               goto out;
+
+       genlmsg_end(msg, hdr);
+
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(wiphy), msg, 0,
+                               NL80211_MCGRP_MLME, GFP_KERNEL);
+       return;
+ out:
+       nlmsg_free(msg);
+}
+
 /* initialisation/exit functions */
 
 int nl80211_init(void)
index 7579974..1e6df96 100644 (file)
@@ -74,6 +74,8 @@ nl80211_radar_notify(struct cfg80211_registered_device *rdev,
                     enum nl80211_radar_event event,
                     struct net_device *netdev, gfp_t gfp);
 
+void nl80211_send_ap_stopped(struct wireless_dev *wdev);
+
 void cfg80211_rdev_free_coalesce(struct cfg80211_registered_device *rdev);
 
 #endif /* __NET_WIRELESS_NL80211_H */
index 9b897fc..27c5253 100644 (file)
@@ -91,7 +91,7 @@ static struct regulatory_request __rcu *last_request =
 /* To trigger userspace events */
 static struct platform_device *reg_pdev;
 
-static struct device_type reg_device_type = {
+static const struct device_type reg_device_type = {
        .uevent = reg_device_uevent,
 };
 
@@ -522,6 +522,77 @@ bool reg_is_valid_request(const char *alpha2)
        return alpha2_equal(lr->alpha2, alpha2);
 }
 
+static const struct ieee80211_regdomain *reg_get_regdomain(struct wiphy *wiphy)
+{
+       struct regulatory_request *lr = get_last_request();
+
+       /*
+        * Follow the driver's regulatory domain, if present, unless a country
+        * IE has been processed or a user wants to help complaince further
+        */
+       if (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE &&
+           lr->initiator != NL80211_REGDOM_SET_BY_USER &&
+           wiphy->regd)
+               return get_wiphy_regdom(wiphy);
+
+       return get_cfg80211_regdom();
+}
+
+unsigned int reg_get_max_bandwidth(const struct ieee80211_regdomain *rd,
+                                  const struct ieee80211_reg_rule *rule)
+{
+       const struct ieee80211_freq_range *freq_range = &rule->freq_range;
+       const struct ieee80211_freq_range *freq_range_tmp;
+       const struct ieee80211_reg_rule *tmp;
+       u32 start_freq, end_freq, idx, no;
+
+       for (idx = 0; idx < rd->n_reg_rules; idx++)
+               if (rule == &rd->reg_rules[idx])
+                       break;
+
+       if (idx == rd->n_reg_rules)
+               return 0;
+
+       /* get start_freq */
+       no = idx;
+
+       while (no) {
+               tmp = &rd->reg_rules[--no];
+               freq_range_tmp = &tmp->freq_range;
+
+               if (freq_range_tmp->end_freq_khz < freq_range->start_freq_khz)
+                       break;
+
+               if (freq_range_tmp->max_bandwidth_khz)
+                       break;
+
+               freq_range = freq_range_tmp;
+       }
+
+       start_freq = freq_range->start_freq_khz;
+
+       /* get end_freq */
+       freq_range = &rule->freq_range;
+       no = idx;
+
+       while (no < rd->n_reg_rules - 1) {
+               tmp = &rd->reg_rules[++no];
+               freq_range_tmp = &tmp->freq_range;
+
+               if (freq_range_tmp->start_freq_khz > freq_range->end_freq_khz)
+                       break;
+
+               if (freq_range_tmp->max_bandwidth_khz)
+                       break;
+
+               freq_range = freq_range_tmp;
+       }
+
+       end_freq = freq_range->end_freq_khz;
+
+       return end_freq - start_freq;
+}
+
 /* Sanity check on a regulatory rule */
 static bool is_valid_reg_rule(const struct ieee80211_reg_rule *rule)
 {
@@ -630,7 +701,9 @@ reg_intersect_dfs_region(const enum nl80211_dfs_regions dfs_region1,
  * Helper for regdom_intersect(), this does the real
  * mathematical intersection fun
  */
-static int reg_rules_intersect(const struct ieee80211_reg_rule *rule1,
+static int reg_rules_intersect(const struct ieee80211_regdomain *rd1,
+                              const struct ieee80211_regdomain *rd2,
+                              const struct ieee80211_reg_rule *rule1,
                               const struct ieee80211_reg_rule *rule2,
                               struct ieee80211_reg_rule *intersected_rule)
 {
@@ -638,7 +711,7 @@ static int reg_rules_intersect(const struct ieee80211_reg_rule *rule1,
        struct ieee80211_freq_range *freq_range;
        const struct ieee80211_power_rule *power_rule1, *power_rule2;
        struct ieee80211_power_rule *power_rule;
-       u32 freq_diff;
+       u32 freq_diff, max_bandwidth1, max_bandwidth2;
 
        freq_range1 = &rule1->freq_range;
        freq_range2 = &rule2->freq_range;
@@ -652,8 +725,24 @@ static int reg_rules_intersect(const struct ieee80211_reg_rule *rule1,
                                         freq_range2->start_freq_khz);
        freq_range->end_freq_khz = min(freq_range1->end_freq_khz,
                                       freq_range2->end_freq_khz);
-       freq_range->max_bandwidth_khz = min(freq_range1->max_bandwidth_khz,
-                                           freq_range2->max_bandwidth_khz);
+
+       max_bandwidth1 = freq_range1->max_bandwidth_khz;
+       max_bandwidth2 = freq_range2->max_bandwidth_khz;
+
+       /*
+        * In case max_bandwidth1 == 0 and max_bandwith2 == 0 set
+        * output bandwidth as 0 (auto calculation). Next we will
+        * calculate this correctly in handle_channel function.
+        * In other case calculate output bandwidth here.
+        */
+       if (max_bandwidth1 || max_bandwidth2) {
+               if (!max_bandwidth1)
+                       max_bandwidth1 = reg_get_max_bandwidth(rd1, rule1);
+               if (!max_bandwidth2)
+                       max_bandwidth2 = reg_get_max_bandwidth(rd2, rule2);
+       }
+
+       freq_range->max_bandwidth_khz = min(max_bandwidth1, max_bandwidth2);
 
        freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz;
        if (freq_range->max_bandwidth_khz > freq_diff)
@@ -713,7 +802,8 @@ regdom_intersect(const struct ieee80211_regdomain *rd1,
                rule1 = &rd1->reg_rules[x];
                for (y = 0; y < rd2->n_reg_rules; y++) {
                        rule2 = &rd2->reg_rules[y];
-                       if (!reg_rules_intersect(rule1, rule2, &dummy_rule))
+                       if (!reg_rules_intersect(rd1, rd2, rule1, rule2,
+                                                &dummy_rule))
                                num_rules++;
                }
        }
@@ -738,7 +828,8 @@ regdom_intersect(const struct ieee80211_regdomain *rd1,
                         * a memcpy()
                         */
                        intersected_rule = &rd->reg_rules[rule_idx];
-                       r = reg_rules_intersect(rule1, rule2, intersected_rule);
+                       r = reg_rules_intersect(rd1, rd2, rule1, rule2,
+                                               intersected_rule);
                        /*
                         * No need to memset here the intersected rule here as
                         * we're not using the stack anymore
@@ -821,18 +912,8 @@ const struct ieee80211_reg_rule *freq_reg_info(struct wiphy *wiphy,
                                               u32 center_freq)
 {
        const struct ieee80211_regdomain *regd;
-       struct regulatory_request *lr = get_last_request();
 
-       /*
-        * Follow the driver's regulatory domain, if present, unless a country
-        * IE has been processed or a user wants to help complaince further
-        */
-       if (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE &&
-           lr->initiator != NL80211_REGDOM_SET_BY_USER &&
-           wiphy->regd)
-               regd = get_wiphy_regdom(wiphy);
-       else
-               regd = get_cfg80211_regdom();
+       regd = reg_get_regdomain(wiphy);
 
        return freq_reg_info_regd(wiphy, center_freq, regd);
 }
@@ -903,6 +984,8 @@ static void handle_channel(struct wiphy *wiphy,
        const struct ieee80211_freq_range *freq_range = NULL;
        struct wiphy *request_wiphy = NULL;
        struct regulatory_request *lr = get_last_request();
+       const struct ieee80211_regdomain *regd;
+       u32 max_bandwidth_khz;
 
        request_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx);
 
@@ -944,11 +1027,18 @@ static void handle_channel(struct wiphy *wiphy,
        power_rule = &reg_rule->power_rule;
        freq_range = &reg_rule->freq_range;
 
-       if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40))
+       max_bandwidth_khz = freq_range->max_bandwidth_khz;
+       /* Check if auto calculation requested */
+       if (!max_bandwidth_khz) {
+               regd = reg_get_regdomain(wiphy);
+               max_bandwidth_khz = reg_get_max_bandwidth(regd, reg_rule);
+       }
+
+       if (max_bandwidth_khz < MHZ_TO_KHZ(40))
                bw_flags = IEEE80211_CHAN_NO_HT40;
-       if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(80))
+       if (max_bandwidth_khz < MHZ_TO_KHZ(80))
                bw_flags |= IEEE80211_CHAN_NO_80MHZ;
-       if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(160))
+       if (max_bandwidth_khz < MHZ_TO_KHZ(160))
                bw_flags |= IEEE80211_CHAN_NO_160MHZ;
 
        if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER &&
@@ -1334,6 +1424,7 @@ static void handle_channel_custom(struct wiphy *wiphy,
        const struct ieee80211_reg_rule *reg_rule = NULL;
        const struct ieee80211_power_rule *power_rule = NULL;
        const struct ieee80211_freq_range *freq_range = NULL;
+       u32 max_bandwidth_khz;
 
        reg_rule = freq_reg_info_regd(wiphy, MHZ_TO_KHZ(chan->center_freq),
                                      regd);
@@ -1351,11 +1442,16 @@ static void handle_channel_custom(struct wiphy *wiphy,
        power_rule = &reg_rule->power_rule;
        freq_range = &reg_rule->freq_range;
 
-       if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40))
+       max_bandwidth_khz = freq_range->max_bandwidth_khz;
+       /* Check if auto calculation requested */
+       if (!max_bandwidth_khz)
+               max_bandwidth_khz = reg_get_max_bandwidth(regd, reg_rule);
+
+       if (max_bandwidth_khz < MHZ_TO_KHZ(40))
                bw_flags = IEEE80211_CHAN_NO_HT40;
-       if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(80))
+       if (max_bandwidth_khz < MHZ_TO_KHZ(80))
                bw_flags |= IEEE80211_CHAN_NO_80MHZ;
-       if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(160))
+       if (max_bandwidth_khz < MHZ_TO_KHZ(160))
                bw_flags |= IEEE80211_CHAN_NO_160MHZ;
 
        chan->flags |= map_regdom_flags(reg_rule->flags) | bw_flags;
@@ -1683,17 +1779,9 @@ static void reg_process_hint(struct regulatory_request *reg_request)
        struct wiphy *wiphy = NULL;
        enum reg_request_treatment treatment;
 
-       if (WARN_ON(!reg_request->alpha2))
-               return;
-
        if (reg_request->wiphy_idx != WIPHY_IDX_INVALID)
                wiphy = wiphy_idx_to_wiphy(reg_request->wiphy_idx);
 
-       if (reg_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && !wiphy) {
-               kfree(reg_request);
-               return;
-       }
-
        switch (reg_request->initiator) {
        case NL80211_REGDOM_SET_BY_CORE:
                reg_process_hint_core(reg_request);
@@ -1703,23 +1791,33 @@ static void reg_process_hint(struct regulatory_request *reg_request)
                if (treatment == REG_REQ_OK ||
                    treatment == REG_REQ_ALREADY_SET)
                        return;
-               schedule_delayed_work(&reg_timeout, msecs_to_jiffies(3142));
+               queue_delayed_work(system_power_efficient_wq,
+                                  &reg_timeout, msecs_to_jiffies(3142));
                return;
        case NL80211_REGDOM_SET_BY_DRIVER:
+               if (!wiphy)
+                       goto out_free;
                treatment = reg_process_hint_driver(wiphy, reg_request);
                break;
        case NL80211_REGDOM_SET_BY_COUNTRY_IE:
+               if (!wiphy)
+                       goto out_free;
                treatment = reg_process_hint_country_ie(wiphy, reg_request);
                break;
        default:
                WARN(1, "invalid initiator %d\n", reg_request->initiator);
-               return;
+               goto out_free;
        }
 
        /* This is required so that the orig_* parameters are saved */
        if (treatment == REG_REQ_ALREADY_SET && wiphy &&
            wiphy->regulatory_flags & REGULATORY_STRICT_REG)
                wiphy_update_regulatory(wiphy, reg_request->initiator);
+
+       return;
+
+out_free:
+       kfree(reg_request);
 }
 
 /*
@@ -2147,6 +2245,7 @@ static void print_rd_rules(const struct ieee80211_regdomain *rd)
        const struct ieee80211_reg_rule *reg_rule = NULL;
        const struct ieee80211_freq_range *freq_range = NULL;
        const struct ieee80211_power_rule *power_rule = NULL;
+       char bw[32];
 
        pr_info("  (start_freq - end_freq @ bandwidth), (max_antenna_gain, max_eirp)\n");
 
@@ -2155,22 +2254,29 @@ static void print_rd_rules(const struct ieee80211_regdomain *rd)
                freq_range = &reg_rule->freq_range;
                power_rule = &reg_rule->power_rule;
 
+               if (!freq_range->max_bandwidth_khz)
+                       snprintf(bw, 32, "%d KHz, AUTO",
+                                reg_get_max_bandwidth(rd, reg_rule));
+               else
+                       snprintf(bw, 32, "%d KHz",
+                                freq_range->max_bandwidth_khz);
+
                /*
                 * There may not be documentation for max antenna gain
                 * in certain regions
                 */
                if (power_rule->max_antenna_gain)
-                       pr_info("  (%d KHz - %d KHz @ %d KHz), (%d mBi, %d mBm)\n",
+                       pr_info("  (%d KHz - %d KHz @ %s), (%d mBi, %d mBm)\n",
                                freq_range->start_freq_khz,
                                freq_range->end_freq_khz,
-                               freq_range->max_bandwidth_khz,
+                               bw,
                                power_rule->max_antenna_gain,
                                power_rule->max_eirp);
                else
-                       pr_info("  (%d KHz - %d KHz @ %d KHz), (N/A, %d mBm)\n",
+                       pr_info("  (%d KHz - %d KHz @ %s), (N/A, %d mBm)\n",
                                freq_range->start_freq_khz,
                                freq_range->end_freq_khz,
-                               freq_range->max_bandwidth_khz,
+                               bw,
                                power_rule->max_eirp);
        }
 }
@@ -2294,7 +2400,8 @@ static int reg_set_rd_driver(const struct ieee80211_regdomain *rd,
 
        request_wiphy = wiphy_idx_to_wiphy(driver_request->wiphy_idx);
        if (!request_wiphy) {
-               schedule_delayed_work(&reg_timeout, 0);
+               queue_delayed_work(system_power_efficient_wq,
+                                  &reg_timeout, 0);
                return -ENODEV;
        }
 
@@ -2354,7 +2461,8 @@ static int reg_set_rd_country_ie(const struct ieee80211_regdomain *rd,
 
        request_wiphy = wiphy_idx_to_wiphy(country_ie_request->wiphy_idx);
        if (!request_wiphy) {
-               schedule_delayed_work(&reg_timeout, 0);
+               queue_delayed_work(system_power_efficient_wq,
+                                  &reg_timeout, 0);
                return -ENODEV;
        }
 
index 02bd8f4..1852461 100644 (file)
@@ -34,6 +34,8 @@ int __init regulatory_init(void);
 void regulatory_exit(void);
 
 int set_regdom(const struct ieee80211_regdomain *rd);
+unsigned int reg_get_max_bandwidth(const struct ieee80211_regdomain *rd,
+                                  const struct ieee80211_reg_rule *rule);
 
 bool reg_last_request_cell_base(void);
 
index fbcc23e..5eaeed5 100644 (file)
@@ -2278,11 +2278,6 @@ DECLARE_EVENT_CLASS(cfg80211_rx_evt,
        TP_printk(NETDEV_PR_FMT ", " MAC_PR_FMT, NETDEV_PR_ARG, MAC_PR_ARG(addr))
 );
 
-DEFINE_EVENT(cfg80211_rx_evt, cfg80211_ibss_joined,
-       TP_PROTO(struct net_device *netdev, const u8 *addr),
-       TP_ARGS(netdev, addr)
-);
-
 DEFINE_EVENT(cfg80211_rx_evt, cfg80211_rx_spurious_frame,
        TP_PROTO(struct net_device *netdev, const u8 *addr),
        TP_ARGS(netdev, addr)
@@ -2293,6 +2288,24 @@ DEFINE_EVENT(cfg80211_rx_evt, cfg80211_rx_unexpected_4addr_frame,
        TP_ARGS(netdev, addr)
 );
 
+TRACE_EVENT(cfg80211_ibss_joined,
+       TP_PROTO(struct net_device *netdev, const u8 *bssid,
+                struct ieee80211_channel *channel),
+       TP_ARGS(netdev, bssid, channel),
+       TP_STRUCT__entry(
+               NETDEV_ENTRY
+               MAC_ENTRY(bssid)
+               CHAN_ENTRY
+       ),
+       TP_fast_assign(
+               NETDEV_ASSIGN;
+               MAC_ASSIGN(bssid, bssid);
+               CHAN_ASSIGN(channel);
+       ),
+       TP_printk(NETDEV_PR_FMT ", bssid: " MAC_PR_FMT ", " CHAN_PR_FMT,
+                 NETDEV_PR_ARG, MAC_PR_ARG(bssid), CHAN_PR_ARG)
+);
+
 TRACE_EVENT(cfg80211_probe_status,
        TP_PROTO(struct net_device *netdev, const u8 *addr, u64 cookie,
                 bool acked),
index d39c371..780b454 100644 (file)
@@ -820,7 +820,8 @@ void cfg80211_process_wdev_events(struct wireless_dev *wdev)
                                                ev->dc.reason, true);
                        break;
                case EVENT_IBSS_JOINED:
-                       __cfg80211_ibss_joined(wdev->netdev, ev->ij.bssid);
+                       __cfg80211_ibss_joined(wdev->netdev, ev->ij.bssid,
+                                              ev->ij.channel);
                        break;
                }
                wdev_unlock(wdev);
@@ -1356,7 +1357,7 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
                 */
                mutex_lock_nested(&wdev_iter->mtx, 1);
                __acquire(wdev_iter->mtx);
-               cfg80211_get_chan_state(wdev_iter, &ch, &chmode);
+               cfg80211_get_chan_state(wdev_iter, &ch, &chmode, &radar_detect);
                wdev_unlock(wdev_iter);
 
                switch (chmode) {