Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/iwlwifi/iwlwif...
authorJohn W. Linville <linville@tuxdriver.com>
Wed, 25 Jun 2014 19:29:03 +0000 (15:29 -0400)
committerJohn W. Linville <linville@tuxdriver.com>
Wed, 25 Jun 2014 19:29:03 +0000 (15:29 -0400)
131 files changed:
MAINTAINERS
drivers/bcma/driver_gpio.c
drivers/net/wireless/at76c50x-usb.c
drivers/net/wireless/ath/ath.h
drivers/net/wireless/ath/ath10k/mac.c
drivers/net/wireless/ath/ath9k/Makefile
drivers/net/wireless/ath/ath9k/ath9k.h
drivers/net/wireless/ath/ath9k/beacon.c
drivers/net/wireless/ath/ath9k/channel.c [new file with mode: 0644]
drivers/net/wireless/ath/ath9k/debug.c
drivers/net/wireless/ath/ath9k/hw.c
drivers/net/wireless/ath/ath9k/hw.h
drivers/net/wireless/ath/ath9k/init.c
drivers/net/wireless/ath/ath9k/link.c
drivers/net/wireless/ath/ath9k/main.c
drivers/net/wireless/ath/ath9k/mci.c
drivers/net/wireless/ath/ath9k/pci.c
drivers/net/wireless/ath/ath9k/recv.c
drivers/net/wireless/ath/ath9k/tx99.c
drivers/net/wireless/ath/ath9k/wow.c
drivers/net/wireless/ath/ath9k/xmit.c
drivers/net/wireless/ath/wil6210/cfg80211.c
drivers/net/wireless/ath/wil6210/debugfs.c
drivers/net/wireless/ath/wil6210/interrupt.c
drivers/net/wireless/ath/wil6210/main.c
drivers/net/wireless/ath/wil6210/pcie_bus.c
drivers/net/wireless/ath/wil6210/rx_reorder.c
drivers/net/wireless/ath/wil6210/txrx.c
drivers/net/wireless/ath/wil6210/wil6210.h
drivers/net/wireless/ath/wil6210/wmi.c
drivers/net/wireless/b43/Kconfig
drivers/net/wireless/b43/Makefile
drivers/net/wireless/b43/main.c
drivers/net/wireless/b43/phy_a.h
drivers/net/wireless/b43/phy_common.c
drivers/net/wireless/b43/phy_common.h
drivers/net/wireless/b43/phy_ht.c
drivers/net/wireless/b43/phy_n.c
drivers/net/wireless/b43/tables_nphy.c
drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
drivers/net/wireless/brcm80211/brcmsmac/phy/phy_cmn.c
drivers/net/wireless/cw1200/scan.c
drivers/net/wireless/cw1200/scan.h
drivers/net/wireless/cw1200/sta.c
drivers/net/wireless/iwlegacy/common.c
drivers/net/wireless/iwlegacy/common.h
drivers/net/wireless/iwlwifi/dvm/mac80211.c
drivers/net/wireless/iwlwifi/mvm/mac80211.c
drivers/net/wireless/iwlwifi/mvm/mvm.h
drivers/net/wireless/iwlwifi/mvm/scan.c
drivers/net/wireless/libertas/Kconfig
drivers/net/wireless/libertas/cmd.c
drivers/net/wireless/mac80211_hwsim.c
drivers/net/wireless/mwifiex/cfg80211.c
drivers/net/wireless/mwifiex/main.c
drivers/net/wireless/mwifiex/sta_ioctl.c
drivers/net/wireless/mwifiex/tdls.c
drivers/net/wireless/mwifiex/uap_txrx.c
drivers/net/wireless/mwifiex/wmm.c
drivers/net/wireless/orinoco/Kconfig
drivers/net/wireless/rsi/rsi_91x_core.c
drivers/net/wireless/rsi/rsi_91x_debugfs.c
drivers/net/wireless/rsi/rsi_91x_mac80211.c
drivers/net/wireless/rsi/rsi_91x_mgmt.c
drivers/net/wireless/rsi/rsi_91x_pkt.c
drivers/net/wireless/rsi/rsi_91x_sdio.c
drivers/net/wireless/rsi/rsi_91x_sdio_ops.c
drivers/net/wireless/rsi/rsi_91x_usb.c
drivers/net/wireless/rsi/rsi_main.h
drivers/net/wireless/rsi/rsi_mgmt.h
drivers/net/wireless/rsi/rsi_sdio.h
drivers/net/wireless/rt2x00/rt2800lib.c
drivers/net/wireless/rt2x00/rt2x00dev.c
drivers/net/wireless/rt2x00/rt2x00mac.c
drivers/net/wireless/rt2x00/rt2x00mmio.c
drivers/net/wireless/rt2x00/rt2x00queue.c
drivers/net/wireless/rt2x00/rt2x00queue.h
drivers/net/wireless/rtl818x/rtl8180/dev.c
drivers/net/wireless/rtlwifi/btcoexist/halbtcoutsrc.h
drivers/net/wireless/rtlwifi/rtl8192de/phy.c
drivers/net/wireless/ti/wl1251/main.c
drivers/net/wireless/ti/wl12xx/scan.c
drivers/net/wireless/ti/wl12xx/scan.h
drivers/net/wireless/ti/wl18xx/scan.c
drivers/net/wireless/ti/wl18xx/scan.h
drivers/net/wireless/ti/wlcore/cmd.c
drivers/net/wireless/ti/wlcore/cmd.h
drivers/net/wireless/ti/wlcore/main.c
drivers/net/wireless/ti/wlcore/scan.h
drivers/net/wireless/ti/wlcore/wlcore.h
include/linux/ieee80211.h
include/net/cfg80211.h
include/net/mac80211.h
include/uapi/linux/nl80211.h
net/mac80211/Kconfig
net/mac80211/Makefile
net/mac80211/agg-tx.c
net/mac80211/cfg.c
net/mac80211/chan.c
net/mac80211/debugfs_sta.c
net/mac80211/driver-ops.h
net/mac80211/ethtool.c [new file with mode: 0644]
net/mac80211/ibss.c
net/mac80211/ieee80211_i.h
net/mac80211/iface.c
net/mac80211/main.c
net/mac80211/mesh.c
net/mac80211/mesh_plink.c
net/mac80211/mlme.c
net/mac80211/offchannel.c
net/mac80211/pm.c
net/mac80211/rate.h
net/mac80211/rc80211_pid.h [deleted file]
net/mac80211/rc80211_pid_algo.c [deleted file]
net/mac80211/rc80211_pid_debugfs.c [deleted file]
net/mac80211/rx.c
net/mac80211/scan.c
net/mac80211/sta_info.c
net/mac80211/sta_info.h
net/mac80211/status.c
net/mac80211/tdls.c
net/mac80211/trace.h
net/mac80211/tx.c
net/mac80211/util.c
net/mac80211/wep.c
net/wireless/core.c
net/wireless/ethtool.c
net/wireless/ethtool.h [deleted file]
net/wireless/nl80211.c
net/wireless/rdev-ops.h
net/wireless/trace.h

index 134483f..54ba0e6 100644 (file)
@@ -5629,16 +5629,6 @@ F:       Documentation/networking/mac80211-injection.txt
 F:     include/net/mac80211.h
 F:     net/mac80211/
 
-MAC80211 PID RATE CONTROL
-M:     Stefano Brivio <stefano.brivio@polimi.it>
-M:     Mattias Nissler <mattias.nissler@gmx.de>
-L:     linux-wireless@vger.kernel.org
-W:     http://wireless.kernel.org/en/developers/Documentation/mac80211/RateControl/PID
-T:     git git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211.git
-T:     git git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next.git
-S:     Maintained
-F:     net/mac80211/rc80211_pid*
-
 MACVLAN DRIVER
 M:     Patrick McHardy <kaber@trash.net>
 L:     netdev@vger.kernel.org
index d7f81ad..aec9f85 100644 (file)
@@ -220,6 +220,7 @@ int bcma_gpio_init(struct bcma_drv_cc *cc)
 #endif
        switch (cc->core->bus->chipinfo.id) {
        case BCMA_CHIP_ID_BCM5357:
+       case BCMA_CHIP_ID_BCM53572:
                chip->ngpio     = 32;
                break;
        default:
index d48776e..334c2ec 100644 (file)
@@ -1955,8 +1955,9 @@ static void at76_dwork_hw_scan(struct work_struct *work)
 
 static int at76_hw_scan(struct ieee80211_hw *hw,
                        struct ieee80211_vif *vif,
-                       struct cfg80211_scan_request *req)
+                       struct ieee80211_scan_request *hw_req)
 {
+       struct cfg80211_scan_request *req = &hw_req->req;
        struct at76_priv *priv = hw->priv;
        struct at76_req_scan scan;
        u8 *ssid = NULL;
index a889fd6..fd9e530 100644 (file)
@@ -63,6 +63,7 @@ enum ath_op_flags {
        ATH_OP_PRIM_STA_VIF,
        ATH_OP_HW_RESET,
        ATH_OP_SCANNING,
+       ATH_OP_MULTI_CHANNEL,
 };
 
 enum ath_bus_type {
index a210800..b8314a5 100644 (file)
@@ -3137,10 +3137,11 @@ exit:
 
 static int ath10k_hw_scan(struct ieee80211_hw *hw,
                          struct ieee80211_vif *vif,
-                         struct cfg80211_scan_request *req)
+                         struct ieee80211_scan_request *hw_req)
 {
        struct ath10k *ar = hw->priv;
        struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       struct cfg80211_scan_request *req = &hw_req->req;
        struct wmi_start_scan_arg arg;
        int ret = 0;
        int i;
index 8fcd586..6b4020a 100644 (file)
@@ -5,7 +5,8 @@ ath9k-y +=      beacon.o \
                recv.o \
                xmit.o \
                link.o \
-               antenna.o
+               antenna.o \
+               channel.o
 
 ath9k-$(CONFIG_ATH9K_BTCOEX_SUPPORT) += mci.o
 ath9k-$(CONFIG_ATH9K_PCI) += pci.o
index 2ca8f7e..11b5e4d 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/interrupt.h>
 #include <linux/leds.h>
 #include <linux/completion.h>
+#include <linux/time.h>
 
 #include "common.h"
 #include "debug.h"
@@ -35,10 +36,7 @@ extern struct ieee80211_ops ath9k_ops;
 extern int ath9k_modparam_nohwcrypt;
 extern int led_blink;
 extern bool is_ath9k_unloaded;
-
-struct ath_config {
-       u16 txpowlimit;
-};
+extern int ath9k_use_chanctx;
 
 /*************************/
 /* Descriptor Management */
@@ -167,7 +165,6 @@ struct ath_txq {
        u32 axq_ampdu_depth;
        bool stopped;
        bool axq_tx_inprogress;
-       struct list_head axq_acq;
        struct list_head txq_fifo[ATH_TXFIFO_DEPTH];
        u8 txq_headidx;
        u8 txq_tailidx;
@@ -280,8 +277,9 @@ struct ath_node {
 struct ath_tx_control {
        struct ath_txq *txq;
        struct ath_node *an;
-       u8 paprd;
        struct ieee80211_sta *sta;
+       u8 paprd;
+       bool force_channel;
 };
 
 
@@ -325,6 +323,116 @@ struct ath_rx {
        u32 ampdu_ref;
 };
 
+struct ath_chanctx {
+       struct cfg80211_chan_def chandef;
+       struct list_head vifs;
+       struct list_head acq[IEEE80211_NUM_ACS];
+       int hw_queue_base;
+
+       /* do not dereference, use for comparison only */
+       struct ieee80211_vif *primary_sta;
+
+       struct ath_beacon_config beacon;
+       struct ath9k_hw_cal_data caldata;
+       struct timespec tsf_ts;
+       u64 tsf_val;
+       u32 last_beacon;
+
+       u16 txpower;
+       bool offchannel;
+       bool stopped;
+       bool active;
+       bool assigned;
+       bool switch_after_beacon;
+};
+
+enum ath_chanctx_event {
+       ATH_CHANCTX_EVENT_BEACON_PREPARE,
+       ATH_CHANCTX_EVENT_BEACON_SENT,
+       ATH_CHANCTX_EVENT_TSF_TIMER,
+       ATH_CHANCTX_EVENT_BEACON_RECEIVED,
+       ATH_CHANCTX_EVENT_ASSOC,
+       ATH_CHANCTX_EVENT_SWITCH,
+       ATH_CHANCTX_EVENT_UNASSIGN,
+       ATH_CHANCTX_EVENT_ENABLE_MULTICHANNEL,
+};
+
+enum ath_chanctx_state {
+       ATH_CHANCTX_STATE_IDLE,
+       ATH_CHANCTX_STATE_WAIT_FOR_BEACON,
+       ATH_CHANCTX_STATE_WAIT_FOR_TIMER,
+       ATH_CHANCTX_STATE_SWITCH,
+       ATH_CHANCTX_STATE_FORCE_ACTIVE,
+};
+
+struct ath_chanctx_sched {
+       bool beacon_pending;
+       bool offchannel_pending;
+       enum ath_chanctx_state state;
+       u8 beacon_miss;
+
+       u32 next_tbtt;
+       u32 switch_start_time;
+       unsigned int offchannel_duration;
+       unsigned int channel_switch_time;
+
+       /* backup, in case the hardware timer fails */
+       struct timer_list timer;
+};
+
+enum ath_offchannel_state {
+       ATH_OFFCHANNEL_IDLE,
+       ATH_OFFCHANNEL_PROBE_SEND,
+       ATH_OFFCHANNEL_PROBE_WAIT,
+       ATH_OFFCHANNEL_SUSPEND,
+       ATH_OFFCHANNEL_ROC_START,
+       ATH_OFFCHANNEL_ROC_WAIT,
+       ATH_OFFCHANNEL_ROC_DONE,
+};
+
+struct ath_offchannel {
+       struct ath_chanctx chan;
+       struct timer_list timer;
+       struct cfg80211_scan_request *scan_req;
+       struct ieee80211_vif *scan_vif;
+       int scan_idx;
+       enum ath_offchannel_state state;
+       struct ieee80211_channel *roc_chan;
+       struct ieee80211_vif *roc_vif;
+       int roc_duration;
+       int duration;
+};
+#define ath_for_each_chanctx(_sc, _ctx)                             \
+       for (ctx = &sc->chanctx[0];                                 \
+            ctx <= &sc->chanctx[ARRAY_SIZE(sc->chanctx) - 1];      \
+            ctx++)
+
+void ath9k_fill_chanctx_ops(void);
+void ath9k_chanctx_force_active(struct ieee80211_hw *hw,
+                               struct ieee80211_vif *vif);
+static inline struct ath_chanctx *
+ath_chanctx_get(struct ieee80211_chanctx_conf *ctx)
+{
+       struct ath_chanctx **ptr = (void *) ctx->drv_priv;
+       return *ptr;
+}
+void ath_chanctx_init(struct ath_softc *sc);
+void ath_chanctx_set_channel(struct ath_softc *sc, struct ath_chanctx *ctx,
+                            struct cfg80211_chan_def *chandef);
+void ath_chanctx_switch(struct ath_softc *sc, struct ath_chanctx *ctx,
+                       struct cfg80211_chan_def *chandef);
+void ath_chanctx_check_active(struct ath_softc *sc, struct ath_chanctx *ctx);
+void ath_offchannel_timer(unsigned long data);
+void ath_offchannel_channel_change(struct ath_softc *sc);
+void ath_chanctx_offchan_switch(struct ath_softc *sc,
+                               struct ieee80211_channel *chan);
+struct ath_chanctx *ath_chanctx_get_oper_chan(struct ath_softc *sc,
+                                             bool active);
+void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif,
+                      enum ath_chanctx_event ev);
+void ath_chanctx_timer(unsigned long data);
+
+int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan);
 int ath_startrecv(struct ath_softc *sc);
 bool ath_stoprecv(struct ath_softc *sc);
 u32 ath_calcrxfilter(struct ath_softc *sc);
@@ -341,6 +449,7 @@ void ath_draintxq(struct ath_softc *sc, struct ath_txq *txq);
 void ath_tx_node_init(struct ath_softc *sc, struct ath_node *an);
 void ath_tx_node_cleanup(struct ath_softc *sc, struct ath_node *an);
 void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq);
+void ath_txq_schedule_all(struct ath_softc *sc);
 int ath_tx_init(struct ath_softc *sc, int nbufs);
 int ath_txq_update(struct ath_softc *sc, int qnum,
                   struct ath9k_tx_queue_info *q);
@@ -370,32 +479,47 @@ void ath9k_release_buffered_frames(struct ieee80211_hw *hw,
 /********/
 
 struct ath_vif {
+       struct list_head list;
+
        struct ieee80211_vif *vif;
        struct ath_node mcast_node;
        int av_bslot;
-       bool primary_sta_vif;
        __le64 tsf_adjust; /* TSF adjustment for staggered beacons */
        struct ath_buf *av_bcbuf;
+       struct ath_chanctx *chanctx;
 
        /* P2P Client */
        struct ieee80211_noa_data noa;
+
+       /* P2P GO */
+       u8 noa_index;
+       u32 offchannel_start;
+       u32 offchannel_duration;
+
+       u32 periodic_noa_start;
+       u32 periodic_noa_duration;
 };
 
 struct ath9k_vif_iter_data {
        u8 hw_macaddr[ETH_ALEN]; /* address of the first vif */
        u8 mask[ETH_ALEN]; /* bssid mask */
        bool has_hw_macaddr;
+       u8 slottime;
+       bool beacons;
 
        int naps;      /* number of AP vifs */
        int nmeshes;   /* number of mesh vifs */
        int nstations; /* number of station vifs */
        int nwds;      /* number of WDS vifs */
        int nadhocs;   /* number of adhoc vifs */
+       struct ieee80211_vif *primary_sta;
 };
 
-void ath9k_calculate_iter_data(struct ieee80211_hw *hw,
-                              struct ieee80211_vif *vif,
+void ath9k_calculate_iter_data(struct ath_softc *sc,
+                              struct ath_chanctx *ctx,
                               struct ath9k_vif_iter_data *iter_data);
+void ath9k_calculate_summary_state(struct ath_softc *sc,
+                                  struct ath_chanctx *ctx);
 
 /*******************/
 /* Beacon Handling */
@@ -458,6 +582,7 @@ void ath9k_csa_update(struct ath_softc *sc);
 #define ATH_PAPRD_TIMEOUT         100 /* msecs */
 #define ATH_PLL_WORK_INTERVAL     100
 
+void ath_chanctx_work(struct work_struct *work);
 void ath_tx_complete_poll_work(struct work_struct *work);
 void ath_reset_work(struct work_struct *work);
 bool ath_hw_check(struct ath_softc *sc);
@@ -473,6 +598,7 @@ void ath9k_queue_reset(struct ath_softc *sc, enum ath_reset_type type);
 void ath_ps_full_sleep(unsigned long data);
 void ath9k_p2p_ps_timer(void *priv);
 void ath9k_update_p2p_ps(struct ath_softc *sc, struct ieee80211_vif *vif);
+void __ath9k_flush(struct ieee80211_hw *hw, u32 queues, bool drop);
 
 /**********/
 /* BTCOEX */
@@ -702,6 +828,8 @@ void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs);
 #define PS_BEACON_SYNC            BIT(4)
 #define PS_WAIT_FOR_ANI           BIT(5)
 
+#define ATH9K_NUM_CHANCTX  2 /* supports 2 operating channels */
+
 struct ath_softc {
        struct ieee80211_hw *hw;
        struct device *dev;
@@ -720,6 +848,7 @@ struct ath_softc {
        struct mutex mutex;
        struct work_struct paprd_work;
        struct work_struct hw_reset_work;
+       struct work_struct chanctx_work;
        struct completion paprd_complete;
        wait_queue_head_t tx_wait;
 
@@ -738,23 +867,27 @@ struct ath_softc {
        short nvifs;
        unsigned long ps_usecount;
 
-       struct ath_config config;
        struct ath_rx rx;
        struct ath_tx tx;
        struct ath_beacon beacon;
 
+       struct cfg80211_chan_def cur_chandef;
+       struct ath_chanctx chanctx[ATH9K_NUM_CHANCTX];
+       struct ath_chanctx *cur_chan;
+       struct ath_chanctx *next_chan;
+       spinlock_t chan_lock;
+       struct ath_offchannel offchannel;
+       struct ath_chanctx_sched sched;
+
 #ifdef CONFIG_MAC80211_LEDS
        bool led_registered;
        char led_name[32];
        struct led_classdev led_cdev;
 #endif
 
-       struct ath9k_hw_cal_data caldata;
-
 #ifdef CONFIG_ATH9K_DEBUGFS
        struct ath9k_debug debug;
 #endif
-       struct ath_beacon_config cur_beacon_conf;
        struct delayed_work tx_complete_work;
        struct delayed_work hw_pll_work;
        struct timer_list sleep_timer;
index e387f0b..eaf8f05 100644 (file)
@@ -80,7 +80,7 @@ static void ath9k_beacon_setup(struct ath_softc *sc, struct ieee80211_vif *vif,
        u8 chainmask = ah->txchainmask;
        u8 rate = 0;
 
-       sband = &common->sbands[common->hw->conf.chandef.chan->band];
+       sband = &common->sbands[sc->cur_chandef.chan->band];
        rate = sband->bitrates[rateidx].hw_value;
        if (vif->bss_conf.use_short_preamble)
                rate |= sband->bitrates[rateidx].hw_value_short;
@@ -108,6 +108,55 @@ static void ath9k_beacon_setup(struct ath_softc *sc, struct ieee80211_vif *vif,
        ath9k_hw_set_txdesc(ah, bf->bf_desc, &info);
 }
 
+static void ath9k_beacon_add_noa(struct ath_softc *sc, struct ath_vif *avp,
+                                struct sk_buff *skb)
+{
+       static const u8 noa_ie_hdr[] = {
+               WLAN_EID_VENDOR_SPECIFIC,       /* type */
+               0,                              /* length */
+               0x50, 0x6f, 0x9a,               /* WFA OUI */
+               0x09,                           /* P2P subtype */
+               0x0c,                           /* Notice of Absence */
+               0x00,                           /* LSB of little-endian len */
+               0x00,                           /* MSB of little-endian len */
+       };
+
+       struct ieee80211_p2p_noa_attr *noa;
+       int noa_len, noa_desc, i = 0;
+       u8 *hdr;
+
+       if (!avp->offchannel_duration && !avp->periodic_noa_duration)
+               return;
+
+       noa_desc = !!avp->offchannel_duration + !!avp->periodic_noa_duration;
+       noa_len = 2 + sizeof(struct ieee80211_p2p_noa_desc) * noa_desc;
+
+       hdr = skb_put(skb, sizeof(noa_ie_hdr));
+       memcpy(hdr, noa_ie_hdr, sizeof(noa_ie_hdr));
+       hdr[1] = sizeof(noa_ie_hdr) + noa_len - 2;
+       hdr[7] = noa_len;
+
+       noa = (void *) skb_put(skb, noa_len);
+       memset(noa, 0, noa_len);
+
+       noa->index = avp->noa_index;
+       if (avp->periodic_noa_duration) {
+               u32 interval = TU_TO_USEC(sc->cur_chan->beacon.beacon_interval);
+
+               noa->desc[i].count = 255;
+               noa->desc[i].start_time = cpu_to_le32(avp->periodic_noa_start);
+               noa->desc[i].duration = cpu_to_le32(avp->periodic_noa_duration);
+               noa->desc[i].interval = cpu_to_le32(interval);
+               i++;
+       }
+
+       if (avp->offchannel_duration) {
+               noa->desc[i].count = 1;
+               noa->desc[i].start_time = cpu_to_le32(avp->offchannel_start);
+               noa->desc[i].duration = cpu_to_le32(avp->offchannel_duration);
+       }
+}
+
 static struct ath_buf *ath9k_beacon_generate(struct ieee80211_hw *hw,
                                             struct ieee80211_vif *vif)
 {
@@ -155,6 +204,9 @@ static struct ath_buf *ath9k_beacon_generate(struct ieee80211_hw *hw,
                hdr->seq_ctrl |= cpu_to_le16(sc->tx.seq_no);
        }
 
+       if (vif->p2p)
+               ath9k_beacon_add_noa(sc, avp, skb);
+
        bf->bf_buf_addr = dma_map_single(sc->dev, skb->data,
                                         skb->len, DMA_TO_DEVICE);
        if (unlikely(dma_mapping_error(sc->dev, bf->bf_buf_addr))) {
@@ -249,7 +301,7 @@ void ath9k_beacon_remove_slot(struct ath_softc *sc, struct ieee80211_vif *vif)
 static int ath9k_beacon_choose_slot(struct ath_softc *sc)
 {
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
-       struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf;
+       struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon;
        u16 intval;
        u32 tsftu;
        u64 tsf;
@@ -277,8 +329,8 @@ static int ath9k_beacon_choose_slot(struct ath_softc *sc)
 static void ath9k_set_tsfadjust(struct ath_softc *sc, struct ieee80211_vif *vif)
 {
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
-       struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf;
        struct ath_vif *avp = (void *)vif->drv_priv;
+       struct ath_beacon_config *cur_conf = &avp->chanctx->beacon;
        u32 tsfadjust;
 
        if (avp->av_bslot == 0)
@@ -374,12 +426,19 @@ void ath9k_beacon_tasklet(unsigned long data)
        vif = sc->beacon.bslot[slot];
 
        /* EDMA devices check that in the tx completion function. */
-       if (!edma && ath9k_csa_is_finished(sc, vif))
-               return;
+       if (!edma) {
+               if (sc->sched.beacon_pending)
+                       ath_chanctx_event(sc, NULL,
+                                         ATH_CHANCTX_EVENT_BEACON_SENT);
+
+               if (ath9k_csa_is_finished(sc, vif))
+                       return;
+       }
 
        if (!vif || !vif->bss_conf.enable_beacon)
                return;
 
+       ath_chanctx_event(sc, vif, ATH_CHANCTX_EVENT_BEACON_PREPARE);
        bf = ath9k_beacon_generate(sc->hw, vif);
 
        if (sc->beacon.bmisscnt != 0) {
@@ -500,7 +559,6 @@ static bool ath9k_allow_beacon_config(struct ath_softc *sc,
                                      struct ieee80211_vif *vif)
 {
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
-       struct ath_vif *avp = (void *)vif->drv_priv;
 
        if (sc->sc_ah->opmode == NL80211_IFTYPE_AP) {
                if ((vif->type != NL80211_IFTYPE_AP) ||
@@ -514,7 +572,7 @@ static bool ath9k_allow_beacon_config(struct ath_softc *sc,
        if (sc->sc_ah->opmode == NL80211_IFTYPE_STATION) {
                if ((vif->type == NL80211_IFTYPE_STATION) &&
                    test_bit(ATH_OP_BEACONS, &common->op_flags) &&
-                   !avp->primary_sta_vif) {
+                   vif != sc->cur_chan->primary_sta) {
                        ath_dbg(common, CONFIG,
                                "Beacon already configured for a station interface\n");
                        return false;
@@ -525,10 +583,11 @@ static bool ath9k_allow_beacon_config(struct ath_softc *sc,
 }
 
 static void ath9k_cache_beacon_config(struct ath_softc *sc,
+                                     struct ath_chanctx *ctx,
                                      struct ieee80211_bss_conf *bss_conf)
 {
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
-       struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf;
+       struct ath_beacon_config *cur_conf = &ctx->beacon;
 
        ath_dbg(common, BEACON,
                "Caching beacon data for BSS: %pM\n", bss_conf->bssid);
@@ -564,20 +623,29 @@ void ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif,
                         u32 changed)
 {
        struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
-       struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf;
         struct ath_hw *ah = sc->sc_ah;
         struct ath_common *common = ath9k_hw_common(ah);
+       struct ath_vif *avp = (void *)vif->drv_priv;
+       struct ath_chanctx *ctx = avp->chanctx;
+       struct ath_beacon_config *cur_conf;
        unsigned long flags;
        bool skip_beacon = false;
 
+       if (!ctx)
+               return;
+
+       cur_conf = &avp->chanctx->beacon;
        if (vif->type == NL80211_IFTYPE_AP)
                ath9k_set_tsfadjust(sc, vif);
 
        if (!ath9k_allow_beacon_config(sc, vif))
                return;
 
-       if (sc->sc_ah->opmode == NL80211_IFTYPE_STATION) {
-               ath9k_cache_beacon_config(sc, bss_conf);
+       if (vif->type == NL80211_IFTYPE_STATION) {
+               ath9k_cache_beacon_config(sc, ctx, bss_conf);
+               if (ctx != sc->cur_chan)
+                       return;
+
                ath9k_set_beacon(sc);
                set_bit(ATH_OP_BEACONS, &common->op_flags);
                return;
@@ -593,10 +661,13 @@ void ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif,
                        cur_conf->enable_beacon = false;
                } else if (bss_conf->enable_beacon) {
                        cur_conf->enable_beacon = true;
-                       ath9k_cache_beacon_config(sc, bss_conf);
+                       ath9k_cache_beacon_config(sc, ctx, bss_conf);
                }
        }
 
+       if (ctx != sc->cur_chan)
+               return;
+
        /*
         * Configure the HW beacon registers only when we have a valid
         * beacon interval.
@@ -631,7 +702,7 @@ void ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif,
 void ath9k_set_beacon(struct ath_softc *sc)
 {
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
-       struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf;
+       struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon;
 
        switch (sc->sc_ah->opmode) {
        case NL80211_IFTYPE_AP:
diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c
new file mode 100644 (file)
index 0000000..ba214eb
--- /dev/null
@@ -0,0 +1,685 @@
+/*
+ * Copyright (c) 2014 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "ath9k.h"
+
+/* Set/change channels.  If the channel is really being changed, it's done
+ * by reseting the chip.  To accomplish this we must first cleanup any pending
+ * DMA, then restart stuff.
+ */
+static int ath_set_channel(struct ath_softc *sc)
+{
+       struct ath_hw *ah = sc->sc_ah;
+       struct ath_common *common = ath9k_hw_common(ah);
+       struct ieee80211_hw *hw = sc->hw;
+       struct ath9k_channel *hchan;
+       struct cfg80211_chan_def *chandef = &sc->cur_chan->chandef;
+       struct ieee80211_channel *chan = chandef->chan;
+       int pos = chan->hw_value;
+       int old_pos = -1;
+       int r;
+
+       if (test_bit(ATH_OP_INVALID, &common->op_flags))
+               return -EIO;
+
+       if (ah->curchan)
+               old_pos = ah->curchan - &ah->channels[0];
+
+       ath_dbg(common, CONFIG, "Set channel: %d MHz width: %d\n",
+               chan->center_freq, chandef->width);
+
+       /* update survey stats for the old channel before switching */
+       spin_lock_bh(&common->cc_lock);
+       ath_update_survey_stats(sc);
+       spin_unlock_bh(&common->cc_lock);
+
+       ath9k_cmn_get_channel(hw, ah, chandef);
+
+       /* If the operating channel changes, change the survey in-use flags
+        * along with it.
+        * Reset the survey data for the new channel, unless we're switching
+        * back to the operating channel from an off-channel operation.
+        */
+       if (!sc->cur_chan->offchannel && sc->cur_survey != &sc->survey[pos]) {
+               if (sc->cur_survey)
+                       sc->cur_survey->filled &= ~SURVEY_INFO_IN_USE;
+
+               sc->cur_survey = &sc->survey[pos];
+
+               memset(sc->cur_survey, 0, sizeof(struct survey_info));
+               sc->cur_survey->filled |= SURVEY_INFO_IN_USE;
+       } else if (!(sc->survey[pos].filled & SURVEY_INFO_IN_USE)) {
+               memset(&sc->survey[pos], 0, sizeof(struct survey_info));
+       }
+
+       hchan = &sc->sc_ah->channels[pos];
+       r = ath_reset_internal(sc, hchan);
+       if (r)
+               return r;
+
+       /* The most recent snapshot of channel->noisefloor for the old
+        * channel is only available after the hardware reset. Copy it to
+        * the survey stats now.
+        */
+       if (old_pos >= 0)
+               ath_update_survey_nf(sc, old_pos);
+
+       /* Enable radar pulse detection if on a DFS channel. Spectral
+        * scanning and radar detection can not be used concurrently.
+        */
+       if (hw->conf.radar_enabled) {
+               u32 rxfilter;
+
+               /* set HW specific DFS configuration */
+               ath9k_hw_set_radar_params(ah);
+               rxfilter = ath9k_hw_getrxfilter(ah);
+               rxfilter |= ATH9K_RX_FILTER_PHYRADAR |
+                               ATH9K_RX_FILTER_PHYERR;
+               ath9k_hw_setrxfilter(ah, rxfilter);
+               ath_dbg(common, DFS, "DFS enabled at freq %d\n",
+                       chan->center_freq);
+       } else {
+               /* perform spectral scan if requested. */
+               if (test_bit(ATH_OP_SCANNING, &common->op_flags) &&
+                       sc->spectral_mode == SPECTRAL_CHANSCAN)
+                       ath9k_spectral_scan_trigger(hw);
+       }
+
+       return 0;
+}
+
+static bool
+ath_chanctx_send_vif_ps_frame(struct ath_softc *sc, struct ath_vif *avp,
+                             bool powersave)
+{
+       struct ieee80211_vif *vif = avp->vif;
+       struct ieee80211_sta *sta = NULL;
+       struct ieee80211_hdr_3addr *nullfunc;
+       struct ath_tx_control txctl;
+       struct sk_buff *skb;
+       int band = sc->cur_chan->chandef.chan->band;
+
+       switch (vif->type) {
+       case NL80211_IFTYPE_STATION:
+               if (!vif->bss_conf.assoc)
+                       return false;
+
+               skb = ieee80211_nullfunc_get(sc->hw, vif);
+               if (!skb)
+                       return false;
+
+               nullfunc = (struct ieee80211_hdr_3addr *) skb->data;
+               if (powersave)
+                       nullfunc->frame_control |=
+                               cpu_to_le16(IEEE80211_FCTL_PM);
+
+               skb_set_queue_mapping(skb, IEEE80211_AC_VO);
+               if (!ieee80211_tx_prepare_skb(sc->hw, vif, skb, band, &sta)) {
+                       dev_kfree_skb_any(skb);
+                       return false;
+               }
+               break;
+       default:
+               return false;
+       }
+
+       memset(&txctl, 0, sizeof(txctl));
+       txctl.txq = sc->tx.txq_map[IEEE80211_AC_VO];
+       txctl.sta = sta;
+       txctl.force_channel = true;
+       if (ath_tx_start(sc->hw, skb, &txctl)) {
+               ieee80211_free_txskb(sc->hw, skb);
+               return false;
+       }
+
+       return true;
+}
+
+void ath_chanctx_check_active(struct ath_softc *sc, struct ath_chanctx *ctx)
+{
+       struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+       struct ath_vif *avp;
+       bool active = false;
+       u8 n_active = 0;
+
+       if (!ctx)
+               return;
+
+       list_for_each_entry(avp, &ctx->vifs, list) {
+               struct ieee80211_vif *vif = avp->vif;
+
+               switch (vif->type) {
+               case NL80211_IFTYPE_P2P_CLIENT:
+               case NL80211_IFTYPE_STATION:
+                       if (vif->bss_conf.assoc)
+                               active = true;
+                       break;
+               default:
+                       active = true;
+                       break;
+               }
+       }
+       ctx->active = active;
+
+       ath_for_each_chanctx(sc, ctx) {
+               if (!ctx->assigned || list_empty(&ctx->vifs))
+                       continue;
+               n_active++;
+       }
+
+       if (n_active <= 1) {
+               clear_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags);
+               return;
+       }
+       if (test_and_set_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags))
+               return;
+       ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_ENABLE_MULTICHANNEL);
+}
+
+static bool
+ath_chanctx_send_ps_frame(struct ath_softc *sc, bool powersave)
+{
+       struct ath_vif *avp;
+       bool sent = false;
+
+       rcu_read_lock();
+       list_for_each_entry(avp, &sc->cur_chan->vifs, list) {
+               if (ath_chanctx_send_vif_ps_frame(sc, avp, powersave))
+                       sent = true;
+       }
+       rcu_read_unlock();
+
+       return sent;
+}
+
+static bool ath_chanctx_defer_switch(struct ath_softc *sc)
+{
+       if (sc->cur_chan == &sc->offchannel.chan)
+               return false;
+
+       switch (sc->sched.state) {
+       case ATH_CHANCTX_STATE_SWITCH:
+               return false;
+       case ATH_CHANCTX_STATE_IDLE:
+               if (!sc->cur_chan->switch_after_beacon)
+                       return false;
+
+               sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_BEACON;
+               break;
+       default:
+               break;
+       }
+
+       return true;
+}
+
+static void ath_chanctx_set_next(struct ath_softc *sc, bool force)
+{
+       struct timespec ts;
+       bool measure_time = false;
+       bool send_ps = false;
+
+       spin_lock_bh(&sc->chan_lock);
+       if (!sc->next_chan) {
+               spin_unlock_bh(&sc->chan_lock);
+               return;
+       }
+
+       if (!force && ath_chanctx_defer_switch(sc)) {
+               spin_unlock_bh(&sc->chan_lock);
+               return;
+       }
+
+       if (sc->cur_chan != sc->next_chan) {
+               sc->cur_chan->stopped = true;
+               spin_unlock_bh(&sc->chan_lock);
+
+               if (sc->next_chan == &sc->offchannel.chan) {
+                       getrawmonotonic(&ts);
+                       measure_time = true;
+               }
+               __ath9k_flush(sc->hw, ~0, true);
+
+               if (ath_chanctx_send_ps_frame(sc, true))
+                       __ath9k_flush(sc->hw, BIT(IEEE80211_AC_VO), false);
+
+               send_ps = true;
+               spin_lock_bh(&sc->chan_lock);
+
+               if (sc->cur_chan != &sc->offchannel.chan) {
+                       getrawmonotonic(&sc->cur_chan->tsf_ts);
+                       sc->cur_chan->tsf_val = ath9k_hw_gettsf64(sc->sc_ah);
+               }
+       }
+       sc->cur_chan = sc->next_chan;
+       sc->cur_chan->stopped = false;
+       sc->next_chan = NULL;
+       sc->sched.offchannel_duration = 0;
+       if (sc->sched.state != ATH_CHANCTX_STATE_FORCE_ACTIVE)
+               sc->sched.state = ATH_CHANCTX_STATE_IDLE;
+
+       spin_unlock_bh(&sc->chan_lock);
+
+       if (sc->sc_ah->chip_fullsleep ||
+           memcmp(&sc->cur_chandef, &sc->cur_chan->chandef,
+                  sizeof(sc->cur_chandef))) {
+               ath_set_channel(sc);
+               if (measure_time)
+                       sc->sched.channel_switch_time =
+                               ath9k_hw_get_tsf_offset(&ts, NULL);
+       }
+       if (send_ps)
+               ath_chanctx_send_ps_frame(sc, false);
+
+       ath_offchannel_channel_change(sc);
+       ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_SWITCH);
+}
+
+void ath_chanctx_work(struct work_struct *work)
+{
+       struct ath_softc *sc = container_of(work, struct ath_softc,
+                                           chanctx_work);
+       mutex_lock(&sc->mutex);
+       ath_chanctx_set_next(sc, false);
+       mutex_unlock(&sc->mutex);
+}
+
+void ath_chanctx_init(struct ath_softc *sc)
+{
+       struct ath_chanctx *ctx;
+       struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+       struct ieee80211_supported_band *sband;
+       struct ieee80211_channel *chan;
+       int i, j;
+
+       sband = &common->sbands[IEEE80211_BAND_2GHZ];
+       if (!sband->n_channels)
+               sband = &common->sbands[IEEE80211_BAND_5GHZ];
+
+       chan = &sband->channels[0];
+       for (i = 0; i < ATH9K_NUM_CHANCTX; i++) {
+               ctx = &sc->chanctx[i];
+               cfg80211_chandef_create(&ctx->chandef, chan, NL80211_CHAN_HT20);
+               INIT_LIST_HEAD(&ctx->vifs);
+               ctx->txpower = ATH_TXPOWER_MAX;
+               for (j = 0; j < ARRAY_SIZE(ctx->acq); j++)
+                       INIT_LIST_HEAD(&ctx->acq[j]);
+       }
+       ctx = &sc->offchannel.chan;
+       cfg80211_chandef_create(&ctx->chandef, chan, NL80211_CHAN_HT20);
+       INIT_LIST_HEAD(&ctx->vifs);
+       ctx->txpower = ATH_TXPOWER_MAX;
+       for (j = 0; j < ARRAY_SIZE(ctx->acq); j++)
+               INIT_LIST_HEAD(&ctx->acq[j]);
+       sc->offchannel.chan.offchannel = true;
+
+}
+
+void ath9k_chanctx_force_active(struct ieee80211_hw *hw,
+                               struct ieee80211_vif *vif)
+{
+       struct ath_softc *sc = hw->priv;
+       struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+       struct ath_vif *avp = (struct ath_vif *) vif->drv_priv;
+       bool changed = false;
+
+       if (!test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags))
+               return;
+
+       if (!avp->chanctx)
+               return;
+
+       mutex_lock(&sc->mutex);
+
+       spin_lock_bh(&sc->chan_lock);
+       if (sc->next_chan || (sc->cur_chan != avp->chanctx)) {
+               sc->next_chan = avp->chanctx;
+               changed = true;
+       }
+       sc->sched.state = ATH_CHANCTX_STATE_FORCE_ACTIVE;
+       spin_unlock_bh(&sc->chan_lock);
+
+       if (changed)
+               ath_chanctx_set_next(sc, true);
+
+       mutex_unlock(&sc->mutex);
+}
+
+void ath_chanctx_switch(struct ath_softc *sc, struct ath_chanctx *ctx,
+                       struct cfg80211_chan_def *chandef)
+{
+       struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+
+       spin_lock_bh(&sc->chan_lock);
+
+       if (test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags) &&
+           (sc->cur_chan != ctx) && (ctx == &sc->offchannel.chan)) {
+               sc->sched.offchannel_pending = true;
+               spin_unlock_bh(&sc->chan_lock);
+               return;
+       }
+
+       sc->next_chan = ctx;
+       if (chandef)
+               ctx->chandef = *chandef;
+
+       if (sc->next_chan == &sc->offchannel.chan) {
+               sc->sched.offchannel_duration =
+                       TU_TO_USEC(sc->offchannel.duration) +
+                       sc->sched.channel_switch_time;
+       }
+       spin_unlock_bh(&sc->chan_lock);
+       ieee80211_queue_work(sc->hw, &sc->chanctx_work);
+}
+
+void ath_chanctx_set_channel(struct ath_softc *sc, struct ath_chanctx *ctx,
+                            struct cfg80211_chan_def *chandef)
+{
+       bool cur_chan;
+
+       spin_lock_bh(&sc->chan_lock);
+       if (chandef)
+               memcpy(&ctx->chandef, chandef, sizeof(*chandef));
+       cur_chan = sc->cur_chan == ctx;
+       spin_unlock_bh(&sc->chan_lock);
+
+       if (!cur_chan)
+               return;
+
+       ath_set_channel(sc);
+}
+
+struct ath_chanctx *ath_chanctx_get_oper_chan(struct ath_softc *sc, bool active)
+{
+       struct ath_chanctx *ctx;
+
+       ath_for_each_chanctx(sc, ctx) {
+               if (!ctx->assigned || list_empty(&ctx->vifs))
+                       continue;
+               if (active && !ctx->active)
+                       continue;
+
+               if (ctx->switch_after_beacon)
+                       return ctx;
+       }
+
+       return &sc->chanctx[0];
+}
+
+void ath_chanctx_offchan_switch(struct ath_softc *sc,
+                               struct ieee80211_channel *chan)
+{
+       struct cfg80211_chan_def chandef;
+
+       cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT);
+
+       ath_chanctx_switch(sc, &sc->offchannel.chan, &chandef);
+}
+
+static struct ath_chanctx *
+ath_chanctx_get_next(struct ath_softc *sc, struct ath_chanctx *ctx)
+{
+       int idx = ctx - &sc->chanctx[0];
+
+       return &sc->chanctx[!idx];
+}
+
+static void ath_chanctx_adjust_tbtt_delta(struct ath_softc *sc)
+{
+       struct ath_chanctx *prev, *cur;
+       struct timespec ts;
+       u32 cur_tsf, prev_tsf, beacon_int;
+       s32 offset;
+
+       beacon_int = TU_TO_USEC(sc->cur_chan->beacon.beacon_interval);
+
+       cur = sc->cur_chan;
+       prev = ath_chanctx_get_next(sc, cur);
+
+       getrawmonotonic(&ts);
+       cur_tsf = (u32) cur->tsf_val +
+                 ath9k_hw_get_tsf_offset(&cur->tsf_ts, &ts);
+
+       prev_tsf = prev->last_beacon - (u32) prev->tsf_val + cur_tsf;
+       prev_tsf -= ath9k_hw_get_tsf_offset(&prev->tsf_ts, &ts);
+
+       /* Adjust the TSF time of the AP chanctx to keep its beacons
+        * at half beacon interval offset relative to the STA chanctx.
+        */
+       offset = cur_tsf - prev_tsf;
+
+       /* Ignore stale data or spurious timestamps */
+       if (offset < 0 || offset > 3 * beacon_int)
+               return;
+
+       offset = beacon_int / 2 - (offset % beacon_int);
+       prev->tsf_val += offset;
+}
+
+void ath_chanctx_timer(unsigned long data)
+{
+       struct ath_softc *sc = (struct ath_softc *) data;
+
+       ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_TSF_TIMER);
+}
+
+/* Configure the TSF based hardware timer for a channel switch.
+ * Also set up backup software timer, in case the gen timer fails.
+ * This could be caused by a hardware reset.
+ */
+static void ath_chanctx_setup_timer(struct ath_softc *sc, u32 tsf_time)
+{
+       struct ath_hw *ah = sc->sc_ah;
+
+       ath9k_hw_gen_timer_start(ah, sc->p2p_ps_timer, tsf_time, 1000000);
+       tsf_time -= ath9k_hw_gettsf32(ah);
+       tsf_time = msecs_to_jiffies(tsf_time / 1000) + 1;
+       mod_timer(&sc->sched.timer, tsf_time);
+}
+
+void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif,
+                      enum ath_chanctx_event ev)
+{
+       struct ath_hw *ah = sc->sc_ah;
+       struct ath_common *common = ath9k_hw_common(ah);
+       struct ath_beacon_config *cur_conf;
+       struct ath_vif *avp = NULL;
+       struct ath_chanctx *ctx;
+       u32 tsf_time;
+       u32 beacon_int;
+       bool noa_changed = false;
+
+       if (vif)
+               avp = (struct ath_vif *) vif->drv_priv;
+
+       spin_lock_bh(&sc->chan_lock);
+
+       switch (ev) {
+       case ATH_CHANCTX_EVENT_BEACON_PREPARE:
+               if (avp->offchannel_duration)
+                       avp->offchannel_duration = 0;
+
+               if (avp->chanctx != sc->cur_chan)
+                       break;
+
+               if (sc->sched.offchannel_pending) {
+                       sc->sched.offchannel_pending = false;
+                       sc->next_chan = &sc->offchannel.chan;
+                       sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_BEACON;
+               }
+
+               ctx = ath_chanctx_get_next(sc, sc->cur_chan);
+               if (ctx->active && sc->sched.state == ATH_CHANCTX_STATE_IDLE) {
+                       sc->next_chan = ctx;
+                       sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_BEACON;
+               }
+
+               /* if the timer missed its window, use the next interval */
+               if (sc->sched.state == ATH_CHANCTX_STATE_WAIT_FOR_TIMER)
+                       sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_BEACON;
+
+               if (sc->sched.state != ATH_CHANCTX_STATE_WAIT_FOR_BEACON)
+                       break;
+
+               sc->sched.beacon_pending = true;
+               sc->sched.next_tbtt = REG_READ(ah, AR_NEXT_TBTT_TIMER);
+
+               cur_conf = &sc->cur_chan->beacon;
+               beacon_int = TU_TO_USEC(cur_conf->beacon_interval);
+
+               /* defer channel switch by a quarter beacon interval */
+               tsf_time = sc->sched.next_tbtt + beacon_int / 4;
+               sc->sched.switch_start_time = tsf_time;
+               sc->cur_chan->last_beacon = sc->sched.next_tbtt;
+
+               /* Prevent wrap-around issues */
+               if (avp->periodic_noa_duration &&
+                   tsf_time - avp->periodic_noa_start > BIT(30))
+                       avp->periodic_noa_duration = 0;
+
+               if (ctx->active && !avp->periodic_noa_duration) {
+                       avp->periodic_noa_start = tsf_time;
+                       avp->periodic_noa_duration =
+                               TU_TO_USEC(cur_conf->beacon_interval) / 2 -
+                               sc->sched.channel_switch_time;
+                       noa_changed = true;
+               } else if (!ctx->active && avp->periodic_noa_duration) {
+                       avp->periodic_noa_duration = 0;
+                       noa_changed = true;
+               }
+
+               /* If at least two consecutive beacons were missed on the STA
+                * chanctx, stay on the STA channel for one extra beacon period,
+                * to resync the timer properly.
+                */
+               if (ctx->active && sc->sched.beacon_miss >= 2)
+                       sc->sched.offchannel_duration = 3 * beacon_int / 2;
+
+               if (sc->sched.offchannel_duration) {
+                       noa_changed = true;
+                       avp->offchannel_start = tsf_time;
+                       avp->offchannel_duration =
+                               sc->sched.offchannel_duration;
+               }
+
+               if (noa_changed)
+                       avp->noa_index++;
+               break;
+       case ATH_CHANCTX_EVENT_BEACON_SENT:
+               if (!sc->sched.beacon_pending)
+                       break;
+
+               sc->sched.beacon_pending = false;
+               if (sc->sched.state != ATH_CHANCTX_STATE_WAIT_FOR_BEACON)
+                       break;
+
+               sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_TIMER;
+               ath_chanctx_setup_timer(sc, sc->sched.switch_start_time);
+               break;
+       case ATH_CHANCTX_EVENT_TSF_TIMER:
+               if (sc->sched.state != ATH_CHANCTX_STATE_WAIT_FOR_TIMER)
+                       break;
+
+               if (!sc->cur_chan->switch_after_beacon &&
+                   sc->sched.beacon_pending)
+                       sc->sched.beacon_miss++;
+
+               sc->sched.state = ATH_CHANCTX_STATE_SWITCH;
+               ieee80211_queue_work(sc->hw, &sc->chanctx_work);
+               break;
+       case ATH_CHANCTX_EVENT_BEACON_RECEIVED:
+               if (!test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags) ||
+                   sc->cur_chan == &sc->offchannel.chan)
+                       break;
+
+               ath_chanctx_adjust_tbtt_delta(sc);
+               sc->sched.beacon_pending = false;
+               sc->sched.beacon_miss = 0;
+
+               /* TSF time might have been updated by the incoming beacon,
+                * need update the channel switch timer to reflect the change.
+                */
+               tsf_time = sc->sched.switch_start_time;
+               tsf_time -= (u32) sc->cur_chan->tsf_val +
+                       ath9k_hw_get_tsf_offset(&sc->cur_chan->tsf_ts, NULL);
+               tsf_time += ath9k_hw_gettsf32(ah);
+
+
+               ath_chanctx_setup_timer(sc, tsf_time);
+               break;
+       case ATH_CHANCTX_EVENT_ASSOC:
+               if (sc->sched.state != ATH_CHANCTX_STATE_FORCE_ACTIVE ||
+                   avp->chanctx != sc->cur_chan)
+                       break;
+
+               sc->sched.state = ATH_CHANCTX_STATE_IDLE;
+               /* fall through */
+       case ATH_CHANCTX_EVENT_SWITCH:
+               if (!test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags) ||
+                   sc->sched.state == ATH_CHANCTX_STATE_FORCE_ACTIVE ||
+                   sc->cur_chan->switch_after_beacon ||
+                   sc->cur_chan == &sc->offchannel.chan)
+                       break;
+
+               /* If this is a station chanctx, stay active for a half
+                * beacon period (minus channel switch time)
+                */
+               sc->next_chan = ath_chanctx_get_next(sc, sc->cur_chan);
+               cur_conf = &sc->cur_chan->beacon;
+
+               sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_TIMER;
+
+               tsf_time = TU_TO_USEC(cur_conf->beacon_interval) / 2;
+               if (sc->sched.beacon_miss >= 2) {
+                       sc->sched.beacon_miss = 0;
+                       tsf_time *= 3;
+               }
+
+               tsf_time -= sc->sched.channel_switch_time;
+               tsf_time += ath9k_hw_gettsf32(sc->sc_ah);
+               sc->sched.switch_start_time = tsf_time;
+
+               ath_chanctx_setup_timer(sc, tsf_time);
+               sc->sched.beacon_pending = true;
+               break;
+       case ATH_CHANCTX_EVENT_ENABLE_MULTICHANNEL:
+               if (sc->cur_chan == &sc->offchannel.chan ||
+                   sc->cur_chan->switch_after_beacon)
+                       break;
+
+               sc->next_chan = ath_chanctx_get_next(sc, sc->cur_chan);
+               ieee80211_queue_work(sc->hw, &sc->chanctx_work);
+               break;
+       case ATH_CHANCTX_EVENT_UNASSIGN:
+               if (sc->cur_chan->assigned) {
+                       if (sc->next_chan && !sc->next_chan->assigned &&
+                           sc->next_chan != &sc->offchannel.chan)
+                               sc->sched.state = ATH_CHANCTX_STATE_IDLE;
+                       break;
+               }
+
+               ctx = ath_chanctx_get_next(sc, sc->cur_chan);
+               sc->sched.state = ATH_CHANCTX_STATE_IDLE;
+               if (!ctx->assigned)
+                       break;
+
+               sc->next_chan = ctx;
+               ieee80211_queue_work(sc->hw, &sc->chanctx_work);
+               break;
+       }
+
+       spin_unlock_bh(&sc->chan_lock);
+}
index 6cc42be..ce073e9 100644 (file)
@@ -750,13 +750,13 @@ static ssize_t read_file_misc(struct file *file, char __user *user_buf,
 {
        struct ath_softc *sc = file->private_data;
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
-       struct ieee80211_hw *hw = sc->hw;
        struct ath9k_vif_iter_data iter_data;
+       struct ath_chanctx *ctx;
        char buf[512];
        unsigned int len = 0;
        ssize_t retval = 0;
        unsigned int reg;
-       u32 rxfilter;
+       u32 rxfilter, i;
 
        len += scnprintf(buf + len, sizeof(buf) - len,
                         "BSSID: %pM\n", common->curbssid);
@@ -826,14 +826,20 @@ static ssize_t read_file_misc(struct file *file, char __user *user_buf,
 
        len += scnprintf(buf + len, sizeof(buf) - len, "\n");
 
-       ath9k_calculate_iter_data(hw, NULL, &iter_data);
-
-       len += scnprintf(buf + len, sizeof(buf) - len,
-                        "VIF-COUNTS: AP: %i STA: %i MESH: %i WDS: %i"
-                        " ADHOC: %i TOTAL: %hi BEACON-VIF: %hi\n",
-                        iter_data.naps, iter_data.nstations, iter_data.nmeshes,
-                        iter_data.nwds, iter_data.nadhocs,
-                        sc->nvifs, sc->nbcnvifs);
+       i = 0;
+       ath_for_each_chanctx(sc, ctx) {
+               if (!ctx->assigned || list_empty(&ctx->vifs))
+                       continue;
+               ath9k_calculate_iter_data(sc, ctx, &iter_data);
+
+               len += scnprintf(buf + len, sizeof(buf) - len,
+                       "VIF-COUNTS: CTX %i AP: %i STA: %i MESH: %i WDS: %i",
+                       i++, iter_data.naps, iter_data.nstations,
+                       iter_data.nmeshes, iter_data.nwds);
+               len += scnprintf(buf + len, sizeof(buf) - len,
+                       " ADHOC: %i TOTAL: %hi BEACON-VIF: %hi\n",
+                       iter_data.nadhocs, sc->nvifs, sc->nbcnvifs);
+       }
 
        if (len > sizeof(buf))
                len = sizeof(buf);
@@ -1080,7 +1086,7 @@ static ssize_t read_file_dump_nfcal(struct file *file, char __user *user_buf,
 {
        struct ath_softc *sc = file->private_data;
        struct ath_hw *ah = sc->sc_ah;
-       struct ath9k_nfcal_hist *h = sc->caldata.nfCalHist;
+       struct ath9k_nfcal_hist *h = sc->cur_chan->caldata.nfCalHist;
        struct ath_common *common = ath9k_hw_common(ah);
        struct ieee80211_conf *conf = &common->hw->conf;
        u32 len = 0, size = 1500;
index 2a8ed83..ace4fe2 100644 (file)
@@ -1730,6 +1730,23 @@ fail:
        return -EINVAL;
 }
 
+u32 ath9k_hw_get_tsf_offset(struct timespec *last, struct timespec *cur)
+{
+       struct timespec ts;
+       s64 usec;
+
+       if (!cur) {
+               getrawmonotonic(&ts);
+               cur = &ts;
+       }
+
+       usec = cur->tv_sec * 1000000ULL + cur->tv_nsec / 1000;
+       usec -= last->tv_sec * 1000000ULL + last->tv_nsec / 1000;
+
+       return (u32) usec;
+}
+EXPORT_SYMBOL(ath9k_hw_get_tsf_offset);
+
 int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
                   struct ath9k_hw_cal_data *caldata, bool fastcc)
 {
@@ -1739,7 +1756,6 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
        u32 saveDefAntenna;
        u32 macStaId1;
        u64 tsf = 0;
-       s64 usec = 0;
        int r;
        bool start_mci_reset = false;
        bool save_fullsleep = ah->chip_fullsleep;
@@ -1785,7 +1801,6 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
        /* Save TSF before chip reset, a cold reset clears it */
        tsf = ath9k_hw_gettsf64(ah);
        getrawmonotonic(&ts);
-       usec = ts.tv_sec * 1000000ULL + ts.tv_nsec / 1000;
 
        saveLedState = REG_READ(ah, AR_CFG_LED) &
                (AR_CFG_LED_ASSOC_CTL | AR_CFG_LED_MODE_SEL |
@@ -1818,9 +1833,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
        }
 
        /* Restore TSF */
-       getrawmonotonic(&ts);
-       usec = ts.tv_sec * 1000000ULL + ts.tv_nsec / 1000 - usec;
-       ath9k_hw_settsf64(ah, tsf + usec);
+       ath9k_hw_settsf64(ah, tsf + ath9k_hw_get_tsf_offset(&ts, NULL));
 
        if (AR_SREV_9280_20_OR_LATER(ah))
                REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, AR_GPIO_JTAG_DISABLE);
index 0acd4b5..51b4ebe 100644 (file)
@@ -1000,6 +1000,7 @@ u32 ath9k_hw_gettsf32(struct ath_hw *ah);
 u64 ath9k_hw_gettsf64(struct ath_hw *ah);
 void ath9k_hw_settsf64(struct ath_hw *ah, u64 tsf64);
 void ath9k_hw_reset_tsf(struct ath_hw *ah);
+u32 ath9k_hw_get_tsf_offset(struct timespec *last, struct timespec *cur);
 void ath9k_hw_set_tsfadjust(struct ath_hw *ah, bool set);
 void ath9k_hw_init_global_settings(struct ath_hw *ah);
 u32 ar9003_get_pll_sqsum_dvc(struct ath_hw *ah);
index 0246b99..79fdab6 100644 (file)
@@ -61,7 +61,7 @@ static int ath9k_ps_enable;
 module_param_named(ps_enable, ath9k_ps_enable, int, 0444);
 MODULE_PARM_DESC(ps_enable, "Enable WLAN PowerSave");
 
-static int ath9k_use_chanctx;
+int ath9k_use_chanctx;
 module_param_named(use_chanctx, ath9k_use_chanctx, int, 0444);
 MODULE_PARM_DESC(use_chanctx, "Enable channel context for concurrency");
 
@@ -169,9 +169,9 @@ static void ath9k_reg_notifier(struct wiphy *wiphy,
 
        /* Set tx power */
        if (ah->curchan) {
-               sc->config.txpowlimit = 2 * ah->curchan->chan->max_power;
+               sc->cur_chan->txpower = 2 * ah->curchan->chan->max_power;
                ath9k_ps_wakeup(sc);
-               ath9k_hw_set_txpowerlimit(ah, sc->config.txpowlimit, false);
+               ath9k_hw_set_txpowerlimit(ah, sc->cur_chan->txpower, false);
                sc->curtxpow = ath9k_hw_regulatory(ah)->power_limit;
                /* synchronize DFS detector if regulatory domain changed */
                if (sc->dfs_detector != NULL)
@@ -335,7 +335,6 @@ static void ath9k_init_misc(struct ath_softc *sc)
        setup_timer(&common->ani.timer, ath_ani_calibrate, (unsigned long)sc);
 
        common->last_rssi = ATH_RSSI_DUMMY_MARKER;
-       sc->config.txpowlimit = ATH_TXPOWER_MAX;
        memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN);
        sc->beacon.slottime = ATH9K_SLOT_TIME_9;
 
@@ -511,6 +510,9 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
        sc->dfs_detector = dfs_pattern_detector_init(common, NL80211_DFS_UNSET);
        sc->tx99_power = MAX_RATE_POWER + 1;
        init_waitqueue_head(&sc->tx_wait);
+       sc->cur_chan = &sc->chanctx[0];
+       if (!ath9k_use_chanctx)
+               sc->cur_chan->hw_queue_base = 0;
 
        if (!pdata || pdata->use_eeprom) {
                ah->ah_flags |= AH_USE_EEPROM;
@@ -556,6 +558,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
        spin_lock_init(&common->cc_lock);
        spin_lock_init(&sc->sc_serial_rw);
        spin_lock_init(&sc->sc_pm_lock);
+       spin_lock_init(&sc->chan_lock);
        mutex_init(&sc->mutex);
        tasklet_init(&sc->intr_tq, ath9k_tasklet, (unsigned long)sc);
        tasklet_init(&sc->bcon_tasklet, ath9k_beacon_tasklet,
@@ -564,7 +567,11 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
        setup_timer(&sc->sleep_timer, ath_ps_full_sleep, (unsigned long)sc);
        INIT_WORK(&sc->hw_reset_work, ath_reset_work);
        INIT_WORK(&sc->paprd_work, ath_paprd_calibrate);
+       INIT_WORK(&sc->chanctx_work, ath_chanctx_work);
        INIT_DELAYED_WORK(&sc->hw_pll_work, ath_hw_pll_work);
+       setup_timer(&sc->offchannel.timer, ath_offchannel_timer,
+                   (unsigned long)sc);
+       setup_timer(&sc->sched.timer, ath_chanctx_timer, (unsigned long)sc);
 
        /*
         * Cache line size is used to size and align various
@@ -599,6 +606,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
        ath9k_cmn_init_crypto(sc->sc_ah);
        ath9k_init_misc(sc);
        ath_fill_led_pin(sc);
+       ath_chanctx_init(sc);
 
        if (common->bus_ops->aspm_init)
                common->bus_ops->aspm_init(common);
@@ -664,6 +672,12 @@ static const struct ieee80211_iface_limit wds_limits[] = {
        { .max = 2048,  .types = BIT(NL80211_IFTYPE_WDS) },
 };
 
+static const struct ieee80211_iface_limit if_limits_multi[] = {
+       { .max = 1,     .types = BIT(NL80211_IFTYPE_STATION) },
+       { .max = 1,     .types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
+                                BIT(NL80211_IFTYPE_P2P_GO) },
+};
+
 static const struct ieee80211_iface_limit if_dfs_limits[] = {
        { .max = 1,     .types = BIT(NL80211_IFTYPE_AP) |
 #ifdef CONFIG_MAC80211_MESH
@@ -672,6 +686,16 @@ static const struct ieee80211_iface_limit if_dfs_limits[] = {
                                 BIT(NL80211_IFTYPE_ADHOC) },
 };
 
+static const struct ieee80211_iface_combination if_comb_multi[] = {
+       {
+               .limits = if_limits_multi,
+               .n_limits = ARRAY_SIZE(if_limits_multi),
+               .max_interfaces = 2,
+               .num_different_channels = 2,
+               .beacon_int_infra_match = true,
+       },
+};
+
 static const struct ieee80211_iface_combination if_comb[] = {
        {
                .limits = if_limits,
@@ -712,6 +736,7 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
                IEEE80211_HW_SPECTRUM_MGMT |
                IEEE80211_HW_REPORTS_TX_ACK_STATUS |
                IEEE80211_HW_SUPPORTS_RC_TABLE |
+               IEEE80211_HW_QUEUE_CONTROL |
                IEEE80211_HW_SUPPORTS_HT_CCK_RATES;
 
        if (ath9k_ps_enable)
@@ -739,12 +764,21 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
                        BIT(NL80211_IFTYPE_STATION) |
                        BIT(NL80211_IFTYPE_ADHOC) |
                        BIT(NL80211_IFTYPE_MESH_POINT);
-               hw->wiphy->iface_combinations = if_comb;
                if (!ath9k_use_chanctx) {
+                       hw->wiphy->iface_combinations = if_comb;
                        hw->wiphy->n_iface_combinations = ARRAY_SIZE(if_comb);
                        hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_WDS);
-               } else
-                       hw->wiphy->n_iface_combinations = 1;
+               } else {
+                       hw->wiphy->iface_combinations = if_comb_multi;
+                       hw->wiphy->n_iface_combinations =
+                               ARRAY_SIZE(if_comb_multi);
+                       hw->wiphy->max_scan_ssids = 255;
+                       hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
+                       hw->wiphy->max_remain_on_channel_duration = 10000;
+                       hw->chanctx_data_size = sizeof(void *);
+                       hw->extra_beacon_tailroom =
+                               sizeof(struct ieee80211_p2p_noa_attr) + 9;
+               }
        }
 
        hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
@@ -756,7 +790,12 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
        hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH;
        hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
 
-       hw->queues = 4;
+       /* allow 4 queues per channel context +
+        * 1 cab queue + 1 offchannel tx queue
+        */
+       hw->queues = 10;
+       /* last queue for offchannel */
+       hw->offchannel_tx_hw_queue = hw->queues - 1;
        hw->max_rates = 4;
        hw->max_listen_interval = 1;
        hw->max_rate_tries = 10;
index 72a715f..2343f56 100644 (file)
@@ -178,7 +178,7 @@ static bool ath_paprd_send_frame(struct ath_softc *sc, struct sk_buff *skb, int
        txctl.txq = sc->tx.txq_map[IEEE80211_AC_BE];
 
        memset(tx_info, 0, sizeof(*tx_info));
-       tx_info->band = hw->conf.chandef.chan->band;
+       tx_info->band = sc->cur_chandef.chan->band;
        tx_info->flags |= IEEE80211_TX_CTL_NO_ACK;
        tx_info->control.rates[0].idx = 0;
        tx_info->control.rates[0].count = 1;
@@ -416,7 +416,7 @@ void ath_start_ani(struct ath_softc *sc)
 
        if (common->disable_ani ||
            !test_bit(ATH_OP_ANI_RUN, &common->op_flags) ||
-           (sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL))
+           sc->cur_chan->offchannel)
                return;
 
        common->ani.longcal_timer = timestamp;
@@ -440,7 +440,7 @@ void ath_check_ani(struct ath_softc *sc)
 {
        struct ath_hw *ah = sc->sc_ah;
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
-       struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf;
+       struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon;
 
        /*
         * Check for the various conditions in which ANI has to
index 62ac95d..f5727c7 100644 (file)
@@ -19,9 +19,6 @@
 #include "ath9k.h"
 #include "btcoex.h"
 
-static void ath9k_set_assoc_state(struct ath_softc *sc,
-                                 struct ieee80211_vif *vif);
-
 u8 ath9k_parse_mpdudensity(u8 mpdudensity)
 {
        /*
@@ -63,9 +60,16 @@ static bool ath9k_has_pending_frames(struct ath_softc *sc, struct ath_txq *txq)
 
        spin_lock_bh(&txq->axq_lock);
 
-       if (txq->axq_depth || !list_empty(&txq->axq_acq))
+       if (txq->axq_depth)
                pending = true;
 
+       if (txq->mac80211_qnum >= 0) {
+               struct list_head *list;
+
+               list = &sc->cur_chan->acq[txq->mac80211_qnum];
+               if (!list_empty(list))
+                       pending = true;
+       }
        spin_unlock_bh(&txq->axq_lock);
        return pending;
 }
@@ -227,13 +231,22 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start)
        }
 
        ath9k_cmn_update_txpow(ah, sc->curtxpow,
-                              sc->config.txpowlimit, &sc->curtxpow);
+                              sc->cur_chan->txpower, &sc->curtxpow);
 
        clear_bit(ATH_OP_HW_RESET, &common->op_flags);
-       ath9k_hw_set_interrupts(ah);
-       ath9k_hw_enable_interrupts(ah);
+       ath9k_calculate_summary_state(sc, sc->cur_chan);
+
+       if (!sc->cur_chan->offchannel && start) {
+               /* restore per chanctx TSF timer */
+               if (sc->cur_chan->tsf_val) {
+                       u32 offset;
+
+                       offset = ath9k_hw_get_tsf_offset(&sc->cur_chan->tsf_ts,
+                                                        NULL);
+                       ath9k_hw_settsf64(ah, sc->cur_chan->tsf_val + offset);
+               }
+
 
-       if (!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL) && start) {
                if (!test_bit(ATH_OP_BEACONS, &common->op_flags))
                        goto work;
 
@@ -247,26 +260,35 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start)
                }
        work:
                ath_restart_work(sc);
+               ath_txq_schedule_all(sc);
+       }
 
-               for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
-                       if (!ATH_TXQ_SETUP(sc, i))
-                               continue;
+       sc->gtt_cnt = 0;
 
-                       spin_lock_bh(&sc->tx.txq[i].axq_lock);
-                       ath_txq_schedule(sc, &sc->tx.txq[i]);
-                       spin_unlock_bh(&sc->tx.txq[i].axq_lock);
+       ath9k_hw_set_interrupts(ah);
+       ath9k_hw_enable_interrupts(ah);
+
+       if (!ath9k_use_chanctx)
+               ieee80211_wake_queues(sc->hw);
+       else {
+               if (sc->cur_chan == &sc->offchannel.chan)
+                       ieee80211_wake_queue(sc->hw,
+                                       sc->hw->offchannel_tx_hw_queue);
+               else {
+                       for (i = 0; i < IEEE80211_NUM_ACS; i++)
+                               ieee80211_wake_queue(sc->hw,
+                                       sc->cur_chan->hw_queue_base + i);
                }
+               if (ah->opmode == NL80211_IFTYPE_AP)
+                       ieee80211_wake_queue(sc->hw, sc->hw->queues - 2);
        }
 
-       sc->gtt_cnt = 0;
-       ieee80211_wake_queues(sc->hw);
-
        ath9k_p2p_ps_timer(sc);
 
        return true;
 }
 
-static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan)
+int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan)
 {
        struct ath_hw *ah = sc->sc_ah;
        struct ath_common *common = ath9k_hw_common(ah);
@@ -279,9 +301,9 @@ static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan)
        tasklet_disable(&sc->intr_tq);
        spin_lock_bh(&sc->sc_pcu_lock);
 
-       if (!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)) {
+       if (!sc->cur_chan->offchannel) {
                fastcc = false;
-               caldata = &sc->caldata;
+               caldata = &sc->cur_chan->caldata;
        }
 
        if (!hchan) {
@@ -292,6 +314,10 @@ static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan)
        if (!ath_prepare_reset(sc))
                fastcc = false;
 
+       spin_lock_bh(&sc->chan_lock);
+       sc->cur_chandef = sc->cur_chan->chandef;
+       spin_unlock_bh(&sc->chan_lock);
+
        ath_dbg(common, CONFIG, "Reset to %u MHz, HT40: %d fastcc: %d\n",
                hchan->channel, IS_CHAN_HT40(hchan), fastcc);
 
@@ -307,7 +333,7 @@ static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan)
        }
 
        if (ath9k_hw_mci_is_enabled(sc->sc_ah) &&
-           (sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL))
+           sc->cur_chan->offchannel)
                ath9k_mci_set_txpower(sc, true, false);
 
        if (!ath_complete_reset(sc, true))
@@ -320,98 +346,6 @@ out:
        return r;
 }
 
-
-/*
- * Set/change channels.  If the channel is really being changed, it's done
- * by reseting the chip.  To accomplish this we must first cleanup any pending
- * DMA, then restart stuff.
-*/
-static int ath_set_channel(struct ath_softc *sc, struct cfg80211_chan_def *chandef)
-{
-       struct ath_hw *ah = sc->sc_ah;
-       struct ath_common *common = ath9k_hw_common(ah);
-       struct ieee80211_hw *hw = sc->hw;
-       struct ath9k_channel *hchan;
-       struct ieee80211_channel *chan = chandef->chan;
-       bool offchannel;
-       int pos = chan->hw_value;
-       int old_pos = -1;
-       int r;
-
-       if (test_bit(ATH_OP_INVALID, &common->op_flags))
-               return -EIO;
-
-       offchannel = !!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL);
-
-       if (ah->curchan)
-               old_pos = ah->curchan - &ah->channels[0];
-
-       ath_dbg(common, CONFIG, "Set channel: %d MHz width: %d\n",
-               chan->center_freq, chandef->width);
-
-       /* update survey stats for the old channel before switching */
-       spin_lock_bh(&common->cc_lock);
-       ath_update_survey_stats(sc);
-       spin_unlock_bh(&common->cc_lock);
-
-       ath9k_cmn_get_channel(hw, ah, chandef);
-
-       /*
-        * If the operating channel changes, change the survey in-use flags
-        * along with it.
-        * Reset the survey data for the new channel, unless we're switching
-        * back to the operating channel from an off-channel operation.
-        */
-       if (!offchannel && sc->cur_survey != &sc->survey[pos]) {
-               if (sc->cur_survey)
-                       sc->cur_survey->filled &= ~SURVEY_INFO_IN_USE;
-
-               sc->cur_survey = &sc->survey[pos];
-
-               memset(sc->cur_survey, 0, sizeof(struct survey_info));
-               sc->cur_survey->filled |= SURVEY_INFO_IN_USE;
-       } else if (!(sc->survey[pos].filled & SURVEY_INFO_IN_USE)) {
-               memset(&sc->survey[pos], 0, sizeof(struct survey_info));
-       }
-
-       hchan = &sc->sc_ah->channels[pos];
-       r = ath_reset_internal(sc, hchan);
-       if (r)
-               return r;
-
-       /*
-        * The most recent snapshot of channel->noisefloor for the old
-        * channel is only available after the hardware reset. Copy it to
-        * the survey stats now.
-        */
-       if (old_pos >= 0)
-               ath_update_survey_nf(sc, old_pos);
-
-       /*
-        * Enable radar pulse detection if on a DFS channel. Spectral
-        * scanning and radar detection can not be used concurrently.
-        */
-       if (hw->conf.radar_enabled) {
-               u32 rxfilter;
-
-               /* set HW specific DFS configuration */
-               ath9k_hw_set_radar_params(ah);
-               rxfilter = ath9k_hw_getrxfilter(ah);
-               rxfilter |= ATH9K_RX_FILTER_PHYRADAR |
-                               ATH9K_RX_FILTER_PHYERR;
-               ath9k_hw_setrxfilter(ah, rxfilter);
-               ath_dbg(common, DFS, "DFS enabled at freq %d\n",
-                       chan->center_freq);
-       } else {
-               /* perform spectral scan if requested. */
-               if (test_bit(ATH_OP_SCANNING, &common->op_flags) &&
-                       sc->spectral_mode == SPECTRAL_CHANSCAN)
-                       ath9k_spectral_scan_trigger(hw);
-       }
-
-       return 0;
-}
-
 static void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta,
                            struct ieee80211_vif *vif)
 {
@@ -712,7 +646,8 @@ static int ath9k_start(struct ieee80211_hw *hw)
        struct ath_softc *sc = hw->priv;
        struct ath_hw *ah = sc->sc_ah;
        struct ath_common *common = ath9k_hw_common(ah);
-       struct ieee80211_channel *curchan = hw->conf.chandef.chan;
+       struct ieee80211_channel *curchan = sc->cur_chan->chandef.chan;
+       struct ath_chanctx *ctx = sc->cur_chan;
        struct ath9k_channel *init_channel;
        int r;
 
@@ -723,7 +658,8 @@ static int ath9k_start(struct ieee80211_hw *hw)
        ath9k_ps_wakeup(sc);
        mutex_lock(&sc->mutex);
 
-       init_channel = ath9k_cmn_get_channel(hw, ah, &hw->conf.chandef);
+       init_channel = ath9k_cmn_get_channel(hw, ah, &ctx->chandef);
+       sc->cur_chandef = hw->conf.chandef;
 
        /* Reset SERDES registers */
        ath9k_hw_configpcipowersave(ah, false);
@@ -886,6 +822,7 @@ static void ath9k_stop(struct ieee80211_hw *hw)
        struct ath_common *common = ath9k_hw_common(ah);
        bool prev_idle;
 
+       cancel_work_sync(&sc->chanctx_work);
        mutex_lock(&sc->mutex);
 
        ath_cancel_work(sc);
@@ -934,7 +871,8 @@ static void ath9k_stop(struct ieee80211_hw *hw)
        }
 
        if (!ah->curchan)
-               ah->curchan = ath9k_cmn_get_channel(hw, ah, &hw->conf.chandef);
+               ah->curchan = ath9k_cmn_get_channel(hw, ah,
+                                                   &sc->cur_chan->chandef);
 
        ath9k_hw_reset(ah, ah->curchan, ah->caldata, false);
        ath9k_hw_phy_disable(ah);
@@ -979,18 +917,29 @@ static void ath9k_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
                iter_data->has_hw_macaddr = true;
        }
 
+       if (!vif->bss_conf.use_short_slot)
+               iter_data->slottime = ATH9K_SLOT_TIME_20;
+
        switch (vif->type) {
        case NL80211_IFTYPE_AP:
                iter_data->naps++;
+               if (vif->bss_conf.enable_beacon)
+                       iter_data->beacons = true;
                break;
        case NL80211_IFTYPE_STATION:
                iter_data->nstations++;
+               if (vif->bss_conf.assoc && !iter_data->primary_sta)
+                       iter_data->primary_sta = vif;
                break;
        case NL80211_IFTYPE_ADHOC:
                iter_data->nadhocs++;
+               if (vif->bss_conf.enable_beacon)
+                       iter_data->beacons = true;
                break;
        case NL80211_IFTYPE_MESH_POINT:
                iter_data->nmeshes++;
+               if (vif->bss_conf.enable_beacon)
+                       iter_data->beacons = true;
                break;
        case NL80211_IFTYPE_WDS:
                iter_data->nwds++;
@@ -1000,26 +949,12 @@ static void ath9k_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
        }
 }
 
-static void ath9k_sta_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
-{
-       struct ath_softc *sc = data;
-       struct ath_vif *avp = (void *)vif->drv_priv;
-
-       if (vif->type != NL80211_IFTYPE_STATION)
-               return;
-
-       if (avp->primary_sta_vif)
-               ath9k_set_assoc_state(sc, vif);
-}
-
 /* Called with sc->mutex held. */
-void ath9k_calculate_iter_data(struct ieee80211_hw *hw,
-                              struct ieee80211_vif *vif,
+void ath9k_calculate_iter_data(struct ath_softc *sc,
+                              struct ath_chanctx *ctx,
                               struct ath9k_vif_iter_data *iter_data)
 {
-       struct ath_softc *sc = hw->priv;
-       struct ath_hw *ah = sc->sc_ah;
-       struct ath_common *common = ath9k_hw_common(ah);
+       struct ath_vif *avp;
 
        /*
         * Pick the MAC address of the first interface as the new hardware
@@ -1028,29 +963,80 @@ void ath9k_calculate_iter_data(struct ieee80211_hw *hw,
         */
        memset(iter_data, 0, sizeof(*iter_data));
        memset(&iter_data->mask, 0xff, ETH_ALEN);
+       iter_data->slottime = ATH9K_SLOT_TIME_9;
+
+       list_for_each_entry(avp, &ctx->vifs, list)
+               ath9k_vif_iter(iter_data, avp->vif->addr, avp->vif);
+
+       if (ctx == &sc->offchannel.chan) {
+               struct ieee80211_vif *vif;
+
+               if (sc->offchannel.state < ATH_OFFCHANNEL_ROC_START)
+                       vif = sc->offchannel.scan_vif;
+               else
+                       vif = sc->offchannel.roc_vif;
+
+               if (vif)
+                       ath9k_vif_iter(iter_data, vif->addr, vif);
+               iter_data->beacons = false;
+       }
+}
+
+static void ath9k_set_assoc_state(struct ath_softc *sc,
+                                 struct ieee80211_vif *vif, bool changed)
+{
+       struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+       struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
+       unsigned long flags;
 
-       if (vif)
-               ath9k_vif_iter(iter_data, vif->addr, vif);
+       set_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags);
+       /* Set the AID, BSSID and do beacon-sync only when
+        * the HW opmode is STATION.
+        *
+        * But the primary bit is set above in any case.
+        */
+       if (sc->sc_ah->opmode != NL80211_IFTYPE_STATION)
+               return;
+
+       ether_addr_copy(common->curbssid, bss_conf->bssid);
+       common->curaid = bss_conf->aid;
+       ath9k_hw_write_associd(sc->sc_ah);
 
-       /* Get list of all active MAC addresses */
-       ieee80211_iterate_active_interfaces_atomic(
-               sc->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
-               ath9k_vif_iter, iter_data);
+       if (changed) {
+               common->last_rssi = ATH_RSSI_DUMMY_MARKER;
+               sc->sc_ah->stats.avgbrssi = ATH_RSSI_DUMMY_MARKER;
 
-       memcpy(common->macaddr, iter_data->hw_macaddr, ETH_ALEN);
+               spin_lock_irqsave(&sc->sc_pm_lock, flags);
+               sc->ps_flags |= PS_BEACON_SYNC | PS_WAIT_FOR_BEACON;
+               spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
+       }
+
+       if (ath9k_hw_mci_is_enabled(sc->sc_ah))
+               ath9k_mci_update_wlan_channels(sc, false);
+
+       ath_dbg(common, CONFIG,
+               "Primary Station interface: %pM, BSSID: %pM\n",
+               vif->addr, common->curbssid);
 }
 
 /* Called with sc->mutex held. */
-static void ath9k_calculate_summary_state(struct ieee80211_hw *hw,
-                                         struct ieee80211_vif *vif)
+void ath9k_calculate_summary_state(struct ath_softc *sc,
+                                  struct ath_chanctx *ctx)
 {
-       struct ath_softc *sc = hw->priv;
        struct ath_hw *ah = sc->sc_ah;
        struct ath_common *common = ath9k_hw_common(ah);
        struct ath9k_vif_iter_data iter_data;
-       enum nl80211_iftype old_opmode = ah->opmode;
 
-       ath9k_calculate_iter_data(hw, vif, &iter_data);
+       ath_chanctx_check_active(sc, ctx);
+
+       if (ctx != sc->cur_chan)
+               return;
+
+       ath9k_ps_wakeup(sc);
+       ath9k_calculate_iter_data(sc, ctx, &iter_data);
+
+       if (iter_data.has_hw_macaddr)
+               ether_addr_copy(common->macaddr, iter_data.hw_macaddr);
 
        memcpy(common->bssidmask, iter_data.mask, ETH_ALEN);
        ath_hw_setbssidmask(common);
@@ -1073,24 +1059,57 @@ static void ath9k_calculate_summary_state(struct ieee80211_hw *hw,
 
        ath9k_hw_setopmode(ah);
 
+       ctx->switch_after_beacon = false;
        if ((iter_data.nstations + iter_data.nadhocs + iter_data.nmeshes) > 0)
                ah->imask |= ATH9K_INT_TSFOOR;
-       else
+       else {
                ah->imask &= ~ATH9K_INT_TSFOOR;
+               if (iter_data.naps == 1 && iter_data.beacons)
+                       ctx->switch_after_beacon = true;
+       }
+
+       ah->imask &= ~ATH9K_INT_SWBA;
+       if (ah->opmode == NL80211_IFTYPE_STATION) {
+               bool changed = (iter_data.primary_sta != ctx->primary_sta);
 
+               iter_data.beacons = true;
+               if (iter_data.primary_sta) {
+                       ath9k_set_assoc_state(sc, iter_data.primary_sta,
+                                             changed);
+                       if (!ctx->primary_sta ||
+                           !ctx->primary_sta->bss_conf.assoc)
+                               ctx->primary_sta = iter_data.primary_sta;
+               } else {
+                       ctx->primary_sta = NULL;
+                       memset(common->curbssid, 0, ETH_ALEN);
+                       common->curaid = 0;
+                       ath9k_hw_write_associd(sc->sc_ah);
+                       if (ath9k_hw_mci_is_enabled(sc->sc_ah))
+                               ath9k_mci_update_wlan_channels(sc, true);
+               }
+       } else if (iter_data.beacons) {
+               ah->imask |= ATH9K_INT_SWBA;
+       }
        ath9k_hw_set_interrupts(ah);
 
-       /*
-        * If we are changing the opmode to STATION,
-        * a beacon sync needs to be done.
-        */
-       if (ah->opmode == NL80211_IFTYPE_STATION &&
-           old_opmode == NL80211_IFTYPE_AP &&
-           test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags)) {
-               ieee80211_iterate_active_interfaces_atomic(
-                       sc->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
-                       ath9k_sta_vif_iter, sc);
+       if (iter_data.beacons)
+               set_bit(ATH_OP_BEACONS, &common->op_flags);
+       else
+               clear_bit(ATH_OP_BEACONS, &common->op_flags);
+
+       if (ah->slottime != iter_data.slottime) {
+               ah->slottime = iter_data.slottime;
+               ath9k_hw_init_global_settings(ah);
        }
+
+       if (iter_data.primary_sta)
+               set_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags);
+       else
+               clear_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags);
+
+       ctx->primary_sta = iter_data.primary_sta;
+
+       ath9k_ps_restore(sc);
 }
 
 static int ath9k_add_interface(struct ieee80211_hw *hw,
@@ -1101,6 +1120,7 @@ static int ath9k_add_interface(struct ieee80211_hw *hw,
        struct ath_common *common = ath9k_hw_common(ah);
        struct ath_vif *avp = (void *)vif->drv_priv;
        struct ath_node *an = &avp->mcast_node;
+       int i;
 
        mutex_lock(&sc->mutex);
 
@@ -1115,14 +1135,20 @@ static int ath9k_add_interface(struct ieee80211_hw *hw,
        ath_dbg(common, CONFIG, "Attach a VIF of type: %d\n", vif->type);
        sc->nvifs++;
 
-       ath9k_ps_wakeup(sc);
-       ath9k_calculate_summary_state(hw, vif);
-       ath9k_ps_restore(sc);
-
        if (ath9k_uses_beacons(vif->type))
                ath9k_beacon_assign_slot(sc, vif);
 
        avp->vif = vif;
+       if (!ath9k_use_chanctx) {
+               avp->chanctx = sc->cur_chan;
+               list_add_tail(&avp->list, &avp->chanctx->vifs);
+       }
+       for (i = 0; i < IEEE80211_NUM_ACS; i++)
+               vif->hw_queue[i] = i;
+       if (vif->type == NL80211_IFTYPE_AP)
+               vif->cab_queue = hw->queues - 2;
+       else
+               vif->cab_queue = IEEE80211_INVAL_HW_QUEUE;
 
        an->sc = sc;
        an->sta = NULL;
@@ -1141,6 +1167,8 @@ static int ath9k_change_interface(struct ieee80211_hw *hw,
 {
        struct ath_softc *sc = hw->priv;
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+       struct ath_vif *avp = (void *)vif->drv_priv;
+       int i;
 
        mutex_lock(&sc->mutex);
 
@@ -1157,13 +1185,19 @@ static int ath9k_change_interface(struct ieee80211_hw *hw,
        vif->type = new_type;
        vif->p2p = p2p;
 
-       ath9k_ps_wakeup(sc);
-       ath9k_calculate_summary_state(hw, vif);
-       ath9k_ps_restore(sc);
-
        if (ath9k_uses_beacons(vif->type))
                ath9k_beacon_assign_slot(sc, vif);
 
+       for (i = 0; i < IEEE80211_NUM_ACS; i++)
+               vif->hw_queue[i] = i;
+
+       if (vif->type == NL80211_IFTYPE_AP)
+               vif->cab_queue = hw->queues - 2;
+       else
+               vif->cab_queue = IEEE80211_INVAL_HW_QUEUE;
+
+       ath9k_calculate_summary_state(sc, avp->chanctx);
+
        mutex_unlock(&sc->mutex);
        return 0;
 }
@@ -1211,14 +1245,12 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw,
 
        sc->nvifs--;
        sc->tx99_vif = NULL;
+       if (!ath9k_use_chanctx)
+               list_del(&avp->list);
 
        if (ath9k_uses_beacons(vif->type))
                ath9k_beacon_remove_slot(sc, vif);
 
-       ath9k_ps_wakeup(sc);
-       ath9k_calculate_summary_state(hw, NULL);
-       ath9k_ps_restore(sc);
-
        ath_tx_node_cleanup(sc, &avp->mcast_node);
 
        mutex_unlock(&sc->mutex);
@@ -1345,7 +1377,7 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
        struct ath_hw *ah = sc->sc_ah;
        struct ath_common *common = ath9k_hw_common(ah);
        struct ieee80211_conf *conf = &hw->conf;
-       bool reset_channel = false;
+       struct ath_chanctx *ctx = sc->cur_chan;
 
        ath9k_ps_wakeup(sc);
        mutex_lock(&sc->mutex);
@@ -1361,7 +1393,7 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
                         * The chip needs a reset to properly wake up from
                         * full sleep
                         */
-                       reset_channel = ah->chip_fullsleep;
+                       ath_chanctx_set_channel(sc, ctx, &ctx->chandef);
                }
        }
 
@@ -1391,20 +1423,16 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
                }
        }
 
-       if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) || reset_channel) {
-               if (ath_set_channel(sc, &hw->conf.chandef) < 0) {
-                       ath_err(common, "Unable to set channel\n");
-                       mutex_unlock(&sc->mutex);
-                       ath9k_ps_restore(sc);
-                       return -EINVAL;
-               }
+       if (!ath9k_use_chanctx && (changed & IEEE80211_CONF_CHANGE_CHANNEL)) {
+               ctx->offchannel = !!(conf->flags & IEEE80211_CONF_OFFCHANNEL);
+               ath_chanctx_set_channel(sc, ctx, &hw->conf.chandef);
        }
 
        if (changed & IEEE80211_CONF_CHANGE_POWER) {
                ath_dbg(common, CONFIG, "Set power: %d\n", conf->power_level);
-               sc->config.txpowlimit = 2 * conf->power_level;
+               sc->cur_chan->txpower = 2 * conf->power_level;
                ath9k_cmn_update_txpow(ah, sc->curtxpow,
-                                      sc->config.txpowlimit, &sc->curtxpow);
+                                      sc->cur_chan->txpower, &sc->curtxpow);
        }
 
        mutex_unlock(&sc->mutex);
@@ -1659,58 +1687,6 @@ static int ath9k_set_key(struct ieee80211_hw *hw,
        return ret;
 }
 
-static void ath9k_set_assoc_state(struct ath_softc *sc,
-                                 struct ieee80211_vif *vif)
-{
-       struct ath_common *common = ath9k_hw_common(sc->sc_ah);
-       struct ath_vif *avp = (void *)vif->drv_priv;
-       struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
-       unsigned long flags;
-
-       set_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags);
-       avp->primary_sta_vif = true;
-
-       /*
-        * Set the AID, BSSID and do beacon-sync only when
-        * the HW opmode is STATION.
-        *
-        * But the primary bit is set above in any case.
-        */
-       if (sc->sc_ah->opmode != NL80211_IFTYPE_STATION)
-               return;
-
-       memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
-       common->curaid = bss_conf->aid;
-       ath9k_hw_write_associd(sc->sc_ah);
-
-       common->last_rssi = ATH_RSSI_DUMMY_MARKER;
-       sc->sc_ah->stats.avgbrssi = ATH_RSSI_DUMMY_MARKER;
-
-       spin_lock_irqsave(&sc->sc_pm_lock, flags);
-       sc->ps_flags |= PS_BEACON_SYNC | PS_WAIT_FOR_BEACON;
-       spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
-
-       if (ath9k_hw_mci_is_enabled(sc->sc_ah))
-               ath9k_mci_update_wlan_channels(sc, false);
-
-       ath_dbg(common, CONFIG,
-               "Primary Station interface: %pM, BSSID: %pM\n",
-               vif->addr, common->curbssid);
-}
-
-static void ath9k_bss_assoc_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
-{
-       struct ath_softc *sc = data;
-       struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
-       struct ath_common *common = ath9k_hw_common(sc->sc_ah);
-
-       if (test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags))
-               return;
-
-       if (bss_conf->assoc)
-               ath9k_set_assoc_state(sc, vif);
-}
-
 void ath9k_p2p_ps_timer(void *priv)
 {
        struct ath_softc *sc = priv;
@@ -1720,7 +1696,11 @@ void ath9k_p2p_ps_timer(void *priv)
        struct ath_node *an;
        u32 tsf;
 
-       if (!avp)
+       del_timer_sync(&sc->sched.timer);
+       ath9k_hw_gen_timer_stop(sc->sc_ah, sc->p2p_ps_timer);
+       ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_TSF_TIMER);
+
+       if (!avp || avp->chanctx != sc->cur_chan)
                return;
 
        tsf = ath9k_hw_gettsf32(sc->sc_ah);
@@ -1795,26 +1775,9 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw,
                ath_dbg(common, CONFIG, "BSSID %pM Changed ASSOC %d\n",
                        bss_conf->bssid, bss_conf->assoc);
 
-               if (avp->primary_sta_vif && !bss_conf->assoc) {
-                       clear_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags);
-                       avp->primary_sta_vif = false;
-
-                       if (ah->opmode == NL80211_IFTYPE_STATION)
-                               clear_bit(ATH_OP_BEACONS, &common->op_flags);
-               }
-
-               ieee80211_iterate_active_interfaces_atomic(
-                       sc->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
-                       ath9k_bss_assoc_iter, sc);
-
-               if (!test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags) &&
-                   ah->opmode == NL80211_IFTYPE_STATION) {
-                       memset(common->curbssid, 0, ETH_ALEN);
-                       common->curaid = 0;
-                       ath9k_hw_write_associd(sc->sc_ah);
-                       if (ath9k_hw_mci_is_enabled(sc->sc_ah))
-                               ath9k_mci_update_wlan_channels(sc, true);
-               }
+               ath9k_calculate_summary_state(sc, avp->chanctx);
+               if (bss_conf->assoc)
+                       ath_chanctx_event(sc, vif, ATH_CHANCTX_EVENT_ASSOC);
        }
 
        if (changed & BSS_CHANGED_IBSS) {
@@ -1824,10 +1787,14 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw,
        }
 
        if ((changed & BSS_CHANGED_BEACON_ENABLED) ||
-           (changed & BSS_CHANGED_BEACON_INT))
+           (changed & BSS_CHANGED_BEACON_INT)) {
+               if (changed & BSS_CHANGED_BEACON_ENABLED)
+                       ath9k_calculate_summary_state(sc, avp->chanctx);
                ath9k_beacon_config(sc, vif, changed);
+       }
 
-       if (changed & BSS_CHANGED_ERP_SLOT) {
+       if ((avp->chanctx == sc->cur_chan) &&
+           (changed & BSS_CHANGED_ERP_SLOT)) {
                if (bss_conf->use_short_slot)
                        slottime = 9;
                else
@@ -2030,25 +1997,32 @@ static bool ath9k_has_tx_pending(struct ath_softc *sc)
 
 static void ath9k_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                        u32 queues, bool drop)
+{
+       struct ath_softc *sc = hw->priv;
+
+       mutex_lock(&sc->mutex);
+       __ath9k_flush(hw, queues, drop);
+       mutex_unlock(&sc->mutex);
+}
+
+void __ath9k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
 {
        struct ath_softc *sc = hw->priv;
        struct ath_hw *ah = sc->sc_ah;
        struct ath_common *common = ath9k_hw_common(ah);
        int timeout = HZ / 5; /* 200 ms */
        bool drain_txq;
+       int i;
 
-       mutex_lock(&sc->mutex);
        cancel_delayed_work_sync(&sc->tx_complete_work);
 
        if (ah->ah_flags & AH_UNPLUGGED) {
                ath_dbg(common, ANY, "Device has been unplugged!\n");
-               mutex_unlock(&sc->mutex);
                return;
        }
 
        if (test_bit(ATH_OP_INVALID, &common->op_flags)) {
                ath_dbg(common, ANY, "Device not present\n");
-               mutex_unlock(&sc->mutex);
                return;
        }
 
@@ -2066,11 +2040,13 @@ static void ath9k_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                        ath_reset(sc);
 
                ath9k_ps_restore(sc);
-               ieee80211_wake_queues(hw);
+               for (i = 0; i < IEEE80211_NUM_ACS; i++) {
+                       ieee80211_wake_queue(sc->hw,
+                                            sc->cur_chan->hw_queue_base + i);
+               }
        }
 
        ieee80211_queue_delayed_work(hw, &sc->tx_complete_work, 0);
-       mutex_unlock(&sc->mutex);
 }
 
 static bool ath9k_tx_frames_pending(struct ieee80211_hw *hw)
@@ -2230,6 +2206,403 @@ static void ath9k_sw_scan_complete(struct ieee80211_hw *hw)
        clear_bit(ATH_OP_SCANNING, &common->op_flags);
 }
 
+static int ath_scan_channel_duration(struct ath_softc *sc,
+                                    struct ieee80211_channel *chan)
+{
+       struct cfg80211_scan_request *req = sc->offchannel.scan_req;
+
+       if (!req->n_ssids || (chan->flags & IEEE80211_CHAN_NO_IR))
+               return (HZ / 9); /* ~110 ms */
+
+       return (HZ / 16); /* ~60 ms */
+}
+
+static void
+ath_scan_next_channel(struct ath_softc *sc)
+{
+       struct cfg80211_scan_request *req = sc->offchannel.scan_req;
+       struct ieee80211_channel *chan;
+
+       if (sc->offchannel.scan_idx >= req->n_channels) {
+               sc->offchannel.state = ATH_OFFCHANNEL_IDLE;
+               ath_chanctx_switch(sc, ath_chanctx_get_oper_chan(sc, false),
+                                  NULL);
+               return;
+       }
+
+       chan = req->channels[sc->offchannel.scan_idx++];
+       sc->offchannel.duration = ath_scan_channel_duration(sc, chan);
+       sc->offchannel.state = ATH_OFFCHANNEL_PROBE_SEND;
+       ath_chanctx_offchan_switch(sc, chan);
+}
+
+static void ath_offchannel_next(struct ath_softc *sc)
+{
+       struct ieee80211_vif *vif;
+
+       if (sc->offchannel.scan_req) {
+               vif = sc->offchannel.scan_vif;
+               sc->offchannel.chan.txpower = vif->bss_conf.txpower;
+               ath_scan_next_channel(sc);
+       } else if (sc->offchannel.roc_vif) {
+               vif = sc->offchannel.roc_vif;
+               sc->offchannel.chan.txpower = vif->bss_conf.txpower;
+               sc->offchannel.duration = sc->offchannel.roc_duration;
+               sc->offchannel.state = ATH_OFFCHANNEL_ROC_START;
+               ath_chanctx_offchan_switch(sc, sc->offchannel.roc_chan);
+       } else {
+               ath_chanctx_switch(sc, ath_chanctx_get_oper_chan(sc, false),
+                                  NULL);
+               sc->offchannel.state = ATH_OFFCHANNEL_IDLE;
+               if (sc->ps_idle)
+                       ath_cancel_work(sc);
+       }
+}
+
+static void ath_roc_complete(struct ath_softc *sc, bool abort)
+{
+       sc->offchannel.roc_vif = NULL;
+       sc->offchannel.roc_chan = NULL;
+       if (!abort)
+               ieee80211_remain_on_channel_expired(sc->hw);
+       ath_offchannel_next(sc);
+       ath9k_ps_restore(sc);
+}
+
+static void ath_scan_complete(struct ath_softc *sc, bool abort)
+{
+       struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+
+       sc->offchannel.scan_req = NULL;
+       sc->offchannel.scan_vif = NULL;
+       sc->offchannel.state = ATH_OFFCHANNEL_IDLE;
+       ieee80211_scan_completed(sc->hw, abort);
+       clear_bit(ATH_OP_SCANNING, &common->op_flags);
+       ath_offchannel_next(sc);
+       ath9k_ps_restore(sc);
+}
+
+static void ath_scan_send_probe(struct ath_softc *sc,
+                               struct cfg80211_ssid *ssid)
+{
+       struct cfg80211_scan_request *req = sc->offchannel.scan_req;
+       struct ieee80211_vif *vif = sc->offchannel.scan_vif;
+       struct ath_tx_control txctl = {};
+       struct sk_buff *skb;
+       struct ieee80211_tx_info *info;
+       int band = sc->offchannel.chan.chandef.chan->band;
+
+       skb = ieee80211_probereq_get(sc->hw, vif,
+                       ssid->ssid, ssid->ssid_len, req->ie_len);
+       if (!skb)
+               return;
+
+       info = IEEE80211_SKB_CB(skb);
+       if (req->no_cck)
+               info->flags |= IEEE80211_TX_CTL_NO_CCK_RATE;
+
+       if (req->ie_len)
+               memcpy(skb_put(skb, req->ie_len), req->ie, req->ie_len);
+
+       skb_set_queue_mapping(skb, IEEE80211_AC_VO);
+
+       if (!ieee80211_tx_prepare_skb(sc->hw, vif, skb, band, NULL))
+               goto error;
+
+       txctl.txq = sc->tx.txq_map[IEEE80211_AC_VO];
+       txctl.force_channel = true;
+       if (ath_tx_start(sc->hw, skb, &txctl))
+               goto error;
+
+       return;
+
+error:
+       ieee80211_free_txskb(sc->hw, skb);
+}
+
+static void ath_scan_channel_start(struct ath_softc *sc)
+{
+       struct cfg80211_scan_request *req = sc->offchannel.scan_req;
+       int i;
+
+       if (!(sc->cur_chan->chandef.chan->flags & IEEE80211_CHAN_NO_IR) &&
+           req->n_ssids) {
+               for (i = 0; i < req->n_ssids; i++)
+                       ath_scan_send_probe(sc, &req->ssids[i]);
+
+       }
+
+       sc->offchannel.state = ATH_OFFCHANNEL_PROBE_WAIT;
+       mod_timer(&sc->offchannel.timer, jiffies + sc->offchannel.duration);
+}
+
+void ath_offchannel_channel_change(struct ath_softc *sc)
+{
+       switch (sc->offchannel.state) {
+       case ATH_OFFCHANNEL_PROBE_SEND:
+               if (!sc->offchannel.scan_req)
+                       return;
+
+               if (sc->cur_chan->chandef.chan !=
+                   sc->offchannel.chan.chandef.chan)
+                       return;
+
+               ath_scan_channel_start(sc);
+               break;
+       case ATH_OFFCHANNEL_IDLE:
+               if (!sc->offchannel.scan_req)
+                       return;
+
+               ath_scan_complete(sc, false);
+               break;
+       case ATH_OFFCHANNEL_ROC_START:
+               if (sc->cur_chan != &sc->offchannel.chan)
+                       break;
+
+               sc->offchannel.state = ATH_OFFCHANNEL_ROC_WAIT;
+               mod_timer(&sc->offchannel.timer, jiffies +
+                         msecs_to_jiffies(sc->offchannel.duration));
+               ieee80211_ready_on_channel(sc->hw);
+               break;
+       case ATH_OFFCHANNEL_ROC_DONE:
+               ath_roc_complete(sc, false);
+               break;
+       default:
+               break;
+       }
+}
+
+void ath_offchannel_timer(unsigned long data)
+{
+       struct ath_softc *sc = (struct ath_softc *)data;
+       struct ath_chanctx *ctx;
+
+       switch (sc->offchannel.state) {
+       case ATH_OFFCHANNEL_PROBE_WAIT:
+               if (!sc->offchannel.scan_req)
+                       return;
+
+               /* get first active channel context */
+               ctx = ath_chanctx_get_oper_chan(sc, true);
+               if (ctx->active) {
+                       sc->offchannel.state = ATH_OFFCHANNEL_SUSPEND;
+                       ath_chanctx_switch(sc, ctx, NULL);
+                       mod_timer(&sc->offchannel.timer, jiffies + HZ / 10);
+                       break;
+               }
+               /* fall through */
+       case ATH_OFFCHANNEL_SUSPEND:
+               if (!sc->offchannel.scan_req)
+                       return;
+
+               ath_scan_next_channel(sc);
+               break;
+       case ATH_OFFCHANNEL_ROC_START:
+       case ATH_OFFCHANNEL_ROC_WAIT:
+               ctx = ath_chanctx_get_oper_chan(sc, false);
+               sc->offchannel.state = ATH_OFFCHANNEL_ROC_DONE;
+               ath_chanctx_switch(sc, ctx, NULL);
+               break;
+       default:
+               break;
+       }
+}
+
+static int ath9k_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                        struct ieee80211_scan_request *hw_req)
+{
+       struct cfg80211_scan_request *req = &hw_req->req;
+       struct ath_softc *sc = hw->priv;
+       struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+       int ret = 0;
+
+       mutex_lock(&sc->mutex);
+
+       if (WARN_ON(sc->offchannel.scan_req)) {
+               ret = -EBUSY;
+               goto out;
+       }
+
+       ath9k_ps_wakeup(sc);
+       set_bit(ATH_OP_SCANNING, &common->op_flags);
+       sc->offchannel.scan_vif = vif;
+       sc->offchannel.scan_req = req;
+       sc->offchannel.scan_idx = 0;
+
+       if (sc->offchannel.state == ATH_OFFCHANNEL_IDLE)
+               ath_offchannel_next(sc);
+
+out:
+       mutex_unlock(&sc->mutex);
+
+       return ret;
+}
+
+static void ath9k_cancel_hw_scan(struct ieee80211_hw *hw,
+                                struct ieee80211_vif *vif)
+{
+       struct ath_softc *sc = hw->priv;
+
+       mutex_lock(&sc->mutex);
+       del_timer_sync(&sc->offchannel.timer);
+       ath_scan_complete(sc, true);
+       mutex_unlock(&sc->mutex);
+}
+
+static int ath9k_remain_on_channel(struct ieee80211_hw *hw,
+                                  struct ieee80211_vif *vif,
+                                  struct ieee80211_channel *chan, int duration,
+                                  enum ieee80211_roc_type type)
+{
+       struct ath_softc *sc = hw->priv;
+       int ret = 0;
+
+       mutex_lock(&sc->mutex);
+
+       if (WARN_ON(sc->offchannel.roc_vif)) {
+               ret = -EBUSY;
+               goto out;
+       }
+
+       ath9k_ps_wakeup(sc);
+       sc->offchannel.roc_vif = vif;
+       sc->offchannel.roc_chan = chan;
+       sc->offchannel.roc_duration = duration;
+
+       if (sc->offchannel.state == ATH_OFFCHANNEL_IDLE)
+               ath_offchannel_next(sc);
+
+out:
+       mutex_unlock(&sc->mutex);
+
+       return ret;
+}
+
+static int ath9k_cancel_remain_on_channel(struct ieee80211_hw *hw)
+{
+       struct ath_softc *sc = hw->priv;
+
+       mutex_lock(&sc->mutex);
+
+       del_timer_sync(&sc->offchannel.timer);
+
+       if (sc->offchannel.roc_vif) {
+               if (sc->offchannel.state >= ATH_OFFCHANNEL_ROC_START)
+                       ath_roc_complete(sc, true);
+       }
+
+       mutex_unlock(&sc->mutex);
+
+       return 0;
+}
+
+static int ath9k_add_chanctx(struct ieee80211_hw *hw,
+                            struct ieee80211_chanctx_conf *conf)
+{
+       struct ath_softc *sc = hw->priv;
+       struct ath_chanctx *ctx, **ptr;
+       int pos;
+
+       mutex_lock(&sc->mutex);
+
+       ath_for_each_chanctx(sc, ctx) {
+               if (ctx->assigned)
+                       continue;
+
+               ptr = (void *) conf->drv_priv;
+               *ptr = ctx;
+               ctx->assigned = true;
+               pos = ctx - &sc->chanctx[0];
+               ctx->hw_queue_base = pos * IEEE80211_NUM_ACS;
+               ath_chanctx_set_channel(sc, ctx, &conf->def);
+               mutex_unlock(&sc->mutex);
+               return 0;
+       }
+       mutex_unlock(&sc->mutex);
+       return -ENOSPC;
+}
+
+
+static void ath9k_remove_chanctx(struct ieee80211_hw *hw,
+                                struct ieee80211_chanctx_conf *conf)
+{
+       struct ath_softc *sc = hw->priv;
+       struct ath_chanctx *ctx = ath_chanctx_get(conf);
+
+       mutex_lock(&sc->mutex);
+       ctx->assigned = false;
+       ctx->hw_queue_base = -1;
+       ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_UNASSIGN);
+       mutex_unlock(&sc->mutex);
+}
+
+static void ath9k_change_chanctx(struct ieee80211_hw *hw,
+                                struct ieee80211_chanctx_conf *conf,
+                                u32 changed)
+{
+       struct ath_softc *sc = hw->priv;
+       struct ath_chanctx *ctx = ath_chanctx_get(conf);
+
+       mutex_lock(&sc->mutex);
+       ath_chanctx_set_channel(sc, ctx, &conf->def);
+       mutex_unlock(&sc->mutex);
+}
+
+static int ath9k_assign_vif_chanctx(struct ieee80211_hw *hw,
+                                   struct ieee80211_vif *vif,
+                                   struct ieee80211_chanctx_conf *conf)
+{
+       struct ath_softc *sc = hw->priv;
+       struct ath_vif *avp = (void *)vif->drv_priv;
+       struct ath_chanctx *ctx = ath_chanctx_get(conf);
+       int i;
+
+       mutex_lock(&sc->mutex);
+       avp->chanctx = ctx;
+       list_add_tail(&avp->list, &ctx->vifs);
+       ath9k_calculate_summary_state(sc, ctx);
+       for (i = 0; i < IEEE80211_NUM_ACS; i++)
+               vif->hw_queue[i] = ctx->hw_queue_base + i;
+       mutex_unlock(&sc->mutex);
+
+       return 0;
+}
+
+static void ath9k_unassign_vif_chanctx(struct ieee80211_hw *hw,
+                                      struct ieee80211_vif *vif,
+                                      struct ieee80211_chanctx_conf *conf)
+{
+       struct ath_softc *sc = hw->priv;
+       struct ath_vif *avp = (void *)vif->drv_priv;
+       struct ath_chanctx *ctx = ath_chanctx_get(conf);
+       int ac;
+
+       mutex_lock(&sc->mutex);
+       avp->chanctx = NULL;
+       list_del(&avp->list);
+       ath9k_calculate_summary_state(sc, ctx);
+       for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
+               vif->hw_queue[ac] = IEEE80211_INVAL_HW_QUEUE;
+       mutex_unlock(&sc->mutex);
+}
+
+void ath9k_fill_chanctx_ops(void)
+{
+       if (!ath9k_use_chanctx)
+               return;
+
+       ath9k_ops.hw_scan = ath9k_hw_scan;
+       ath9k_ops.cancel_hw_scan = ath9k_cancel_hw_scan;
+       ath9k_ops.remain_on_channel  = ath9k_remain_on_channel;
+       ath9k_ops.cancel_remain_on_channel = ath9k_cancel_remain_on_channel;
+       ath9k_ops.add_chanctx        = ath9k_add_chanctx;
+       ath9k_ops.remove_chanctx     = ath9k_remove_chanctx;
+       ath9k_ops.change_chanctx     = ath9k_change_chanctx;
+       ath9k_ops.assign_vif_chanctx = ath9k_assign_vif_chanctx;
+       ath9k_ops.unassign_vif_chanctx = ath9k_unassign_vif_chanctx;
+       ath9k_ops.mgd_prepare_tx = ath9k_chanctx_force_active;
+}
+
 struct ieee80211_ops ath9k_ops = {
        .tx                 = ath9k_tx,
        .start              = ath9k_start,
index a0dbcc4..3f7a11e 100644 (file)
@@ -706,7 +706,7 @@ void ath9k_mci_set_txpower(struct ath_softc *sc, bool setchannel,
                return;
 
        if (setchannel) {
-               struct ath9k_hw_cal_data *caldata = &sc->caldata;
+               struct ath9k_hw_cal_data *caldata = &sc->cur_chan->caldata;
                if (IS_CHAN_HT40PLUS(ah->curchan) &&
                    (ah->curchan->channel > caldata->channel) &&
                    (ah->curchan->channel <= caldata->channel + 20))
@@ -720,7 +720,7 @@ void ath9k_mci_set_txpower(struct ath_softc *sc, bool setchannel,
                mci_hw->concur_tx = concur_tx;
 
        if (old_concur_tx != mci_hw->concur_tx)
-               ath9k_hw_set_txpowerlimit(ah, sc->config.txpowlimit, false);
+               ath9k_hw_set_txpowerlimit(ah, sc->cur_chan->txpower, false);
 }
 
 static void ath9k_mci_stomp_audio(struct ath_softc *sc)
index 4dec09e..7a2b2c5 100644 (file)
@@ -843,6 +843,7 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
                return -ENODEV;
        }
 
+       ath9k_fill_chanctx_ops();
        hw = ieee80211_alloc_hw(sizeof(struct ath_softc), &ath9k_ops);
        if (!hw) {
                dev_err(&pdev->dev, "No memory for ieee80211_hw\n");
index 9105a92..74ab1d0 100644 (file)
@@ -259,7 +259,7 @@ static void ath_edma_start_recv(struct ath_softc *sc)
        ath_rx_addbuffer_edma(sc, ATH9K_RX_QUEUE_HP);
        ath_rx_addbuffer_edma(sc, ATH9K_RX_QUEUE_LP);
        ath_opmode_init(sc);
-       ath9k_hw_startpcureceive(sc->sc_ah, !!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL));
+       ath9k_hw_startpcureceive(sc->sc_ah, sc->cur_chan->offchannel);
 }
 
 static void ath_edma_stop_recv(struct ath_softc *sc)
@@ -374,6 +374,7 @@ void ath_rx_cleanup(struct ath_softc *sc)
 
 u32 ath_calcrxfilter(struct ath_softc *sc)
 {
+       struct ath_common *common = ath9k_hw_common(sc->sc_ah);
        u32 rfilt;
 
        if (config_enabled(CONFIG_ATH9K_TX99))
@@ -424,6 +425,10 @@ u32 ath_calcrxfilter(struct ath_softc *sc)
        if (AR_SREV_9550(sc->sc_ah) || AR_SREV_9531(sc->sc_ah))
                rfilt |= ATH9K_RX_FILTER_4ADDRESS;
 
+       if (ath9k_use_chanctx &&
+           test_bit(ATH_OP_SCANNING, &common->op_flags))
+               rfilt |= ATH9K_RX_FILTER_BEACON;
+
        return rfilt;
 
 }
@@ -457,7 +462,7 @@ int ath_startrecv(struct ath_softc *sc)
 
 start_recv:
        ath_opmode_init(sc);
-       ath9k_hw_startpcureceive(ah, !!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL));
+       ath9k_hw_startpcureceive(ah, sc->cur_chan->offchannel);
 
        return 0;
 }
@@ -540,7 +545,7 @@ static void ath_rx_ps_beacon(struct ath_softc *sc, struct sk_buff *skb)
                sc->ps_flags &= ~PS_BEACON_SYNC;
                ath_dbg(common, PS,
                        "Reconfigure beacon timers based on synchronized timestamp\n");
-               if (!(WARN_ON_ONCE(sc->cur_beacon_conf.beacon_interval == 0)))
+               if (!(WARN_ON_ONCE(sc->cur_chan->beacon.beacon_interval == 0)))
                        ath9k_set_beacon(sc);
                if (sc->p2p_ps_vif)
                        ath9k_update_p2p_ps(sc, sc->p2p_ps_vif->vif);
@@ -887,6 +892,11 @@ static int ath9k_rx_skb_preprocess(struct ath_softc *sc,
                return -EINVAL;
        }
 
+       if (rx_stats->is_mybeacon) {
+               sc->sched.next_tbtt = rx_stats->rs_tstamp;
+               ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_BEACON_RECEIVED);
+       }
+
        ath9k_cmn_process_rssi(common, hw, rx_stats, rx_status);
 
        rx_status->band = ah->curchan->chan->band;
index a65cfb9..2397292 100644 (file)
@@ -76,7 +76,7 @@ static struct sk_buff *ath9k_build_tx99_skb(struct ath_softc *sc)
        tx_info = IEEE80211_SKB_CB(skb);
        memset(tx_info, 0, sizeof(*tx_info));
        rate = &tx_info->control.rates[0];
-       tx_info->band = hw->conf.chandef.chan->band;
+       tx_info->band = sc->cur_chan->chandef.chan->band;
        tx_info->flags = IEEE80211_TX_CTL_NO_ACK;
        tx_info->control.vif = sc->tx99_vif;
        rate->count = 1;
index 2879887..a4f4f0d 100644 (file)
@@ -193,6 +193,7 @@ int ath9k_suspend(struct ieee80211_hw *hw,
        u32 wow_triggers_enabled = 0;
        int ret = 0;
 
+       cancel_work_sync(&sc->chanctx_work);
        mutex_lock(&sc->mutex);
 
        ath_cancel_work(sc);
index 66acb2c..d4927c9 100644 (file)
@@ -103,9 +103,16 @@ void ath_txq_unlock_complete(struct ath_softc *sc, struct ath_txq *txq)
                ieee80211_tx_status(sc->hw, skb);
 }
 
-static void ath_tx_queue_tid(struct ath_txq *txq, struct ath_atx_tid *tid)
+static void ath_tx_queue_tid(struct ath_softc *sc, struct ath_txq *txq,
+                            struct ath_atx_tid *tid)
 {
        struct ath_atx_ac *ac = tid->ac;
+       struct list_head *list;
+       struct ath_vif *avp = (struct ath_vif *) tid->an->vif->drv_priv;
+       struct ath_chanctx *ctx = avp->chanctx;
+
+       if (!ctx)
+               return;
 
        if (tid->sched)
                return;
@@ -117,7 +124,9 @@ static void ath_tx_queue_tid(struct ath_txq *txq, struct ath_atx_tid *tid)
                return;
 
        ac->sched = true;
-       list_add_tail(&ac->list, &txq->axq_acq);
+
+       list = &ctx->acq[TID_TO_WME_AC(tid->tidno)];
+       list_add_tail(&ac->list, list);
 }
 
 static struct ath_frame_info *get_frame_info(struct sk_buff *skb)
@@ -147,7 +156,8 @@ static void ath_set_rates(struct ieee80211_vif *vif, struct ieee80211_sta *sta,
 static void ath_txq_skb_done(struct ath_softc *sc, struct ath_txq *txq,
                             struct sk_buff *skb)
 {
-       int q;
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       int q, hw_queue;
 
        q = skb_get_queue_mapping(skb);
        if (txq == sc->tx.uapsdq)
@@ -159,9 +169,10 @@ static void ath_txq_skb_done(struct ath_softc *sc, struct ath_txq *txq,
        if (WARN_ON(--txq->pending_frames < 0))
                txq->pending_frames = 0;
 
+       hw_queue = (info->hw_queue >= sc->hw->queues - 2) ? q : info->hw_queue;
        if (txq->stopped &&
            txq->pending_frames < sc->tx.txq_max_pending[q]) {
-               ieee80211_wake_queue(sc->hw, q);
+               ieee80211_wake_queue(sc->hw, hw_queue);
                txq->stopped = false;
        }
 }
@@ -626,7 +637,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
 
                skb_queue_splice_tail(&bf_pending, &tid->retry_q);
                if (!an->sleeping) {
-                       ath_tx_queue_tid(txq, tid);
+                       ath_tx_queue_tid(sc, txq, tid);
 
                        if (ts->ts_status & (ATH9K_TXERR_FILT | ATH9K_TXERR_XRETRY))
                                tid->ac->clear_ps_filter = true;
@@ -1483,7 +1494,7 @@ void ath_tx_aggr_wakeup(struct ath_softc *sc, struct ath_node *an)
                ac->clear_ps_filter = true;
 
                if (ath_tid_has_buffered(tid)) {
-                       ath_tx_queue_tid(txq, tid);
+                       ath_tx_queue_tid(sc, txq, tid);
                        ath_txq_schedule(sc, txq);
                }
 
@@ -1507,7 +1518,7 @@ void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta,
        tid->baw_size = IEEE80211_MIN_AMPDU_BUF << sta->ht_cap.ampdu_factor;
 
        if (ath_tid_has_buffered(tid)) {
-               ath_tx_queue_tid(txq, tid);
+               ath_tx_queue_tid(sc, txq, tid);
                ath_txq_schedule(sc, txq);
        }
 
@@ -1642,7 +1653,6 @@ struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype)
                txq->axq_link = NULL;
                __skb_queue_head_init(&txq->complete_q);
                INIT_LIST_HEAD(&txq->axq_q);
-               INIT_LIST_HEAD(&txq->axq_acq);
                spin_lock_init(&txq->axq_lock);
                txq->axq_depth = 0;
                txq->axq_ampdu_depth = 0;
@@ -1686,7 +1696,7 @@ int ath_txq_update(struct ath_softc *sc, int qnum,
 int ath_cabq_update(struct ath_softc *sc)
 {
        struct ath9k_tx_queue_info qi;
-       struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf;
+       struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon;
        int qnum = sc->beacon.cabq->axq_qnum;
 
        ath9k_hw_get_txq_props(sc->sc_ah, qnum, &qi);
@@ -1804,7 +1814,7 @@ void ath_tx_cleanupq(struct ath_softc *sc, struct ath_txq *txq)
        sc->tx.txqsetup &= ~(1<<txq->axq_qnum);
 }
 
-/* For each axq_acq entry, for each tid, try to schedule packets
+/* For each acq entry, for each tid, try to schedule packets
  * for transmit until ampdu_depth has reached min Q depth.
  */
 void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq)
@@ -1812,19 +1822,31 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq)
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
        struct ath_atx_ac *ac, *last_ac;
        struct ath_atx_tid *tid, *last_tid;
+       struct list_head *ac_list;
        bool sent = false;
 
+       if (txq->mac80211_qnum < 0)
+               return;
+
+       spin_lock_bh(&sc->chan_lock);
+       ac_list = &sc->cur_chan->acq[txq->mac80211_qnum];
+       spin_unlock_bh(&sc->chan_lock);
+
        if (test_bit(ATH_OP_HW_RESET, &common->op_flags) ||
-           list_empty(&txq->axq_acq))
+           list_empty(ac_list))
                return;
 
+       spin_lock_bh(&sc->chan_lock);
        rcu_read_lock();
 
-       last_ac = list_entry(txq->axq_acq.prev, struct ath_atx_ac, list);
-       while (!list_empty(&txq->axq_acq)) {
+       last_ac = list_entry(ac_list->prev, struct ath_atx_ac, list);
+       while (!list_empty(ac_list)) {
                bool stop = false;
 
-               ac = list_first_entry(&txq->axq_acq, struct ath_atx_ac, list);
+               if (sc->cur_chan->stopped)
+                       break;
+
+               ac = list_first_entry(ac_list, struct ath_atx_ac, list);
                last_tid = list_entry(ac->tid_q.prev, struct ath_atx_tid, list);
                list_del(&ac->list);
                ac->sched = false;
@@ -1844,7 +1866,7 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq)
                         * are pending for the tid
                         */
                        if (ath_tid_has_buffered(tid))
-                               ath_tx_queue_tid(txq, tid);
+                               ath_tx_queue_tid(sc, txq, tid);
 
                        if (stop || tid == last_tid)
                                break;
@@ -1852,7 +1874,7 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq)
 
                if (!list_empty(&ac->tid_q) && !ac->sched) {
                        ac->sched = true;
-                       list_add_tail(&ac->list, &txq->axq_acq);
+                       list_add_tail(&ac->list, ac_list);
                }
 
                if (stop)
@@ -1863,12 +1885,27 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq)
                                break;
 
                        sent = false;
-                       last_ac = list_entry(txq->axq_acq.prev,
+                       last_ac = list_entry(ac_list->prev,
                                             struct ath_atx_ac, list);
                }
        }
 
        rcu_read_unlock();
+       spin_unlock_bh(&sc->chan_lock);
+}
+
+void ath_txq_schedule_all(struct ath_softc *sc)
+{
+       struct ath_txq *txq;
+       int i;
+
+       for (i = 0; i < IEEE80211_NUM_ACS; i++) {
+               txq = sc->tx.txq_map[i];
+
+               spin_lock_bh(&txq->axq_lock);
+               ath_txq_schedule(sc, txq);
+               spin_unlock_bh(&txq->axq_lock);
+       }
 }
 
 /***********/
@@ -2150,13 +2187,21 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
        struct ieee80211_sta *sta = txctl->sta;
        struct ieee80211_vif *vif = info->control.vif;
+       struct ath_vif *avp = NULL;
        struct ath_softc *sc = hw->priv;
        struct ath_txq *txq = txctl->txq;
        struct ath_atx_tid *tid = NULL;
        struct ath_buf *bf;
-       int q;
+       bool queue;
+       int q, hw_queue;
        int ret;
 
+       if (vif)
+               avp = (void *)vif->drv_priv;
+
+       if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN)
+               txctl->force_channel = true;
+
        ret = ath_tx_prepare(hw, skb, txctl);
        if (ret)
            return ret;
@@ -2168,24 +2213,39 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
         */
 
        q = skb_get_queue_mapping(skb);
+       hw_queue = (info->hw_queue >= sc->hw->queues - 2) ? q : info->hw_queue;
 
        ath_txq_lock(sc, txq);
        if (txq == sc->tx.txq_map[q] &&
            ++txq->pending_frames > sc->tx.txq_max_pending[q] &&
            !txq->stopped) {
-               ieee80211_stop_queue(sc->hw, q);
+               ieee80211_stop_queue(sc->hw, hw_queue);
                txq->stopped = true;
        }
 
-       if (txctl->an && ieee80211_is_data_present(hdr->frame_control))
+       queue = ieee80211_is_data_present(hdr->frame_control);
+
+       /* Force queueing of all frames that belong to a virtual interface on
+        * a different channel context, to ensure that they are sent on the
+        * correct channel.
+        */
+       if (((avp && avp->chanctx != sc->cur_chan) ||
+            sc->cur_chan->stopped) && !txctl->force_channel) {
+               if (!txctl->an)
+                       txctl->an = &avp->mcast_node;
+               info->flags &= ~IEEE80211_TX_CTL_PS_RESPONSE;
+               queue = true;
+       }
+
+       if (txctl->an && queue)
                tid = ath_get_skb_tid(sc, txctl->an, skb);
 
-       if (info->flags & IEEE80211_TX_CTL_PS_RESPONSE) {
+       if (info->flags & (IEEE80211_TX_CTL_PS_RESPONSE |
+                          IEEE80211_TX_CTL_TX_OFFCHAN)) {
                ath_txq_unlock(sc, txq);
                txq = sc->tx.uapsdq;
                ath_txq_lock(sc, txq);
-       } else if (txctl->an &&
-                  ieee80211_is_data_present(hdr->frame_control)) {
+       } else if (txctl->an && queue) {
                WARN_ON(tid->ac->txq != txctl->txq);
 
                if (info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT)
@@ -2198,7 +2258,7 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
                TX_STAT_INC(txq->axq_qnum, a_queued_sw);
                __skb_queue_tail(&tid->buf_q, skb);
                if (!txctl->an->sleeping)
-                       ath_tx_queue_tid(txq, tid);
+                       ath_tx_queue_tid(sc, txq, tid);
 
                ath_txq_schedule(sc, txq);
                goto out;
@@ -2244,8 +2304,8 @@ void ath_tx_cabq(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
        int max_duration;
 
        max_duration =
-               sc->cur_beacon_conf.beacon_interval * 1000 *
-               sc->cur_beacon_conf.dtim_period / ATH_BCBUF;
+               sc->cur_chan->beacon.beacon_interval * 1000 *
+               sc->cur_chan->beacon.dtim_period / ATH_BCBUF;
 
        do {
                struct ath_frame_info *fi = get_frame_info(skb);
@@ -2560,6 +2620,8 @@ void ath_tx_edma_tasklet(struct ath_softc *sc)
                        sc->beacon.tx_processed = true;
                        sc->beacon.tx_last = !(ts.ts_status & ATH9K_TXERR_MASK);
 
+                       ath_chanctx_event(sc, NULL,
+                                         ATH_CHANCTX_EVENT_BEACON_SENT);
                        ath9k_csa_update(sc);
                        continue;
                }
index 820d4eb..4ac2c20 100644 (file)
@@ -104,8 +104,8 @@ int wil_iftype_nl2wmi(enum nl80211_iftype type)
        return -EOPNOTSUPP;
 }
 
-static int wil_cid_fill_sinfo(struct wil6210_priv *wil, int cid,
-                             struct station_info *sinfo)
+int wil_cid_fill_sinfo(struct wil6210_priv *wil, int cid,
+                      struct station_info *sinfo)
 {
        struct wmi_notify_req_cmd cmd = {
                .cid = cid,
@@ -287,6 +287,7 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,
                return -EBUSY;
        }
 
+       wil_dbg_misc(wil, "Start scan_request 0x%p\n", request);
        wil->scan_request = request;
        mod_timer(&wil->scan_timer, jiffies + WIL6210_SCAN_TO);
 
@@ -443,15 +444,15 @@ static int wil_cfg80211_disconnect(struct wiphy *wiphy,
        return rc;
 }
 
-static int wil_cfg80211_mgmt_tx(struct wiphy *wiphy,
-                               struct wireless_dev *wdev,
-                               struct cfg80211_mgmt_tx_params *params,
-                               u64 *cookie)
+int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
+                        struct cfg80211_mgmt_tx_params *params,
+                        u64 *cookie)
 {
        const u8 *buf = params->buf;
        size_t len = params->len;
        struct wil6210_priv *wil = wiphy_to_wil(wiphy);
        int rc;
+       bool tx_status = false;
        struct ieee80211_mgmt *mgmt_frame = (void *)buf;
        struct wmi_sw_tx_req_cmd *cmd;
        struct {
@@ -460,8 +461,10 @@ static int wil_cfg80211_mgmt_tx(struct wiphy *wiphy,
        } __packed evt;
 
        cmd = kmalloc(sizeof(*cmd) + len, GFP_KERNEL);
-       if (!cmd)
-               return -ENOMEM;
+       if (!cmd) {
+               rc = -ENOMEM;
+               goto out;
+       }
 
        memcpy(cmd->dst_mac, mgmt_frame->da, WMI_MAC_LEN);
        cmd->len = cpu_to_le16(len);
@@ -470,10 +473,12 @@ static int wil_cfg80211_mgmt_tx(struct wiphy *wiphy,
        rc = wmi_call(wil, WMI_SW_TX_REQ_CMDID, cmd, sizeof(*cmd) + len,
                      WMI_SW_TX_COMPLETE_EVENTID, &evt, sizeof(evt), 2000);
        if (rc == 0)
-               rc = evt.evt.status;
+               tx_status = !evt.evt.status;
 
        kfree(cmd);
-
+ out:
+       cfg80211_mgmt_tx_status(wdev, cookie ? *cookie : 0, buf, len,
+                               tx_status, GFP_KERNEL);
        return rc;
 }
 
@@ -562,6 +567,34 @@ static int wil_cancel_remain_on_channel(struct wiphy *wiphy,
        return rc;
 }
 
+static void wil_print_bcon_data(struct cfg80211_beacon_data *b)
+{
+       print_hex_dump_bytes("head     ", DUMP_PREFIX_OFFSET,
+                            b->head, b->head_len);
+       print_hex_dump_bytes("tail     ", DUMP_PREFIX_OFFSET,
+                            b->tail, b->tail_len);
+       print_hex_dump_bytes("BCON IE  ", DUMP_PREFIX_OFFSET,
+                            b->beacon_ies, b->beacon_ies_len);
+       print_hex_dump_bytes("PROBE    ", DUMP_PREFIX_OFFSET,
+                            b->probe_resp, b->probe_resp_len);
+       print_hex_dump_bytes("PROBE IE ", DUMP_PREFIX_OFFSET,
+                            b->proberesp_ies, b->proberesp_ies_len);
+       print_hex_dump_bytes("ASSOC IE ", DUMP_PREFIX_OFFSET,
+                            b->assocresp_ies, b->assocresp_ies_len);
+}
+
+static void wil_print_crypto(struct wil6210_priv *wil,
+                            struct cfg80211_crypto_settings *c)
+{
+       wil_dbg_misc(wil, "WPA versions: 0x%08x cipher group 0x%08x\n",
+                    c->wpa_versions, c->cipher_group);
+       wil_dbg_misc(wil, "Pairwise ciphers [%d]\n", c->n_ciphers_pairwise);
+       wil_dbg_misc(wil, "AKM suites [%d]\n", c->n_akm_suites);
+       wil_dbg_misc(wil, "Control port : %d, eth_type 0x%04x no_encrypt %d\n",
+                    c->control_port, be16_to_cpu(c->control_port_ethertype),
+                    c->control_port_no_encrypt);
+}
+
 static int wil_fix_bcon(struct wil6210_priv *wil,
                        struct cfg80211_beacon_data *bcon)
 {
@@ -595,8 +628,11 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy,
        struct wireless_dev *wdev = ndev->ieee80211_ptr;
        struct ieee80211_channel *channel = info->chandef.chan;
        struct cfg80211_beacon_data *bcon = &info->beacon;
+       struct cfg80211_crypto_settings *crypto = &info->crypto;
        u8 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype);
 
+       wil_dbg_misc(wil, "%s()\n", __func__);
+
        if (!channel) {
                wil_err(wil, "AP: No channel???\n");
                return -EINVAL;
@@ -604,11 +640,19 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy,
 
        wil_dbg_misc(wil, "AP on Channel %d %d MHz, %s\n", channel->hw_value,
                     channel->center_freq, info->privacy ? "secure" : "open");
+       wil_dbg_misc(wil, "Privacy: %d auth_type %d\n",
+                    info->privacy, info->auth_type);
+       wil_dbg_misc(wil, "BI %d DTIM %d\n", info->beacon_interval,
+                    info->dtim_period);
        print_hex_dump_bytes("SSID ", DUMP_PREFIX_OFFSET,
                             info->ssid, info->ssid_len);
+       wil_print_bcon_data(bcon);
+       wil_print_crypto(wil, crypto);
 
-       if (wil_fix_bcon(wil, bcon))
+       if (wil_fix_bcon(wil, bcon)) {
                wil_dbg_misc(wil, "Fixed bcon\n");
+               wil_print_bcon_data(bcon);
+       }
 
        mutex_lock(&wil->mutex);
 
@@ -663,6 +707,8 @@ static int wil_cfg80211_stop_ap(struct wiphy *wiphy,
        int rc = 0;
        struct wil6210_priv *wil = wiphy_to_wil(wiphy);
 
+       wil_dbg_misc(wil, "%s()\n", __func__);
+
        mutex_lock(&wil->mutex);
 
        rc = wmi_pcp_stop(wil);
index 8d4bc4b..a868c5e 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/seq_file.h>
 #include <linux/pci.h>
 #include <linux/rtnetlink.h>
+#include <linux/power_supply.h>
 
 #include "wil6210.h"
 #include "txrx.h"
@@ -69,14 +70,32 @@ static int wil_vring_debugfs_show(struct seq_file *s, void *data)
 
        for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) {
                struct vring *vring = &(wil->vring_tx[i]);
+               struct vring_tx_data *txdata = &wil->vring_tx_data[i];
+
                if (vring->va) {
                        int cid = wil->vring2cid_tid[i][0];
                        int tid = wil->vring2cid_tid[i][1];
+                       u32 swhead = vring->swhead;
+                       u32 swtail = vring->swtail;
+                       int used = (vring->size + swhead - swtail)
+                                  % vring->size;
+                       int avail = vring->size - used - 1;
                        char name[10];
+                       /* performance monitoring */
+                       cycles_t now = get_cycles();
+                       cycles_t idle = txdata->idle * 100;
+                       cycles_t total = now - txdata->begin;
+
+                       do_div(idle, total);
+                       txdata->begin = now;
+                       txdata->idle = 0ULL;
+
                        snprintf(name, sizeof(name), "tx_%2d", i);
 
-                       seq_printf(s, "\n%pM CID %d TID %d\n",
-                                  wil->sta[cid].addr, cid, tid);
+                       seq_printf(s, "\n%pM CID %d TID %d [%3d|%3d] idle %3d%%\n",
+                                  wil->sta[cid].addr, cid, tid, used, avail,
+                                  (int)idle);
+
                        wil_print_vring(s, wil, name, vring, '_', 'H');
                }
        }
@@ -231,6 +250,26 @@ static struct dentry *wil_debugfs_create_iomem_x32(const char *name,
                                   &fops_iomem_x32);
 }
 
+static int wil_debugfs_ulong_set(void *data, u64 val)
+{
+       *(ulong *)data = val;
+       return 0;
+}
+static int wil_debugfs_ulong_get(void *data, u64 *val)
+{
+       *val = *(ulong *)data;
+       return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(wil_fops_ulong, wil_debugfs_ulong_get,
+                       wil_debugfs_ulong_set, "%llu\n");
+
+static struct dentry *wil_debugfs_create_ulong(const char *name, umode_t mode,
+                                              struct dentry *parent,
+                                              ulong *value)
+{
+       return debugfs_create_file(name, mode, parent, value, &wil_fops_ulong);
+}
+
 static int wil6210_debugfs_create_ISR(struct wil6210_priv *wil,
                                      const char *name,
                                      struct dentry *parent, u32 off)
@@ -284,11 +323,11 @@ static int wil6210_debugfs_create_ITR_CNT(struct wil6210_priv *wil,
        if (IS_ERR_OR_NULL(d))
                return -ENODEV;
 
-       wil_debugfs_create_iomem_x32("TRSH", S_IRUGO, d, wil->csr +
+       wil_debugfs_create_iomem_x32("TRSH", S_IRUGO | S_IWUSR, d, wil->csr +
                                     HOSTADDR(RGF_DMA_ITR_CNT_TRSH));
-       wil_debugfs_create_iomem_x32("DATA", S_IRUGO, d, wil->csr +
+       wil_debugfs_create_iomem_x32("DATA", S_IRUGO | S_IWUSR, d, wil->csr +
                                     HOSTADDR(RGF_DMA_ITR_CNT_DATA));
-       wil_debugfs_create_iomem_x32("CTL", S_IRUGO, d, wil->csr +
+       wil_debugfs_create_iomem_x32("CTL", S_IRUGO | S_IWUSR, d, wil->csr +
                                     HOSTADDR(RGF_DMA_ITR_CNT_CRL));
 
        return 0;
@@ -397,6 +436,124 @@ static const struct file_operations fops_reset = {
        .write = wil_write_file_reset,
        .open  = simple_open,
 };
+/*---write channel 1..4 to rxon for it, 0 to rxoff---*/
+static ssize_t wil_write_file_rxon(struct file *file, const char __user *buf,
+                                  size_t len, loff_t *ppos)
+{
+       struct wil6210_priv *wil = file->private_data;
+       int rc;
+       long channel;
+       bool on;
+
+       char *kbuf = kmalloc(len + 1, GFP_KERNEL);
+       if (!kbuf)
+               return -ENOMEM;
+       if (copy_from_user(kbuf, buf, len))
+               return -EIO;
+
+       kbuf[len] = '\0';
+       rc = kstrtol(kbuf, 0, &channel);
+       kfree(kbuf);
+       if (rc)
+               return rc;
+
+       if ((channel < 0) || (channel > 4)) {
+               wil_err(wil, "Invalid channel %ld\n", channel);
+               return -EINVAL;
+       }
+       on = !!channel;
+
+       if (on) {
+               rc = wmi_set_channel(wil, (int)channel);
+               if (rc)
+                       return rc;
+       }
+
+       rc = wmi_rxon(wil, on);
+       if (rc)
+               return rc;
+
+       return len;
+}
+
+static const struct file_operations fops_rxon = {
+       .write = wil_write_file_rxon,
+       .open  = simple_open,
+};
+/*---tx_mgmt---*/
+/* Write mgmt frame to this file to send it */
+static ssize_t wil_write_file_txmgmt(struct file *file, const char __user *buf,
+                                    size_t len, loff_t *ppos)
+{
+       struct wil6210_priv *wil = file->private_data;
+       struct wiphy *wiphy = wil_to_wiphy(wil);
+       struct wireless_dev *wdev = wil_to_wdev(wil);
+       struct cfg80211_mgmt_tx_params params;
+       int rc;
+
+       void *frame = kmalloc(len, GFP_KERNEL);
+       if (!frame)
+               return -ENOMEM;
+
+       if (copy_from_user(frame, buf, len))
+               return -EIO;
+
+       params.buf = frame;
+       params.len = len;
+       params.chan = wdev->preset_chandef.chan;
+
+       rc = wil_cfg80211_mgmt_tx(wiphy, wdev, &params, NULL);
+
+       kfree(frame);
+       wil_info(wil, "%s() -> %d\n", __func__, rc);
+
+       return len;
+}
+
+static const struct file_operations fops_txmgmt = {
+       .write = wil_write_file_txmgmt,
+       .open  = simple_open,
+};
+
+/* Write WMI command (w/o mbox header) to this file to send it
+ * WMI starts from wil6210_mbox_hdr_wmi header
+ */
+static ssize_t wil_write_file_wmi(struct file *file, const char __user *buf,
+                                 size_t len, loff_t *ppos)
+{
+       struct wil6210_priv *wil = file->private_data;
+       struct wil6210_mbox_hdr_wmi *wmi;
+       void *cmd;
+       int cmdlen = len - sizeof(struct wil6210_mbox_hdr_wmi);
+       u16 cmdid;
+       int rc, rc1;
+
+       if (cmdlen <= 0)
+               return -EINVAL;
+
+       wmi = kmalloc(len, GFP_KERNEL);
+       if (!wmi)
+               return -ENOMEM;
+
+       rc = simple_write_to_buffer(wmi, len, ppos, buf, len);
+       if (rc < 0)
+               return rc;
+
+       cmd = &wmi[1];
+       cmdid = le16_to_cpu(wmi->id);
+
+       rc1 = wmi_send(wil, cmdid, cmd, cmdlen);
+       kfree(wmi);
+
+       wil_info(wil, "%s(0x%04x[%d]) -> %d\n", __func__, cmdid, cmdlen, rc1);
+
+       return rc;
+}
+
+static const struct file_operations fops_wmi = {
+       .write = wil_write_file_wmi,
+       .open  = simple_open,
+};
 
 static void wil_seq_hexdump(struct seq_file *s, void *p, int len,
                            const char *prefix)
@@ -600,8 +757,8 @@ static int wil_temp_debugfs_show(struct seq_file *s, void *data)
                return 0;
        }
 
-       print_temp(s, "MAC temperature   :", t_m);
-       print_temp(s, "Radio temperature :", t_r);
+       print_temp(s, "T_mac   =", t_m);
+       print_temp(s, "T_radio =", t_r);
 
        return 0;
 }
@@ -618,6 +775,130 @@ static const struct file_operations fops_temp = {
        .llseek         = seq_lseek,
 };
 
+/*---------freq------------*/
+static int wil_freq_debugfs_show(struct seq_file *s, void *data)
+{
+       struct wil6210_priv *wil = s->private;
+       struct wireless_dev *wdev = wil_to_wdev(wil);
+       u16 freq = wdev->chandef.chan ? wdev->chandef.chan->center_freq : 0;
+
+       seq_printf(s, "Freq = %d\n", freq);
+
+       return 0;
+}
+
+static int wil_freq_seq_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, wil_freq_debugfs_show, inode->i_private);
+}
+
+static const struct file_operations fops_freq = {
+       .open           = wil_freq_seq_open,
+       .release        = single_release,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+};
+
+/*---------link------------*/
+static int wil_link_debugfs_show(struct seq_file *s, void *data)
+{
+       struct wil6210_priv *wil = s->private;
+       struct station_info sinfo;
+       int i, rc;
+
+       for (i = 0; i < ARRAY_SIZE(wil->sta); i++) {
+               struct wil_sta_info *p = &wil->sta[i];
+               char *status = "unknown";
+               switch (p->status) {
+               case wil_sta_unused:
+                       status = "unused   ";
+                       break;
+               case wil_sta_conn_pending:
+                       status = "pending  ";
+                       break;
+               case wil_sta_connected:
+                       status = "connected";
+                       break;
+               }
+               seq_printf(s, "[%d] %pM %s%s\n", i, p->addr, status,
+                          (p->data_port_open ? " data_port_open" : ""));
+
+               if (p->status == wil_sta_connected) {
+                       rc = wil_cid_fill_sinfo(wil, i, &sinfo);
+                       if (rc)
+                               return rc;
+
+                       seq_printf(s, "  Tx_mcs = %d\n", sinfo.txrate.mcs);
+                       seq_printf(s, "  Rx_mcs = %d\n", sinfo.rxrate.mcs);
+                       seq_printf(s, "  SQ     = %d\n", sinfo.signal);
+               }
+       }
+
+       return 0;
+}
+
+static int wil_link_seq_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, wil_link_debugfs_show, inode->i_private);
+}
+
+static const struct file_operations fops_link = {
+       .open           = wil_link_seq_open,
+       .release        = single_release,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+};
+
+/*---------info------------*/
+static int wil_info_debugfs_show(struct seq_file *s, void *data)
+{
+       struct wil6210_priv *wil = s->private;
+       struct net_device *ndev = wil_to_ndev(wil);
+       int is_ac = power_supply_is_system_supplied();
+       int rx = atomic_xchg(&wil->isr_count_rx, 0);
+       int tx = atomic_xchg(&wil->isr_count_tx, 0);
+       static ulong rxf_old, txf_old;
+       ulong rxf = ndev->stats.rx_packets;
+       ulong txf = ndev->stats.tx_packets;
+       unsigned int i;
+
+       /* >0 : AC; 0 : battery; <0 : error */
+       seq_printf(s, "AC powered : %d\n", is_ac);
+       seq_printf(s, "Rx irqs:packets : %8d : %8ld\n", rx, rxf - rxf_old);
+       seq_printf(s, "Tx irqs:packets : %8d : %8ld\n", tx, txf - txf_old);
+       rxf_old = rxf;
+       txf_old = txf;
+
+
+#define CHECK_QSTATE(x) (state & BIT(__QUEUE_STATE_ ## x)) ? \
+       " " __stringify(x) : ""
+
+       for (i = 0; i < ndev->num_tx_queues; i++) {
+               struct netdev_queue *txq = netdev_get_tx_queue(ndev, i);
+               unsigned long state = txq->state;
+
+               seq_printf(s, "Tx queue[%i] state : 0x%lx%s%s%s\n", i, state,
+                          CHECK_QSTATE(DRV_XOFF),
+                          CHECK_QSTATE(STACK_XOFF),
+                          CHECK_QSTATE(FROZEN)
+                         );
+       }
+#undef CHECK_QSTATE
+       return 0;
+}
+
+static int wil_info_seq_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, wil_info_debugfs_show, inode->i_private);
+}
+
+static const struct file_operations fops_info = {
+       .open           = wil_info_seq_open,
+       .release        = single_release,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+};
+
 /*---------Station matrix------------*/
 static void wil_print_rxtid(struct seq_file *s, struct wil_tid_ampdu_rx *r)
 {
@@ -630,7 +911,7 @@ static void wil_print_rxtid(struct seq_file *s, struct wil_tid_ampdu_rx *r)
                else
                        seq_printf(s, "%c", r->reorder_buf[i] ? '*' : '_');
        }
-       seq_puts(s, "]\n");
+       seq_printf(s, "] last drop 0x%03x\n", r->ssn_last_drop);
 }
 
 static int wil_sta_debugfs_show(struct seq_file *s, void *data)
@@ -703,6 +984,8 @@ int wil6210_debugfs_init(struct wil6210_priv *wil)
        debugfs_create_file("ssid", S_IRUGO | S_IWUSR, dbg, wil, &fops_ssid);
        debugfs_create_u32("secure_pcp", S_IRUGO | S_IWUSR, dbg,
                           &wil->secure_pcp);
+       wil_debugfs_create_ulong("status", S_IRUGO | S_IWUSR, dbg,
+                                &wil->status);
 
        wil6210_debugfs_create_ISR(wil, "USER_ICR", dbg,
                                   HOSTADDR(RGF_USER_USER_ICR));
@@ -719,7 +1002,13 @@ int wil6210_debugfs_init(struct wil6210_priv *wil)
        debugfs_create_file("mem_val", S_IRUGO, dbg, wil, &fops_memread);
 
        debugfs_create_file("reset", S_IWUSR, dbg, wil, &fops_reset);
+       debugfs_create_file("rxon", S_IWUSR, dbg, wil, &fops_rxon);
+       debugfs_create_file("tx_mgmt", S_IWUSR, dbg, wil, &fops_txmgmt);
+       debugfs_create_file("wmi_send", S_IWUSR, dbg, wil, &fops_wmi);
        debugfs_create_file("temp", S_IRUGO, dbg, wil, &fops_temp);
+       debugfs_create_file("freq", S_IRUGO, dbg, wil, &fops_freq);
+       debugfs_create_file("link", S_IRUGO, dbg, wil, &fops_link);
+       debugfs_create_file("info", S_IRUGO, dbg, wil, &fops_info);
 
        wil->rgf_blob.data = (void * __force)wil->csr + 0;
        wil->rgf_blob.size = 0xa000;
index 73593aa..67f1002 100644 (file)
@@ -208,6 +208,7 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie)
 
        /* Rx IRQ will be enabled when NAPI processing finished */
 
+       atomic_inc(&wil->isr_count_rx);
        return IRQ_HANDLED;
 }
 
@@ -246,6 +247,7 @@ static irqreturn_t wil6210_irq_tx(int irq, void *cookie)
 
        /* Tx IRQ will be enabled when NAPI processing finished */
 
+       atomic_inc(&wil->isr_count_tx);
        return IRQ_HANDLED;
 }
 
@@ -257,6 +259,7 @@ static void wil_notify_fw_error(struct wil6210_priv *wil)
                [1] = "EVENT=FW_ERROR",
                [2] = NULL,
        };
+       wil_err(wil, "Notify about firmware error\n");
        kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
 }
 
index 11e6d9d..53a689e 100644 (file)
@@ -61,11 +61,24 @@ void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src,
 static void wil_disconnect_cid(struct wil6210_priv *wil, int cid)
 {
        uint i;
+       struct net_device *ndev = wil_to_ndev(wil);
+       struct wireless_dev *wdev = wil->wdev;
        struct wil_sta_info *sta = &wil->sta[cid];
+       wil_dbg_misc(wil, "%s(CID %d, status %d)\n", __func__, cid,
+                    sta->status);
 
        sta->data_port_open = false;
        if (sta->status != wil_sta_unused) {
                wmi_disconnect_sta(wil, sta->addr, WLAN_REASON_DEAUTH_LEAVING);
+               switch (wdev->iftype) {
+               case NL80211_IFTYPE_AP:
+               case NL80211_IFTYPE_P2P_GO:
+                       /* AP-like interface */
+                       cfg80211_del_sta(ndev, sta->addr, GFP_KERNEL);
+                       break;
+               default:
+                       break;
+               }
                sta->status = wil_sta_unused;
        }
 
@@ -119,11 +132,6 @@ static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid)
                clear_bit(wil_status_fwconnecting, &wil->status);
                break;
        default:
-               /* AP-like interface and monitor:
-                * never scan, always connected
-                */
-               if (bssid)
-                       cfg80211_del_sta(ndev, bssid, GFP_KERNEL);
                break;
        }
 }
@@ -465,6 +473,7 @@ void wil_link_on(struct wil6210_priv *wil)
        wil_dbg_misc(wil, "%s()\n", __func__);
 
        netif_carrier_on(ndev);
+       wil_dbg_misc(wil, "netif_tx_wake : link on\n");
        netif_tx_wake_all_queues(ndev);
 }
 
@@ -475,6 +484,7 @@ void wil_link_off(struct wil6210_priv *wil)
        wil_dbg_misc(wil, "%s()\n", __func__);
 
        netif_tx_stop_all_queues(ndev);
+       wil_dbg_misc(wil, "netif_tx_stop : link off\n");
        netif_carrier_off(ndev);
 }
 
@@ -552,6 +562,8 @@ static int __wil_down(struct wil6210_priv *wil)
        napi_disable(&wil->napi_tx);
 
        if (wil->scan_request) {
+               wil_dbg_misc(wil, "Abort scan_request 0x%p\n",
+                            wil->scan_request);
                del_timer_sync(&wil->scan_timer);
                cfg80211_scan_done(wil->scan_request, true);
                wil->scan_request = NULL;
index 1e2e07b..77b6272 100644 (file)
@@ -15,7 +15,6 @@
  */
 
 #include <linux/module.h>
-#include <linux/debugfs.h>
 #include <linux/pci.h>
 #include <linux/moduleparam.h>
 
@@ -27,11 +26,22 @@ MODULE_PARM_DESC(use_msi,
                 " Use MSI interrupt: "
                 "0 - don't, 1 - (default) - single, or 3");
 
+static bool debug_fw; /* = false; */
+module_param(debug_fw, bool, S_IRUGO);
+MODULE_PARM_DESC(debug_fw, " load driver if FW not ready. For FW debug");
+
 /* Bus ops */
 static int wil_if_pcie_enable(struct wil6210_priv *wil)
 {
        struct pci_dev *pdev = wil->pdev;
        int rc;
+       /* on platforms with buggy ACPI, pdev->msi_enabled may be set to
+        * allow pci_enable_device to work. This indicates INTx was not routed
+        * and only MSI should be used
+        */
+       int msi_only = pdev->msi_enabled;
+
+       pdev->msi_enabled = 0;
 
        pci_set_master(pdev);
 
@@ -63,6 +73,12 @@ static int wil_if_pcie_enable(struct wil6210_priv *wil)
 
        wil->n_msi = use_msi;
 
+       if ((wil->n_msi == 0) && msi_only) {
+               wil_err(wil, "Interrupt pin not routed, unable to use INTx\n");
+               rc = -ENODEV;
+               goto stop_master;
+       }
+
        rc = wil6210_init_irq(wil, pdev->irq);
        if (rc)
                goto stop_master;
@@ -71,6 +87,8 @@ static int wil_if_pcie_enable(struct wil6210_priv *wil)
        mutex_lock(&wil->mutex);
        rc = wil_reset(wil);
        mutex_unlock(&wil->mutex);
+       if (debug_fw)
+               rc = 0;
        if (rc)
                goto release_irq;
 
@@ -119,9 +137,16 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 
        rc = pci_enable_device(pdev);
        if (rc) {
-               dev_err(&pdev->dev, "pci_enable_device failed\n");
-               return -ENODEV;
+               dev_err(&pdev->dev,
+                       "pci_enable_device failed, retry with MSI only\n");
+               /* Work around for platforms that can't allocate IRQ:
+                * retry with MSI only
+                */
+               pdev->msi_enabled = 1;
+               rc = pci_enable_device(pdev);
        }
+       if (rc)
+               return -ENODEV;
        /* rollback to err_disable_pdev */
 
        rc = pci_request_region(pdev, 0, WIL_NAME);
index 747ae12..180ca47 100644 (file)
@@ -116,6 +116,7 @@ void wil_rx_reorder(struct wil6210_priv *wil, struct sk_buff *skb)
 
        /* frame with out of date sequence number */
        if (seq_less(seq, r->head_seq_num)) {
+               r->ssn_last_drop = seq;
                dev_kfree_skb(skb);
                goto out;
        }
index 0784ef3..af4b93e 100644 (file)
@@ -525,6 +525,17 @@ void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev)
                ndev->stats.rx_bytes += len;
                stats->rx_bytes += len;
        }
+       {
+               static const char * const gro_res_str[] = {
+                       [GRO_MERGED]            = "GRO_MERGED",
+                       [GRO_MERGED_FREE]       = "GRO_MERGED_FREE",
+                       [GRO_HELD]              = "GRO_HELD",
+                       [GRO_NORMAL]            = "GRO_NORMAL",
+                       [GRO_DROP]              = "GRO_DROP",
+               };
+               wil_dbg_txrx(wil, "Rx complete %d bytes => %s,\n",
+                            len, gro_res_str[rc]);
+       }
 }
 
 /**
@@ -760,7 +771,7 @@ static struct vring *wil_tx_bcast(struct wil6210_priv *wil,
                goto found;
        }
 
-       wil_err(wil, "Tx while no vrings active?\n");
+       wil_dbg_txrx(wil, "Tx while no vrings active?\n");
 
        return NULL;
 
@@ -881,6 +892,7 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
        int nr_frags = skb_shinfo(skb)->nr_frags;
        uint f = 0;
        int vring_index = vring - wil->vring_tx;
+       struct vring_tx_data *txdata = &wil->vring_tx_data[vring_index];
        uint i = swhead;
        dma_addr_t pa;
 
@@ -953,6 +965,9 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
        wil_hex_dump_txrx("Tx ", DUMP_PREFIX_NONE, 32, 4,
                          (const void *)d, sizeof(*d), false);
 
+       if (wil_vring_is_empty(vring)) /* performance monitoring */
+               txdata->idle += get_cycles() - txdata->last_idle;
+
        /* advance swhead */
        wil_vring_advance_head(vring, nr_frags + 1);
        wil_dbg_txrx(wil, "Tx swhead %d -> %d\n", swhead, vring->swhead);
@@ -1016,15 +1031,17 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
                vring = wil_tx_bcast(wil, skb);
        }
        if (!vring) {
-               wil_err(wil, "No Tx VRING found for %pM\n", eth->h_dest);
+               wil_dbg_txrx(wil, "No Tx VRING found for %pM\n", eth->h_dest);
                goto drop;
        }
        /* set up vring entry */
        rc = wil_tx_vring(wil, vring, skb);
 
        /* do we still have enough room in the vring? */
-       if (wil_vring_avail_tx(vring) < wil_vring_wmark_low(vring))
+       if (wil_vring_avail_tx(vring) < wil_vring_wmark_low(vring)) {
                netif_tx_stop_all_queues(wil_to_ndev(wil));
+               wil_dbg_txrx(wil, "netif_tx_stop : ring full\n");
+       }
 
        switch (rc) {
        case 0:
@@ -1132,8 +1149,16 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid)
                        done++;
                }
        }
-       if (wil_vring_avail_tx(vring) > wil_vring_wmark_high(vring))
+
+       if (wil_vring_is_empty(vring)) { /* performance monitoring */
+               wil_dbg_txrx(wil, "Ring[%2d] empty\n", ringid);
+               txdata->last_idle = get_cycles();
+       }
+
+       if (wil_vring_avail_tx(vring) > wil_vring_wmark_high(vring)) {
+               wil_dbg_txrx(wil, "netif_tx_wake : ring not full\n");
                netif_tx_wake_all_queues(wil_to_ndev(wil));
+       }
 
        return done;
 }
index e25edc5..4249066 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/netdevice.h>
 #include <linux/wireless.h>
 #include <net/cfg80211.h>
+#include <linux/timex.h>
 
 #define WIL_NAME "wil6210"
 
@@ -251,7 +252,7 @@ struct vring {
  */
 struct vring_tx_data {
        int enabled;
-
+       cycles_t idle, last_idle, begin;
 };
 
 enum { /* for wil6210_priv.status */
@@ -303,6 +304,7 @@ struct wil_tid_ampdu_rx {
        u16 ssn;
        u16 buf_size;
        u16 timeout;
+       u16 ssn_last_drop;
        u8 dialog_token;
        bool first_time; /* is it 1-st time this buffer used? */
 };
@@ -410,6 +412,7 @@ struct wil6210_priv {
        struct mutex mutex; /* for wil6210_priv access in wil_{up|down} */
        /* statistics */
        struct wil6210_stats stats;
+       atomic_t isr_count_rx, isr_count_tx;
        /* debugfs */
        struct dentry *debug;
        struct debugfs_blob_wrapper fw_code_blob;
@@ -504,9 +507,14 @@ int wil6210_init_irq(struct wil6210_priv *wil, int irq);
 void wil6210_fini_irq(struct wil6210_priv *wil, int irq);
 void wil6210_disable_irq(struct wil6210_priv *wil);
 void wil6210_enable_irq(struct wil6210_priv *wil);
+int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
+                        struct cfg80211_mgmt_tx_params *params,
+                        u64 *cookie);
 
 int wil6210_debugfs_init(struct wil6210_priv *wil);
 void wil6210_debugfs_remove(struct wil6210_priv *wil);
+int wil_cid_fill_sinfo(struct wil6210_priv *wil, int cid,
+                      struct station_info *sinfo);
 
 struct wireless_dev *wil_cfg80211_init(struct device *dev);
 void wil_wdev_free(struct wil6210_priv *wil);
index 6cc0e18..a136dab 100644 (file)
@@ -75,6 +75,7 @@ static const struct {
        {0x800000, 0x808000, 0x900000}, /* FW data RAM 32k */
        {0x840000, 0x860000, 0x908000}, /* peripheral data RAM 128k/96k used */
        {0x880000, 0x88a000, 0x880000}, /* various RGF */
+       {0x88b000, 0x88c000, 0x88b000}, /* Pcie_ext_rgf */
        {0x8c0000, 0x949000, 0x8c0000}, /* trivial mapping for upper area */
        /*
         * 920000..930000 ucode code RAM
@@ -327,6 +328,17 @@ static void wmi_evt_rx_mgmt(struct wil6210_priv *wil, int id, void *d, int len)
 
        if (ieee80211_is_beacon(fc) || ieee80211_is_probe_resp(fc)) {
                struct cfg80211_bss *bss;
+               u64 tsf = le64_to_cpu(rx_mgmt_frame->u.beacon.timestamp);
+               u16 cap = le16_to_cpu(rx_mgmt_frame->u.beacon.capab_info);
+               u16 bi = le16_to_cpu(rx_mgmt_frame->u.beacon.beacon_int);
+               const u8 *ie_buf = rx_mgmt_frame->u.beacon.variable;
+               size_t ie_len = d_len - offsetof(struct ieee80211_mgmt,
+                                                u.beacon.variable);
+               wil_dbg_wmi(wil, "Capability info : 0x%04x\n", cap);
+               wil_dbg_wmi(wil, "TSF : 0x%016llx\n", tsf);
+               wil_dbg_wmi(wil, "Beacon interval : %d\n", bi);
+               wil_hex_dump_wmi("IE ", DUMP_PREFIX_OFFSET, 16, 1, ie_buf,
+                                ie_len, true);
 
                bss = cfg80211_inform_bss_frame(wiphy, channel, rx_mgmt_frame,
                                                d_len, signal, GFP_KERNEL);
@@ -351,6 +363,9 @@ static void wmi_evt_scan_complete(struct wil6210_priv *wil, int id,
                bool aborted = (data->status != WMI_SCAN_SUCCESS);
 
                wil_dbg_wmi(wil, "SCAN_COMPLETE(0x%08x)\n", data->status);
+               wil_dbg_misc(wil, "Complete scan_request 0x%p aborted %d\n",
+                            wil->scan_request, aborted);
+
                del_timer_sync(&wil->scan_timer);
                cfg80211_scan_done(wil->scan_request, aborted);
                wil->scan_request = NULL;
@@ -668,14 +683,12 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
 
        for (n = 0;; n++) {
                u16 len;
+               bool q;
 
                r->head = ioread32(wil->csr + HOST_MBOX +
                                   offsetof(struct wil6210_mbox_ctl, rx.head));
-               if (r->tail == r->head) {
-                       if (n == 0)
-                               wil_dbg_wmi(wil, "No events?\n");
-                       return;
-               }
+               if (r->tail == r->head)
+                       break;
 
                wil_dbg_wmi(wil, "Mbox head %08x tail %08x\n",
                            r->head, r->tail);
@@ -684,14 +697,14 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
                                     sizeof(struct wil6210_mbox_ring_desc));
                if (d_tail.sync == 0) {
                        wil_err(wil, "Mbox evt not owned by FW?\n");
-                       return;
+                       break;
                }
 
                /* read cmd header from descriptor */
                if (0 != wmi_read_hdr(wil, d_tail.addr, &hdr)) {
                        wil_err(wil, "Mbox evt at 0x%08x?\n",
                                le32_to_cpu(d_tail.addr));
-                       return;
+                       break;
                }
                len = le16_to_cpu(hdr.len);
                wil_dbg_wmi(wil, "Mbox evt %04x %04x %04x %02x\n",
@@ -705,7 +718,7 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
                                             event.wmi) + len, 4),
                              GFP_KERNEL);
                if (!evt)
-                       return;
+                       break;
 
                evt->event.hdr = hdr;
                cmd = (void *)&evt->event.wmi;
@@ -737,14 +750,11 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
                spin_lock_irqsave(&wil->wmi_ev_lock, flags);
                list_add_tail(&evt->list, &wil->pending_wmi_ev);
                spin_unlock_irqrestore(&wil->wmi_ev_lock, flags);
-               {
-                       int q = queue_work(wil->wmi_wq,
-                                          &wil->wmi_event_worker);
-                       wil_dbg_wmi(wil, "queue_work -> %d\n", q);
-               }
+               q = queue_work(wil->wmi_wq, &wil->wmi_event_worker);
+               wil_dbg_wmi(wil, "queue_work -> %d\n", q);
        }
-       if (n > 1)
-               wil_dbg_wmi(wil, "%s -> %d events processed\n", __func__, n);
+       /* normally, 1 event per IRQ should be processed */
+       wil_dbg_wmi(wil, "%s -> %d events queued\n", __func__, n);
 }
 
 int wmi_call(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len,
index e3f67b8..037a4e3 100644 (file)
@@ -122,6 +122,15 @@ config B43_PIO
        select SSB_BLOCKIO
        default y
 
+config B43_PHY_G
+       bool "Support for G-PHY (802.11g) devices"
+       depends on B43 && B43_SSB
+       default y
+       ---help---
+         This PHY type can be found in the following chipsets:
+         PCI: BCM4306, BCM4311, BCM4318
+         SoC: BCM4712, BCM5352E
+
 config B43_PHY_N
        bool "Support for 802.11n (N-PHY) devices"
        depends on B43
index 098fe9e..6e00b88 100644 (file)
@@ -1,13 +1,11 @@
 b43-y                          += main.o
 b43-y                          += bus.o
-b43-y                          += tables.o
+b43-$(CONFIG_B43_PHY_G)                += phy_a.o phy_g.o tables.o lo.o wa.o
 b43-$(CONFIG_B43_PHY_N)                += tables_nphy.o
 b43-$(CONFIG_B43_PHY_N)                += radio_2055.o
 b43-$(CONFIG_B43_PHY_N)                += radio_2056.o
 b43-$(CONFIG_B43_PHY_N)                += radio_2057.o
 b43-y                          += phy_common.o
-b43-y                          += phy_g.o
-b43-y                          += phy_a.o
 b43-$(CONFIG_B43_PHY_N)                += phy_n.o
 b43-$(CONFIG_B43_PHY_LP)       += phy_lp.o
 b43-$(CONFIG_B43_PHY_LP)       += tables_lpphy.o
@@ -17,8 +15,6 @@ b43-$(CONFIG_B43_PHY_HT)      += radio_2059.o
 b43-$(CONFIG_B43_PHY_LCN)      += phy_lcn.o tables_phy_lcn.o
 b43-y                          += sysfs.o
 b43-y                          += xmit.o
-b43-y                          += lo.o
-b43-y                          += wa.o
 b43-y                          += dma.o
 b43-y                          += pio.o
 b43-y                          += rfkill.o
index 32538ac..9cf07bb 100644 (file)
@@ -3798,38 +3798,29 @@ static void b43_set_retry_limits(struct b43_wldev *dev,
 static int b43_op_config(struct ieee80211_hw *hw, u32 changed)
 {
        struct b43_wl *wl = hw_to_b43_wl(hw);
-       struct b43_wldev *dev;
-       struct b43_phy *phy;
+       struct b43_wldev *dev = wl->current_dev;
+       struct b43_phy *phy = &dev->phy;
        struct ieee80211_conf *conf = &hw->conf;
        int antenna;
        int err = 0;
-       bool reload_bss = false;
 
        mutex_lock(&wl->mutex);
-
-       dev = wl->current_dev;
-
        b43_mac_suspend(dev);
 
-       /* Switch the band (if necessary). This might change the active core. */
-       err = b43_switch_band(dev, conf->chandef.chan);
-       if (err)
-               goto out_unlock_mutex;
-
-       /* Need to reload all settings if the core changed */
-       if (dev != wl->current_dev) {
-               dev = wl->current_dev;
-               changed = ~0;
-               reload_bss = true;
-       }
+       if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
+               phy->chandef = &conf->chandef;
+               phy->channel = conf->chandef.chan->hw_value;
 
-       phy = &dev->phy;
+               /* Switch the band (if necessary). */
+               err = b43_switch_band(dev, conf->chandef.chan);
+               if (err)
+                       goto out_mac_enable;
 
-       if (conf_is_ht(conf))
-               phy->is_40mhz =
-                       (conf_is_ht40_minus(conf) || conf_is_ht40_plus(conf));
-       else
-               phy->is_40mhz = false;
+               /* Switch to the requested channel.
+                * The firmware takes care of races with the TX handler.
+                */
+               b43_switch_channel(dev, phy->channel);
+       }
 
        if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS)
                b43_set_retry_limits(dev, conf->short_frame_max_tx_count,
@@ -3838,11 +3829,6 @@ static int b43_op_config(struct ieee80211_hw *hw, u32 changed)
        if (!changed)
                goto out_mac_enable;
 
-       /* Switch to the requested channel.
-        * The firmware takes care of races with the TX handler. */
-       if (conf->chandef.chan->hw_value != phy->channel)
-               b43_switch_channel(dev, conf->chandef.chan->hw_value);
-
        dev->wl->radiotap_enabled = !!(conf->flags & IEEE80211_CONF_MONITOR);
 
        /* Adjust the desired TX power level. */
@@ -3878,12 +3864,8 @@ static int b43_op_config(struct ieee80211_hw *hw, u32 changed)
 
 out_mac_enable:
        b43_mac_enable(dev);
-out_unlock_mutex:
        mutex_unlock(&wl->mutex);
 
-       if (wl->vif && reload_bss)
-               b43_op_bss_info_changed(hw, wl->vif, &wl->vif->bss_conf, ~0);
-
        return err;
 }
 
@@ -4324,19 +4306,12 @@ static int b43_phy_versioning(struct b43_wldev *dev)
        phy_type = (tmp & B43_PHYVER_TYPE) >> B43_PHYVER_TYPE_SHIFT;
        phy_rev = (tmp & B43_PHYVER_VERSION);
        switch (phy_type) {
-       case B43_PHYTYPE_A:
-               if (phy_rev >= 4)
-                       unsupported = 1;
-               break;
-       case B43_PHYTYPE_B:
-               if (phy_rev != 2 && phy_rev != 4 && phy_rev != 6
-                   && phy_rev != 7)
-                       unsupported = 1;
-               break;
+#ifdef CONFIG_B43_PHY_G
        case B43_PHYTYPE_G:
                if (phy_rev > 9)
                        unsupported = 1;
                break;
+#endif
 #ifdef CONFIG_B43_PHY_N
        case B43_PHYTYPE_N:
                if (phy_rev > 9)
index 5cfaab7..f7d0d92 100644 (file)
@@ -123,8 +123,4 @@ struct b43_phy_a {
  */
 void b43_phy_inita(struct b43_wldev *dev);
 
-
-struct b43_phy_operations;
-extern const struct b43_phy_operations b43_phyops_a;
-
 #endif /* LINUX_B43_PHY_A_H_ */
index 08244b3..3cbef21 100644 (file)
@@ -45,11 +45,10 @@ int b43_phy_allocate(struct b43_wldev *dev)
        phy->ops = NULL;
 
        switch (phy->type) {
-       case B43_PHYTYPE_A:
-               phy->ops = &b43_phyops_a;
-               break;
        case B43_PHYTYPE_G:
+#ifdef CONFIG_B43_PHY_G
                phy->ops = &b43_phyops_g;
+#endif
                break;
        case B43_PHYTYPE_N:
 #ifdef CONFIG_B43_PHY_N
@@ -94,7 +93,13 @@ int b43_phy_init(struct b43_wldev *dev)
        const struct b43_phy_operations *ops = phy->ops;
        int err;
 
-       phy->channel = ops->get_default_chan(dev);
+       /* During PHY init we need to use some channel. On the first init this
+        * function is called *before* b43_op_config, so our pointer is NULL.
+        */
+       if (!phy->chandef) {
+               phy->chandef = &dev->wl->hw->conf.chandef;
+               phy->channel = phy->chandef->chan->hw_value;
+       }
 
        phy->ops->switch_analog(dev, true);
        b43_software_rfkill(dev, false);
@@ -106,9 +111,7 @@ int b43_phy_init(struct b43_wldev *dev)
        }
        phy->do_full_init = false;
 
-       /* Make sure to switch hardware and firmware (SHM) to
-        * the default channel. */
-       err = b43_switch_channel(dev, ops->get_default_chan(dev));
+       err = b43_switch_channel(dev, phy->channel);
        if (err) {
                b43err(dev->wl, "PHY init: Channel switch to default failed\n");
                goto err_phy_exit;
@@ -408,9 +411,6 @@ int b43_switch_channel(struct b43_wldev *dev, unsigned int new_channel)
        u16 channelcookie, savedcookie;
        int err;
 
-       if (new_channel == B43_DEFAULT_CHANNEL)
-               new_channel = phy->ops->get_default_chan(dev);
-
        /* First we set the channel radio code to prevent the
         * firmware from sending ghost packets.
         */
@@ -428,7 +428,6 @@ int b43_switch_channel(struct b43_wldev *dev, unsigned int new_channel)
        if (err)
                goto err_restore_cookie;
 
-       dev->phy.channel = new_channel;
        /* Wait for the radio to tune to the channel and stabilize. */
        msleep(8);
 
@@ -547,10 +546,9 @@ void b43_phyop_switch_analog_generic(struct b43_wldev *dev, bool on)
 }
 
 
-bool b43_channel_type_is_40mhz(enum nl80211_channel_type channel_type)
+bool b43_is_40mhz(struct b43_wldev *dev)
 {
-       return (channel_type == NL80211_CHAN_HT40MINUS ||
-               channel_type == NL80211_CHAN_HT40PLUS);
+       return dev->phy.chandef->width == NL80211_CHAN_WIDTH_40;
 }
 
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/BmacPhyClkFgc */
index 4ad6240..3912274 100644 (file)
@@ -228,9 +228,6 @@ struct b43_phy {
        bool supports_2ghz;
        bool supports_5ghz;
 
-       /* HT info */
-       bool is_40mhz;
-
        /* Is GMODE (2 GHz mode) bit enabled? */
        bool gmode;
 
@@ -267,9 +264,8 @@ struct b43_phy {
        unsigned long next_txpwr_check_time;
 
        /* Current channel */
+       struct cfg80211_chan_def *chandef;
        unsigned int channel;
-       u16 channel_freq;
-       enum nl80211_channel_type channel_type;
 
        /* PHY TX errors counter. */
        atomic_t txerr_cnt;
@@ -400,10 +396,6 @@ void b43_phy_take_out_of_reset(struct b43_wldev *dev);
  * b43_switch_channel - Switch to another channel
  */
 int b43_switch_channel(struct b43_wldev *dev, unsigned int new_channel);
-/**
- * B43_DEFAULT_CHANNEL - Switch to the default channel.
- */
-#define B43_DEFAULT_CHANNEL    UINT_MAX
 
 /**
  * b43_software_rfkill - Turn the radio ON or OFF in software.
@@ -454,7 +446,7 @@ int b43_phy_shm_tssi_read(struct b43_wldev *dev, u16 shm_offset);
  */
 void b43_phyop_switch_analog_generic(struct b43_wldev *dev, bool on);
 
-bool b43_channel_type_is_40mhz(enum nl80211_channel_type channel_type);
+bool b43_is_40mhz(struct b43_wldev *dev);
 
 void b43_phy_force_clock(struct b43_wldev *dev, bool force);
 
index 5d6833f..f2974c6 100644 (file)
@@ -596,7 +596,7 @@ static void b43_phy_ht_tx_power_ctl_setup(struct b43_wldev *dev)
        u8 target[3];
        s16 a1[3], b0[3], b1[3];
 
-       u16 freq = dev->phy.channel_freq;
+       u16 freq = dev->phy.chandef->chan->center_freq;
        int i, c;
 
        if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
index 86569f6..6398c7e 100644 (file)
@@ -798,6 +798,7 @@ static void b43_chantab_radio_2056_upload(struct b43_wldev *dev,
 static void b43_radio_2056_setup(struct b43_wldev *dev,
                                const struct b43_nphy_channeltab_entry_rev3 *e)
 {
+       struct b43_phy *phy = &dev->phy;
        struct ssb_sprom *sprom = dev->dev->bus_sprom;
        enum ieee80211_band band = b43_current_band(dev->wl);
        u16 offset;
@@ -895,7 +896,7 @@ static void b43_radio_2056_setup(struct b43_wldev *dev,
                                        offset | B2056_TX_MIXG_BOOST_TUNE,
                                        mixg_boost);
                        } else {
-                               bias = dev->phy.is_40mhz ? 0x40 : 0x20;
+                               bias = b43_is_40mhz(dev) ? 0x40 : 0x20;
                                b43_radio_write(dev,
                                        offset | B2056_TX_INTPAG_IMAIN_STAT,
                                        bias);
@@ -909,7 +910,7 @@ static void b43_radio_2056_setup(struct b43_wldev *dev,
                        b43_radio_write(dev, offset | B2056_TX_PA_SPARE1, 0xee);
                }
        } else if (dev->phy.n->ipa5g_on && band == IEEE80211_BAND_5GHZ) {
-               u16 freq = dev->phy.channel_freq;
+               u16 freq = phy->chandef->chan->center_freq;
                if (freq < 5100) {
                        paa_boost = 0xA;
                        pada_boost = 0x77;
@@ -1210,8 +1211,7 @@ static u16 b43_nphy_gen_load_samples(struct b43_wldev *dev, u32 freq, u16 max,
        u16 bw, len, rot, angle;
        struct b43_c32 *samples;
 
-
-       bw = (dev->phy.is_40mhz) ? 40 : 20;
+       bw = b43_is_40mhz(dev) ? 40 : 20;
        len = bw << 3;
 
        if (test) {
@@ -1220,7 +1220,7 @@ static u16 b43_nphy_gen_load_samples(struct b43_wldev *dev, u32 freq, u16 max,
                else
                        bw = 80;
 
-               if (dev->phy.is_40mhz)
+               if (b43_is_40mhz(dev))
                        bw <<= 1;
 
                len = bw << 1;
@@ -1263,7 +1263,7 @@ static void b43_nphy_run_samples(struct b43_wldev *dev, u16 samps, u16 loops,
        }
 
        /* TODO: add modify_bbmult argument */
-       if (!dev->phy.is_40mhz)
+       if (!b43_is_40mhz(dev))
                tmp = 0x6464;
        else
                tmp = 0x4747;
@@ -1675,6 +1675,7 @@ static int b43_nphy_poll_rssi(struct b43_wldev *dev, enum n_rssi_type rssi_type,
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RSSICalRev3 */
 static void b43_nphy_rev3_rssi_cal(struct b43_wldev *dev)
 {
+       struct b43_phy *phy = &dev->phy;
        struct b43_phy_n *nphy = dev->phy.n;
 
        u16 saved_regs_phy_rfctl[2];
@@ -1897,9 +1898,9 @@ static void b43_nphy_rev3_rssi_cal(struct b43_wldev *dev)
 
        /* Remember for which channel we store configuration */
        if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ)
-               nphy->rssical_chanspec_2G.center_freq = dev->phy.channel_freq;
+               nphy->rssical_chanspec_2G.center_freq = phy->chandef->chan->center_freq;
        else
-               nphy->rssical_chanspec_5G.center_freq = dev->phy.channel_freq;
+               nphy->rssical_chanspec_5G.center_freq = phy->chandef->chan->center_freq;
 
        /* End of calibration, restore configuration */
        b43_nphy_classifier(dev, 7, class);
@@ -2192,7 +2193,7 @@ static void b43_nphy_gain_ctl_workarounds_rev1_2(struct b43_wldev *dev)
        b43_phy_write(dev, B43_NPHY_C1_NBCLIPTHRES, 0x84);
        b43_phy_write(dev, B43_NPHY_C2_NBCLIPTHRES, 0x84);
 
-       if (!dev->phy.is_40mhz) {
+       if (!b43_is_40mhz(dev)) {
                /* Set dwell lengths */
                b43_phy_write(dev, B43_NPHY_CLIP1_NBDWELL_LEN, 0x002B);
                b43_phy_write(dev, B43_NPHY_CLIP2_NBDWELL_LEN, 0x002B);
@@ -2206,7 +2207,7 @@ static void b43_nphy_gain_ctl_workarounds_rev1_2(struct b43_wldev *dev)
        b43_phy_maskset(dev, B43_NPHY_C2_CLIPWBTHRES,
                        ~B43_NPHY_C2_CLIPWBTHRES_CLIP2, 21);
 
-       if (!dev->phy.is_40mhz) {
+       if (!b43_is_40mhz(dev)) {
                b43_phy_maskset(dev, B43_NPHY_C1_CGAINI,
                        ~B43_NPHY_C1_CGAINI_GAINBKOFF, 0x1);
                b43_phy_maskset(dev, B43_NPHY_C2_CGAINI,
@@ -2221,12 +2222,12 @@ static void b43_nphy_gain_ctl_workarounds_rev1_2(struct b43_wldev *dev)
 
        if (nphy->gain_boost) {
                if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ &&
-                       dev->phy.is_40mhz)
+                   b43_is_40mhz(dev))
                        code = 4;
                else
                        code = 5;
        } else {
-               code = dev->phy.is_40mhz ? 6 : 7;
+               code = b43_is_40mhz(dev) ? 6 : 7;
        }
 
        /* Set HPVGA2 index */
@@ -2298,7 +2299,7 @@ static void b43_nphy_gain_ctl_workarounds(struct b43_wldev *dev)
 static u16 b43_nphy_read_lpf_ctl(struct b43_wldev *dev, u16 offset)
 {
        if (!offset)
-               offset = (dev->phy.is_40mhz) ? 0x159 : 0x154;
+               offset = b43_is_40mhz(dev) ? 0x159 : 0x154;
        return b43_ntab_read(dev, B43_NTAB16(7, offset)) & 0x7;
 }
 
@@ -2371,13 +2372,13 @@ static void b43_nphy_workarounds_rev7plus(struct b43_wldev *dev)
        lpf_40 = b43_nphy_read_lpf_ctl(dev, 0x159);
        lpf_11b = b43_nphy_read_lpf_ctl(dev, 0x152);
        if (b43_nphy_ipa(dev)) {
-               if ((phy->radio_rev == 5 && phy->is_40mhz) ||
+               if ((phy->radio_rev == 5 && b43_is_40mhz(dev)) ||
                    phy->radio_rev == 7 || phy->radio_rev == 8) {
                        bcap_val = b43_radio_read(dev, 0x16b);
                        scap_val = b43_radio_read(dev, 0x16a);
                        scap_val_11b = scap_val;
                        bcap_val_11b = bcap_val;
-                       if (phy->radio_rev == 5 && phy->is_40mhz) {
+                       if (phy->radio_rev == 5 && b43_is_40mhz(dev)) {
                                scap_val_11n_20 = scap_val;
                                bcap_val_11n_20 = bcap_val;
                                scap_val_11n_40 = bcap_val_11n_40 = 0xc;
@@ -2519,7 +2520,7 @@ static void b43_nphy_workarounds_rev7plus(struct b43_wldev *dev)
                                        }
                                }
                        } else if (phy->radio_rev == 7 || phy->radio_rev == 8) {
-                               if (!phy->is_40mhz) {
+                               if (!b43_is_40mhz(dev)) {
                                        b43_radio_write(dev, 0x5F, 0x14);
                                        b43_radio_write(dev, 0xE8, 0x12);
                                } else {
@@ -2528,7 +2529,7 @@ static void b43_nphy_workarounds_rev7plus(struct b43_wldev *dev)
                                }
                        }
                } else {
-                       u16 freq = phy->channel_freq;
+                       u16 freq = phy->chandef->chan->center_freq;
                        if ((freq >= 5180 && freq <= 5230) ||
                            (freq >= 5745 && freq <= 5805)) {
                                b43_radio_write(dev, 0x7D, 0xFF);
@@ -2592,7 +2593,7 @@ static void b43_nphy_workarounds_rev7plus(struct b43_wldev *dev)
        b43_ntab_write(dev, B43_NTAB16(7, 0x123), 0x77);
        b43_ntab_write(dev, B43_NTAB16(7, 0x12A), 0x77);
 
-       if (!phy->is_40mhz) {
+       if (!b43_is_40mhz(dev)) {
                b43_ntab_write(dev, B43_NTAB32(16, 0x03), 0x18D);
                b43_ntab_write(dev, B43_NTAB32(16, 0x7F), 0x18D);
        } else {
@@ -2691,7 +2692,7 @@ static void b43_nphy_workarounds_rev3plus(struct b43_wldev *dev)
 
        b43_phy_maskset(dev, B43_NPHY_SGILTRNOFFSET, 0xF0FF, 0x0700);
 
-       if (!dev->phy.is_40mhz) {
+       if (!b43_is_40mhz(dev)) {
                b43_ntab_write(dev, B43_NTAB32(16, 3), 0x18D);
                b43_ntab_write(dev, B43_NTAB32(16, 127), 0x18D);
        } else {
@@ -3114,7 +3115,7 @@ static void b43_nphy_tx_power_ctrl(struct b43_wldev *dev, bool enable)
                        b43_phy_maskset(dev, B43_NPHY_BPHY_CTL3,
                                ~B43_NPHY_BPHY_CTL3_SCALE, 0x5A);
 
-               if (dev->phy.rev < 2 && dev->phy.is_40mhz)
+               if (dev->phy.rev < 2 && b43_is_40mhz(dev))
                        b43_hf_write(dev, b43_hf_read(dev) | B43_HF_TSSIRPSMW);
        } else {
                b43_ntab_write_bulk(dev, B43_NTAB16(26, 64), 84,
@@ -3168,7 +3169,7 @@ static void b43_nphy_tx_power_ctrl(struct b43_wldev *dev, bool enable)
                else if (dev->phy.rev < 2)
                        b43_phy_maskset(dev, B43_NPHY_BPHY_CTL3, ~0xFF, 0x40);
 
-               if (dev->phy.rev < 2 && dev->phy.is_40mhz)
+               if (dev->phy.rev < 2 && b43_is_40mhz(dev))
                        b43_hf_write(dev, b43_hf_read(dev) & ~B43_HF_TSSIRPSMW);
 
                if (b43_nphy_ipa(dev)) {
@@ -3184,12 +3185,13 @@ static void b43_nphy_tx_power_ctrl(struct b43_wldev *dev, bool enable)
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxPwrFix */
 static void b43_nphy_tx_power_fix(struct b43_wldev *dev)
 {
+       struct b43_phy *phy = &dev->phy;
        struct b43_phy_n *nphy = dev->phy.n;
        struct ssb_sprom *sprom = dev->dev->bus_sprom;
 
        u8 txpi[2], bbmult, i;
        u16 tmp, radio_gain, dac_gain;
-       u16 freq = dev->phy.channel_freq;
+       u16 freq = phy->chandef->chan->center_freq;
        u32 txgain;
        /* u32 gaintbl; rev3+ */
 
@@ -3439,21 +3441,21 @@ static void b43_nphy_tx_prepare_adjusted_power_table(struct b43_wldev *dev)
                delta = 0;
                switch (stf_mode) {
                case 0:
-                       if (dev->phy.is_40mhz && dev->phy.rev >= 5) {
+                       if (b43_is_40mhz(dev) && dev->phy.rev >= 5) {
                                idx = 68;
                        } else {
                                delta = 1;
-                               idx = dev->phy.is_40mhz ? 52 : 4;
+                               idx = b43_is_40mhz(dev) ? 52 : 4;
                        }
                        break;
                case 1:
-                       idx = dev->phy.is_40mhz ? 76 : 28;
+                       idx = b43_is_40mhz(dev) ? 76 : 28;
                        break;
                case 2:
-                       idx = dev->phy.is_40mhz ? 84 : 36;
+                       idx = b43_is_40mhz(dev) ? 84 : 36;
                        break;
                case 3:
-                       idx = dev->phy.is_40mhz ? 92 : 44;
+                       idx = b43_is_40mhz(dev) ? 92 : 44;
                        break;
                }
 
@@ -3474,6 +3476,7 @@ static void b43_nphy_tx_prepare_adjusted_power_table(struct b43_wldev *dev)
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxPwrCtrlSetup */
 static void b43_nphy_tx_power_ctl_setup(struct b43_wldev *dev)
 {
+       struct b43_phy *phy = &dev->phy;
        struct b43_phy_n *nphy = dev->phy.n;
        struct ssb_sprom *sprom = dev->dev->bus_sprom;
 
@@ -3483,7 +3486,7 @@ static void b43_nphy_tx_power_ctl_setup(struct b43_wldev *dev)
        s32 num, den, pwr;
        u32 regval[64];
 
-       u16 freq = dev->phy.channel_freq;
+       u16 freq = phy->chandef->chan->center_freq;
        u16 tmp;
        u16 r; /* routing */
        u8 i, c;
@@ -3992,7 +3995,7 @@ static void b43_nphy_spur_workaround(struct b43_wldev *dev)
 
        if (nphy->gband_spurwar_en) {
                /* TODO: N PHY Adjust Analog Pfbw (7) */
-               if (channel == 11 && dev->phy.is_40mhz)
+               if (channel == 11 && b43_is_40mhz(dev))
                        ; /* TODO: N PHY Adjust Min Noise Var(2, tone, noise)*/
                else
                        ; /* TODO: N PHY Adjust Min Noise Var(0, NULL, NULL)*/
@@ -4286,7 +4289,7 @@ static void b43_nphy_int_pa_set_tx_dig_filters(struct b43_wldev *dev)
                        b43_phy_write(dev, B43_PHY_N(offset[i] + j),
                                        tbl_tx_filter_coef_rev4[i][j]);
 
-       if (dev->phy.is_40mhz) {
+       if (b43_is_40mhz(dev)) {
                for (j = 0; j < 15; j++)
                        b43_phy_write(dev, B43_PHY_N(offset[0] + j),
                                        tbl_tx_filter_coef_rev4[3][j]);
@@ -4500,8 +4503,9 @@ static void b43_nphy_save_cal(struct b43_wldev *dev)
                txcal_radio_regs[2] = b43_radio_read(dev, 0x8D);
                txcal_radio_regs[3] = b43_radio_read(dev, 0xBC);
        }
-       iqcal_chanspec->center_freq = dev->phy.channel_freq;
-       iqcal_chanspec->channel_type = dev->phy.channel_type;
+       iqcal_chanspec->center_freq = dev->phy.chandef->chan->center_freq;
+       iqcal_chanspec->channel_type =
+                               cfg80211_get_chandef_type(dev->phy.chandef);
        b43_ntab_read_bulk(dev, B43_NTAB16(15, 80), 8, table);
 
        if (nphy->hang_avoid)
@@ -4581,6 +4585,7 @@ static int b43_nphy_cal_tx_iq_lo(struct b43_wldev *dev,
                                struct nphy_txgains target,
                                bool full, bool mphase)
 {
+       struct b43_phy *phy = &dev->phy;
        struct b43_phy_n *nphy = dev->phy.n;
        int i;
        int error = 0;
@@ -4621,7 +4626,7 @@ static int b43_nphy_cal_tx_iq_lo(struct b43_wldev *dev,
                (dev->phy.rev == 5 && nphy->ipa2g_on &&
                b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ);
        if (phy6or5x) {
-               if (dev->phy.is_40mhz) {
+               if (b43_is_40mhz(dev)) {
                        b43_ntab_write_bulk(dev, B43_NTAB16(15, 0), 18,
                                        tbl_tx_iqlo_cal_loft_ladder_40);
                        b43_ntab_write_bulk(dev, B43_NTAB16(15, 32), 18,
@@ -4636,13 +4641,13 @@ static int b43_nphy_cal_tx_iq_lo(struct b43_wldev *dev,
 
        b43_phy_write(dev, B43_NPHY_IQLOCAL_CMDGCTL, 0x8AA9);
 
-       if (!dev->phy.is_40mhz)
+       if (!b43_is_40mhz(dev))
                freq = 2500;
        else
                freq = 5000;
 
        if (nphy->mphase_cal_phase_id > 2)
-               b43_nphy_run_samples(dev, (dev->phy.is_40mhz ? 40 : 20) * 8,
+               b43_nphy_run_samples(dev, (b43_is_40mhz(dev) ? 40 : 20) * 8,
                                        0xFFFF, 0, true, false);
        else
                error = b43_nphy_tx_tone(dev, freq, 250, true, false);
@@ -4773,9 +4778,9 @@ static int b43_nphy_cal_tx_iq_lo(struct b43_wldev *dev,
                                                nphy->txiqlocal_bestc);
                        nphy->txiqlocal_coeffsvalid = true;
                        nphy->txiqlocal_chanspec.center_freq =
-                                                       dev->phy.channel_freq;
+                                               phy->chandef->chan->center_freq;
                        nphy->txiqlocal_chanspec.channel_type =
-                                                       dev->phy.channel_type;
+                                       cfg80211_get_chandef_type(phy->chandef);
                } else {
                        length = 11;
                        if (dev->phy.rev < 3)
@@ -4811,8 +4816,8 @@ static void b43_nphy_reapply_tx_cal_coeffs(struct b43_wldev *dev)
        bool equal = true;
 
        if (!nphy->txiqlocal_coeffsvalid ||
-           nphy->txiqlocal_chanspec.center_freq != dev->phy.channel_freq ||
-           nphy->txiqlocal_chanspec.channel_type != dev->phy.channel_type)
+           nphy->txiqlocal_chanspec.center_freq != dev->phy.chandef->chan->center_freq ||
+           nphy->txiqlocal_chanspec.channel_type != cfg80211_get_chandef_type(dev->phy.chandef))
                return;
 
        b43_ntab_read_bulk(dev, B43_NTAB16(15, 80), 7, buffer);
@@ -5437,7 +5442,7 @@ static void b43_nphy_channel_setup(struct b43_wldev *dev,
                bool avoid = false;
                if (dev->phy.n->spur_avoid == B43_SPUR_AVOID_FORCE) {
                        avoid = true;
-               } else if (!b43_channel_type_is_40mhz(phy->channel_type)) {
+               } else if (!b43_is_40mhz(dev)) {
                        if ((ch >= 5 && ch <= 8) || ch == 13 || ch == 14)
                                avoid = true;
                } else { /* 40MHz */
@@ -5502,11 +5507,12 @@ static int b43_nphy_set_channel(struct b43_wldev *dev,
        /* Channel is set later in common code, but we need to set it on our
           own to let this function's subcalls work properly. */
        phy->channel = channel->hw_value;
-       phy->channel_freq = channel->center_freq;
 
+#if 0
        if (b43_channel_type_is_40mhz(phy->channel_type) !=
                b43_channel_type_is_40mhz(channel_type))
                ; /* TODO: BMAC BW Set (channel_type) */
+#endif
 
        if (channel_type == NL80211_CHAN_HT40PLUS)
                b43_phy_set(dev, B43_NPHY_RXCTL,
index 4047c05..b221715 100644 (file)
@@ -3191,7 +3191,7 @@ struct nphy_gain_ctl_workaround_entry *b43_nphy_get_gain_ctl_workaround_ent(
        /* Some workarounds to the workarounds... */
        if (ghz5 && dev->phy.rev >= 6) {
                if (dev->phy.radio_rev == 11 &&
-                   !b43_channel_type_is_40mhz(dev->phy.channel_type))
+                   !b43_is_40mhz(dev))
                        e->cliplo_gain = 0x2d;
        } else if (!ghz5 && dev->phy.rev >= 5) {
                static const int gain_data[] = {0x0062, 0x0064, 0x006a, 0x106a,
index d8fa276..db3d848 100644 (file)
@@ -1331,7 +1331,6 @@ static s32
 brcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev)
 {
        struct brcmf_if *ifp = netdev_priv(ndev);
-       s32 err = 0;
 
        brcmf_dbg(TRACE, "Enter\n");
        if (!check_vif_up(ifp->vif))
@@ -1341,7 +1340,7 @@ brcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev)
 
        brcmf_dbg(TRACE, "Exit\n");
 
-       return err;
+       return 0;
 }
 
 static s32 brcmf_set_wpa_version(struct net_device *ndev,
@@ -2388,7 +2387,6 @@ static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg,
        struct cfg80211_bss *bss;
        struct ieee80211_supported_band *band;
        struct brcmu_chan ch;
-       s32 err = 0;
        u16 channel;
        u32 freq;
        u16 notify_capability;
@@ -2438,7 +2436,7 @@ static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg,
 
        cfg80211_put_bss(wiphy, bss);
 
-       return err;
+       return 0;
 }
 
 static struct brcmf_bss_info_le *
@@ -2690,7 +2688,6 @@ brcmf_cfg80211_escan_handler(struct brcmf_if *ifp,
 {
        struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
        s32 status;
-       s32 err = 0;
        struct brcmf_escan_result_le *escan_result_le;
        struct brcmf_bss_info_le *bss_info_le;
        struct brcmf_bss_info_le *bss = NULL;
@@ -2781,7 +2778,7 @@ brcmf_cfg80211_escan_handler(struct brcmf_if *ifp,
                                  status);
        }
 exit:
-       return err;
+       return 0;
 }
 
 static void brcmf_init_escan(struct brcmf_cfg80211_info *cfg)
@@ -3507,7 +3504,6 @@ static s32
 brcmf_parse_vndr_ies(const u8 *vndr_ie_buf, u32 vndr_ie_len,
                     struct parsed_vndr_ies *vndr_ies)
 {
-       s32 err = 0;
        struct brcmf_vs_tlv *vndrie;
        struct brcmf_tlv *ie;
        struct parsed_vndr_ie_info *parsed_info;
@@ -3560,7 +3556,7 @@ next:
                        ie = (struct brcmf_tlv *)(((u8 *)ie) + ie->len +
                                TLV_HDR_LEN);
        }
-       return err;
+       return 0;
 }
 
 static u32
@@ -4650,7 +4646,6 @@ brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg,
        struct brcmf_if *ifp = netdev_priv(ndev);
        struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
        struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
-       s32 err = 0;
 
        brcmf_dbg(TRACE, "Enter\n");
 
@@ -4676,7 +4671,7 @@ brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg,
                          completed ? "succeeded" : "failed");
        }
        brcmf_dbg(TRACE, "Exit\n");
-       return err;
+       return 0;
 }
 
 static s32
@@ -4768,7 +4763,6 @@ brcmf_notify_roaming_status(struct brcmf_if *ifp,
                            const struct brcmf_event_msg *e, void *data)
 {
        struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
-       s32 err = 0;
        u32 event = e->event_code;
        u32 status = e->status;
 
@@ -4779,7 +4773,7 @@ brcmf_notify_roaming_status(struct brcmf_if *ifp,
                        brcmf_bss_connect_done(cfg, ifp->ndev, e, true);
        }
 
-       return err;
+       return 0;
 }
 
 static s32
index b0fd807..57ecc05 100644 (file)
@@ -1538,11 +1538,7 @@ static s8
 wlc_user_txpwr_antport_to_rfport(struct brcms_phy *pi, uint chan, u32 band,
                                 u8 rate)
 {
-       s8 offset = 0;
-
-       if (!pi->user_txpwr_at_rfport)
-               return offset;
-       return offset;
+       return 0;
 }
 
 void wlc_phy_txpower_recalc_target(struct brcms_phy *pi)
index 9afcd4c..b2fb6c6 100644 (file)
@@ -53,9 +53,10 @@ static int cw1200_scan_start(struct cw1200_common *priv, struct wsm_scan *scan)
 
 int cw1200_hw_scan(struct ieee80211_hw *hw,
                   struct ieee80211_vif *vif,
-                  struct cfg80211_scan_request *req)
+                  struct ieee80211_scan_request *hw_req)
 {
        struct cw1200_common *priv = hw->priv;
+       struct cfg80211_scan_request *req = &hw_req->req;
        struct wsm_template_frame frame = {
                .frame_type = WSM_FRAME_TYPE_PROBE_REQUEST,
        };
index 5a8296c..cc75459 100644 (file)
@@ -41,7 +41,7 @@ struct cw1200_scan {
 
 int cw1200_hw_scan(struct ieee80211_hw *hw,
                   struct ieee80211_vif *vif,
-                  struct cfg80211_scan_request *req);
+                  struct ieee80211_scan_request *hw_req);
 void cw1200_scan_work(struct work_struct *work);
 void cw1200_scan_timeout(struct work_struct *work);
 void cw1200_clear_recent_scan_work(struct work_struct *work);
index cd0cad7..5b84664 100644 (file)
@@ -2289,7 +2289,6 @@ static int cw1200_upload_null(struct cw1200_common *priv)
 
 static int cw1200_upload_qosnull(struct cw1200_common *priv)
 {
-       int ret = 0;
        /* TODO:  This needs to be implemented
 
        struct wsm_template_frame frame = {
@@ -2306,7 +2305,7 @@ static int cw1200_upload_qosnull(struct cw1200_common *priv)
        dev_kfree_skb(frame.skb);
 
        */
-       return ret;
+       return 0;
 }
 
 static int cw1200_enable_beaconing(struct cw1200_common *priv,
index ecc6746..03de746 100644 (file)
@@ -1572,8 +1572,9 @@ il_scan_initiate(struct il_priv *il, struct ieee80211_vif *vif)
 
 int
 il_mac_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-              struct cfg80211_scan_request *req)
+              struct ieee80211_scan_request *hw_req)
 {
+       struct cfg80211_scan_request *req = &hw_req->req;
        struct il_priv *il = hw->priv;
        int ret;
 
index ea5c0f8..5b97279 100644 (file)
@@ -1787,7 +1787,7 @@ int il_scan_cancel(struct il_priv *il);
 int il_scan_cancel_timeout(struct il_priv *il, unsigned long ms);
 void il_force_scan_end(struct il_priv *il);
 int il_mac_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-                  struct cfg80211_scan_request *req);
+                  struct ieee80211_scan_request *hw_req);
 void il_internal_short_hw_scan(struct il_priv *il);
 int il_force_reset(struct il_priv *il, bool external);
 u16 il_fill_probe_req(struct il_priv *il, struct ieee80211_mgmt *frame,
index 29af7b5..afb98f4 100644 (file)
@@ -1495,9 +1495,10 @@ static int iwlagn_mac_change_interface(struct ieee80211_hw *hw,
 
 static int iwlagn_mac_hw_scan(struct ieee80211_hw *hw,
                              struct ieee80211_vif *vif,
-                             struct cfg80211_scan_request *req)
+                             struct ieee80211_scan_request *hw_req)
 {
        struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
+       struct cfg80211_scan_request *req = &hw_req->req;
        int ret;
 
        IWL_DEBUG_MAC80211(priv, "enter\n");
index 3c2c9b9..72e3146 100644 (file)
@@ -1549,9 +1549,10 @@ static void iwl_mvm_bss_info_changed(struct ieee80211_hw *hw,
 
 static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw,
                               struct ieee80211_vif *vif,
-                              struct cfg80211_scan_request *req)
+                              struct ieee80211_scan_request *hw_req)
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct cfg80211_scan_request *req = &hw_req->req;
        int ret;
 
        if (req->n_channels == 0 ||
@@ -1840,7 +1841,7 @@ static void iwl_mvm_mac_mgd_prepare_tx(struct ieee80211_hw *hw,
 static int iwl_mvm_mac_sched_scan_start(struct ieee80211_hw *hw,
                                        struct ieee80211_vif *vif,
                                        struct cfg80211_sched_scan_request *req,
-                                       struct ieee80211_sched_scan_ies *ies)
+                                       struct ieee80211_scan_ies *ies)
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
        int ret;
index da69258..f7e54a5 100644 (file)
@@ -867,7 +867,7 @@ int iwl_mvm_rx_scan_offload_complete_notif(struct iwl_mvm *mvm,
 int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm,
                              struct ieee80211_vif *vif,
                              struct cfg80211_sched_scan_request *req,
-                             struct ieee80211_sched_scan_ies *ies);
+                             struct ieee80211_scan_ies *ies);
 int iwl_mvm_config_sched_scan_profiles(struct iwl_mvm *mvm,
                                       struct cfg80211_sched_scan_request *req);
 int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm,
index 349d8e6..f2dde56 100644 (file)
@@ -204,7 +204,8 @@ static void iwl_mvm_scan_fill_channels(struct iwl_scan_cmd *cmd,
  */
 static u16 iwl_mvm_fill_probe_req(struct ieee80211_mgmt *frame, const u8 *ta,
                                  int n_ssids, const u8 *ssid, int ssid_len,
-                                 const u8 *ie, int ie_len,
+                                 const u8 *band_ie, int band_ie_len,
+                                 const u8 *common_ie, int common_ie_len,
                                  int left)
 {
        int len = 0;
@@ -244,12 +245,19 @@ static u16 iwl_mvm_fill_probe_req(struct ieee80211_mgmt *frame, const u8 *ta,
 
        len += ssid_len + 2;
 
-       if (WARN_ON(left < ie_len))
+       if (WARN_ON(left < band_ie_len + common_ie_len))
                return len;
 
-       if (ie && ie_len) {
-               memcpy(pos, ie, ie_len);
-               len += ie_len;
+       if (band_ie && band_ie_len) {
+               memcpy(pos, band_ie, band_ie_len);
+               pos += band_ie_len;
+               len += band_ie_len;
+       }
+
+       if (common_ie && common_ie_len) {
+               memcpy(pos, common_ie, common_ie_len);
+               pos += common_ie_len;
+               len += common_ie_len;
        }
 
        return (u16)len;
@@ -383,7 +391,7 @@ int iwl_mvm_scan_request(struct iwl_mvm *mvm,
                            (struct ieee80211_mgmt *)cmd->data,
                            vif->addr,
                            req->n_ssids, ssid, ssid_len,
-                           req->ie, req->ie_len,
+                           req->ie, req->ie_len, NULL, 0,
                            mvm->fw->ucode_capa.max_probe_length));
 
        iwl_mvm_scan_fill_channels(cmd, req, basic_ssid, &params);
@@ -562,7 +570,7 @@ int iwl_mvm_rx_scan_offload_complete_notif(struct iwl_mvm *mvm,
 
 static void iwl_scan_offload_build_tx_cmd(struct iwl_mvm *mvm,
                                          struct ieee80211_vif *vif,
-                                         struct ieee80211_sched_scan_ies *ies,
+                                         struct ieee80211_scan_ies *ies,
                                          enum ieee80211_band band,
                                          struct iwl_tx_cmd *cmd,
                                          u8 *data)
@@ -578,7 +586,8 @@ static void iwl_scan_offload_build_tx_cmd(struct iwl_mvm *mvm,
        cmd_len = iwl_mvm_fill_probe_req((struct ieee80211_mgmt *)data,
                                         vif->addr,
                                         1, NULL, 0,
-                                        ies->ie[band], ies->len[band],
+                                        ies->ies[band], ies->len[band],
+                                        ies->common_ies, ies->common_ie_len,
                                         SCAN_OFFLOAD_PROBE_REQ_SIZE);
        cmd->len = cpu_to_le16(cmd_len);
 }
@@ -716,7 +725,7 @@ static void iwl_build_channel_cfg(struct iwl_mvm *mvm,
 int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm,
                              struct ieee80211_vif *vif,
                              struct cfg80211_sched_scan_request *req,
-                             struct ieee80211_sched_scan_ies *ies)
+                             struct ieee80211_scan_ies *ies)
 {
        int band_2ghz = mvm->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels;
        int band_5ghz = mvm->nvm_data->bands[IEEE80211_BAND_5GHZ].n_channels;
index 0485c99..e6268ce 100644 (file)
@@ -16,7 +16,7 @@ config LIBERTAS_USB
 
 config LIBERTAS_CS
        tristate "Marvell Libertas 8385 CompactFlash 802.11b/g cards"
-       depends on LIBERTAS && PCMCIA
+       depends on LIBERTAS && PCMCIA && HAS_IOPORT_MAP
        ---help---
          A driver for Marvell Libertas 8385 CompactFlash devices.
 
index aaa2973..0387a5b 100644 (file)
@@ -1111,6 +1111,7 @@ int lbs_set_radio(struct lbs_private *priv, u8 preamble, u8 radio_on)
 
        cmd.hdr.size = cpu_to_le16(sizeof(cmd));
        cmd.action = cpu_to_le16(CMD_ACT_SET);
+       cmd.control = 0;
 
        /* Only v8 and below support setting the preamble */
        if (priv->fwrelease < 0x09000000) {
index a312c65..eba5146 100644 (file)
@@ -781,6 +781,36 @@ static void mac80211_hwsim_monitor_ack(struct ieee80211_channel *chan,
        netif_rx(skb);
 }
 
+struct mac80211_hwsim_addr_match_data {
+       u8 addr[ETH_ALEN];
+       bool ret;
+};
+
+static void mac80211_hwsim_addr_iter(void *data, u8 *mac,
+                                    struct ieee80211_vif *vif)
+{
+       struct mac80211_hwsim_addr_match_data *md = data;
+
+       if (memcmp(mac, md->addr, ETH_ALEN) == 0)
+               md->ret = true;
+}
+
+static bool mac80211_hwsim_addr_match(struct mac80211_hwsim_data *data,
+                                     const u8 *addr)
+{
+       struct mac80211_hwsim_addr_match_data md = {
+               .ret = false,
+       };
+
+       memcpy(md.addr, addr, ETH_ALEN);
+
+       ieee80211_iterate_active_interfaces_atomic(data->hw,
+                                                  IEEE80211_IFACE_ITER_NORMAL,
+                                                  mac80211_hwsim_addr_iter,
+                                                  &md);
+
+       return md.ret;
+}
 
 static bool hwsim_ps_rx_ok(struct mac80211_hwsim_data *data,
                           struct sk_buff *skb)
@@ -798,8 +828,7 @@ static bool hwsim_ps_rx_ok(struct mac80211_hwsim_data *data,
                /* Allow unicast frames to own address if there is a pending
                 * PS-Poll */
                if (data->ps_poll_pending &&
-                   memcmp(data->hw->wiphy->perm_addr, skb->data + 4,
-                          ETH_ALEN) == 0) {
+                   mac80211_hwsim_addr_match(data, skb->data + 4)) {
                        data->ps_poll_pending = false;
                        return true;
                }
@@ -809,39 +838,6 @@ static bool hwsim_ps_rx_ok(struct mac80211_hwsim_data *data,
        return true;
 }
 
-
-struct mac80211_hwsim_addr_match_data {
-       bool ret;
-       const u8 *addr;
-};
-
-static void mac80211_hwsim_addr_iter(void *data, u8 *mac,
-                                    struct ieee80211_vif *vif)
-{
-       struct mac80211_hwsim_addr_match_data *md = data;
-       if (memcmp(mac, md->addr, ETH_ALEN) == 0)
-               md->ret = true;
-}
-
-
-static bool mac80211_hwsim_addr_match(struct mac80211_hwsim_data *data,
-                                     const u8 *addr)
-{
-       struct mac80211_hwsim_addr_match_data md;
-
-       if (memcmp(addr, data->hw->wiphy->perm_addr, ETH_ALEN) == 0)
-               return true;
-
-       md.ret = false;
-       md.addr = addr;
-       ieee80211_iterate_active_interfaces_atomic(data->hw,
-                                                  IEEE80211_IFACE_ITER_NORMAL,
-                                                  mac80211_hwsim_addr_iter,
-                                                  &md);
-
-       return md.ret;
-}
-
 static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw,
                                       struct sk_buff *my_skb,
                                       int dst_portid)
@@ -1740,9 +1736,10 @@ static void hw_scan_work(struct work_struct *work)
 
 static int mac80211_hwsim_hw_scan(struct ieee80211_hw *hw,
                                  struct ieee80211_vif *vif,
-                                 struct cfg80211_scan_request *req)
+                                 struct ieee80211_scan_request *hw_req)
 {
        struct mac80211_hwsim_data *hwsim = hw->priv;
+       struct cfg80211_scan_request *req = &hw_req->req;
 
        mutex_lock(&hwsim->mutex);
        if (WARN_ON(hwsim->tmp_chan || hwsim->hw_scan_request)) {
index e95dec9..e33a034 100644 (file)
@@ -42,36 +42,6 @@ static const struct ieee80211_iface_combination mwifiex_iface_comb_ap_sta = {
        .beacon_int_infra_match = true,
 };
 
-static const struct ieee80211_regdomain mwifiex_world_regdom_custom = {
-       .n_reg_rules = 7,
-       .alpha2 =  "99",
-       .reg_rules = {
-               /* Channel 1 - 11 */
-               REG_RULE(2412-10, 2462+10, 40, 3, 20, 0),
-               /* Channel 12 - 13 */
-               REG_RULE(2467-10, 2472+10, 20, 3, 20,
-                        NL80211_RRF_NO_IR),
-               /* Channel 14 */
-               REG_RULE(2484-10, 2484+10, 20, 3, 20,
-                        NL80211_RRF_NO_IR |
-                        NL80211_RRF_NO_OFDM),
-               /* Channel 36 - 48 */
-               REG_RULE(5180-10, 5240+10, 40, 3, 20,
-                        NL80211_RRF_NO_IR),
-               /* Channel 149 - 165 */
-               REG_RULE(5745-10, 5825+10, 40, 3, 20,
-                        NL80211_RRF_NO_IR),
-               /* Channel 52 - 64 */
-               REG_RULE(5260-10, 5320+10, 40, 3, 30,
-                        NL80211_RRF_NO_IR |
-                        NL80211_RRF_DFS),
-               /* Channel 100 - 140 */
-               REG_RULE(5500-10, 5700+10, 40, 3, 30,
-                        NL80211_RRF_NO_IR |
-                        NL80211_RRF_DFS),
-       }
-};
-
 /*
  * This function maps the nl802.11 channel type into driver channel type.
  *
@@ -151,7 +121,6 @@ mwifiex_form_mgmt_frame(struct sk_buff *skb, const u8 *buf, size_t len)
        u8 addr[ETH_ALEN] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
        u16 pkt_len;
        u32 tx_control = 0, pkt_type = PKT_TYPE_MGMT;
-       struct timeval tv;
 
        pkt_len = len + ETH_ALEN;
 
@@ -173,8 +142,7 @@ mwifiex_form_mgmt_frame(struct sk_buff *skb, const u8 *buf, size_t len)
               len - sizeof(struct ieee80211_hdr_3addr));
 
        skb->priority = LOW_PRIO_TID;
-       do_gettimeofday(&tv);
-       skb->tstamp = timeval_to_ktime(tv);
+       __net_timestamp(skb);
 
        return 0;
 }
@@ -2483,6 +2451,16 @@ static int mwifiex_cfg80211_suspend(struct wiphy *wiphy,
                mef_entry->filter[filt_num].filt_type = TYPE_EQ;
                if (filt_num)
                        mef_entry->filter[filt_num].filt_action = TYPE_OR;
+
+               filt_num++;
+               mef_entry->filter[filt_num].repeat = 16;
+               memcpy(mef_entry->filter[filt_num].byte_seq, priv->curr_addr,
+                      ETH_ALEN);
+               mef_entry->filter[filt_num].byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] =
+                                                               ETH_ALEN;
+               mef_entry->filter[filt_num].offset = 56;
+               mef_entry->filter[filt_num].filt_type = TYPE_EQ;
+               mef_entry->filter[filt_num].filt_action = TYPE_OR;
        }
 
        if (!mef_cfg.criteria)
@@ -2631,7 +2609,8 @@ static int
 mwifiex_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
                           const u8 *peer, u8 action_code, u8 dialog_token,
                           u16 status_code, u32 peer_capability,
-                          const u8 *extra_ies, size_t extra_ies_len)
+                          bool initiator, const u8 *extra_ies,
+                          size_t extra_ies_len)
 {
        struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
        int ret;
@@ -2916,12 +2895,6 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter)
                wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS |
                                WIPHY_FLAG_TDLS_EXTERNAL_SETUP;
 
-       wiphy->regulatory_flags |=
-                       REGULATORY_CUSTOM_REG |
-                       REGULATORY_STRICT_REG;
-
-       wiphy_apply_custom_regulatory(wiphy, &mwifiex_world_regdom_custom);
-
 #ifdef CONFIG_PM
        wiphy->wowlan = &mwifiex_wowlan_support;
 #endif
index cbabc12..40e4dbd 100644 (file)
@@ -609,7 +609,6 @@ mwifiex_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
        struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
        struct sk_buff *new_skb;
        struct mwifiex_txinfo *tx_info;
-       struct timeval tv;
 
        dev_dbg(priv->adapter->dev, "data: %lu BSS(%d-%d): Data <= kernel\n",
                jiffies, priv->bss_type, priv->bss_num);
@@ -656,8 +655,7 @@ mwifiex_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
         * firmware for aggregate delay calculation for stats and
         * MSDU lifetime expiry.
         */
-       do_gettimeofday(&tv);
-       skb->tstamp = timeval_to_ktime(tv);
+       __net_timestamp(skb);
 
        mwifiex_queue_tx_pkt(priv, skb);
 
index 536c14a..2295263 100644 (file)
@@ -26,7 +26,7 @@
 #include "11n.h"
 #include "cfg80211.h"
 
-static int disconnect_on_suspend = 1;
+static int disconnect_on_suspend;
 module_param(disconnect_on_suspend, int, 0644);
 
 /*
index e73034f..3efbcbe 100644 (file)
@@ -530,7 +530,6 @@ int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv, const u8 *peer,
 {
        struct sk_buff *skb;
        struct mwifiex_txinfo *tx_info;
-       struct timeval tv;
        int ret;
        u16 skb_len;
 
@@ -608,8 +607,7 @@ int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv, const u8 *peer,
        tx_info->bss_num = priv->bss_num;
        tx_info->bss_type = priv->bss_type;
 
-       do_gettimeofday(&tv);
-       skb->tstamp = timeval_to_ktime(tv);
+       __net_timestamp(skb);
        mwifiex_queue_tx_pkt(priv, skb);
 
        return 0;
@@ -702,7 +700,6 @@ int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv, const u8 *peer,
 {
        struct sk_buff *skb;
        struct mwifiex_txinfo *tx_info;
-       struct timeval tv;
        u8 *pos;
        u32 pkt_type, tx_control;
        u16 pkt_len, skb_len;
@@ -767,8 +764,7 @@ int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv, const u8 *peer,
        pkt_len = skb->len - MWIFIEX_MGMT_FRAME_HEADER_SIZE - sizeof(pkt_len);
        memcpy(skb->data + MWIFIEX_MGMT_FRAME_HEADER_SIZE, &pkt_len,
               sizeof(pkt_len));
-       do_gettimeofday(&tv);
-       skb->tstamp = timeval_to_ktime(tv);
+       __net_timestamp(skb);
        mwifiex_queue_tx_pkt(priv, skb);
 
        return 0;
index 9a56bc6..0c87f8f 100644 (file)
@@ -96,7 +96,6 @@ static void mwifiex_uap_queue_bridged_pkt(struct mwifiex_private *priv,
        struct sk_buff *new_skb;
        struct mwifiex_txinfo *tx_info;
        int hdr_chop;
-       struct timeval tv;
        struct ethhdr *p_ethhdr;
 
        uap_rx_pd = (struct uap_rxpd *)(skb->data);
@@ -192,8 +191,7 @@ static void mwifiex_uap_queue_bridged_pkt(struct mwifiex_private *priv,
                tx_info->pkt_len = skb->len;
        }
 
-       do_gettimeofday(&tv);
-       skb->tstamp = timeval_to_ktime(tv);
+       __net_timestamp(skb);
        mwifiex_wmm_add_buf_txqueue(priv, skb);
        atomic_inc(&adapter->tx_pending);
        atomic_inc(&adapter->pending_bridged_pkts);
index d3671d0..6e86d1e 100644 (file)
@@ -878,15 +878,8 @@ u8
 mwifiex_wmm_compute_drv_pkt_delay(struct mwifiex_private *priv,
                                  const struct sk_buff *skb)
 {
+       u32 queue_delay = ktime_to_ms(ktime_sub(ktime_get(), skb->tstamp));
        u8 ret_val;
-       struct timeval out_tstamp, in_tstamp;
-       u32 queue_delay;
-
-       do_gettimeofday(&out_tstamp);
-       in_tstamp = ktime_to_timeval(skb->tstamp);
-
-       queue_delay = (out_tstamp.tv_sec - in_tstamp.tv_sec) * 1000;
-       queue_delay += (out_tstamp.tv_usec - in_tstamp.tv_usec) / 1000;
 
        /*
         * Queue delay is passed as a uint8 in units of 2ms (ms shifted
index 60819bc..60698b0 100644 (file)
@@ -107,7 +107,7 @@ config PCI_HERMES
 
 config PCMCIA_HERMES
        tristate "Hermes PCMCIA card support"
-       depends on PCMCIA && HERMES
+       depends on PCMCIA && HERMES && HAS_IOPORT_MAP
        ---help---
          A driver for "Hermes" chipset based PCMCIA wireless adaptors, such
          as the Lucent WavelanIEEE/Orinoco cards and their OEM (Cabletron/
@@ -122,7 +122,7 @@ config PCMCIA_HERMES
 
 config PCMCIA_SPECTRUM
        tristate "Symbol Spectrum24 Trilogy PCMCIA card support"
-       depends on PCMCIA && HERMES
+       depends on PCMCIA && HERMES && HAS_IOPORT_MAP
        ---help---
 
          This is a driver for 802.11b cards using RAM-loadable Symbol
index cf61d6e..1cd0914 100644 (file)
@@ -76,6 +76,50 @@ static bool rsi_recalculate_weights(struct rsi_common *common)
        return recontend_queue;
 }
 
+/**
+ * rsi_get_num_pkts_dequeue() - This function determines the number of
+ *                             packets to be dequeued based on the number
+ *                             of bytes calculated using txop.
+ *
+ * @common: Pointer to the driver private structure.
+ * @q_num: the queue from which pkts have to be dequeued
+ *
+ * Return: pkt_num: Number of pkts to be dequeued.
+ */
+static u32 rsi_get_num_pkts_dequeue(struct rsi_common *common, u8 q_num)
+{
+       struct rsi_hw *adapter = common->priv;
+       struct sk_buff *skb;
+       u32 pkt_cnt = 0;
+       s16 txop = common->tx_qinfo[q_num].txop * 32;
+       struct ieee80211_rate rate;
+
+       rate.bitrate = RSI_RATE_MCS0 * 5 * 10; /* Convert to Kbps */
+       if (q_num == VI_Q)
+               txop = ((txop << 5) / 80);
+
+       if (skb_queue_len(&common->tx_queue[q_num]))
+               skb = skb_peek(&common->tx_queue[q_num]);
+       else
+               return 0;
+
+       do {
+               txop -= ieee80211_generic_frame_duration(adapter->hw,
+                                                        adapter->vifs[0],
+                                                        common->band,
+                                                        skb->len, &rate);
+               pkt_cnt += 1;
+               /*checking if pkts are still there*/
+               if (skb_queue_len(&common->tx_queue[q_num]) - pkt_cnt)
+                       skb = skb->next;
+               else
+                       break;
+
+       } while (txop > 0);
+
+       return pkt_cnt;
+}
+
 /**
  * rsi_core_determine_hal_queue() - This function determines the queue from
  *                                 which packet has to be dequeued.
@@ -88,7 +132,7 @@ static u8 rsi_core_determine_hal_queue(struct rsi_common *common)
        bool recontend_queue = false;
        u32 q_len = 0;
        u8 q_num = INVALID_QUEUE;
-       u8 ii = 0, min = 0;
+       u8 ii = 0;
 
        if (skb_queue_len(&common->tx_queue[MGMT_SOFT_Q])) {
                if (!common->mgmt_q_block)
@@ -96,6 +140,9 @@ static u8 rsi_core_determine_hal_queue(struct rsi_common *common)
                return q_num;
        }
 
+       if (common->hw_data_qs_blocked)
+               return q_num;
+
        if (common->pkt_cnt != 0) {
                --common->pkt_cnt;
                return common->selected_qnum;
@@ -106,14 +153,15 @@ get_queue_num:
 
        q_num = rsi_determine_min_weight_queue(common);
 
-       q_len = skb_queue_len(&common->tx_queue[ii]);
        ii = q_num;
 
        /* Selecting the queue with least back off */
        for (; ii < NUM_EDCA_QUEUES; ii++) {
+               q_len = skb_queue_len(&common->tx_queue[ii]);
                if (((common->tx_qinfo[ii].pkt_contended) &&
-                    (common->tx_qinfo[ii].weight < min)) && q_len) {
-                       min = common->tx_qinfo[ii].weight;
+                    (common->tx_qinfo[ii].weight < common->min_weight)) &&
+                     q_len) {
+                       common->min_weight = common->tx_qinfo[ii].weight;
                        q_num = ii;
                }
        }
@@ -140,26 +188,10 @@ get_queue_num:
        common->selected_qnum = q_num;
        q_len = skb_queue_len(&common->tx_queue[q_num]);
 
-       switch (common->selected_qnum) {
-       case VO_Q:
-               if (q_len > MAX_CONTINUOUS_VO_PKTS)
-                       common->pkt_cnt = (MAX_CONTINUOUS_VO_PKTS - 1);
-               else
-                       common->pkt_cnt = --q_len;
-               break;
-
-       case VI_Q:
-               if (q_len > MAX_CONTINUOUS_VI_PKTS)
-                       common->pkt_cnt = (MAX_CONTINUOUS_VI_PKTS - 1);
-               else
-                       common->pkt_cnt = --q_len;
-
-               break;
-
-       default:
-               common->pkt_cnt = 0;
-               break;
-       }
+       if (q_num == VO_Q || q_num == VI_Q) {
+               common->pkt_cnt = rsi_get_num_pkts_dequeue(common, q_num);
+               common->pkt_cnt -= 1;
+       };
 
        return q_num;
 }
@@ -252,6 +284,7 @@ void rsi_core_qos_processor(struct rsi_common *common)
 
                skb = rsi_core_dequeue_pkt(common, q_num);
                if (skb == NULL) {
+                       rsi_dbg(ERR_ZONE, "skb null\n");
                        mutex_unlock(&common->tx_rxlock);
                        break;
                }
@@ -306,7 +339,8 @@ void rsi_core_xmit(struct rsi_common *common, struct sk_buff *skb)
        }
 
        if ((ieee80211_is_mgmt(tmp_hdr->frame_control)) ||
-           (ieee80211_is_ctl(tmp_hdr->frame_control))) {
+           (ieee80211_is_ctl(tmp_hdr->frame_control)) ||
+           (ieee80211_is_qos_nullfunc(tmp_hdr->frame_control))) {
                q_num = MGMT_SOFT_Q;
                skb->priority = q_num;
        } else {
@@ -325,6 +359,7 @@ void rsi_core_xmit(struct rsi_common *common, struct sk_buff *skb)
        if ((q_num != MGMT_SOFT_Q) &&
            ((skb_queue_len(&common->tx_queue[q_num]) + 1) >=
             DATA_QUEUE_WATER_MARK)) {
+               rsi_dbg(ERR_ZONE, "%s: sw queue full\n", __func__);
                if (!ieee80211_queue_stopped(adapter->hw, WME_AC(q_num)))
                        ieee80211_stop_queue(adapter->hw, WME_AC(q_num));
                rsi_set_event(&common->tx_thread.event);
index c466246..828a042 100644 (file)
@@ -145,7 +145,7 @@ static int rsi_stats_read(struct seq_file *seq, void *data)
        seq_printf(seq, "total_mgmt_pkt_send : %d\n",
                   common->tx_stats.total_tx_pkt_send[MGMT_SOFT_Q]);
        seq_printf(seq, "total_mgmt_pkt_queued : %d\n",
-                  skb_queue_len(&common->tx_queue[4]));
+                  skb_queue_len(&common->tx_queue[MGMT_SOFT_Q]));
        seq_printf(seq, "total_mgmt_pkt_freed  : %d\n",
                   common->tx_stats.total_tx_pkt_freed[MGMT_SOFT_Q]);
 
@@ -153,25 +153,25 @@ static int rsi_stats_read(struct seq_file *seq, void *data)
        seq_printf(seq, "total_data_vo_pkt_send: %8d\t",
                   common->tx_stats.total_tx_pkt_send[VO_Q]);
        seq_printf(seq, "total_data_vo_pkt_queued:  %8d\t",
-                  skb_queue_len(&common->tx_queue[0]));
+                  skb_queue_len(&common->tx_queue[VO_Q]));
        seq_printf(seq, "total_vo_pkt_freed: %8d\n",
                   common->tx_stats.total_tx_pkt_freed[VO_Q]);
        seq_printf(seq, "total_data_vi_pkt_send: %8d\t",
                   common->tx_stats.total_tx_pkt_send[VI_Q]);
        seq_printf(seq, "total_data_vi_pkt_queued:  %8d\t",
-                  skb_queue_len(&common->tx_queue[1]));
+                  skb_queue_len(&common->tx_queue[VI_Q]));
        seq_printf(seq, "total_vi_pkt_freed: %8d\n",
                   common->tx_stats.total_tx_pkt_freed[VI_Q]);
        seq_printf(seq,  "total_data_be_pkt_send: %8d\t",
                   common->tx_stats.total_tx_pkt_send[BE_Q]);
        seq_printf(seq, "total_data_be_pkt_queued:  %8d\t",
-                  skb_queue_len(&common->tx_queue[2]));
+                  skb_queue_len(&common->tx_queue[BE_Q]));
        seq_printf(seq, "total_be_pkt_freed: %8d\n",
                   common->tx_stats.total_tx_pkt_freed[BE_Q]);
        seq_printf(seq, "total_data_bk_pkt_send: %8d\t",
                   common->tx_stats.total_tx_pkt_send[BK_Q]);
        seq_printf(seq, "total_data_bk_pkt_queued:  %8d\t",
-                  skb_queue_len(&common->tx_queue[3]));
+                  skb_queue_len(&common->tx_queue[BK_Q]));
        seq_printf(seq, "total_bk_pkt_freed: %8d\n",
                   common->tx_stats.total_tx_pkt_freed[BK_Q]);
 
index 54aaeb0..aeaf87b 100644 (file)
@@ -177,7 +177,7 @@ static void rsi_register_rates_channels(struct rsi_hw *adapter, int band)
        sbands->ht_cap.cap = (IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
                              IEEE80211_HT_CAP_SGI_20 |
                              IEEE80211_HT_CAP_SGI_40);
-       sbands->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K;
+       sbands->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K;
        sbands->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE;
        sbands->ht_cap.mcs.rx_mask[0] = 0xff;
        sbands->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
@@ -185,7 +185,7 @@ static void rsi_register_rates_channels(struct rsi_hw *adapter, int band)
 }
 
 /**
- * rsi_mac80211_attach() - This function is used to de-initialize the
+ * rsi_mac80211_detach() - This function is used to de-initialize the
  *                        Mac80211 stack.
  * @adapter: Pointer to the adapter structure.
  *
@@ -340,6 +340,59 @@ static void rsi_mac80211_remove_interface(struct ieee80211_hw *hw,
        mutex_unlock(&common->mutex);
 }
 
+/**
+ * rsi_channel_change() - This function is a performs the checks
+ *                       required for changing a channel and sets
+ *                       the channel accordingly.
+ * @hw: Pointer to the ieee80211_hw structure.
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+static int rsi_channel_change(struct ieee80211_hw *hw)
+{
+       struct rsi_hw *adapter = hw->priv;
+       struct rsi_common *common = adapter->priv;
+       int status = -EOPNOTSUPP;
+       struct ieee80211_channel *curchan = hw->conf.chandef.chan;
+       u16 channel = curchan->hw_value;
+       struct ieee80211_bss_conf *bss = &adapter->vifs[0]->bss_conf;
+
+       rsi_dbg(INFO_ZONE,
+               "%s: Set channel: %d MHz type: %d channel_no %d\n",
+               __func__, curchan->center_freq,
+               curchan->flags, channel);
+
+       if (bss->assoc) {
+               if (!common->hw_data_qs_blocked &&
+                   (rsi_get_connected_channel(adapter) != channel)) {
+                       rsi_dbg(INFO_ZONE, "blk data q %d\n", channel);
+                       if (!rsi_send_block_unblock_frame(common, true))
+                               common->hw_data_qs_blocked = true;
+               }
+       }
+
+       status = rsi_band_check(common);
+       if (!status)
+               status = rsi_set_channel(adapter->priv, channel);
+
+       if (bss->assoc) {
+               if (common->hw_data_qs_blocked &&
+                   (rsi_get_connected_channel(adapter) == channel)) {
+                       rsi_dbg(INFO_ZONE, "unblk data q %d\n", channel);
+                       if (!rsi_send_block_unblock_frame(common, false))
+                               common->hw_data_qs_blocked = false;
+               }
+       } else {
+               if (common->hw_data_qs_blocked) {
+                       rsi_dbg(INFO_ZONE, "unblk data q %d\n", channel);
+                       if (!rsi_send_block_unblock_frame(common, false))
+                               common->hw_data_qs_blocked = false;
+               }
+       }
+
+       return status;
+}
+
 /**
  * rsi_mac80211_config() - This function is a handler for configuration
  *                        requests. The stack calls this function to
@@ -357,17 +410,10 @@ static int rsi_mac80211_config(struct ieee80211_hw *hw,
        int status = -EOPNOTSUPP;
 
        mutex_lock(&common->mutex);
-       if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
-               struct ieee80211_channel *curchan = hw->conf.chandef.chan;
-               u16 channel = curchan->hw_value;
-
-               rsi_dbg(INFO_ZONE,
-                       "%s: Set channel: %d MHz type: %d channel_no %d\n",
-                       __func__, curchan->center_freq,
-                       curchan->flags, channel);
-               common->band = curchan->band;
-               status = rsi_set_channel(adapter->priv, channel);
-       }
+
+       if (changed & IEEE80211_CONF_CHANGE_CHANNEL)
+               status = rsi_channel_change(hw);
+
        mutex_unlock(&common->mutex);
 
        return status;
@@ -421,6 +467,15 @@ static void rsi_mac80211_bss_info_changed(struct ieee80211_hw *hw,
                                      bss_conf->qos,
                                      bss_conf->aid);
        }
+
+       if (changed & BSS_CHANGED_CQM) {
+               common->cqm_info.last_cqm_event_rssi = 0;
+               common->cqm_info.rssi_thold = bss_conf->cqm_rssi_thold;
+               common->cqm_info.rssi_hyst = bss_conf->cqm_rssi_hyst;
+               rsi_dbg(INFO_ZONE, "RSSI throld & hysteresis are: %d %d\n",
+                       common->cqm_info.rssi_thold,
+                       common->cqm_info.rssi_hyst);
+       }
        mutex_unlock(&common->mutex);
 }
 
@@ -723,23 +778,54 @@ static int rsi_mac80211_set_rate_mask(struct ieee80211_hw *hw,
 {
        struct rsi_hw *adapter = hw->priv;
        struct rsi_common *common = adapter->priv;
+       enum ieee80211_band band = hw->conf.chandef.chan->band;
 
        mutex_lock(&common->mutex);
+       common->fixedrate_mask[band] = 0;
 
-       common->fixedrate_mask[IEEE80211_BAND_2GHZ] = 0;
-
-       if (mask->control[IEEE80211_BAND_2GHZ].legacy == 0xfff) {
-               common->fixedrate_mask[IEEE80211_BAND_2GHZ] =
-                       (mask->control[IEEE80211_BAND_2GHZ].ht_mcs[0] << 12);
+       if (mask->control[band].legacy == 0xfff) {
+               common->fixedrate_mask[band] =
+                       (mask->control[band].ht_mcs[0] << 12);
        } else {
-               common->fixedrate_mask[IEEE80211_BAND_2GHZ] =
-                       mask->control[IEEE80211_BAND_2GHZ].legacy;
+               common->fixedrate_mask[band] =
+                       mask->control[band].legacy;
        }
        mutex_unlock(&common->mutex);
 
        return 0;
 }
 
+/**
+ * rsi_perform_cqm() - This function performs cqm.
+ * @common: Pointer to the driver private structure.
+ * @bssid: pointer to the bssid.
+ * @rssi: RSSI value.
+ */
+static void rsi_perform_cqm(struct rsi_common *common,
+                           u8 *bssid,
+                           s8 rssi)
+{
+       struct rsi_hw *adapter = common->priv;
+       s8 last_event = common->cqm_info.last_cqm_event_rssi;
+       int thold = common->cqm_info.rssi_thold;
+       u32 hyst = common->cqm_info.rssi_hyst;
+       enum nl80211_cqm_rssi_threshold_event event;
+
+       if (rssi < thold && (last_event == 0 || rssi < (last_event - hyst)))
+               event = NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW;
+       else if (rssi > thold &&
+                (last_event == 0 || rssi > (last_event + hyst)))
+               event = NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH;
+       else
+               return;
+
+       common->cqm_info.last_cqm_event_rssi = rssi;
+       rsi_dbg(INFO_ZONE, "CQM: Notifying event: %d\n", event);
+       ieee80211_cqm_rssi_notify(adapter->vifs[0], event, GFP_KERNEL);
+
+       return;
+}
+
 /**
  * rsi_fill_rx_status() - This function fills rx status in
  *                       ieee80211_rx_status structure.
@@ -755,6 +841,7 @@ static void rsi_fill_rx_status(struct ieee80211_hw *hw,
                               struct rsi_common *common,
                               struct ieee80211_rx_status *rxs)
 {
+       struct ieee80211_bss_conf *bss = &common->priv->vifs[0]->bss_conf;
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
        struct skb_info *rx_params = (struct skb_info *)info->driver_data;
        struct ieee80211_hdr *hdr;
@@ -770,10 +857,7 @@ static void rsi_fill_rx_status(struct ieee80211_hw *hw,
 
        rxs->signal = -(rssi);
 
-       if (channel <= 14)
-               rxs->band = IEEE80211_BAND_2GHZ;
-       else
-               rxs->band = IEEE80211_BAND_5GHZ;
+       rxs->band = common->band;
 
        freq = ieee80211_channel_to_frequency(channel, rxs->band);
 
@@ -792,6 +876,14 @@ static void rsi_fill_rx_status(struct ieee80211_hw *hw,
                rxs->flag |= RX_FLAG_DECRYPTED;
                rxs->flag |= RX_FLAG_IV_STRIPPED;
        }
+
+       /* CQM only for connected AP beacons, the RSSI is a weighted avg */
+       if (bss->assoc && !(memcmp(bss->bssid, hdr->addr2, ETH_ALEN))) {
+               if (ieee80211_is_beacon(hdr->frame_control))
+                       rsi_perform_cqm(common, hdr->addr2, rxs->signal);
+       }
+
+       return;
 }
 
 /**
@@ -983,6 +1075,7 @@ int rsi_mac80211_attach(struct rsi_common *common)
 
        hw->max_tx_aggregation_subframes = 6;
        rsi_register_rates_channels(adapter, IEEE80211_BAND_2GHZ);
+       rsi_register_rates_channels(adapter, IEEE80211_BAND_5GHZ);
        hw->rate_control_algorithm = "AARF";
 
        SET_IEEE80211_PERM_ADDR(hw, common->mac_addr);
@@ -1000,6 +1093,8 @@ int rsi_mac80211_attach(struct rsi_common *common)
        wiphy->available_antennas_tx = 1;
        wiphy->bands[IEEE80211_BAND_2GHZ] =
                &adapter->sbands[IEEE80211_BAND_2GHZ];
+       wiphy->bands[IEEE80211_BAND_5GHZ] =
+               &adapter->sbands[IEEE80211_BAND_5GHZ];
 
        status = ieee80211_register_hw(hw);
        if (status)
index 2eefbf1..83abe58 100644 (file)
@@ -217,6 +217,7 @@ static void rsi_set_default_parameters(struct rsi_common *common)
        common->min_rate = 0xffff;
        common->fsm_state = FSM_CARD_NOT_READY;
        common->iface_down = true;
+       common->endpoint = EP_2GHZ_20MHZ;
 }
 
 /**
@@ -276,7 +277,6 @@ static int rsi_load_radio_caps(struct rsi_common *common)
 {
        struct rsi_radio_caps *radio_caps;
        struct rsi_hw *adapter = common->priv;
-       struct ieee80211_hw *hw = adapter->hw;
        u16 inx = 0;
        u8 ii;
        u8 radio_id = 0;
@@ -285,7 +285,6 @@ static int rsi_load_radio_caps(struct rsi_common *common)
                      0xf0, 0xf0, 0xf0, 0xf0,
                      0xf0, 0xf0, 0xf0, 0xf0,
                      0xf0, 0xf0, 0xf0, 0xf0};
-       struct ieee80211_conf *conf = &hw->conf;
        struct sk_buff *skb;
 
        rsi_dbg(INFO_ZONE, "%s: Sending rate symbol req frame\n", __func__);
@@ -307,29 +306,36 @@ static int rsi_load_radio_caps(struct rsi_common *common)
        if (common->channel_width == BW_40MHZ) {
                radio_caps->desc_word[7] |= cpu_to_le16(RSI_LMAC_CLOCK_80MHZ);
                radio_caps->desc_word[7] |= cpu_to_le16(RSI_ENABLE_40MHZ);
-               if (common->channel_width) {
-                       radio_caps->desc_word[5] =
-                               cpu_to_le16(common->channel_width << 12);
-                       radio_caps->desc_word[5] |= cpu_to_le16(FULL40M_ENABLE);
-               }
 
-               if (conf_is_ht40_minus(conf)) {
-                       radio_caps->desc_word[5] = 0;
-                       radio_caps->desc_word[5] |=
-                               cpu_to_le16(LOWER_20_ENABLE);
-                       radio_caps->desc_word[5] |=
-                               cpu_to_le16(LOWER_20_ENABLE >> 12);
-               }
-
-               if (conf_is_ht40_plus(conf)) {
-                       radio_caps->desc_word[5] = 0;
-                       radio_caps->desc_word[5] |=
-                               cpu_to_le16(UPPER_20_ENABLE);
-                       radio_caps->desc_word[5] |=
-                               cpu_to_le16(UPPER_20_ENABLE >> 12);
+               if (common->fsm_state == FSM_MAC_INIT_DONE) {
+                       struct ieee80211_hw *hw = adapter->hw;
+                       struct ieee80211_conf *conf = &hw->conf;
+                       if (conf_is_ht40_plus(conf)) {
+                               radio_caps->desc_word[5] =
+                                       cpu_to_le16(LOWER_20_ENABLE);
+                               radio_caps->desc_word[5] |=
+                                       cpu_to_le16(LOWER_20_ENABLE >> 12);
+                       } else if (conf_is_ht40_minus(conf)) {
+                               radio_caps->desc_word[5] =
+                                       cpu_to_le16(UPPER_20_ENABLE);
+                               radio_caps->desc_word[5] |=
+                                       cpu_to_le16(UPPER_20_ENABLE >> 12);
+                       } else {
+                               radio_caps->desc_word[5] =
+                                       cpu_to_le16(BW_40MHZ << 12);
+                               radio_caps->desc_word[5] |=
+                                       cpu_to_le16(FULL40M_ENABLE);
+                       }
                }
        }
 
+       radio_caps->sifs_tx_11n = cpu_to_le16(SIFS_TX_11N_VALUE);
+       radio_caps->sifs_tx_11b = cpu_to_le16(SIFS_TX_11B_VALUE);
+       radio_caps->slot_rx_11n = cpu_to_le16(SHORT_SLOT_VALUE);
+       radio_caps->ofdm_ack_tout = cpu_to_le16(OFDM_ACK_TOUT_VALUE);
+       radio_caps->cck_ack_tout = cpu_to_le16(CCK_ACK_TOUT_VALUE);
+       radio_caps->preamble_type = cpu_to_le16(LONG_PREAMBLE);
+
        radio_caps->desc_word[7] |= cpu_to_le16(radio_id << 8);
 
        for (ii = 0; ii < MAX_HW_QUEUES; ii++) {
@@ -588,7 +594,7 @@ static int rsi_program_bb_rf(struct rsi_common *common)
 
        mgmt_frame->desc_word[0] = cpu_to_le16(RSI_WIFI_MGMT_Q << 12);
        mgmt_frame->desc_word[1] = cpu_to_le16(BBP_PROG_IN_TA);
-       mgmt_frame->desc_word[4] = cpu_to_le16(common->endpoint << 8);
+       mgmt_frame->desc_word[4] = cpu_to_le16(common->endpoint);
 
        if (common->rf_reset) {
                mgmt_frame->desc_word[7] =  cpu_to_le16(RF_RESET_ENABLE);
@@ -615,6 +621,9 @@ int rsi_set_vap_capabilities(struct rsi_common *common, enum opmode mode)
 {
        struct sk_buff *skb = NULL;
        struct rsi_vap_caps *vap_caps;
+       struct rsi_hw *adapter = common->priv;
+       struct ieee80211_hw *hw = adapter->hw;
+       struct ieee80211_conf *conf = &hw->conf;
        u16 vap_id = 0;
 
        rsi_dbg(MGMT_TX_ZONE, "%s: Sending VAP capabilities frame\n", __func__);
@@ -644,13 +653,24 @@ int rsi_set_vap_capabilities(struct rsi_common *common, enum opmode mode)
        vap_caps->frag_threshold = cpu_to_le16(IEEE80211_MAX_FRAG_THRESHOLD);
 
        vap_caps->rts_threshold = cpu_to_le16(common->rts_threshold);
-       vap_caps->default_mgmt_rate = 0;
-       if (conf_is_ht40(&common->priv->hw->conf)) {
-               vap_caps->default_ctrl_rate =
-                               cpu_to_le32(RSI_RATE_6 | FULL40M_ENABLE << 16);
-       } else {
+       vap_caps->default_mgmt_rate = cpu_to_le32(RSI_RATE_6);
+
+       if (common->band == IEEE80211_BAND_5GHZ) {
                vap_caps->default_ctrl_rate = cpu_to_le32(RSI_RATE_6);
+               if (conf_is_ht40(&common->priv->hw->conf)) {
+                       vap_caps->default_ctrl_rate |=
+                               cpu_to_le32(FULL40M_ENABLE << 16);
+               }
+       } else {
+               vap_caps->default_ctrl_rate = cpu_to_le32(RSI_RATE_1);
+               if (conf_is_ht40_minus(conf))
+                       vap_caps->default_ctrl_rate |=
+                               cpu_to_le32(UPPER_20_ENABLE << 16);
+               else if (conf_is_ht40_plus(conf))
+                       vap_caps->default_ctrl_rate |=
+                               cpu_to_le32(LOWER_20_ENABLE << 16);
        }
+
        vap_caps->default_data_rate = 0;
        vap_caps->beacon_interval = cpu_to_le16(200);
        vap_caps->dtim_period = cpu_to_le16(4);
@@ -826,6 +846,63 @@ static int rsi_send_reset_mac(struct rsi_common *common)
        return rsi_send_internal_mgmt_frame(common, skb);
 }
 
+/**
+ * rsi_band_check() - This function programs the band
+ * @common: Pointer to the driver private structure.
+ *
+ * Return: 0 on success, corresponding error code on failure.
+ */
+int rsi_band_check(struct rsi_common *common)
+{
+       struct rsi_hw *adapter = common->priv;
+       struct ieee80211_hw *hw = adapter->hw;
+       u8 prev_bw = common->channel_width;
+       u8 prev_ep = common->endpoint;
+       struct ieee80211_channel *curchan = hw->conf.chandef.chan;
+       int status = 0;
+
+       if (common->band != curchan->band) {
+               common->rf_reset = 1;
+               common->band = curchan->band;
+       }
+
+       if ((hw->conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT) ||
+           (hw->conf.chandef.width == NL80211_CHAN_WIDTH_20))
+               common->channel_width = BW_20MHZ;
+       else
+               common->channel_width = BW_40MHZ;
+
+       if (common->band == IEEE80211_BAND_2GHZ) {
+               if (common->channel_width)
+                       common->endpoint = EP_2GHZ_40MHZ;
+               else
+                       common->endpoint = EP_2GHZ_20MHZ;
+       } else {
+               if (common->channel_width)
+                       common->endpoint = EP_5GHZ_40MHZ;
+               else
+                       common->endpoint = EP_5GHZ_20MHZ;
+       }
+
+       if (common->endpoint != prev_ep) {
+               status = rsi_program_bb_rf(common);
+               if (status)
+                       return status;
+       }
+
+       if (common->channel_width != prev_bw) {
+               status = rsi_load_bootup_params(common);
+               if (status)
+                       return status;
+
+               status = rsi_load_radio_caps(common);
+               if (status)
+                       return status;
+       }
+
+       return status;
+}
+
 /**
  * rsi_set_channel() - This function programs the channel.
  * @common: Pointer to the driver private structure.
@@ -841,23 +918,6 @@ int rsi_set_channel(struct rsi_common *common, u16 channel)
        rsi_dbg(MGMT_TX_ZONE,
                "%s: Sending scan req frame\n", __func__);
 
-       if (common->band == IEEE80211_BAND_5GHZ) {
-               if ((channel >= 36) && (channel <= 64))
-                       channel = ((channel - 32) / 4);
-               else if ((channel > 64) && (channel <= 140))
-                       channel = ((channel - 102) / 4) + 8;
-               else if (channel >= 149)
-                       channel = ((channel - 151) / 4) + 18;
-               else
-                       return -EINVAL;
-       } else {
-               if (channel > 14) {
-                       rsi_dbg(ERR_ZONE, "%s: Invalid chno %d, band = %d\n",
-                               __func__, channel, common->band);
-                       return -EINVAL;
-               }
-       }
-
        skb = dev_alloc_skb(FRAME_DESC_SZ);
        if (!skb) {
                rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n",
@@ -877,6 +937,7 @@ int rsi_set_channel(struct rsi_common *common, u16 channel)
                                               (RSI_RF_TYPE << 4));
 
        mgmt_frame->desc_word[5] = cpu_to_le16(0x01);
+       mgmt_frame->desc_word[6] = cpu_to_le16(0x12);
 
        if (common->channel_width == BW_40MHZ)
                mgmt_frame->desc_word[5] |= cpu_to_le16(0x1 << 8);
@@ -950,7 +1011,7 @@ static int rsi_send_auto_rate_request(struct rsi_common *common)
        struct ieee80211_hw *hw = common->priv->hw;
        u8 band = hw->conf.chandef.chan->band;
        u8 num_supported_rates = 0;
-       u8 rate_offset = 0;
+       u8 rate_table_offset, rate_offset = 0;
        u32 rate_bitmap = common->bitrate_mask[band];
 
        u16 *selected_rates, min_rate;
@@ -986,14 +1047,18 @@ static int rsi_send_auto_rate_request(struct rsi_common *common)
        if (common->channel_width == BW_40MHZ)
                auto_rate->desc_word[7] |= cpu_to_le16(1);
 
-       if (band == IEEE80211_BAND_2GHZ)
-               min_rate = STD_RATE_01;
-       else
-               min_rate = STD_RATE_06;
+       if (band == IEEE80211_BAND_2GHZ) {
+               min_rate = RSI_RATE_1;
+               rate_table_offset = 0;
+       } else {
+               min_rate = RSI_RATE_6;
+               rate_table_offset = 4;
+       }
 
        for (ii = 0, jj = 0; ii < ARRAY_SIZE(rsi_rates); ii++) {
                if (rate_bitmap & BIT(ii)) {
-                       selected_rates[jj++] = (rsi_rates[ii].bitrate / 5);
+                       selected_rates[jj++] =
+                       (rsi_rates[ii + rate_table_offset].bitrate / 5);
                        rate_offset++;
                }
        }
@@ -1006,13 +1071,6 @@ static int rsi_send_auto_rate_request(struct rsi_common *common)
                rate_offset += ARRAY_SIZE(mcs);
        }
 
-       if (rate_offset < (RSI_TBL_SZ / 2) - 1) {
-               for (ii = jj; ii < (RSI_TBL_SZ / 2); ii++) {
-                       selected_rates[jj++] = min_rate;
-                       rate_offset++;
-               }
-       }
-
        sort(selected_rates, jj, sizeof(u16), &rsi_compare, NULL);
 
        /* mapping the rates to RSI rates */
@@ -1028,25 +1086,25 @@ static int rsi_send_auto_rate_request(struct rsi_common *common)
 
        /* loading HT rates in the bottom half of the auto rate table */
        if (common->vif_info[0].is_ht) {
-               if (common->vif_info[0].sgi)
-                       auto_rate->supported_rates[rate_offset++] =
-                               cpu_to_le16(RSI_RATE_MCS7_SG);
-
                for (ii = rate_offset, kk = ARRAY_SIZE(rsi_mcsrates) - 1;
                     ii < rate_offset + 2 * ARRAY_SIZE(rsi_mcsrates); ii++) {
-                       if (common->vif_info[0].sgi)
+                       if (common->vif_info[0].sgi ||
+                           conf_is_ht40(&common->priv->hw->conf))
                                auto_rate->supported_rates[ii++] =
                                        cpu_to_le16(rsi_mcsrates[kk] | BIT(9));
                        auto_rate->supported_rates[ii] =
                                cpu_to_le16(rsi_mcsrates[kk--]);
                }
 
-               for (; ii < RSI_TBL_SZ; ii++) {
+               for (; ii < (RSI_TBL_SZ - 1); ii++) {
                        auto_rate->supported_rates[ii] =
                                cpu_to_le16(rsi_mcsrates[0]);
                }
        }
 
+       for (; ii < RSI_TBL_SZ; ii++)
+               auto_rate->supported_rates[ii] = min_rate;
+
        auto_rate->num_supported_rates = cpu_to_le16(num_supported_rates * 2);
        auto_rate->moderate_rate_inx = cpu_to_le16(num_supported_rates / 2);
        auto_rate->desc_word[7] |= cpu_to_le16(0 << 8);
@@ -1140,6 +1198,49 @@ static int rsi_eeprom_read(struct rsi_common *common)
        return rsi_send_internal_mgmt_frame(common, skb);
 }
 
+/**
+ * This function sends a frame to block/unblock
+ * data queues in the firmware
+ *
+ * @param common Pointer to the driver private structure.
+ * @param block event - block if true, unblock if false
+ * @return 0 on success, -1 on failure.
+ */
+int rsi_send_block_unblock_frame(struct rsi_common *common, bool block_event)
+{
+       struct rsi_mac_frame *mgmt_frame;
+       struct sk_buff *skb;
+
+       rsi_dbg(MGMT_TX_ZONE, "%s: Sending block/unblock frame\n", __func__);
+
+       skb = dev_alloc_skb(FRAME_DESC_SZ);
+       if (!skb) {
+               rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n",
+                       __func__);
+               return -ENOMEM;
+       }
+
+       memset(skb->data, 0, FRAME_DESC_SZ);
+       mgmt_frame = (struct rsi_mac_frame *)skb->data;
+
+       mgmt_frame->desc_word[0] = cpu_to_le16(RSI_WIFI_MGMT_Q << 12);
+       mgmt_frame->desc_word[1] = cpu_to_le16(BLOCK_HW_QUEUE);
+
+       if (block_event == true) {
+               rsi_dbg(INFO_ZONE, "blocking the data qs\n");
+               mgmt_frame->desc_word[4] = cpu_to_le16(0xf);
+       } else {
+               rsi_dbg(INFO_ZONE, "unblocking the data qs\n");
+               mgmt_frame->desc_word[5] = cpu_to_le16(0xf);
+       }
+
+       skb_put(skb, FRAME_DESC_SZ);
+
+       return rsi_send_internal_mgmt_frame(common, skb);
+
+}
+
+
 /**
  * rsi_handle_ta_confirm_type() - This function handles the confirm frames.
  * @common: Pointer to the driver private structure.
@@ -1164,7 +1265,7 @@ static int rsi_handle_ta_confirm_type(struct rsi_common *common,
                                common->fsm_state = FSM_EEPROM_READ_MAC_ADDR;
                        }
                } else {
-                       rsi_dbg(ERR_ZONE,
+                       rsi_dbg(INFO_ZONE,
                                "%s: Received bootup params cfm in %d state\n",
                                 __func__, common->fsm_state);
                        return 0;
@@ -1227,7 +1328,7 @@ static int rsi_handle_ta_confirm_type(struct rsi_common *common,
                                        __func__);
                        }
                } else {
-                       rsi_dbg(ERR_ZONE,
+                       rsi_dbg(INFO_ZONE,
                                "%s: Received radio caps cfm in %d state\n",
                                 __func__, common->fsm_state);
                        return 0;
@@ -1245,7 +1346,10 @@ static int rsi_handle_ta_confirm_type(struct rsi_common *common,
                                return rsi_mac80211_attach(common);
                        }
                } else {
-                       goto out;
+                       rsi_dbg(INFO_ZONE,
+                               "%s: Received bbb_rf cfm in %d state\n",
+                                __func__, common->fsm_state);
+                       return 0;
                }
                break;
 
index 8e48e72..702593f 100644 (file)
@@ -81,6 +81,16 @@ int rsi_send_data_pkt(struct rsi_common *common, struct sk_buff *skb)
                /* Send fixed rate */
                frame_desc[3] = cpu_to_le16(RATE_INFO_ENABLE);
                frame_desc[4] = cpu_to_le16(common->min_rate);
+
+               if (conf_is_ht40(&common->priv->hw->conf))
+                       frame_desc[5] = cpu_to_le16(FULL40M_ENABLE);
+
+               if (common->vif_info[0].sgi) {
+                       if (common->min_rate & 0x100) /* Only MCS rates */
+                               frame_desc[4] |=
+                                       cpu_to_le16(ENABLE_SHORTGI_RATE);
+               }
+
        }
 
        frame_desc[6] |= cpu_to_le16(seq_num & 0xfff);
@@ -116,6 +126,8 @@ int rsi_send_mgmt_pkt(struct rsi_common *common,
        struct ieee80211_hdr *wh = NULL;
        struct ieee80211_tx_info *info;
        struct ieee80211_bss_conf *bss = NULL;
+       struct ieee80211_hw *hw = adapter->hw;
+       struct ieee80211_conf *conf = &hw->conf;
        struct skb_info *tx_params;
        int status = -E2BIG;
        __le16 *msg = NULL;
@@ -175,6 +187,11 @@ int rsi_send_mgmt_pkt(struct rsi_common *common,
        else
                msg[4] = cpu_to_le16((RSI_RATE_6 & 0x0f) | RSI_11G_MODE);
 
+       if (conf_is_ht40(conf)) {
+               msg[4] = cpu_to_le16(0xB | RSI_11G_MODE);
+               msg[5] = cpu_to_le16(0x6);
+       }
+
        /* Indicate to firmware to give cfm */
        if ((skb->data[16] == IEEE80211_STYPE_PROBE_REQ) && (!bss->assoc)) {
                msg[1] |= cpu_to_le16(BIT(10));
index 46e7af4..8428858 100644 (file)
@@ -820,9 +820,11 @@ static struct sdio_driver rsi_driver = {
  */
 static int rsi_module_init(void)
 {
-       sdio_register_driver(&rsi_driver);
+       int ret;
+
+       ret = sdio_register_driver(&rsi_driver);
        rsi_dbg(INIT_ZONE, "%s: Registering driver\n", __func__);
-       return 0;
+       return ret;
 }
 
 /**
index 20d11cc..4834a9a 100644 (file)
@@ -401,14 +401,16 @@ void rsi_interrupt_handler(struct rsi_hw *adapter)
                        case BUFFER_AVAILABLE:
                                dev->rx_info.watch_bufferfull_count = 0;
                                dev->rx_info.buffer_full = false;
+                               dev->rx_info.semi_buffer_full = false;
                                dev->rx_info.mgmt_buffer_full = false;
                                rsi_sdio_ack_intr(common->priv,
                                                  (1 << PKT_BUFF_AVAILABLE));
-                               rsi_set_event((&common->tx_thread.event));
+                               rsi_set_event(&common->tx_thread.event);
+
                                rsi_dbg(ISR_ZONE,
-                                       "%s: ==> BUFFER_AVILABLE <==\n",
+                                       "%s: ==> BUFFER_AVAILABLE <==\n",
                                        __func__);
-                               dev->rx_info.buf_avilable_counter++;
+                               dev->rx_info.buf_available_counter++;
                                break;
 
                        case FIRMWARE_ASSERT_IND:
index 4c46e56..3226f19 100644 (file)
@@ -550,33 +550,7 @@ static struct usb_driver rsi_driver = {
 #endif
 };
 
-/**
- * rsi_module_init() - This function registers the client driver.
- * @void: Void.
- *
- * Return: 0 on success.
- */
-static int rsi_module_init(void)
-{
-       usb_register(&rsi_driver);
-       rsi_dbg(INIT_ZONE, "%s: Registering driver\n", __func__);
-       return 0;
-}
-
-/**
- * rsi_module_exit() - This function unregisters the client driver.
- * @void: Void.
- *
- * Return: None.
- */
-static void rsi_module_exit(void)
-{
-       usb_deregister(&rsi_driver);
-       rsi_dbg(INFO_ZONE, "%s: Unregistering driver\n", __func__);
-}
-
-module_init(rsi_module_init);
-module_exit(rsi_module_exit);
+module_usb_driver(rsi_driver);
 
 MODULE_AUTHOR("Redpine Signals Inc");
 MODULE_DESCRIPTION("Common USB layer for RSI drivers");
index 2cb73e7..5baed94 100644 (file)
@@ -115,6 +115,7 @@ struct wmm_qinfo {
        s32 weight;
        s32 wme_params;
        s32 pkt_contended;
+       s32 txop;
 };
 
 struct transmit_q_stats {
@@ -141,6 +142,12 @@ struct rsi_thread {
        atomic_t thread_done;
 };
 
+struct cqm_info {
+       s8 last_cqm_event_rssi;
+       int rssi_thold;
+       u32 rssi_hyst;
+};
+
 struct rsi_hw;
 
 struct rsi_common {
@@ -192,6 +199,11 @@ struct rsi_common {
        u8 selected_qnum;
        u32 pkt_cnt;
        u8 min_weight;
+
+       /* bgscan related */
+       struct cqm_info cqm_info;
+
+       bool hw_data_qs_blocked;
 };
 
 struct rsi_hw {
index 225215a..3741173 100644 (file)
@@ -69,6 +69,7 @@
 
 #define RSI_LMAC_CLOCK_80MHZ            0x1
 #define RSI_ENABLE_40MHZ                (0x1 << 3)
+#define ENABLE_SHORTGI_RATE            BIT(9)
 
 #define RX_BA_INDICATION                1
 #define RSI_TBL_SZ                      40
 #define BW_20MHZ                        0
 #define BW_40MHZ                        1
 
+#define EP_2GHZ_20MHZ                  0
+#define EP_2GHZ_40MHZ                  1
+#define EP_5GHZ_20MHZ                  2
+#define EP_5GHZ_40MHZ                  3
+
+#define SIFS_TX_11N_VALUE              580
+#define SIFS_TX_11B_VALUE              346
+#define SHORT_SLOT_VALUE               360
+#define LONG_SLOT_VALUE                        640
+#define OFDM_ACK_TOUT_VALUE            2720
+#define CCK_ACK_TOUT_VALUE             9440
+#define LONG_PREAMBLE                  0x0000
+#define SHORT_PREAMBLE                 0x0001
+
 #define RSI_SUPP_FILTERS       (FIF_ALLMULTI | FIF_PROBE_REQ |\
                                 FIF_BCN_PRBRESP_PROMISC)
 enum opmode {
@@ -153,7 +168,7 @@ enum cmd_frame_type {
        SCAN_REQUEST,
        TSF_UPDATE,
        PEER_NOTIFY,
-       BLOCK_UNBLOCK,
+       BLOCK_HW_QUEUE,
        SET_KEY_REQ,
        AUTO_RATE_IND,
        BOOTUP_PARAMS_REQUEST,
@@ -238,6 +253,12 @@ struct rsi_radio_caps {
        u8 num_11n_rates;
        u8 num_11ac_rates;
        __le16 gcpd_per_rate[20];
+       __le16 sifs_tx_11n;
+       __le16 sifs_tx_11b;
+       __le16 slot_rx_11n;
+       __le16 ofdm_ack_tout;
+       __le16 cck_ack_tout;
+       __le16 preamble_type;
 } __packed;
 
 static inline u32 rsi_get_queueno(u8 *addr, u16 offset)
@@ -272,6 +293,7 @@ int rsi_send_aggregation_params_frame(struct rsi_common *common, u16 tid,
 int rsi_hal_load_key(struct rsi_common *common, u8 *data, u16 key_len,
                     u8 key_type, u8 key_id, u32 cipher);
 int rsi_set_channel(struct rsi_common *common, u16 chno);
+int rsi_send_block_unblock_frame(struct rsi_common *common, bool event);
 void rsi_inform_bss_status(struct rsi_common *common, u8 status,
                           const u8 *bssid, u8 qos_enable, u16 aid);
 void rsi_indicate_pkt_to_os(struct rsi_common *common, struct sk_buff *skb);
@@ -283,4 +305,5 @@ void rsi_core_qos_processor(struct rsi_common *common);
 void rsi_core_xmit(struct rsi_common *common, struct sk_buff *skb);
 int rsi_send_mgmt_pkt(struct rsi_common *common, struct sk_buff *skb);
 int rsi_send_data_pkt(struct rsi_common *common, struct sk_buff *skb);
+int rsi_band_check(struct rsi_common *common);
 #endif
index df4b5e2..c7e8f2b 100644 (file)
@@ -30,7 +30,7 @@
 
 enum sdio_interrupt_type {
        BUFFER_FULL         = 0x0,
-       BUFFER_AVAILABLE    = 0x1,
+       BUFFER_AVAILABLE    = 0x2,
        FIRMWARE_ASSERT_IND = 0x3,
        MSDU_PACKET_PENDING = 0x4,
        UNKNOWN_INT         = 0XE
@@ -42,7 +42,7 @@ enum sdio_interrupt_type {
 #define PKT_MGMT_BUFF_FULL                      2
 #define MSDU_PKT_PENDING                        3
 /* Interrupt Bit Related Macros */
-#define PKT_BUFF_AVAILABLE                      0
+#define PKT_BUFF_AVAILABLE                      1
 #define FW_ASSERT_IND                           2
 
 #define RSI_DEVICE_BUFFER_STATUS_REGISTER       0xf3
@@ -84,7 +84,7 @@ enum sdio_interrupt_type {
 #define TA_HOLD_THREAD_VALUE         cpu_to_le32(0xF)
 #define TA_RELEASE_THREAD_VALUE      cpu_to_le32(0xF)
 #define TA_BASE_ADDR                 0x2200
-#define MISC_CFG_BASE_ADDR           0x4150
+#define MISC_CFG_BASE_ADDR           0x4105
 
 struct receive_info {
        bool buffer_full;
@@ -98,7 +98,7 @@ struct receive_info {
        u32 total_sdio_msdu_pending_intr;
        u32 total_sdio_unknown_intr;
        u32 buf_full_counter;
-       u32 buf_avilable_counter;
+       u32 buf_available_counter;
 };
 
 struct rsi_91x_sdiodev {
index c17fcf2..893c9d5 100644 (file)
@@ -947,6 +947,40 @@ static inline u8 rt2800_get_beacon_offset(struct rt2x00_dev *rt2x00dev,
        return BEACON_BASE_TO_OFFSET(rt2800_hw_beacon_base(rt2x00dev, index));
 }
 
+static void rt2800_update_beacons_setup(struct rt2x00_dev *rt2x00dev)
+{
+       struct data_queue *queue = rt2x00dev->bcn;
+       struct queue_entry *entry;
+       int i, bcn_num = 0;
+       u64 off, reg = 0;
+       u32 bssid_dw1;
+
+       /*
+        * Setup offsets of all active beacons in BCN_OFFSET{0,1} registers.
+        */
+       for (i = 0; i < queue->limit; i++) {
+               entry = &queue->entries[i];
+               if (!test_bit(ENTRY_BCN_ENABLED, &entry->flags))
+                       continue;
+               off = rt2800_get_beacon_offset(rt2x00dev, entry->entry_idx);
+               reg |= off << (8 * bcn_num);
+               bcn_num++;
+       }
+
+       WARN_ON_ONCE(bcn_num != rt2x00dev->intf_beaconing);
+
+       rt2800_register_write(rt2x00dev, BCN_OFFSET0, (u32) reg);
+       rt2800_register_write(rt2x00dev, BCN_OFFSET1, (u32) (reg >> 32));
+
+       /*
+        * H/W sends up to MAC_BSSID_DW1_BSS_BCN_NUM + 1 consecutive beacons.
+        */
+       rt2800_register_read(rt2x00dev, MAC_BSSID_DW1, &bssid_dw1);
+       rt2x00_set_field32(&bssid_dw1, MAC_BSSID_DW1_BSS_BCN_NUM,
+                          bcn_num > 0 ? bcn_num - 1 : 0);
+       rt2800_register_write(rt2x00dev, MAC_BSSID_DW1, bssid_dw1);
+}
+
 void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc)
 {
        struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
@@ -1003,6 +1037,12 @@ void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc)
 
        rt2800_register_multiwrite(rt2x00dev, beacon_base, entry->skb->data,
                                   entry->skb->len + padding_len);
+       __set_bit(ENTRY_BCN_ENABLED, &entry->flags);
+
+       /*
+        * Change global beacons settings.
+        */
+       rt2800_update_beacons_setup(rt2x00dev);
 
        /*
         * Restore beaconing state.
@@ -1053,7 +1093,12 @@ void rt2800_clear_beacon(struct queue_entry *entry)
         * Clear beacon.
         */
        rt2800_clear_beacon_register(rt2x00dev, entry->entry_idx);
+       __clear_bit(ENTRY_BCN_ENABLED, &entry->flags);
 
+       /*
+        * Change global beacons settings.
+        */
+       rt2800_update_beacons_setup(rt2x00dev);
        /*
         * Restore beaconing state.
         */
@@ -1556,7 +1601,7 @@ void rt2800_config_intf(struct rt2x00_dev *rt2x00dev, struct rt2x00_intf *intf,
                if (!is_zero_ether_addr((const u8 *)conf->bssid)) {
                        reg = le32_to_cpu(conf->bssid[1]);
                        rt2x00_set_field32(&reg, MAC_BSSID_DW1_BSS_ID_MASK, 3);
-                       rt2x00_set_field32(&reg, MAC_BSSID_DW1_BSS_BCN_NUM, 7);
+                       rt2x00_set_field32(&reg, MAC_BSSID_DW1_BSS_BCN_NUM, 0);
                        conf->bssid[1] = cpu_to_le32(reg);
                }
 
@@ -4517,28 +4562,6 @@ static int rt2800_init_registers(struct rt2x00_dev *rt2x00dev)
        if (ret)
                return ret;
 
-       rt2800_register_read(rt2x00dev, BCN_OFFSET0, &reg);
-       rt2x00_set_field32(&reg, BCN_OFFSET0_BCN0,
-                          rt2800_get_beacon_offset(rt2x00dev, 0));
-       rt2x00_set_field32(&reg, BCN_OFFSET0_BCN1,
-                          rt2800_get_beacon_offset(rt2x00dev, 1));
-       rt2x00_set_field32(&reg, BCN_OFFSET0_BCN2,
-                          rt2800_get_beacon_offset(rt2x00dev, 2));
-       rt2x00_set_field32(&reg, BCN_OFFSET0_BCN3,
-                          rt2800_get_beacon_offset(rt2x00dev, 3));
-       rt2800_register_write(rt2x00dev, BCN_OFFSET0, reg);
-
-       rt2800_register_read(rt2x00dev, BCN_OFFSET1, &reg);
-       rt2x00_set_field32(&reg, BCN_OFFSET1_BCN4,
-                          rt2800_get_beacon_offset(rt2x00dev, 4));
-       rt2x00_set_field32(&reg, BCN_OFFSET1_BCN5,
-                          rt2800_get_beacon_offset(rt2x00dev, 5));
-       rt2x00_set_field32(&reg, BCN_OFFSET1_BCN6,
-                          rt2800_get_beacon_offset(rt2x00dev, 6));
-       rt2x00_set_field32(&reg, BCN_OFFSET1_BCN7,
-                          rt2800_get_beacon_offset(rt2x00dev, 7));
-       rt2800_register_write(rt2x00dev, BCN_OFFSET1, reg);
-
        rt2800_register_write(rt2x00dev, LEGACY_BASIC_RATE, 0x0000013f);
        rt2800_register_write(rt2x00dev, HT_BASIC_RATE, 0x00008003);
 
index 2bde672..c6ae9a4 100644 (file)
@@ -141,8 +141,11 @@ static void rt2x00lib_intf_scheduled_iter(void *data, u8 *mac,
        if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
                return;
 
-       if (test_and_clear_bit(DELAYED_UPDATE_BEACON, &intf->delayed_flags))
+       if (test_and_clear_bit(DELAYED_UPDATE_BEACON, &intf->delayed_flags)) {
+               mutex_lock(&intf->beacon_skb_mutex);
                rt2x00queue_update_beacon(rt2x00dev, vif);
+               mutex_unlock(&intf->beacon_skb_mutex);
+       }
 }
 
 static void rt2x00lib_intf_scheduled(struct work_struct *work)
@@ -216,7 +219,7 @@ static void rt2x00lib_beaconupdate_iter(void *data, u8 *mac,
         * never be called for USB devices.
         */
        WARN_ON(rt2x00_is_usb(rt2x00dev));
-       rt2x00queue_update_beacon_locked(rt2x00dev, vif);
+       rt2x00queue_update_beacon(rt2x00dev, vif);
 }
 
 void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev)
@@ -1452,8 +1455,7 @@ void rt2x00lib_remove_dev(struct rt2x00_dev *rt2x00dev)
        /*
         * Free the driver data.
         */
-       if (rt2x00dev->drv_data)
-               kfree(rt2x00dev->drv_data);
+       kfree(rt2x00dev->drv_data);
 }
 EXPORT_SYMBOL_GPL(rt2x00lib_remove_dev);
 
index 212ac48..e5935ea 100644 (file)
@@ -624,25 +624,24 @@ void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw,
         * Start/stop beaconing.
         */
        if (changes & BSS_CHANGED_BEACON_ENABLED) {
+               mutex_lock(&intf->beacon_skb_mutex);
                if (!bss_conf->enable_beacon && intf->enable_beacon) {
                        rt2x00dev->intf_beaconing--;
                        intf->enable_beacon = false;
-                       /*
-                        * Clear beacon in the H/W for this vif. This is needed
-                        * to disable beaconing on this particular interface
-                        * and keep it running on other interfaces.
-                        */
-                       rt2x00queue_clear_beacon(rt2x00dev, vif);
 
                        if (rt2x00dev->intf_beaconing == 0) {
                                /*
                                 * Last beaconing interface disabled
                                 * -> stop beacon queue.
                                 */
-                               mutex_lock(&intf->beacon_skb_mutex);
                                rt2x00queue_stop_queue(rt2x00dev->bcn);
-                               mutex_unlock(&intf->beacon_skb_mutex);
                        }
+                       /*
+                        * Clear beacon in the H/W for this vif. This is needed
+                        * to disable beaconing on this particular interface
+                        * and keep it running on other interfaces.
+                        */
+                       rt2x00queue_clear_beacon(rt2x00dev, vif);
                } else if (bss_conf->enable_beacon && !intf->enable_beacon) {
                        rt2x00dev->intf_beaconing++;
                        intf->enable_beacon = true;
@@ -658,11 +657,10 @@ void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw,
                                 * First beaconing interface enabled
                                 * -> start beacon queue.
                                 */
-                               mutex_lock(&intf->beacon_skb_mutex);
                                rt2x00queue_start_queue(rt2x00dev->bcn);
-                               mutex_unlock(&intf->beacon_skb_mutex);
                        }
                }
+               mutex_unlock(&intf->beacon_skb_mutex);
        }
 
        /*
@@ -799,6 +797,8 @@ int rt2x00mac_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant)
 
        setup.tx = tx_ant;
        setup.rx = rx_ant;
+       setup.rx_chain_num = 0;
+       setup.tx_chain_num = 0;
 
        rt2x00lib_config_antenna(rt2x00dev, setup);
 
index 6f236ea..f0178fd 100644 (file)
@@ -119,14 +119,12 @@ static int rt2x00mmio_alloc_queue_dma(struct rt2x00_dev *rt2x00dev,
        /*
         * Allocate DMA memory for descriptor and buffer.
         */
-       addr = dma_alloc_coherent(rt2x00dev->dev,
-                                 queue->limit * queue->desc_size,
-                                 &dma, GFP_KERNEL);
+       addr = dma_zalloc_coherent(rt2x00dev->dev,
+                                  queue->limit * queue->desc_size, &dma,
+                                  GFP_KERNEL);
        if (!addr)
                return -ENOMEM;
 
-       memset(addr, 0, queue->limit * queue->desc_size);
-
        /*
         * Initialize all queue entries to contain valid addresses.
         */
index 5642ccc..8e68f87 100644 (file)
@@ -754,8 +754,6 @@ int rt2x00queue_clear_beacon(struct rt2x00_dev *rt2x00dev,
        if (unlikely(!intf->beacon))
                return -ENOBUFS;
 
-       mutex_lock(&intf->beacon_skb_mutex);
-
        /*
         * Clean up the beacon skb.
         */
@@ -768,13 +766,11 @@ int rt2x00queue_clear_beacon(struct rt2x00_dev *rt2x00dev,
        if (rt2x00dev->ops->lib->clear_beacon)
                rt2x00dev->ops->lib->clear_beacon(intf->beacon);
 
-       mutex_unlock(&intf->beacon_skb_mutex);
-
        return 0;
 }
 
-int rt2x00queue_update_beacon_locked(struct rt2x00_dev *rt2x00dev,
-                                    struct ieee80211_vif *vif)
+int rt2x00queue_update_beacon(struct rt2x00_dev *rt2x00dev,
+                             struct ieee80211_vif *vif)
 {
        struct rt2x00_intf *intf = vif_to_intf(vif);
        struct skb_frame_desc *skbdesc;
@@ -815,19 +811,6 @@ int rt2x00queue_update_beacon_locked(struct rt2x00_dev *rt2x00dev,
 
 }
 
-int rt2x00queue_update_beacon(struct rt2x00_dev *rt2x00dev,
-                             struct ieee80211_vif *vif)
-{
-       struct rt2x00_intf *intf = vif_to_intf(vif);
-       int ret;
-
-       mutex_lock(&intf->beacon_skb_mutex);
-       ret = rt2x00queue_update_beacon_locked(rt2x00dev, vif);
-       mutex_unlock(&intf->beacon_skb_mutex);
-
-       return ret;
-}
-
 bool rt2x00queue_for_each_entry(struct data_queue *queue,
                                enum queue_index start,
                                enum queue_index end,
index c48125b..2233b91 100644 (file)
@@ -353,6 +353,7 @@ struct txentry_desc {
  */
 enum queue_entry_flags {
        ENTRY_BCN_ASSIGNED,
+       ENTRY_BCN_ENABLED,
        ENTRY_OWNER_DEVICE_DATA,
        ENTRY_DATA_PENDING,
        ENTRY_DATA_IO_FAILED,
index 2c1c02b..1e25929 100644 (file)
@@ -209,7 +209,7 @@ static void rtl8180_handle_rx(struct ieee80211_hw *dev)
        struct rtl8180_priv *priv = dev->priv;
        struct rtl818x_rx_cmd_desc *cmd_desc;
        unsigned int count = 32;
-       u8 signal, agc, sq;
+       u8 agc, sq, signal = 1;
        dma_addr_t mapping;
 
        while (count--) {
@@ -222,12 +222,20 @@ static void rtl8180_handle_rx(struct ieee80211_hw *dev)
                        struct rtl8187se_rx_desc *desc = entry;
 
                        flags = le32_to_cpu(desc->flags);
+                       /* if ownership flag is set, then we can trust the
+                        * HW has written other fields. We must not trust
+                        * other descriptor data read before we checked (read)
+                        * the ownership flag
+                        */
+                       rmb();
                        flags2 = le32_to_cpu(desc->flags2);
                        tsft = le64_to_cpu(desc->tsft);
                } else {
                        struct rtl8180_rx_desc *desc = entry;
 
                        flags = le32_to_cpu(desc->flags);
+                       /* same as above */
+                       rmb();
                        flags2 = le32_to_cpu(desc->flags2);
                        tsft = le64_to_cpu(desc->tsft);
                }
@@ -266,18 +274,21 @@ static void rtl8180_handle_rx(struct ieee80211_hw *dev)
                        rx_status.rate_idx = (flags >> 20) & 0xF;
                        agc = (flags2 >> 17) & 0x7F;
 
-                       if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8185) {
+                       switch (priv->chip_family) {
+                       case RTL818X_CHIP_FAMILY_RTL8185:
                                if (rx_status.rate_idx > 3)
-                                       signal = 90 - clamp_t(u8, agc, 25, 90);
+                                       signal = -clamp_t(u8, agc, 25, 90) - 9;
                                else
-                                       signal = 95 - clamp_t(u8, agc, 30, 95);
-                       } else if (priv->chip_family ==
-                                  RTL818X_CHIP_FAMILY_RTL8180) {
+                                       signal = -clamp_t(u8, agc, 30, 95);
+                               break;
+                       case RTL818X_CHIP_FAMILY_RTL8180:
                                sq = flags2 & 0xff;
                                signal = priv->rf->calc_rssi(agc, sq);
-                       } else {
+                               break;
+                       case RTL818X_CHIP_FAMILY_RTL8187SE:
                                /* TODO: rtl8187se rssi */
                                signal = 10;
+                               break;
                        }
                        rx_status.signal = signal;
                        rx_status.freq = dev->conf.chandef.chan->center_freq;
@@ -1751,8 +1762,7 @@ static int rtl8180_probe(struct pci_dev *pdev,
        dev->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band;
 
        dev->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
-                    IEEE80211_HW_RX_INCLUDES_FCS |
-                    IEEE80211_HW_SIGNAL_UNSPEC;
+               IEEE80211_HW_RX_INCLUDES_FCS;
        dev->vif_data_size = sizeof(struct rtl8180_vif);
        dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
                                        BIT(NL80211_IFTYPE_ADHOC);
@@ -1809,6 +1819,11 @@ static int rtl8180_probe(struct pci_dev *pdev,
                pci_try_set_mwi(pdev);
        }
 
+       if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8185)
+               dev->flags |= IEEE80211_HW_SIGNAL_DBM;
+       else
+               dev->flags |= IEEE80211_HW_SIGNAL_UNSPEC;
+
        rtl8180_eeprom_read(priv);
 
        switch (priv->rf_type) {
index 871fc3c..049f4c8 100644 (file)
@@ -114,7 +114,7 @@ extern u32 btc_dbg_type[];
 
 
 #define        CL_SPRINTF      snprintf
-#define        CL_PRINTF       printk
+#define        CL_PRINTF(buf)  printk("%s", buf)
 
 #define        BTC_PRINT(dbgtype, dbgflag, printstr, ...)              \
        do {                                                    \
index 3d1f0dd..592125a 100644 (file)
@@ -203,11 +203,12 @@ u32 rtl92d_phy_query_bb_reg(struct ieee80211_hw *hw, u32 regaddr, u32 bitmask)
        struct rtl_priv *rtlpriv = rtl_priv(hw);
        struct rtl_hal *rtlhal = rtl_hal(rtlpriv);
        u32 returnvalue, originalvalue, bitshift;
-       u8 dbi_direct;
 
        RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, "regaddr(%#x), bitmask(%#x)\n",
                 regaddr, bitmask);
        if (rtlhal->during_mac1init_radioa || rtlhal->during_mac0init_radiob) {
+               u8 dbi_direct = 0;
+
                /* mac1 use phy0 read radio_b. */
                /* mac0 use phy1 read radio_b. */
                if (rtlhal->during_mac1init_radioa)
index 4e782f1..3823485 100644 (file)
@@ -991,8 +991,9 @@ out:
 
 static int wl1251_op_hw_scan(struct ieee80211_hw *hw,
                             struct ieee80211_vif *vif,
-                            struct cfg80211_scan_request *req)
+                            struct ieee80211_scan_request *hw_req)
 {
+       struct cfg80211_scan_request *req = &hw_req->req;
        struct wl1251 *wl = hw->priv;
        struct sk_buff *skb;
        size_t ssid_len = 0;
index 7541bd1..0c0d5cd 100644 (file)
@@ -156,7 +156,7 @@ static int wl1271_scan_send(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                                         cmd->params.role_id, band,
                                         wl->scan.ssid, wl->scan.ssid_len,
                                         wl->scan.req->ie,
-                                        wl->scan.req->ie_len, false);
+                                        wl->scan.req->ie_len, NULL, 0, false);
        if (ret < 0) {
                wl1271_error("PROBE request template failed");
                goto out;
@@ -317,7 +317,7 @@ static void wl12xx_adjust_channels(struct wl1271_cmd_sched_scan_config *cmd,
 int wl1271_scan_sched_scan_config(struct wl1271 *wl,
                                  struct wl12xx_vif *wlvif,
                                  struct cfg80211_sched_scan_request *req,
-                                 struct ieee80211_sched_scan_ies *ies)
+                                 struct ieee80211_scan_ies *ies)
 {
        struct wl1271_cmd_sched_scan_config *cfg = NULL;
        struct wlcore_scan_channels *cfg_channels = NULL;
@@ -378,8 +378,11 @@ int wl1271_scan_sched_scan_config(struct wl1271 *wl,
                                                 wlvif->role_id, band,
                                                 req->ssids[0].ssid,
                                                 req->ssids[0].ssid_len,
-                                                ies->ie[band],
-                                                ies->len[band], true);
+                                                ies->ies[band],
+                                                ies->len[band],
+                                                ies->common_ies,
+                                                ies->common_ie_len,
+                                                true);
                if (ret < 0) {
                        wl1271_error("2.4GHz PROBE request template failed");
                        goto out;
@@ -392,8 +395,11 @@ int wl1271_scan_sched_scan_config(struct wl1271 *wl,
                                                 wlvif->role_id, band,
                                                 req->ssids[0].ssid,
                                                 req->ssids[0].ssid_len,
-                                                ies->ie[band],
-                                                ies->len[band], true);
+                                                ies->ies[band],
+                                                ies->len[band],
+                                                ies->common_ies,
+                                                ies->common_ie_len,
+                                                true);
                if (ret < 0) {
                        wl1271_error("5GHz PROBE request template failed");
                        goto out;
@@ -449,7 +455,7 @@ out_free:
 
 int wl12xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif  *wlvif,
                            struct cfg80211_sched_scan_request *req,
-                           struct ieee80211_sched_scan_ies *ies)
+                           struct ieee80211_scan_ies *ies)
 {
        int ret;
 
index 264af7a..427f9af 100644 (file)
@@ -135,6 +135,6 @@ int wl12xx_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif);
 void wl12xx_scan_completed(struct wl1271 *wl, struct wl12xx_vif *wlvif);
 int wl12xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif  *wlvif,
                            struct cfg80211_sched_scan_request *req,
-                           struct ieee80211_sched_scan_ies *ies);
+                           struct ieee80211_scan_ies *ies);
 void wl12xx_scan_sched_scan_stop(struct wl1271 *wl,  struct wl12xx_vif *wlvif);
 #endif
index 2b642f8..98666f2 100644 (file)
@@ -113,6 +113,8 @@ static int wl18xx_scan_send(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                                 req->ssids ? req->ssids[0].ssid_len : 0,
                                 req->ie,
                                 req->ie_len,
+                                NULL,
+                                0,
                                 false);
                if (ret < 0) {
                        wl1271_error("2.4GHz PROBE request template failed");
@@ -128,6 +130,8 @@ static int wl18xx_scan_send(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                                 req->ssids ? req->ssids[0].ssid_len : 0,
                                 req->ie,
                                 req->ie_len,
+                                NULL,
+                                0,
                                 false);
                if (ret < 0) {
                        wl1271_error("5GHz PROBE request template failed");
@@ -161,7 +165,7 @@ static
 int wl18xx_scan_sched_scan_config(struct wl1271 *wl,
                                  struct wl12xx_vif *wlvif,
                                  struct cfg80211_sched_scan_request *req,
-                                 struct ieee80211_sched_scan_ies *ies)
+                                 struct ieee80211_scan_ies *ies)
 {
        struct wl18xx_cmd_scan_params *cmd;
        struct wlcore_scan_channels *cmd_channels = NULL;
@@ -237,8 +241,10 @@ int wl18xx_scan_sched_scan_config(struct wl1271 *wl,
                                 cmd->role_id, band,
                                 req->ssids ? req->ssids[0].ssid : NULL,
                                 req->ssids ? req->ssids[0].ssid_len : 0,
-                                ies->ie[band],
+                                ies->ies[band],
                                 ies->len[band],
+                                ies->common_ies,
+                                ies->common_ie_len,
                                 true);
                if (ret < 0) {
                        wl1271_error("2.4GHz PROBE request template failed");
@@ -252,8 +258,10 @@ int wl18xx_scan_sched_scan_config(struct wl1271 *wl,
                                 cmd->role_id, band,
                                 req->ssids ? req->ssids[0].ssid : NULL,
                                 req->ssids ? req->ssids[0].ssid_len : 0,
-                                ies->ie[band],
+                                ies->ies[band],
                                 ies->len[band],
+                                ies->common_ies,
+                                ies->common_ie_len,
                                 true);
                if (ret < 0) {
                        wl1271_error("5GHz PROBE request template failed");
@@ -277,7 +285,7 @@ out:
 
 int wl18xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                            struct cfg80211_sched_scan_request *req,
-                           struct ieee80211_sched_scan_ies *ies)
+                           struct ieee80211_scan_ies *ies)
 {
        return wl18xx_scan_sched_scan_config(wl, wlvif, req, ies);
 }
index eadee42..2e636aa 100644 (file)
@@ -122,6 +122,6 @@ int wl18xx_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif);
 void wl18xx_scan_completed(struct wl1271 *wl, struct wl12xx_vif *wlvif);
 int wl18xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                            struct cfg80211_sched_scan_request *req,
-                           struct ieee80211_sched_scan_ies *ies);
+                           struct ieee80211_scan_ies *ies);
 void wl18xx_scan_sched_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif);
 #endif
index 40dc30f..e269c0a 100644 (file)
@@ -1124,7 +1124,8 @@ out:
 int wl12xx_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                               u8 role_id, u8 band,
                               const u8 *ssid, size_t ssid_len,
-                              const u8 *ie, size_t ie_len, bool sched_scan)
+                              const u8 *ie0, size_t ie0_len, const u8 *ie1,
+                              size_t ie1_len, bool sched_scan)
 {
        struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
        struct sk_buff *skb;
@@ -1136,13 +1137,15 @@ int wl12xx_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif,
        wl1271_debug(DEBUG_SCAN, "build probe request band %d", band);
 
        skb = ieee80211_probereq_get(wl->hw, vif, ssid, ssid_len,
-                                    ie_len);
+                                    ie0_len + ie1_len);
        if (!skb) {
                ret = -ENOMEM;
                goto out;
        }
-       if (ie_len)
-               memcpy(skb_put(skb, ie_len), ie, ie_len);
+       if (ie0_len)
+               memcpy(skb_put(skb, ie0_len), ie0, ie0_len);
+       if (ie1_len)
+               memcpy(skb_put(skb, ie1_len), ie1, ie1_len);
 
        if (sched_scan &&
            (wl->quirks & WLCORE_QUIRK_DUAL_PROBE_TMPL)) {
index b084830..6788d73 100644 (file)
@@ -64,7 +64,8 @@ int wl1271_cmd_build_ps_poll(struct wl1271 *wl, struct wl12xx_vif *wlvif,
 int wl12xx_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                               u8 role_id, u8 band,
                               const u8 *ssid, size_t ssid_len,
-                              const u8 *ie, size_t ie_len, bool sched_scan);
+                              const u8 *ie, size_t ie_len, const u8 *common_ie,
+                              size_t common_ie_len, bool sched_scan);
 struct sk_buff *wl1271_cmd_build_ap_probe_req(struct wl1271 *wl,
                                              struct wl12xx_vif *wlvif,
                                              struct sk_buff *skb);
index 3d6028e..48f8386 100644 (file)
@@ -3540,8 +3540,9 @@ out:
 
 static int wl1271_op_hw_scan(struct ieee80211_hw *hw,
                             struct ieee80211_vif *vif,
-                            struct cfg80211_scan_request *req)
+                            struct ieee80211_scan_request *hw_req)
 {
+       struct cfg80211_scan_request *req = &hw_req->req;
        struct wl1271 *wl = hw->priv;
        int ret;
        u8 *ssid = NULL;
@@ -3636,7 +3637,7 @@ out:
 static int wl1271_op_sched_scan_start(struct ieee80211_hw *hw,
                                      struct ieee80211_vif *vif,
                                      struct cfg80211_sched_scan_request *req,
-                                     struct ieee80211_sched_scan_ies *ies)
+                                     struct ieee80211_scan_ies *ies)
 {
        struct wl1271 *wl = hw->priv;
        struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
index a6ab24b..4dadd0c 100644 (file)
@@ -37,7 +37,7 @@ void wl1271_scan_complete_work(struct work_struct *work);
 int wl1271_scan_sched_scan_config(struct wl1271 *wl,
                                     struct wl12xx_vif *wlvif,
                                     struct cfg80211_sched_scan_request *req,
-                                    struct ieee80211_sched_scan_ies *ies);
+                                    struct ieee80211_scan_ies *ies);
 int wl1271_scan_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif);
 void wlcore_scan_sched_scan_results(struct wl1271 *wl);
 
index 95a5450..7132050 100644 (file)
@@ -95,7 +95,7 @@ struct wlcore_ops {
        int (*scan_stop)(struct wl1271 *wl, struct wl12xx_vif *wlvif);
        int (*sched_scan_start)(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                                struct cfg80211_sched_scan_request *req,
-                               struct ieee80211_sched_scan_ies *ies);
+                               struct ieee80211_scan_ies *ies);
        void (*sched_scan_stop)(struct wl1271 *wl, struct wl12xx_vif *wlvif);
        int (*get_spare_blocks)(struct wl1271 *wl, bool is_gem);
        int (*set_key)(struct wl1271 *wl, enum set_key_cmd cmd,
index 6bff13f..75d17e1 100644 (file)
@@ -1621,6 +1621,9 @@ enum ieee80211_reasoncode {
        WLAN_REASON_INVALID_RSN_IE_CAP = 22,
        WLAN_REASON_IEEE8021X_FAILED = 23,
        WLAN_REASON_CIPHER_SUITE_REJECTED = 24,
+       /* TDLS (802.11z) */
+       WLAN_REASON_TDLS_TEARDOWN_UNREACHABLE = 25,
+       WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED = 26,
        /* 802.11e */
        WLAN_REASON_DISASSOC_UNSPECIFIED_QOS = 32,
        WLAN_REASON_DISASSOC_QAP_NO_BANDWIDTH = 33,
index e46c437..0a080c4 100644 (file)
@@ -2266,10 +2266,6 @@ struct cfg80211_qos_map {
  *
  * @get_antenna: Get current antenna configuration from device (tx_ant, rx_ant).
  *
- * @set_ringparam: Set tx and rx ring sizes.
- *
- * @get_ringparam: Get tx and rx ring current and maximum sizes.
- *
  * @tdls_mgmt: Transmit a TDLS management frame.
  * @tdls_oper: Perform a high-level TDLS operation (e.g. TDLS link setup).
  *
@@ -2278,16 +2274,6 @@ struct cfg80211_qos_map {
  *
  * @set_noack_map: Set the NoAck Map for the TIDs.
  *
- * @get_et_sset_count:  Ethtool API to get string-set count.
- *     See @ethtool_ops.get_sset_count
- *
- * @get_et_stats:  Ethtool API to get a set of u64 stats.
- *     See @ethtool_ops.get_ethtool_stats
- *
- * @get_et_strings:  Ethtool API to get a set of strings to describe stats
- *     and perhaps other supported types of ethtool data-sets.
- *     See @ethtool_ops.get_strings
- *
  * @get_channel: Get the current operating channel for the virtual interface.
  *     For monitor interfaces, it should return %NULL unless there's a single
  *     current monitoring channel.
@@ -2315,7 +2301,12 @@ struct cfg80211_qos_map {
  *     reliability. This operation can not fail.
  * @set_coalesce: Set coalesce parameters.
  *
- * @channel_switch: initiate channel-switch procedure (with CSA)
+ * @channel_switch: initiate channel-switch procedure (with CSA). Driver is
+ *     responsible for veryfing if the switch is possible. Since this is
+ *     inherently tricky driver may decide to disconnect an interface later
+ *     with cfg80211_stop_iface(). This doesn't mean driver can accept
+ *     everything. It should do it's best to verify requests and reject them
+ *     as soon as possible.
  *
  * @set_qos_map: Set QoS mapping information to the driver
  *
@@ -2503,10 +2494,6 @@ struct cfg80211_ops {
        int     (*set_antenna)(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant);
        int     (*get_antenna)(struct wiphy *wiphy, u32 *tx_ant, u32 *rx_ant);
 
-       int     (*set_ringparam)(struct wiphy *wiphy, u32 tx, u32 rx);
-       void    (*get_ringparam)(struct wiphy *wiphy,
-                                u32 *tx, u32 *tx_max, u32 *rx, u32 *rx_max);
-
        int     (*sched_scan_start)(struct wiphy *wiphy,
                                struct net_device *dev,
                                struct cfg80211_sched_scan_request *request);
@@ -2518,7 +2505,7 @@ struct cfg80211_ops {
        int     (*tdls_mgmt)(struct wiphy *wiphy, struct net_device *dev,
                             const u8 *peer, u8 action_code,  u8 dialog_token,
                             u16 status_code, u32 peer_capability,
-                            const u8 *buf, size_t len);
+                            bool initiator, const u8 *buf, size_t len);
        int     (*tdls_oper)(struct wiphy *wiphy, struct net_device *dev,
                             const u8 *peer, enum nl80211_tdls_operation oper);
 
@@ -2529,13 +2516,6 @@ struct cfg80211_ops {
                                  struct net_device *dev,
                                  u16 noack_map);
 
-       int     (*get_et_sset_count)(struct wiphy *wiphy,
-                                    struct net_device *dev, int sset);
-       void    (*get_et_stats)(struct wiphy *wiphy, struct net_device *dev,
-                               struct ethtool_stats *stats, u64 *data);
-       void    (*get_et_strings)(struct wiphy *wiphy, struct net_device *dev,
-                                 u32 sset, u8 *data);
-
        int     (*get_channel)(struct wiphy *wiphy,
                               struct wireless_dev *wdev,
                               struct cfg80211_chan_def *chandef);
@@ -4843,6 +4823,10 @@ void cfg80211_stop_iface(struct wiphy *wiphy, struct wireless_dev *wdev,
  */
 void cfg80211_shutdown_all_interfaces(struct wiphy *wiphy);
 
+
+/* ethtool helper */
+void cfg80211_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info);
+
 /* Logging, debugging and troubleshooting/diagnostic helpers. */
 
 /* wiphy_printk helpers, similar to dev_printk */
index 421b6ec..9ce5cb1 100644 (file)
@@ -754,20 +754,25 @@ struct ieee80211_tx_info {
 };
 
 /**
- * struct ieee80211_sched_scan_ies - scheduled scan IEs
+ * struct ieee80211_scan_ies - descriptors for different blocks of IEs
  *
- * This structure is used to pass the appropriate IEs to be used in scheduled
- * scans for all bands.  It contains both the IEs passed from the userspace
+ * This structure is used to point to different blocks of IEs in HW scan
+ * and scheduled scan. These blocks contain the IEs passed by userspace
  * and the ones generated by mac80211.
  *
- * @ie: array with the IEs for each supported band
- * @len: array with the total length of the IEs for each band
+ * @ies: pointers to band specific IEs.
+ * @len: lengths of band_specific IEs.
+ * @common_ies: IEs for all bands (especially vendor specific ones)
+ * @common_ie_len: length of the common_ies
  */
-struct ieee80211_sched_scan_ies {
-       u8 *ie[IEEE80211_NUM_BANDS];
+struct ieee80211_scan_ies {
+       const u8 *ies[IEEE80211_NUM_BANDS];
        size_t len[IEEE80211_NUM_BANDS];
+       const u8 *common_ies;
+       size_t common_ie_len;
 };
 
+
 static inline struct ieee80211_tx_info *IEEE80211_SKB_CB(struct sk_buff *skb)
 {
        return (struct ieee80211_tx_info *)skb->cb;
@@ -1601,11 +1606,8 @@ struct ieee80211_tx_control {
  *     is not enabled the default action is to disconnect when getting the
  *     CSA frame.
  *
- * @IEEE80211_HW_CHANGE_RUNNING_CHANCTX: The hardware can change a
- *     channel context on-the-fly.  This is needed for channel switch
- *     on single-channel hardware.  It can also be used as an
- *     optimization in certain channel switch cases with
- *     multi-channel.
+ * @IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS: The HW supports scanning on all bands
+ *     in one command, mac80211 doesn't have to run separate scans per band.
  */
 enum ieee80211_hw_flags {
        IEEE80211_HW_HAS_RATE_CONTROL                   = 1<<0,
@@ -1637,7 +1639,8 @@ enum ieee80211_hw_flags {
        IEEE80211_HW_TIMING_BEACON_ONLY                 = 1<<26,
        IEEE80211_HW_SUPPORTS_HT_CCK_RATES              = 1<<27,
        IEEE80211_HW_CHANCTX_STA_CSA                    = 1<<28,
-       IEEE80211_HW_CHANGE_RUNNING_CHANCTX             = 1<<29,
+       /* bit 29 unused */
+       IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS           = 1<<30,
 };
 
 /**
@@ -1763,6 +1766,19 @@ struct ieee80211_hw {
        const struct ieee80211_cipher_scheme *cipher_schemes;
 };
 
+/**
+ * struct ieee80211_scan_request - hw scan request
+ *
+ * @ies: pointers different parts of IEs (in req.ie)
+ * @req: cfg80211 request.
+ */
+struct ieee80211_scan_request {
+       struct ieee80211_scan_ies ies;
+
+       /* Keep last */
+       struct cfg80211_scan_request req;
+};
+
 /**
  * wiphy_to_ieee80211_hw - return a mac80211 driver hw struct from a wiphy
  *
@@ -2764,6 +2780,15 @@ enum ieee80211_roc_type {
  *     mac80211 will transmit the frame right away.
  *     The callback is optional and can (should!) sleep.
  *
+ * @mgd_protect_tdls_discover: Protect a TDLS discovery session. After sending
+ *     a TDLS discovery-request, we expect a reply to arrive on the AP's
+ *     channel. We must stay on the channel (no PSM, scan, etc.), since a TDLS
+ *     setup-response is a direct packet not buffered by the AP.
+ *     mac80211 will call this function just before the transmission of a TDLS
+ *     discovery-request. The recommended period of protection is at least
+ *     2 * (DTIM period).
+ *     The callback is optional and can sleep.
+ *
  * @add_chanctx: Notifies device driver about new channel context creation.
  * @remove_chanctx: Notifies device driver about channel context destruction.
  * @change_chanctx: Notifies device driver about channel context changes that
@@ -2865,13 +2890,13 @@ struct ieee80211_ops {
        void (*set_default_unicast_key)(struct ieee80211_hw *hw,
                                        struct ieee80211_vif *vif, int idx);
        int (*hw_scan)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-                      struct cfg80211_scan_request *req);
+                      struct ieee80211_scan_request *req);
        void (*cancel_hw_scan)(struct ieee80211_hw *hw,
                               struct ieee80211_vif *vif);
        int (*sched_scan_start)(struct ieee80211_hw *hw,
                                struct ieee80211_vif *vif,
                                struct cfg80211_sched_scan_request *req,
-                               struct ieee80211_sched_scan_ies *ies);
+                               struct ieee80211_scan_ies *ies);
        int (*sched_scan_stop)(struct ieee80211_hw *hw,
                               struct ieee80211_vif *vif);
        void (*sw_scan_start)(struct ieee80211_hw *hw);
@@ -2981,6 +3006,9 @@ struct ieee80211_ops {
        void    (*mgd_prepare_tx)(struct ieee80211_hw *hw,
                                  struct ieee80211_vif *vif);
 
+       void    (*mgd_protect_tdls_discover)(struct ieee80211_hw *hw,
+                                            struct ieee80211_vif *vif);
+
        int (*add_chanctx)(struct ieee80211_hw *hw,
                           struct ieee80211_chanctx_conf *ctx);
        void (*remove_chanctx)(struct ieee80211_hw *hw,
@@ -4815,4 +4843,17 @@ int ieee80211_parse_p2p_noa(const struct ieee80211_p2p_noa_attr *attr,
  */
 void ieee80211_update_p2p_noa(struct ieee80211_noa_data *data, u32 tsf);
 
+/**
+ * ieee80211_tdls_oper - request userspace to perform a TDLS operation
+ * @vif: virtual interface
+ * @peer: the peer's destination address
+ * @oper: the requested TDLS operation
+ * @reason_code: reason code for the operation, valid for TDLS teardown
+ * @gfp: allocation flags
+ *
+ * See cfg80211_tdls_oper_request().
+ */
+void ieee80211_tdls_oper_request(struct ieee80211_vif *vif, const u8 *peer,
+                                enum nl80211_tdls_operation oper,
+                                u16 reason_code, gfp_t gfp);
 #endif /* MAC80211_H */
index be9519b..f1db15b 100644 (file)
@@ -1591,6 +1591,9 @@ enum nl80211_commands {
  *     creation then the new interface will be owned by the netlink socket
  *     that created it and will be destroyed when the socket is closed
  *
+ * @NL80211_ATTR_TDLS_INITIATOR: flag attribute indicating the current end is
+ *     the TDLS link initiator.
+ *
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
  */
@@ -1931,6 +1934,8 @@ enum nl80211_attrs {
        NL80211_ATTR_CSA_C_OFFSETS_TX,
        NL80211_ATTR_MAX_CSA_COUNTERS,
 
+       NL80211_ATTR_TDLS_INITIATOR,
+
        /* add attributes here, update the policy in nl80211.c */
 
        __NL80211_ATTR_AFTER_LAST,
index 97b5dca..aeb6a48 100644 (file)
@@ -19,14 +19,6 @@ if MAC80211 != n
 config MAC80211_HAS_RC
        bool
 
-config MAC80211_RC_PID
-       bool "PID controller based rate control algorithm" if EXPERT
-       select MAC80211_HAS_RC
-       ---help---
-         This option enables a TX rate control algorithm for
-         mac80211 that uses a PID controller to select the TX
-         rate.
-
 config MAC80211_RC_MINSTREL
        bool "Minstrel" if EXPERT
        select MAC80211_HAS_RC
@@ -51,14 +43,6 @@ choice
          overridden through the ieee80211_default_rc_algo module
          parameter if different algorithms are available.
 
-config MAC80211_RC_DEFAULT_PID
-       bool "PID controller based rate control algorithm"
-       depends on MAC80211_RC_PID
-       ---help---
-         Select the PID controller based rate control as the
-         default rate control algorithm. You should choose
-         this unless you know what you are doing.
-
 config MAC80211_RC_DEFAULT_MINSTREL
        bool "Minstrel"
        depends on MAC80211_RC_MINSTREL
@@ -72,7 +56,6 @@ config MAC80211_RC_DEFAULT
        string
        default "minstrel_ht" if MAC80211_RC_DEFAULT_MINSTREL && MAC80211_RC_MINSTREL_HT
        default "minstrel" if MAC80211_RC_DEFAULT_MINSTREL
-       default "pid" if MAC80211_RC_DEFAULT_PID
        default ""
 
 endif
index 1e46ffa..7273d27 100644 (file)
@@ -17,6 +17,7 @@ mac80211-y := \
        aes_ccm.o \
        aes_cmac.o \
        cfg.o \
+       ethtool.o \
        rx.o \
        spectmgmt.o \
        tx.o \
@@ -47,17 +48,12 @@ mac80211-$(CONFIG_PM) += pm.o
 
 CFLAGS_trace.o := -I$(src)
 
-# objects for PID algorithm
-rc80211_pid-y := rc80211_pid_algo.o
-rc80211_pid-$(CONFIG_MAC80211_DEBUGFS) += rc80211_pid_debugfs.o
-
 rc80211_minstrel-y := rc80211_minstrel.o
 rc80211_minstrel-$(CONFIG_MAC80211_DEBUGFS) += rc80211_minstrel_debugfs.o
 
 rc80211_minstrel_ht-y := rc80211_minstrel_ht.o
 rc80211_minstrel_ht-$(CONFIG_MAC80211_DEBUGFS) += rc80211_minstrel_ht_debugfs.o
 
-mac80211-$(CONFIG_MAC80211_RC_PID) += $(rc80211_pid-y)
 mac80211-$(CONFIG_MAC80211_RC_MINSTREL) += $(rc80211_minstrel-y)
 mac80211-$(CONFIG_MAC80211_RC_MINSTREL_HT) += $(rc80211_minstrel_ht-y)
 
index ce9633a..d6986f3 100644 (file)
@@ -170,10 +170,13 @@ ieee80211_stop_queue_agg(struct ieee80211_sub_if_data *sdata, int tid)
 {
        int queue = sdata->vif.hw_queue[ieee80211_ac_from_tid(tid)];
 
+       /* we do refcounting here, so don't use the queue reason refcounting */
+
        if (atomic_inc_return(&sdata->local->agg_queue_stop[queue]) == 1)
                ieee80211_stop_queue_by_reason(
                        &sdata->local->hw, queue,
-                       IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
+                       IEEE80211_QUEUE_STOP_REASON_AGGREGATION,
+                       false);
        __acquire(agg_queue);
 }
 
@@ -185,7 +188,8 @@ ieee80211_wake_queue_agg(struct ieee80211_sub_if_data *sdata, int tid)
        if (atomic_dec_return(&sdata->local->agg_queue_stop[queue]) == 0)
                ieee80211_wake_queue_by_reason(
                        &sdata->local->hw, queue,
-                       IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
+                       IEEE80211_QUEUE_STOP_REASON_AGGREGATION,
+                       false);
        __release(agg_queue);
 }
 
index d7513a5..927b4ea 100644 (file)
@@ -468,327 +468,6 @@ void sta_set_rate_info_rx(struct sta_info *sta, struct rate_info *rinfo)
                rinfo->flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH;
 }
 
-static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
-{
-       struct ieee80211_sub_if_data *sdata = sta->sdata;
-       struct ieee80211_local *local = sdata->local;
-       struct rate_control_ref *ref = local->rate_ctrl;
-       struct timespec uptime;
-       u64 packets = 0;
-       u32 thr = 0;
-       int i, ac;
-
-       sinfo->generation = sdata->local->sta_generation;
-
-       sinfo->filled = STATION_INFO_INACTIVE_TIME |
-                       STATION_INFO_RX_BYTES64 |
-                       STATION_INFO_TX_BYTES64 |
-                       STATION_INFO_RX_PACKETS |
-                       STATION_INFO_TX_PACKETS |
-                       STATION_INFO_TX_RETRIES |
-                       STATION_INFO_TX_FAILED |
-                       STATION_INFO_TX_BITRATE |
-                       STATION_INFO_RX_BITRATE |
-                       STATION_INFO_RX_DROP_MISC |
-                       STATION_INFO_BSS_PARAM |
-                       STATION_INFO_CONNECTED_TIME |
-                       STATION_INFO_STA_FLAGS |
-                       STATION_INFO_BEACON_LOSS_COUNT;
-
-       do_posix_clock_monotonic_gettime(&uptime);
-       sinfo->connected_time = uptime.tv_sec - sta->last_connected;
-
-       sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx);
-       sinfo->tx_bytes = 0;
-       for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
-               sinfo->tx_bytes += sta->tx_bytes[ac];
-               packets += sta->tx_packets[ac];
-       }
-       sinfo->tx_packets = packets;
-       sinfo->rx_bytes = sta->rx_bytes;
-       sinfo->rx_packets = sta->rx_packets;
-       sinfo->tx_retries = sta->tx_retry_count;
-       sinfo->tx_failed = sta->tx_retry_failed;
-       sinfo->rx_dropped_misc = sta->rx_dropped;
-       sinfo->beacon_loss_count = sta->beacon_loss_count;
-
-       if ((sta->local->hw.flags & IEEE80211_HW_SIGNAL_DBM) ||
-           (sta->local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC)) {
-               sinfo->filled |= STATION_INFO_SIGNAL | STATION_INFO_SIGNAL_AVG;
-               if (!local->ops->get_rssi ||
-                   drv_get_rssi(local, sdata, &sta->sta, &sinfo->signal))
-                       sinfo->signal = (s8)sta->last_signal;
-               sinfo->signal_avg = (s8) -ewma_read(&sta->avg_signal);
-       }
-       if (sta->chains) {
-               sinfo->filled |= STATION_INFO_CHAIN_SIGNAL |
-                                STATION_INFO_CHAIN_SIGNAL_AVG;
-
-               sinfo->chains = sta->chains;
-               for (i = 0; i < ARRAY_SIZE(sinfo->chain_signal); i++) {
-                       sinfo->chain_signal[i] = sta->chain_signal_last[i];
-                       sinfo->chain_signal_avg[i] =
-                               (s8) -ewma_read(&sta->chain_signal_avg[i]);
-               }
-       }
-
-       sta_set_rate_info_tx(sta, &sta->last_tx_rate, &sinfo->txrate);
-       sta_set_rate_info_rx(sta, &sinfo->rxrate);
-
-       if (ieee80211_vif_is_mesh(&sdata->vif)) {
-#ifdef CONFIG_MAC80211_MESH
-               sinfo->filled |= STATION_INFO_LLID |
-                                STATION_INFO_PLID |
-                                STATION_INFO_PLINK_STATE |
-                                STATION_INFO_LOCAL_PM |
-                                STATION_INFO_PEER_PM |
-                                STATION_INFO_NONPEER_PM;
-
-               sinfo->llid = sta->llid;
-               sinfo->plid = sta->plid;
-               sinfo->plink_state = sta->plink_state;
-               if (test_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN)) {
-                       sinfo->filled |= STATION_INFO_T_OFFSET;
-                       sinfo->t_offset = sta->t_offset;
-               }
-               sinfo->local_pm = sta->local_pm;
-               sinfo->peer_pm = sta->peer_pm;
-               sinfo->nonpeer_pm = sta->nonpeer_pm;
-#endif
-       }
-
-       sinfo->bss_param.flags = 0;
-       if (sdata->vif.bss_conf.use_cts_prot)
-               sinfo->bss_param.flags |= BSS_PARAM_FLAGS_CTS_PROT;
-       if (sdata->vif.bss_conf.use_short_preamble)
-               sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_PREAMBLE;
-       if (sdata->vif.bss_conf.use_short_slot)
-               sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME;
-       sinfo->bss_param.dtim_period = sdata->local->hw.conf.ps_dtim_period;
-       sinfo->bss_param.beacon_interval = sdata->vif.bss_conf.beacon_int;
-
-       sinfo->sta_flags.set = 0;
-       sinfo->sta_flags.mask = BIT(NL80211_STA_FLAG_AUTHORIZED) |
-                               BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) |
-                               BIT(NL80211_STA_FLAG_WME) |
-                               BIT(NL80211_STA_FLAG_MFP) |
-                               BIT(NL80211_STA_FLAG_AUTHENTICATED) |
-                               BIT(NL80211_STA_FLAG_ASSOCIATED) |
-                               BIT(NL80211_STA_FLAG_TDLS_PEER);
-       if (test_sta_flag(sta, WLAN_STA_AUTHORIZED))
-               sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_AUTHORIZED);
-       if (test_sta_flag(sta, WLAN_STA_SHORT_PREAMBLE))
-               sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_SHORT_PREAMBLE);
-       if (test_sta_flag(sta, WLAN_STA_WME))
-               sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_WME);
-       if (test_sta_flag(sta, WLAN_STA_MFP))
-               sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_MFP);
-       if (test_sta_flag(sta, WLAN_STA_AUTH))
-               sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_AUTHENTICATED);
-       if (test_sta_flag(sta, WLAN_STA_ASSOC))
-               sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_ASSOCIATED);
-       if (test_sta_flag(sta, WLAN_STA_TDLS_PEER))
-               sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER);
-
-       /* check if the driver has a SW RC implementation */
-       if (ref && ref->ops->get_expected_throughput)
-               thr = ref->ops->get_expected_throughput(sta->rate_ctrl_priv);
-       else
-               thr = drv_get_expected_throughput(local, &sta->sta);
-
-       if (thr != 0) {
-               sinfo->filled |= STATION_INFO_EXPECTED_THROUGHPUT;
-               sinfo->expected_throughput = thr;
-       }
-}
-
-static const char ieee80211_gstrings_sta_stats[][ETH_GSTRING_LEN] = {
-       "rx_packets", "rx_bytes", "wep_weak_iv_count",
-       "rx_duplicates", "rx_fragments", "rx_dropped",
-       "tx_packets", "tx_bytes", "tx_fragments",
-       "tx_filtered", "tx_retry_failed", "tx_retries",
-       "beacon_loss", "sta_state", "txrate", "rxrate", "signal",
-       "channel", "noise", "ch_time", "ch_time_busy",
-       "ch_time_ext_busy", "ch_time_rx", "ch_time_tx"
-};
-#define STA_STATS_LEN  ARRAY_SIZE(ieee80211_gstrings_sta_stats)
-
-static int ieee80211_get_et_sset_count(struct wiphy *wiphy,
-                                      struct net_device *dev,
-                                      int sset)
-{
-       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-       int rv = 0;
-
-       if (sset == ETH_SS_STATS)
-               rv += STA_STATS_LEN;
-
-       rv += drv_get_et_sset_count(sdata, sset);
-
-       if (rv == 0)
-               return -EOPNOTSUPP;
-       return rv;
-}
-
-static void ieee80211_get_et_stats(struct wiphy *wiphy,
-                                  struct net_device *dev,
-                                  struct ethtool_stats *stats,
-                                  u64 *data)
-{
-       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-       struct ieee80211_chanctx_conf *chanctx_conf;
-       struct ieee80211_channel *channel;
-       struct sta_info *sta;
-       struct ieee80211_local *local = sdata->local;
-       struct station_info sinfo;
-       struct survey_info survey;
-       int i, q;
-#define STA_STATS_SURVEY_LEN 7
-
-       memset(data, 0, sizeof(u64) * STA_STATS_LEN);
-
-#define ADD_STA_STATS(sta)                             \
-       do {                                            \
-               data[i++] += sta->rx_packets;           \
-               data[i++] += sta->rx_bytes;             \
-               data[i++] += sta->wep_weak_iv_count;    \
-               data[i++] += sta->num_duplicates;       \
-               data[i++] += sta->rx_fragments;         \
-               data[i++] += sta->rx_dropped;           \
-                                                       \
-               data[i++] += sinfo.tx_packets;          \
-               data[i++] += sinfo.tx_bytes;            \
-               data[i++] += sta->tx_fragments;         \
-               data[i++] += sta->tx_filtered_count;    \
-               data[i++] += sta->tx_retry_failed;      \
-               data[i++] += sta->tx_retry_count;       \
-               data[i++] += sta->beacon_loss_count;    \
-       } while (0)
-
-       /* For Managed stations, find the single station based on BSSID
-        * and use that.  For interface types, iterate through all available
-        * stations and add stats for any station that is assigned to this
-        * network device.
-        */
-
-       mutex_lock(&local->sta_mtx);
-
-       if (sdata->vif.type == NL80211_IFTYPE_STATION) {
-               sta = sta_info_get_bss(sdata, sdata->u.mgd.bssid);
-
-               if (!(sta && !WARN_ON(sta->sdata->dev != dev)))
-                       goto do_survey;
-
-               sinfo.filled = 0;
-               sta_set_sinfo(sta, &sinfo);
-
-               i = 0;
-               ADD_STA_STATS(sta);
-
-               data[i++] = sta->sta_state;
-
-
-               if (sinfo.filled & STATION_INFO_TX_BITRATE)
-                       data[i] = 100000 *
-                               cfg80211_calculate_bitrate(&sinfo.txrate);
-               i++;
-               if (sinfo.filled & STATION_INFO_RX_BITRATE)
-                       data[i] = 100000 *
-                               cfg80211_calculate_bitrate(&sinfo.rxrate);
-               i++;
-
-               if (sinfo.filled & STATION_INFO_SIGNAL_AVG)
-                       data[i] = (u8)sinfo.signal_avg;
-               i++;
-       } else {
-               list_for_each_entry(sta, &local->sta_list, list) {
-                       /* Make sure this station belongs to the proper dev */
-                       if (sta->sdata->dev != dev)
-                               continue;
-
-                       sinfo.filled = 0;
-                       sta_set_sinfo(sta, &sinfo);
-                       i = 0;
-                       ADD_STA_STATS(sta);
-               }
-       }
-
-do_survey:
-       i = STA_STATS_LEN - STA_STATS_SURVEY_LEN;
-       /* Get survey stats for current channel */
-       survey.filled = 0;
-
-       rcu_read_lock();
-       chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
-       if (chanctx_conf)
-               channel = chanctx_conf->def.chan;
-       else
-               channel = NULL;
-       rcu_read_unlock();
-
-       if (channel) {
-               q = 0;
-               do {
-                       survey.filled = 0;
-                       if (drv_get_survey(local, q, &survey) != 0) {
-                               survey.filled = 0;
-                               break;
-                       }
-                       q++;
-               } while (channel != survey.channel);
-       }
-
-       if (survey.filled)
-               data[i++] = survey.channel->center_freq;
-       else
-               data[i++] = 0;
-       if (survey.filled & SURVEY_INFO_NOISE_DBM)
-               data[i++] = (u8)survey.noise;
-       else
-               data[i++] = -1LL;
-       if (survey.filled & SURVEY_INFO_CHANNEL_TIME)
-               data[i++] = survey.channel_time;
-       else
-               data[i++] = -1LL;
-       if (survey.filled & SURVEY_INFO_CHANNEL_TIME_BUSY)
-               data[i++] = survey.channel_time_busy;
-       else
-               data[i++] = -1LL;
-       if (survey.filled & SURVEY_INFO_CHANNEL_TIME_EXT_BUSY)
-               data[i++] = survey.channel_time_ext_busy;
-       else
-               data[i++] = -1LL;
-       if (survey.filled & SURVEY_INFO_CHANNEL_TIME_RX)
-               data[i++] = survey.channel_time_rx;
-       else
-               data[i++] = -1LL;
-       if (survey.filled & SURVEY_INFO_CHANNEL_TIME_TX)
-               data[i++] = survey.channel_time_tx;
-       else
-               data[i++] = -1LL;
-
-       mutex_unlock(&local->sta_mtx);
-
-       if (WARN_ON(i != STA_STATS_LEN))
-               return;
-
-       drv_get_et_stats(sdata, stats, &(data[STA_STATS_LEN]));
-}
-
-static void ieee80211_get_et_strings(struct wiphy *wiphy,
-                                    struct net_device *dev,
-                                    u32 sset, u8 *data)
-{
-       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-       int sz_sta_stats = 0;
-
-       if (sset == ETH_SS_STATS) {
-               sz_sta_stats = sizeof(ieee80211_gstrings_sta_stats);
-               memcpy(data, ieee80211_gstrings_sta_stats, sz_sta_stats);
-       }
-       drv_get_et_strings(sdata, sset, &(data[sz_sta_stats]));
-}
-
 static int ieee80211_dump_station(struct wiphy *wiphy, struct net_device *dev,
                                  int idx, u8 *mac, struct station_info *sinfo)
 {
@@ -875,7 +554,8 @@ static int ieee80211_set_monitor_channel(struct wiphy *wiphy,
 }
 
 static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata,
-                                   const u8 *resp, size_t resp_len)
+                                   const u8 *resp, size_t resp_len,
+                                   const struct ieee80211_csa_settings *csa)
 {
        struct probe_resp *new, *old;
 
@@ -891,6 +571,11 @@ static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata,
        new->len = resp_len;
        memcpy(new->data, resp, resp_len);
 
+       if (csa)
+               memcpy(new->csa_counter_offsets, csa->counter_offsets_presp,
+                      csa->n_counter_offsets_presp *
+                      sizeof(new->csa_counter_offsets[0]));
+
        rcu_assign_pointer(sdata->u.ap.probe_resp, new);
        if (old)
                kfree_rcu(old, rcu_head);
@@ -899,7 +584,8 @@ static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata,
 }
 
 static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
-                                  struct cfg80211_beacon_data *params)
+                                  struct cfg80211_beacon_data *params,
+                                  const struct ieee80211_csa_settings *csa)
 {
        struct beacon_data *new, *old;
        int new_head_len, new_tail_len;
@@ -943,6 +629,13 @@ static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
        new->head_len = new_head_len;
        new->tail_len = new_tail_len;
 
+       if (csa) {
+               new->csa_current_counter = csa->count;
+               memcpy(new->csa_counter_offsets, csa->counter_offsets_beacon,
+                      csa->n_counter_offsets_beacon *
+                      sizeof(new->csa_counter_offsets[0]));
+       }
+
        /* copy in head */
        if (params->head)
                memcpy(new->head, params->head, new_head_len);
@@ -957,7 +650,7 @@ static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
                        memcpy(new->tail, old->tail, new_tail_len);
 
        err = ieee80211_set_probe_resp(sdata, params->probe_resp,
-                                      params->probe_resp_len);
+                                      params->probe_resp_len, csa);
        if (err < 0)
                return err;
        if (err == 0)
@@ -1042,7 +735,7 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
                sdata->vif.bss_conf.p2p_noa_attr.oppps_ctwindow |=
                                        IEEE80211_P2P_OPPPS_ENABLE_BIT;
 
-       err = ieee80211_assign_beacon(sdata, &params->beacon);
+       err = ieee80211_assign_beacon(sdata, &params->beacon, NULL);
        if (err < 0) {
                ieee80211_vif_release_channel(sdata);
                return err;
@@ -1090,38 +783,13 @@ static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev,
        if (!old)
                return -ENOENT;
 
-       err = ieee80211_assign_beacon(sdata, params);
+       err = ieee80211_assign_beacon(sdata, params, NULL);
        if (err < 0)
                return err;
        ieee80211_bss_info_change_notify(sdata, err);
        return 0;
 }
 
-bool ieee80211_csa_needs_block_tx(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 (!ieee80211_sdata_running(sdata))
-                       continue;
-
-               if (!sdata->vif.csa_active)
-                       continue;
-
-               if (!sdata->csa_block_tx)
-                       continue;
-
-               rcu_read_unlock();
-               return true;
-       }
-       rcu_read_unlock();
-
-       return false;
-}
-
 static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
@@ -1141,10 +809,12 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
        /* abort any running channel switch */
        mutex_lock(&local->mtx);
        sdata->vif.csa_active = false;
-       if (!ieee80211_csa_needs_block_tx(local))
-               ieee80211_wake_queues_by_reason(&local->hw,
-                                       IEEE80211_MAX_QUEUE_MAP,
-                                       IEEE80211_QUEUE_STOP_REASON_CSA);
+       if (sdata->csa_block_tx) {
+               ieee80211_wake_vif_queues(local, sdata,
+                                         IEEE80211_QUEUE_STOP_REASON_CSA);
+               sdata->csa_block_tx = false;
+       }
+
        mutex_unlock(&local->mtx);
 
        kfree(sdata->u.ap.next_beacon);
@@ -1327,9 +997,12 @@ static int sta_apply_parameters(struct ieee80211_local *local,
                }
        }
 
-       ret = sta_apply_auth_flags(local, sta, mask, set);
-       if (ret)
-               return ret;
+       /* auth flags will be set later for TDLS stations */
+       if (!test_sta_flag(sta, WLAN_STA_TDLS_PEER)) {
+               ret = sta_apply_auth_flags(local, sta, mask, set);
+               if (ret)
+                       return ret;
+       }
 
        if (mask & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE)) {
                if (set & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE))
@@ -1466,6 +1139,13 @@ static int sta_apply_parameters(struct ieee80211_local *local,
 #endif
        }
 
+       /* set the STA state after all sta info from usermode has been set */
+       if (test_sta_flag(sta, WLAN_STA_TDLS_PEER)) {
+               ret = sta_apply_auth_flags(local, sta, mask, set);
+               if (ret)
+                       return ret;
+       }
+
        return 0;
 }
 
@@ -3073,7 +2753,8 @@ static int ieee80211_set_after_csa_beacon(struct ieee80211_sub_if_data *sdata,
 
        switch (sdata->vif.type) {
        case NL80211_IFTYPE_AP:
-               err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon);
+               err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon,
+                                             NULL);
                kfree(sdata->u.ap.next_beacon);
                sdata->u.ap.next_beacon = NULL;
 
@@ -3111,17 +2792,35 @@ static int __ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata)
 
        sdata_assert_lock(sdata);
        lockdep_assert_held(&local->mtx);
+       lockdep_assert_held(&local->chanctx_mtx);
 
-       sdata->radar_required = sdata->csa_radar_required;
-       err = ieee80211_vif_change_channel(sdata, &changed);
-       if (err < 0)
-               return err;
+       /*
+        * using reservation isn't immediate as it may be deferred until later
+        * with multi-vif. once reservation is complete it will re-schedule the
+        * work with no reserved_chanctx so verify chandef to check if it
+        * completed successfully
+        */
 
-       if (!local->use_chanctx) {
-               local->_oper_chandef = sdata->csa_chandef;
-               ieee80211_hw_config(local, 0);
+       if (sdata->reserved_chanctx) {
+               /*
+                * with multi-vif csa driver may call ieee80211_csa_finish()
+                * many times while waiting for other interfaces to use their
+                * reservations
+                */
+               if (sdata->reserved_ready)
+                       return 0;
+
+               err = ieee80211_vif_use_reserved_context(sdata);
+               if (err)
+                       return err;
+
+               return 0;
        }
 
+       if (!cfg80211_chandef_identical(&sdata->vif.bss_conf.chandef,
+                                       &sdata->csa_chandef))
+               return -EINVAL;
+
        sdata->vif.csa_active = false;
 
        err = ieee80211_set_after_csa_beacon(sdata, &changed);
@@ -3131,10 +2830,11 @@ static int __ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata)
        ieee80211_bss_info_change_notify(sdata, changed);
        cfg80211_ch_switch_notify(sdata->dev, &sdata->csa_chandef);
 
-       if (!ieee80211_csa_needs_block_tx(local))
-               ieee80211_wake_queues_by_reason(&local->hw,
-                                       IEEE80211_MAX_QUEUE_MAP,
-                                       IEEE80211_QUEUE_STOP_REASON_CSA);
+       if (sdata->csa_block_tx) {
+               ieee80211_wake_vif_queues(local, sdata,
+                                         IEEE80211_QUEUE_STOP_REASON_CSA);
+               sdata->csa_block_tx = false;
+       }
 
        return 0;
 }
@@ -3157,6 +2857,7 @@ void ieee80211_csa_finalize_work(struct work_struct *work)
 
        sdata_lock(sdata);
        mutex_lock(&local->mtx);
+       mutex_lock(&local->chanctx_mtx);
 
        /* AP might have been stopped while waiting for the lock. */
        if (!sdata->vif.csa_active)
@@ -3168,6 +2869,7 @@ void ieee80211_csa_finalize_work(struct work_struct *work)
        ieee80211_csa_finalize(sdata);
 
 unlock:
+       mutex_unlock(&local->chanctx_mtx);
        mutex_unlock(&local->mtx);
        sdata_unlock(sdata);
 }
@@ -3176,6 +2878,7 @@ static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata,
                                    struct cfg80211_csa_settings *params,
                                    u32 *changed)
 {
+       struct ieee80211_csa_settings csa = {};
        int err;
 
        switch (sdata->vif.type) {
@@ -3210,20 +2913,13 @@ static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata,
                     IEEE80211_MAX_CSA_COUNTERS_NUM))
                        return -EINVAL;
 
-               /* make sure we don't have garbage in other counters */
-               memset(sdata->csa_counter_offset_beacon, 0,
-                      sizeof(sdata->csa_counter_offset_beacon));
-               memset(sdata->csa_counter_offset_presp, 0,
-                      sizeof(sdata->csa_counter_offset_presp));
-
-               memcpy(sdata->csa_counter_offset_beacon,
-                      params->counter_offsets_beacon,
-                      params->n_counter_offsets_beacon * sizeof(u16));
-               memcpy(sdata->csa_counter_offset_presp,
-                      params->counter_offsets_presp,
-                      params->n_counter_offsets_presp * sizeof(u16));
+               csa.counter_offsets_beacon = params->counter_offsets_beacon;
+               csa.counter_offsets_presp = params->counter_offsets_presp;
+               csa.n_counter_offsets_beacon = params->n_counter_offsets_beacon;
+               csa.n_counter_offsets_presp = params->n_counter_offsets_presp;
+               csa.count = params->count;
 
-               err = ieee80211_assign_beacon(sdata, &params->beacon_csa);
+               err = ieee80211_assign_beacon(sdata, &params->beacon_csa, &csa);
                if (err < 0) {
                        kfree(sdata->u.ap.next_beacon);
                        return err;
@@ -3319,7 +3015,7 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_chanctx_conf *conf;
        struct ieee80211_chanctx *chanctx;
-       int err, num_chanctx, changed = 0;
+       int err, changed = 0;
 
        sdata_assert_lock(sdata);
        lockdep_assert_held(&local->mtx);
@@ -3334,46 +3030,50 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
                                       &sdata->vif.bss_conf.chandef))
                return -EINVAL;
 
+       /* don't allow another channel switch if one is already active. */
+       if (sdata->vif.csa_active)
+               return -EBUSY;
+
        mutex_lock(&local->chanctx_mtx);
        conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
                                         lockdep_is_held(&local->chanctx_mtx));
        if (!conf) {
-               mutex_unlock(&local->chanctx_mtx);
-               return -EBUSY;
+               err = -EBUSY;
+               goto out;
        }
 
-       /* don't handle for multi-VIF cases */
        chanctx = container_of(conf, struct ieee80211_chanctx, conf);
-       if (ieee80211_chanctx_refcount(local, chanctx) > 1) {
-               mutex_unlock(&local->chanctx_mtx);
-               return -EBUSY;
+       if (!chanctx) {
+               err = -EBUSY;
+               goto out;
        }
-       num_chanctx = 0;
-       list_for_each_entry_rcu(chanctx, &local->chanctx_list, list)
-               num_chanctx++;
-       mutex_unlock(&local->chanctx_mtx);
 
-       if (num_chanctx > 1)
-               return -EBUSY;
+       err = ieee80211_vif_reserve_chanctx(sdata, &params->chandef,
+                                           chanctx->mode,
+                                           params->radar_required);
+       if (err)
+               goto out;
 
-       /* don't allow another channel switch if one is already active. */
-       if (sdata->vif.csa_active)
-               return -EBUSY;
+       /* if reservation is invalid then this will fail */
+       err = ieee80211_check_combinations(sdata, NULL, chanctx->mode, 0);
+       if (err) {
+               ieee80211_vif_unreserve_chanctx(sdata);
+               goto out;
+       }
 
        err = ieee80211_set_csa_beacon(sdata, params, &changed);
-       if (err)
-               return err;
+       if (err) {
+               ieee80211_vif_unreserve_chanctx(sdata);
+               goto out;
+       }
 
-       sdata->csa_radar_required = params->radar_required;
        sdata->csa_chandef = params->chandef;
        sdata->csa_block_tx = params->block_tx;
-       sdata->csa_current_counter = params->count;
        sdata->vif.csa_active = true;
 
        if (sdata->csa_block_tx)
-               ieee80211_stop_queues_by_reason(&local->hw,
-                                       IEEE80211_MAX_QUEUE_MAP,
-                                       IEEE80211_QUEUE_STOP_REASON_CSA);
+               ieee80211_stop_vif_queues(local, sdata,
+                                         IEEE80211_QUEUE_STOP_REASON_CSA);
 
        if (changed) {
                ieee80211_bss_info_change_notify(sdata, changed);
@@ -3383,7 +3083,9 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
                ieee80211_csa_finalize(sdata);
        }
 
-       return 0;
+out:
+       mutex_unlock(&local->chanctx_mtx);
+       return err;
 }
 
 int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
@@ -3515,10 +3217,23 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
             sdata->vif.type == NL80211_IFTYPE_ADHOC) &&
            params->n_csa_offsets) {
                int i;
-               u8 c = sdata->csa_current_counter;
+               struct beacon_data *beacon = NULL;
 
-               for (i = 0; i < params->n_csa_offsets; i++)
-                       data[params->csa_offsets[i]] = c;
+               rcu_read_lock();
+
+               if (sdata->vif.type == NL80211_IFTYPE_AP)
+                       beacon = rcu_dereference(sdata->u.ap.beacon);
+               else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
+                       beacon = rcu_dereference(sdata->u.ibss.presp);
+               else if (ieee80211_vif_is_mesh(&sdata->vif))
+                       beacon = rcu_dereference(sdata->u.mesh.beacon);
+
+               if (beacon)
+                       for (i = 0; i < params->n_csa_offsets; i++)
+                               data[params->csa_offsets[i]] =
+                                       beacon->csa_current_counter;
+
+               rcu_read_unlock();
        }
 
        IEEE80211_SKB_CB(skb)->flags = flags;
@@ -3598,21 +3313,6 @@ static int ieee80211_get_antenna(struct wiphy *wiphy, u32 *tx_ant, u32 *rx_ant)
        return drv_get_antenna(local, tx_ant, rx_ant);
 }
 
-static int ieee80211_set_ringparam(struct wiphy *wiphy, u32 tx, u32 rx)
-{
-       struct ieee80211_local *local = wiphy_priv(wiphy);
-
-       return drv_set_ringparam(local, tx, rx);
-}
-
-static void ieee80211_get_ringparam(struct wiphy *wiphy,
-                                   u32 *tx, u32 *tx_max, u32 *rx, u32 *rx_max)
-{
-       struct ieee80211_local *local = wiphy_priv(wiphy);
-
-       drv_get_ringparam(local, tx, tx_max, rx, rx_max);
-}
-
 static int ieee80211_set_rekey_data(struct wiphy *wiphy,
                                    struct net_device *dev,
                                    struct cfg80211_gtk_rekey_data *data)
@@ -3844,8 +3544,6 @@ const struct cfg80211_ops mac80211_config_ops = {
        .mgmt_frame_register = ieee80211_mgmt_frame_register,
        .set_antenna = ieee80211_set_antenna,
        .get_antenna = ieee80211_get_antenna,
-       .set_ringparam = ieee80211_set_ringparam,
-       .get_ringparam = ieee80211_get_ringparam,
        .set_rekey_data = ieee80211_set_rekey_data,
        .tdls_oper = ieee80211_tdls_oper,
        .tdls_mgmt = ieee80211_tdls_mgmt,
@@ -3854,9 +3552,6 @@ const struct cfg80211_ops mac80211_config_ops = {
 #ifdef CONFIG_PM
        .set_wakeup = ieee80211_set_wakeup,
 #endif
-       .get_et_sset_count = ieee80211_get_et_sset_count,
-       .get_et_stats = ieee80211_get_et_stats,
-       .get_et_strings = ieee80211_get_et_strings,
        .get_channel = ieee80211_cfg_get_channel,
        .start_radar_detection = ieee80211_start_radar_detection,
        .channel_switch = ieee80211_channel_switch,
index a310e33..c3fd4d2 100644 (file)
@@ -63,6 +63,20 @@ static bool ieee80211_can_create_new_chanctx(struct ieee80211_local *local)
        return ieee80211_num_chanctx(local) < ieee80211_max_num_channels(local);
 }
 
+static struct ieee80211_chanctx *
+ieee80211_vif_get_chanctx(struct ieee80211_sub_if_data *sdata)
+{
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_chanctx_conf *conf;
+
+       conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
+                                        lockdep_is_held(&local->chanctx_mtx));
+       if (!conf)
+               return NULL;
+
+       return container_of(conf, struct ieee80211_chanctx, conf);
+}
+
 static const struct cfg80211_chan_def *
 ieee80211_chanctx_reserved_chandef(struct ieee80211_local *local,
                                   struct ieee80211_chanctx *ctx,
@@ -160,6 +174,9 @@ ieee80211_find_reservation_chanctx(struct ieee80211_local *local,
                return NULL;
 
        list_for_each_entry(ctx, &local->chanctx_list, list) {
+               if (ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED)
+                       continue;
+
                if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE)
                        continue;
 
@@ -347,6 +364,9 @@ ieee80211_find_chanctx(struct ieee80211_local *local,
        list_for_each_entry(ctx, &local->chanctx_list, list) {
                const struct cfg80211_chan_def *compat;
 
+               if (ctx->replace_state != IEEE80211_CHANCTX_REPLACE_NONE)
+                       continue;
+
                if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE)
                        continue;
 
@@ -622,6 +642,7 @@ static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_chanctx_conf *conf;
        struct ieee80211_chanctx *ctx;
+       bool use_reserved_switch = false;
 
        lockdep_assert_held(&local->chanctx_mtx);
 
@@ -632,12 +653,23 @@ static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
 
        ctx = container_of(conf, struct ieee80211_chanctx, conf);
 
-       if (sdata->reserved_chanctx)
+       if (sdata->reserved_chanctx) {
+               if (sdata->reserved_chanctx->replace_state ==
+                   IEEE80211_CHANCTX_REPLACES_OTHER &&
+                   ieee80211_chanctx_num_reserved(local,
+                                                  sdata->reserved_chanctx) > 1)
+                       use_reserved_switch = true;
+
                ieee80211_vif_unreserve_chanctx(sdata);
+       }
 
        ieee80211_assign_vif_chanctx(sdata, NULL);
        if (ieee80211_chanctx_refcount(local, ctx) == 0)
                ieee80211_free_chanctx(local, ctx);
+
+       /* Unreserving may ready an in-place reservation. */
+       if (use_reserved_switch)
+               ieee80211_vif_use_reserved_switch(local);
 }
 
 void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
@@ -787,70 +819,6 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
        return ret;
 }
 
-static int __ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata,
-                                         struct ieee80211_chanctx *ctx,
-                                         u32 *changed)
-{
-       struct ieee80211_local *local = sdata->local;
-       const struct cfg80211_chan_def *chandef = &sdata->csa_chandef;
-       u32 chanctx_changed = 0;
-
-       if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef,
-                                    IEEE80211_CHAN_DISABLED))
-               return -EINVAL;
-
-       if (ieee80211_chanctx_refcount(local, ctx) != 1)
-               return -EINVAL;
-
-       if (sdata->vif.bss_conf.chandef.width != chandef->width) {
-               chanctx_changed = IEEE80211_CHANCTX_CHANGE_WIDTH;
-               *changed |= BSS_CHANGED_BANDWIDTH;
-       }
-
-       sdata->vif.bss_conf.chandef = *chandef;
-       ctx->conf.def = *chandef;
-
-       chanctx_changed |= IEEE80211_CHANCTX_CHANGE_CHANNEL;
-       drv_change_chanctx(local, ctx, chanctx_changed);
-
-       ieee80211_recalc_chanctx_chantype(local, ctx);
-       ieee80211_recalc_smps_chanctx(local, ctx);
-       ieee80211_recalc_radar_chanctx(local, ctx);
-       ieee80211_recalc_chanctx_min_def(local, ctx);
-
-       return 0;
-}
-
-int ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata,
-                                u32 *changed)
-{
-       struct ieee80211_local *local = sdata->local;
-       struct ieee80211_chanctx_conf *conf;
-       struct ieee80211_chanctx *ctx;
-       int ret;
-
-       lockdep_assert_held(&local->mtx);
-
-       /* should never be called if not performing a channel switch. */
-       if (WARN_ON(!sdata->vif.csa_active))
-               return -EINVAL;
-
-       mutex_lock(&local->chanctx_mtx);
-       conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
-                                        lockdep_is_held(&local->chanctx_mtx));
-       if (!conf) {
-               ret = -EINVAL;
-               goto out;
-       }
-
-       ctx = container_of(conf, struct ieee80211_chanctx, conf);
-
-       ret = __ieee80211_vif_change_channel(sdata, ctx, changed);
- out:
-       mutex_unlock(&local->chanctx_mtx);
-       return ret;
-}
-
 static void
 __ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata,
                                      bool clear)
@@ -905,8 +873,25 @@ int ieee80211_vif_unreserve_chanctx(struct ieee80211_sub_if_data *sdata)
        list_del(&sdata->reserved_chanctx_list);
        sdata->reserved_chanctx = NULL;
 
-       if (ieee80211_chanctx_refcount(sdata->local, ctx) == 0)
-               ieee80211_free_chanctx(sdata->local, ctx);
+       if (ieee80211_chanctx_refcount(sdata->local, ctx) == 0) {
+               if (ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER) {
+                       if (WARN_ON(!ctx->replace_ctx))
+                               return -EINVAL;
+
+                       WARN_ON(ctx->replace_ctx->replace_state !=
+                               IEEE80211_CHANCTX_WILL_BE_REPLACED);
+                       WARN_ON(ctx->replace_ctx->replace_ctx != ctx);
+
+                       ctx->replace_ctx->replace_ctx = NULL;
+                       ctx->replace_ctx->replace_state =
+                                       IEEE80211_CHANCTX_REPLACE_NONE;
+
+                       list_del_rcu(&ctx->list);
+                       kfree_rcu(ctx, rcu_head);
+               } else {
+                       ieee80211_free_chanctx(sdata->local, ctx);
+               }
+       }
 
        return 0;
 }
@@ -917,40 +902,84 @@ int ieee80211_vif_reserve_chanctx(struct ieee80211_sub_if_data *sdata,
                                  bool radar_required)
 {
        struct ieee80211_local *local = sdata->local;
-       struct ieee80211_chanctx_conf *conf;
-       struct ieee80211_chanctx *new_ctx, *curr_ctx;
-       int ret = 0;
+       struct ieee80211_chanctx *new_ctx, *curr_ctx, *ctx;
 
-       mutex_lock(&local->chanctx_mtx);
-
-       conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
-                                        lockdep_is_held(&local->chanctx_mtx));
-       if (!conf) {
-               ret = -EINVAL;
-               goto out;
-       }
+       lockdep_assert_held(&local->chanctx_mtx);
 
-       curr_ctx = container_of(conf, struct ieee80211_chanctx, conf);
+       curr_ctx = ieee80211_vif_get_chanctx(sdata);
+       if (curr_ctx && local->use_chanctx && !local->ops->switch_vif_chanctx)
+               return -ENOTSUPP;
 
        new_ctx = ieee80211_find_reservation_chanctx(local, chandef, mode);
        if (!new_ctx) {
-               if (ieee80211_chanctx_refcount(local, curr_ctx) == 1 &&
-                   (local->hw.flags & IEEE80211_HW_CHANGE_RUNNING_CHANCTX)) {
-                       /* if we're the only users of the chanctx and
-                        * the driver supports changing a running
-                        * context, reserve our current context
-                        */
-                       new_ctx = curr_ctx;
-               } else if (ieee80211_can_create_new_chanctx(local)) {
-                       /* create a new context and reserve it */
+               if (ieee80211_can_create_new_chanctx(local)) {
                        new_ctx = ieee80211_new_chanctx(local, chandef, mode);
-                       if (IS_ERR(new_ctx)) {
-                               ret = PTR_ERR(new_ctx);
-                               goto out;
-                       }
+                       if (IS_ERR(new_ctx))
+                               return PTR_ERR(new_ctx);
                } else {
-                       ret = -EBUSY;
-                       goto out;
+                       if (!curr_ctx ||
+                           (curr_ctx->replace_state ==
+                            IEEE80211_CHANCTX_WILL_BE_REPLACED) ||
+                           !list_empty(&curr_ctx->reserved_vifs)) {
+                               /*
+                                * Another vif already requested this context
+                                * for a reservation. Find another one hoping
+                                * all vifs assigned to it will also switch
+                                * soon enough.
+                                *
+                                * TODO: This needs a little more work as some
+                                * cases (more than 2 chanctx capable devices)
+                                * may fail which could otherwise succeed
+                                * provided some channel context juggling was
+                                * performed.
+                                *
+                                * Consider ctx1..3, vif1..6, each ctx has 2
+                                * vifs. vif1 and vif2 from ctx1 request new
+                                * different chandefs starting 2 in-place
+                                * reserations with ctx4 and ctx5 replacing
+                                * ctx1 and ctx2 respectively. Next vif5 and
+                                * vif6 from ctx3 reserve ctx4. If vif3 and
+                                * vif4 remain on ctx2 as they are then this
+                                * fails unless `replace_ctx` from ctx5 is
+                                * replaced with ctx3.
+                                */
+                               list_for_each_entry(ctx, &local->chanctx_list,
+                                                   list) {
+                                       if (ctx->replace_state !=
+                                           IEEE80211_CHANCTX_REPLACE_NONE)
+                                               continue;
+
+                                       if (!list_empty(&ctx->reserved_vifs))
+                                               continue;
+
+                                       curr_ctx = ctx;
+                                       break;
+                               }
+                       }
+
+                       /*
+                        * If that's true then all available contexts already
+                        * have reservations and cannot be used.
+                        */
+                       if (!curr_ctx ||
+                           (curr_ctx->replace_state ==
+                            IEEE80211_CHANCTX_WILL_BE_REPLACED) ||
+                           !list_empty(&curr_ctx->reserved_vifs))
+                               return -EBUSY;
+
+                       new_ctx = ieee80211_alloc_chanctx(local, chandef, mode);
+                       if (!new_ctx)
+                               return -ENOMEM;
+
+                       new_ctx->replace_ctx = curr_ctx;
+                       new_ctx->replace_state =
+                                       IEEE80211_CHANCTX_REPLACES_OTHER;
+
+                       curr_ctx->replace_ctx = new_ctx;
+                       curr_ctx->replace_state =
+                                       IEEE80211_CHANCTX_WILL_BE_REPLACED;
+
+                       list_add_rcu(&new_ctx->list, &local->chanctx_list);
                }
        }
 
@@ -958,82 +987,601 @@ int ieee80211_vif_reserve_chanctx(struct ieee80211_sub_if_data *sdata,
        sdata->reserved_chanctx = new_ctx;
        sdata->reserved_chandef = *chandef;
        sdata->reserved_radar_required = radar_required;
-out:
-       mutex_unlock(&local->chanctx_mtx);
-       return ret;
+       sdata->reserved_ready = false;
+
+       return 0;
 }
 
-int ieee80211_vif_use_reserved_context(struct ieee80211_sub_if_data *sdata,
-                                      u32 *changed)
+static void
+ieee80211_vif_chanctx_reservation_complete(struct ieee80211_sub_if_data *sdata)
 {
-       struct ieee80211_local *local = sdata->local;
-       struct ieee80211_chanctx *ctx;
-       struct ieee80211_chanctx *old_ctx;
-       struct ieee80211_chanctx_conf *conf;
-       int ret;
-       u32 tmp_changed = *changed;
+       switch (sdata->vif.type) {
+       case NL80211_IFTYPE_ADHOC:
+       case NL80211_IFTYPE_AP:
+       case NL80211_IFTYPE_MESH_POINT:
+               ieee80211_queue_work(&sdata->local->hw,
+                                    &sdata->csa_finalize_work);
+               break;
+       case NL80211_IFTYPE_STATION:
+               ieee80211_queue_work(&sdata->local->hw,
+                                    &sdata->u.mgd.chswitch_work);
+               break;
+       case NL80211_IFTYPE_UNSPECIFIED:
+       case NL80211_IFTYPE_AP_VLAN:
+       case NL80211_IFTYPE_WDS:
+       case NL80211_IFTYPE_MONITOR:
+       case NL80211_IFTYPE_P2P_CLIENT:
+       case NL80211_IFTYPE_P2P_GO:
+       case NL80211_IFTYPE_P2P_DEVICE:
+       case NUM_NL80211_IFTYPES:
+               WARN_ON(1);
+               break;
+       }
+}
 
-       /* TODO: need to recheck if the chandef is usable etc.? */
+static int
+ieee80211_vif_use_reserved_reassign(struct ieee80211_sub_if_data *sdata)
+{
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_vif_chanctx_switch vif_chsw[1] = {};
+       struct ieee80211_chanctx *old_ctx, *new_ctx;
+       const struct cfg80211_chan_def *chandef;
+       u32 changed = 0;
+       int err;
 
        lockdep_assert_held(&local->mtx);
+       lockdep_assert_held(&local->chanctx_mtx);
 
-       mutex_lock(&local->chanctx_mtx);
+       new_ctx = sdata->reserved_chanctx;
+       old_ctx = ieee80211_vif_get_chanctx(sdata);
 
-       ctx = sdata->reserved_chanctx;
-       if (WARN_ON(!ctx)) {
-               ret = -EINVAL;
-               goto out;
-       }
+       if (WARN_ON(!sdata->reserved_ready))
+               return -EBUSY;
+
+       if (WARN_ON(!new_ctx))
+               return -EINVAL;
+
+       if (WARN_ON(!old_ctx))
+               return -EINVAL;
+
+       if (WARN_ON(new_ctx->replace_state ==
+                   IEEE80211_CHANCTX_REPLACES_OTHER))
+               return -EINVAL;
+
+       chandef = ieee80211_chanctx_non_reserved_chandef(local, new_ctx,
+                               &sdata->reserved_chandef);
+       if (WARN_ON(!chandef))
+               return -EINVAL;
+
+       vif_chsw[0].vif = &sdata->vif;
+       vif_chsw[0].old_ctx = &old_ctx->conf;
+       vif_chsw[0].new_ctx = &new_ctx->conf;
+
+       list_del(&sdata->reserved_chanctx_list);
+       sdata->reserved_chanctx = NULL;
+
+       err = drv_switch_vif_chanctx(local, vif_chsw, 1,
+                                    CHANCTX_SWMODE_REASSIGN_VIF);
+       if (err) {
+               if (ieee80211_chanctx_refcount(local, new_ctx) == 0)
+                       ieee80211_free_chanctx(local, new_ctx);
 
-       conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
-                                        lockdep_is_held(&local->chanctx_mtx));
-       if (!conf) {
-               ret = -EINVAL;
                goto out;
        }
 
-       old_ctx = container_of(conf, struct ieee80211_chanctx, conf);
+       list_move(&sdata->assigned_chanctx_list, &new_ctx->assigned_vifs);
+       rcu_assign_pointer(sdata->vif.chanctx_conf, &new_ctx->conf);
+
+       if (sdata->vif.type == NL80211_IFTYPE_AP)
+               __ieee80211_vif_copy_chanctx_to_vlans(sdata, false);
+
+       if (ieee80211_chanctx_refcount(local, old_ctx) == 0)
+               ieee80211_free_chanctx(local, old_ctx);
 
        if (sdata->vif.bss_conf.chandef.width != sdata->reserved_chandef.width)
-               tmp_changed |= BSS_CHANGED_BANDWIDTH;
+               changed = BSS_CHANGED_BANDWIDTH;
 
        sdata->vif.bss_conf.chandef = sdata->reserved_chandef;
 
-       /* unref our reservation */
-       sdata->reserved_chanctx = NULL;
-       sdata->radar_required = sdata->reserved_radar_required;
+       if (changed)
+               ieee80211_bss_info_change_notify(sdata, changed);
+
+out:
+       ieee80211_vif_chanctx_reservation_complete(sdata);
+       return err;
+}
+
+static int
+ieee80211_vif_use_reserved_assign(struct ieee80211_sub_if_data *sdata)
+{
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_chanctx *old_ctx, *new_ctx;
+       const struct cfg80211_chan_def *chandef;
+       int err;
+
+       old_ctx = ieee80211_vif_get_chanctx(sdata);
+       new_ctx = sdata->reserved_chanctx;
+
+       if (WARN_ON(!sdata->reserved_ready))
+               return -EINVAL;
+
+       if (WARN_ON(old_ctx))
+               return -EINVAL;
+
+       if (WARN_ON(!new_ctx))
+               return -EINVAL;
+
+       if (WARN_ON(new_ctx->replace_state ==
+                   IEEE80211_CHANCTX_REPLACES_OTHER))
+               return -EINVAL;
+
+       chandef = ieee80211_chanctx_non_reserved_chandef(local, new_ctx,
+                               &sdata->reserved_chandef);
+       if (WARN_ON(!chandef))
+               return -EINVAL;
+
        list_del(&sdata->reserved_chanctx_list);
+       sdata->reserved_chanctx = NULL;
 
-       if (old_ctx == ctx) {
-               /* This is our own context, just change it */
-               ret = __ieee80211_vif_change_channel(sdata, old_ctx,
-                                                    &tmp_changed);
-               if (ret)
-                       goto out;
-       } else {
-               ret = ieee80211_assign_vif_chanctx(sdata, ctx);
-               if (ieee80211_chanctx_refcount(local, old_ctx) == 0)
-                       ieee80211_free_chanctx(local, old_ctx);
-               if (ret) {
-                       /* if assign fails refcount stays the same */
-                       if (ieee80211_chanctx_refcount(local, ctx) == 0)
-                               ieee80211_free_chanctx(local, ctx);
+       err = ieee80211_assign_vif_chanctx(sdata, new_ctx);
+       if (err) {
+               if (ieee80211_chanctx_refcount(local, new_ctx) == 0)
+                       ieee80211_free_chanctx(local, new_ctx);
+
+               goto out;
+       }
+
+out:
+       ieee80211_vif_chanctx_reservation_complete(sdata);
+       return err;
+}
+
+static bool
+ieee80211_vif_has_in_place_reservation(struct ieee80211_sub_if_data *sdata)
+{
+       struct ieee80211_chanctx *old_ctx, *new_ctx;
+
+       lockdep_assert_held(&sdata->local->chanctx_mtx);
+
+       new_ctx = sdata->reserved_chanctx;
+       old_ctx = ieee80211_vif_get_chanctx(sdata);
+
+       if (!old_ctx)
+               return false;
+
+       if (WARN_ON(!new_ctx))
+               return false;
+
+       if (old_ctx->replace_state != IEEE80211_CHANCTX_WILL_BE_REPLACED)
+               return false;
+
+       if (new_ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
+               return false;
+
+       return true;
+}
+
+static int ieee80211_chsw_switch_hwconf(struct ieee80211_local *local,
+                                       struct ieee80211_chanctx *new_ctx)
+{
+       const struct cfg80211_chan_def *chandef;
+
+       lockdep_assert_held(&local->mtx);
+       lockdep_assert_held(&local->chanctx_mtx);
+
+       chandef = ieee80211_chanctx_reserved_chandef(local, new_ctx, NULL);
+       if (WARN_ON(!chandef))
+               return -EINVAL;
+
+       local->hw.conf.radar_enabled = new_ctx->conf.radar_enabled;
+       local->_oper_chandef = *chandef;
+       ieee80211_hw_config(local, 0);
+
+       return 0;
+}
+
+static int ieee80211_chsw_switch_vifs(struct ieee80211_local *local,
+                                     int n_vifs)
+{
+       struct ieee80211_vif_chanctx_switch *vif_chsw;
+       struct ieee80211_sub_if_data *sdata;
+       struct ieee80211_chanctx *ctx, *old_ctx;
+       int i, err;
+
+       lockdep_assert_held(&local->mtx);
+       lockdep_assert_held(&local->chanctx_mtx);
+
+       vif_chsw = kzalloc(sizeof(vif_chsw[0]) * n_vifs, GFP_KERNEL);
+       if (!vif_chsw)
+               return -ENOMEM;
+
+       i = 0;
+       list_for_each_entry(ctx, &local->chanctx_list, list) {
+               if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
+                       continue;
+
+               if (WARN_ON(!ctx->replace_ctx)) {
+                       err = -EINVAL;
                        goto out;
                }
 
-               if (sdata->vif.type == NL80211_IFTYPE_AP)
-                       __ieee80211_vif_copy_chanctx_to_vlans(sdata, false);
+               list_for_each_entry(sdata, &ctx->reserved_vifs,
+                                   reserved_chanctx_list) {
+                       if (!ieee80211_vif_has_in_place_reservation(
+                                       sdata))
+                               continue;
+
+                       old_ctx = ieee80211_vif_get_chanctx(sdata);
+                       vif_chsw[i].vif = &sdata->vif;
+                       vif_chsw[i].old_ctx = &old_ctx->conf;
+                       vif_chsw[i].new_ctx = &ctx->conf;
+
+                       i++;
+               }
        }
 
-       *changed = tmp_changed;
+       err = drv_switch_vif_chanctx(local, vif_chsw, n_vifs,
+                                    CHANCTX_SWMODE_SWAP_CONTEXTS);
 
-       ieee80211_recalc_chanctx_chantype(local, ctx);
-       ieee80211_recalc_smps_chanctx(local, ctx);
-       ieee80211_recalc_radar_chanctx(local, ctx);
-       ieee80211_recalc_chanctx_min_def(local, ctx);
 out:
-       mutex_unlock(&local->chanctx_mtx);
-       return ret;
+       kfree(vif_chsw);
+       return err;
+}
+
+static int ieee80211_chsw_switch_ctxs(struct ieee80211_local *local)
+{
+       struct ieee80211_chanctx *ctx;
+       int err;
+
+       lockdep_assert_held(&local->mtx);
+       lockdep_assert_held(&local->chanctx_mtx);
+
+       list_for_each_entry(ctx, &local->chanctx_list, list) {
+               if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
+                       continue;
+
+               if (!list_empty(&ctx->replace_ctx->assigned_vifs))
+                       continue;
+
+               ieee80211_del_chanctx(local, ctx->replace_ctx);
+               err = ieee80211_add_chanctx(local, ctx);
+               if (err)
+                       goto err;
+       }
+
+       return 0;
+
+err:
+       WARN_ON(ieee80211_add_chanctx(local, ctx));
+       list_for_each_entry_continue_reverse(ctx, &local->chanctx_list, list) {
+               if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
+                       continue;
+
+               if (!list_empty(&ctx->replace_ctx->assigned_vifs))
+                       continue;
+
+               ieee80211_del_chanctx(local, ctx);
+               WARN_ON(ieee80211_add_chanctx(local, ctx->replace_ctx));
+       }
+
+       return err;
+}
+
+int
+ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
+{
+       struct ieee80211_sub_if_data *sdata, *sdata_tmp;
+       struct ieee80211_chanctx *ctx, *ctx_tmp, *old_ctx;
+       struct ieee80211_chanctx *new_ctx = NULL;
+       int i, err, n_assigned, n_reserved, n_ready;
+       int n_ctx = 0, n_vifs_switch = 0, n_vifs_assign = 0, n_vifs_ctxless = 0;
+
+       lockdep_assert_held(&local->mtx);
+       lockdep_assert_held(&local->chanctx_mtx);
+
+       /*
+        * If there are 2 independent pairs of channel contexts performing
+        * cross-switch of their vifs this code will still wait until both are
+        * ready even though it could be possible to switch one before the
+        * other is ready.
+        *
+        * For practical reasons and code simplicity just do a single huge
+        * switch.
+        */
+
+       /*
+        * Verify if the reservation is still feasible.
+        *  - if it's not then disconnect
+        *  - if it is but not all vifs necessary are ready then defer
+        */
+
+       list_for_each_entry(ctx, &local->chanctx_list, list) {
+               if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
+                       continue;
+
+               if (WARN_ON(!ctx->replace_ctx)) {
+                       err = -EINVAL;
+                       goto err;
+               }
+
+               if (!local->use_chanctx)
+                       new_ctx = ctx;
+
+               n_ctx++;
+
+               n_assigned = 0;
+               n_reserved = 0;
+               n_ready = 0;
+
+               list_for_each_entry(sdata, &ctx->replace_ctx->assigned_vifs,
+                                   assigned_chanctx_list) {
+                       n_assigned++;
+                       if (sdata->reserved_chanctx) {
+                               n_reserved++;
+                               if (sdata->reserved_ready)
+                                       n_ready++;
+                       }
+               }
+
+               if (n_assigned != n_reserved) {
+                       if (n_ready == n_reserved) {
+                               wiphy_info(local->hw.wiphy,
+                                          "channel context reservation cannot be finalized because some interfaces aren't switching\n");
+                               err = -EBUSY;
+                               goto err;
+                       }
+
+                       return -EAGAIN;
+               }
+
+               ctx->conf.radar_enabled = false;
+               list_for_each_entry(sdata, &ctx->reserved_vifs,
+                                   reserved_chanctx_list) {
+                       if (ieee80211_vif_has_in_place_reservation(sdata) &&
+                           !sdata->reserved_ready)
+                               return -EAGAIN;
+
+                       old_ctx = ieee80211_vif_get_chanctx(sdata);
+                       if (old_ctx) {
+                               if (old_ctx->replace_state ==
+                                   IEEE80211_CHANCTX_WILL_BE_REPLACED)
+                                       n_vifs_switch++;
+                               else
+                                       n_vifs_assign++;
+                       } else {
+                               n_vifs_ctxless++;
+                       }
+
+                       if (sdata->reserved_radar_required)
+                               ctx->conf.radar_enabled = true;
+               }
+       }
+
+       if (WARN_ON(n_ctx == 0) ||
+           WARN_ON(n_vifs_switch == 0 &&
+                   n_vifs_assign == 0 &&
+                   n_vifs_ctxless == 0) ||
+           WARN_ON(n_ctx > 1 && !local->use_chanctx) ||
+           WARN_ON(!new_ctx && !local->use_chanctx)) {
+               err = -EINVAL;
+               goto err;
+       }
+
+       /*
+        * All necessary vifs are ready. Perform the switch now depending on
+        * reservations and driver capabilities.
+        */
+
+       if (local->use_chanctx) {
+               if (n_vifs_switch > 0) {
+                       err = ieee80211_chsw_switch_vifs(local, n_vifs_switch);
+                       if (err)
+                               goto err;
+               }
+
+               if (n_vifs_assign > 0 || n_vifs_ctxless > 0) {
+                       err = ieee80211_chsw_switch_ctxs(local);
+                       if (err)
+                               goto err;
+               }
+       } else {
+               err = ieee80211_chsw_switch_hwconf(local, new_ctx);
+               if (err)
+                       goto err;
+       }
+
+       /*
+        * Update all structures, values and pointers to point to new channel
+        * context(s).
+        */
+
+       i = 0;
+       list_for_each_entry(ctx, &local->chanctx_list, list) {
+               if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
+                       continue;
+
+               if (WARN_ON(!ctx->replace_ctx)) {
+                       err = -EINVAL;
+                       goto err;
+               }
+
+               list_for_each_entry(sdata, &ctx->reserved_vifs,
+                                   reserved_chanctx_list) {
+                       u32 changed = 0;
+
+                       if (!ieee80211_vif_has_in_place_reservation(sdata))
+                               continue;
+
+                       rcu_assign_pointer(sdata->vif.chanctx_conf, &ctx->conf);
+
+                       if (sdata->vif.type == NL80211_IFTYPE_AP)
+                               __ieee80211_vif_copy_chanctx_to_vlans(sdata,
+                                                                     false);
+
+                       sdata->radar_required = sdata->reserved_radar_required;
+
+                       if (sdata->vif.bss_conf.chandef.width !=
+                           sdata->reserved_chandef.width)
+                               changed = BSS_CHANGED_BANDWIDTH;
+
+                       sdata->vif.bss_conf.chandef = sdata->reserved_chandef;
+                       if (changed)
+                               ieee80211_bss_info_change_notify(sdata,
+                                                                changed);
+
+                       ieee80211_recalc_txpower(sdata);
+               }
+
+               ieee80211_recalc_chanctx_chantype(local, ctx);
+               ieee80211_recalc_smps_chanctx(local, ctx);
+               ieee80211_recalc_radar_chanctx(local, ctx);
+               ieee80211_recalc_chanctx_min_def(local, ctx);
+
+               list_for_each_entry_safe(sdata, sdata_tmp, &ctx->reserved_vifs,
+                                        reserved_chanctx_list) {
+                       if (ieee80211_vif_get_chanctx(sdata) != ctx)
+                               continue;
+
+                       list_del(&sdata->reserved_chanctx_list);
+                       list_move(&sdata->assigned_chanctx_list,
+                                 &new_ctx->assigned_vifs);
+                       sdata->reserved_chanctx = NULL;
+
+                       ieee80211_vif_chanctx_reservation_complete(sdata);
+               }
+
+               /*
+                * This context might have been a dependency for an already
+                * ready re-assign reservation interface that was deferred. Do
+                * not propagate error to the caller though. The in-place
+                * reservation for originally requested interface has already
+                * succeeded at this point.
+                */
+               list_for_each_entry_safe(sdata, sdata_tmp, &ctx->reserved_vifs,
+                                        reserved_chanctx_list) {
+                       if (WARN_ON(ieee80211_vif_has_in_place_reservation(
+                                       sdata)))
+                               continue;
+
+                       if (WARN_ON(sdata->reserved_chanctx != ctx))
+                               continue;
+
+                       if (!sdata->reserved_ready)
+                               continue;
+
+                       if (ieee80211_vif_get_chanctx(sdata))
+                               err = ieee80211_vif_use_reserved_reassign(
+                                               sdata);
+                       else
+                               err = ieee80211_vif_use_reserved_assign(sdata);
+
+                       if (err) {
+                               sdata_info(sdata,
+                                          "failed to finalize (re-)assign reservation (err=%d)\n",
+                                          err);
+                               ieee80211_vif_unreserve_chanctx(sdata);
+                               cfg80211_stop_iface(local->hw.wiphy,
+                                                   &sdata->wdev,
+                                                   GFP_KERNEL);
+                       }
+               }
+       }
+
+       /*
+        * Finally free old contexts
+        */
+
+       list_for_each_entry_safe(ctx, ctx_tmp, &local->chanctx_list, list) {
+               if (ctx->replace_state != IEEE80211_CHANCTX_WILL_BE_REPLACED)
+                       continue;
+
+               ctx->replace_ctx->replace_ctx = NULL;
+               ctx->replace_ctx->replace_state =
+                               IEEE80211_CHANCTX_REPLACE_NONE;
+
+               list_del_rcu(&ctx->list);
+               kfree_rcu(ctx, rcu_head);
+       }
+
+       return 0;
+
+err:
+       list_for_each_entry(ctx, &local->chanctx_list, list) {
+               if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
+                       continue;
+
+               list_for_each_entry_safe(sdata, sdata_tmp, &ctx->reserved_vifs,
+                                        reserved_chanctx_list) {
+                       ieee80211_vif_unreserve_chanctx(sdata);
+                       ieee80211_vif_chanctx_reservation_complete(sdata);
+               }
+       }
+
+       return err;
+}
+
+int ieee80211_vif_use_reserved_context(struct ieee80211_sub_if_data *sdata)
+{
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_chanctx *new_ctx;
+       struct ieee80211_chanctx *old_ctx;
+       int err;
+
+       lockdep_assert_held(&local->mtx);
+       lockdep_assert_held(&local->chanctx_mtx);
+
+       new_ctx = sdata->reserved_chanctx;
+       old_ctx = ieee80211_vif_get_chanctx(sdata);
+
+       if (WARN_ON(!new_ctx))
+               return -EINVAL;
+
+       if (WARN_ON(new_ctx->replace_state ==
+                   IEEE80211_CHANCTX_WILL_BE_REPLACED))
+               return -EINVAL;
+
+       if (WARN_ON(sdata->reserved_ready))
+               return -EINVAL;
+
+       sdata->reserved_ready = true;
+
+       if (new_ctx->replace_state == IEEE80211_CHANCTX_REPLACE_NONE) {
+               if (old_ctx)
+                       err = ieee80211_vif_use_reserved_reassign(sdata);
+               else
+                       err = ieee80211_vif_use_reserved_assign(sdata);
+
+               if (err)
+                       return err;
+       }
+
+       /*
+        * In-place reservation may need to be finalized now either if:
+        *  a) sdata is taking part in the swapping itself and is the last one
+        *  b) sdata has switched with a re-assign reservation to an existing
+        *     context readying in-place switching of old_ctx
+        *
+        * In case of (b) do not propagate the error up because the requested
+        * sdata already switched successfully. Just spill an extra warning.
+        * The ieee80211_vif_use_reserved_switch() already stops all necessary
+        * interfaces upon failure.
+        */
+       if ((old_ctx &&
+            old_ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED) ||
+           new_ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER) {
+               err = ieee80211_vif_use_reserved_switch(local);
+               if (err && err != -EAGAIN) {
+                       if (new_ctx->replace_state ==
+                           IEEE80211_CHANCTX_REPLACES_OTHER)
+                               return err;
+
+                       wiphy_info(local->hw.wiphy,
+                                  "depending in-place reservation failed (err=%d)\n",
+                                  err);
+               }
+       }
+
+       return 0;
 }
 
 int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata,
@@ -1043,6 +1591,7 @@ int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_chanctx_conf *conf;
        struct ieee80211_chanctx *ctx;
+       const struct cfg80211_chan_def *compat;
        int ret;
 
        if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef,
@@ -1069,11 +1618,33 @@ int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata,
        }
 
        ctx = container_of(conf, struct ieee80211_chanctx, conf);
-       if (!cfg80211_chandef_compatible(&conf->def, chandef)) {
+
+       compat = cfg80211_chandef_compatible(&conf->def, chandef);
+       if (!compat) {
                ret = -EINVAL;
                goto out;
        }
 
+       switch (ctx->replace_state) {
+       case IEEE80211_CHANCTX_REPLACE_NONE:
+               if (!ieee80211_chanctx_reserved_chandef(local, ctx, compat)) {
+                       ret = -EBUSY;
+                       goto out;
+               }
+               break;
+       case IEEE80211_CHANCTX_WILL_BE_REPLACED:
+               /* TODO: Perhaps the bandwith change could be treated as a
+                * reservation itself? */
+               ret = -EBUSY;
+               goto out;
+       case IEEE80211_CHANCTX_REPLACES_OTHER:
+               /* channel context that is going to replace another channel
+                * context doesn't really exist and shouldn't be assigned
+                * anywhere yet */
+               WARN_ON(1);
+               break;
+       }
+
        sdata->vif.bss_conf.chandef = *chandef;
 
        ieee80211_recalc_chanctx_chantype(local, ctx);
index 2ecb4de..3db9664 100644 (file)
@@ -124,7 +124,7 @@ static ssize_t sta_connected_time_read(struct file *file, char __user *userbuf,
        long connected_time_secs;
        char buf[100];
        int res;
-       do_posix_clock_monotonic_gettime(&uptime);
+       ktime_get_ts(&uptime);
        connected_time_secs = uptime.tv_sec - sta->last_connected;
        time_to_tm(connected_time_secs, 0, &result);
        result.tm_year -= 70;
@@ -587,7 +587,6 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta)
        DEBUGFS_ADD_COUNTER(tx_filtered, tx_filtered_count);
        DEBUGFS_ADD_COUNTER(tx_retry_failed, tx_retry_failed);
        DEBUGFS_ADD_COUNTER(tx_retry_count, tx_retry_count);
-       DEBUGFS_ADD_COUNTER(wep_weak_iv_count, wep_weak_iv_count);
 
        if (sizeof(sta->driver_buffered_tids) == sizeof(u32))
                debugfs_create_x32("driver_buffered_tids", 0400,
index bd782dc..1142395 100644 (file)
@@ -314,7 +314,7 @@ static inline void drv_update_tkip_key(struct ieee80211_local *local,
 
 static inline int drv_hw_scan(struct ieee80211_local *local,
                              struct ieee80211_sub_if_data *sdata,
-                             struct cfg80211_scan_request *req)
+                             struct ieee80211_scan_request *req)
 {
        int ret;
 
@@ -346,7 +346,7 @@ static inline int
 drv_sched_scan_start(struct ieee80211_local *local,
                     struct ieee80211_sub_if_data *sdata,
                     struct cfg80211_sched_scan_request *req,
-                    struct ieee80211_sched_scan_ies *ies)
+                    struct ieee80211_scan_ies *ies)
 {
        int ret;
 
@@ -970,6 +970,22 @@ static inline void drv_mgd_prepare_tx(struct ieee80211_local *local,
        trace_drv_return_void(local);
 }
 
+static inline void
+drv_mgd_protect_tdls_discover(struct ieee80211_local *local,
+                             struct ieee80211_sub_if_data *sdata)
+{
+       might_sleep();
+
+       if (!check_sdata_in_driver(sdata))
+               return;
+       WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_STATION);
+
+       trace_drv_mgd_protect_tdls_discover(local, sdata);
+       if (local->ops->mgd_protect_tdls_discover)
+               local->ops->mgd_protect_tdls_discover(&local->hw, &sdata->vif);
+       trace_drv_return_void(local);
+}
+
 static inline int drv_add_chanctx(struct ieee80211_local *local,
                                  struct ieee80211_chanctx *ctx)
 {
diff --git a/net/mac80211/ethtool.c b/net/mac80211/ethtool.c
new file mode 100644 (file)
index 0000000..ebfc809
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+ * mac80211 ethtool hooks for cfg80211
+ *
+ * Copied from cfg.c - originally
+ * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2014      Intel Corporation (Author: Johannes Berg)
+ *
+ * This file is GPLv2 as found in COPYING.
+ */
+#include <linux/types.h>
+#include <net/cfg80211.h>
+#include "ieee80211_i.h"
+#include "sta_info.h"
+#include "driver-ops.h"
+
+static int ieee80211_set_ringparam(struct net_device *dev,
+                                  struct ethtool_ringparam *rp)
+{
+       struct ieee80211_local *local = wiphy_priv(dev->ieee80211_ptr->wiphy);
+
+       if (rp->rx_mini_pending != 0 || rp->rx_jumbo_pending != 0)
+               return -EINVAL;
+
+       return drv_set_ringparam(local, rp->tx_pending, rp->rx_pending);
+}
+
+static void ieee80211_get_ringparam(struct net_device *dev,
+                                   struct ethtool_ringparam *rp)
+{
+       struct ieee80211_local *local = wiphy_priv(dev->ieee80211_ptr->wiphy);
+
+       memset(rp, 0, sizeof(*rp));
+
+       drv_get_ringparam(local, &rp->tx_pending, &rp->tx_max_pending,
+                         &rp->rx_pending, &rp->rx_max_pending);
+}
+
+static const char ieee80211_gstrings_sta_stats[][ETH_GSTRING_LEN] = {
+       "rx_packets", "rx_bytes",
+       "rx_duplicates", "rx_fragments", "rx_dropped",
+       "tx_packets", "tx_bytes", "tx_fragments",
+       "tx_filtered", "tx_retry_failed", "tx_retries",
+       "beacon_loss", "sta_state", "txrate", "rxrate", "signal",
+       "channel", "noise", "ch_time", "ch_time_busy",
+       "ch_time_ext_busy", "ch_time_rx", "ch_time_tx"
+};
+#define STA_STATS_LEN  ARRAY_SIZE(ieee80211_gstrings_sta_stats)
+
+static int ieee80211_get_sset_count(struct net_device *dev, int sset)
+{
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       int rv = 0;
+
+       if (sset == ETH_SS_STATS)
+               rv += STA_STATS_LEN;
+
+       rv += drv_get_et_sset_count(sdata, sset);
+
+       if (rv == 0)
+               return -EOPNOTSUPP;
+       return rv;
+}
+
+static void ieee80211_get_stats(struct net_device *dev,
+                               struct ethtool_stats *stats,
+                               u64 *data)
+{
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       struct ieee80211_chanctx_conf *chanctx_conf;
+       struct ieee80211_channel *channel;
+       struct sta_info *sta;
+       struct ieee80211_local *local = sdata->local;
+       struct station_info sinfo;
+       struct survey_info survey;
+       int i, q;
+#define STA_STATS_SURVEY_LEN 7
+
+       memset(data, 0, sizeof(u64) * STA_STATS_LEN);
+
+#define ADD_STA_STATS(sta)                             \
+       do {                                            \
+               data[i++] += sta->rx_packets;           \
+               data[i++] += sta->rx_bytes;             \
+               data[i++] += sta->num_duplicates;       \
+               data[i++] += sta->rx_fragments;         \
+               data[i++] += sta->rx_dropped;           \
+                                                       \
+               data[i++] += sinfo.tx_packets;          \
+               data[i++] += sinfo.tx_bytes;            \
+               data[i++] += sta->tx_fragments;         \
+               data[i++] += sta->tx_filtered_count;    \
+               data[i++] += sta->tx_retry_failed;      \
+               data[i++] += sta->tx_retry_count;       \
+               data[i++] += sta->beacon_loss_count;    \
+       } while (0)
+
+       /* For Managed stations, find the single station based on BSSID
+        * and use that.  For interface types, iterate through all available
+        * stations and add stats for any station that is assigned to this
+        * network device.
+        */
+
+       mutex_lock(&local->sta_mtx);
+
+       if (sdata->vif.type == NL80211_IFTYPE_STATION) {
+               sta = sta_info_get_bss(sdata, sdata->u.mgd.bssid);
+
+               if (!(sta && !WARN_ON(sta->sdata->dev != dev)))
+                       goto do_survey;
+
+               sinfo.filled = 0;
+               sta_set_sinfo(sta, &sinfo);
+
+               i = 0;
+               ADD_STA_STATS(sta);
+
+               data[i++] = sta->sta_state;
+
+
+               if (sinfo.filled & STATION_INFO_TX_BITRATE)
+                       data[i] = 100000 *
+                               cfg80211_calculate_bitrate(&sinfo.txrate);
+               i++;
+               if (sinfo.filled & STATION_INFO_RX_BITRATE)
+                       data[i] = 100000 *
+                               cfg80211_calculate_bitrate(&sinfo.rxrate);
+               i++;
+
+               if (sinfo.filled & STATION_INFO_SIGNAL_AVG)
+                       data[i] = (u8)sinfo.signal_avg;
+               i++;
+       } else {
+               list_for_each_entry(sta, &local->sta_list, list) {
+                       /* Make sure this station belongs to the proper dev */
+                       if (sta->sdata->dev != dev)
+                               continue;
+
+                       sinfo.filled = 0;
+                       sta_set_sinfo(sta, &sinfo);
+                       i = 0;
+                       ADD_STA_STATS(sta);
+               }
+       }
+
+do_survey:
+       i = STA_STATS_LEN - STA_STATS_SURVEY_LEN;
+       /* Get survey stats for current channel */
+       survey.filled = 0;
+
+       rcu_read_lock();
+       chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+       if (chanctx_conf)
+               channel = chanctx_conf->def.chan;
+       else
+               channel = NULL;
+       rcu_read_unlock();
+
+       if (channel) {
+               q = 0;
+               do {
+                       survey.filled = 0;
+                       if (drv_get_survey(local, q, &survey) != 0) {
+                               survey.filled = 0;
+                               break;
+                       }
+                       q++;
+               } while (channel != survey.channel);
+       }
+
+       if (survey.filled)
+               data[i++] = survey.channel->center_freq;
+       else
+               data[i++] = 0;
+       if (survey.filled & SURVEY_INFO_NOISE_DBM)
+               data[i++] = (u8)survey.noise;
+       else
+               data[i++] = -1LL;
+       if (survey.filled & SURVEY_INFO_CHANNEL_TIME)
+               data[i++] = survey.channel_time;
+       else
+               data[i++] = -1LL;
+       if (survey.filled & SURVEY_INFO_CHANNEL_TIME_BUSY)
+               data[i++] = survey.channel_time_busy;
+       else
+               data[i++] = -1LL;
+       if (survey.filled & SURVEY_INFO_CHANNEL_TIME_EXT_BUSY)
+               data[i++] = survey.channel_time_ext_busy;
+       else
+               data[i++] = -1LL;
+       if (survey.filled & SURVEY_INFO_CHANNEL_TIME_RX)
+               data[i++] = survey.channel_time_rx;
+       else
+               data[i++] = -1LL;
+       if (survey.filled & SURVEY_INFO_CHANNEL_TIME_TX)
+               data[i++] = survey.channel_time_tx;
+       else
+               data[i++] = -1LL;
+
+       mutex_unlock(&local->sta_mtx);
+
+       if (WARN_ON(i != STA_STATS_LEN))
+               return;
+
+       drv_get_et_stats(sdata, stats, &(data[STA_STATS_LEN]));
+}
+
+static void ieee80211_get_strings(struct net_device *dev, u32 sset, u8 *data)
+{
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       int sz_sta_stats = 0;
+
+       if (sset == ETH_SS_STATS) {
+               sz_sta_stats = sizeof(ieee80211_gstrings_sta_stats);
+               memcpy(data, ieee80211_gstrings_sta_stats, sz_sta_stats);
+       }
+       drv_get_et_strings(sdata, sset, &(data[sz_sta_stats]));
+}
+
+static int ieee80211_get_regs_len(struct net_device *dev)
+{
+       return 0;
+}
+
+static void ieee80211_get_regs(struct net_device *dev,
+                              struct ethtool_regs *regs,
+                              void *data)
+{
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+
+       regs->version = wdev->wiphy->hw_version;
+       regs->len = 0;
+}
+
+const struct ethtool_ops ieee80211_ethtool_ops = {
+       .get_drvinfo = cfg80211_get_drvinfo,
+       .get_regs_len = ieee80211_get_regs_len,
+       .get_regs = ieee80211_get_regs,
+       .get_link = ethtool_op_get_link,
+       .get_ringparam = ieee80211_get_ringparam,
+       .set_ringparam = ieee80211_set_ringparam,
+       .get_strings = ieee80211_get_strings,
+       .get_ethtool_stats = ieee80211_get_stats,
+       .get_sset_count = ieee80211_get_sset_count,
+};
index 18ee0a2..713485f 100644 (file)
@@ -143,7 +143,7 @@ ieee80211_ibss_build_presp(struct ieee80211_sub_if_data *sdata,
                *pos++ = csa_settings->block_tx ? 1 : 0;
                *pos++ = ieee80211_frequency_to_channel(
                                csa_settings->chandef.chan->center_freq);
-               sdata->csa_counter_offset_beacon[0] = (pos - presp->head);
+               presp->csa_counter_offsets[0] = (pos - presp->head);
                *pos++ = csa_settings->count;
        }
 
index ac9836e..9e025e1 100644 (file)
@@ -229,16 +229,29 @@ struct ieee80211_rx_data {
        u16 tkip_iv16;
 };
 
+struct ieee80211_csa_settings {
+       const u16 *counter_offsets_beacon;
+       const u16 *counter_offsets_presp;
+
+       int n_counter_offsets_beacon;
+       int n_counter_offsets_presp;
+
+       u8 count;
+};
+
 struct beacon_data {
        u8 *head, *tail;
        int head_len, tail_len;
        struct ieee80211_meshconf_ie *meshconf;
+       u16 csa_counter_offsets[IEEE80211_MAX_CSA_COUNTERS_NUM];
+       u8 csa_current_counter;
        struct rcu_head rcu_head;
 };
 
 struct probe_resp {
        struct rcu_head rcu_head;
        int len;
+       u16 csa_counter_offsets[IEEE80211_MAX_CSA_COUNTERS_NUM];
        u8 data[0];
 };
 
@@ -688,6 +701,24 @@ enum ieee80211_chanctx_mode {
        IEEE80211_CHANCTX_EXCLUSIVE
 };
 
+/**
+ * enum ieee80211_chanctx_replace_state - channel context replacement state
+ *
+ * This is used for channel context in-place reservations that require channel
+ * context switch/swap.
+ *
+ * @IEEE80211_CHANCTX_REPLACE_NONE: no replacement is taking place
+ * @IEEE80211_CHANCTX_WILL_BE_REPLACED: this channel context will be replaced
+ *     by a (not yet registered) channel context pointed by %replace_ctx.
+ * @IEEE80211_CHANCTX_REPLACES_OTHER: this (not yet registered) channel context
+ *     replaces an existing channel context pointed to by %replace_ctx.
+ */
+enum ieee80211_chanctx_replace_state {
+       IEEE80211_CHANCTX_REPLACE_NONE,
+       IEEE80211_CHANCTX_WILL_BE_REPLACED,
+       IEEE80211_CHANCTX_REPLACES_OTHER,
+};
+
 struct ieee80211_chanctx {
        struct list_head list;
        struct rcu_head rcu_head;
@@ -695,6 +726,9 @@ struct ieee80211_chanctx {
        struct list_head assigned_vifs;
        struct list_head reserved_vifs;
 
+       enum ieee80211_chanctx_replace_state replace_state;
+       struct ieee80211_chanctx *replace_ctx;
+
        enum ieee80211_chanctx_mode mode;
        bool driver_present;
 
@@ -754,9 +788,6 @@ struct ieee80211_sub_if_data {
        struct mac80211_qos_map __rcu *qos_map;
 
        struct work_struct csa_finalize_work;
-       u16 csa_counter_offset_beacon[IEEE80211_MAX_CSA_COUNTERS_NUM];
-       u16 csa_counter_offset_presp[IEEE80211_MAX_CSA_COUNTERS_NUM];
-       bool csa_radar_required;
        bool csa_block_tx; /* write-protected by sdata_lock and local->mtx */
        struct cfg80211_chan_def csa_chandef;
 
@@ -767,7 +798,7 @@ struct ieee80211_sub_if_data {
        struct ieee80211_chanctx *reserved_chanctx;
        struct cfg80211_chan_def reserved_chandef;
        bool reserved_radar_required;
-       u8 csa_current_counter;
+       bool reserved_ready;
 
        /* used to reconfigure hardware SM PS */
        struct work_struct recalc_smps;
@@ -784,6 +815,9 @@ struct ieee80211_sub_if_data {
        bool radar_required;
        struct delayed_work dfs_cac_timer_work;
 
+       u8 tdls_peer[ETH_ALEN] __aligned(2);
+       struct delayed_work tdls_peer_del_work;
+
        /*
         * AP this belongs to: self in AP mode and
         * corresponding AP in VLAN mode, NULL for
@@ -912,6 +946,9 @@ enum queue_stop_reason {
        IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
        IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL,
        IEEE80211_QUEUE_STOP_REASON_FLUSH,
+       IEEE80211_QUEUE_STOP_REASON_TDLS_TEARDOWN,
+
+       IEEE80211_QUEUE_STOP_REASONS,
 };
 
 #ifdef CONFIG_MAC80211_LEDS
@@ -1008,6 +1045,7 @@ struct ieee80211_local {
        struct workqueue_struct *workqueue;
 
        unsigned long queue_stop_reasons[IEEE80211_MAX_QUEUES];
+       int q_stop_reasons[IEEE80211_MAX_QUEUES][IEEE80211_QUEUE_STOP_REASONS];
        /* also used to protect ampdu_ac_queue and amdpu_ac_stop_refcnt */
        spinlock_t queue_stop_reason_lock;
 
@@ -1135,7 +1173,8 @@ struct ieee80211_local {
        unsigned long scanning;
        struct cfg80211_ssid scan_ssid;
        struct cfg80211_scan_request *int_scan_req;
-       struct cfg80211_scan_request *scan_req, *hw_scan_req;
+       struct cfg80211_scan_request *scan_req;
+       struct ieee80211_scan_request *hw_scan_req;
        struct cfg80211_chan_def scan_chandef;
        enum ieee80211_band hw_scan_band;
        int scan_channel_idx;
@@ -1476,7 +1515,6 @@ void ieee80211_sw_roc_work(struct work_struct *work);
 void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc);
 
 /* channel switch handling */
-bool ieee80211_csa_needs_block_tx(struct ieee80211_local *local);
 void ieee80211_csa_finalize_work(struct work_struct *work);
 int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
                             struct cfg80211_csa_settings *params);
@@ -1705,14 +1743,24 @@ void ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata,
 
 void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
                                     unsigned long queues,
-                                    enum queue_stop_reason reason);
+                                    enum queue_stop_reason reason,
+                                    bool refcounted);
+void ieee80211_stop_vif_queues(struct ieee80211_local *local,
+                              struct ieee80211_sub_if_data *sdata,
+                              enum queue_stop_reason reason);
+void ieee80211_wake_vif_queues(struct ieee80211_local *local,
+                              struct ieee80211_sub_if_data *sdata,
+                              enum queue_stop_reason reason);
 void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw,
                                     unsigned long queues,
-                                    enum queue_stop_reason reason);
+                                    enum queue_stop_reason reason,
+                                    bool refcounted);
 void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue,
-                                   enum queue_stop_reason reason);
+                                   enum queue_stop_reason reason,
+                                   bool refcounted);
 void ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue,
-                                   enum queue_stop_reason reason);
+                                   enum queue_stop_reason reason,
+                                   bool refcounted);
 void ieee80211_propagate_queue_wake(struct ieee80211_local *local, int queue);
 void ieee80211_add_pending_skb(struct ieee80211_local *local,
                               struct sk_buff *skb);
@@ -1730,8 +1778,10 @@ void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
                                    const u8 *bssid, u16 stype, u16 reason,
                                    bool send_frame, u8 *frame_buf);
 int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
-                            size_t buffer_len, const u8 *ie, size_t ie_len,
-                            enum ieee80211_band band, u32 rate_mask,
+                            size_t buffer_len,
+                            struct ieee80211_scan_ies *ie_desc,
+                            const u8 *ie, size_t ie_len,
+                            u8 bands_used, u32 *rate_masks,
                             struct cfg80211_chan_def *chandef);
 struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
                                          u8 *dst, u32 ratemask,
@@ -1791,18 +1841,14 @@ ieee80211_vif_reserve_chanctx(struct ieee80211_sub_if_data *sdata,
                              enum ieee80211_chanctx_mode mode,
                              bool radar_required);
 int __must_check
-ieee80211_vif_use_reserved_context(struct ieee80211_sub_if_data *sdata,
-                                  u32 *changed);
+ieee80211_vif_use_reserved_context(struct ieee80211_sub_if_data *sdata);
 int ieee80211_vif_unreserve_chanctx(struct ieee80211_sub_if_data *sdata);
+int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local);
 
 int __must_check
 ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata,
                               const struct cfg80211_chan_def *chandef,
                               u32 *changed);
-/* NOTE: only use ieee80211_vif_change_channel() for channel switch */
-int __must_check
-ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata,
-                            u32 *changed);
 void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata);
 void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata);
 void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata,
@@ -1842,11 +1888,14 @@ int ieee80211_max_num_channels(struct ieee80211_local *local);
 int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
                        const u8 *peer, u8 action_code, u8 dialog_token,
                        u16 status_code, u32 peer_capability,
-                       const u8 *extra_ies, size_t extra_ies_len);
+                       bool initiator, const u8 *extra_ies,
+                       size_t extra_ies_len);
 int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
                        const u8 *peer, enum nl80211_tdls_operation oper);
 
 
+extern const struct ethtool_ops ieee80211_ethtool_ops;
+
 #ifdef CONFIG_MAC80211_NOINLINE
 #define debug_noinline noinline
 #else
@@ -1854,3 +1903,4 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
 #endif
 
 #endif /* IEEE80211_I_H */
+void ieee80211_tdls_peer_del_work(struct work_struct *wk);
index 388b863..bbf51b2 100644 (file)
@@ -841,10 +841,11 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
        sdata_lock(sdata);
        mutex_lock(&local->mtx);
        sdata->vif.csa_active = false;
-       if (!ieee80211_csa_needs_block_tx(local))
-               ieee80211_wake_queues_by_reason(&local->hw,
-                                       IEEE80211_MAX_QUEUE_MAP,
-                                       IEEE80211_QUEUE_STOP_REASON_CSA);
+       if (sdata->csa_block_tx) {
+               ieee80211_wake_vif_queues(local, sdata,
+                                         IEEE80211_QUEUE_STOP_REASON_CSA);
+               sdata->csa_block_tx = false;
+       }
        mutex_unlock(&local->mtx);
        sdata_unlock(sdata);
 
@@ -1671,6 +1672,8 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
                          ieee80211_dfs_cac_timer_work);
        INIT_DELAYED_WORK(&sdata->dec_tailroom_needed_wk,
                          ieee80211_delayed_tailroom_dec);
+       INIT_DELAYED_WORK(&sdata->tdls_peer_del_work,
+                         ieee80211_tdls_peer_del_work);
 
        for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
                struct ieee80211_supported_band *sband;
@@ -1705,6 +1708,8 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
 
                ndev->features |= local->hw.netdev_features;
 
+               netdev_set_default_ethtool_ops(ndev, &ieee80211_ethtool_ops);
+
                ret = register_netdevice(ndev);
                if (ret) {
                        free_netdev(ndev);
index d17c26d..e0ab432 100644 (file)
@@ -272,7 +272,8 @@ void ieee80211_restart_hw(struct ieee80211_hw *hw)
 
        /* use this reason, ieee80211_reconfig will unblock it */
        ieee80211_stop_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP,
-                                       IEEE80211_QUEUE_STOP_REASON_SUSPEND);
+                                       IEEE80211_QUEUE_STOP_REASON_SUSPEND,
+                                       false);
 
        /*
         * Stop all Rx during the reconfig. We don't want state changes
@@ -1187,18 +1188,12 @@ static int __init ieee80211_init(void)
        if (ret)
                goto err_minstrel;
 
-       ret = rc80211_pid_init();
-       if (ret)
-               goto err_pid;
-
        ret = ieee80211_iface_init();
        if (ret)
                goto err_netdev;
 
        return 0;
  err_netdev:
-       rc80211_pid_exit();
- err_pid:
        rc80211_minstrel_ht_exit();
  err_minstrel:
        rc80211_minstrel_exit();
@@ -1208,7 +1203,6 @@ static int __init ieee80211_init(void)
 
 static void __exit ieee80211_exit(void)
 {
-       rc80211_pid_exit();
        rc80211_minstrel_ht_exit();
        rc80211_minstrel_exit();
 
index 6495a3f..e9f99c1 100644 (file)
@@ -679,7 +679,7 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
                *pos++ = 0x0;
                *pos++ = ieee80211_frequency_to_channel(
                                csa->settings.chandef.chan->center_freq);
-               sdata->csa_counter_offset_beacon[0] = hdr_len + 6;
+               bcn->csa_counter_offsets[0] = hdr_len + 6;
                *pos++ = csa->settings.count;
                *pos++ = WLAN_EID_CHAN_SWITCH_PARAM;
                *pos++ = 6;
@@ -1122,7 +1122,7 @@ static int mesh_fwd_csa_frame(struct ieee80211_sub_if_data *sdata,
        mgmt_fwd = (struct ieee80211_mgmt *) skb_put(skb, len);
 
        /* offset_ttl is based on whether the secondary channel
-        * offset is available or not. Substract 1 from the mesh TTL
+        * offset is available or not. Subtract 1 from the mesh TTL
         * and disable the initiator flag before forwarding.
         */
        offset_ttl = (len < 42) ? 7 : 10;
index e8f60aa..63b8741 100644 (file)
@@ -551,11 +551,30 @@ static void mesh_plink_timer(unsigned long data)
                return;
 
        spin_lock_bh(&sta->lock);
-       if (sta->ignore_plink_timer) {
-               sta->ignore_plink_timer = false;
+
+       /* If a timer fires just before a state transition on another CPU,
+        * we may have already extended the timeout and changed state by the
+        * time we've acquired the lock and arrived  here.  In that case,
+        * skip this timer and wait for the new one.
+        */
+       if (time_before(jiffies, sta->plink_timer.expires)) {
+               mpl_dbg(sta->sdata,
+                       "Ignoring timer for %pM in state %s (timer adjusted)",
+                       sta->sta.addr, mplstates[sta->plink_state]);
                spin_unlock_bh(&sta->lock);
                return;
        }
+
+       /* del_timer() and handler may race when entering these states */
+       if (sta->plink_state == NL80211_PLINK_LISTEN ||
+           sta->plink_state == NL80211_PLINK_ESTAB) {
+               mpl_dbg(sta->sdata,
+                       "Ignoring timer for %pM in state %s (timer deleted)",
+                       sta->sta.addr, mplstates[sta->plink_state]);
+               spin_unlock_bh(&sta->lock);
+               return;
+       }
+
        mpl_dbg(sta->sdata,
                "Mesh plink timer for %pM fired on state %s\n",
                sta->sta.addr, mplstates[sta->plink_state]);
@@ -773,9 +792,7 @@ static u32 mesh_plink_fsm(struct ieee80211_sub_if_data *sdata,
                        break;
                case CNF_ACPT:
                        sta->plink_state = NL80211_PLINK_CNF_RCVD;
-                       if (!mod_plink_timer(sta,
-                                            mshcfg->dot11MeshConfirmTimeout))
-                               sta->ignore_plink_timer = true;
+                       mod_plink_timer(sta, mshcfg->dot11MeshConfirmTimeout);
                        break;
                default:
                        break;
@@ -834,8 +851,7 @@ static u32 mesh_plink_fsm(struct ieee80211_sub_if_data *sdata,
        case NL80211_PLINK_HOLDING:
                switch (event) {
                case CLS_ACPT:
-                       if (del_timer(&sta->plink_timer))
-                               sta->ignore_plink_timer = 1;
+                       del_timer(&sta->plink_timer);
                        mesh_plink_fsm_restart(sta);
                        break;
                case OPN_ACPT:
index 3345401..931330b 100644 (file)
@@ -940,51 +940,70 @@ static void ieee80211_chswitch_work(struct work_struct *work)
                container_of(work, struct ieee80211_sub_if_data, u.mgd.chswitch_work);
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-       u32 changed = 0;
        int ret;
 
        if (!ieee80211_sdata_running(sdata))
                return;
 
        sdata_lock(sdata);
+       mutex_lock(&local->mtx);
+       mutex_lock(&local->chanctx_mtx);
+
        if (!ifmgd->associated)
                goto out;
 
-       mutex_lock(&local->mtx);
-       ret = ieee80211_vif_change_channel(sdata, &changed);
-       mutex_unlock(&local->mtx);
-       if (ret) {
+       if (!sdata->vif.csa_active)
+               goto out;
+
+       /*
+        * using reservation isn't immediate as it may be deferred until later
+        * with multi-vif. once reservation is complete it will re-schedule the
+        * work with no reserved_chanctx so verify chandef to check if it
+        * completed successfully
+        */
+
+       if (sdata->reserved_chanctx) {
+               /*
+                * with multi-vif csa driver may call ieee80211_csa_finish()
+                * many times while waiting for other interfaces to use their
+                * reservations
+                */
+               if (sdata->reserved_ready)
+                       goto out;
+
+               ret = ieee80211_vif_use_reserved_context(sdata);
+               if (ret) {
+                       sdata_info(sdata,
+                                  "failed to use reserved channel context, disconnecting (err=%d)\n",
+                                  ret);
+                       ieee80211_queue_work(&sdata->local->hw,
+                                            &ifmgd->csa_connection_drop_work);
+                       goto out;
+               }
+
+               goto out;
+       }
+
+       if (!cfg80211_chandef_identical(&sdata->vif.bss_conf.chandef,
+                                       &sdata->csa_chandef)) {
                sdata_info(sdata,
-                          "vif channel switch failed, disconnecting\n");
+                          "failed to finalize channel switch, disconnecting\n");
                ieee80211_queue_work(&sdata->local->hw,
                                     &ifmgd->csa_connection_drop_work);
                goto out;
        }
 
-       if (!local->use_chanctx) {
-               local->_oper_chandef = sdata->csa_chandef;
-               /* Call "hw_config" only if doing sw channel switch.
-                * Otherwise update the channel directly
-                */
-               if (!local->ops->channel_switch)
-                       ieee80211_hw_config(local, 0);
-               else
-                       local->hw.conf.chandef = local->_oper_chandef;
-       }
-
        /* XXX: shouldn't really modify cfg80211-owned data! */
        ifmgd->associated->channel = sdata->csa_chandef.chan;
 
-       ieee80211_bss_info_change_notify(sdata, changed);
-
-       mutex_lock(&local->mtx);
        sdata->vif.csa_active = false;
+
        /* XXX: wait for a beacon first? */
-       if (!ieee80211_csa_needs_block_tx(local))
-               ieee80211_wake_queues_by_reason(&local->hw,
-                                       IEEE80211_MAX_QUEUE_MAP,
-                                       IEEE80211_QUEUE_STOP_REASON_CSA);
-       mutex_unlock(&local->mtx);
+       if (sdata->csa_block_tx) {
+               ieee80211_wake_vif_queues(local, sdata,
+                                         IEEE80211_QUEUE_STOP_REASON_CSA);
+               sdata->csa_block_tx = false;
+       }
 
        ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
 
@@ -992,6 +1011,8 @@ static void ieee80211_chswitch_work(struct work_struct *work)
        ieee80211_sta_reset_conn_monitor(sdata);
 
 out:
+       mutex_unlock(&local->chanctx_mtx);
+       mutex_unlock(&local->mtx);
        sdata_unlock(sdata);
 }
 
@@ -1028,6 +1049,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        struct cfg80211_bss *cbss = ifmgd->associated;
+       struct ieee80211_chanctx_conf *conf;
        struct ieee80211_chanctx *chanctx;
        enum ieee80211_band current_band;
        struct ieee80211_csa_ie csa_ie;
@@ -1071,7 +1093,22 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
 
        ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED;
 
+       mutex_lock(&local->mtx);
        mutex_lock(&local->chanctx_mtx);
+       conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
+                                        lockdep_is_held(&local->chanctx_mtx));
+       if (!conf) {
+               sdata_info(sdata,
+                          "no channel context assigned to vif?, disconnecting\n");
+               ieee80211_queue_work(&local->hw,
+                                    &ifmgd->csa_connection_drop_work);
+               mutex_unlock(&local->chanctx_mtx);
+               mutex_unlock(&local->mtx);
+               return;
+       }
+
+       chanctx = container_of(conf, struct ieee80211_chanctx, conf);
+
        if (local->use_chanctx) {
                u32 num_chanctx = 0;
                list_for_each_entry(chanctx, &local->chanctx_list, list)
@@ -1084,38 +1121,32 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
                        ieee80211_queue_work(&local->hw,
                                             &ifmgd->csa_connection_drop_work);
                        mutex_unlock(&local->chanctx_mtx);
+                       mutex_unlock(&local->mtx);
                        return;
                }
        }
 
-       if (WARN_ON(!rcu_access_pointer(sdata->vif.chanctx_conf))) {
-               ieee80211_queue_work(&local->hw,
-                                    &ifmgd->csa_connection_drop_work);
-               mutex_unlock(&local->chanctx_mtx);
-               return;
-       }
-       chanctx = container_of(rcu_access_pointer(sdata->vif.chanctx_conf),
-                              struct ieee80211_chanctx, conf);
-       if (ieee80211_chanctx_refcount(local, chanctx) > 1) {
+       res = ieee80211_vif_reserve_chanctx(sdata, &csa_ie.chandef,
+                                           chanctx->mode, false);
+       if (res) {
                sdata_info(sdata,
-                          "channel switch with multiple interfaces on the same channel, disconnecting\n");
+                          "failed to reserve channel context for channel switch, disconnecting (err=%d)\n",
+                          res);
                ieee80211_queue_work(&local->hw,
                                     &ifmgd->csa_connection_drop_work);
                mutex_unlock(&local->chanctx_mtx);
+               mutex_unlock(&local->mtx);
                return;
        }
        mutex_unlock(&local->chanctx_mtx);
 
-       sdata->csa_chandef = csa_ie.chandef;
-
-       mutex_lock(&local->mtx);
        sdata->vif.csa_active = true;
+       sdata->csa_chandef = csa_ie.chandef;
        sdata->csa_block_tx = csa_ie.mode;
 
        if (sdata->csa_block_tx)
-               ieee80211_stop_queues_by_reason(&local->hw,
-                                       IEEE80211_MAX_QUEUE_MAP,
-                                       IEEE80211_QUEUE_STOP_REASON_CSA);
+               ieee80211_stop_vif_queues(local, sdata,
+                                         IEEE80211_QUEUE_STOP_REASON_CSA);
        mutex_unlock(&local->mtx);
 
        if (local->ops->channel_switch) {
@@ -1385,7 +1416,8 @@ void ieee80211_dynamic_ps_disable_work(struct work_struct *work)
 
        ieee80211_wake_queues_by_reason(&local->hw,
                                        IEEE80211_MAX_QUEUE_MAP,
-                                       IEEE80211_QUEUE_STOP_REASON_PS);
+                                       IEEE80211_QUEUE_STOP_REASON_PS,
+                                       false);
 }
 
 void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
@@ -1830,10 +1862,11 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
        ieee80211_vif_release_channel(sdata);
 
        sdata->vif.csa_active = false;
-       if (!ieee80211_csa_needs_block_tx(local))
-               ieee80211_wake_queues_by_reason(&local->hw,
-                                       IEEE80211_MAX_QUEUE_MAP,
-                                       IEEE80211_QUEUE_STOP_REASON_CSA);
+       if (sdata->csa_block_tx) {
+               ieee80211_wake_vif_queues(local, sdata,
+                                         IEEE80211_QUEUE_STOP_REASON_CSA);
+               sdata->csa_block_tx = false;
+       }
        mutex_unlock(&local->mtx);
 
        sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM;
@@ -2079,10 +2112,11 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
 
        mutex_lock(&local->mtx);
        sdata->vif.csa_active = false;
-       if (!ieee80211_csa_needs_block_tx(local))
-               ieee80211_wake_queues_by_reason(&local->hw,
-                                       IEEE80211_MAX_QUEUE_MAP,
-                                       IEEE80211_QUEUE_STOP_REASON_CSA);
+       if (sdata->csa_block_tx) {
+               ieee80211_wake_vif_queues(local, sdata,
+                                         IEEE80211_QUEUE_STOP_REASON_CSA);
+               sdata->csa_block_tx = false;
+       }
        mutex_unlock(&local->mtx);
 
        cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
index 7a17dec..ff20b2e 100644 (file)
@@ -119,7 +119,8 @@ void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local)
         * before sending nullfunc to enable powersave at the AP.
         */
        ieee80211_stop_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP,
-                                       IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL);
+                                       IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL,
+                                       false);
        ieee80211_flush_queues(local, NULL);
 
        mutex_lock(&local->iflist_mtx);
@@ -182,7 +183,8 @@ void ieee80211_offchannel_return(struct ieee80211_local *local)
        mutex_unlock(&local->iflist_mtx);
 
        ieee80211_wake_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP,
-                                       IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL);
+                                       IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL,
+                                       false);
 }
 
 void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc)
index d478b88..4c5192e 100644 (file)
@@ -35,7 +35,8 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
 
        ieee80211_stop_queues_by_reason(hw,
                                        IEEE80211_MAX_QUEUE_MAP,
-                                       IEEE80211_QUEUE_STOP_REASON_SUSPEND);
+                                       IEEE80211_QUEUE_STOP_REASON_SUSPEND,
+                                       false);
 
        /* flush out all packets */
        synchronize_net();
@@ -74,7 +75,8 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
                        }
                        ieee80211_wake_queues_by_reason(hw,
                                        IEEE80211_MAX_QUEUE_MAP,
-                                       IEEE80211_QUEUE_STOP_REASON_SUSPEND);
+                                       IEEE80211_QUEUE_STOP_REASON_SUSPEND,
+                                       false);
                        return err;
                } else if (err > 0) {
                        WARN_ON(err != 1);
index 9aa2a11..18babe3 100644 (file)
@@ -143,19 +143,6 @@ void rate_control_deinitialize(struct ieee80211_local *local);
 
 
 /* Rate control algorithms */
-#ifdef CONFIG_MAC80211_RC_PID
-int rc80211_pid_init(void);
-void rc80211_pid_exit(void);
-#else
-static inline int rc80211_pid_init(void)
-{
-       return 0;
-}
-static inline void rc80211_pid_exit(void)
-{
-}
-#endif
-
 #ifdef CONFIG_MAC80211_RC_MINSTREL
 int rc80211_minstrel_init(void);
 void rc80211_minstrel_exit(void);
diff --git a/net/mac80211/rc80211_pid.h b/net/mac80211/rc80211_pid.h
deleted file mode 100644 (file)
index 19111c7..0000000
+++ /dev/null
@@ -1,278 +0,0 @@
-/*
- * Copyright 2007, Mattias Nissler <mattias.nissler@gmx.de>
- * Copyright 2007, Stefano Brivio <stefano.brivio@polimi.it>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef RC80211_PID_H
-#define RC80211_PID_H
-
-/* Sampling period for measuring percentage of failed frames in ms. */
-#define RC_PID_INTERVAL                        125
-
-/* Exponential averaging smoothness (used for I part of PID controller) */
-#define RC_PID_SMOOTHING_SHIFT         3
-#define RC_PID_SMOOTHING               (1 << RC_PID_SMOOTHING_SHIFT)
-
-/* Sharpening factor (used for D part of PID controller) */
-#define RC_PID_SHARPENING_FACTOR       0
-#define RC_PID_SHARPENING_DURATION     0
-
-/* Fixed point arithmetic shifting amount. */
-#define RC_PID_ARITH_SHIFT             8
-
-/* Proportional PID component coefficient. */
-#define RC_PID_COEFF_P                 15
-/* Integral PID component coefficient. */
-#define RC_PID_COEFF_I                 9
-/* Derivative PID component coefficient. */
-#define RC_PID_COEFF_D                 15
-
-/* Target failed frames rate for the PID controller. NB: This effectively gives
- * maximum failed frames percentage we're willing to accept. If the wireless
- * link quality is good, the controller will fail to adjust failed frames
- * percentage to the target. This is intentional.
- */
-#define RC_PID_TARGET_PF               14
-
-/* Rate behaviour normalization quantity over time. */
-#define RC_PID_NORM_OFFSET             3
-
-/* Push high rates right after loading. */
-#define RC_PID_FAST_START              0
-
-/* Arithmetic right shift for positive and negative values for ISO C. */
-#define RC_PID_DO_ARITH_RIGHT_SHIFT(x, y) \
-       ((x) < 0 ? -((-(x)) >> (y)) : (x) >> (y))
-
-enum rc_pid_event_type {
-       RC_PID_EVENT_TYPE_TX_STATUS,
-       RC_PID_EVENT_TYPE_RATE_CHANGE,
-       RC_PID_EVENT_TYPE_TX_RATE,
-       RC_PID_EVENT_TYPE_PF_SAMPLE,
-};
-
-union rc_pid_event_data {
-       /* RC_PID_EVENT_TX_STATUS */
-       struct {
-               u32 flags;
-               struct ieee80211_tx_info tx_status;
-       };
-       /* RC_PID_EVENT_TYPE_RATE_CHANGE */
-       /* RC_PID_EVENT_TYPE_TX_RATE */
-       struct {
-               int index;
-               int rate;
-       };
-       /* RC_PID_EVENT_TYPE_PF_SAMPLE */
-       struct {
-               s32 pf_sample;
-               s32 prop_err;
-               s32 int_err;
-               s32 der_err;
-       };
-};
-
-struct rc_pid_event {
-       /* The time when the event occurred */
-       unsigned long timestamp;
-
-       /* Event ID number */
-       unsigned int id;
-
-       /* Type of event */
-       enum rc_pid_event_type type;
-
-       /* type specific data */
-       union rc_pid_event_data data;
-};
-
-/* Size of the event ring buffer. */
-#define RC_PID_EVENT_RING_SIZE 32
-
-struct rc_pid_event_buffer {
-       /* Counter that generates event IDs */
-       unsigned int ev_count;
-
-       /* Ring buffer of events */
-       struct rc_pid_event ring[RC_PID_EVENT_RING_SIZE];
-
-       /* Index to the entry in events_buf to be reused */
-       unsigned int next_entry;
-
-       /* Lock that guards against concurrent access to this buffer struct */
-       spinlock_t lock;
-
-       /* Wait queue for poll/select and blocking I/O */
-       wait_queue_head_t waitqueue;
-};
-
-struct rc_pid_events_file_info {
-       /* The event buffer we read */
-       struct rc_pid_event_buffer *events;
-
-       /* The entry we have should read next */
-       unsigned int next_entry;
-};
-
-/**
- * struct rc_pid_debugfs_entries - tunable parameters
- *
- * Algorithm parameters, tunable via debugfs.
- * @target: target percentage for failed frames
- * @sampling_period: error sampling interval in milliseconds
- * @coeff_p: absolute value of the proportional coefficient
- * @coeff_i: absolute value of the integral coefficient
- * @coeff_d: absolute value of the derivative coefficient
- * @smoothing_shift: absolute value of the integral smoothing factor (i.e.
- *     amount of smoothing introduced by the exponential moving average)
- * @sharpen_factor: absolute value of the derivative sharpening factor (i.e.
- *     amount of emphasis given to the derivative term after low activity
- *     events)
- * @sharpen_duration: duration of the sharpening effect after the detected low
- *     activity event, relative to sampling_period
- * @norm_offset: amount of normalization periodically performed on the learnt
- *     rate behaviour values (lower means we should trust more what we learnt
- *     about behaviour of rates, higher means we should trust more the natural
- *     ordering of rates)
- */
-struct rc_pid_debugfs_entries {
-       struct dentry *target;
-       struct dentry *sampling_period;
-       struct dentry *coeff_p;
-       struct dentry *coeff_i;
-       struct dentry *coeff_d;
-       struct dentry *smoothing_shift;
-       struct dentry *sharpen_factor;
-       struct dentry *sharpen_duration;
-       struct dentry *norm_offset;
-};
-
-void rate_control_pid_event_tx_status(struct rc_pid_event_buffer *buf,
-                                     struct ieee80211_tx_info *stat);
-
-void rate_control_pid_event_rate_change(struct rc_pid_event_buffer *buf,
-                                              int index, int rate);
-
-void rate_control_pid_event_tx_rate(struct rc_pid_event_buffer *buf,
-                                          int index, int rate);
-
-void rate_control_pid_event_pf_sample(struct rc_pid_event_buffer *buf,
-                                            s32 pf_sample, s32 prop_err,
-                                            s32 int_err, s32 der_err);
-
-void rate_control_pid_add_sta_debugfs(void *priv, void *priv_sta,
-                                            struct dentry *dir);
-
-void rate_control_pid_remove_sta_debugfs(void *priv, void *priv_sta);
-
-struct rc_pid_sta_info {
-       unsigned long last_change;
-       unsigned long last_sample;
-
-       u32 tx_num_failed;
-       u32 tx_num_xmit;
-
-       int txrate_idx;
-
-       /* Average failed frames percentage error (i.e. actual vs. target
-        * percentage), scaled by RC_PID_SMOOTHING. This value is computed
-        * using using an exponential weighted average technique:
-        *
-        *           (RC_PID_SMOOTHING - 1) * err_avg_old + err
-        * err_avg = ------------------------------------------
-        *                       RC_PID_SMOOTHING
-        *
-        * where err_avg is the new approximation, err_avg_old the previous one
-        * and err is the error w.r.t. to the current failed frames percentage
-        * sample. Note that the bigger RC_PID_SMOOTHING the more weight is
-        * given to the previous estimate, resulting in smoother behavior (i.e.
-        * corresponding to a longer integration window).
-        *
-        * For computation, we actually don't use the above formula, but this
-        * one:
-        *
-        * err_avg_scaled = err_avg_old_scaled - err_avg_old + err
-        *
-        * where:
-        *      err_avg_scaled = err * RC_PID_SMOOTHING
-        *      err_avg_old_scaled = err_avg_old * RC_PID_SMOOTHING
-        *
-        * This avoids floating point numbers and the per_failed_old value can
-        * easily be obtained by shifting per_failed_old_scaled right by
-        * RC_PID_SMOOTHING_SHIFT.
-        */
-       s32 err_avg_sc;
-
-       /* Last framed failes percentage sample. */
-       u32 last_pf;
-
-       /* Sharpening needed. */
-       u8 sharp_cnt;
-
-#ifdef CONFIG_MAC80211_DEBUGFS
-       /* Event buffer */
-       struct rc_pid_event_buffer events;
-
-       /* Events debugfs file entry */
-       struct dentry *events_entry;
-#endif
-};
-
-/* Algorithm parameters. We keep them on a per-algorithm approach, so they can
- * be tuned individually for each interface.
- */
-struct rc_pid_rateinfo {
-
-       /* Map sorted rates to rates in ieee80211_hw_mode. */
-       int index;
-
-       /* Map rates in ieee80211_hw_mode to sorted rates. */
-       int rev_index;
-
-       /* Did we do any measurement on this rate? */
-       bool valid;
-
-       /* Comparison with the lowest rate. */
-       int diff;
-};
-
-struct rc_pid_info {
-
-       /* The failed frames percentage target. */
-       unsigned int target;
-
-       /* Rate at which failed frames percentage is sampled in 0.001s. */
-       unsigned int sampling_period;
-
-       /* P, I and D coefficients. */
-       int coeff_p;
-       int coeff_i;
-       int coeff_d;
-
-       /* Exponential averaging shift. */
-       unsigned int smoothing_shift;
-
-       /* Sharpening factor and duration. */
-       unsigned int sharpen_factor;
-       unsigned int sharpen_duration;
-
-       /* Normalization offset. */
-       unsigned int norm_offset;
-
-       /* Rates information. */
-       struct rc_pid_rateinfo *rinfo;
-
-       /* Index of the last used rate. */
-       int oldrate;
-
-#ifdef CONFIG_MAC80211_DEBUGFS
-       /* Debugfs entries created for the parameters above. */
-       struct rc_pid_debugfs_entries dentries;
-#endif
-};
-
-#endif /* RC80211_PID_H */
diff --git a/net/mac80211/rc80211_pid_algo.c b/net/mac80211/rc80211_pid_algo.c
deleted file mode 100644 (file)
index d0da2a7..0000000
+++ /dev/null
@@ -1,478 +0,0 @@
-/*
- * Copyright 2002-2005, Instant802 Networks, Inc.
- * Copyright 2005, Devicescape Software, Inc.
- * Copyright 2007, Mattias Nissler <mattias.nissler@gmx.de>
- * Copyright 2007-2008, Stefano Brivio <stefano.brivio@polimi.it>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/netdevice.h>
-#include <linux/types.h>
-#include <linux/skbuff.h>
-#include <linux/debugfs.h>
-#include <linux/slab.h>
-#include <net/mac80211.h>
-#include "rate.h"
-#include "mesh.h"
-#include "rc80211_pid.h"
-
-
-/* This is an implementation of a TX rate control algorithm that uses a PID
- * controller. Given a target failed frames rate, the controller decides about
- * TX rate changes to meet the target failed frames rate.
- *
- * The controller basically computes the following:
- *
- * adj = CP * err + CI * err_avg + CD * (err - last_err) * (1 + sharpening)
- *
- * where
- *     adj     adjustment value that is used to switch TX rate (see below)
- *     err     current error: target vs. current failed frames percentage
- *     last_err        last error
- *     err_avg average (i.e. poor man's integral) of recent errors
- *     sharpening      non-zero when fast response is needed (i.e. right after
- *                     association or no frames sent for a long time), heading
- *                     to zero over time
- *     CP      Proportional coefficient
- *     CI      Integral coefficient
- *     CD      Derivative coefficient
- *
- * CP, CI, CD are subject to careful tuning.
- *
- * The integral component uses a exponential moving average approach instead of
- * an actual sliding window. The advantage is that we don't need to keep an
- * array of the last N error values and computation is easier.
- *
- * Once we have the adj value, we map it to a rate by means of a learning
- * algorithm. This algorithm keeps the state of the percentual failed frames
- * difference between rates. The behaviour of the lowest available rate is kept
- * as a reference value, and every time we switch between two rates, we compute
- * the difference between the failed frames each rate exhibited. By doing so,
- * we compare behaviours which different rates exhibited in adjacent timeslices,
- * thus the comparison is minimally affected by external conditions. This
- * difference gets propagated to the whole set of measurements, so that the
- * reference is always the same. Periodically, we normalize this set so that
- * recent events weigh the most. By comparing the adj value with this set, we
- * avoid pejorative switches to lower rates and allow for switches to higher
- * rates if they behaved well.
- *
- * Note that for the computations we use a fixed-point representation to avoid
- * floating point arithmetic. Hence, all values are shifted left by
- * RC_PID_ARITH_SHIFT.
- */
-
-
-/* Adjust the rate while ensuring that we won't switch to a lower rate if it
- * exhibited a worse failed frames behaviour and we'll choose the highest rate
- * whose failed frames behaviour is not worse than the one of the original rate
- * target. While at it, check that the new rate is valid. */
-static void rate_control_pid_adjust_rate(struct ieee80211_supported_band *sband,
-                                        struct ieee80211_sta *sta,
-                                        struct rc_pid_sta_info *spinfo, int adj,
-                                        struct rc_pid_rateinfo *rinfo)
-{
-       int cur_sorted, new_sorted, probe, tmp, n_bitrates, band;
-       int cur = spinfo->txrate_idx;
-
-       band = sband->band;
-       n_bitrates = sband->n_bitrates;
-
-       /* Map passed arguments to sorted values. */
-       cur_sorted = rinfo[cur].rev_index;
-       new_sorted = cur_sorted + adj;
-
-       /* Check limits. */
-       if (new_sorted < 0)
-               new_sorted = rinfo[0].rev_index;
-       else if (new_sorted >= n_bitrates)
-               new_sorted = rinfo[n_bitrates - 1].rev_index;
-
-       tmp = new_sorted;
-
-       if (adj < 0) {
-               /* Ensure that the rate decrease isn't disadvantageous. */
-               for (probe = cur_sorted; probe >= new_sorted; probe--)
-                       if (rinfo[probe].diff <= rinfo[cur_sorted].diff &&
-                           rate_supported(sta, band, rinfo[probe].index))
-                               tmp = probe;
-       } else {
-               /* Look for rate increase with zero (or below) cost. */
-               for (probe = new_sorted + 1; probe < n_bitrates; probe++)
-                       if (rinfo[probe].diff <= rinfo[new_sorted].diff &&
-                           rate_supported(sta, band, rinfo[probe].index))
-                               tmp = probe;
-       }
-
-       /* Fit the rate found to the nearest supported rate. */
-       do {
-               if (rate_supported(sta, band, rinfo[tmp].index)) {
-                       spinfo->txrate_idx = rinfo[tmp].index;
-                       break;
-               }
-               if (adj < 0)
-                       tmp--;
-               else
-                       tmp++;
-       } while (tmp < n_bitrates && tmp >= 0);
-
-#ifdef CONFIG_MAC80211_DEBUGFS
-       rate_control_pid_event_rate_change(&spinfo->events,
-               spinfo->txrate_idx,
-               sband->bitrates[spinfo->txrate_idx].bitrate);
-#endif
-}
-
-/* Normalize the failed frames per-rate differences. */
-static void rate_control_pid_normalize(struct rc_pid_info *pinfo, int l)
-{
-       int i, norm_offset = pinfo->norm_offset;
-       struct rc_pid_rateinfo *r = pinfo->rinfo;
-
-       if (r[0].diff > norm_offset)
-               r[0].diff -= norm_offset;
-       else if (r[0].diff < -norm_offset)
-               r[0].diff += norm_offset;
-       for (i = 0; i < l - 1; i++)
-               if (r[i + 1].diff > r[i].diff + norm_offset)
-                       r[i + 1].diff -= norm_offset;
-               else if (r[i + 1].diff <= r[i].diff)
-                       r[i + 1].diff += norm_offset;
-}
-
-static void rate_control_pid_sample(struct rc_pid_info *pinfo,
-                                   struct ieee80211_supported_band *sband,
-                                   struct ieee80211_sta *sta,
-                                   struct rc_pid_sta_info *spinfo)
-{
-       struct rc_pid_rateinfo *rinfo = pinfo->rinfo;
-       u32 pf;
-       s32 err_avg;
-       u32 err_prop;
-       u32 err_int;
-       u32 err_der;
-       int adj, i, j, tmp;
-       unsigned long period;
-
-       /* In case nothing happened during the previous control interval, turn
-        * the sharpening factor on. */
-       period = msecs_to_jiffies(pinfo->sampling_period);
-       if (jiffies - spinfo->last_sample > 2 * period)
-               spinfo->sharp_cnt = pinfo->sharpen_duration;
-
-       spinfo->last_sample = jiffies;
-
-       /* This should never happen, but in case, we assume the old sample is
-        * still a good measurement and copy it. */
-       if (unlikely(spinfo->tx_num_xmit == 0))
-               pf = spinfo->last_pf;
-       else
-               pf = spinfo->tx_num_failed * 100 / spinfo->tx_num_xmit;
-
-       spinfo->tx_num_xmit = 0;
-       spinfo->tx_num_failed = 0;
-
-       /* If we just switched rate, update the rate behaviour info. */
-       if (pinfo->oldrate != spinfo->txrate_idx) {
-
-               i = rinfo[pinfo->oldrate].rev_index;
-               j = rinfo[spinfo->txrate_idx].rev_index;
-
-               tmp = (pf - spinfo->last_pf);
-               tmp = RC_PID_DO_ARITH_RIGHT_SHIFT(tmp, RC_PID_ARITH_SHIFT);
-
-               rinfo[j].diff = rinfo[i].diff + tmp;
-               pinfo->oldrate = spinfo->txrate_idx;
-       }
-       rate_control_pid_normalize(pinfo, sband->n_bitrates);
-
-       /* Compute the proportional, integral and derivative errors. */
-       err_prop = (pinfo->target - pf) << RC_PID_ARITH_SHIFT;
-
-       err_avg = spinfo->err_avg_sc >> pinfo->smoothing_shift;
-       spinfo->err_avg_sc = spinfo->err_avg_sc - err_avg + err_prop;
-       err_int = spinfo->err_avg_sc >> pinfo->smoothing_shift;
-
-       err_der = (pf - spinfo->last_pf) *
-                 (1 + pinfo->sharpen_factor * spinfo->sharp_cnt);
-       spinfo->last_pf = pf;
-       if (spinfo->sharp_cnt)
-                       spinfo->sharp_cnt--;
-
-#ifdef CONFIG_MAC80211_DEBUGFS
-       rate_control_pid_event_pf_sample(&spinfo->events, pf, err_prop, err_int,
-                                        err_der);
-#endif
-
-       /* Compute the controller output. */
-       adj = (err_prop * pinfo->coeff_p + err_int * pinfo->coeff_i
-             + err_der * pinfo->coeff_d);
-       adj = RC_PID_DO_ARITH_RIGHT_SHIFT(adj, 2 * RC_PID_ARITH_SHIFT);
-
-       /* Change rate. */
-       if (adj)
-               rate_control_pid_adjust_rate(sband, sta, spinfo, adj, rinfo);
-}
-
-static void rate_control_pid_tx_status(void *priv, struct ieee80211_supported_band *sband,
-                                      struct ieee80211_sta *sta, void *priv_sta,
-                                      struct sk_buff *skb)
-{
-       struct rc_pid_info *pinfo = priv;
-       struct rc_pid_sta_info *spinfo = priv_sta;
-       unsigned long period;
-       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-
-       if (!spinfo)
-               return;
-
-       /* Ignore all frames that were sent with a different rate than the rate
-        * we currently advise mac80211 to use. */
-       if (info->status.rates[0].idx != spinfo->txrate_idx)
-               return;
-
-       spinfo->tx_num_xmit++;
-
-#ifdef CONFIG_MAC80211_DEBUGFS
-       rate_control_pid_event_tx_status(&spinfo->events, info);
-#endif
-
-       /* We count frames that totally failed to be transmitted as two bad
-        * frames, those that made it out but had some retries as one good and
-        * one bad frame. */
-       if (!(info->flags & IEEE80211_TX_STAT_ACK)) {
-               spinfo->tx_num_failed += 2;
-               spinfo->tx_num_xmit++;
-       } else if (info->status.rates[0].count > 1) {
-               spinfo->tx_num_failed++;
-               spinfo->tx_num_xmit++;
-       }
-
-       /* Update PID controller state. */
-       period = msecs_to_jiffies(pinfo->sampling_period);
-       if (time_after(jiffies, spinfo->last_sample + period))
-               rate_control_pid_sample(pinfo, sband, sta, spinfo);
-}
-
-static void
-rate_control_pid_get_rate(void *priv, struct ieee80211_sta *sta,
-                         void *priv_sta,
-                         struct ieee80211_tx_rate_control *txrc)
-{
-       struct sk_buff *skb = txrc->skb;
-       struct ieee80211_supported_band *sband = txrc->sband;
-       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-       struct rc_pid_sta_info *spinfo = priv_sta;
-       int rateidx;
-
-       if (txrc->rts)
-               info->control.rates[0].count =
-                       txrc->hw->conf.long_frame_max_tx_count;
-       else
-               info->control.rates[0].count =
-                       txrc->hw->conf.short_frame_max_tx_count;
-
-       /* Send management frames and NO_ACK data using lowest rate. */
-       if (rate_control_send_low(sta, priv_sta, txrc))
-               return;
-
-       rateidx = spinfo->txrate_idx;
-
-       if (rateidx >= sband->n_bitrates)
-               rateidx = sband->n_bitrates - 1;
-
-       info->control.rates[0].idx = rateidx;
-
-#ifdef CONFIG_MAC80211_DEBUGFS
-       rate_control_pid_event_tx_rate(&spinfo->events,
-               rateidx, sband->bitrates[rateidx].bitrate);
-#endif
-}
-
-static void
-rate_control_pid_rate_init(void *priv, struct ieee80211_supported_band *sband,
-                          struct cfg80211_chan_def *chandef,
-                          struct ieee80211_sta *sta, void *priv_sta)
-{
-       struct rc_pid_sta_info *spinfo = priv_sta;
-       struct rc_pid_info *pinfo = priv;
-       struct rc_pid_rateinfo *rinfo = pinfo->rinfo;
-       int i, j, tmp;
-       bool s;
-
-       /* TODO: This routine should consider using RSSI from previous packets
-        * as we need to have IEEE 802.1X auth succeed immediately after assoc..
-        * Until that method is implemented, we will use the lowest supported
-        * rate as a workaround. */
-
-       /* Sort the rates. This is optimized for the most common case (i.e.
-        * almost-sorted CCK+OFDM rates). Kind of bubble-sort with reversed
-        * mapping too. */
-       for (i = 0; i < sband->n_bitrates; i++) {
-               rinfo[i].index = i;
-               rinfo[i].rev_index = i;
-               if (RC_PID_FAST_START)
-                       rinfo[i].diff = 0;
-               else
-                       rinfo[i].diff = i * pinfo->norm_offset;
-       }
-       for (i = 1; i < sband->n_bitrates; i++) {
-               s = false;
-               for (j = 0; j < sband->n_bitrates - i; j++)
-                       if (unlikely(sband->bitrates[rinfo[j].index].bitrate >
-                                    sband->bitrates[rinfo[j + 1].index].bitrate)) {
-                               tmp = rinfo[j].index;
-                               rinfo[j].index = rinfo[j + 1].index;
-                               rinfo[j + 1].index = tmp;
-                               rinfo[rinfo[j].index].rev_index = j;
-                               rinfo[rinfo[j + 1].index].rev_index = j + 1;
-                               s = true;
-                       }
-               if (!s)
-                       break;
-       }
-
-       spinfo->txrate_idx = rate_lowest_index(sband, sta);
-}
-
-static void *rate_control_pid_alloc(struct ieee80211_hw *hw,
-                                   struct dentry *debugfsdir)
-{
-       struct rc_pid_info *pinfo;
-       struct rc_pid_rateinfo *rinfo;
-       struct ieee80211_supported_band *sband;
-       int i, max_rates = 0;
-#ifdef CONFIG_MAC80211_DEBUGFS
-       struct rc_pid_debugfs_entries *de;
-#endif
-
-       pinfo = kmalloc(sizeof(*pinfo), GFP_ATOMIC);
-       if (!pinfo)
-               return NULL;
-
-       for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
-               sband = hw->wiphy->bands[i];
-               if (sband && sband->n_bitrates > max_rates)
-                       max_rates = sband->n_bitrates;
-       }
-
-       rinfo = kmalloc(sizeof(*rinfo) * max_rates, GFP_ATOMIC);
-       if (!rinfo) {
-               kfree(pinfo);
-               return NULL;
-       }
-
-       pinfo->target = RC_PID_TARGET_PF;
-       pinfo->sampling_period = RC_PID_INTERVAL;
-       pinfo->coeff_p = RC_PID_COEFF_P;
-       pinfo->coeff_i = RC_PID_COEFF_I;
-       pinfo->coeff_d = RC_PID_COEFF_D;
-       pinfo->smoothing_shift = RC_PID_SMOOTHING_SHIFT;
-       pinfo->sharpen_factor = RC_PID_SHARPENING_FACTOR;
-       pinfo->sharpen_duration = RC_PID_SHARPENING_DURATION;
-       pinfo->norm_offset = RC_PID_NORM_OFFSET;
-       pinfo->rinfo = rinfo;
-       pinfo->oldrate = 0;
-
-#ifdef CONFIG_MAC80211_DEBUGFS
-       de = &pinfo->dentries;
-       de->target = debugfs_create_u32("target_pf", S_IRUSR | S_IWUSR,
-                                       debugfsdir, &pinfo->target);
-       de->sampling_period = debugfs_create_u32("sampling_period",
-                                                S_IRUSR | S_IWUSR, debugfsdir,
-                                                &pinfo->sampling_period);
-       de->coeff_p = debugfs_create_u32("coeff_p", S_IRUSR | S_IWUSR,
-                                        debugfsdir, (u32 *)&pinfo->coeff_p);
-       de->coeff_i = debugfs_create_u32("coeff_i", S_IRUSR | S_IWUSR,
-                                        debugfsdir, (u32 *)&pinfo->coeff_i);
-       de->coeff_d = debugfs_create_u32("coeff_d", S_IRUSR | S_IWUSR,
-                                        debugfsdir, (u32 *)&pinfo->coeff_d);
-       de->smoothing_shift = debugfs_create_u32("smoothing_shift",
-                                                S_IRUSR | S_IWUSR, debugfsdir,
-                                                &pinfo->smoothing_shift);
-       de->sharpen_factor = debugfs_create_u32("sharpen_factor",
-                                              S_IRUSR | S_IWUSR, debugfsdir,
-                                              &pinfo->sharpen_factor);
-       de->sharpen_duration = debugfs_create_u32("sharpen_duration",
-                                                 S_IRUSR | S_IWUSR, debugfsdir,
-                                                 &pinfo->sharpen_duration);
-       de->norm_offset = debugfs_create_u32("norm_offset",
-                                            S_IRUSR | S_IWUSR, debugfsdir,
-                                            &pinfo->norm_offset);
-#endif
-
-       return pinfo;
-}
-
-static void rate_control_pid_free(void *priv)
-{
-       struct rc_pid_info *pinfo = priv;
-#ifdef CONFIG_MAC80211_DEBUGFS
-       struct rc_pid_debugfs_entries *de = &pinfo->dentries;
-
-       debugfs_remove(de->norm_offset);
-       debugfs_remove(de->sharpen_duration);
-       debugfs_remove(de->sharpen_factor);
-       debugfs_remove(de->smoothing_shift);
-       debugfs_remove(de->coeff_d);
-       debugfs_remove(de->coeff_i);
-       debugfs_remove(de->coeff_p);
-       debugfs_remove(de->sampling_period);
-       debugfs_remove(de->target);
-#endif
-
-       kfree(pinfo->rinfo);
-       kfree(pinfo);
-}
-
-static void *rate_control_pid_alloc_sta(void *priv, struct ieee80211_sta *sta,
-                                       gfp_t gfp)
-{
-       struct rc_pid_sta_info *spinfo;
-
-       spinfo = kzalloc(sizeof(*spinfo), gfp);
-       if (spinfo == NULL)
-               return NULL;
-
-       spinfo->last_sample = jiffies;
-
-#ifdef CONFIG_MAC80211_DEBUGFS
-       spin_lock_init(&spinfo->events.lock);
-       init_waitqueue_head(&spinfo->events.waitqueue);
-#endif
-
-       return spinfo;
-}
-
-static void rate_control_pid_free_sta(void *priv, struct ieee80211_sta *sta,
-                                     void *priv_sta)
-{
-       kfree(priv_sta);
-}
-
-static const struct rate_control_ops mac80211_rcpid = {
-       .name = "pid",
-       .tx_status = rate_control_pid_tx_status,
-       .get_rate = rate_control_pid_get_rate,
-       .rate_init = rate_control_pid_rate_init,
-       .alloc = rate_control_pid_alloc,
-       .free = rate_control_pid_free,
-       .alloc_sta = rate_control_pid_alloc_sta,
-       .free_sta = rate_control_pid_free_sta,
-#ifdef CONFIG_MAC80211_DEBUGFS
-       .add_sta_debugfs = rate_control_pid_add_sta_debugfs,
-       .remove_sta_debugfs = rate_control_pid_remove_sta_debugfs,
-#endif
-};
-
-int __init rc80211_pid_init(void)
-{
-       return ieee80211_rate_control_register(&mac80211_rcpid);
-}
-
-void rc80211_pid_exit(void)
-{
-       ieee80211_rate_control_unregister(&mac80211_rcpid);
-}
diff --git a/net/mac80211/rc80211_pid_debugfs.c b/net/mac80211/rc80211_pid_debugfs.c
deleted file mode 100644 (file)
index 6ff1346..0000000
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- * Copyright 2007, Mattias Nissler <mattias.nissler@gmx.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/sched.h>
-#include <linux/spinlock.h>
-#include <linux/poll.h>
-#include <linux/netdevice.h>
-#include <linux/types.h>
-#include <linux/skbuff.h>
-#include <linux/slab.h>
-#include <linux/export.h>
-
-#include <net/mac80211.h>
-#include "rate.h"
-
-#include "rc80211_pid.h"
-
-static void rate_control_pid_event(struct rc_pid_event_buffer *buf,
-                                  enum rc_pid_event_type type,
-                                  union rc_pid_event_data *data)
-{
-       struct rc_pid_event *ev;
-       unsigned long status;
-
-       spin_lock_irqsave(&buf->lock, status);
-       ev = &(buf->ring[buf->next_entry]);
-       buf->next_entry = (buf->next_entry + 1) % RC_PID_EVENT_RING_SIZE;
-
-       ev->timestamp = jiffies;
-       ev->id = buf->ev_count++;
-       ev->type = type;
-       ev->data = *data;
-
-       spin_unlock_irqrestore(&buf->lock, status);
-
-       wake_up_all(&buf->waitqueue);
-}
-
-void rate_control_pid_event_tx_status(struct rc_pid_event_buffer *buf,
-                                     struct ieee80211_tx_info *stat)
-{
-       union rc_pid_event_data evd;
-
-       evd.flags = stat->flags;
-       memcpy(&evd.tx_status, stat, sizeof(struct ieee80211_tx_info));
-       rate_control_pid_event(buf, RC_PID_EVENT_TYPE_TX_STATUS, &evd);
-}
-
-void rate_control_pid_event_rate_change(struct rc_pid_event_buffer *buf,
-                                              int index, int rate)
-{
-       union rc_pid_event_data evd;
-
-       evd.index = index;
-       evd.rate = rate;
-       rate_control_pid_event(buf, RC_PID_EVENT_TYPE_RATE_CHANGE, &evd);
-}
-
-void rate_control_pid_event_tx_rate(struct rc_pid_event_buffer *buf,
-                                          int index, int rate)
-{
-       union rc_pid_event_data evd;
-
-       evd.index = index;
-       evd.rate = rate;
-       rate_control_pid_event(buf, RC_PID_EVENT_TYPE_TX_RATE, &evd);
-}
-
-void rate_control_pid_event_pf_sample(struct rc_pid_event_buffer *buf,
-                                            s32 pf_sample, s32 prop_err,
-                                            s32 int_err, s32 der_err)
-{
-       union rc_pid_event_data evd;
-
-       evd.pf_sample = pf_sample;
-       evd.prop_err = prop_err;
-       evd.int_err = int_err;
-       evd.der_err = der_err;
-       rate_control_pid_event(buf, RC_PID_EVENT_TYPE_PF_SAMPLE, &evd);
-}
-
-static int rate_control_pid_events_open(struct inode *inode, struct file *file)
-{
-       struct rc_pid_sta_info *sinfo = inode->i_private;
-       struct rc_pid_event_buffer *events = &sinfo->events;
-       struct rc_pid_events_file_info *file_info;
-       unsigned long status;
-
-       /* Allocate a state struct */
-       file_info = kmalloc(sizeof(*file_info), GFP_KERNEL);
-       if (file_info == NULL)
-               return -ENOMEM;
-
-       spin_lock_irqsave(&events->lock, status);
-
-       file_info->next_entry = events->next_entry;
-       file_info->events = events;
-
-       spin_unlock_irqrestore(&events->lock, status);
-
-       file->private_data = file_info;
-
-       return 0;
-}
-
-static int rate_control_pid_events_release(struct inode *inode,
-                                          struct file *file)
-{
-       struct rc_pid_events_file_info *file_info = file->private_data;
-
-       kfree(file_info);
-
-       return 0;
-}
-
-static unsigned int rate_control_pid_events_poll(struct file *file,
-                                                poll_table *wait)
-{
-       struct rc_pid_events_file_info *file_info = file->private_data;
-
-       poll_wait(file, &file_info->events->waitqueue, wait);
-
-       return POLLIN | POLLRDNORM;
-}
-
-#define RC_PID_PRINT_BUF_SIZE 64
-
-static ssize_t rate_control_pid_events_read(struct file *file, char __user *buf,
-                                           size_t length, loff_t *offset)
-{
-       struct rc_pid_events_file_info *file_info = file->private_data;
-       struct rc_pid_event_buffer *events = file_info->events;
-       struct rc_pid_event *ev;
-       char pb[RC_PID_PRINT_BUF_SIZE];
-       int ret;
-       int p;
-       unsigned long status;
-
-       /* Check if there is something to read. */
-       if (events->next_entry == file_info->next_entry) {
-               if (file->f_flags & O_NONBLOCK)
-                       return -EAGAIN;
-
-               /* Wait */
-               ret = wait_event_interruptible(events->waitqueue,
-                               events->next_entry != file_info->next_entry);
-
-               if (ret)
-                       return ret;
-       }
-
-       /* Write out one event per call. I don't care whether it's a little
-        * inefficient, this is debugging code anyway. */
-       spin_lock_irqsave(&events->lock, status);
-
-       /* Get an event */
-       ev = &(events->ring[file_info->next_entry]);
-       file_info->next_entry = (file_info->next_entry + 1) %
-                               RC_PID_EVENT_RING_SIZE;
-
-       /* Print information about the event. Note that userspace needs to
-        * provide large enough buffers. */
-       length = length < RC_PID_PRINT_BUF_SIZE ?
-                length : RC_PID_PRINT_BUF_SIZE;
-       p = scnprintf(pb, length, "%u %lu ", ev->id, ev->timestamp);
-       switch (ev->type) {
-       case RC_PID_EVENT_TYPE_TX_STATUS:
-               p += scnprintf(pb + p, length - p, "tx_status %u %u",
-                              !(ev->data.flags & IEEE80211_TX_STAT_ACK),
-                              ev->data.tx_status.status.rates[0].idx);
-               break;
-       case RC_PID_EVENT_TYPE_RATE_CHANGE:
-               p += scnprintf(pb + p, length - p, "rate_change %d %d",
-                              ev->data.index, ev->data.rate);
-               break;
-       case RC_PID_EVENT_TYPE_TX_RATE:
-               p += scnprintf(pb + p, length - p, "tx_rate %d %d",
-                              ev->data.index, ev->data.rate);
-               break;
-       case RC_PID_EVENT_TYPE_PF_SAMPLE:
-               p += scnprintf(pb + p, length - p,
-                              "pf_sample %d %d %d %d",
-                              ev->data.pf_sample, ev->data.prop_err,
-                              ev->data.int_err, ev->data.der_err);
-               break;
-       }
-       p += scnprintf(pb + p, length - p, "\n");
-
-       spin_unlock_irqrestore(&events->lock, status);
-
-       if (copy_to_user(buf, pb, p))
-               return -EFAULT;
-
-       return p;
-}
-
-#undef RC_PID_PRINT_BUF_SIZE
-
-static const struct file_operations rc_pid_fop_events = {
-       .owner = THIS_MODULE,
-       .read = rate_control_pid_events_read,
-       .poll = rate_control_pid_events_poll,
-       .open = rate_control_pid_events_open,
-       .release = rate_control_pid_events_release,
-       .llseek = noop_llseek,
-};
-
-void rate_control_pid_add_sta_debugfs(void *priv, void *priv_sta,
-                                            struct dentry *dir)
-{
-       struct rc_pid_sta_info *spinfo = priv_sta;
-
-       spinfo->events_entry = debugfs_create_file("rc_pid_events", S_IRUGO,
-                                                  dir, spinfo,
-                                                  &rc_pid_fop_events);
-}
-
-void rate_control_pid_remove_sta_debugfs(void *priv, void *priv_sta)
-{
-       struct rc_pid_sta_info *spinfo = priv_sta;
-
-       debugfs_remove(spinfo->events_entry);
-}
index 394e201..5f572be 100644 (file)
@@ -1107,6 +1107,8 @@ static void sta_ps_end(struct sta_info *sta)
                return;
        }
 
+       set_sta_flag(sta, WLAN_STA_PS_DELIVER);
+       clear_sta_flag(sta, WLAN_STA_PS_STA);
        ieee80211_sta_ps_deliver_wakeup(sta);
 }
 
index f40661e..a0a9381 100644 (file)
@@ -235,38 +235,51 @@ static bool ieee80211_prep_hw_scan(struct ieee80211_local *local)
 {
        struct cfg80211_scan_request *req = local->scan_req;
        struct cfg80211_chan_def chandef;
-       enum ieee80211_band band;
+       u8 bands_used = 0;
        int i, ielen, n_chans;
 
        if (test_bit(SCAN_HW_CANCELLED, &local->scanning))
                return false;
 
-       do {
-               if (local->hw_scan_band == IEEE80211_NUM_BANDS)
-                       return false;
-
-               band = local->hw_scan_band;
-               n_chans = 0;
+       if (local->hw.flags & IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS) {
                for (i = 0; i < req->n_channels; i++) {
-                       if (req->channels[i]->band == band) {
-                               local->hw_scan_req->channels[n_chans] =
+                       local->hw_scan_req->req.channels[i] = req->channels[i];
+                       bands_used |= BIT(req->channels[i]->band);
+               }
+
+               n_chans = req->n_channels;
+       } else {
+               do {
+                       if (local->hw_scan_band == IEEE80211_NUM_BANDS)
+                               return false;
+
+                       n_chans = 0;
+
+                       for (i = 0; i < req->n_channels; i++) {
+                               if (req->channels[i]->band !=
+                                   local->hw_scan_band)
+                                       continue;
+                               local->hw_scan_req->req.channels[n_chans] =
                                                        req->channels[i];
                                n_chans++;
+                               bands_used |= BIT(req->channels[i]->band);
                        }
-               }
 
-               local->hw_scan_band++;
-       } while (!n_chans);
+                       local->hw_scan_band++;
+               } while (!n_chans);
+       }
 
-       local->hw_scan_req->n_channels = n_chans;
+       local->hw_scan_req->req.n_channels = n_chans;
        ieee80211_prepare_scan_chandef(&chandef, req->scan_width);
 
-       ielen = ieee80211_build_preq_ies(local, (u8 *)local->hw_scan_req->ie,
+       ielen = ieee80211_build_preq_ies(local,
+                                        (u8 *)local->hw_scan_req->req.ie,
                                         local->hw_scan_ies_bufsize,
-                                        req->ie, req->ie_len, band,
-                                        req->rates[band], &chandef);
-       local->hw_scan_req->ie_len = ielen;
-       local->hw_scan_req->no_cck = req->no_cck;
+                                        &local->hw_scan_req->ies,
+                                        req->ie, req->ie_len,
+                                        bands_used, req->rates, &chandef);
+       local->hw_scan_req->req.ie_len = ielen;
+       local->hw_scan_req->req.no_cck = req->no_cck;
 
        return true;
 }
@@ -291,7 +304,9 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
        if (WARN_ON(!local->scan_req))
                return;
 
-       if (hw_scan && !aborted && ieee80211_prep_hw_scan(local)) {
+       if (hw_scan && !aborted &&
+           !(local->hw.flags & IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS) &&
+           ieee80211_prep_hw_scan(local)) {
                int rc;
 
                rc = drv_hw_scan(local,
@@ -473,6 +488,21 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
                u8 *ies;
 
                local->hw_scan_ies_bufsize = local->scan_ies_len + req->ie_len;
+
+               if (local->hw.flags & IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS) {
+                       int i, n_bands = 0;
+                       u8 bands_counted = 0;
+
+                       for (i = 0; i < req->n_channels; i++) {
+                               if (bands_counted & BIT(req->channels[i]->band))
+                                       continue;
+                               bands_counted |= BIT(req->channels[i]->band);
+                               n_bands++;
+                       }
+
+                       local->hw_scan_ies_bufsize *= n_bands;
+               }
+
                local->hw_scan_req = kmalloc(
                                sizeof(*local->hw_scan_req) +
                                req->n_channels * sizeof(req->channels[0]) +
@@ -480,13 +510,13 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
                if (!local->hw_scan_req)
                        return -ENOMEM;
 
-               local->hw_scan_req->ssids = req->ssids;
-               local->hw_scan_req->n_ssids = req->n_ssids;
+               local->hw_scan_req->req.ssids = req->ssids;
+               local->hw_scan_req->req.n_ssids = req->n_ssids;
                ies = (u8 *)local->hw_scan_req +
                        sizeof(*local->hw_scan_req) +
                        req->n_channels * sizeof(req->channels[0]);
-               local->hw_scan_req->ie = ies;
-               local->hw_scan_req->flags = req->flags;
+               local->hw_scan_req->req.ie = ies;
+               local->hw_scan_req->req.flags = req->flags;
 
                local->hw_scan_band = 0;
 
@@ -973,9 +1003,13 @@ int __ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
                                        struct cfg80211_sched_scan_request *req)
 {
        struct ieee80211_local *local = sdata->local;
-       struct ieee80211_sched_scan_ies sched_scan_ies = {};
+       struct ieee80211_scan_ies sched_scan_ies = {};
        struct cfg80211_chan_def chandef;
-       int ret, i, iebufsz;
+       int ret, i, iebufsz, num_bands = 0;
+       u32 rate_masks[IEEE80211_NUM_BANDS] = {};
+       u8 bands_used = 0;
+       u8 *ie;
+       size_t len;
 
        iebufsz = local->scan_ies_len + req->ie_len;
 
@@ -985,33 +1019,35 @@ int __ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
                return -ENOTSUPP;
 
        for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
-               if (!local->hw.wiphy->bands[i])
-                       continue;
-
-               sched_scan_ies.ie[i] = kzalloc(iebufsz, GFP_KERNEL);
-               if (!sched_scan_ies.ie[i]) {
-                       ret = -ENOMEM;
-                       goto out_free;
+               if (local->hw.wiphy->bands[i]) {
+                       bands_used |= BIT(i);
+                       rate_masks[i] = (u32) -1;
+                       num_bands++;
                }
+       }
 
-               ieee80211_prepare_scan_chandef(&chandef, req->scan_width);
-
-               sched_scan_ies.len[i] =
-                       ieee80211_build_preq_ies(local, sched_scan_ies.ie[i],
-                                                iebufsz, req->ie, req->ie_len,
-                                                i, (u32) -1, &chandef);
+       ie = kzalloc(num_bands * iebufsz, GFP_KERNEL);
+       if (!ie) {
+               ret = -ENOMEM;
+               goto out;
        }
 
+       ieee80211_prepare_scan_chandef(&chandef, req->scan_width);
+
+       len = ieee80211_build_preq_ies(local, ie, num_bands * iebufsz,
+                                      &sched_scan_ies, req->ie,
+                                      req->ie_len, bands_used,
+                                      rate_masks, &chandef);
+
        ret = drv_sched_scan_start(local, sdata, req, &sched_scan_ies);
        if (ret == 0) {
                rcu_assign_pointer(local->sched_scan_sdata, sdata);
                local->sched_scan_req = req;
        }
 
-out_free:
-       while (i > 0)
-               kfree(sched_scan_ies.ie[--i]);
+       kfree(ie);
 
+out:
        if (ret) {
                /* Clean in case of failure after HW restart or upon resume. */
                RCU_INIT_POINTER(local->sched_scan_sdata, NULL);
index a9b46d8..f41177f 100644 (file)
@@ -100,7 +100,8 @@ static void __cleanup_single_sta(struct sta_info *sta)
        struct ps_data *ps;
 
        if (test_sta_flag(sta, WLAN_STA_PS_STA) ||
-           test_sta_flag(sta, WLAN_STA_PS_DRIVER)) {
+           test_sta_flag(sta, WLAN_STA_PS_DRIVER) ||
+           test_sta_flag(sta, WLAN_STA_PS_DELIVER)) {
                if (sta->sdata->vif.type == NL80211_IFTYPE_AP ||
                    sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
                        ps = &sdata->bss->ps;
@@ -111,6 +112,7 @@ static void __cleanup_single_sta(struct sta_info *sta)
 
                clear_sta_flag(sta, WLAN_STA_PS_STA);
                clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
+               clear_sta_flag(sta, WLAN_STA_PS_DELIVER);
 
                atomic_dec(&ps->num_sta_ps);
                sta_info_recalc_tim(sta);
@@ -125,7 +127,7 @@ static void __cleanup_single_sta(struct sta_info *sta)
        if (ieee80211_vif_is_mesh(&sdata->vif))
                mesh_sta_cleanup(sta);
 
-       cancel_work_sync(&sta->drv_unblock_wk);
+       cancel_work_sync(&sta->drv_deliver_wk);
 
        /*
         * Destroy aggregation state here. It would be nice to wait for the
@@ -253,33 +255,23 @@ static void sta_info_hash_add(struct ieee80211_local *local,
        rcu_assign_pointer(local->sta_hash[STA_HASH(sta->sta.addr)], sta);
 }
 
-static void sta_unblock(struct work_struct *wk)
+static void sta_deliver_ps_frames(struct work_struct *wk)
 {
        struct sta_info *sta;
 
-       sta = container_of(wk, struct sta_info, drv_unblock_wk);
+       sta = container_of(wk, struct sta_info, drv_deliver_wk);
 
        if (sta->dead)
                return;
 
-       if (!test_sta_flag(sta, WLAN_STA_PS_STA)) {
-               local_bh_disable();
+       local_bh_disable();
+       if (!test_sta_flag(sta, WLAN_STA_PS_STA))
                ieee80211_sta_ps_deliver_wakeup(sta);
-               local_bh_enable();
-       } else if (test_and_clear_sta_flag(sta, WLAN_STA_PSPOLL)) {
-               clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
-
-               local_bh_disable();
+       else if (test_and_clear_sta_flag(sta, WLAN_STA_PSPOLL))
                ieee80211_sta_ps_deliver_poll_response(sta);
-               local_bh_enable();
-       } else if (test_and_clear_sta_flag(sta, WLAN_STA_UAPSD)) {
-               clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
-
-               local_bh_disable();
+       else if (test_and_clear_sta_flag(sta, WLAN_STA_UAPSD))
                ieee80211_sta_ps_deliver_uapsd(sta);
-               local_bh_enable();
-       } else
-               clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
+       local_bh_enable();
 }
 
 static int sta_prepare_rate_control(struct ieee80211_local *local,
@@ -341,7 +333,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
 
        spin_lock_init(&sta->lock);
        spin_lock_init(&sta->ps_lock);
-       INIT_WORK(&sta->drv_unblock_wk, sta_unblock);
+       INIT_WORK(&sta->drv_deliver_wk, sta_deliver_ps_frames);
        INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work);
        mutex_init(&sta->ampdu_mlme.mtx);
 #ifdef CONFIG_MAC80211_MESH
@@ -358,7 +350,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
 
        sta->sta_state = IEEE80211_STA_NONE;
 
-       do_posix_clock_monotonic_gettime(&uptime);
+       ktime_get_ts(&uptime);
        sta->last_connected = uptime.tv_sec;
        ewma_init(&sta->avg_signal, 1024, 8);
        for (i = 0; i < ARRAY_SIZE(sta->chain_signal_avg); i++)
@@ -1141,8 +1133,15 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
        }
 
        ieee80211_add_pending_skbs(local, &pending);
-       clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
-       clear_sta_flag(sta, WLAN_STA_PS_STA);
+
+       /* now we're no longer in the deliver code */
+       clear_sta_flag(sta, WLAN_STA_PS_DELIVER);
+
+       /* The station might have polled and then woken up before we responded,
+        * so clear these flags now to avoid them sticking around.
+        */
+       clear_sta_flag(sta, WLAN_STA_PSPOLL);
+       clear_sta_flag(sta, WLAN_STA_UAPSD);
        spin_unlock(&sta->ps_lock);
 
        atomic_dec(&ps->num_sta_ps);
@@ -1543,10 +1542,26 @@ void ieee80211_sta_block_awake(struct ieee80211_hw *hw,
 
        trace_api_sta_block_awake(sta->local, pubsta, block);
 
-       if (block)
+       if (block) {
                set_sta_flag(sta, WLAN_STA_PS_DRIVER);
-       else if (test_sta_flag(sta, WLAN_STA_PS_DRIVER))
-               ieee80211_queue_work(hw, &sta->drv_unblock_wk);
+               return;
+       }
+
+       if (!test_sta_flag(sta, WLAN_STA_PS_DRIVER))
+               return;
+
+       if (!test_sta_flag(sta, WLAN_STA_PS_STA)) {
+               set_sta_flag(sta, WLAN_STA_PS_DELIVER);
+               clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
+               ieee80211_queue_work(hw, &sta->drv_deliver_wk);
+       } else if (test_sta_flag(sta, WLAN_STA_PSPOLL) ||
+                  test_sta_flag(sta, WLAN_STA_UAPSD)) {
+               /* must be asleep in this case */
+               clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
+               ieee80211_queue_work(hw, &sta->drv_deliver_wk);
+       } else {
+               clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
+       }
 }
 EXPORT_SYMBOL(ieee80211_sta_block_awake);
 
@@ -1704,3 +1719,137 @@ u8 sta_info_tx_streams(struct sta_info *sta)
        return ((ht_cap->mcs.tx_params & IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK)
                        >> IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT) + 1;
 }
+
+void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
+{
+       struct ieee80211_sub_if_data *sdata = sta->sdata;
+       struct ieee80211_local *local = sdata->local;
+       struct rate_control_ref *ref = local->rate_ctrl;
+       struct timespec uptime;
+       u64 packets = 0;
+       u32 thr = 0;
+       int i, ac;
+
+       sinfo->generation = sdata->local->sta_generation;
+
+       sinfo->filled = STATION_INFO_INACTIVE_TIME |
+                       STATION_INFO_RX_BYTES64 |
+                       STATION_INFO_TX_BYTES64 |
+                       STATION_INFO_RX_PACKETS |
+                       STATION_INFO_TX_PACKETS |
+                       STATION_INFO_TX_RETRIES |
+                       STATION_INFO_TX_FAILED |
+                       STATION_INFO_TX_BITRATE |
+                       STATION_INFO_RX_BITRATE |
+                       STATION_INFO_RX_DROP_MISC |
+                       STATION_INFO_BSS_PARAM |
+                       STATION_INFO_CONNECTED_TIME |
+                       STATION_INFO_STA_FLAGS |
+                       STATION_INFO_BEACON_LOSS_COUNT;
+
+       ktime_get_ts(&uptime);
+       sinfo->connected_time = uptime.tv_sec - sta->last_connected;
+
+       sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx);
+       sinfo->tx_bytes = 0;
+       for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+               sinfo->tx_bytes += sta->tx_bytes[ac];
+               packets += sta->tx_packets[ac];
+       }
+       sinfo->tx_packets = packets;
+       sinfo->rx_bytes = sta->rx_bytes;
+       sinfo->rx_packets = sta->rx_packets;
+       sinfo->tx_retries = sta->tx_retry_count;
+       sinfo->tx_failed = sta->tx_retry_failed;
+       sinfo->rx_dropped_misc = sta->rx_dropped;
+       sinfo->beacon_loss_count = sta->beacon_loss_count;
+
+       if ((sta->local->hw.flags & IEEE80211_HW_SIGNAL_DBM) ||
+           (sta->local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC)) {
+               sinfo->filled |= STATION_INFO_SIGNAL | STATION_INFO_SIGNAL_AVG;
+               if (!local->ops->get_rssi ||
+                   drv_get_rssi(local, sdata, &sta->sta, &sinfo->signal))
+                       sinfo->signal = (s8)sta->last_signal;
+               sinfo->signal_avg = (s8) -ewma_read(&sta->avg_signal);
+       }
+       if (sta->chains) {
+               sinfo->filled |= STATION_INFO_CHAIN_SIGNAL |
+                                STATION_INFO_CHAIN_SIGNAL_AVG;
+
+               sinfo->chains = sta->chains;
+               for (i = 0; i < ARRAY_SIZE(sinfo->chain_signal); i++) {
+                       sinfo->chain_signal[i] = sta->chain_signal_last[i];
+                       sinfo->chain_signal_avg[i] =
+                               (s8) -ewma_read(&sta->chain_signal_avg[i]);
+               }
+       }
+
+       sta_set_rate_info_tx(sta, &sta->last_tx_rate, &sinfo->txrate);
+       sta_set_rate_info_rx(sta, &sinfo->rxrate);
+
+       if (ieee80211_vif_is_mesh(&sdata->vif)) {
+#ifdef CONFIG_MAC80211_MESH
+               sinfo->filled |= STATION_INFO_LLID |
+                                STATION_INFO_PLID |
+                                STATION_INFO_PLINK_STATE |
+                                STATION_INFO_LOCAL_PM |
+                                STATION_INFO_PEER_PM |
+                                STATION_INFO_NONPEER_PM;
+
+               sinfo->llid = sta->llid;
+               sinfo->plid = sta->plid;
+               sinfo->plink_state = sta->plink_state;
+               if (test_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN)) {
+                       sinfo->filled |= STATION_INFO_T_OFFSET;
+                       sinfo->t_offset = sta->t_offset;
+               }
+               sinfo->local_pm = sta->local_pm;
+               sinfo->peer_pm = sta->peer_pm;
+               sinfo->nonpeer_pm = sta->nonpeer_pm;
+#endif
+       }
+
+       sinfo->bss_param.flags = 0;
+       if (sdata->vif.bss_conf.use_cts_prot)
+               sinfo->bss_param.flags |= BSS_PARAM_FLAGS_CTS_PROT;
+       if (sdata->vif.bss_conf.use_short_preamble)
+               sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_PREAMBLE;
+       if (sdata->vif.bss_conf.use_short_slot)
+               sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME;
+       sinfo->bss_param.dtim_period = sdata->local->hw.conf.ps_dtim_period;
+       sinfo->bss_param.beacon_interval = sdata->vif.bss_conf.beacon_int;
+
+       sinfo->sta_flags.set = 0;
+       sinfo->sta_flags.mask = BIT(NL80211_STA_FLAG_AUTHORIZED) |
+                               BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) |
+                               BIT(NL80211_STA_FLAG_WME) |
+                               BIT(NL80211_STA_FLAG_MFP) |
+                               BIT(NL80211_STA_FLAG_AUTHENTICATED) |
+                               BIT(NL80211_STA_FLAG_ASSOCIATED) |
+                               BIT(NL80211_STA_FLAG_TDLS_PEER);
+       if (test_sta_flag(sta, WLAN_STA_AUTHORIZED))
+               sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_AUTHORIZED);
+       if (test_sta_flag(sta, WLAN_STA_SHORT_PREAMBLE))
+               sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_SHORT_PREAMBLE);
+       if (test_sta_flag(sta, WLAN_STA_WME))
+               sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_WME);
+       if (test_sta_flag(sta, WLAN_STA_MFP))
+               sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_MFP);
+       if (test_sta_flag(sta, WLAN_STA_AUTH))
+               sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_AUTHENTICATED);
+       if (test_sta_flag(sta, WLAN_STA_ASSOC))
+               sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_ASSOCIATED);
+       if (test_sta_flag(sta, WLAN_STA_TDLS_PEER))
+               sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER);
+
+       /* check if the driver has a SW RC implementation */
+       if (ref && ref->ops->get_expected_throughput)
+               thr = ref->ops->get_expected_throughput(sta->rate_ctrl_priv);
+       else
+               thr = drv_get_expected_throughput(local, &sta->sta);
+
+       if (thr != 0) {
+               sinfo->filled |= STATION_INFO_EXPECTED_THROUGHPUT;
+               sinfo->expected_throughput = thr;
+       }
+}
index 4acc5fc..2a04361 100644 (file)
@@ -58,6 +58,8 @@
  * @WLAN_STA_TOFFSET_KNOWN: toffset calculated for this station is valid.
  * @WLAN_STA_MPSP_OWNER: local STA is owner of a mesh Peer Service Period.
  * @WLAN_STA_MPSP_RECIPIENT: local STA is recipient of a MPSP.
+ * @WLAN_STA_PS_DELIVER: station woke up, but we're still blocking TX
+ *     until pending frames are delivered
  */
 enum ieee80211_sta_info_flags {
        WLAN_STA_AUTH,
@@ -82,6 +84,7 @@ enum ieee80211_sta_info_flags {
        WLAN_STA_TOFFSET_KNOWN,
        WLAN_STA_MPSP_OWNER,
        WLAN_STA_MPSP_RECIPIENT,
+       WLAN_STA_PS_DELIVER,
 };
 
 #define ADDBA_RESP_INTERVAL HZ
@@ -265,7 +268,7 @@ struct ieee80211_tx_latency_stat {
  * @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.
- * @drv_unblock_wk: used for driver PS unblocking
+ * @drv_deliver_wk: used for delivering frames after driver PS unblocking
  * @listen_interval: listen interval of this station, when we're acting as AP
  * @_flags: STA flags, see &enum ieee80211_sta_info_flags, do not use directly
  * @ps_lock: used for powersave (when mac80211 is the AP) related locking
@@ -278,7 +281,6 @@ struct ieee80211_tx_latency_stat {
  * @driver_buffered_tids: bitmap of TIDs the driver has data buffered on
  * @rx_packets: Number of MSDUs received from this STA
  * @rx_bytes: Number of bytes received from this STA
- * @wep_weak_iv_count: number of weak WEP IVs received from this station
  * @last_rx: time (in jiffies) when last frame was received from this STA
  * @last_connected: time (in seconds) when a station got connected
  * @num_duplicates: number of duplicate frames received from this STA
@@ -303,7 +305,6 @@ struct ieee80211_tx_latency_stat {
  * @plid: Peer link ID
  * @reason: Cancel reason on PLINK_HOLDING state
  * @plink_retries: Retries in establishment
- * @ignore_plink_timer: ignore the peer-link timer (used internally)
  * @plink_state: peer link state
  * @plink_timeout: timeout of peer link
  * @plink_timer: peer link watch timer
@@ -345,7 +346,7 @@ struct sta_info {
        void *rate_ctrl_priv;
        spinlock_t lock;
 
-       struct work_struct drv_unblock_wk;
+       struct work_struct drv_deliver_wk;
 
        u16 listen_interval;
 
@@ -367,7 +368,6 @@ struct sta_info {
        /* Updated from RX path only, no locking requirements */
        unsigned long rx_packets;
        u64 rx_bytes;
-       unsigned long wep_weak_iv_count;
        unsigned long last_rx;
        long last_connected;
        unsigned long num_duplicates;
@@ -418,7 +418,6 @@ struct sta_info {
        u16 plid;
        u16 reason;
        u8 plink_retries;
-       bool ignore_plink_timer;
        enum nl80211_plink_state plink_state;
        u32 plink_timeout;
        struct timer_list plink_timer;
@@ -628,6 +627,8 @@ void sta_set_rate_info_tx(struct sta_info *sta,
                          struct rate_info *rinfo);
 void sta_set_rate_info_rx(struct sta_info *sta,
                          struct rate_info *rinfo);
+void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo);
+
 void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
                          unsigned long exp_time);
 u8 sta_info_tx_streams(struct sta_info *sta);
index ba29ebc..aa06dca 100644 (file)
@@ -473,8 +473,6 @@ static void ieee80211_tx_latency_end_msrmnt(struct ieee80211_local *local,
                                            struct sta_info *sta,
                                            struct ieee80211_hdr *hdr)
 {
-       ktime_t skb_dprt;
-       struct timespec dprt_time;
        u32 msrmnt;
        u16 tid;
        u8 *qc;
@@ -506,9 +504,8 @@ static void ieee80211_tx_latency_end_msrmnt(struct ieee80211_local *local,
 
        tx_lat = &sta->tx_lat[tid];
 
-       ktime_get_ts(&dprt_time); /* time stamp completion time */
-       skb_dprt = ktime_set(dprt_time.tv_sec, dprt_time.tv_nsec);
-       msrmnt = ktime_to_ms(ktime_sub(skb_dprt, skb_arv));
+       /* Calculate the latency */
+       msrmnt = ktime_to_ms(ktime_sub(ktime_get(), skb_arv));
 
        if (tx_lat->max < msrmnt) /* update stats */
                tx_lat->max = msrmnt;
index 652813b..f718533 100644 (file)
@@ -8,7 +8,30 @@
  */
 
 #include <linux/ieee80211.h>
+#include <net/cfg80211.h>
 #include "ieee80211_i.h"
+#include "driver-ops.h"
+
+/* give usermode some time for retries in setting up the TDLS session */
+#define TDLS_PEER_SETUP_TIMEOUT        (15 * HZ)
+
+void ieee80211_tdls_peer_del_work(struct work_struct *wk)
+{
+       struct ieee80211_sub_if_data *sdata;
+       struct ieee80211_local *local;
+
+       sdata = container_of(wk, struct ieee80211_sub_if_data,
+                            tdls_peer_del_work.work);
+       local = sdata->local;
+
+       mutex_lock(&local->mtx);
+       if (!is_zero_ether_addr(sdata->tdls_peer)) {
+               tdls_dbg(sdata, "TDLS del peer %pM\n", sdata->tdls_peer);
+               sta_info_destroy_addr(sdata, sdata->tdls_peer);
+               eth_zero_addr(sdata->tdls_peer);
+       }
+       mutex_unlock(&local->mtx);
+}
 
 static void ieee80211_tdls_add_ext_capab(struct sk_buff *skb)
 {
@@ -168,28 +191,20 @@ ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev,
        return 0;
 }
 
-int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
-                       const u8 *peer, u8 action_code, u8 dialog_token,
-                       u16 status_code, u32 peer_capability,
-                       const u8 *extra_ies, size_t extra_ies_len)
+static int
+ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
+                               const u8 *peer, u8 action_code,
+                               u8 dialog_token, u16 status_code,
+                               u32 peer_capability, bool initiator,
+                               const u8 *extra_ies, size_t extra_ies_len)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        struct ieee80211_local *local = sdata->local;
        struct sk_buff *skb = NULL;
        bool send_direct;
+       const u8 *init_addr, *rsp_addr;
        int ret;
 
-       if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS))
-               return -ENOTSUPP;
-
-       /* make sure we are in managed mode, and associated */
-       if (sdata->vif.type != NL80211_IFTYPE_STATION ||
-           !sdata->u.mgd.associated)
-               return -EINVAL;
-
-       tdls_dbg(sdata, "TDLS mgmt action %d peer %pM\n",
-                action_code, peer);
-
        skb = dev_alloc_skb(local->hw.extra_tx_headroom +
                            max(sizeof(struct ieee80211_mgmt),
                                sizeof(struct ieee80211_tdls_data)) +
@@ -230,27 +245,42 @@ int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
        if (extra_ies_len)
                memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len);
 
-       /* the TDLS link IE is always added last */
+       /* sanity check for initiator */
        switch (action_code) {
        case WLAN_TDLS_SETUP_REQUEST:
        case WLAN_TDLS_SETUP_CONFIRM:
-       case WLAN_TDLS_TEARDOWN:
        case WLAN_TDLS_DISCOVERY_REQUEST:
-               /* we are the initiator */
-               ieee80211_tdls_add_link_ie(skb, sdata->vif.addr, peer,
-                                          sdata->u.mgd.bssid);
+               if (!initiator) {
+                       ret = -EINVAL;
+                       goto fail;
+               }
                break;
        case WLAN_TDLS_SETUP_RESPONSE:
        case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
-               /* we are the responder */
-               ieee80211_tdls_add_link_ie(skb, peer, sdata->vif.addr,
-                                          sdata->u.mgd.bssid);
+               if (initiator) {
+                       ret = -EINVAL;
+                       goto fail;
+               }
+               break;
+       case WLAN_TDLS_TEARDOWN:
+               /* any value is ok */
                break;
        default:
                ret = -ENOTSUPP;
                goto fail;
        }
 
+       if (initiator) {
+               init_addr = sdata->vif.addr;
+               rsp_addr = peer;
+       } else {
+               init_addr = peer;
+               rsp_addr = sdata->vif.addr;
+       }
+
+       ieee80211_tdls_add_link_ie(skb, init_addr, rsp_addr,
+                                  sdata->u.mgd.bssid);
+
        if (send_direct) {
                ieee80211_tx_skb(sdata, skb);
                return 0;
@@ -284,11 +314,171 @@ fail:
        return ret;
 }
 
+static int
+ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev,
+                         const u8 *peer, u8 action_code, u8 dialog_token,
+                         u16 status_code, u32 peer_capability, bool initiator,
+                         const u8 *extra_ies, size_t extra_ies_len)
+{
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       struct ieee80211_local *local = sdata->local;
+       int ret;
+
+       mutex_lock(&local->mtx);
+
+       /* we don't support concurrent TDLS peer setups */
+       if (!is_zero_ether_addr(sdata->tdls_peer) &&
+           !ether_addr_equal(sdata->tdls_peer, peer)) {
+               ret = -EBUSY;
+               goto exit;
+       }
+
+       /*
+        * make sure we have a STA representing the peer so we drop or buffer
+        * non-TDLS-setup frames to the peer. We can't send other packets
+        * during setup through the AP path
+        */
+       rcu_read_lock();
+       if (!sta_info_get(sdata, peer)) {
+               rcu_read_unlock();
+               ret = -ENOLINK;
+               goto exit;
+       }
+       rcu_read_unlock();
+
+       ieee80211_flush_queues(local, sdata);
+
+       ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer, action_code,
+                                             dialog_token, status_code,
+                                             peer_capability, initiator,
+                                             extra_ies, extra_ies_len);
+       if (ret < 0)
+               goto exit;
+
+       memcpy(sdata->tdls_peer, peer, ETH_ALEN);
+       ieee80211_queue_delayed_work(&sdata->local->hw,
+                                    &sdata->tdls_peer_del_work,
+                                    TDLS_PEER_SETUP_TIMEOUT);
+
+exit:
+       mutex_unlock(&local->mtx);
+       return ret;
+}
+
+static int
+ieee80211_tdls_mgmt_teardown(struct wiphy *wiphy, struct net_device *dev,
+                            const u8 *peer, u8 action_code, u8 dialog_token,
+                            u16 status_code, u32 peer_capability,
+                            bool initiator, const u8 *extra_ies,
+                            size_t extra_ies_len)
+{
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       struct ieee80211_local *local = sdata->local;
+       struct sta_info *sta;
+       int ret;
+
+       /*
+        * No packets can be transmitted to the peer via the AP during setup -
+        * the STA is set as a TDLS peer, but is not authorized.
+        * During teardown, we prevent direct transmissions by stopping the
+        * queues and flushing all direct packets.
+        */
+       ieee80211_stop_vif_queues(local, sdata,
+                                 IEEE80211_QUEUE_STOP_REASON_TDLS_TEARDOWN);
+       ieee80211_flush_queues(local, sdata);
+
+       ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer, action_code,
+                                             dialog_token, status_code,
+                                             peer_capability, initiator,
+                                             extra_ies, extra_ies_len);
+       if (ret < 0)
+               sdata_err(sdata, "Failed sending TDLS teardown packet %d\n",
+                         ret);
+
+       /*
+        * Remove the STA AUTH flag to force further traffic through the AP. If
+        * the STA was unreachable, it was already removed.
+        */
+       rcu_read_lock();
+       sta = sta_info_get(sdata, peer);
+       if (sta)
+               clear_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH);
+       rcu_read_unlock();
+
+       ieee80211_wake_vif_queues(local, sdata,
+                                 IEEE80211_QUEUE_STOP_REASON_TDLS_TEARDOWN);
+
+       return 0;
+}
+
+int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
+                       const u8 *peer, u8 action_code, u8 dialog_token,
+                       u16 status_code, u32 peer_capability,
+                       bool initiator, const u8 *extra_ies,
+                       size_t extra_ies_len)
+{
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       int ret;
+
+       if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS))
+               return -ENOTSUPP;
+
+       /* make sure we are in managed mode, and associated */
+       if (sdata->vif.type != NL80211_IFTYPE_STATION ||
+           !sdata->u.mgd.associated)
+               return -EINVAL;
+
+       switch (action_code) {
+       case WLAN_TDLS_SETUP_REQUEST:
+       case WLAN_TDLS_SETUP_RESPONSE:
+               ret = ieee80211_tdls_mgmt_setup(wiphy, dev, peer, action_code,
+                                               dialog_token, status_code,
+                                               peer_capability, initiator,
+                                               extra_ies, extra_ies_len);
+               break;
+       case WLAN_TDLS_TEARDOWN:
+               ret = ieee80211_tdls_mgmt_teardown(wiphy, dev, peer,
+                                                  action_code, dialog_token,
+                                                  status_code,
+                                                  peer_capability, initiator,
+                                                  extra_ies, extra_ies_len);
+               break;
+       case WLAN_TDLS_DISCOVERY_REQUEST:
+               /*
+                * Protect the discovery so we can hear the TDLS discovery
+                * response frame. It is transmitted directly and not buffered
+                * by the AP.
+                */
+               drv_mgd_protect_tdls_discover(sdata->local, sdata);
+               /* fall-through */
+       case WLAN_TDLS_SETUP_CONFIRM:
+       case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
+               /* no special handling */
+               ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer,
+                                                     action_code,
+                                                     dialog_token,
+                                                     status_code,
+                                                     peer_capability,
+                                                     initiator, extra_ies,
+                                                     extra_ies_len);
+               break;
+       default:
+               ret = -EOPNOTSUPP;
+               break;
+       }
+
+       tdls_dbg(sdata, "TDLS mgmt action %d peer %pM status %d\n",
+                action_code, peer, ret);
+       return ret;
+}
+
 int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
                        const u8 *peer, enum nl80211_tdls_operation oper)
 {
        struct sta_info *sta;
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       struct ieee80211_local *local = sdata->local;
+       int ret;
 
        if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS))
                return -ENOTSUPP;
@@ -296,6 +486,18 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
        if (sdata->vif.type != NL80211_IFTYPE_STATION)
                return -EINVAL;
 
+       switch (oper) {
+       case NL80211_TDLS_ENABLE_LINK:
+       case NL80211_TDLS_DISABLE_LINK:
+               break;
+       case NL80211_TDLS_TEARDOWN:
+       case NL80211_TDLS_SETUP:
+       case NL80211_TDLS_DISCOVERY_REQ:
+               /* We don't support in-driver setup/teardown/discovery */
+               return -ENOTSUPP;
+       }
+
+       mutex_lock(&local->mtx);
        tdls_dbg(sdata, "TDLS oper %d peer %pM\n", oper, peer);
 
        switch (oper) {
@@ -304,22 +506,49 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
                sta = sta_info_get(sdata, peer);
                if (!sta) {
                        rcu_read_unlock();
-                       return -ENOLINK;
+                       ret = -ENOLINK;
+                       break;
                }
 
                set_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH);
                rcu_read_unlock();
+
+               WARN_ON_ONCE(is_zero_ether_addr(sdata->tdls_peer) ||
+                            !ether_addr_equal(sdata->tdls_peer, peer));
+               ret = 0;
                break;
        case NL80211_TDLS_DISABLE_LINK:
-               return sta_info_destroy_addr(sdata, peer);
-       case NL80211_TDLS_TEARDOWN:
-       case NL80211_TDLS_SETUP:
-       case NL80211_TDLS_DISCOVERY_REQ:
-               /* We don't support in-driver setup/teardown/discovery */
-               return -ENOTSUPP;
+               /* flush a potentially queued teardown packet */
+               ieee80211_flush_queues(local, sdata);
+
+               ret = sta_info_destroy_addr(sdata, peer);
+               break;
        default:
-               return -ENOTSUPP;
+               ret = -ENOTSUPP;
+               break;
        }
 
-       return 0;
+       if (ret == 0 && ether_addr_equal(sdata->tdls_peer, peer)) {
+               cancel_delayed_work(&sdata->tdls_peer_del_work);
+               eth_zero_addr(sdata->tdls_peer);
+       }
+
+       mutex_unlock(&local->mtx);
+       return ret;
+}
+
+void ieee80211_tdls_oper_request(struct ieee80211_vif *vif, const u8 *peer,
+                                enum nl80211_tdls_operation oper,
+                                u16 reason_code, gfp_t gfp)
+{
+       struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+
+       if (vif->type != NL80211_IFTYPE_STATION || !vif->bss_conf.assoc) {
+               sdata_err(sdata, "Discarding TDLS oper %d - not STA or disconnected\n",
+                         oper);
+               return;
+       }
+
+       cfg80211_tdls_oper_request(sdata->dev, peer, oper, reason_code, gfp);
 }
+EXPORT_SYMBOL(ieee80211_tdls_oper_request);
index cfe1a06..02ac535 100644 (file)
@@ -1330,6 +1330,13 @@ DEFINE_EVENT(local_sdata_evt, drv_mgd_prepare_tx,
        TP_ARGS(local, sdata)
 );
 
+DEFINE_EVENT(local_sdata_evt, drv_mgd_protect_tdls_discover,
+       TP_PROTO(struct ieee80211_local *local,
+                struct ieee80211_sub_if_data *sdata),
+
+       TP_ARGS(local, sdata)
+);
+
 DECLARE_EVENT_CLASS(local_chanctx,
        TP_PROTO(struct ieee80211_local *local,
                 struct ieee80211_chanctx *ctx),
index 5214686..865bdaf 100644 (file)
@@ -250,7 +250,8 @@ ieee80211_tx_h_dynamic_ps(struct ieee80211_tx_data *tx)
        if (local->hw.conf.flags & IEEE80211_CONF_PS) {
                ieee80211_stop_queues_by_reason(&local->hw,
                                                IEEE80211_MAX_QUEUE_MAP,
-                                               IEEE80211_QUEUE_STOP_REASON_PS);
+                                               IEEE80211_QUEUE_STOP_REASON_PS,
+                                               false);
                ifmgd->flags &= ~IEEE80211_STA_NULLFUNC_ACKED;
                ieee80211_queue_work(&local->hw,
                                     &local->dynamic_ps_disable_work);
@@ -469,7 +470,8 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
                return TX_CONTINUE;
 
        if (unlikely((test_sta_flag(sta, WLAN_STA_PS_STA) ||
-                     test_sta_flag(sta, WLAN_STA_PS_DRIVER)) &&
+                     test_sta_flag(sta, WLAN_STA_PS_DRIVER) ||
+                     test_sta_flag(sta, WLAN_STA_PS_DELIVER)) &&
                     !(info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER))) {
                int ac = skb_get_queue_mapping(tx->skb);
 
@@ -486,7 +488,8 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
                 * ahead and Tx the packet.
                 */
                if (!test_sta_flag(sta, WLAN_STA_PS_STA) &&
-                   !test_sta_flag(sta, WLAN_STA_PS_DRIVER)) {
+                   !test_sta_flag(sta, WLAN_STA_PS_DRIVER) &&
+                   !test_sta_flag(sta, WLAN_STA_PS_DELIVER)) {
                        spin_unlock(&sta->ps_lock);
                        return TX_CONTINUE;
                }
@@ -1618,12 +1621,12 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
 {
        struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
        struct ieee80211_chanctx_conf *chanctx_conf;
-       struct ieee80211_channel *chan;
        struct ieee80211_radiotap_header *prthdr =
                (struct ieee80211_radiotap_header *)skb->data;
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
        struct ieee80211_hdr *hdr;
        struct ieee80211_sub_if_data *tmp_sdata, *sdata;
+       struct cfg80211_chan_def *chandef;
        u16 len_rthdr;
        int hdrlen;
 
@@ -1721,9 +1724,9 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
        }
 
        if (chanctx_conf)
-               chan = chanctx_conf->def.chan;
+               chandef = &chanctx_conf->def;
        else if (!local->use_chanctx)
-               chan = local->_oper_chandef.chan;
+               chandef = &local->_oper_chandef;
        else
                goto fail_rcu;
 
@@ -1743,10 +1746,11 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
         * radar detection by itself. We can do that later by adding a
         * monitor flag interfaces used for AP support.
         */
-       if ((chan->flags & (IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_RADAR)))
+       if (!cfg80211_reg_can_beacon(local->hw.wiphy, chandef,
+                                    sdata->vif.type))
                goto fail_rcu;
 
-       ieee80211_xmit(sdata, skb, chan->band);
+       ieee80211_xmit(sdata, skb, chandef->chan->band);
        rcu_read_unlock();
 
        return NETDEV_TX_OK;
@@ -1767,15 +1771,12 @@ fail:
 static void ieee80211_tx_latency_start_msrmnt(struct ieee80211_local *local,
                                              struct sk_buff *skb)
 {
-       struct timespec skb_arv;
        struct ieee80211_tx_latency_bin_ranges *tx_latency;
 
        tx_latency = rcu_dereference(local->tx_latency);
        if (!tx_latency)
                return;
-
-       ktime_get_ts(&skb_arv);
-       skb->tstamp = ktime_set(skb_arv.tv_sec, skb_arv.tv_nsec);
+       skb->tstamp = ktime_get();
 }
 
 /**
@@ -1810,7 +1811,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
        int nh_pos, h_pos;
        struct sta_info *sta = NULL;
        bool wme_sta = false, authorized = false, tdls_auth = false;
-       bool tdls_direct = false;
+       bool tdls_peer = false, tdls_setup_frame = false;
        bool multicast;
        u32 info_flags = 0;
        u16 info_id = 0;
@@ -1952,34 +1953,35 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
 #endif
        case NL80211_IFTYPE_STATION:
                if (sdata->wdev.wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) {
-                       bool tdls_peer = false;
-
                        sta = sta_info_get(sdata, skb->data);
                        if (sta) {
                                authorized = test_sta_flag(sta,
                                                        WLAN_STA_AUTHORIZED);
                                wme_sta = test_sta_flag(sta, WLAN_STA_WME);
                                tdls_peer = test_sta_flag(sta,
-                                                        WLAN_STA_TDLS_PEER);
+                                                         WLAN_STA_TDLS_PEER);
                                tdls_auth = test_sta_flag(sta,
                                                WLAN_STA_TDLS_PEER_AUTH);
                        }
 
-                       /*
-                        * If the TDLS link is enabled, send everything
-                        * directly. Otherwise, allow TDLS setup frames
-                        * to be transmitted indirectly.
-                        */
-                       tdls_direct = tdls_peer && (tdls_auth ||
-                                !(ethertype == ETH_P_TDLS && skb->len > 14 &&
-                                  skb->data[14] == WLAN_TDLS_SNAP_RFTYPE));
+                       if (tdls_peer)
+                               tdls_setup_frame =
+                                       ethertype == ETH_P_TDLS &&
+                                       skb->len > 14 &&
+                                       skb->data[14] == WLAN_TDLS_SNAP_RFTYPE;
                }
 
-               if (tdls_direct) {
-                       /* link during setup - throw out frames to peer */
-                       if (!tdls_auth)
-                               goto fail_rcu;
+               /*
+                * TDLS link during setup - throw out frames to peer. We allow
+                * TDLS-setup frames to unauthorized peers for the special case
+                * of a link teardown after a TDLS sta is removed due to being
+                * unreachable.
+                */
+               if (tdls_peer && !tdls_auth && !tdls_setup_frame)
+                       goto fail_rcu;
 
+               /* send direct packets to authorized TDLS peers */
+               if (tdls_peer && tdls_auth) {
                        /* DA SA BSSID */
                        memcpy(hdr.addr1, skb->data, ETH_ALEN);
                        memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
@@ -2423,7 +2425,7 @@ static void ieee80211_set_csa(struct ieee80211_sub_if_data *sdata,
        u8 *beacon_data;
        size_t beacon_data_len;
        int i;
-       u8 count = sdata->csa_current_counter;
+       u8 count = beacon->csa_current_counter;
 
        switch (sdata->vif.type) {
        case NL80211_IFTYPE_AP:
@@ -2442,46 +2444,53 @@ static void ieee80211_set_csa(struct ieee80211_sub_if_data *sdata,
                return;
        }
 
+       rcu_read_lock();
        for (i = 0; i < IEEE80211_MAX_CSA_COUNTERS_NUM; ++i) {
-               u16 counter_offset_beacon =
-                       sdata->csa_counter_offset_beacon[i];
-               u16 counter_offset_presp = sdata->csa_counter_offset_presp[i];
+               resp = rcu_dereference(sdata->u.ap.probe_resp);
 
-               if (counter_offset_beacon) {
-                       if (WARN_ON(counter_offset_beacon >= beacon_data_len))
-                               return;
-
-                       beacon_data[counter_offset_beacon] = count;
-               }
-
-               if (sdata->vif.type == NL80211_IFTYPE_AP &&
-                   counter_offset_presp) {
-                       rcu_read_lock();
-                       resp = rcu_dereference(sdata->u.ap.probe_resp);
-
-                       /* If nl80211 accepted the offset, this should
-                        * not happen.
-                        */
-                       if (WARN_ON(!resp)) {
+               if (beacon->csa_counter_offsets[i]) {
+                       if (WARN_ON_ONCE(beacon->csa_counter_offsets[i] >=
+                                        beacon_data_len)) {
                                rcu_read_unlock();
                                return;
                        }
-                       resp->data[counter_offset_presp] = count;
-                       rcu_read_unlock();
+
+                       beacon_data[beacon->csa_counter_offsets[i]] = count;
                }
+
+               if (sdata->vif.type == NL80211_IFTYPE_AP && resp)
+                       resp->data[resp->csa_counter_offsets[i]] = count;
        }
+       rcu_read_unlock();
 }
 
 u8 ieee80211_csa_update_counter(struct ieee80211_vif *vif)
 {
        struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+       struct beacon_data *beacon = NULL;
+       u8 count = 0;
+
+       rcu_read_lock();
 
-       sdata->csa_current_counter--;
+       if (sdata->vif.type == NL80211_IFTYPE_AP)
+               beacon = rcu_dereference(sdata->u.ap.beacon);
+       else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
+               beacon = rcu_dereference(sdata->u.ibss.presp);
+       else if (ieee80211_vif_is_mesh(&sdata->vif))
+               beacon = rcu_dereference(sdata->u.mesh.beacon);
+
+       if (!beacon)
+               goto unlock;
+
+       beacon->csa_current_counter--;
 
        /* the counter should never reach 0 */
-       WARN_ON(!sdata->csa_current_counter);
+       WARN_ON_ONCE(!beacon->csa_current_counter);
+       count = beacon->csa_current_counter;
 
-       return sdata->csa_current_counter;
+unlock:
+       rcu_read_unlock();
+       return count;
 }
 EXPORT_SYMBOL(ieee80211_csa_update_counter);
 
@@ -2491,7 +2500,6 @@ bool ieee80211_csa_is_complete(struct ieee80211_vif *vif)
        struct beacon_data *beacon = NULL;
        u8 *beacon_data;
        size_t beacon_data_len;
-       int counter_beacon = sdata->csa_counter_offset_beacon[0];
        int ret = false;
 
        if (!ieee80211_sdata_running(sdata))
@@ -2529,10 +2537,13 @@ bool ieee80211_csa_is_complete(struct ieee80211_vif *vif)
                goto out;
        }
 
-       if (WARN_ON(counter_beacon > beacon_data_len))
+       if (!beacon->csa_counter_offsets[0])
                goto out;
 
-       if (beacon_data[counter_beacon] == 1)
+       if (WARN_ON_ONCE(beacon->csa_counter_offsets[0] > beacon_data_len))
+               goto out;
+
+       if (beacon_data[beacon->csa_counter_offsets[0]] == 1)
                ret = true;
  out:
        rcu_read_unlock();
@@ -2548,6 +2559,7 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw,
                       bool is_template)
 {
        struct ieee80211_local *local = hw_to_local(hw);
+       struct beacon_data *beacon = NULL;
        struct sk_buff *skb = NULL;
        struct ieee80211_tx_info *info;
        struct ieee80211_sub_if_data *sdata = NULL;
@@ -2569,10 +2581,10 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw,
 
        if (sdata->vif.type == NL80211_IFTYPE_AP) {
                struct ieee80211_if_ap *ap = &sdata->u.ap;
-               struct beacon_data *beacon = rcu_dereference(ap->beacon);
 
+               beacon = rcu_dereference(ap->beacon);
                if (beacon) {
-                       if (sdata->vif.csa_active) {
+                       if (beacon->csa_counter_offsets[0]) {
                                if (!is_template)
                                        ieee80211_csa_update_counter(vif);
 
@@ -2613,37 +2625,37 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw,
        } else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
                struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
                struct ieee80211_hdr *hdr;
-               struct beacon_data *presp = rcu_dereference(ifibss->presp);
 
-               if (!presp)
+               beacon = rcu_dereference(ifibss->presp);
+               if (!beacon)
                        goto out;
 
-               if (sdata->vif.csa_active) {
+               if (beacon->csa_counter_offsets[0]) {
                        if (!is_template)
                                ieee80211_csa_update_counter(vif);
 
-                       ieee80211_set_csa(sdata, presp);
+                       ieee80211_set_csa(sdata, beacon);
                }
 
-               skb = dev_alloc_skb(local->tx_headroom + presp->head_len +
+               skb = dev_alloc_skb(local->tx_headroom + beacon->head_len +
                                    local->hw.extra_beacon_tailroom);
                if (!skb)
                        goto out;
                skb_reserve(skb, local->tx_headroom);
-               memcpy(skb_put(skb, presp->head_len), presp->head,
-                      presp->head_len);
+               memcpy(skb_put(skb, beacon->head_len), beacon->head,
+                      beacon->head_len);
 
                hdr = (struct ieee80211_hdr *) skb->data;
                hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
                                                 IEEE80211_STYPE_BEACON);
        } else if (ieee80211_vif_is_mesh(&sdata->vif)) {
                struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
-               struct beacon_data *bcn = rcu_dereference(ifmsh->beacon);
 
-               if (!bcn)
+               beacon = rcu_dereference(ifmsh->beacon);
+               if (!beacon)
                        goto out;
 
-               if (sdata->vif.csa_active) {
+               if (beacon->csa_counter_offsets[0]) {
                        if (!is_template)
                                /* TODO: For mesh csa_counter is in TU, so
                                 * decrementing it by one isn't correct, but
@@ -2652,40 +2664,42 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw,
                                 */
                                ieee80211_csa_update_counter(vif);
 
-                       ieee80211_set_csa(sdata, bcn);
+                       ieee80211_set_csa(sdata, beacon);
                }
 
                if (ifmsh->sync_ops)
-                       ifmsh->sync_ops->adjust_tbtt(sdata, bcn);
+                       ifmsh->sync_ops->adjust_tbtt(sdata, beacon);
 
                skb = dev_alloc_skb(local->tx_headroom +
-                                   bcn->head_len +
+                                   beacon->head_len +
                                    256 + /* TIM IE */
-                                   bcn->tail_len +
+                                   beacon->tail_len +
                                    local->hw.extra_beacon_tailroom);
                if (!skb)
                        goto out;
                skb_reserve(skb, local->tx_headroom);
-               memcpy(skb_put(skb, bcn->head_len), bcn->head, bcn->head_len);
+               memcpy(skb_put(skb, beacon->head_len), beacon->head,
+                      beacon->head_len);
                ieee80211_beacon_add_tim(sdata, &ifmsh->ps, skb, is_template);
 
                if (offs) {
-                       offs->tim_offset = bcn->head_len;
-                       offs->tim_length = skb->len - bcn->head_len;
+                       offs->tim_offset = beacon->head_len;
+                       offs->tim_length = skb->len - beacon->head_len;
                }
 
-               memcpy(skb_put(skb, bcn->tail_len), bcn->tail, bcn->tail_len);
+               memcpy(skb_put(skb, beacon->tail_len), beacon->tail,
+                      beacon->tail_len);
        } else {
                WARN_ON(1);
                goto out;
        }
 
        /* CSA offsets */
-       if (offs) {
+       if (offs && beacon) {
                int i;
 
                for (i = 0; i < IEEE80211_MAX_CSA_COUNTERS_NUM; i++) {
-                       u16 csa_off = sdata->csa_counter_offset_beacon[i];
+                       u16 csa_off = beacon->csa_counter_offsets[i];
 
                        if (!csa_off)
                                continue;
index 6886601..ea79668 100644 (file)
@@ -317,7 +317,8 @@ void ieee80211_propagate_queue_wake(struct ieee80211_local *local, int queue)
 }
 
 static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue,
-                                  enum queue_stop_reason reason)
+                                  enum queue_stop_reason reason,
+                                  bool refcounted)
 {
        struct ieee80211_local *local = hw_to_local(hw);
 
@@ -329,7 +330,13 @@ static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue,
        if (!test_bit(reason, &local->queue_stop_reasons[queue]))
                return;
 
-       __clear_bit(reason, &local->queue_stop_reasons[queue]);
+       if (!refcounted)
+               local->q_stop_reasons[queue][reason] = 0;
+       else
+               local->q_stop_reasons[queue][reason]--;
+
+       if (local->q_stop_reasons[queue][reason] == 0)
+               __clear_bit(reason, &local->queue_stop_reasons[queue]);
 
        if (local->queue_stop_reasons[queue] != 0)
                /* someone still has this queue stopped */
@@ -344,25 +351,28 @@ static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue,
 }
 
 void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue,
-                                   enum queue_stop_reason reason)
+                                   enum queue_stop_reason reason,
+                                   bool refcounted)
 {
        struct ieee80211_local *local = hw_to_local(hw);
        unsigned long flags;
 
        spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
-       __ieee80211_wake_queue(hw, queue, reason);
+       __ieee80211_wake_queue(hw, queue, reason, refcounted);
        spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
 }
 
 void ieee80211_wake_queue(struct ieee80211_hw *hw, int queue)
 {
        ieee80211_wake_queue_by_reason(hw, queue,
-                                      IEEE80211_QUEUE_STOP_REASON_DRIVER);
+                                      IEEE80211_QUEUE_STOP_REASON_DRIVER,
+                                      false);
 }
 EXPORT_SYMBOL(ieee80211_wake_queue);
 
 static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue,
-                                  enum queue_stop_reason reason)
+                                  enum queue_stop_reason reason,
+                                  bool refcounted)
 {
        struct ieee80211_local *local = hw_to_local(hw);
        struct ieee80211_sub_if_data *sdata;
@@ -373,10 +383,13 @@ static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue,
        if (WARN_ON(queue >= hw->queues))
                return;
 
-       if (test_bit(reason, &local->queue_stop_reasons[queue]))
-               return;
+       if (!refcounted)
+               local->q_stop_reasons[queue][reason] = 1;
+       else
+               local->q_stop_reasons[queue][reason]++;
 
-       __set_bit(reason, &local->queue_stop_reasons[queue]);
+       if (__test_and_set_bit(reason, &local->queue_stop_reasons[queue]))
+               return;
 
        if (local->hw.queues < IEEE80211_NUM_ACS)
                n_acs = 1;
@@ -398,20 +411,22 @@ static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue,
 }
 
 void ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue,
-                                   enum queue_stop_reason reason)
+                                   enum queue_stop_reason reason,
+                                   bool refcounted)
 {
        struct ieee80211_local *local = hw_to_local(hw);
        unsigned long flags;
 
        spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
-       __ieee80211_stop_queue(hw, queue, reason);
+       __ieee80211_stop_queue(hw, queue, reason, refcounted);
        spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
 }
 
 void ieee80211_stop_queue(struct ieee80211_hw *hw, int queue)
 {
        ieee80211_stop_queue_by_reason(hw, queue,
-                                      IEEE80211_QUEUE_STOP_REASON_DRIVER);
+                                      IEEE80211_QUEUE_STOP_REASON_DRIVER,
+                                      false);
 }
 EXPORT_SYMBOL(ieee80211_stop_queue);
 
@@ -429,9 +444,11 @@ void ieee80211_add_pending_skb(struct ieee80211_local *local,
        }
 
        spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
-       __ieee80211_stop_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_SKB_ADD);
+       __ieee80211_stop_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
+                              false);
        __skb_queue_tail(&local->pending[queue], skb);
-       __ieee80211_wake_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_SKB_ADD);
+       __ieee80211_wake_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
+                              false);
        spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
 }
 
@@ -455,20 +472,23 @@ void ieee80211_add_pending_skbs(struct ieee80211_local *local,
                queue = info->hw_queue;
 
                __ieee80211_stop_queue(hw, queue,
-                               IEEE80211_QUEUE_STOP_REASON_SKB_ADD);
+                               IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
+                               false);
 
                __skb_queue_tail(&local->pending[queue], skb);
        }
 
        for (i = 0; i < hw->queues; i++)
                __ieee80211_wake_queue(hw, i,
-                       IEEE80211_QUEUE_STOP_REASON_SKB_ADD);
+                       IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
+                       false);
        spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
 }
 
 void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw,
                                     unsigned long queues,
-                                    enum queue_stop_reason reason)
+                                    enum queue_stop_reason reason,
+                                    bool refcounted)
 {
        struct ieee80211_local *local = hw_to_local(hw);
        unsigned long flags;
@@ -477,7 +497,7 @@ void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw,
        spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
 
        for_each_set_bit(i, &queues, hw->queues)
-               __ieee80211_stop_queue(hw, i, reason);
+               __ieee80211_stop_queue(hw, i, reason, refcounted);
 
        spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
 }
@@ -485,7 +505,8 @@ void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw,
 void ieee80211_stop_queues(struct ieee80211_hw *hw)
 {
        ieee80211_stop_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP,
-                                       IEEE80211_QUEUE_STOP_REASON_DRIVER);
+                                       IEEE80211_QUEUE_STOP_REASON_DRIVER,
+                                       false);
 }
 EXPORT_SYMBOL(ieee80211_stop_queues);
 
@@ -508,7 +529,8 @@ EXPORT_SYMBOL(ieee80211_queue_stopped);
 
 void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
                                     unsigned long queues,
-                                    enum queue_stop_reason reason)
+                                    enum queue_stop_reason reason,
+                                    bool refcounted)
 {
        struct ieee80211_local *local = hw_to_local(hw);
        unsigned long flags;
@@ -517,7 +539,7 @@ void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
        spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
 
        for_each_set_bit(i, &queues, hw->queues)
-               __ieee80211_wake_queue(hw, i, reason);
+               __ieee80211_wake_queue(hw, i, reason, refcounted);
 
        spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
 }
@@ -525,17 +547,16 @@ void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
 void ieee80211_wake_queues(struct ieee80211_hw *hw)
 {
        ieee80211_wake_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP,
-                                       IEEE80211_QUEUE_STOP_REASON_DRIVER);
+                                       IEEE80211_QUEUE_STOP_REASON_DRIVER,
+                                       false);
 }
 EXPORT_SYMBOL(ieee80211_wake_queues);
 
-void ieee80211_flush_queues(struct ieee80211_local *local,
-                           struct ieee80211_sub_if_data *sdata)
+static unsigned int
+ieee80211_get_vif_queues(struct ieee80211_local *local,
+                        struct ieee80211_sub_if_data *sdata)
 {
-       u32 queues;
-
-       if (!local->ops->flush)
-               return;
+       unsigned int queues;
 
        if (sdata && local->hw.flags & IEEE80211_HW_QUEUE_CONTROL) {
                int ac;
@@ -551,13 +572,46 @@ void ieee80211_flush_queues(struct ieee80211_local *local,
                queues = BIT(local->hw.queues) - 1;
        }
 
-       ieee80211_stop_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP,
-                                       IEEE80211_QUEUE_STOP_REASON_FLUSH);
+       return queues;
+}
+
+void ieee80211_flush_queues(struct ieee80211_local *local,
+                           struct ieee80211_sub_if_data *sdata)
+{
+       unsigned int queues;
+
+       if (!local->ops->flush)
+               return;
+
+       queues = ieee80211_get_vif_queues(local, sdata);
+
+       ieee80211_stop_queues_by_reason(&local->hw, queues,
+                                       IEEE80211_QUEUE_STOP_REASON_FLUSH,
+                                       false);
 
        drv_flush(local, sdata, queues, false);
 
-       ieee80211_wake_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP,
-                                       IEEE80211_QUEUE_STOP_REASON_FLUSH);
+       ieee80211_wake_queues_by_reason(&local->hw, queues,
+                                       IEEE80211_QUEUE_STOP_REASON_FLUSH,
+                                       false);
+}
+
+void ieee80211_stop_vif_queues(struct ieee80211_local *local,
+                              struct ieee80211_sub_if_data *sdata,
+                              enum queue_stop_reason reason)
+{
+       ieee80211_stop_queues_by_reason(&local->hw,
+                                       ieee80211_get_vif_queues(local, sdata),
+                                       reason, true);
+}
+
+void ieee80211_wake_vif_queues(struct ieee80211_local *local,
+                              struct ieee80211_sub_if_data *sdata,
+                              enum queue_stop_reason reason)
+{
+       ieee80211_wake_queues_by_reason(&local->hw,
+                                       ieee80211_get_vif_queues(local, sdata),
+                                       reason, true);
 }
 
 static void __iterate_active_interfaces(struct ieee80211_local *local,
@@ -1165,14 +1219,17 @@ void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
        }
 }
 
-int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
-                            size_t buffer_len, const u8 *ie, size_t ie_len,
-                            enum ieee80211_band band, u32 rate_mask,
-                            struct cfg80211_chan_def *chandef)
+static int ieee80211_build_preq_ies_band(struct ieee80211_local *local,
+                                        u8 *buffer, size_t buffer_len,
+                                        const u8 *ie, size_t ie_len,
+                                        enum ieee80211_band band,
+                                        u32 rate_mask,
+                                        struct cfg80211_chan_def *chandef,
+                                        size_t *offset)
 {
        struct ieee80211_supported_band *sband;
        u8 *pos = buffer, *end = buffer + buffer_len;
-       size_t offset = 0, noffset;
+       size_t noffset;
        int supp_rates_len, i;
        u8 rates[32];
        int num_rates;
@@ -1180,6 +1237,8 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
        int shift;
        u32 rate_flags;
 
+       *offset = 0;
+
        sband = local->hw.wiphy->bands[band];
        if (WARN_ON_ONCE(!sband))
                return 0;
@@ -1218,12 +1277,12 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
                noffset = ieee80211_ie_split(ie, ie_len,
                                             before_extrates,
                                             ARRAY_SIZE(before_extrates),
-                                            offset);
-               if (end - pos < noffset - offset)
+                                            *offset);
+               if (end - pos < noffset - *offset)
                        goto out_err;
-               memcpy(pos, ie + offset, noffset - offset);
-               pos += noffset - offset;
-               offset = noffset;
+               memcpy(pos, ie + *offset, noffset - *offset);
+               pos += noffset - *offset;
+               *offset = noffset;
        }
 
        ext_rates_len = num_rates - supp_rates_len;
@@ -1257,12 +1316,12 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
                };
                noffset = ieee80211_ie_split(ie, ie_len,
                                             before_ht, ARRAY_SIZE(before_ht),
-                                            offset);
-               if (end - pos < noffset - offset)
+                                            *offset);
+               if (end - pos < noffset - *offset)
                        goto out_err;
-               memcpy(pos, ie + offset, noffset - offset);
-               pos += noffset - offset;
-               offset = noffset;
+               memcpy(pos, ie + *offset, noffset - *offset);
+               pos += noffset - *offset;
+               *offset = noffset;
        }
 
        if (sband->ht_cap.ht_supported) {
@@ -1297,12 +1356,12 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
                };
                noffset = ieee80211_ie_split(ie, ie_len,
                                             before_vht, ARRAY_SIZE(before_vht),
-                                            offset);
-               if (end - pos < noffset - offset)
+                                            *offset);
+               if (end - pos < noffset - *offset)
                        goto out_err;
-               memcpy(pos, ie + offset, noffset - offset);
-               pos += noffset - offset;
-               offset = noffset;
+               memcpy(pos, ie + *offset, noffset - *offset);
+               pos += noffset - *offset;
+               *offset = noffset;
        }
 
        if (sband->vht_cap.vht_supported) {
@@ -1312,21 +1371,54 @@ 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");
        return pos - buffer;
 }
 
+int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
+                            size_t buffer_len,
+                            struct ieee80211_scan_ies *ie_desc,
+                            const u8 *ie, size_t ie_len,
+                            u8 bands_used, u32 *rate_masks,
+                            struct cfg80211_chan_def *chandef)
+{
+       size_t pos = 0, old_pos = 0, custom_ie_offset = 0;
+       int i;
+
+       memset(ie_desc, 0, sizeof(*ie_desc));
+
+       for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
+               if (bands_used & BIT(i)) {
+                       pos += ieee80211_build_preq_ies_band(local,
+                                                            buffer + pos,
+                                                            buffer_len - pos,
+                                                            ie, ie_len, i,
+                                                            rate_masks[i],
+                                                            chandef,
+                                                            &custom_ie_offset);
+                       ie_desc->ies[i] = buffer + old_pos;
+                       ie_desc->len[i] = pos - old_pos;
+                       old_pos = pos;
+               }
+       }
+
+       /* add any remaining custom IEs */
+       if (ie && ie_len) {
+               if (WARN_ONCE(buffer_len - pos < ie_len - custom_ie_offset,
+                             "not enough space for preq custom IEs\n"))
+                       return pos;
+               memcpy(buffer + pos, ie + custom_ie_offset,
+                      ie_len - custom_ie_offset);
+               ie_desc->common_ies = buffer + pos;
+               ie_desc->common_ie_len = ie_len - custom_ie_offset;
+               pos += ie_len - custom_ie_offset;
+       }
+
+       return pos;
+};
+
 struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
                                          u8 *dst, u32 ratemask,
                                          struct ieee80211_channel *chan,
@@ -1339,6 +1431,8 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
        struct sk_buff *skb;
        struct ieee80211_mgmt *mgmt;
        int ies_len;
+       u32 rate_masks[IEEE80211_NUM_BANDS] = {};
+       struct ieee80211_scan_ies dummy_ie_desc;
 
        /*
         * Do not send DS Channel parameter for directed probe requests
@@ -1356,10 +1450,11 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
        if (!skb)
                return NULL;
 
+       rate_masks[chan->band] = ratemask;
        ies_len = ieee80211_build_preq_ies(local, skb_tail_pointer(skb),
-                                          skb_tailroom(skb),
-                                          ie, ie_len, chan->band,
-                                          ratemask, &chandef);
+                                          skb_tailroom(skb), &dummy_ie_desc,
+                                          ie, ie_len, BIT(chan->band),
+                                          rate_masks, &chandef);
        skb_put(skb, ies_len);
 
        if (dst) {
@@ -1603,7 +1698,9 @@ int ieee80211_reconfig(struct ieee80211_local *local)
        if (local->use_chanctx) {
                mutex_lock(&local->chanctx_mtx);
                list_for_each_entry(ctx, &local->chanctx_list, list)
-                       WARN_ON(drv_add_chanctx(local, ctx));
+                       if (ctx->replace_state !=
+                           IEEE80211_CHANCTX_REPLACES_OTHER)
+                               WARN_ON(drv_add_chanctx(local, ctx));
                mutex_unlock(&local->chanctx_mtx);
 
                list_for_each_entry(sdata, &local->interfaces, list) {
@@ -1797,7 +1894,8 @@ int ieee80211_reconfig(struct ieee80211_local *local)
        }
 
        ieee80211_wake_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP,
-                                       IEEE80211_QUEUE_STOP_REASON_SUSPEND);
+                                       IEEE80211_QUEUE_STOP_REASON_SUSPEND,
+                                       false);
 
        /*
         * Reconfigure sched scan if it was interrupted by FW restart or
@@ -2835,6 +2933,35 @@ void ieee80211_recalc_dtim(struct ieee80211_local *local,
        ps->dtim_count = dtim_count;
 }
 
+static u8 ieee80211_chanctx_radar_detect(struct ieee80211_local *local,
+                                        struct ieee80211_chanctx *ctx)
+{
+       struct ieee80211_sub_if_data *sdata;
+       u8 radar_detect = 0;
+
+       lockdep_assert_held(&local->chanctx_mtx);
+
+       if (WARN_ON(ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED))
+               return 0;
+
+       list_for_each_entry(sdata, &ctx->reserved_vifs, reserved_chanctx_list)
+               if (sdata->reserved_radar_required)
+                       radar_detect |= BIT(sdata->reserved_chandef.width);
+
+       /*
+        * An in-place reservation context should not have any assigned vifs
+        * until it replaces the other context.
+        */
+       WARN_ON(ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER &&
+               !list_empty(&ctx->assigned_vifs));
+
+       list_for_each_entry(sdata, &ctx->assigned_vifs, assigned_chanctx_list)
+               if (sdata->radar_required)
+                       radar_detect |= BIT(sdata->vif.bss_conf.chandef.width);
+
+       return radar_detect;
+}
+
 int ieee80211_check_combinations(struct ieee80211_sub_if_data *sdata,
                                 const struct cfg80211_chan_def *chandef,
                                 enum ieee80211_chanctx_mode chanmode,
@@ -2876,8 +3003,9 @@ int ieee80211_check_combinations(struct ieee80211_sub_if_data *sdata,
                num[iftype] = 1;
 
        list_for_each_entry(ctx, &local->chanctx_list, list) {
-               if (ctx->conf.radar_enabled)
-                       radar_detect |= BIT(ctx->conf.def.width);
+               if (ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED)
+                       continue;
+               radar_detect |= ieee80211_chanctx_radar_detect(local, ctx);
                if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) {
                        num_different_channels++;
                        continue;
@@ -2934,10 +3062,12 @@ int ieee80211_max_num_channels(struct ieee80211_local *local)
        lockdep_assert_held(&local->chanctx_mtx);
 
        list_for_each_entry(ctx, &local->chanctx_list, list) {
+               if (ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED)
+                       continue;
+
                num_different_channels++;
 
-               if (ctx->conf.radar_enabled)
-                       radar_detect |= BIT(ctx->conf.def.width);
+               radar_detect |= ieee80211_chanctx_radar_detect(local, ctx);
        }
 
        list_for_each_entry_rcu(sdata, &local->interfaces, list)
index 6ee2b58..9181fb6 100644 (file)
@@ -271,22 +271,6 @@ static int ieee80211_wep_decrypt(struct ieee80211_local *local,
        return ret;
 }
 
-
-static bool ieee80211_wep_is_weak_iv(struct sk_buff *skb,
-                                    struct ieee80211_key *key)
-{
-       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
-       unsigned int hdrlen;
-       u8 *ivpos;
-       u32 iv;
-
-       hdrlen = ieee80211_hdrlen(hdr->frame_control);
-       ivpos = skb->data + hdrlen;
-       iv = (ivpos[0] << 16) | (ivpos[1] << 8) | ivpos[2];
-
-       return ieee80211_wep_weak_iv(iv, key->conf.keylen);
-}
-
 ieee80211_rx_result
 ieee80211_crypto_wep_decrypt(struct ieee80211_rx_data *rx)
 {
@@ -301,16 +285,12 @@ ieee80211_crypto_wep_decrypt(struct ieee80211_rx_data *rx)
        if (!(status->flag & RX_FLAG_DECRYPTED)) {
                if (skb_linearize(rx->skb))
                        return RX_DROP_UNUSABLE;
-               if (rx->sta && ieee80211_wep_is_weak_iv(rx->skb, rx->key))
-                       rx->sta->wep_weak_iv_count++;
                if (ieee80211_wep_decrypt(rx->local, rx->skb, rx->key))
                        return RX_DROP_UNUSABLE;
        } else if (!(status->flag & RX_FLAG_IV_STRIPPED)) {
                if (!pskb_may_pull(rx->skb, ieee80211_hdrlen(fc) +
                                            IEEE80211_WEP_IV_LEN))
                        return RX_DROP_UNUSABLE;
-               if (rx->sta && ieee80211_wep_is_weak_iv(rx->skb, rx->key))
-                       rx->sta->wep_weak_iv_count++;
                ieee80211_wep_remove_iv(rx->local, rx->skb, rx->key);
                /* remove ICV */
                if (pskb_trim(rx->skb, rx->skb->len - IEEE80211_WEP_ICV_LEN))
index a1c4065..afee5e0 100644 (file)
@@ -25,7 +25,6 @@
 #include "sysfs.h"
 #include "debugfs.h"
 #include "wext-compat.h"
-#include "ethtool.h"
 #include "rdev-ops.h"
 
 /* name for sysfs, %d is appended */
@@ -927,8 +926,6 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
                /* allow mac80211 to determine the timeout */
                wdev->ps_timeout = -1;
 
-               netdev_set_default_ethtool_ops(dev, &cfg80211_ethtool_ops);
-
                if ((wdev->iftype == NL80211_IFTYPE_STATION ||
                     wdev->iftype == NL80211_IFTYPE_P2P_CLIENT ||
                     wdev->iftype == NL80211_IFTYPE_ADHOC) && !wdev->use_4addr)
index d4860bf..e9e9129 100644 (file)
@@ -1,11 +1,9 @@
 #include <linux/utsname.h>
 #include <net/cfg80211.h>
 #include "core.h"
-#include "ethtool.h"
 #include "rdev-ops.h"
 
-static void cfg80211_get_drvinfo(struct net_device *dev,
-                                       struct ethtool_drvinfo *info)
+void cfg80211_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
 
@@ -23,84 +21,4 @@ static void cfg80211_get_drvinfo(struct net_device *dev,
        strlcpy(info->bus_info, dev_name(wiphy_dev(wdev->wiphy)),
                sizeof(info->bus_info));
 }
-
-static int cfg80211_get_regs_len(struct net_device *dev)
-{
-       /* For now, return 0... */
-       return 0;
-}
-
-static void cfg80211_get_regs(struct net_device *dev, struct ethtool_regs *regs,
-                       void *data)
-{
-       struct wireless_dev *wdev = dev->ieee80211_ptr;
-
-       regs->version = wdev->wiphy->hw_version;
-       regs->len = 0;
-}
-
-static void cfg80211_get_ringparam(struct net_device *dev,
-                                  struct ethtool_ringparam *rp)
-{
-       struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
-
-       memset(rp, 0, sizeof(*rp));
-
-       if (rdev->ops->get_ringparam)
-               rdev_get_ringparam(rdev, &rp->tx_pending, &rp->tx_max_pending,
-                                  &rp->rx_pending, &rp->rx_max_pending);
-}
-
-static int cfg80211_set_ringparam(struct net_device *dev,
-                                 struct ethtool_ringparam *rp)
-{
-       struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
-
-       if (rp->rx_mini_pending != 0 || rp->rx_jumbo_pending != 0)
-               return -EINVAL;
-
-       if (rdev->ops->set_ringparam)
-               return rdev_set_ringparam(rdev, rp->tx_pending, rp->rx_pending);
-
-       return -ENOTSUPP;
-}
-
-static int cfg80211_get_sset_count(struct net_device *dev, int sset)
-{
-       struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
-       if (rdev->ops->get_et_sset_count)
-               return rdev_get_et_sset_count(rdev, dev, sset);
-       return -EOPNOTSUPP;
-}
-
-static void cfg80211_get_stats(struct net_device *dev,
-                              struct ethtool_stats *stats, u64 *data)
-{
-       struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
-       if (rdev->ops->get_et_stats)
-               rdev_get_et_stats(rdev, dev, stats, data);
-}
-
-static void cfg80211_get_strings(struct net_device *dev, u32 sset, u8 *data)
-{
-       struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
-       if (rdev->ops->get_et_strings)
-               rdev_get_et_strings(rdev, dev, sset, data);
-}
-
-const struct ethtool_ops cfg80211_ethtool_ops = {
-       .get_drvinfo = cfg80211_get_drvinfo,
-       .get_regs_len = cfg80211_get_regs_len,
-       .get_regs = cfg80211_get_regs,
-       .get_link = ethtool_op_get_link,
-       .get_ringparam = cfg80211_get_ringparam,
-       .set_ringparam = cfg80211_set_ringparam,
-       .get_strings = cfg80211_get_strings,
-       .get_ethtool_stats = cfg80211_get_stats,
-       .get_sset_count = cfg80211_get_sset_count,
-};
+EXPORT_SYMBOL(cfg80211_get_drvinfo);
diff --git a/net/wireless/ethtool.h b/net/wireless/ethtool.h
deleted file mode 100644 (file)
index 695ecad..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef __CFG80211_ETHTOOL__
-#define __CFG80211_ETHTOOL__
-
-extern const struct ethtool_ops cfg80211_ethtool_ops;
-
-#endif /* __CFG80211_ETHTOOL__ */
index ba4f172..c102951 100644 (file)
@@ -337,6 +337,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
        [NL80211_ATTR_TDLS_OPERATION] = { .type = NLA_U8 },
        [NL80211_ATTR_TDLS_SUPPORT] = { .type = NLA_FLAG },
        [NL80211_ATTR_TDLS_EXTERNAL_SETUP] = { .type = NLA_FLAG },
+       [NL80211_ATTR_TDLS_INITIATOR] = { .type = NLA_FLAG },
        [NL80211_ATTR_DONT_WAIT_FOR_ACK] = { .type = NLA_FLAG },
        [NL80211_ATTR_PROBE_RESP] = { .type = NLA_BINARY,
                                      .len = IEEE80211_MAX_DATA_LEN },
@@ -6012,17 +6013,6 @@ skip_beacons:
                params.radar_required = true;
        }
 
-       /* TODO: I left this here for now.  With channel switch, the
-        * verification is a bit more complicated, because we only do
-        * it later when the channel switch really happens.
-        */
-       err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
-                                          params.chandef.chan,
-                                          CHAN_MODE_SHARED,
-                                          radar_detect_width);
-       if (err)
-               return err;
-
        if (info->attrs[NL80211_ATTR_CH_SWITCH_BLOCK_TX])
                params.block_tx = true;
 
@@ -7365,6 +7355,7 @@ static int nl80211_tdls_mgmt(struct sk_buff *skb, struct genl_info *info)
        u32 peer_capability = 0;
        u16 status_code;
        u8 *peer;
+       bool initiator;
 
        if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) ||
            !rdev->ops->tdls_mgmt)
@@ -7381,12 +7372,14 @@ static int nl80211_tdls_mgmt(struct sk_buff *skb, struct genl_info *info)
        action_code = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_ACTION]);
        status_code = nla_get_u16(info->attrs[NL80211_ATTR_STATUS_CODE]);
        dialog_token = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_DIALOG_TOKEN]);
+       initiator = nla_get_flag(info->attrs[NL80211_ATTR_TDLS_INITIATOR]);
        if (info->attrs[NL80211_ATTR_TDLS_PEER_CAPABILITY])
                peer_capability =
                        nla_get_u32(info->attrs[NL80211_ATTR_TDLS_PEER_CAPABILITY]);
 
        return rdev_tdls_mgmt(rdev, dev, peer, action_code,
                              dialog_token, status_code, peer_capability,
+                             initiator,
                              nla_data(info->attrs[NL80211_ATTR_IE]),
                              nla_len(info->attrs[NL80211_ATTR_IE]));
 }
index d95bbe3..56c2240 100644 (file)
@@ -714,25 +714,6 @@ static inline int rdev_get_antenna(struct cfg80211_registered_device *rdev,
        return ret;
 }
 
-static inline int rdev_set_ringparam(struct cfg80211_registered_device *rdev,
-                                    u32 tx, u32 rx)
-{
-       int ret;
-       trace_rdev_set_ringparam(&rdev->wiphy, tx, rx);
-       ret = rdev->ops->set_ringparam(&rdev->wiphy, tx, rx);
-       trace_rdev_return_int(&rdev->wiphy, ret);
-       return ret;
-}
-
-static inline void rdev_get_ringparam(struct cfg80211_registered_device *rdev,
-                                     u32 *tx, u32 *tx_max, u32 *rx,
-                                     u32 *rx_max)
-{
-       trace_rdev_get_ringparam(&rdev->wiphy);
-       rdev->ops->get_ringparam(&rdev->wiphy, tx, tx_max, rx, rx_max);
-       trace_rdev_return_void_tx_rx(&rdev->wiphy, *tx, *tx_max, *rx, *rx_max);
-}
-
 static inline int
 rdev_sched_scan_start(struct cfg80211_registered_device *rdev,
                      struct net_device *dev,
@@ -770,15 +751,15 @@ static inline int rdev_tdls_mgmt(struct cfg80211_registered_device *rdev,
                                 struct net_device *dev, u8 *peer,
                                 u8 action_code, u8 dialog_token,
                                 u16 status_code, u32 peer_capability,
-                                const u8 *buf, size_t len)
+                                bool initiator, const u8 *buf, size_t len)
 {
        int ret;
        trace_rdev_tdls_mgmt(&rdev->wiphy, dev, peer, action_code,
                             dialog_token, status_code, peer_capability,
-                            buf, len);
+                            initiator, buf, len);
        ret = rdev->ops->tdls_mgmt(&rdev->wiphy, dev, peer, action_code,
                                   dialog_token, status_code, peer_capability,
-                                  buf, len);
+                                  initiator, buf, len);
        trace_rdev_return_int(&rdev->wiphy, ret);
        return ret;
 }
@@ -815,35 +796,6 @@ static inline int rdev_set_noack_map(struct cfg80211_registered_device *rdev,
        return ret;
 }
 
-static inline int
-rdev_get_et_sset_count(struct cfg80211_registered_device *rdev,
-                      struct net_device *dev, int sset)
-{
-       int ret;
-       trace_rdev_get_et_sset_count(&rdev->wiphy, dev, sset);
-       ret = rdev->ops->get_et_sset_count(&rdev->wiphy, dev, sset);
-       trace_rdev_return_int(&rdev->wiphy, ret);
-       return ret;
-}
-
-static inline void rdev_get_et_stats(struct cfg80211_registered_device *rdev,
-                                    struct net_device *dev,
-                                    struct ethtool_stats *stats, u64 *data)
-{
-       trace_rdev_get_et_stats(&rdev->wiphy, dev);
-       rdev->ops->get_et_stats(&rdev->wiphy, dev, stats, data);
-       trace_rdev_return_void(&rdev->wiphy);
-}
-
-static inline void rdev_get_et_strings(struct cfg80211_registered_device *rdev,
-                                      struct net_device *dev, u32 sset,
-                                      u8 *data)
-{
-       trace_rdev_get_et_strings(&rdev->wiphy, dev, sset);
-       rdev->ops->get_et_strings(&rdev->wiphy, dev, sset, data);
-       trace_rdev_return_void(&rdev->wiphy);
-}
-
 static inline int
 rdev_get_channel(struct cfg80211_registered_device *rdev,
                 struct wireless_dev *wdev,
index 560ed77..85474ee 100644 (file)
@@ -298,11 +298,6 @@ DEFINE_EVENT(wiphy_only_evt, rdev_return_void,
        TP_ARGS(wiphy)
 );
 
-DEFINE_EVENT(wiphy_only_evt, rdev_get_ringparam,
-       TP_PROTO(struct wiphy *wiphy),
-       TP_ARGS(wiphy)
-);
-
 DEFINE_EVENT(wiphy_only_evt, rdev_get_antenna,
        TP_PROTO(struct wiphy *wiphy),
        TP_ARGS(wiphy)
@@ -580,11 +575,6 @@ DEFINE_EVENT(wiphy_netdev_evt, rdev_stop_ap,
        TP_ARGS(wiphy, netdev)
 );
 
-DEFINE_EVENT(wiphy_netdev_evt, rdev_get_et_stats,
-       TP_PROTO(struct wiphy *wiphy, struct net_device *netdev),
-       TP_ARGS(wiphy, netdev)
-);
-
 DEFINE_EVENT(wiphy_netdev_evt, rdev_sched_scan_stop,
        TP_PROTO(struct wiphy *wiphy, struct net_device *netdev),
        TP_ARGS(wiphy, netdev)
@@ -1439,11 +1429,6 @@ DECLARE_EVENT_CLASS(tx_rx_evt,
                  WIPHY_PR_ARG, __entry->tx, __entry->rx)
 );
 
-DEFINE_EVENT(tx_rx_evt, rdev_set_ringparam,
-       TP_PROTO(struct wiphy *wiphy, u32 tx, u32 rx),
-       TP_ARGS(wiphy, rx, tx)
-);
-
 DEFINE_EVENT(tx_rx_evt, rdev_set_antenna,
        TP_PROTO(struct wiphy *wiphy, u32 tx, u32 rx),
        TP_ARGS(wiphy, rx, tx)
@@ -1469,9 +1454,9 @@ TRACE_EVENT(rdev_tdls_mgmt,
        TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
                 u8 *peer, u8 action_code, u8 dialog_token,
                 u16 status_code, u32 peer_capability,
-                const u8 *buf, size_t len),
+                bool initiator, const u8 *buf, size_t len),
        TP_ARGS(wiphy, netdev, peer, action_code, dialog_token, status_code,
-               peer_capability, buf, len),
+               peer_capability, initiator, buf, len),
        TP_STRUCT__entry(
                WIPHY_ENTRY
                NETDEV_ENTRY
@@ -1480,6 +1465,7 @@ TRACE_EVENT(rdev_tdls_mgmt,
                __field(u8, dialog_token)
                __field(u16, status_code)
                __field(u32, peer_capability)
+               __field(bool, initiator)
                __dynamic_array(u8, buf, len)
        ),
        TP_fast_assign(
@@ -1490,13 +1476,16 @@ TRACE_EVENT(rdev_tdls_mgmt,
                __entry->dialog_token = dialog_token;
                __entry->status_code = status_code;
                __entry->peer_capability = peer_capability;
+               __entry->initiator = initiator;
                memcpy(__get_dynamic_array(buf), buf, len);
        ),
        TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " MAC_PR_FMT ", action_code: %u, "
-                 "dialog_token: %u, status_code: %u, peer_capability: %u buf: %#.2x ",
+                 "dialog_token: %u, status_code: %u, peer_capability: %u "
+                 "initiator: %s buf: %#.2x ",
                  WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer),
                  __entry->action_code, __entry->dialog_token,
                  __entry->status_code, __entry->peer_capability,
+                 BOOL_TO_STR(__entry->initiator),
                  ((u8 *)__get_dynamic_array(buf))[0])
 );
 
@@ -1725,40 +1714,6 @@ TRACE_EVENT(rdev_set_noack_map,
                  WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->noack_map)
 );
 
-TRACE_EVENT(rdev_get_et_sset_count,
-       TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int sset),
-       TP_ARGS(wiphy, netdev, sset),
-       TP_STRUCT__entry(
-               WIPHY_ENTRY
-               NETDEV_ENTRY
-               __field(int, sset)
-       ),
-       TP_fast_assign(
-               WIPHY_ASSIGN;
-               NETDEV_ASSIGN;
-               __entry->sset = sset;
-       ),
-       TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", sset: %d",
-                 WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->sset)
-);
-
-TRACE_EVENT(rdev_get_et_strings,
-       TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u32 sset),
-       TP_ARGS(wiphy, netdev, sset),
-       TP_STRUCT__entry(
-               WIPHY_ENTRY
-               NETDEV_ENTRY
-               __field(u32, sset)
-       ),
-       TP_fast_assign(
-               WIPHY_ASSIGN;
-               NETDEV_ASSIGN;
-               __entry->sset = sset;
-       ),
-       TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", sset: %u",
-                 WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->sset)
-);
-
 DEFINE_EVENT(wiphy_wdev_evt, rdev_get_channel,
        TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev),
        TP_ARGS(wiphy, wdev)