]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
Merge remote-tracking branch 'mac80211-next/master'
authorStephen Rothwell <sfr@canb.auug.org.au>
Thu, 11 Feb 2016 01:04:35 +0000 (12:04 +1100)
committerStephen Rothwell <sfr@canb.auug.org.au>
Thu, 11 Feb 2016 01:04:35 +0000 (12:04 +1100)
62 files changed:
Documentation/ABI/obsolete/sysfs-class-rfkill
Documentation/ABI/removed/sysfs-class-rfkill [new file with mode: 0644]
Documentation/networking/mac80211-injection.txt
drivers/net/wireless/ath/ath10k/mac.c
drivers/net/wireless/ath/ath9k/htc_drv_main.c
drivers/net/wireless/ath/ath9k/main.c
drivers/net/wireless/ath/carl9170/main.c
drivers/net/wireless/ath/wcn36xx/main.c
drivers/net/wireless/ath/wil6210/cfg80211.c
drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c
drivers/net/wireless/intel/iwlegacy/4965-mac.c
drivers/net/wireless/intel/iwlegacy/4965.h
drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c
drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
drivers/net/wireless/mac80211_hwsim.c
drivers/net/wireless/marvell/mwl8k.c
drivers/net/wireless/mediatek/mt7601u/main.c
drivers/net/wireless/ralink/rt2x00/rt2800lib.c
drivers/net/wireless/ralink/rt2x00/rt2800lib.h
drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.c
drivers/net/wireless/realtek/rtlwifi/core.c
drivers/net/wireless/rsi/rsi_91x_mac80211.c
drivers/net/wireless/st/cw1200/sta.c
drivers/net/wireless/st/cw1200/sta.h
drivers/net/wireless/ti/wlcore/main.c
include/linux/ieee80211.h
include/linux/rfkill.h
include/net/cfg80211.h
include/net/mac80211.h
include/uapi/linux/nl80211.h
include/uapi/linux/rfkill.h
net/mac80211/agg-rx.c
net/mac80211/agg-tx.c
net/mac80211/cfg.c
net/mac80211/chan.c
net/mac80211/debugfs.c
net/mac80211/driver-ops.c
net/mac80211/driver-ops.h
net/mac80211/ht.c
net/mac80211/ibss.c
net/mac80211/ieee80211_i.h
net/mac80211/iface.c
net/mac80211/mesh.c
net/mac80211/mesh.h
net/mac80211/mesh_pathtbl.c
net/mac80211/mesh_plink.c
net/mac80211/mlme.c
net/mac80211/rx.c
net/mac80211/sta_info.c
net/mac80211/sta_info.h
net/mac80211/trace.h
net/mac80211/tx.c
net/mac80211/util.c
net/mac80211/vht.c
net/rfkill/core.c
net/wireless/Kconfig
net/wireless/core.c
net/wireless/mlme.c
net/wireless/nl80211.c
net/wireless/reg.c
net/wireless/sme.c
net/wireless/util.c

index ff60ad9eca4c7ba390e22bd0eda0378bfbe236f1..e736d145085f302b031f4aa40ff3ca0e0c5fe060 100644 (file)
@@ -18,12 +18,3 @@ Values:      A numeric value.
                2: RFKILL_STATE_HARD_BLOCKED
                        transmitter is forced off by something outside of
                        the driver's control.
-
-What:          /sys/class/rfkill/rfkill[0-9]+/claim
-Date:          09-Jul-2007
-KernelVersion  v2.6.22
-Contact:       linux-wireless@vger.kernel.org
-Description:   This file is deprecated because there no longer is a way to
-               claim just control over a single rfkill instance.
-               This file is scheduled to be removed in 2012.
-Values:        0: Kernel handles events
diff --git a/Documentation/ABI/removed/sysfs-class-rfkill b/Documentation/ABI/removed/sysfs-class-rfkill
new file mode 100644 (file)
index 0000000..3ce6231
--- /dev/null
@@ -0,0 +1,13 @@
+rfkill - radio frequency (RF) connector kill switch support
+
+For details to this subsystem look at Documentation/rfkill.txt.
+
+What:          /sys/class/rfkill/rfkill[0-9]+/claim
+Date:          09-Jul-2007
+KernelVersion  v2.6.22
+Contact:       linux-wireless@vger.kernel.org
+Description:   This file was deprecated because there no longer was a way to
+               claim just control over a single rfkill instance.
+               This file was scheduled to be removed in 2012, and was removed
+               in 2016.
+Values:        0: Kernel handles events
index 3a930072b161fd51b3b1d007daac673526609dc2..ec8f934c2eb240f93a54b0044faadd913e7855f8 100644 (file)
@@ -28,6 +28,23 @@ radiotap headers and used to control injection:
    IEEE80211_RADIOTAP_F_TX_NOACK: frame should be sent without waiting for
                                  an ACK even if it is a unicast frame
 
+ * IEEE80211_RADIOTAP_RATE
+
+   legacy rate for the transmission (only for devices without own rate control)
+
+ * IEEE80211_RADIOTAP_MCS
+
+   HT rate for the transmission (only for devices without own rate control).
+   Also some flags are parsed
+
+   IEEE80211_TX_RC_SHORT_GI: use short guard interval
+   IEEE80211_TX_RC_40_MHZ_WIDTH: send in HT40 mode
+
+ * IEEE80211_RADIOTAP_DATA_RETRIES
+
+   number of retries when either IEEE80211_RADIOTAP_RATE or
+   IEEE80211_RADIOTAP_MCS was used
+
 The injection code can also skip all other currently defined radiotap fields
 facilitating replay of captured radiotap headers directly.
 
index 6146a293601a7fea098a353aeb11fc803f5523a4..368de5e5a04f64c46003a131a49546724d6fc7a6 100644 (file)
@@ -6366,12 +6366,13 @@ static u64 ath10k_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
 
 static int ath10k_ampdu_action(struct ieee80211_hw *hw,
                               struct ieee80211_vif *vif,
-                              enum ieee80211_ampdu_mlme_action action,
-                              struct ieee80211_sta *sta, u16 tid, u16 *ssn,
-                              u8 buf_size, bool amsdu)
+                              struct ieee80211_ampdu_params *params)
 {
        struct ath10k *ar = hw->priv;
        struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       struct ieee80211_sta *sta = params->sta;
+       enum ieee80211_ampdu_mlme_action action = params->action;
+       u16 tid = params->tid;
 
        ath10k_dbg(ar, ATH10K_DBG_MAC, "mac ampdu vdev_id %i sta %pM tid %hu action %d\n",
                   arvif->vdev_id, sta->addr, tid, action);
index fe1fd1a5ae1502a2c5d2947d1a40ff3ee6193d76..639294a9e34df6b2461b4c4c8052af75cbf2b258 100644 (file)
@@ -1657,13 +1657,14 @@ static void ath9k_htc_reset_tsf(struct ieee80211_hw *hw,
 
 static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw,
                                  struct ieee80211_vif *vif,
-                                 enum ieee80211_ampdu_mlme_action action,
-                                 struct ieee80211_sta *sta,
-                                 u16 tid, u16 *ssn, u8 buf_size, bool amsdu)
+                                 struct ieee80211_ampdu_params *params)
 {
        struct ath9k_htc_priv *priv = hw->priv;
        struct ath9k_htc_sta *ista;
        int ret = 0;
+       struct ieee80211_sta *sta = params->sta;
+       enum ieee80211_ampdu_mlme_action action = params->action;
+       u16 tid = params->tid;
 
        mutex_lock(&priv->mutex);
        ath9k_htc_ps_wakeup(priv);
index c1b33fdcca0875a20cd2be3b54b21e46c37b725c..cf58a304e9f0aa08d577dc00d49361b568f450d3 100644 (file)
@@ -1864,14 +1864,16 @@ static void ath9k_reset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
 
 static int ath9k_ampdu_action(struct ieee80211_hw *hw,
                              struct ieee80211_vif *vif,
-                             enum ieee80211_ampdu_mlme_action action,
-                             struct ieee80211_sta *sta,
-                             u16 tid, u16 *ssn, u8 buf_size, bool amsdu)
+                             struct ieee80211_ampdu_params *params)
 {
        struct ath_softc *sc = hw->priv;
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
        bool flush = false;
        int ret = 0;
+       struct ieee80211_sta *sta = params->sta;
+       enum ieee80211_ampdu_mlme_action action = params->action;
+       u16 tid = params->tid;
+       u16 *ssn = &params->ssn;
 
        mutex_lock(&sc->mutex);
 
index 19d3d64416bf66825eb113590474b4e4a68f8ec2..4d1527a2e292a2ba2d29907554625cf0fbb980f4 100644 (file)
@@ -1413,10 +1413,12 @@ static void carl9170_ampdu_work(struct work_struct *work)
 
 static int carl9170_op_ampdu_action(struct ieee80211_hw *hw,
                                    struct ieee80211_vif *vif,
-                                   enum ieee80211_ampdu_mlme_action action,
-                                   struct ieee80211_sta *sta,
-                                   u16 tid, u16 *ssn, u8 buf_size, bool amsdu)
+                                   struct ieee80211_ampdu_params *params)
 {
+       struct ieee80211_sta *sta = params->sta;
+       enum ieee80211_ampdu_mlme_action action = params->action;
+       u16 tid = params->tid;
+       u16 *ssn = &params->ssn;
        struct ar9170 *ar = hw->priv;
        struct carl9170_sta_info *sta_info = (void *) sta->drv_priv;
        struct carl9170_sta_tid *tid_info;
index 7c169abdbafee95ccfe8f461e670fbb8ab5ca5eb..a27279c2c6950913c0b1f14fd4cacdd3bc650428 100644 (file)
@@ -857,12 +857,14 @@ static int wcn36xx_resume(struct ieee80211_hw *hw)
 
 static int wcn36xx_ampdu_action(struct ieee80211_hw *hw,
                    struct ieee80211_vif *vif,
-                   enum ieee80211_ampdu_mlme_action action,
-                   struct ieee80211_sta *sta, u16 tid, u16 *ssn,
-                   u8 buf_size, bool amsdu)
+                   struct ieee80211_ampdu_params *params)
 {
        struct wcn36xx *wcn = hw->priv;
        struct wcn36xx_sta *sta_priv = NULL;
+       struct ieee80211_sta *sta = params->sta;
+       enum ieee80211_ampdu_mlme_action action = params->action;
+       u16 tid = params->tid;
+       u16 *ssn = &params->ssn;
 
        wcn36xx_dbg(WCN36XX_DBG_MAC, "mac ampdu action action %d tid %d\n",
                    action, tid);
index 20d07ef679e89d467170abae532819f36f283821..1f231cd0813861fa24bafdb378e2e8cbb0ed3ae1 100644 (file)
@@ -422,6 +422,11 @@ static int wil_cfg80211_connect(struct wiphy *wiphy,
        if (sme->privacy && !rsn_eid)
                wil_info(wil, "WSC connection\n");
 
+       if (sme->pbss) {
+               wil_err(wil, "connect - PBSS not yet supported\n");
+               return -EOPNOTSUPP;
+       }
+
        bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid,
                               sme->ssid, sme->ssid_len,
                               IEEE80211_BSS_TYPE_ESS, IEEE80211_PRIVACY_ANY);
@@ -870,6 +875,11 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy,
                return -EINVAL;
        }
 
+       if (info->pbss) {
+               wil_err(wil, "AP: PBSS not yet supported\n");
+               return -EOPNOTSUPP;
+       }
+
        switch (info->hidden_ssid) {
        case NL80211_HIDDEN_SSID_NOT_IN_USE:
                hidden_ssid = WMI_HIDDEN_SSID_DISABLED;
index bec2dc1ca2e406bcf4ac0ee0d00fa27a704cf7f2..61ae2768132a0b16f0f98a57b3319c82adfda95b 100644 (file)
@@ -818,13 +818,15 @@ brcms_ops_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
 static int
 brcms_ops_ampdu_action(struct ieee80211_hw *hw,
                    struct ieee80211_vif *vif,
-                   enum ieee80211_ampdu_mlme_action action,
-                   struct ieee80211_sta *sta, u16 tid, u16 *ssn,
-                   u8 buf_size, bool amsdu)
+                   struct ieee80211_ampdu_params *params)
 {
        struct brcms_info *wl = hw->priv;
        struct scb *scb = &wl->wlc->pri_scb;
        int status;
+       struct ieee80211_sta *sta = params->sta;
+       enum ieee80211_ampdu_mlme_action action = params->action;
+       u16 tid = params->tid;
+       u8 buf_size = params->buf_size;
 
        if (WARN_ON(scb->magic != SCB_MAGIC))
                return -EIDRM;
index fd38aa0763e4f5f30384d29249e7bf205b7d5eeb..b75f4ef3cdc7b278ec837f83fd576444f5ce0008 100644 (file)
@@ -5982,12 +5982,14 @@ il4965_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
 
 int
 il4965_mac_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-                       enum ieee80211_ampdu_mlme_action action,
-                       struct ieee80211_sta *sta, u16 tid, u16 * ssn,
-                       u8 buf_size, bool amsdu)
+                       struct ieee80211_ampdu_params *params)
 {
        struct il_priv *il = hw->priv;
        int ret = -EINVAL;
+       struct ieee80211_sta *sta = params->sta;
+       enum ieee80211_ampdu_mlme_action action = params->action;
+       u16 tid = params->tid;
+       u16 *ssn = &params->ssn;
 
        D_HT("A-MPDU action on addr %pM tid %d\n", sta->addr, tid);
 
index 8ab8706f942267fcd2467928b1cbdf248a05921f..e432715e02d89dedcce89615d855233662c26d82 100644 (file)
@@ -182,9 +182,7 @@ void il4965_mac_update_tkip_key(struct ieee80211_hw *hw,
                                struct ieee80211_sta *sta, u32 iv32,
                                u16 *phase1key);
 int il4965_mac_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-                           enum ieee80211_ampdu_mlme_action action,
-                           struct ieee80211_sta *sta, u16 tid, u16 * ssn,
-                           u8 buf_size, bool amsdu);
+                           struct ieee80211_ampdu_params *params);
 int il4965_mac_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                       struct ieee80211_sta *sta);
 void
index 4db4cb7aa73aa8593b0a3061281ab2c013885690..c63ea79571ff5f0d6b0bbfd07b4a6faba42f1593 100644 (file)
@@ -732,12 +732,15 @@ static inline bool iwl_enable_tx_ampdu(const struct iwl_cfg *cfg)
 
 static int iwlagn_mac_ampdu_action(struct ieee80211_hw *hw,
                                   struct ieee80211_vif *vif,
-                                  enum ieee80211_ampdu_mlme_action action,
-                                  struct ieee80211_sta *sta, u16 tid, u16 *ssn,
-                                  u8 buf_size, bool amsdu)
+                                  struct ieee80211_ampdu_params *params)
 {
        struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
        int ret = -EINVAL;
+       struct ieee80211_sta *sta = params->sta;
+       enum ieee80211_ampdu_mlme_action action = params->action;
+       u16 tid = params->tid;
+       u16 *ssn = &params->ssn;
+       u8 buf_size = params->buf_size;
        struct iwl_station_priv *sta_priv = (void *) sta->drv_priv;
 
        IWL_DEBUG_HT(priv, "A-MPDU action on addr %pM tid %d\n",
index 01476f5456956b199b262d58efa840eda62dbdb2..24c46c2232463d5b42d2314ce30da5aaaf13d964 100644 (file)
@@ -837,13 +837,16 @@ iwl_mvm_ampdu_check_trigger(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
 
 static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw,
                                    struct ieee80211_vif *vif,
-                                   enum ieee80211_ampdu_mlme_action action,
-                                   struct ieee80211_sta *sta, u16 tid,
-                                   u16 *ssn, u8 buf_size, bool amsdu)
+                                   struct ieee80211_ampdu_params *params)
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
        int ret;
        bool tx_agg_ref = false;
+       struct ieee80211_sta *sta = params->sta;
+       enum ieee80211_ampdu_mlme_action action = params->action;
+       u16 tid = params->tid;
+       u16 *ssn = &params->ssn;
+       u8 buf_size = params->buf_size;
 
        IWL_DEBUG_HT(mvm, "A-MPDU action on addr %pM tid %d: action %d\n",
                     sta->addr, tid, action);
index a28414c50edf11131e41aa65f02957a5a07c796f..a723a85f5635716aae4afa86cc1d45657f899af0 100644 (file)
@@ -1334,10 +1334,8 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw,
        data->tx_bytes += skb->len;
        ack = mac80211_hwsim_tx_frame_no_nl(hw, skb, channel);
 
-       if (ack && skb->len >= 16) {
-               struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+       if (ack && skb->len >= 16)
                mac80211_hwsim_monitor_ack(channel, hdr->addr2);
-       }
 
        ieee80211_tx_info_clear_status(txi);
 
@@ -1846,10 +1844,12 @@ static int mac80211_hwsim_testmode_cmd(struct ieee80211_hw *hw,
 
 static int mac80211_hwsim_ampdu_action(struct ieee80211_hw *hw,
                                       struct ieee80211_vif *vif,
-                                      enum ieee80211_ampdu_mlme_action action,
-                                      struct ieee80211_sta *sta, u16 tid, u16 *ssn,
-                                      u8 buf_size, bool amsdu)
+                                      struct ieee80211_ampdu_params *params)
 {
+       struct ieee80211_sta *sta = params->sta;
+       enum ieee80211_ampdu_mlme_action action = params->action;
+       u16 tid = params->tid;
+
        switch (action) {
        case IEEE80211_AMPDU_TX_START:
                ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
index 30e3aaae32e2288ed1f7541b6da7a42eb6a8729a..088429d0a634d8c4372d45e2dc1cd3b7d2fabf1e 100644 (file)
@@ -5421,11 +5421,13 @@ static int mwl8k_get_survey(struct ieee80211_hw *hw, int idx,
 
 static int
 mwl8k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-                  enum ieee80211_ampdu_mlme_action action,
-                  struct ieee80211_sta *sta, u16 tid, u16 *ssn,
-                  u8 buf_size, bool amsdu)
+                  struct ieee80211_ampdu_params *params)
 {
-
+       struct ieee80211_sta *sta = params->sta;
+       enum ieee80211_ampdu_mlme_action action = params->action;
+       u16 tid = params->tid;
+       u16 *ssn = &params->ssn;
+       u8 buf_size = params->buf_size;
        int i, rc = 0;
        struct mwl8k_priv *priv = hw->priv;
        struct mwl8k_ampdu_stream *stream;
index f715eee398510df829a61ba0f431610d1ab32baa..e70dd95239117f7e0ef8a2d668b6798727a06b46 100644 (file)
@@ -334,11 +334,13 @@ static int mt7601u_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
 
 static int
 mt76_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-                 enum ieee80211_ampdu_mlme_action action,
-                 struct ieee80211_sta *sta, u16 tid, u16 *ssn, u8 buf_size,
-                 bool amsdu)
+                 struct ieee80211_ampdu_params *params)
 {
        struct mt7601u_dev *dev = hw->priv;
+       struct ieee80211_sta *sta = params->sta;
+       enum ieee80211_ampdu_mlme_action action = params->action;
+       u16 tid = params->tid;
+       u16 *ssn = &params->ssn;
        struct mt76_sta *msta = (struct mt76_sta *) sta->drv_priv;
 
        WARN_ON(msta->wcid.idx > GROUP_WCID(0));
index a26afcab03ed02242fdc7fa2f3b94307fc52024e..7fa0128de7e310be2978b35a97746bc011acb324 100644 (file)
@@ -7936,10 +7936,11 @@ u64 rt2800_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
 EXPORT_SYMBOL_GPL(rt2800_get_tsf);
 
 int rt2800_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-                       enum ieee80211_ampdu_mlme_action action,
-                       struct ieee80211_sta *sta, u16 tid, u16 *ssn,
-                       u8 buf_size, bool amsdu)
+                       struct ieee80211_ampdu_params *params)
 {
+       struct ieee80211_sta *sta = params->sta;
+       enum ieee80211_ampdu_mlme_action action = params->action;
+       u16 tid = params->tid;
        struct rt2x00_sta *sta_priv = (struct rt2x00_sta *)sta->drv_priv;
        int ret = 0;
 
index 440790b92b19e2927fd9b9d7446cbd3c8eef4f30..83f1a44fb9b481cb5f2a8dda9e9914b8113e91bb 100644 (file)
@@ -218,9 +218,7 @@ int rt2800_conf_tx(struct ieee80211_hw *hw,
                   const struct ieee80211_tx_queue_params *params);
 u64 rt2800_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
 int rt2800_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-                       enum ieee80211_ampdu_mlme_action action,
-                       struct ieee80211_sta *sta, u16 tid, u16 *ssn,
-                       u8 buf_size, bool amsdu);
+                       struct ieee80211_ampdu_params *params);
 int rt2800_get_survey(struct ieee80211_hw *hw, int idx,
                      struct survey_info *survey);
 void rt2800_disable_wpdma(struct rt2x00_dev *rt2x00dev);
index 6aed923a709ae3606225307cdcb2b7170442e11d..7d820c3953754260c05a5f7e21478e1cbf14555e 100644 (file)
@@ -5375,13 +5375,13 @@ static int rtl8xxxu_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
 
 static int
 rtl8xxxu_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-                     enum ieee80211_ampdu_mlme_action action,
-                     struct ieee80211_sta *sta, u16 tid, u16 *ssn, u8 buf_size,
-                     bool amsdu)
+                     struct ieee80211_ampdu_params *params)
 {
        struct rtl8xxxu_priv *priv = hw->priv;
        struct device *dev = &priv->udev->dev;
        u8 ampdu_factor, ampdu_density;
+       struct ieee80211_sta *sta = params->sta;
+       enum ieee80211_ampdu_mlme_action action = params->action;
 
        switch (action) {
        case IEEE80211_AMPDU_TX_START:
index 4ae421ef30d94f4f8bc93d85645eeda4548ba835..f2507610314ba81b94accf2a94cfb0284c11b641 100644 (file)
@@ -1371,11 +1371,13 @@ static void rtl_op_sta_notify(struct ieee80211_hw *hw,
 
 static int rtl_op_ampdu_action(struct ieee80211_hw *hw,
                               struct ieee80211_vif *vif,
-                              enum ieee80211_ampdu_mlme_action action,
-                              struct ieee80211_sta *sta, u16 tid, u16 *ssn,
-                              u8 buf_size, bool amsdu)
+                              struct ieee80211_ampdu_params *params)
 {
        struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct ieee80211_sta *sta = params->sta;
+       enum ieee80211_ampdu_mlme_action action = params->action;
+       u16 tid = params->tid;
+       u16 *ssn = &params->ssn;
 
        switch (action) {
        case IEEE80211_AMPDU_TX_START:
index b5bcc933a2a683df412139b1204fee320096a889..4df992de7d0731508a6c42fccf51096928156e9e 100644 (file)
@@ -659,29 +659,24 @@ static int rsi_mac80211_set_key(struct ieee80211_hw *hw,
  *                              informs the f/w regarding this.
  * @hw: Pointer to the ieee80211_hw structure.
  * @vif: Pointer to the ieee80211_vif structure.
- * @action: ieee80211_ampdu_mlme_action enum.
- * @sta: Pointer to the ieee80211_sta structure.
- * @tid: Traffic identifier.
- * @ssn: Pointer to ssn value.
- * @buf_size: Buffer size (for kernel version > 2.6.38).
- * @amsdu: is AMSDU in AMPDU allowed
+ * @params: Pointer to A-MPDU action parameters
  *
  * Return: status: 0 on success, negative error code on failure.
  */
 static int rsi_mac80211_ampdu_action(struct ieee80211_hw *hw,
                                     struct ieee80211_vif *vif,
-                                    enum ieee80211_ampdu_mlme_action action,
-                                    struct ieee80211_sta *sta,
-                                    unsigned short tid,
-                                    unsigned short *ssn,
-                                    unsigned char buf_size,
-                                    bool amsdu)
+                                    struct ieee80211_ampdu_params *params)
 {
        int status = -EOPNOTSUPP;
        struct rsi_hw *adapter = hw->priv;
        struct rsi_common *common = adapter->priv;
        u16 seq_no = 0;
        u8 ii = 0;
+       struct ieee80211_sta *sta = params->sta;
+       enum ieee80211_ampdu_mlme_action action = params->action;
+       u16 tid = params->tid;
+       u16 *ssn = &params->ssn;
+       u8 buf_size = params->buf_size;
 
        for (ii = 0; ii < RSI_MAX_VIFS; ii++) {
                if (vif == adapter->vifs[ii])
index 06321c799c902689825c89098c796e875c737079..d0ddcde6c695fb6c5efd9b2283c76e98328fdbdf 100644 (file)
@@ -2129,9 +2129,7 @@ void cw1200_mcast_timeout(unsigned long arg)
 
 int cw1200_ampdu_action(struct ieee80211_hw *hw,
                        struct ieee80211_vif *vif,
-                       enum ieee80211_ampdu_mlme_action action,
-                       struct ieee80211_sta *sta, u16 tid, u16 *ssn,
-                       u8 buf_size, bool amsdu)
+                       struct ieee80211_ampdu_params *params)
 {
        /* Aggregation is implemented fully in firmware,
         * including block ack negotiation. Do not allow
index bebb3379017f6d40c3cd58b92ddfaabab1259f59..a0bacaa39b3193f230c61526afe6bdc68f38b7a0 100644 (file)
@@ -109,9 +109,7 @@ void cw1200_bss_info_changed(struct ieee80211_hw *dev,
                             u32 changed);
 int cw1200_ampdu_action(struct ieee80211_hw *hw,
                        struct ieee80211_vif *vif,
-                       enum ieee80211_ampdu_mlme_action action,
-                       struct ieee80211_sta *sta, u16 tid, u16 *ssn,
-                       u8 buf_size, bool amsdu);
+                       struct ieee80211_ampdu_params *params);
 
 void cw1200_suspend_resume(struct cw1200_common *priv,
                          struct wsm_suspend_resume *arg);
index d1109c4f0f0d1d7570ec373734db3f93117556f9..45662cf3169f78dd20554cde3373336f84cb6516 100644 (file)
@@ -5187,14 +5187,16 @@ out:
 
 static int wl1271_op_ampdu_action(struct ieee80211_hw *hw,
                                  struct ieee80211_vif *vif,
-                                 enum ieee80211_ampdu_mlme_action action,
-                                 struct ieee80211_sta *sta, u16 tid, u16 *ssn,
-                                 u8 buf_size, bool amsdu)
+                                 struct ieee80211_ampdu_params *params)
 {
        struct wl1271 *wl = hw->priv;
        struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
        int ret;
        u8 hlid, *ba_bitmap;
+       struct ieee80211_sta *sta = params->sta;
+       enum ieee80211_ampdu_mlme_action action = params->action;
+       u16 tid = params->tid;
+       u16 *ssn = &params->ssn;
 
        wl1271_debug(DEBUG_MAC80211, "mac80211 ampdu action %d tid %d", action,
                     tid);
index 452c0b0d2f3219dc0f11bec80878e5ea0b8e8803..3b1f6cef95136fa381d06eb5ccb409dde3e030a0 100644 (file)
@@ -163,6 +163,14 @@ static inline u16 ieee80211_sn_sub(u16 sn1, u16 sn2)
 /* 30 byte 4 addr hdr, 2 byte QoS, 2304 byte MSDU, 12 byte crypt, 4 byte FCS */
 #define IEEE80211_MAX_FRAME_LEN                2352
 
+/* Maximal size of an A-MSDU */
+#define IEEE80211_MAX_MPDU_LEN_HT_3839         3839
+#define IEEE80211_MAX_MPDU_LEN_HT_7935         7935
+
+#define IEEE80211_MAX_MPDU_LEN_VHT_3895                3895
+#define IEEE80211_MAX_MPDU_LEN_VHT_7991                7991
+#define IEEE80211_MAX_MPDU_LEN_VHT_11454       11454
+
 #define IEEE80211_MAX_SSID_LEN         32
 
 #define IEEE80211_MAX_MESH_ID_LEN      32
@@ -843,6 +851,8 @@ enum ieee80211_vht_opmode_bits {
 };
 
 #define WLAN_SA_QUERY_TR_ID_LEN 2
+#define WLAN_MEMBERSHIP_LEN 8
+#define WLAN_USER_POSITION_LEN 16
 
 /**
  * struct ieee80211_tpc_report_ie
@@ -989,6 +999,11 @@ struct ieee80211_mgmt {
                                        u8 action_code;
                                        u8 operating_mode;
                                } __packed vht_opmode_notif;
+                               struct {
+                                       u8 action_code;
+                                       u8 membership[WLAN_MEMBERSHIP_LEN];
+                                       u8 position[WLAN_USER_POSITION_LEN];
+                               } __packed vht_group_notif;
                                struct {
                                        u8 action_code;
                                        u8 dialog_token;
@@ -1498,6 +1513,7 @@ struct ieee80211_vht_operation {
 #define IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895                 0x00000000
 #define IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991                 0x00000001
 #define IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454                        0x00000002
+#define IEEE80211_VHT_CAP_MAX_MPDU_MASK                                0x00000003
 #define IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ               0x00000004
 #define IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ      0x00000008
 #define IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK                 0x0000000C
@@ -2079,6 +2095,16 @@ enum ieee80211_tdls_actioncode {
 #define WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED    BIT(5)
 #define WLAN_EXT_CAPA8_OPMODE_NOTIF    BIT(6)
 
+/* Defines the maximal number of MSDUs in an A-MSDU. */
+#define WLAN_EXT_CAPA8_MAX_MSDU_IN_AMSDU_LSB   BIT(7)
+#define WLAN_EXT_CAPA9_MAX_MSDU_IN_AMSDU_MSB   BIT(0)
+
+/*
+ * Fine Timing Measurement Initiator - bit 71 of @WLAN_EID_EXT_CAPABILITY
+ * information element
+ */
+#define WLAN_EXT_CAPA9_FTM_INITIATOR   BIT(7)
+
 /* TDLS specific payload type in the LLC/SNAP header */
 #define WLAN_TDLS_SNAP_RFTYPE  0x2
 
index d9010789b4e8215cc74a2ffc12e83f3cfd9b73cd..7af625f6d226a4d16266107282668fdb9bfc3bdc 100644 (file)
@@ -104,7 +104,8 @@ int __must_check rfkill_register(struct rfkill *rfkill);
  *
  * Pause polling -- say transmitter is off for other reasons.
  * NOTE: not necessary for suspend/resume -- in that case the
- * core stops polling anyway
+ * core stops polling anyway (but will also correctly handle
+ * the case of polling having been paused before suspend.)
  */
 void rfkill_pause_polling(struct rfkill *rfkill);
 
index 9bcaaf7cd15ab053a5de9cbf92fd02f0fb0ffaf7..9e1b24c29f0c65ce997d71a566e63125f7a6e248 100644 (file)
@@ -712,6 +712,8 @@ struct cfg80211_acl_data {
  * @p2p_opp_ps: P2P opportunistic PS
  * @acl: ACL configuration used by the drivers which has support for
  *     MAC address based access control
+ * @pbss: If set, start as a PCP instead of AP. Relevant for DMG
+ *     networks.
  */
 struct cfg80211_ap_settings {
        struct cfg80211_chan_def chandef;
@@ -730,6 +732,7 @@ struct cfg80211_ap_settings {
        u8 p2p_ctwindow;
        bool p2p_opp_ps;
        const struct cfg80211_acl_data *acl;
+       bool pbss;
 };
 
 /**
@@ -1888,6 +1891,8 @@ struct cfg80211_ibss_params {
  * @ht_capa_mask:  The bits of ht_capa which are to be used.
  * @vht_capa:  VHT Capability overrides
  * @vht_capa_mask: The bits of vht_capa which are to be used.
+ * @pbss: if set, connect to a PCP instead of AP. Valid for DMG
+ *     networks.
  */
 struct cfg80211_connect_params {
        struct ieee80211_channel *channel;
@@ -1910,6 +1915,7 @@ struct cfg80211_connect_params {
        struct ieee80211_ht_cap ht_capa_mask;
        struct ieee80211_vht_cap vht_capa;
        struct ieee80211_vht_cap vht_capa_mask;
+       bool pbss;
 };
 
 /**
@@ -3489,6 +3495,7 @@ struct cfg80211_cached_keys;
  *     registered for unexpected class 3 frames (AP mode)
  * @conn: (private) cfg80211 software SME connection state machine data
  * @connect_keys: (private) keys to set after connection is established
+ * @conn_bss_type: connecting/connected BSS type
  * @ibss_fixed: (private) IBSS is using fixed BSSID
  * @ibss_dfs_possible: (private) IBSS may change to a DFS channel
  * @event_list: (private) list for internal event processing
@@ -3519,6 +3526,7 @@ struct wireless_dev {
        u8 ssid_len, mesh_id_len, mesh_id_up_len;
        struct cfg80211_conn *conn;
        struct cfg80211_cached_keys *connect_keys;
+       enum ieee80211_bss_type conn_bss_type;
 
        struct list_head event_list;
        spinlock_t event_lock;
index 7c30faff245f262a59831132f84f5cb37c2cc8a5..57147749ae423eee59083c61ea60b159078e2ac5 100644 (file)
@@ -5,7 +5,7 @@
  * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
  * Copyright 2007-2010 Johannes Berg <johannes@sipsolutions.net>
  * Copyright 2013-2014  Intel Mobile Communications GmbH
- * Copyright (C) 2015 Intel Deutschland GmbH
+ * Copyright (C) 2015 - 2016 Intel Deutschland GmbH
  *
  * 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
@@ -298,6 +298,7 @@ struct ieee80211_vif_chanctx_switch {
  *     note that this is only called when it changes after the channel
  *     context had been assigned.
  * @BSS_CHANGED_OCB: OCB join status changed
+ * @BSS_CHANGED_MU_GROUPS: VHT MU-MIMO group id or user position changed
  */
 enum ieee80211_bss_change {
        BSS_CHANGED_ASSOC               = 1<<0,
@@ -323,6 +324,7 @@ enum ieee80211_bss_change {
        BSS_CHANGED_BEACON_INFO         = 1<<20,
        BSS_CHANGED_BANDWIDTH           = 1<<21,
        BSS_CHANGED_OCB                 = 1<<22,
+       BSS_CHANGED_MU_GROUPS           = 1<<23,
 
        /* when adding here, make sure to change ieee80211_reconfig */
 };
@@ -435,6 +437,19 @@ struct ieee80211_event {
        } u;
 };
 
+/**
+ * struct ieee80211_mu_group_data - STA's VHT MU-MIMO group data
+ *
+ * This structure describes the group id data of VHT MU-MIMO
+ *
+ * @membership: 64 bits array - a bit is set if station is member of the group
+ * @position: 2 bits per group id indicating the position in the group
+ */
+struct ieee80211_mu_group_data {
+       u8 membership[WLAN_MEMBERSHIP_LEN];
+       u8 position[WLAN_USER_POSITION_LEN];
+};
+
 /**
  * struct ieee80211_bss_conf - holds the BSS's changing parameters
  *
@@ -477,6 +492,7 @@ struct ieee80211_event {
  * @enable_beacon: whether beaconing should be enabled or not
  * @chandef: Channel definition for this BSS -- the hardware might be
  *     configured a higher bandwidth than this BSS uses, for example.
+ * @mu_group: VHT MU-MIMO group membership data
  * @ht_operation_mode: HT operation mode like in &struct ieee80211_ht_operation.
  *     This field is only valid when the channel is a wide HT/VHT channel.
  *     Note that with TDLS this can be the case (channel is HT, protection must
@@ -535,6 +551,7 @@ struct ieee80211_bss_conf {
        s32 cqm_rssi_thold;
        u32 cqm_rssi_hyst;
        struct cfg80211_chan_def chandef;
+       struct ieee80211_mu_group_data mu_group;
        __be32 arp_addr_list[IEEE80211_BSS_ARP_ADDR_LIST_LEN];
        int arp_addr_cnt;
        bool qos;
@@ -691,12 +708,14 @@ enum mac80211_tx_info_flags {
  *     protocol frame (e.g. EAP)
  * @IEEE80211_TX_CTRL_PS_RESPONSE: This frame is a response to a poll
  *     frame (PS-Poll or uAPSD).
+ * @IEEE80211_TX_CTRL_RATE_INJECT: This frame is injected with rate information
  *
  * These flags are used in tx_info->control.flags.
  */
 enum mac80211_tx_control_flags {
        IEEE80211_TX_CTRL_PORT_CTRL_PROTO       = BIT(0),
        IEEE80211_TX_CTRL_PS_RESPONSE           = BIT(1),
+       IEEE80211_TX_CTRL_RATE_INJECT           = BIT(2),
 };
 
 /*
@@ -993,6 +1012,8 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info)
  * @RX_FLAG_MACTIME_END: The timestamp passed in the RX status (@mactime
  *     field) is valid and contains the time the last symbol of the MPDU
  *     (including FCS) was received.
+ * @RX_FLAG_MACTIME_PLCP_START: The timestamp passed in the RX status (@mactime
+ *     field) is valid and contains the time the SYNC preamble was received.
  * @RX_FLAG_SHORTPRE: Short preamble was used for this frame
  * @RX_FLAG_HT: HT MCS was used and rate_idx is MCS index
  * @RX_FLAG_VHT: VHT MCS was used and rate_index is MCS index
@@ -1014,6 +1035,14 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info)
  * @RX_FLAG_AMPDU_DELIM_CRC_KNOWN: The delimiter CRC field is known (the CRC
  *     is stored in the @ampdu_delimiter_crc field)
  * @RX_FLAG_LDPC: LDPC was used
+ * @RX_FLAG_ONLY_MONITOR: Report frame only to monitor interfaces without
+ *     processing it in any regular way.
+ *     This is useful if drivers offload some frames but still want to report
+ *     them for sniffing purposes.
+ * @RX_FLAG_SKIP_MONITOR: Process and report frame to all interfaces except
+ *     monitor interfaces.
+ *     This is useful if drivers offload some frames but still want to report
+ *     them for sniffing purposes.
  * @RX_FLAG_STBC_MASK: STBC 2 bit bitmask. 1 - Nss=1, 2 - Nss=2, 3 - Nss=3
  * @RX_FLAG_10MHZ: 10 MHz (half channel) was used
  * @RX_FLAG_5MHZ: 5 MHz (quarter channel) was used
@@ -1033,6 +1062,7 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info)
 enum mac80211_rx_flags {
        RX_FLAG_MMIC_ERROR              = BIT(0),
        RX_FLAG_DECRYPTED               = BIT(1),
+       RX_FLAG_MACTIME_PLCP_START      = BIT(2),
        RX_FLAG_MMIC_STRIPPED           = BIT(3),
        RX_FLAG_IV_STRIPPED             = BIT(4),
        RX_FLAG_FAILED_FCS_CRC          = BIT(5),
@@ -1046,7 +1076,7 @@ enum mac80211_rx_flags {
        RX_FLAG_HT_GF                   = BIT(13),
        RX_FLAG_AMPDU_DETAILS           = BIT(14),
        RX_FLAG_PN_VALIDATED            = BIT(15),
-       /* bit 16 free */
+       RX_FLAG_DUP_VALIDATED           = BIT(16),
        RX_FLAG_AMPDU_LAST_KNOWN        = BIT(17),
        RX_FLAG_AMPDU_IS_LAST           = BIT(18),
        RX_FLAG_AMPDU_DELIM_CRC_ERROR   = BIT(19),
@@ -1054,6 +1084,8 @@ enum mac80211_rx_flags {
        RX_FLAG_MACTIME_END             = BIT(21),
        RX_FLAG_VHT                     = BIT(22),
        RX_FLAG_LDPC                    = BIT(23),
+       RX_FLAG_ONLY_MONITOR            = BIT(24),
+       RX_FLAG_SKIP_MONITOR            = BIT(25),
        RX_FLAG_STBC_MASK               = BIT(26) | BIT(27),
        RX_FLAG_10MHZ                   = BIT(28),
        RX_FLAG_5MHZ                    = BIT(29),
@@ -1072,6 +1104,7 @@ enum mac80211_rx_flags {
  * @RX_VHT_FLAG_160MHZ: 160 MHz was used
  * @RX_VHT_FLAG_BF: packet was beamformed
  */
+
 enum mac80211_rx_vht_flags {
        RX_VHT_FLAG_80MHZ               = BIT(0),
        RX_VHT_FLAG_160MHZ              = BIT(1),
@@ -1091,6 +1124,8 @@ enum mac80211_rx_vht_flags {
  *     it but can store it and pass it back to the driver for synchronisation
  * @band: the active band when this frame was received
  * @freq: frequency the radio was tuned to when receiving this frame, in MHz
+ *     This field must be set for management frames, but isn't strictly needed
+ *     for data (other) frames - for those it only affects radiotap reporting.
  * @signal: signal strength when receiving this frame, either in dBm, in dB or
  *     unspecified depending on the hardware capabilities flags
  *     @IEEE80211_HW_SIGNAL_*
@@ -1684,6 +1719,18 @@ struct ieee80211_sta_rates {
  * @tdls_initiator: indicates the STA is an initiator of the TDLS link. Only
  *     valid if the STA is a TDLS peer in the first place.
  * @mfp: indicates whether the STA uses management frame protection or not.
+ * @max_amsdu_subframes: indicates the maximal number of MSDUs in a single
+ *     A-MSDU. Taken from the Extended Capabilities element. 0 means
+ *     unlimited.
+ * @max_amsdu_len: indicates the maximal length of an A-MSDU in bytes. This
+ *     field is always valid for packets with a VHT preamble. For packets
+ *     with a HT preamble, additional limits apply:
+ *             + If the skb is transmitted as part of a BA agreement, the
+ *               A-MSDU maximal size is min(max_amsdu_len, 4065) bytes.
+ *             + If the skb is not part of a BA aggreement, the A-MSDU maximal
+ *               size is min(max_amsdu_len, 7935) bytes.
+ *     Both additional HT limits must be enforced by the low level driver.
+ *     This is defined by the spec (IEEE 802.11-2012 section 8.3.2.2 NOTE 2).
  * @txq: per-TID data TX queues (if driver uses the TXQ abstraction)
  */
 struct ieee80211_sta {
@@ -1702,6 +1749,8 @@ struct ieee80211_sta {
        bool tdls;
        bool tdls_initiator;
        bool mfp;
+       u8 max_amsdu_subframes;
+       u16 max_amsdu_len;
 
        struct ieee80211_txq *txq[IEEE80211_NUM_TIDS];
 
@@ -1910,6 +1959,11 @@ struct ieee80211_txq {
  *     by just its MAC address; this prevents, for example, the same station
  *     from connecting to two virtual AP interfaces at the same time.
  *
+ * @IEEE80211_HW_SUPPORTS_REORDERING_BUFFER: Hardware (or driver) manages the
+ *     reordering buffer internally, guaranteeing mac80211 receives frames in
+ *     order and does not need to manage its own reorder buffer or BA session
+ *     timeout.
+ *
  * @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays
  */
 enum ieee80211_hw_flags {
@@ -1946,6 +2000,7 @@ enum ieee80211_hw_flags {
        IEEE80211_HW_SUPPORTS_AMSDU_IN_AMPDU,
        IEEE80211_HW_BEACON_TX_STATUS,
        IEEE80211_HW_NEEDS_UNIQUE_STA_ADDR,
+       IEEE80211_HW_SUPPORTS_REORDERING_BUFFER,
 
        /* keep last, obviously */
        NUM_IEEE80211_HW_FLAGS
@@ -2167,7 +2222,7 @@ static inline void SET_IEEE80211_DEV(struct ieee80211_hw *hw, struct device *dev
  * @hw: the &struct ieee80211_hw to set the MAC address for
  * @addr: the address to set
  */
-static inline void SET_IEEE80211_PERM_ADDR(struct ieee80211_hw *hw, u8 *addr)
+static inline void SET_IEEE80211_PERM_ADDR(struct ieee80211_hw *hw, const u8 *addr)
 {
        memcpy(hw->wiphy->perm_addr, addr, ETH_ALEN);
 }
@@ -2683,6 +2738,33 @@ enum ieee80211_ampdu_mlme_action {
        IEEE80211_AMPDU_TX_OPERATIONAL,
 };
 
+/**
+ * struct ieee80211_ampdu_params - AMPDU action parameters
+ *
+ * @action: the ampdu action, value from %ieee80211_ampdu_mlme_action.
+ * @sta: peer of this AMPDU session
+ * @tid: tid of the BA session
+ * @ssn: start sequence number of the session. TX/RX_STOP can pass 0. When
+ *     action is set to %IEEE80211_AMPDU_RX_START the driver passes back the
+ *     actual ssn value used to start the session and writes the value here.
+ * @buf_size: reorder buffer size  (number of subframes). Valid only when the
+ *     action is set to %IEEE80211_AMPDU_RX_START or
+ *     %IEEE80211_AMPDU_TX_OPERATIONAL
+ * @amsdu: indicates the peer's ability to receive A-MSDU within A-MPDU.
+ *     valid when the action is set to %IEEE80211_AMPDU_TX_OPERATIONAL
+ * @timeout: BA session timeout. Valid only when the action is set to
+ *     %IEEE80211_AMPDU_RX_START
+ */
+struct ieee80211_ampdu_params {
+       enum ieee80211_ampdu_mlme_action action;
+       struct ieee80211_sta *sta;
+       u16 tid;
+       u16 ssn;
+       u8 buf_size;
+       bool amsdu;
+       u16 timeout;
+};
+
 /**
  * enum ieee80211_frame_release_type - frame release reason
  * @IEEE80211_FRAME_RELEASE_PSPOLL: frame released for PS-Poll
@@ -3027,13 +3109,9 @@ enum ieee80211_reconfig_type {
  * @ampdu_action: Perform a certain A-MPDU action
  *     The RA/TID combination determines the destination and TID we want
  *     the ampdu action to be performed for. The action is defined through
- *     ieee80211_ampdu_mlme_action. Starting sequence number (@ssn)
- *     is the first frame we expect to perform the action on. Notice
- *     that TX/RX_STOP can pass NULL for this parameter.
- *     The @buf_size parameter is only valid when the action is set to
- *     %IEEE80211_AMPDU_TX_OPERATIONAL and indicates the peer's reorder
- *     buffer size (number of subframes) for this session -- the driver
- *     may neither send aggregates containing more subframes than this
+ *     ieee80211_ampdu_mlme_action.
+ *     When the action is set to %IEEE80211_AMPDU_TX_OPERATIONAL the driver
+ *     may neither send aggregates containing more subframes than @buf_size
  *     nor send aggregates in a way that lost frames would exceed the
  *     buffer size. If just limiting the aggregate size, this would be
  *     possible with a buf_size of 8:
@@ -3044,9 +3122,6 @@ enum ieee80211_reconfig_type {
  *     buffer size of 8. Correct ways to retransmit #1 would be:
  *      - TX:       1 or 18 or 81
  *     Even "189" would be wrong since 1 could be lost again.
- *     The @amsdu parameter is valid when the action is set to
- *     %IEEE80211_AMPDU_TX_OPERATIONAL and indicates the peer's ability
- *     to receive A-MSDU within A-MPDU.
  *
  *     Returns a negative error code on failure.
  *     The callback can sleep.
@@ -3388,9 +3463,7 @@ struct ieee80211_ops {
        int (*tx_last_beacon)(struct ieee80211_hw *hw);
        int (*ampdu_action)(struct ieee80211_hw *hw,
                            struct ieee80211_vif *vif,
-                           enum ieee80211_ampdu_mlme_action action,
-                           struct ieee80211_sta *sta, u16 tid, u16 *ssn,
-                           u8 buf_size, bool amsdu);
+                           struct ieee80211_ampdu_params *params);
        int (*get_survey)(struct ieee80211_hw *hw, int idx,
                struct survey_info *survey);
        void (*rfkill_poll)(struct ieee80211_hw *hw);
@@ -5120,6 +5193,24 @@ void ieee80211_remain_on_channel_expired(struct ieee80211_hw *hw);
 void ieee80211_stop_rx_ba_session(struct ieee80211_vif *vif, u16 ba_rx_bitmap,
                                  const u8 *addr);
 
+/**
+ * ieee80211_mark_rx_ba_filtered_frames - move RX BA window and mark filtered
+ * @pubsta: station struct
+ * @tid: the session's TID
+ * @ssn: starting sequence number of the bitmap, all frames before this are
+ *     assumed to be out of the window after the call
+ * @filtered: bitmap of filtered frames, BIT(0) is the @ssn entry etc.
+ * @received_mpdus: number of received mpdus in firmware
+ *
+ * This function moves the BA window and releases all frames before @ssn, and
+ * marks frames marked in the bitmap as having been filtered. Afterwards, it
+ * checks if any frames in the window starting from @ssn can now be released
+ * (in case they were only waiting for frames that were filtered.)
+ */
+void ieee80211_mark_rx_ba_filtered_frames(struct ieee80211_sta *pubsta, u8 tid,
+                                         u16 ssn, u64 filtered,
+                                         u16 received_mpdus);
+
 /**
  * ieee80211_send_bar - send a BlockAckReq frame
  *
@@ -5523,4 +5614,19 @@ void ieee80211_unreserve_tid(struct ieee80211_sta *sta, u8 tid);
  */
 struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw,
                                     struct ieee80211_txq *txq);
+
+/**
+ * ieee80211_txq_get_depth - get pending frame/byte count of given txq
+ *
+ * The values are not guaranteed to be coherent with regard to each other, i.e.
+ * txq state can change half-way of this function and the caller may end up
+ * with "new" frame_cnt and "old" byte_cnt or vice-versa.
+ *
+ * @txq: pointer obtained from station or virtual interface
+ * @frame_cnt: pointer to store frame count
+ * @byte_cnt: pointer to store byte count
+ */
+void ieee80211_txq_get_depth(struct ieee80211_txq *txq,
+                            unsigned long *frame_cnt,
+                            unsigned long *byte_cnt);
 #endif /* MAC80211_H */
index 5b7b5ebe7ca8731868e38bfab6218c166cb0317d..7758969a2a8e83f53e1bfa8f810b3826b9c7d44a 100644 (file)
@@ -1789,6 +1789,10 @@ enum nl80211_commands {
  *     thus it must not specify the number of iterations, only the interval
  *     between scans. The scan plans are executed sequentially.
  *     Each scan plan is a nested attribute of &enum nl80211_sched_scan_plan.
+ * @NL80211_ATTR_PBSS: flag attribute. If set it means operate
+ *     in a PBSS. Specified in %NL80211_CMD_CONNECT to request
+ *     connecting to a PCP, and in %NL80211_CMD_START_AP to start
+ *     a PCP instead of AP. Relevant for DMG networks only.
  *
  * @NUM_NL80211_ATTR: total number of nl80211_attrs available
  * @NL80211_ATTR_MAX: highest attribute number currently defined
@@ -2164,6 +2168,8 @@ enum nl80211_attrs {
        NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS,
        NL80211_ATTR_SCHED_SCAN_PLANS,
 
+       NL80211_ATTR_PBSS,
+
        /* add attributes here, update the policy in nl80211.c */
 
        __NL80211_ATTR_AFTER_LAST,
index 058757f7a7339b104f9f44ebd77569cf20f35ef6..2e00dcebebd0700ac835d0d98221ef1cb4717974 100644 (file)
@@ -59,6 +59,8 @@ enum rfkill_type {
  * @RFKILL_OP_DEL: a device was removed
  * @RFKILL_OP_CHANGE: a device's state changed -- userspace changes one device
  * @RFKILL_OP_CHANGE_ALL: userspace changes all devices (of a type, or all)
+ *     into a state, also updating the default state used for devices that
+ *     are hot-plugged later.
  */
 enum rfkill_operation {
        RFKILL_OP_ADD = 0,
index 367784be5df20f26fd3940c4608f1ea715bfddd6..3a8f881b22f10c983ab354a4393157e090fd6d3a 100644 (file)
@@ -7,6 +7,7 @@
  * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
  * Copyright 2007, Michael Wu <flamingice@sourmilk.net>
  * Copyright 2007-2010, Intel Corporation
+ * Copyright(c) 2015 Intel Deutschland GmbH
  *
  * 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
@@ -61,16 +62,25 @@ void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
 {
        struct ieee80211_local *local = sta->local;
        struct tid_ampdu_rx *tid_rx;
+       struct ieee80211_ampdu_params params = {
+               .sta = &sta->sta,
+               .action = IEEE80211_AMPDU_RX_STOP,
+               .tid = tid,
+               .amsdu = false,
+               .timeout = 0,
+               .ssn = 0,
+       };
 
        lockdep_assert_held(&sta->ampdu_mlme.mtx);
 
        tid_rx = rcu_dereference_protected(sta->ampdu_mlme.tid_rx[tid],
                                        lockdep_is_held(&sta->ampdu_mlme.mtx));
 
-       if (!tid_rx)
+       if (!test_bit(tid, sta->ampdu_mlme.agg_session_valid))
                return;
 
        RCU_INIT_POINTER(sta->ampdu_mlme.tid_rx[tid], NULL);
+       __clear_bit(tid, sta->ampdu_mlme.agg_session_valid);
 
        ht_dbg(sta->sdata,
               "Rx BA session stop requested for %pM tid %u %s reason: %d\n",
@@ -78,8 +88,7 @@ void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
               initiator == WLAN_BACK_RECIPIENT ? "recipient" : "inititator",
               (int)reason);
 
-       if (drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_RX_STOP,
-                            &sta->sta, tid, NULL, 0, false))
+       if (drv_ampdu_action(local, sta->sdata, &params))
                sdata_info(sta->sdata,
                           "HW problem - can not stop rx aggregation for %pM tid %d\n",
                           sta->sta.addr, tid);
@@ -89,6 +98,13 @@ void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
                ieee80211_send_delba(sta->sdata, sta->sta.addr,
                                     tid, WLAN_BACK_RECIPIENT, reason);
 
+       /*
+        * return here in case tid_rx is not assigned - which will happen if
+        * IEEE80211_HW_SUPPORTS_REORDERING_BUFFER is set.
+        */
+       if (!tid_rx)
+               return;
+
        del_timer_sync(&tid_rx->session_timer);
 
        /* make sure ieee80211_sta_reorder_release() doesn't re-arm the timer */
@@ -237,6 +253,15 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta,
 {
        struct ieee80211_local *local = sta->sdata->local;
        struct tid_ampdu_rx *tid_agg_rx;
+       struct ieee80211_ampdu_params params = {
+               .sta = &sta->sta,
+               .action = IEEE80211_AMPDU_RX_START,
+               .tid = tid,
+               .amsdu = false,
+               .timeout = timeout,
+               .ssn = start_seq_num,
+       };
+
        int i, ret = -EOPNOTSUPP;
        u16 status = WLAN_STATUS_REQUEST_DECLINED;
 
@@ -275,11 +300,12 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta,
        /* make sure the size doesn't exceed the maximum supported by the hw */
        if (buf_size > local->hw.max_rx_aggregation_subframes)
                buf_size = local->hw.max_rx_aggregation_subframes;
+       params.buf_size = buf_size;
 
        /* examine state machine */
        mutex_lock(&sta->ampdu_mlme.mtx);
 
-       if (sta->ampdu_mlme.tid_rx[tid]) {
+       if (test_bit(tid, sta->ampdu_mlme.agg_session_valid)) {
                ht_dbg_ratelimited(sta->sdata,
                                   "unexpected AddBA Req from %pM on tid %u\n",
                                   sta->sta.addr, tid);
@@ -290,6 +316,16 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta,
                                                false);
        }
 
+       if (ieee80211_hw_check(&local->hw, SUPPORTS_REORDERING_BUFFER)) {
+               ret = drv_ampdu_action(local, sta->sdata, &params);
+               ht_dbg(sta->sdata,
+                      "Rx A-MPDU request on %pM tid %d result %d\n",
+                      sta->sta.addr, tid, ret);
+               if (!ret)
+                       status = WLAN_STATUS_SUCCESS;
+               goto end;
+       }
+
        /* prepare A-MPDU MLME for Rx aggregation */
        tid_agg_rx = kzalloc(sizeof(*tid_agg_rx), GFP_KERNEL);
        if (!tid_agg_rx)
@@ -322,8 +358,7 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta,
        for (i = 0; i < buf_size; i++)
                __skb_queue_head_init(&tid_agg_rx->reorder_buf[i]);
 
-       ret = drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_RX_START,
-                              &sta->sta, tid, &start_seq_num, 0, false);
+       ret = drv_ampdu_action(local, sta->sdata, &params);
        ht_dbg(sta->sdata, "Rx A-MPDU request on %pM tid %d result %d\n",
               sta->sta.addr, tid, ret);
        if (ret) {
@@ -341,6 +376,7 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta,
        tid_agg_rx->timeout = timeout;
        tid_agg_rx->stored_mpdu_num = 0;
        tid_agg_rx->auto_seq = auto_seq;
+       tid_agg_rx->reorder_buf_filtered = 0;
        status = WLAN_STATUS_SUCCESS;
 
        /* activate it for RX */
@@ -352,6 +388,8 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta,
        }
 
 end:
+       if (status == WLAN_STATUS_SUCCESS)
+               __set_bit(tid, sta->ampdu_mlme.agg_session_valid);
        mutex_unlock(&sta->ampdu_mlme.mtx);
 
 end_no_lock:
index ff757181b0a85c820e1acc53a088e95c78b87bff..4932e9f243a2cb9e156e7e24ac1c098fc9a4b19c 100644 (file)
@@ -7,6 +7,7 @@
  * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
  * Copyright 2007, Michael Wu <flamingice@sourmilk.net>
  * Copyright 2007-2010, Intel Corporation
+ * Copyright(c) 2015 Intel Deutschland GmbH
  *
  * 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
@@ -295,7 +296,14 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
 {
        struct ieee80211_local *local = sta->local;
        struct tid_ampdu_tx *tid_tx;
-       enum ieee80211_ampdu_mlme_action action;
+       struct ieee80211_ampdu_params params = {
+               .sta = &sta->sta,
+               .tid = tid,
+               .buf_size = 0,
+               .amsdu = false,
+               .timeout = 0,
+               .ssn = 0,
+       };
        int ret;
 
        lockdep_assert_held(&sta->ampdu_mlme.mtx);
@@ -304,10 +312,10 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
        case AGG_STOP_DECLINED:
        case AGG_STOP_LOCAL_REQUEST:
        case AGG_STOP_PEER_REQUEST:
-               action = IEEE80211_AMPDU_TX_STOP_CONT;
+               params.action = IEEE80211_AMPDU_TX_STOP_CONT;
                break;
        case AGG_STOP_DESTROY_STA:
-               action = IEEE80211_AMPDU_TX_STOP_FLUSH;
+               params.action = IEEE80211_AMPDU_TX_STOP_FLUSH;
                break;
        default:
                WARN_ON_ONCE(1);
@@ -330,9 +338,8 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
                spin_unlock_bh(&sta->lock);
                if (reason != AGG_STOP_DESTROY_STA)
                        return -EALREADY;
-               ret = drv_ampdu_action(local, sta->sdata,
-                                      IEEE80211_AMPDU_TX_STOP_FLUSH_CONT,
-                                      &sta->sta, tid, NULL, 0, false);
+               params.action = IEEE80211_AMPDU_TX_STOP_FLUSH_CONT;
+               ret = drv_ampdu_action(local, sta->sdata, &params);
                WARN_ON_ONCE(ret);
                return 0;
        }
@@ -381,8 +388,7 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
                                        WLAN_BACK_INITIATOR;
        tid_tx->tx_stop = reason == AGG_STOP_LOCAL_REQUEST;
 
-       ret = drv_ampdu_action(local, sta->sdata, action,
-                              &sta->sta, tid, NULL, 0, false);
+       ret = drv_ampdu_action(local, sta->sdata, &params);
 
        /* HW shall not deny going back to legacy */
        if (WARN_ON(ret)) {
@@ -445,7 +451,14 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid)
        struct tid_ampdu_tx *tid_tx;
        struct ieee80211_local *local = sta->local;
        struct ieee80211_sub_if_data *sdata = sta->sdata;
-       u16 start_seq_num;
+       struct ieee80211_ampdu_params params = {
+               .sta = &sta->sta,
+               .action = IEEE80211_AMPDU_TX_START,
+               .tid = tid,
+               .buf_size = 0,
+               .amsdu = false,
+               .timeout = 0,
+       };
        int ret;
 
        tid_tx = rcu_dereference_protected_tid_tx(sta, tid);
@@ -467,10 +480,8 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid)
         */
        synchronize_net();
 
-       start_seq_num = sta->tid_seq[tid] >> 4;
-
-       ret = drv_ampdu_action(local, sdata, IEEE80211_AMPDU_TX_START,
-                              &sta->sta, tid, &start_seq_num, 0, false);
+       params.ssn = sta->tid_seq[tid] >> 4;
+       ret = drv_ampdu_action(local, sdata, &params);
        if (ret) {
                ht_dbg(sdata,
                       "BA request denied - HW unavailable for %pM tid %d\n",
@@ -499,7 +510,7 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid)
 
        /* send AddBA request */
        ieee80211_send_addba_request(sdata, sta->sta.addr, tid,
-                                    tid_tx->dialog_token, start_seq_num,
+                                    tid_tx->dialog_token, params.ssn,
                                     IEEE80211_MAX_AMPDU_BUF,
                                     tid_tx->timeout);
 }
@@ -684,18 +695,24 @@ static void ieee80211_agg_tx_operational(struct ieee80211_local *local,
                                         struct sta_info *sta, u16 tid)
 {
        struct tid_ampdu_tx *tid_tx;
+       struct ieee80211_ampdu_params params = {
+               .sta = &sta->sta,
+               .action = IEEE80211_AMPDU_TX_OPERATIONAL,
+               .tid = tid,
+               .timeout = 0,
+               .ssn = 0,
+       };
 
        lockdep_assert_held(&sta->ampdu_mlme.mtx);
 
        tid_tx = rcu_dereference_protected_tid_tx(sta, tid);
+       params.buf_size = tid_tx->buf_size;
+       params.amsdu = tid_tx->amsdu;
 
        ht_dbg(sta->sdata, "Aggregation is on for %pM tid %d\n",
               sta->sta.addr, tid);
 
-       drv_ampdu_action(local, sta->sdata,
-                        IEEE80211_AMPDU_TX_OPERATIONAL,
-                        &sta->sta, tid, NULL, tid_tx->buf_size,
-                        tid_tx->amsdu);
+       drv_ampdu_action(local, sta->sdata, &params);
 
        /*
         * synchronize with TX path, while splicing the TX path
index 166a29fe6c35f63a5357a01ceeb140f19346f9bd..66d22de93c8df75e7d0b747bf71a45cb3b45f927 100644 (file)
@@ -1131,6 +1131,34 @@ static int sta_apply_parameters(struct ieee80211_local *local,
                sta->sta.max_sp = params->max_sp;
        }
 
+       /* The sender might not have sent the last bit, consider it to be 0 */
+       if (params->ext_capab_len >= 8) {
+               u8 val = (params->ext_capab[7] &
+                         WLAN_EXT_CAPA8_MAX_MSDU_IN_AMSDU_LSB) >> 7;
+
+               /* we did get all the bits, take the MSB as well */
+               if (params->ext_capab_len >= 9) {
+                       u8 val_msb = params->ext_capab[8] &
+                               WLAN_EXT_CAPA9_MAX_MSDU_IN_AMSDU_MSB;
+                       val_msb <<= 1;
+                       val |= val_msb;
+               }
+
+               switch (val) {
+               case 1:
+                       sta->sta.max_amsdu_subframes = 32;
+                       break;
+               case 2:
+                       sta->sta.max_amsdu_subframes = 16;
+                       break;
+               case 3:
+                       sta->sta.max_amsdu_subframes = 8;
+                       break;
+               default:
+                       sta->sta.max_amsdu_subframes = 0;
+               }
+       }
+
        /*
         * cfg80211 validates this (1-2007) and allows setting the AID
         * only when creating a new station entry
@@ -1160,6 +1188,7 @@ static int sta_apply_parameters(struct ieee80211_local *local,
                ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
                                                  params->ht_capa, sta);
 
+       /* VHT can override some HT caps such as the A-MSDU max length */
        if (params->vht_capa)
                ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
                                                    params->vht_capa, sta);
index 1d1b9b7bdefe74ac851ca6d01554d663fae39541..283981108ca80cb5bb7182b28e626a1a14c09fb1 100644 (file)
@@ -231,7 +231,7 @@ ieee80211_get_max_required_bw(struct ieee80211_sub_if_data *sdata)
                    !(sta->sdata->bss && sta->sdata->bss == sdata->bss))
                        continue;
 
-               if (!sta->uploaded)
+               if (!sta->uploaded || !test_sta_flag(sta, WLAN_STA_ASSOC))
                        continue;
 
                max_bw = max(max_bw, ieee80211_get_sta_bw(&sta->sta));
index 3e24d0ddb51bfaca18d39669e41dba2bb6a91bb5..4ab5c522ceeeee5def93f869fd96618999a2daf5 100644 (file)
@@ -126,6 +126,7 @@ static const char *hw_flag_names[] = {
        FLAG(SUPPORTS_AMSDU_IN_AMPDU),
        FLAG(BEACON_TX_STATUS),
        FLAG(NEEDS_UNIQUE_STA_ADDR),
+       FLAG(SUPPORTS_REORDERING_BUFFER),
 #undef FLAG
 };
 
index ca1fe5576103767c3a98b52e037c0034b949887f..c258f1041d330885d08869e302a5ec255b470b4b 100644 (file)
@@ -284,9 +284,7 @@ int drv_switch_vif_chanctx(struct ieee80211_local *local,
 
 int drv_ampdu_action(struct ieee80211_local *local,
                     struct ieee80211_sub_if_data *sdata,
-                    enum ieee80211_ampdu_mlme_action action,
-                    struct ieee80211_sta *sta, u16 tid,
-                    u16 *ssn, u8 buf_size, bool amsdu)
+                    struct ieee80211_ampdu_params *params)
 {
        int ret = -EOPNOTSUPP;
 
@@ -296,12 +294,10 @@ int drv_ampdu_action(struct ieee80211_local *local,
        if (!check_sdata_in_driver(sdata))
                return -EIO;
 
-       trace_drv_ampdu_action(local, sdata, action, sta, tid,
-                              ssn, buf_size, amsdu);
+       trace_drv_ampdu_action(local, sdata, params);
 
        if (local->ops->ampdu_action)
-               ret = local->ops->ampdu_action(&local->hw, &sdata->vif, action,
-                                              sta, tid, ssn, buf_size, amsdu);
+               ret = local->ops->ampdu_action(&local->hw, &sdata->vif, params);
 
        trace_drv_return_int(local, ret);
 
index 154ce4b13406d5a31bac8797f8069184ca9cd6f5..18b0d65baff000156c8015f76b9ce527a938e95a 100644 (file)
@@ -585,9 +585,7 @@ static inline int drv_tx_last_beacon(struct ieee80211_local *local)
 
 int drv_ampdu_action(struct ieee80211_local *local,
                     struct ieee80211_sub_if_data *sdata,
-                    enum ieee80211_ampdu_mlme_action action,
-                    struct ieee80211_sta *sta, u16 tid,
-                    u16 *ssn, u8 buf_size, bool amsdu);
+                    struct ieee80211_ampdu_params *params);
 
 static inline int drv_get_survey(struct ieee80211_local *local, int idx,
                                struct survey_info *survey)
index 7a76ce639d58d6071681551e916f0125cf376212..f4a52877356349abed96d0a77d6ae75f301181e4 100644 (file)
@@ -230,6 +230,11 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
        /* set Rx highest rate */
        ht_cap.mcs.rx_highest = ht_cap_ie->mcs.rx_highest;
 
+       if (ht_cap.cap & IEEE80211_HT_CAP_MAX_AMSDU)
+               sta->sta.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_HT_7935;
+       else
+               sta->sta.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_HT_3839;
+
  apply:
        changed = memcmp(&sta->sta.ht_cap, &ht_cap, sizeof(ht_cap));
 
index 978d3bc31df727468602a7b234d10b5b1e9ba35c..fc3238376b39546d025f81ad92240356a77aa48b 100644 (file)
@@ -7,6 +7,7 @@
  * Copyright 2007, Michael Wu <flamingice@sourmilk.net>
  * Copyright 2009, Johannes Berg <johannes@sipsolutions.net>
  * Copyright 2013-2014  Intel Mobile Communications GmbH
+ * Copyright(c) 2016 Intel Deutschland GmbH
  *
  * 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
@@ -1050,9 +1051,8 @@ static void ieee80211_update_sta_info(struct ieee80211_sub_if_data *sdata,
                struct cfg80211_chan_def chandef;
                enum ieee80211_sta_rx_bandwidth bw = sta->sta.bandwidth;
 
-               ieee80211_ht_oper_to_chandef(channel,
-                                            elems->ht_operation,
-                                            &chandef);
+               cfg80211_chandef_create(&chandef, channel, NL80211_CHAN_NO_HT);
+               ieee80211_chandef_ht_oper(elems->ht_operation, &chandef);
 
                memcpy(&htcap_ie, elems->ht_cap_elem, sizeof(htcap_ie));
                rates_updated |= ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
@@ -1066,9 +1066,8 @@ static void ieee80211_update_sta_info(struct ieee80211_sub_if_data *sdata,
                        struct ieee80211_vht_cap cap_ie;
                        struct ieee80211_sta_vht_cap cap = sta->sta.vht_cap;
 
-                       ieee80211_vht_oper_to_chandef(channel,
-                                                     elems->vht_operation,
-                                                     &chandef);
+                       ieee80211_chandef_vht_oper(elems->vht_operation,
+                                                  &chandef);
                        memcpy(&cap_ie, elems->vht_cap_elem, sizeof(cap_ie));
                        ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
                                                            &cap_ie, sta);
@@ -1485,14 +1484,21 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)
 
                sdata_info(sdata, "Trigger new scan to find an IBSS to join\n");
 
-               num = ieee80211_ibss_setup_scan_channels(local->hw.wiphy,
-                                                        &ifibss->chandef,
-                                                        channels,
-                                                        ARRAY_SIZE(channels));
                scan_width = cfg80211_chandef_to_scan_width(&ifibss->chandef);
-               ieee80211_request_ibss_scan(sdata, ifibss->ssid,
-                                           ifibss->ssid_len, channels, num,
-                                           scan_width);
+
+               if (ifibss->fixed_channel) {
+                       num = ieee80211_ibss_setup_scan_channels(local->hw.wiphy,
+                                                                &ifibss->chandef,
+                                                                channels,
+                                                                ARRAY_SIZE(channels));
+                       ieee80211_request_ibss_scan(sdata, ifibss->ssid,
+                                                   ifibss->ssid_len, channels,
+                                                   num, scan_width);
+               } else {
+                       ieee80211_request_ibss_scan(sdata, ifibss->ssid,
+                                                   ifibss->ssid_len, NULL,
+                                                   0, scan_width);
+               }
        } else {
                int interval = IEEE80211_SCAN_INTERVAL;
 
index b84f6aa32c0831ce200f286af2ecd947ef0fefde..a49c10361f1c2f58faa922eb5f9866cf43cbf842 100644 (file)
@@ -804,6 +804,7 @@ enum txq_info_flags {
 struct txq_info {
        struct sk_buff_head queue;
        unsigned long flags;
+       unsigned long byte_cnt;
 
        /* keep last! */
        struct ieee80211_txq txq;
@@ -1466,7 +1467,13 @@ ieee80211_have_rx_timestamp(struct ieee80211_rx_status *status)
 {
        WARN_ON_ONCE(status->flag & RX_FLAG_MACTIME_START &&
                     status->flag & RX_FLAG_MACTIME_END);
-       return status->flag & (RX_FLAG_MACTIME_START | RX_FLAG_MACTIME_END);
+       if (status->flag & (RX_FLAG_MACTIME_START | RX_FLAG_MACTIME_END))
+               return true;
+       /* can't handle HT/VHT preamble yet */
+       if (status->flag & RX_FLAG_MACTIME_PLCP_START &&
+           !(status->flag & (RX_FLAG_HT | RX_FLAG_VHT)))
+               return true;
+       return false;
 }
 
 u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local,
@@ -1714,6 +1721,8 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
 enum ieee80211_sta_rx_bandwidth ieee80211_sta_cap_rx_bw(struct sta_info *sta);
 enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta);
 void ieee80211_sta_set_rx_nss(struct sta_info *sta);
+void ieee80211_process_mu_groups(struct ieee80211_sub_if_data *sdata,
+                                struct ieee80211_mgmt *mgmt);
 u32 __ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
                                   struct sta_info *sta, u8 opmode,
                                  enum ieee80211_band band);
@@ -1829,20 +1838,6 @@ static inline void ieee802_11_parse_elems(const u8 *start, size_t len,
        ieee802_11_parse_elems_crc(start, len, action, elems, 0, 0);
 }
 
-static inline bool ieee80211_rx_reorder_ready(struct sk_buff_head *frames)
-{
-       struct sk_buff *tail = skb_peek_tail(frames);
-       struct ieee80211_rx_status *status;
-
-       if (!tail)
-               return false;
-
-       status = IEEE80211_SKB_RXCB(tail);
-       if (status->flag & RX_FLAG_AMSDU_MORE)
-               return false;
-
-       return true;
-}
 
 extern const int ieee802_1d_to_ac[8];
 
@@ -1986,12 +1981,10 @@ int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata,
 u8 *ieee80211_add_wmm_info_ie(u8 *buf, u8 qosinfo);
 
 /* channel management */
-void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan,
-                                 const struct ieee80211_ht_operation *ht_oper,
-                                 struct cfg80211_chan_def *chandef);
-void ieee80211_vht_oper_to_chandef(struct ieee80211_channel *control_chan,
-                                  const struct ieee80211_vht_operation *oper,
-                                  struct cfg80211_chan_def *chandef);
+bool ieee80211_chandef_ht_oper(const struct ieee80211_ht_operation *ht_oper,
+                              struct cfg80211_chan_def *chandef);
+bool ieee80211_chandef_vht_oper(const struct ieee80211_vht_operation *oper,
+                               struct cfg80211_chan_def *chandef);
 u32 ieee80211_chandef_downgrade(struct cfg80211_chan_def *c);
 
 int __must_check
index c9e325d2e120c0f9c230dbace71c3c405b9216a3..453b4e7417804105cb136e2ec7f4c57a39392db3 100644 (file)
@@ -977,7 +977,11 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
        if (sdata->vif.txq) {
                struct txq_info *txqi = to_txq_info(sdata->vif.txq);
 
+               spin_lock_bh(&txqi->queue.lock);
                ieee80211_purge_tx_queue(&local->hw, &txqi->queue);
+               txqi->byte_cnt = 0;
+               spin_unlock_bh(&txqi->queue.lock);
+
                atomic_set(&sdata->txqs_len[txqi->txq.ac], 0);
        }
 
@@ -1271,6 +1275,16 @@ static void ieee80211_iface_work(struct work_struct *work)
                                }
                        }
                        mutex_unlock(&local->sta_mtx);
+               } else if (ieee80211_is_action(mgmt->frame_control) &&
+                          mgmt->u.action.category == WLAN_CATEGORY_VHT) {
+                       switch (mgmt->u.action.u.vht_group_notif.action_code) {
+                       case WLAN_VHT_ACTION_GROUPID_MGMT:
+                               ieee80211_process_mu_groups(sdata, mgmt);
+                               break;
+                       default:
+                               WARN_ON(1);
+                               break;
+                       }
                } else if (ieee80211_is_data_qos(mgmt->frame_control)) {
                        struct ieee80211_hdr *hdr = (void *)mgmt;
                        /*
index 6f85b6ab8e51015ab9a0520822fc4daff6af92b0..d32cefcb63b02ecd4f1af220c559a2a82c75430d 100644 (file)
@@ -91,11 +91,10 @@ bool mesh_matches_local(struct ieee80211_sub_if_data *sdata,
        if (sdata->vif.bss_conf.basic_rates != basic_rates)
                return false;
 
-       ieee80211_ht_oper_to_chandef(sdata->vif.bss_conf.chandef.chan,
-                                    ie->ht_operation, &sta_chan_def);
-
-       ieee80211_vht_oper_to_chandef(sdata->vif.bss_conf.chandef.chan,
-                                     ie->vht_operation, &sta_chan_def);
+       cfg80211_chandef_create(&sta_chan_def, sdata->vif.bss_conf.chandef.chan,
+                               NL80211_CHAN_NO_HT);
+       ieee80211_chandef_ht_oper(ie->ht_operation, &sta_chan_def);
+       ieee80211_chandef_vht_oper(ie->vht_operation, &sta_chan_def);
 
        if (!cfg80211_chandef_compatible(&sdata->vif.bss_conf.chandef,
                                         &sta_chan_def))
index 4a8019f79fb2cba035a316b0a8d56a7e8f25d614..87c017a3b1ce8db928a999bec05fb1cf32169b73 100644 (file)
@@ -137,8 +137,6 @@ struct mesh_path {
  * @copy_node: function to copy nodes of the table
  * @size_order: determines size of the table, there will be 2^size_order hash
  *     buckets
- * @mean_chain_len: maximum average length for the hash buckets' list, if it is
- *     reached, the table will grow
  * @known_gates: list of known mesh gates and their mpaths by the station. The
  * gate's mpath may or may not be resolved and active.
  *
@@ -154,7 +152,6 @@ struct mesh_table {
        void (*free_node) (struct hlist_node *p, bool free_leafs);
        int (*copy_node) (struct hlist_node *p, struct mesh_table *newtbl);
        int size_order;
-       int mean_chain_len;
        struct hlist_head *known_gates;
        spinlock_t gates_lock;
 
index dadf8dc6f1cfdceb762cabcf057e2e485ad853e8..2ba7aa56b11c0ec121d7851665c989e1a2b3b2d1 100644 (file)
@@ -55,16 +55,21 @@ int mpp_paths_generation;
 static DEFINE_RWLOCK(pathtbl_resize_lock);
 
 
+static inline struct mesh_table *resize_dereference_paths(
+       struct mesh_table __rcu *table)
+{
+       return rcu_dereference_protected(table,
+                                       lockdep_is_held(&pathtbl_resize_lock));
+}
+
 static inline struct mesh_table *resize_dereference_mesh_paths(void)
 {
-       return rcu_dereference_protected(mesh_paths,
-               lockdep_is_held(&pathtbl_resize_lock));
+       return resize_dereference_paths(mesh_paths);
 }
 
 static inline struct mesh_table *resize_dereference_mpp_paths(void)
 {
-       return rcu_dereference_protected(mpp_paths,
-               lockdep_is_held(&pathtbl_resize_lock));
+       return resize_dereference_paths(mpp_paths);
 }
 
 /*
@@ -160,11 +165,10 @@ static int mesh_table_grow(struct mesh_table *oldtbl,
        int i;
 
        if (atomic_read(&oldtbl->entries)
-                       < oldtbl->mean_chain_len * (oldtbl->hash_mask + 1))
+                       < MEAN_CHAIN_LEN * (oldtbl->hash_mask + 1))
                return -EAGAIN;
 
        newtbl->free_node = oldtbl->free_node;
-       newtbl->mean_chain_len = oldtbl->mean_chain_len;
        newtbl->copy_node = oldtbl->copy_node;
        newtbl->known_gates = oldtbl->known_gates;
        atomic_set(&newtbl->entries, atomic_read(&oldtbl->entries));
@@ -585,7 +589,7 @@ struct mesh_path *mesh_path_add(struct ieee80211_sub_if_data *sdata,
 
        hlist_add_head_rcu(&new_node->list, bucket);
        if (atomic_inc_return(&tbl->entries) >=
-           tbl->mean_chain_len * (tbl->hash_mask + 1))
+           MEAN_CHAIN_LEN * (tbl->hash_mask + 1))
                grow = 1;
 
        mesh_paths_generation++;
@@ -714,7 +718,7 @@ int mpp_path_add(struct ieee80211_sub_if_data *sdata,
 
        hlist_add_head_rcu(&new_node->list, bucket);
        if (atomic_inc_return(&tbl->entries) >=
-           tbl->mean_chain_len * (tbl->hash_mask + 1))
+           MEAN_CHAIN_LEN * (tbl->hash_mask + 1))
                grow = 1;
 
        spin_unlock(&tbl->hashwlock[hash_idx]);
@@ -835,6 +839,29 @@ void mesh_path_flush_by_nexthop(struct sta_info *sta)
        rcu_read_unlock();
 }
 
+static void mpp_flush_by_proxy(struct ieee80211_sub_if_data *sdata,
+                              const u8 *proxy)
+{
+       struct mesh_table *tbl;
+       struct mesh_path *mpp;
+       struct mpath_node *node;
+       int i;
+
+       rcu_read_lock();
+       read_lock_bh(&pathtbl_resize_lock);
+       tbl = resize_dereference_mpp_paths();
+       for_each_mesh_entry(tbl, node, i) {
+               mpp = node->mpath;
+               if (ether_addr_equal(mpp->mpp, proxy)) {
+                       spin_lock(&tbl->hashwlock[i]);
+                       __mesh_path_del(tbl, node);
+                       spin_unlock(&tbl->hashwlock[i]);
+               }
+       }
+       read_unlock_bh(&pathtbl_resize_lock);
+       rcu_read_unlock();
+}
+
 static void table_flush_by_iface(struct mesh_table *tbl,
                                 struct ieee80211_sub_if_data *sdata)
 {
@@ -876,14 +903,17 @@ void mesh_path_flush_by_iface(struct ieee80211_sub_if_data *sdata)
 }
 
 /**
- * mesh_path_del - delete a mesh path from the table
+ * table_path_del - delete a path from the mesh or mpp table
  *
- * @addr: dst address (ETH_ALEN length)
+ * @tbl: mesh or mpp path table
  * @sdata: local subif
+ * @addr: dst address (ETH_ALEN length)
  *
  * Returns: 0 if successful
  */
-int mesh_path_del(struct ieee80211_sub_if_data *sdata, const u8 *addr)
+static int table_path_del(struct mesh_table __rcu *rcu_tbl,
+                         struct ieee80211_sub_if_data *sdata,
+                         const u8 *addr)
 {
        struct mesh_table *tbl;
        struct mesh_path *mpath;
@@ -892,8 +922,7 @@ int mesh_path_del(struct ieee80211_sub_if_data *sdata, const u8 *addr)
        int hash_idx;
        int err = 0;
 
-       read_lock_bh(&pathtbl_resize_lock);
-       tbl = resize_dereference_mesh_paths();
+       tbl = resize_dereference_paths(rcu_tbl);
        hash_idx = mesh_table_hash(addr, sdata, tbl);
        bucket = &tbl->hash_buckets[hash_idx];
 
@@ -909,9 +938,50 @@ int mesh_path_del(struct ieee80211_sub_if_data *sdata, const u8 *addr)
 
        err = -ENXIO;
 enddel:
-       mesh_paths_generation++;
        spin_unlock(&tbl->hashwlock[hash_idx]);
+       return err;
+}
+
+/**
+ * mesh_path_del - delete a mesh path from the table
+ *
+ * @addr: dst address (ETH_ALEN length)
+ * @sdata: local subif
+ *
+ * Returns: 0 if successful
+ */
+int mesh_path_del(struct ieee80211_sub_if_data *sdata, const u8 *addr)
+{
+       int err = 0;
+
+       /* flush relevant mpp entries first */
+       mpp_flush_by_proxy(sdata, addr);
+
+       read_lock_bh(&pathtbl_resize_lock);
+       err = table_path_del(mesh_paths, sdata, addr);
+       mesh_paths_generation++;
        read_unlock_bh(&pathtbl_resize_lock);
+
+       return err;
+}
+
+/**
+ * mpp_path_del - delete a mesh proxy path from the table
+ *
+ * @addr: addr address (ETH_ALEN length)
+ * @sdata: local subif
+ *
+ * Returns: 0 if successful
+ */
+static int mpp_path_del(struct ieee80211_sub_if_data *sdata, const u8 *addr)
+{
+       int err = 0;
+
+       read_lock_bh(&pathtbl_resize_lock);
+       err = table_path_del(mpp_paths, sdata, addr);
+       mpp_paths_generation++;
+       read_unlock_bh(&pathtbl_resize_lock);
+
        return err;
 }
 
@@ -1076,7 +1146,6 @@ int mesh_pathtbl_init(void)
                return -ENOMEM;
        tbl_path->free_node = &mesh_path_node_free;
        tbl_path->copy_node = &mesh_path_node_copy;
-       tbl_path->mean_chain_len = MEAN_CHAIN_LEN;
        tbl_path->known_gates = kzalloc(sizeof(struct hlist_head), GFP_ATOMIC);
        if (!tbl_path->known_gates) {
                ret = -ENOMEM;
@@ -1092,7 +1161,6 @@ int mesh_pathtbl_init(void)
        }
        tbl_mpp->free_node = &mesh_path_node_free;
        tbl_mpp->copy_node = &mesh_path_node_copy;
-       tbl_mpp->mean_chain_len = MEAN_CHAIN_LEN;
        tbl_mpp->known_gates = kzalloc(sizeof(struct hlist_head), GFP_ATOMIC);
        if (!tbl_mpp->known_gates) {
                ret = -ENOMEM;
@@ -1131,6 +1199,17 @@ void mesh_path_expire(struct ieee80211_sub_if_data *sdata)
                     time_after(jiffies, mpath->exp_time + MESH_PATH_EXPIRE))
                        mesh_path_del(mpath->sdata, mpath->dst);
        }
+
+       tbl = rcu_dereference(mpp_paths);
+       for_each_mesh_entry(tbl, node, i) {
+               if (node->mpath->sdata != sdata)
+                       continue;
+               mpath = node->mpath;
+               if ((!(mpath->flags & MESH_PATH_FIXED)) &&
+                   time_after(jiffies, mpath->exp_time + MESH_PATH_EXPIRE))
+                       mpp_path_del(mpath->sdata, mpath->dst);
+       }
+
        rcu_read_unlock();
 }
 
index bd3d55eb21d4f8fb5d4cf7a0da75a506ecdf00eb..a07e93c21c9ede90bd94328f1e4aaa51476d58bd 100644 (file)
@@ -976,6 +976,10 @@ mesh_plink_get_event(struct ieee80211_sub_if_data *sdata,
                        mpl_dbg(sdata, "Mesh plink error: no more free plinks\n");
                        goto out;
                }
+
+               /* new matching peer */
+               event = OPN_ACPT;
+               goto out;
        } else {
                if (!test_sta_flag(sta, WLAN_STA_AUTH)) {
                        mpl_dbg(sdata, "Mesh plink: Action frame from non-authed peer\n");
@@ -985,12 +989,6 @@ mesh_plink_get_event(struct ieee80211_sub_if_data *sdata,
                        goto out;
        }
 
-       /* new matching peer */
-       if (!sta) {
-               event = OPN_ACPT;
-               goto out;
-       }
-
        switch (ftype) {
        case WLAN_SP_MESH_PEERING_OPEN:
                if (!matches_local)
index bfbb1acafdd1f004ff4960b2238fbc5343302f38..cbb752ab21720f585a3f2412a3490a552b2786e6 100644 (file)
@@ -196,16 +196,7 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
 
        /* check 40 MHz support, if we have it */
        if (sta_ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) {
-               switch (ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
-               case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
-                       chandef->width = NL80211_CHAN_WIDTH_40;
-                       chandef->center_freq1 += 10;
-                       break;
-               case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
-                       chandef->width = NL80211_CHAN_WIDTH_40;
-                       chandef->center_freq1 -= 10;
-                       break;
-               }
+               ieee80211_chandef_ht_oper(ht_oper, chandef);
        } else {
                /* 40 MHz (and 80 MHz) must be supported for VHT */
                ret = IEEE80211_STA_DISABLE_VHT;
@@ -219,35 +210,11 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
                goto out;
        }
 
-       vht_chandef.chan = channel;
-       vht_chandef.center_freq1 =
-               ieee80211_channel_to_frequency(vht_oper->center_freq_seg1_idx,
-                                              channel->band);
-       vht_chandef.center_freq2 = 0;
-
-       switch (vht_oper->chan_width) {
-       case IEEE80211_VHT_CHANWIDTH_USE_HT:
-               vht_chandef.width = chandef->width;
-               vht_chandef.center_freq1 = chandef->center_freq1;
-               break;
-       case IEEE80211_VHT_CHANWIDTH_80MHZ:
-               vht_chandef.width = NL80211_CHAN_WIDTH_80;
-               break;
-       case IEEE80211_VHT_CHANWIDTH_160MHZ:
-               vht_chandef.width = NL80211_CHAN_WIDTH_160;
-               break;
-       case IEEE80211_VHT_CHANWIDTH_80P80MHZ:
-               vht_chandef.width = NL80211_CHAN_WIDTH_80P80;
-               vht_chandef.center_freq2 =
-                       ieee80211_channel_to_frequency(
-                               vht_oper->center_freq_seg2_idx,
-                               channel->band);
-               break;
-       default:
+       vht_chandef = *chandef;
+       if (!ieee80211_chandef_vht_oper(vht_oper, &vht_chandef)) {
                if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT))
                        sdata_info(sdata,
-                                  "AP VHT operation IE has invalid channel width (%d), disable VHT\n",
-                                  vht_oper->chan_width);
+                                  "AP VHT information is invalid, disable VHT\n");
                ret = IEEE80211_STA_DISABLE_VHT;
                goto out;
        }
@@ -1638,8 +1605,7 @@ void ieee80211_dynamic_ps_timer(unsigned long data)
 
 void ieee80211_dfs_cac_timer_work(struct work_struct *work)
 {
-       struct delayed_work *delayed_work =
-               container_of(work, struct delayed_work, work);
+       struct delayed_work *delayed_work = to_delayed_work(work);
        struct ieee80211_sub_if_data *sdata =
                container_of(delayed_work, struct ieee80211_sub_if_data,
                             dfs_cac_timer_work);
@@ -2079,6 +2045,13 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
        memset(&ifmgd->ht_capa_mask, 0, sizeof(ifmgd->ht_capa_mask));
        memset(&ifmgd->vht_capa, 0, sizeof(ifmgd->vht_capa));
        memset(&ifmgd->vht_capa_mask, 0, sizeof(ifmgd->vht_capa_mask));
+
+       /* reset MU-MIMO ownership and group data */
+       memset(sdata->vif.bss_conf.mu_group.membership, 0,
+              sizeof(sdata->vif.bss_conf.mu_group.membership));
+       memset(sdata->vif.bss_conf.mu_group.position, 0,
+              sizeof(sdata->vif.bss_conf.mu_group.position));
+       changed |= BSS_CHANGED_MU_GROUPS;
        sdata->flags &= ~IEEE80211_SDATA_MU_MIMO_OWNER;
 
        sdata->ap_power_level = IEEE80211_UNSET_POWER_LEVEL;
@@ -3571,6 +3544,9 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
                                elems.ht_cap_elem, elems.ht_operation,
                                elems.vht_operation, bssid, &changed)) {
                mutex_unlock(&local->sta_mtx);
+               sdata_info(sdata,
+                          "failed to follow AP %pM bandwidth change, disconnect\n",
+                          bssid);
                ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
                                       WLAN_REASON_DEAUTH_LEAVING,
                                       true, deauth_buf);
@@ -3946,11 +3922,9 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
                         * We actually lost the connection ... or did we?
                         * Let's make sure!
                         */
-                       wiphy_debug(local->hw.wiphy,
-                                   "%s: No probe response from AP %pM"
-                                   " after %dms, disconnecting.\n",
-                                   sdata->name,
-                                   bssid, probe_wait_ms);
+                       mlme_dbg(sdata,
+                                "No probe response from AP %pM after %dms, disconnecting.\n",
+                                bssid, probe_wait_ms);
 
                        ieee80211_sta_connection_lost(sdata, bssid,
                                WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, false);
@@ -4536,6 +4510,9 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
        if (ifmgd->associated) {
                u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
 
+               sdata_info(sdata,
+                          "disconnect from AP %pM for new auth to %pM\n",
+                          ifmgd->associated->bssid, req->bss->bssid);
                ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
                                       WLAN_REASON_UNSPECIFIED,
                                       false, frame_buf);
@@ -4604,6 +4581,9 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
        if (ifmgd->associated) {
                u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
 
+               sdata_info(sdata,
+                          "disconnect from AP %pM for new assoc to %pM\n",
+                          ifmgd->associated->bssid, req->bss->bssid);
                ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
                                       WLAN_REASON_UNSPECIFIED,
                                       false, frame_buf);
index bc081850ac0e5538241bd1cab37b65aeefd2c244..91279576f4a716d00cfd810e5d6db18306520c1c 100644 (file)
@@ -4,6 +4,7 @@
  * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
  * Copyright 2007-2010 Johannes Berg <johannes@sipsolutions.net>
  * Copyright 2013-2014  Intel Mobile Communications GmbH
+ * Copyright(c) 2015 - 2016 Intel Deutschland GmbH
  *
  * 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
@@ -18,6 +19,7 @@
 #include <linux/etherdevice.h>
 #include <linux/rcupdate.h>
 #include <linux/export.h>
+#include <linux/bitops.h>
 #include <net/mac80211.h>
 #include <net/ieee80211_radiotap.h>
 #include <asm/unaligned.h>
@@ -122,7 +124,8 @@ static inline bool should_drop_frame(struct sk_buff *skb, int present_fcs_len,
        hdr = (void *)(skb->data + rtap_vendor_space);
 
        if (status->flag & (RX_FLAG_FAILED_FCS_CRC |
-                           RX_FLAG_FAILED_PLCP_CRC))
+                           RX_FLAG_FAILED_PLCP_CRC |
+                           RX_FLAG_ONLY_MONITOR))
                return true;
 
        if (unlikely(skb->len < 16 + present_fcs_len + rtap_vendor_space))
@@ -507,7 +510,7 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
                return NULL;
        }
 
-       if (!local->monitors) {
+       if (!local->monitors || (status->flag & RX_FLAG_SKIP_MONITOR)) {
                if (should_drop_frame(origskb, present_fcs_len,
                                      rtap_vendor_space)) {
                        dev_kfree_skb(origskb);
@@ -797,6 +800,26 @@ static ieee80211_rx_result ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
        return RX_CONTINUE;
 }
 
+static inline bool ieee80211_rx_reorder_ready(struct tid_ampdu_rx *tid_agg_rx,
+                                             int index)
+{
+       struct sk_buff_head *frames = &tid_agg_rx->reorder_buf[index];
+       struct sk_buff *tail = skb_peek_tail(frames);
+       struct ieee80211_rx_status *status;
+
+       if (tid_agg_rx->reorder_buf_filtered & BIT_ULL(index))
+               return true;
+
+       if (!tail)
+               return false;
+
+       status = IEEE80211_SKB_RXCB(tail);
+       if (status->flag & RX_FLAG_AMSDU_MORE)
+               return false;
+
+       return true;
+}
+
 static void ieee80211_release_reorder_frame(struct ieee80211_sub_if_data *sdata,
                                            struct tid_ampdu_rx *tid_agg_rx,
                                            int index,
@@ -811,7 +834,7 @@ static void ieee80211_release_reorder_frame(struct ieee80211_sub_if_data *sdata,
        if (skb_queue_empty(skb_list))
                goto no_frame;
 
-       if (!ieee80211_rx_reorder_ready(skb_list)) {
+       if (!ieee80211_rx_reorder_ready(tid_agg_rx, index)) {
                __skb_queue_purge(skb_list);
                goto no_frame;
        }
@@ -825,6 +848,7 @@ static void ieee80211_release_reorder_frame(struct ieee80211_sub_if_data *sdata,
        }
 
 no_frame:
+       tid_agg_rx->reorder_buf_filtered &= ~BIT_ULL(index);
        tid_agg_rx->head_seq_num = ieee80211_sn_inc(tid_agg_rx->head_seq_num);
 }
 
@@ -865,7 +889,7 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,
 
        /* release the buffer until next missing frame */
        index = tid_agg_rx->head_seq_num % tid_agg_rx->buf_size;
-       if (!ieee80211_rx_reorder_ready(&tid_agg_rx->reorder_buf[index]) &&
+       if (!ieee80211_rx_reorder_ready(tid_agg_rx, index) &&
            tid_agg_rx->stored_mpdu_num) {
                /*
                 * No buffers ready to be released, but check whether any
@@ -874,8 +898,7 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,
                int skipped = 1;
                for (j = (index + 1) % tid_agg_rx->buf_size; j != index;
                     j = (j + 1) % tid_agg_rx->buf_size) {
-                       if (!ieee80211_rx_reorder_ready(
-                                       &tid_agg_rx->reorder_buf[j])) {
+                       if (!ieee80211_rx_reorder_ready(tid_agg_rx, j)) {
                                skipped++;
                                continue;
                        }
@@ -902,8 +925,7 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,
                                 skipped) & IEEE80211_SN_MASK;
                        skipped = 0;
                }
-       } else while (ieee80211_rx_reorder_ready(
-                               &tid_agg_rx->reorder_buf[index])) {
+       } else while (ieee80211_rx_reorder_ready(tid_agg_rx, index)) {
                ieee80211_release_reorder_frame(sdata, tid_agg_rx, index,
                                                frames);
                index = tid_agg_rx->head_seq_num % tid_agg_rx->buf_size;
@@ -914,8 +936,7 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,
 
                for (; j != (index - 1) % tid_agg_rx->buf_size;
                     j = (j + 1) % tid_agg_rx->buf_size) {
-                       if (ieee80211_rx_reorder_ready(
-                                       &tid_agg_rx->reorder_buf[j]))
+                       if (ieee80211_rx_reorder_ready(tid_agg_rx, j))
                                break;
                }
 
@@ -986,7 +1007,7 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata
        index = mpdu_seq_num % tid_agg_rx->buf_size;
 
        /* check if we already stored this frame */
-       if (ieee80211_rx_reorder_ready(&tid_agg_rx->reorder_buf[index])) {
+       if (ieee80211_rx_reorder_ready(tid_agg_rx, index)) {
                dev_kfree_skb(skb);
                goto out;
        }
@@ -1099,6 +1120,9 @@ ieee80211_rx_h_check_dup(struct ieee80211_rx_data *rx)
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
        struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb);
 
+       if (status->flag & RX_FLAG_DUP_VALIDATED)
+               return RX_CONTINUE;
+
        /*
         * Drop duplicate 802.11 retransmissions
         * (IEEE 802.11-2012: 9.3.2.10 "Duplicate detection and recovery")
@@ -2199,9 +2223,6 @@ ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx)
        skb->dev = dev;
        __skb_queue_head_init(&frame_list);
 
-       if (skb_linearize(skb))
-               return RX_DROP_UNUSABLE;
-
        ieee80211_amsdu_to_8023s(skb, &frame_list, dev->dev_addr,
                                 rx->sdata->vif.type,
                                 rx->local->hw.extra_tx_headroom, true);
@@ -2231,7 +2252,7 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
        struct ieee80211_local *local = rx->local;
        struct ieee80211_sub_if_data *sdata = rx->sdata;
        struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
-       u16 q, hdrlen;
+       u16 ac, q, hdrlen;
 
        hdr = (struct ieee80211_hdr *) skb->data;
        hdrlen = ieee80211_hdrlen(hdr->frame_control);
@@ -2290,6 +2311,7 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
                        spin_lock_bh(&mppath->state_lock);
                        if (!ether_addr_equal(mppath->mpp, mpp_addr))
                                memcpy(mppath->mpp, mpp_addr, ETH_ALEN);
+                       mppath->exp_time = jiffies;
                        spin_unlock_bh(&mppath->state_lock);
                }
                rcu_read_unlock();
@@ -2300,7 +2322,8 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
            ether_addr_equal(sdata->vif.addr, hdr->addr3))
                return RX_CONTINUE;
 
-       q = ieee80211_select_queue_80211(sdata, skb, hdr);
+       ac = ieee80211_select_queue_80211(sdata, skb, hdr);
+       q = sdata->vif.hw_queue[ac];
        if (ieee80211_queue_stopped(&local->hw, q)) {
                IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_congestion);
                return RX_DROP_MONITOR;
@@ -2738,6 +2761,11 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
                                                    opmode, status->band);
                        goto handled;
                }
+               case WLAN_VHT_ACTION_GROUPID_MGMT: {
+                       if (len < IEEE80211_MIN_ACTION_SIZE + 25)
+                               goto invalid;
+                       goto queue;
+               }
                default:
                        break;
                }
@@ -3275,6 +3303,85 @@ void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid)
        ieee80211_rx_handlers(&rx, &frames);
 }
 
+void ieee80211_mark_rx_ba_filtered_frames(struct ieee80211_sta *pubsta, u8 tid,
+                                         u16 ssn, u64 filtered,
+                                         u16 received_mpdus)
+{
+       struct sta_info *sta;
+       struct tid_ampdu_rx *tid_agg_rx;
+       struct sk_buff_head frames;
+       struct ieee80211_rx_data rx = {
+               /* This is OK -- must be QoS data frame */
+               .security_idx = tid,
+               .seqno_idx = tid,
+       };
+       int i, diff;
+
+       if (WARN_ON(!pubsta || tid >= IEEE80211_NUM_TIDS))
+               return;
+
+       __skb_queue_head_init(&frames);
+
+       sta = container_of(pubsta, struct sta_info, sta);
+
+       rx.sta = sta;
+       rx.sdata = sta->sdata;
+       rx.local = sta->local;
+
+       rcu_read_lock();
+       tid_agg_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[tid]);
+       if (!tid_agg_rx)
+               goto out;
+
+       spin_lock_bh(&tid_agg_rx->reorder_lock);
+
+       if (received_mpdus >= IEEE80211_SN_MODULO >> 1) {
+               int release;
+
+               /* release all frames in the reorder buffer */
+               release = (tid_agg_rx->head_seq_num + tid_agg_rx->buf_size) %
+                          IEEE80211_SN_MODULO;
+               ieee80211_release_reorder_frames(sta->sdata, tid_agg_rx,
+                                                release, &frames);
+               /* update ssn to match received ssn */
+               tid_agg_rx->head_seq_num = ssn;
+       } else {
+               ieee80211_release_reorder_frames(sta->sdata, tid_agg_rx, ssn,
+                                                &frames);
+       }
+
+       /* handle the case that received ssn is behind the mac ssn.
+        * it can be tid_agg_rx->buf_size behind and still be valid */
+       diff = (tid_agg_rx->head_seq_num - ssn) & IEEE80211_SN_MASK;
+       if (diff >= tid_agg_rx->buf_size) {
+               tid_agg_rx->reorder_buf_filtered = 0;
+               goto release;
+       }
+       filtered = filtered >> diff;
+       ssn += diff;
+
+       /* update bitmap */
+       for (i = 0; i < tid_agg_rx->buf_size; i++) {
+               int index = (ssn + i) % tid_agg_rx->buf_size;
+
+               tid_agg_rx->reorder_buf_filtered &= ~BIT_ULL(index);
+               if (filtered & BIT_ULL(i))
+                       tid_agg_rx->reorder_buf_filtered |= BIT_ULL(index);
+       }
+
+       /* now process also frames that the filter marking released */
+       ieee80211_sta_reorder_release(sta->sdata, tid_agg_rx, &frames);
+
+release:
+       spin_unlock_bh(&tid_agg_rx->reorder_lock);
+
+       ieee80211_rx_handlers(&rx, &frames);
+
+ out:
+       rcu_read_unlock();
+}
+EXPORT_SYMBOL(ieee80211_mark_rx_ba_filtered_frames);
+
 /* main receive path */
 
 static bool ieee80211_accept_frame(struct ieee80211_rx_data *rx)
index a4a4f89d3ba01138c033a13a7d023a04a99e3e6d..d20bab5c146c9d435ee67d6a11eec746db1b1bfa 100644 (file)
@@ -116,6 +116,7 @@ static void __cleanup_single_sta(struct sta_info *sta)
 
                        ieee80211_purge_tx_queue(&local->hw, &txqi->queue);
                        atomic_sub(n, &sdata->txqs_len[txqi->txq.ac]);
+                       txqi->byte_cnt = 0;
                }
        }
 
@@ -498,11 +499,17 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU)
 {
        struct ieee80211_local *local = sta->local;
        struct ieee80211_sub_if_data *sdata = sta->sdata;
-       struct station_info sinfo;
+       struct station_info *sinfo;
        int err = 0;
 
        lockdep_assert_held(&local->sta_mtx);
 
+       sinfo = kzalloc(sizeof(struct station_info), GFP_KERNEL);
+       if (!sinfo) {
+               err = -ENOMEM;
+               goto out_err;
+       }
+
        /* check if STA exists already */
        if (sta_info_get_bss(sdata, sta->sta.addr)) {
                err = -EEXIST;
@@ -530,14 +537,12 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU)
        /* accept BA sessions now */
        clear_sta_flag(sta, WLAN_STA_BLOCK_BA);
 
-       ieee80211_recalc_min_chandef(sdata);
        ieee80211_sta_debugfs_add(sta);
        rate_control_add_sta_debugfs(sta);
 
-       memset(&sinfo, 0, sizeof(sinfo));
-       sinfo.filled = 0;
-       sinfo.generation = local->sta_generation;
-       cfg80211_new_sta(sdata->dev, sta->sta.addr, &sinfo, GFP_KERNEL);
+       sinfo->generation = local->sta_generation;
+       cfg80211_new_sta(sdata->dev, sta->sta.addr, sinfo, GFP_KERNEL);
+       kfree(sinfo);
 
        sta_dbg(sdata, "Inserted STA %pM\n", sta->sta.addr);
 
@@ -557,6 +562,7 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU)
        __cleanup_single_sta(sta);
  out_err:
        mutex_unlock(&local->sta_mtx);
+       kfree(sinfo);
        rcu_read_lock();
        return err;
 }
@@ -898,7 +904,7 @@ static void __sta_info_destroy_part2(struct sta_info *sta)
 {
        struct ieee80211_local *local = sta->local;
        struct ieee80211_sub_if_data *sdata = sta->sdata;
-       struct station_info sinfo = {};
+       struct station_info *sinfo;
        int ret;
 
        /*
@@ -936,12 +942,14 @@ static void __sta_info_destroy_part2(struct sta_info *sta)
 
        sta_dbg(sdata, "Removed STA %pM\n", sta->sta.addr);
 
-       sta_set_sinfo(sta, &sinfo);
-       cfg80211_del_sta_sinfo(sdata->dev, sta->sta.addr, &sinfo, GFP_KERNEL);
+       sinfo = kzalloc(sizeof(*sinfo), GFP_KERNEL);
+       if (sinfo)
+               sta_set_sinfo(sta, sinfo);
+       cfg80211_del_sta_sinfo(sdata->dev, sta->sta.addr, sinfo, GFP_KERNEL);
+       kfree(sinfo);
 
        rate_control_remove_sta_debugfs(sta);
        ieee80211_sta_debugfs_remove(sta);
-       ieee80211_recalc_min_chandef(sdata);
 
        cleanup_single_sta(sta);
 }
@@ -1808,14 +1816,17 @@ int sta_info_move_state(struct sta_info *sta,
                        clear_bit(WLAN_STA_AUTH, &sta->_flags);
                break;
        case IEEE80211_STA_AUTH:
-               if (sta->sta_state == IEEE80211_STA_NONE)
+               if (sta->sta_state == IEEE80211_STA_NONE) {
                        set_bit(WLAN_STA_AUTH, &sta->_flags);
-               else if (sta->sta_state == IEEE80211_STA_ASSOC)
+               } else if (sta->sta_state == IEEE80211_STA_ASSOC) {
                        clear_bit(WLAN_STA_ASSOC, &sta->_flags);
+                       ieee80211_recalc_min_chandef(sta->sdata);
+               }
                break;
        case IEEE80211_STA_ASSOC:
                if (sta->sta_state == IEEE80211_STA_AUTH) {
                        set_bit(WLAN_STA_ASSOC, &sta->_flags);
+                       ieee80211_recalc_min_chandef(sta->sdata);
                } else if (sta->sta_state == IEEE80211_STA_AUTHORIZED) {
                        if (sta->sdata->vif.type == NL80211_IFTYPE_AP ||
                            (sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN &&
index d6051629ed155859819a591960183c0062c230d9..053f5c4fa495ba02cd99c5ddb201ce0ad0f7aeff 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * Copyright 2002-2005, Devicescape Software, Inc.
  * Copyright 2013-2014  Intel Mobile Communications GmbH
+ * Copyright(c) 2015 Intel Deutschland GmbH
  *
  * 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
@@ -167,6 +168,8 @@ struct tid_ampdu_tx {
  *
  * @reorder_buf: buffer to reorder incoming aggregated MPDUs. An MPDU may be an
  *     A-MSDU with individually reported subframes.
+ * @reorder_buf_filtered: bitmap indicating where there are filtered frames in
+ *     the reorder buffer that should be ignored when releasing frames
  * @reorder_time: jiffies when skb was added
  * @session_timer: check if peer keeps Tx-ing on the TID (by timeout value)
  * @reorder_timer: releases expired frames from the reorder buffer.
@@ -194,6 +197,7 @@ struct tid_ampdu_tx {
 struct tid_ampdu_rx {
        struct rcu_head rcu_head;
        spinlock_t reorder_lock;
+       u64 reorder_buf_filtered;
        struct sk_buff_head *reorder_buf;
        unsigned long *reorder_time;
        struct timer_list session_timer;
@@ -212,20 +216,21 @@ struct tid_ampdu_rx {
 /**
  * struct sta_ampdu_mlme - STA aggregation information.
  *
+ * @mtx: mutex to protect all TX data (except non-NULL assignments
+ *     to tid_tx[idx], which are protected by the sta spinlock)
+ *     tid_start_tx is also protected by sta->lock.
  * @tid_rx: aggregation info for Rx per TID -- RCU protected
- * @tid_tx: aggregation info for Tx per TID
- * @tid_start_tx: sessions where start was requested
- * @addba_req_num: number of times addBA request has been sent.
- * @last_addba_req_time: timestamp of the last addBA request.
- * @dialog_token_allocator: dialog token enumerator for each new session;
- * @work: work struct for starting/stopping aggregation
  * @tid_rx_timer_expired: bitmap indicating on which TIDs the
  *     RX timer expired until the work for it runs
  * @tid_rx_stop_requested:  bitmap indicating which BA sessions per TID the
  *     driver requested to close until the work for it runs
- * @mtx: mutex to protect all TX data (except non-NULL assignments
- *     to tid_tx[idx], which are protected by the sta spinlock)
- *     tid_start_tx is also protected by sta->lock.
+ * @agg_session_valid: bitmap indicating which TID has a rx BA session open on
+ * @work: work struct for starting/stopping aggregation
+ * @tid_tx: aggregation info for Tx per TID
+ * @tid_start_tx: sessions where start was requested
+ * @last_addba_req_time: timestamp of the last addBA request.
+ * @addba_req_num: number of times addBA request has been sent.
+ * @dialog_token_allocator: dialog token enumerator for each new session;
  */
 struct sta_ampdu_mlme {
        struct mutex mtx;
@@ -233,6 +238,7 @@ struct sta_ampdu_mlme {
        struct tid_ampdu_rx __rcu *tid_rx[IEEE80211_NUM_TIDS];
        unsigned long tid_rx_timer_expired[BITS_TO_LONGS(IEEE80211_NUM_TIDS)];
        unsigned long tid_rx_stop_requested[BITS_TO_LONGS(IEEE80211_NUM_TIDS)];
+       unsigned long agg_session_valid[BITS_TO_LONGS(IEEE80211_NUM_TIDS)];
        /* tx */
        struct work_struct work;
        struct tid_ampdu_tx __rcu *tid_tx[IEEE80211_NUM_TIDS];
index a6b4442776a0519251b1a7650007343a3541181e..2b0a17ee907abff27d2fef4c93e8d2a03eda4c17 100644 (file)
 #define KEY_PR_FMT     " cipher:0x%x, flags=%#x, keyidx=%d, hw_key_idx=%d"
 #define KEY_PR_ARG     __entry->cipher, __entry->flags, __entry->keyidx, __entry->hw_key_idx
 
-
+#define AMPDU_ACTION_ENTRY     __field(enum ieee80211_ampdu_mlme_action,               \
+                                       ieee80211_ampdu_mlme_action)                    \
+                               STA_ENTRY                                               \
+                               __field(u16, tid)                                       \
+                               __field(u16, ssn)                                       \
+                               __field(u8, buf_size)                                   \
+                               __field(bool, amsdu)                                    \
+                               __field(u16, timeout)
+#define AMPDU_ACTION_ASSIGN    STA_NAMED_ASSIGN(params->sta);                          \
+                               __entry->tid = params->tid;                             \
+                               __entry->ssn = params->ssn;                             \
+                               __entry->buf_size = params->buf_size;                   \
+                               __entry->amsdu = params->amsdu;                         \
+                               __entry->timeout = params->timeout;
+#define AMPDU_ACTION_PR_FMT    STA_PR_FMT " tid %d, ssn %d, buf_size %u, amsdu %d, timeout %d"
+#define AMPDU_ACTION_PR_ARG    STA_PR_ARG, __entry->tid, __entry->ssn,                 \
+                               __entry->buf_size, __entry->amsdu, __entry->timeout
 
 /*
  * Tracing for driver callbacks.
@@ -970,38 +986,25 @@ DEFINE_EVENT(local_only_evt, drv_tx_last_beacon,
 TRACE_EVENT(drv_ampdu_action,
        TP_PROTO(struct ieee80211_local *local,
                 struct ieee80211_sub_if_data *sdata,
-                enum ieee80211_ampdu_mlme_action action,
-                struct ieee80211_sta *sta, u16 tid,
-                u16 *ssn, u8 buf_size, bool amsdu),
+                struct ieee80211_ampdu_params *params),
 
-       TP_ARGS(local, sdata, action, sta, tid, ssn, buf_size, amsdu),
+       TP_ARGS(local, sdata, params),
 
        TP_STRUCT__entry(
                LOCAL_ENTRY
-               STA_ENTRY
-               __field(u32, action)
-               __field(u16, tid)
-               __field(u16, ssn)
-               __field(u8, buf_size)
-               __field(bool, amsdu)
                VIF_ENTRY
+               AMPDU_ACTION_ENTRY
        ),
 
        TP_fast_assign(
                LOCAL_ASSIGN;
                VIF_ASSIGN;
-               STA_ASSIGN;
-               __entry->action = action;
-               __entry->tid = tid;
-               __entry->ssn = ssn ? *ssn : 0;
-               __entry->buf_size = buf_size;
-               __entry->amsdu = amsdu;
+               AMPDU_ACTION_ASSIGN;
        ),
 
        TP_printk(
-               LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT " action:%d tid:%d buf:%d amsdu:%d",
-               LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, __entry->action,
-               __entry->tid, __entry->buf_size, __entry->amsdu
+               LOCAL_PR_FMT VIF_PR_FMT AMPDU_ACTION_PR_FMT,
+               LOCAL_PR_ARG, VIF_PR_ARG, AMPDU_ACTION_PR_ARG
        )
 );
 
index 3311ce0f3d6c2f9c93c075458159935c631d144d..7bb67fa9f4d26ee5acf28b411b8558f4420f40c0 100644 (file)
@@ -710,6 +710,10 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)
 
        info->control.short_preamble = txrc.short_preamble;
 
+       /* don't ask rate control when rate already injected via radiotap */
+       if (info->control.flags & IEEE80211_TX_CTRL_RATE_INJECT)
+               return TX_CONTINUE;
+
        if (tx->sta)
                assoc = test_sta_flag(tx->sta, WLAN_STA_ASSOC);
 
@@ -1266,7 +1270,11 @@ static void ieee80211_drv_tx(struct ieee80211_local *local,
        if (atomic_read(&sdata->txqs_len[ac]) >= local->hw.txq_ac_max_pending)
                netif_stop_subqueue(sdata->dev, ac);
 
-       skb_queue_tail(&txqi->queue, skb);
+       spin_lock_bh(&txqi->queue.lock);
+       txqi->byte_cnt += skb->len;
+       __skb_queue_tail(&txqi->queue, skb);
+       spin_unlock_bh(&txqi->queue.lock);
+
        drv_wake_tx_queue(local, txqi);
 
        return;
@@ -1294,6 +1302,8 @@ struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw,
        if (!skb)
                goto out;
 
+       txqi->byte_cnt -= skb->len;
+
        atomic_dec(&sdata->txqs_len[ac]);
        if (__netif_subqueue_stopped(sdata->dev, ac))
                ieee80211_propagate_queue_wake(local, sdata->vif.hw_queue[ac]);
@@ -1665,15 +1675,24 @@ void ieee80211_xmit(struct ieee80211_sub_if_data *sdata,
        ieee80211_tx(sdata, sta, skb, false);
 }
 
-static bool ieee80211_parse_tx_radiotap(struct sk_buff *skb)
+static bool ieee80211_parse_tx_radiotap(struct ieee80211_local *local,
+                                       struct sk_buff *skb)
 {
        struct ieee80211_radiotap_iterator iterator;
        struct ieee80211_radiotap_header *rthdr =
                (struct ieee80211_radiotap_header *) skb->data;
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct ieee80211_supported_band *sband =
+               local->hw.wiphy->bands[info->band];
        int ret = ieee80211_radiotap_iterator_init(&iterator, rthdr, skb->len,
                                                   NULL);
        u16 txflags;
+       u16 rate = 0;
+       bool rate_found = false;
+       u8 rate_retries = 0;
+       u16 rate_flags = 0;
+       u8 mcs_known, mcs_flags;
+       int i;
 
        info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT |
                       IEEE80211_TX_CTL_DONTFRAG;
@@ -1724,6 +1743,35 @@ static bool ieee80211_parse_tx_radiotap(struct sk_buff *skb)
                                info->flags |= IEEE80211_TX_CTL_NO_ACK;
                        break;
 
+               case IEEE80211_RADIOTAP_RATE:
+                       rate = *iterator.this_arg;
+                       rate_flags = 0;
+                       rate_found = true;
+                       break;
+
+               case IEEE80211_RADIOTAP_DATA_RETRIES:
+                       rate_retries = *iterator.this_arg;
+                       break;
+
+               case IEEE80211_RADIOTAP_MCS:
+                       mcs_known = iterator.this_arg[0];
+                       mcs_flags = iterator.this_arg[1];
+                       if (!(mcs_known & IEEE80211_RADIOTAP_MCS_HAVE_MCS))
+                               break;
+
+                       rate_found = true;
+                       rate = iterator.this_arg[2];
+                       rate_flags = IEEE80211_TX_RC_MCS;
+
+                       if (mcs_known & IEEE80211_RADIOTAP_MCS_HAVE_GI &&
+                           mcs_flags & IEEE80211_RADIOTAP_MCS_SGI)
+                               rate_flags |= IEEE80211_TX_RC_SHORT_GI;
+
+                       if (mcs_known & IEEE80211_RADIOTAP_MCS_HAVE_BW &&
+                           mcs_flags & IEEE80211_RADIOTAP_MCS_BW_40)
+                               rate_flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
+                       break;
+
                /*
                 * Please update the file
                 * Documentation/networking/mac80211-injection.txt
@@ -1738,6 +1786,32 @@ static bool ieee80211_parse_tx_radiotap(struct sk_buff *skb)
        if (ret != -ENOENT) /* ie, if we didn't simply run out of fields */
                return false;
 
+       if (rate_found) {
+               info->control.flags |= IEEE80211_TX_CTRL_RATE_INJECT;
+
+               for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
+                       info->control.rates[i].idx = -1;
+                       info->control.rates[i].flags = 0;
+                       info->control.rates[i].count = 0;
+               }
+
+               if (rate_flags & IEEE80211_TX_RC_MCS) {
+                       info->control.rates[0].idx = rate;
+               } else {
+                       for (i = 0; i < sband->n_bitrates; i++) {
+                               if (rate * 5 != sband->bitrates[i].bitrate)
+                                       continue;
+
+                               info->control.rates[0].idx = i;
+                               break;
+                       }
+               }
+
+               info->control.rates[0].flags = rate_flags;
+               info->control.rates[0].count = min_t(u8, rate_retries + 1,
+                                                    local->hw.max_rate_tries);
+       }
+
        /*
         * remove the radiotap header
         * iterator->_max_length was sanity-checked against
@@ -1819,7 +1893,7 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
                      IEEE80211_TX_CTL_INJECTED;
 
        /* process and remove the injection radiotap header */
-       if (!ieee80211_parse_tx_radiotap(skb))
+       if (!ieee80211_parse_tx_radiotap(local, skb))
                goto fail;
 
        rcu_read_lock();
@@ -2099,8 +2173,11 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata,
                                        mpp_lookup = true;
                        }
 
-                       if (mpp_lookup)
+                       if (mpp_lookup) {
                                mppath = mpp_path_lookup(sdata, skb->data);
+                               if (mppath)
+                                       mppath->exp_time = jiffies;
+                       }
 
                        if (mppath && mpath)
                                mesh_path_del(mpath->sdata, mpath->dst);
index 58f58bd5202ff5ad99630bc7584a8afeb8c2cf26..74dd4d72b2332281f0ee3e73138b8fec3a53e380 100644 (file)
@@ -4,7 +4,7 @@
  * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
  * Copyright 2007      Johannes Berg <johannes@sipsolutions.net>
  * Copyright 2013-2014  Intel Mobile Communications GmbH
- * Copyright (C) 2015  Intel Deutschland GmbH
+ * Copyright (C) 2015-2016     Intel Deutschland GmbH
  *
  * 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
@@ -1928,6 +1928,9 @@ int ieee80211_reconfig(struct ieee80211_local *local)
                          BSS_CHANGED_IDLE |
                          BSS_CHANGED_TXPOWER;
 
+               if (sdata->flags & IEEE80211_SDATA_MU_MIMO_OWNER)
+                       changed |= BSS_CHANGED_MU_GROUPS;
+
                switch (sdata->vif.type) {
                case NL80211_IFTYPE_STATION:
                        changed |= BSS_CHANGED_ASSOC |
@@ -2371,10 +2374,23 @@ u8 *ieee80211_ie_build_vht_oper(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
 
        switch (chandef->width) {
        case NL80211_CHAN_WIDTH_160:
-               vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_160MHZ;
+               /*
+                * Convert 160 MHz channel width to new style as interop
+                * workaround.
+                */
+               vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ;
+               vht_oper->center_freq_seg2_idx = vht_oper->center_freq_seg1_idx;
+               if (chandef->chan->center_freq < chandef->center_freq1)
+                       vht_oper->center_freq_seg1_idx -= 8;
+               else
+                       vht_oper->center_freq_seg1_idx += 8;
                break;
        case NL80211_CHAN_WIDTH_80P80:
-               vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80P80MHZ;
+               /*
+                * Convert 80+80 MHz channel width to new style as interop
+                * workaround.
+                */
+               vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ;
                break;
        case NL80211_CHAN_WIDTH_80:
                vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ;
@@ -2390,17 +2406,13 @@ u8 *ieee80211_ie_build_vht_oper(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
        return pos + sizeof(struct ieee80211_vht_operation);
 }
 
-void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan,
-                                 const struct ieee80211_ht_operation *ht_oper,
-                                 struct cfg80211_chan_def *chandef)
+bool ieee80211_chandef_ht_oper(const struct ieee80211_ht_operation *ht_oper,
+                              struct cfg80211_chan_def *chandef)
 {
        enum nl80211_channel_type channel_type;
 
-       if (!ht_oper) {
-               cfg80211_chandef_create(chandef, control_chan,
-                                       NL80211_CHAN_NO_HT);
-               return;
-       }
+       if (!ht_oper)
+               return false;
 
        switch (ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
        case IEEE80211_HT_PARAM_CHA_SEC_NONE:
@@ -2414,42 +2426,66 @@ void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan,
                break;
        default:
                channel_type = NL80211_CHAN_NO_HT;
+               return false;
        }
 
-       cfg80211_chandef_create(chandef, control_chan, channel_type);
+       cfg80211_chandef_create(chandef, chandef->chan, channel_type);
+       return true;
 }
 
-void ieee80211_vht_oper_to_chandef(struct ieee80211_channel *control_chan,
-                                  const struct ieee80211_vht_operation *oper,
-                                  struct cfg80211_chan_def *chandef)
+bool ieee80211_chandef_vht_oper(const struct ieee80211_vht_operation *oper,
+                               struct cfg80211_chan_def *chandef)
 {
+       struct cfg80211_chan_def new = *chandef;
+       int cf1, cf2;
+
        if (!oper)
-               return;
+               return false;
 
-       chandef->chan = control_chan;
+       cf1 = ieee80211_channel_to_frequency(oper->center_freq_seg1_idx,
+                                            chandef->chan->band);
+       cf2 = ieee80211_channel_to_frequency(oper->center_freq_seg2_idx,
+                                            chandef->chan->band);
 
        switch (oper->chan_width) {
        case IEEE80211_VHT_CHANWIDTH_USE_HT:
                break;
        case IEEE80211_VHT_CHANWIDTH_80MHZ:
-               chandef->width = NL80211_CHAN_WIDTH_80;
+               new.width = NL80211_CHAN_WIDTH_80;
+               new.center_freq1 = cf1;
+               /* If needed, adjust based on the newer interop workaround. */
+               if (oper->center_freq_seg2_idx) {
+                       unsigned int diff;
+
+                       diff = abs(oper->center_freq_seg2_idx -
+                                  oper->center_freq_seg1_idx);
+                       if (diff == 8) {
+                               new.width = NL80211_CHAN_WIDTH_160;
+                               new.center_freq1 = cf2;
+                       } else if (diff > 8) {
+                               new.width = NL80211_CHAN_WIDTH_80P80;
+                               new.center_freq2 = cf2;
+                       }
+               }
                break;
        case IEEE80211_VHT_CHANWIDTH_160MHZ:
-               chandef->width = NL80211_CHAN_WIDTH_160;
+               new.width = NL80211_CHAN_WIDTH_160;
+               new.center_freq1 = cf1;
                break;
        case IEEE80211_VHT_CHANWIDTH_80P80MHZ:
-               chandef->width = NL80211_CHAN_WIDTH_80P80;
+               new.width = NL80211_CHAN_WIDTH_80P80;
+               new.center_freq1 = cf1;
+               new.center_freq2 = cf2;
                break;
        default:
-               break;
+               return false;
        }
 
-       chandef->center_freq1 =
-               ieee80211_channel_to_frequency(oper->center_freq_seg1_idx,
-                                              control_chan->band);
-       chandef->center_freq2 =
-               ieee80211_channel_to_frequency(oper->center_freq_seg2_idx,
-                                              control_chan->band);
+       if (!cfg80211_chandef_valid(&new))
+               return false;
+
+       *chandef = new;
+       return true;
 }
 
 int ieee80211_parse_bitrates(struct cfg80211_chan_def *chandef,
@@ -2672,6 +2708,18 @@ u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local,
                sband = local->hw.wiphy->bands[status->band];
                bitrate = sband->bitrates[status->rate_idx].bitrate;
                ri.legacy = DIV_ROUND_UP(bitrate, (1 << shift));
+
+               if (status->flag & RX_FLAG_MACTIME_PLCP_START) {
+                       /* TODO: handle HT/VHT preambles */
+                       if (status->band == IEEE80211_BAND_5GHZ) {
+                               ts += 20 << shift;
+                               mpdu_offset += 2;
+                       } else if (status->flag & RX_FLAG_SHORTPRE) {
+                               ts += 96;
+                       } else {
+                               ts += 192;
+                       }
+               }
        }
 
        rate = cfg80211_calculate_bitrate(&ri);
@@ -3357,3 +3405,17 @@ void ieee80211_init_tx_queue(struct ieee80211_sub_if_data *sdata,
                txqi->txq.ac = IEEE80211_AC_BE;
        }
 }
+
+void ieee80211_txq_get_depth(struct ieee80211_txq *txq,
+                            unsigned long *frame_cnt,
+                            unsigned long *byte_cnt)
+{
+       struct txq_info *txqi = to_txq_info(txq);
+
+       if (frame_cnt)
+               *frame_cnt = txqi->queue.qlen;
+
+       if (byte_cnt)
+               *byte_cnt = txqi->byte_cnt;
+}
+EXPORT_SYMBOL(ieee80211_txq_get_depth);
index c38b2f07a919e20dc22363fe80911f5f5a0b004f..341d192cea522c8f25c695083b84ea9bd5013987 100644 (file)
@@ -1,6 +1,9 @@
 /*
  * VHT handling
  *
+ * Portions of this file
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ *
  * 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.
@@ -278,6 +281,23 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
        }
 
        sta->sta.bandwidth = ieee80211_sta_cur_vht_bw(sta);
+
+       /* If HT IE reported 3839 bytes only, stay with that size. */
+       if (sta->sta.max_amsdu_len == IEEE80211_MAX_MPDU_LEN_HT_3839)
+               return;
+
+       switch (vht_cap->cap & IEEE80211_VHT_CAP_MAX_MPDU_MASK) {
+       case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454:
+               sta->sta.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_VHT_11454;
+               break;
+       case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991:
+               sta->sta.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_VHT_7991;
+               break;
+       case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895:
+       default:
+               sta->sta.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_VHT_3895;
+               break;
+       }
 }
 
 enum ieee80211_sta_rx_bandwidth ieee80211_sta_cap_rx_bw(struct sta_info *sta)
@@ -425,6 +445,30 @@ u32 __ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
        return changed;
 }
 
+void ieee80211_process_mu_groups(struct ieee80211_sub_if_data *sdata,
+                                struct ieee80211_mgmt *mgmt)
+{
+       struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
+
+       if (!(sdata->flags & IEEE80211_SDATA_MU_MIMO_OWNER))
+               return;
+
+       if (!memcmp(mgmt->u.action.u.vht_group_notif.position,
+                   bss_conf->mu_group.position, WLAN_USER_POSITION_LEN) &&
+           !memcmp(mgmt->u.action.u.vht_group_notif.membership,
+                   bss_conf->mu_group.membership, WLAN_MEMBERSHIP_LEN))
+               return;
+
+       memcpy(bss_conf->mu_group.membership,
+              mgmt->u.action.u.vht_group_notif.membership,
+              WLAN_MEMBERSHIP_LEN);
+       memcpy(bss_conf->mu_group.position,
+              mgmt->u.action.u.vht_group_notif.position,
+              WLAN_USER_POSITION_LEN);
+
+       ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_MU_GROUPS);
+}
+
 void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
                                 struct sta_info *sta, u8 opmode,
                                 enum ieee80211_band band)
index cf5b69ab18294bb2fa36a00f2a19dbf02d0f3045..48ebccf0fe727f70e9f09d77b053cf4e2d2324ca 100644 (file)
@@ -57,6 +57,8 @@ struct rfkill {
 
        bool                    registered;
        bool                    persistent;
+       bool                    polling_paused;
+       bool                    suspended;
 
        const struct rfkill_ops *ops;
        void                    *data;
@@ -233,29 +235,6 @@ static void rfkill_event(struct rfkill *rfkill)
        rfkill_send_events(rfkill, RFKILL_OP_CHANGE);
 }
 
-static bool __rfkill_set_hw_state(struct rfkill *rfkill,
-                                 bool blocked, bool *change)
-{
-       unsigned long flags;
-       bool prev, any;
-
-       BUG_ON(!rfkill);
-
-       spin_lock_irqsave(&rfkill->lock, flags);
-       prev = !!(rfkill->state & RFKILL_BLOCK_HW);
-       if (blocked)
-               rfkill->state |= RFKILL_BLOCK_HW;
-       else
-               rfkill->state &= ~RFKILL_BLOCK_HW;
-       *change = prev != blocked;
-       any = !!(rfkill->state & RFKILL_BLOCK_ANY);
-       spin_unlock_irqrestore(&rfkill->lock, flags);
-
-       rfkill_led_trigger_event(rfkill);
-
-       return any;
-}
-
 /**
  * rfkill_set_block - wrapper for set_block method
  *
@@ -285,7 +264,7 @@ static void rfkill_set_block(struct rfkill *rfkill, bool blocked)
        spin_lock_irqsave(&rfkill->lock, flags);
        prev = rfkill->state & RFKILL_BLOCK_SW;
 
-       if (rfkill->state & RFKILL_BLOCK_SW)
+       if (prev)
                rfkill->state |= RFKILL_BLOCK_SW_PREV;
        else
                rfkill->state &= ~RFKILL_BLOCK_SW_PREV;
@@ -332,8 +311,7 @@ static atomic_t rfkill_input_disabled = ATOMIC_INIT(0);
  * @blocked: the new state
  *
  * This function sets the state of all switches of given type,
- * unless a specific switch is claimed by userspace (in which case,
- * that switch is left alone) or suspended.
+ * unless a specific switch is suspended.
  *
  * Caller must have acquired rfkill_global_mutex.
  */
@@ -480,14 +458,26 @@ bool rfkill_get_global_sw_state(const enum rfkill_type type)
 
 bool rfkill_set_hw_state(struct rfkill *rfkill, bool blocked)
 {
-       bool ret, change;
+       unsigned long flags;
+       bool ret, prev;
 
-       ret = __rfkill_set_hw_state(rfkill, blocked, &change);
+       BUG_ON(!rfkill);
+
+       spin_lock_irqsave(&rfkill->lock, flags);
+       prev = !!(rfkill->state & RFKILL_BLOCK_HW);
+       if (blocked)
+               rfkill->state |= RFKILL_BLOCK_HW;
+       else
+               rfkill->state &= ~RFKILL_BLOCK_HW;
+       ret = !!(rfkill->state & RFKILL_BLOCK_ANY);
+       spin_unlock_irqrestore(&rfkill->lock, flags);
+
+       rfkill_led_trigger_event(rfkill);
 
        if (!rfkill->registered)
                return ret;
 
-       if (change)
+       if (prev != blocked)
                schedule_work(&rfkill->uevent_work);
 
        return ret;
@@ -730,20 +720,12 @@ static ssize_t state_store(struct device *dev, struct device_attribute *attr,
 }
 static DEVICE_ATTR_RW(state);
 
-static ssize_t claim_show(struct device *dev, struct device_attribute *attr,
-                         char *buf)
-{
-       return sprintf(buf, "%d\n", 0);
-}
-static DEVICE_ATTR_RO(claim);
-
 static struct attribute *rfkill_dev_attrs[] = {
        &dev_attr_name.attr,
        &dev_attr_type.attr,
        &dev_attr_index.attr,
        &dev_attr_persistent.attr,
        &dev_attr_state.attr,
-       &dev_attr_claim.attr,
        &dev_attr_soft.attr,
        &dev_attr_hard.attr,
        NULL,
@@ -786,6 +768,7 @@ void rfkill_pause_polling(struct rfkill *rfkill)
        if (!rfkill->ops->poll)
                return;
 
+       rfkill->polling_paused = true;
        cancel_delayed_work_sync(&rfkill->poll_work);
 }
 EXPORT_SYMBOL(rfkill_pause_polling);
@@ -797,6 +780,11 @@ void rfkill_resume_polling(struct rfkill *rfkill)
        if (!rfkill->ops->poll)
                return;
 
+       rfkill->polling_paused = false;
+
+       if (rfkill->suspended)
+               return;
+
        queue_delayed_work(system_power_efficient_wq,
                           &rfkill->poll_work, 0);
 }
@@ -807,7 +795,8 @@ static int rfkill_suspend(struct device *dev)
 {
        struct rfkill *rfkill = to_rfkill(dev);
 
-       rfkill_pause_polling(rfkill);
+       rfkill->suspended = true;
+       cancel_delayed_work_sync(&rfkill->poll_work);
 
        return 0;
 }
@@ -817,12 +806,16 @@ static int rfkill_resume(struct device *dev)
        struct rfkill *rfkill = to_rfkill(dev);
        bool cur;
 
+       rfkill->suspended = false;
+
        if (!rfkill->persistent) {
                cur = !!(rfkill->state & RFKILL_BLOCK_SW);
                rfkill_set_block(rfkill, cur);
        }
 
-       rfkill_resume_polling(rfkill);
+       if (rfkill->ops->poll && !rfkill->polling_paused)
+               queue_delayed_work(system_power_efficient_wq,
+                                  &rfkill->poll_work, 0);
 
        return 0;
 }
index da72ed32f14385c3e218cda196e5b4c3511c896e..6c606120abfed96fe7fe4cdb23beb204e5ea109a 100644 (file)
@@ -50,8 +50,8 @@ config CFG80211_DEVELOPER_WARNINGS
        default n
        help
          This option enables some additional warnings that help
-         cfg80211 developers and driver developers, but that can
-         trigger due to races with userspace.
+         cfg80211 developers and driver developers, but beware that
+         they can also trigger due to races with userspace.
 
          For example, when a driver reports that it was disconnected
          from the AP, but the user disconnects manually at the same
@@ -61,19 +61,6 @@ config CFG80211_DEVELOPER_WARNINGS
          on it (or mac80211).
 
 
-config CFG80211_REG_DEBUG
-       bool "cfg80211 regulatory debugging"
-       depends on CFG80211
-       default n
-       ---help---
-         You can enable this if you want to debug regulatory changes.
-         For more information on cfg80211 regulatory refer to the wireless
-         wiki:
-
-         http://wireless.kernel.org/en/developers/Regulatory
-
-         If unsure, say N.
-
 config CFG80211_CERTIFICATION_ONUS
        bool "cfg80211 certification onus"
        depends on CFG80211 && EXPERT
@@ -123,7 +110,7 @@ config CFG80211_REG_RELAX_NO_IR
         interface which associated to an AP which userspace assumes or confirms
         to be an authorized master, i.e., with radar detection support and DFS
         capabilities. However, note that in order to not create daisy chain
-        scenarios, this relaxation is not allowed in cases that the BSS client
+        scenarios, this relaxation is not allowed in cases where the BSS client
         is associated to P2P GO and in addition the P2P GO instantiated on
         a channel due to this relaxation should not allow connection from
         non P2P clients.
@@ -148,7 +135,7 @@ config CFG80211_DEBUGFS
        depends on CFG80211
        depends on DEBUG_FS
        ---help---
-         You can enable this if you want to debugfs entries for cfg80211.
+         You can enable this if you want debugfs entries for cfg80211.
 
          If unsure, say N.
 
@@ -159,7 +146,7 @@ config CFG80211_INTERNAL_REGDB
        ---help---
          This option generates an internal data structure representing
          the wireless regulatory rules described in net/wireless/db.txt
-         and includes code to query that database.  This is an alternative
+         and includes code to query that database. This is an alternative
          to using CRDA for defining regulatory rules for the kernel.
 
          Using this option requires some parsing of the db.txt at build time,
@@ -172,7 +159,7 @@ config CFG80211_INTERNAL_REGDB
 
          http://wireless.kernel.org/en/developers/Regulatory
 
-         Most distributions have a CRDA package.  So if unsure, say N.
+         Most distributions have a CRDA package. So if unsure, say N.
 
 config CFG80211_CRDA_SUPPORT
        bool "support CRDA" if CFG80211_INTERNAL_REGDB
index 8f0bac7e03c406466e0b5ca2b01b3ca2574ab79f..9f1c4aa851efdf55ab81044b10ef58b7bd73ecf6 100644 (file)
@@ -352,6 +352,16 @@ struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv,
        WARN_ON(ops->add_station && !ops->del_station);
        WARN_ON(ops->add_mpath && !ops->del_mpath);
        WARN_ON(ops->join_mesh && !ops->leave_mesh);
+       WARN_ON(ops->start_p2p_device && !ops->stop_p2p_device);
+       WARN_ON(ops->start_ap && !ops->stop_ap);
+       WARN_ON(ops->join_ocb && !ops->leave_ocb);
+       WARN_ON(ops->suspend && !ops->resume);
+       WARN_ON(ops->sched_scan_start && !ops->sched_scan_stop);
+       WARN_ON(ops->remain_on_channel && !ops->cancel_remain_on_channel);
+       WARN_ON(ops->tdls_channel_switch && !ops->tdls_cancel_channel_switch);
+       WARN_ON(ops->add_tx_ts && !ops->del_tx_ts);
+       WARN_ON(ops->set_tx_power && !ops->get_tx_power);
+       WARN_ON(ops->set_antenna && !ops->get_antenna);
 
        alloc_size = sizeof(*rdev) + sizeof_priv;
 
index fb44fa3bf4efa750298163a15534572c43229b2e..ff328250bc442db6a9e8e5136496098d4270bf6f 100644 (file)
@@ -711,7 +711,7 @@ EXPORT_SYMBOL(cfg80211_rx_mgmt);
 
 void cfg80211_dfs_channels_update_work(struct work_struct *work)
 {
-       struct delayed_work *delayed_work;
+       struct delayed_work *delayed_work = to_delayed_work(work);
        struct cfg80211_registered_device *rdev;
        struct cfg80211_chan_def chandef;
        struct ieee80211_supported_band *sband;
@@ -721,7 +721,6 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work)
        unsigned long timeout, next_time = 0;
        int bandid, i;
 
-       delayed_work = container_of(work, struct delayed_work, work);
        rdev = container_of(delayed_work, struct cfg80211_registered_device,
                            dfs_update_channels_wk);
        wiphy = &rdev->wiphy;
index d4786f2802aa3c94f0a9bfcba846c98ec5b2b249..268cb493f6a5496bf596d517b6bc62c348e6e9d9 100644 (file)
@@ -401,6 +401,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
        [NL80211_ATTR_NETNS_FD] = { .type = NLA_U32 },
        [NL80211_ATTR_SCHED_SCAN_DELAY] = { .type = NLA_U32 },
        [NL80211_ATTR_REG_INDOOR] = { .type = NLA_FLAG },
+       [NL80211_ATTR_PBSS] = { .type = NLA_FLAG },
 };
 
 /* policy for the key attributes */
@@ -3461,6 +3462,10 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
                        return PTR_ERR(params.acl);
        }
 
+       params.pbss = nla_get_flag(info->attrs[NL80211_ATTR_PBSS]);
+       if (params.pbss && !rdev->wiphy.bands[IEEE80211_BAND_60GHZ])
+               return -EOPNOTSUPP;
+
        wdev_lock(wdev);
        err = rdev_start_ap(rdev, dev, &params);
        if (!err) {
@@ -7980,6 +7985,12 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
                connect.flags |= ASSOC_REQ_USE_RRM;
        }
 
+       connect.pbss = nla_get_flag(info->attrs[NL80211_ATTR_PBSS]);
+       if (connect.pbss && !rdev->wiphy.bands[IEEE80211_BAND_60GHZ]) {
+               kzfree(connkeys);
+               return -EOPNOTSUPP;
+       }
+
        wdev_lock(dev->ieee80211_ptr);
        err = cfg80211_connect(rdev, dev, &connect, connkeys, NULL);
        wdev_unlock(dev->ieee80211_ptr);
index 547ceecc052310f3db3be04d827dff22d567dc1e..c5fb317eee68f15a4665a090f7aee16b08ec6765 100644 (file)
 #include "regdb.h"
 #include "nl80211.h"
 
-#ifdef CONFIG_CFG80211_REG_DEBUG
-#define REG_DBG_PRINT(format, args...)                 \
-       printk(KERN_DEBUG pr_fmt(format), ##args)
-#else
-#define REG_DBG_PRINT(args...)
-#endif
-
 /*
  * Grace period we give before making sure all current interfaces reside on
  * channels allowed by the current regulatory domain.
@@ -178,12 +171,10 @@ enum nl80211_dfs_regions reg_get_dfs_region(struct wiphy *wiphy)
        if (wiphy_regd->dfs_region == regd->dfs_region)
                goto out;
 
-       REG_DBG_PRINT("%s: device specific dfs_region "
-                     "(%s) disagrees with cfg80211's "
-                     "central dfs_region (%s)\n",
-                     dev_name(&wiphy->dev),
-                     reg_dfs_region_str(wiphy_regd->dfs_region),
-                     reg_dfs_region_str(regd->dfs_region));
+       pr_debug("%s: device specific dfs_region (%s) disagrees with cfg80211's central dfs_region (%s)\n",
+                dev_name(&wiphy->dev),
+                reg_dfs_region_str(wiphy_regd->dfs_region),
+                reg_dfs_region_str(regd->dfs_region));
 
 out:
        return regd->dfs_region;
@@ -543,7 +534,7 @@ static DECLARE_DELAYED_WORK(crda_timeout, crda_timeout_work);
 
 static void crda_timeout_work(struct work_struct *work)
 {
-       REG_DBG_PRINT("Timeout while waiting for CRDA to reply, restoring regulatory settings\n");
+       pr_debug("Timeout while waiting for CRDA to reply, restoring regulatory settings\n");
        rtnl_lock();
        reg_crda_timeouts++;
        restore_regulatory_settings(true);
@@ -585,7 +576,7 @@ static int call_crda(const char *alpha2)
 
        if (!is_world_regdom((char *) alpha2))
                pr_debug("Calling CRDA for country: %c%c\n",
-                       alpha2[0], alpha2[1]);
+                        alpha2[0], alpha2[1]);
        else
                pr_debug("Calling CRDA to update world regulatory domain\n");
 
@@ -1132,42 +1123,6 @@ const char *reg_initiator_name(enum nl80211_reg_initiator initiator)
 }
 EXPORT_SYMBOL(reg_initiator_name);
 
-static void chan_reg_rule_print_dbg(const struct ieee80211_regdomain *regd,
-                                   struct ieee80211_channel *chan,
-                                   const struct ieee80211_reg_rule *reg_rule)
-{
-#ifdef CONFIG_CFG80211_REG_DEBUG
-       const struct ieee80211_power_rule *power_rule;
-       const struct ieee80211_freq_range *freq_range;
-       char max_antenna_gain[32], bw[32];
-
-       power_rule = &reg_rule->power_rule;
-       freq_range = &reg_rule->freq_range;
-
-       if (!power_rule->max_antenna_gain)
-               snprintf(max_antenna_gain, sizeof(max_antenna_gain), "N/A");
-       else
-               snprintf(max_antenna_gain, sizeof(max_antenna_gain), "%d mBi",
-                        power_rule->max_antenna_gain);
-
-       if (reg_rule->flags & NL80211_RRF_AUTO_BW)
-               snprintf(bw, sizeof(bw), "%d KHz, %d KHz AUTO",
-                        freq_range->max_bandwidth_khz,
-                        reg_get_max_bandwidth(regd, reg_rule));
-       else
-               snprintf(bw, sizeof(bw), "%d KHz",
-                        freq_range->max_bandwidth_khz);
-
-       REG_DBG_PRINT("Updating information on frequency %d MHz with regulatory rule:\n",
-                     chan->center_freq);
-
-       REG_DBG_PRINT("(%d KHz - %d KHz @ %s), (%s, %d mBm)\n",
-                     freq_range->start_freq_khz, freq_range->end_freq_khz,
-                     bw, max_antenna_gain,
-                     power_rule->max_eirp);
-#endif
-}
-
 static uint32_t reg_rule_to_chan_bw_flags(const struct ieee80211_regdomain *regd,
                                          const struct ieee80211_reg_rule *reg_rule,
                                          const struct ieee80211_channel *chan)
@@ -1242,20 +1197,19 @@ static void handle_channel(struct wiphy *wiphy,
                if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER &&
                    request_wiphy && request_wiphy == wiphy &&
                    request_wiphy->regulatory_flags & REGULATORY_STRICT_REG) {
-                       REG_DBG_PRINT("Disabling freq %d MHz for good\n",
-                                     chan->center_freq);
+                       pr_debug("Disabling freq %d MHz for good\n",
+                                chan->center_freq);
                        chan->orig_flags |= IEEE80211_CHAN_DISABLED;
                        chan->flags = chan->orig_flags;
                } else {
-                       REG_DBG_PRINT("Disabling freq %d MHz\n",
-                                     chan->center_freq);
+                       pr_debug("Disabling freq %d MHz\n",
+                                chan->center_freq);
                        chan->flags |= IEEE80211_CHAN_DISABLED;
                }
                return;
        }
 
        regd = reg_get_regdomain(wiphy);
-       chan_reg_rule_print_dbg(regd, chan, reg_rule);
 
        power_rule = &reg_rule->power_rule;
        bw_flags = reg_rule_to_chan_bw_flags(regd, reg_rule, chan);
@@ -1393,18 +1347,15 @@ static bool ignore_reg_update(struct wiphy *wiphy,
                return true;
 
        if (!lr) {
-               REG_DBG_PRINT("Ignoring regulatory request set by %s "
-                             "since last_request is not set\n",
-                             reg_initiator_name(initiator));
+               pr_debug("Ignoring regulatory request set by %s since last_request is not set\n",
+                        reg_initiator_name(initiator));
                return true;
        }
 
        if (initiator == NL80211_REGDOM_SET_BY_CORE &&
            wiphy->regulatory_flags & REGULATORY_CUSTOM_REG) {
-               REG_DBG_PRINT("Ignoring regulatory request set by %s "
-                             "since the driver uses its own custom "
-                             "regulatory domain\n",
-                             reg_initiator_name(initiator));
+               pr_debug("Ignoring regulatory request set by %s since the driver uses its own custom regulatory domain\n",
+                        reg_initiator_name(initiator));
                return true;
        }
 
@@ -1415,10 +1366,8 @@ static bool ignore_reg_update(struct wiphy *wiphy,
        if (wiphy_strict_alpha2_regd(wiphy) && !wiphy->regd &&
            initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE &&
            !is_world_regdom(lr->alpha2)) {
-               REG_DBG_PRINT("Ignoring regulatory request set by %s "
-                             "since the driver requires its own regulatory "
-                             "domain to be set first\n",
-                             reg_initiator_name(initiator));
+               pr_debug("Ignoring regulatory request set by %s since the driver requires its own regulatory domain to be set first\n",
+                        reg_initiator_name(initiator));
                return true;
        }
 
@@ -1699,7 +1648,7 @@ static void reg_check_chans_work(struct work_struct *work)
 {
        struct cfg80211_registered_device *rdev;
 
-       REG_DBG_PRINT("Verifying active interfaces after reg change\n");
+       pr_debug("Verifying active interfaces after reg change\n");
        rtnl_lock();
 
        list_for_each_entry(rdev, &cfg80211_rdev_list, list)
@@ -1781,8 +1730,8 @@ static void handle_channel_custom(struct wiphy *wiphy,
        }
 
        if (IS_ERR(reg_rule)) {
-               REG_DBG_PRINT("Disabling freq %d MHz as custom regd has no rule that fits it\n",
-                             chan->center_freq);
+               pr_debug("Disabling freq %d MHz as custom regd has no rule that fits it\n",
+                        chan->center_freq);
                if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) {
                        chan->flags |= IEEE80211_CHAN_DISABLED;
                } else {
@@ -1792,8 +1741,6 @@ static void handle_channel_custom(struct wiphy *wiphy,
                return;
        }
 
-       chan_reg_rule_print_dbg(regd, chan, reg_rule);
-
        power_rule = &reg_rule->power_rule;
        bw_flags = reg_rule_to_chan_bw_flags(regd, reg_rule, chan);
 
@@ -2524,7 +2471,7 @@ static void restore_alpha2(char *alpha2, bool reset_user)
        if (is_user_regdom_saved()) {
                /* Unless we're asked to ignore it and reset it */
                if (reset_user) {
-                       REG_DBG_PRINT("Restoring regulatory settings including user preference\n");
+                       pr_debug("Restoring regulatory settings including user preference\n");
                        user_alpha2[0] = '9';
                        user_alpha2[1] = '7';
 
@@ -2534,24 +2481,24 @@ static void restore_alpha2(char *alpha2, bool reset_user)
                         * back as they were for a full restore.
                         */
                        if (!is_world_regdom(ieee80211_regdom)) {
-                               REG_DBG_PRINT("Keeping preference on module parameter ieee80211_regdom: %c%c\n",
-                                             ieee80211_regdom[0], ieee80211_regdom[1]);
+                               pr_debug("Keeping preference on module parameter ieee80211_regdom: %c%c\n",
+                                        ieee80211_regdom[0], ieee80211_regdom[1]);
                                alpha2[0] = ieee80211_regdom[0];
                                alpha2[1] = ieee80211_regdom[1];
                        }
                } else {
-                       REG_DBG_PRINT("Restoring regulatory settings while preserving user preference for: %c%c\n",
-                                     user_alpha2[0], user_alpha2[1]);
+                       pr_debug("Restoring regulatory settings while preserving user preference for: %c%c\n",
+                                user_alpha2[0], user_alpha2[1]);
                        alpha2[0] = user_alpha2[0];
                        alpha2[1] = user_alpha2[1];
                }
        } else if (!is_world_regdom(ieee80211_regdom)) {
-               REG_DBG_PRINT("Keeping preference on module parameter ieee80211_regdom: %c%c\n",
-                             ieee80211_regdom[0], ieee80211_regdom[1]);
+               pr_debug("Keeping preference on module parameter ieee80211_regdom: %c%c\n",
+                        ieee80211_regdom[0], ieee80211_regdom[1]);
                alpha2[0] = ieee80211_regdom[0];
                alpha2[1] = ieee80211_regdom[1];
        } else
-               REG_DBG_PRINT("Restoring regulatory settings\n");
+               pr_debug("Restoring regulatory settings\n");
 }
 
 static void restore_custom_reg_settings(struct wiphy *wiphy)
@@ -2663,14 +2610,14 @@ static void restore_regulatory_settings(bool reset_user)
        list_splice_tail_init(&tmp_reg_req_list, &reg_requests_list);
        spin_unlock(&reg_requests_lock);
 
-       REG_DBG_PRINT("Kicking the queue\n");
+       pr_debug("Kicking the queue\n");
 
        schedule_work(&reg_work);
 }
 
 void regulatory_hint_disconnect(void)
 {
-       REG_DBG_PRINT("All devices are disconnected, going to restore regulatory settings\n");
+       pr_debug("All devices are disconnected, going to restore regulatory settings\n");
        restore_regulatory_settings(false);
 }
 
@@ -2718,10 +2665,10 @@ int regulatory_hint_found_beacon(struct wiphy *wiphy,
        if (!reg_beacon)
                return -ENOMEM;
 
-       REG_DBG_PRINT("Found new beacon on frequency: %d MHz (Ch %d) on %s\n",
-                     beacon_chan->center_freq,
-                     ieee80211_frequency_to_channel(beacon_chan->center_freq),
-                     wiphy_name(wiphy));
+       pr_debug("Found new beacon on frequency: %d MHz (Ch %d) on %s\n",
+                beacon_chan->center_freq,
+                ieee80211_frequency_to_channel(beacon_chan->center_freq),
+                wiphy_name(wiphy));
 
        memcpy(&reg_beacon->chan, beacon_chan,
               sizeof(struct ieee80211_channel));
@@ -2800,8 +2747,7 @@ bool reg_supported_dfs_region(enum nl80211_dfs_regions dfs_region)
        case NL80211_DFS_JP:
                return true;
        default:
-               REG_DBG_PRINT("Ignoring uknown DFS master region: %d\n",
-                             dfs_region);
+               pr_debug("Ignoring uknown DFS master region: %d\n", dfs_region);
                return false;
        }
 }
index 8020b5b094d4c8fba0c0f2431f8af7d8ecc40dca..79bd3a171caa83ed8ae811b744fd271475b0257a 100644 (file)
@@ -264,7 +264,7 @@ static struct cfg80211_bss *cfg80211_get_conn_bss(struct wireless_dev *wdev)
                               wdev->conn->params.bssid,
                               wdev->conn->params.ssid,
                               wdev->conn->params.ssid_len,
-                              IEEE80211_BSS_TYPE_ESS,
+                              wdev->conn_bss_type,
                               IEEE80211_PRIVACY(wdev->conn->params.privacy));
        if (!bss)
                return NULL;
@@ -687,7 +687,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
                WARN_ON_ONCE(!wiphy_to_rdev(wdev->wiphy)->ops->connect);
                bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid,
                                       wdev->ssid, wdev->ssid_len,
-                                      IEEE80211_BSS_TYPE_ESS,
+                                      wdev->conn_bss_type,
                                       IEEE80211_PRIVACY_ANY);
                if (bss)
                        cfg80211_hold_bss(bss_from_pub(bss));
@@ -846,7 +846,7 @@ void cfg80211_roamed(struct net_device *dev,
 
        bss = cfg80211_get_bss(wdev->wiphy, channel, bssid, wdev->ssid,
                               wdev->ssid_len,
-                              IEEE80211_BSS_TYPE_ESS, IEEE80211_PRIVACY_ANY);
+                              wdev->conn_bss_type, IEEE80211_PRIVACY_ANY);
        if (WARN_ON(!bss))
                return;
 
@@ -1017,6 +1017,9 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev,
        memcpy(wdev->ssid, connect->ssid, connect->ssid_len);
        wdev->ssid_len = connect->ssid_len;
 
+       wdev->conn_bss_type = connect->pbss ? IEEE80211_BSS_TYPE_PBSS :
+                                             IEEE80211_BSS_TYPE_ESS;
+
        if (!rdev->ops->connect)
                err = cfg80211_sme_connect(wdev, connect, prev_bssid);
        else
index 92770427b211f559be4735584d74043ec6301802..6e4eb35551776d497a85d91c1f0fab2ecf25d72b 100644 (file)
@@ -393,9 +393,9 @@ unsigned int ieee80211_get_hdrlen_from_skb(const struct sk_buff *skb)
 }
 EXPORT_SYMBOL(ieee80211_get_hdrlen_from_skb);
 
-unsigned int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr)
+static unsigned int __ieee80211_get_mesh_hdrlen(u8 flags)
 {
-       int ae = meshhdr->flags & MESH_FLAGS_AE;
+       int ae = flags & MESH_FLAGS_AE;
        /* 802.11-2012, 8.2.4.7.3 */
        switch (ae) {
        default:
@@ -407,21 +407,31 @@ unsigned int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr)
                return 18;
        }
 }
+
+unsigned int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr)
+{
+       return __ieee80211_get_mesh_hdrlen(meshhdr->flags);
+}
 EXPORT_SYMBOL(ieee80211_get_mesh_hdrlen);
 
-int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr,
-                          enum nl80211_iftype iftype)
+static int __ieee80211_data_to_8023(struct sk_buff *skb, struct ethhdr *ehdr,
+                                   const u8 *addr, enum nl80211_iftype iftype)
 {
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
-       u16 hdrlen, ethertype;
-       u8 *payload;
-       u8 dst[ETH_ALEN];
-       u8 src[ETH_ALEN] __aligned(2);
+       struct {
+               u8 hdr[ETH_ALEN] __aligned(2);
+               __be16 proto;
+       } payload;
+       struct ethhdr tmp;
+       u16 hdrlen;
+       u8 mesh_flags = 0;
 
        if (unlikely(!ieee80211_is_data_present(hdr->frame_control)))
                return -1;
 
        hdrlen = ieee80211_hdrlen(hdr->frame_control);
+       if (skb->len < hdrlen + 8)
+               return -1;
 
        /* convert IEEE 802.11 header + possible LLC headers into Ethernet
         * header
@@ -432,8 +442,11 @@ int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr,
         *   1     0   BSSID SA    DA    n/a
         *   1     1   RA    TA    DA    SA
         */
-       memcpy(dst, ieee80211_get_DA(hdr), ETH_ALEN);
-       memcpy(src, ieee80211_get_SA(hdr), ETH_ALEN);
+       memcpy(tmp.h_dest, ieee80211_get_DA(hdr), ETH_ALEN);
+       memcpy(tmp.h_source, ieee80211_get_SA(hdr), ETH_ALEN);
+
+       if (iftype == NL80211_IFTYPE_MESH_POINT)
+               skb_copy_bits(skb, hdrlen, &mesh_flags, 1);
 
        switch (hdr->frame_control &
                cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) {
@@ -450,44 +463,31 @@ int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr,
                             iftype != NL80211_IFTYPE_STATION))
                        return -1;
                if (iftype == NL80211_IFTYPE_MESH_POINT) {
-                       struct ieee80211s_hdr *meshdr =
-                               (struct ieee80211s_hdr *) (skb->data + hdrlen);
-                       /* make sure meshdr->flags is on the linear part */
-                       if (!pskb_may_pull(skb, hdrlen + 1))
-                               return -1;
-                       if (meshdr->flags & MESH_FLAGS_AE_A4)
+                       if (mesh_flags & MESH_FLAGS_AE_A4)
                                return -1;
-                       if (meshdr->flags & MESH_FLAGS_AE_A5_A6) {
+                       if (mesh_flags & MESH_FLAGS_AE_A5_A6) {
                                skb_copy_bits(skb, hdrlen +
                                        offsetof(struct ieee80211s_hdr, eaddr1),
-                                       dst, ETH_ALEN);
-                               skb_copy_bits(skb, hdrlen +
-                                       offsetof(struct ieee80211s_hdr, eaddr2),
-                                       src, ETH_ALEN);
+                                       tmp.h_dest, 2 * ETH_ALEN);
                        }
-                       hdrlen += ieee80211_get_mesh_hdrlen(meshdr);
+                       hdrlen += __ieee80211_get_mesh_hdrlen(mesh_flags);
                }
                break;
        case cpu_to_le16(IEEE80211_FCTL_FROMDS):
                if ((iftype != NL80211_IFTYPE_STATION &&
                     iftype != NL80211_IFTYPE_P2P_CLIENT &&
                     iftype != NL80211_IFTYPE_MESH_POINT) ||
-                   (is_multicast_ether_addr(dst) &&
-                    ether_addr_equal(src, addr)))
+                   (is_multicast_ether_addr(tmp.h_dest) &&
+                    ether_addr_equal(tmp.h_source, addr)))
                        return -1;
                if (iftype == NL80211_IFTYPE_MESH_POINT) {
-                       struct ieee80211s_hdr *meshdr =
-                               (struct ieee80211s_hdr *) (skb->data + hdrlen);
-                       /* make sure meshdr->flags is on the linear part */
-                       if (!pskb_may_pull(skb, hdrlen + 1))
-                               return -1;
-                       if (meshdr->flags & MESH_FLAGS_AE_A5_A6)
+                       if (mesh_flags & MESH_FLAGS_AE_A5_A6)
                                return -1;
-                       if (meshdr->flags & MESH_FLAGS_AE_A4)
+                       if (mesh_flags & MESH_FLAGS_AE_A4)
                                skb_copy_bits(skb, hdrlen +
                                        offsetof(struct ieee80211s_hdr, eaddr1),
-                                       src, ETH_ALEN);
-                       hdrlen += ieee80211_get_mesh_hdrlen(meshdr);
+                                       tmp.h_source, ETH_ALEN);
+                       hdrlen += __ieee80211_get_mesh_hdrlen(mesh_flags);
                }
                break;
        case cpu_to_le16(0):
@@ -498,33 +498,33 @@ int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr,
                break;
        }
 
-       if (!pskb_may_pull(skb, hdrlen + 8))
-               return -1;
-
-       payload = skb->data + hdrlen;
-       ethertype = (payload[6] << 8) | payload[7];
+       skb_copy_bits(skb, hdrlen, &payload, sizeof(payload));
+       tmp.h_proto = payload.proto;
 
-       if (likely((ether_addr_equal(payload, rfc1042_header) &&
-                   ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) ||
-                  ether_addr_equal(payload, bridge_tunnel_header))) {
+       if (likely((ether_addr_equal(payload.hdr, rfc1042_header) &&
+                   tmp.h_proto != htons(ETH_P_AARP) &&
+                   tmp.h_proto != htons(ETH_P_IPX)) ||
+                  ether_addr_equal(payload.hdr, bridge_tunnel_header)))
                /* remove RFC1042 or Bridge-Tunnel encapsulation and
                 * replace EtherType */
-               skb_pull(skb, hdrlen + 6);
-               memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN);
-               memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN);
-       } else {
-               struct ethhdr *ehdr;
-               __be16 len;
+               hdrlen += ETH_ALEN + 2;
+       else
+               tmp.h_proto = htons(skb->len);
 
-               skb_pull(skb, hdrlen);
-               len = htons(skb->len);
+       pskb_pull(skb, hdrlen);
+
+       if (!ehdr)
                ehdr = (struct ethhdr *) skb_push(skb, sizeof(struct ethhdr));
-               memcpy(ehdr->h_dest, dst, ETH_ALEN);
-               memcpy(ehdr->h_source, src, ETH_ALEN);
-               ehdr->h_proto = len;
-       }
+       memcpy(ehdr, &tmp, sizeof(tmp));
+
        return 0;
 }
+
+int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr,
+                          enum nl80211_iftype iftype)
+{
+       return __ieee80211_data_to_8023(skb, NULL, addr, iftype);
+}
 EXPORT_SYMBOL(ieee80211_data_to_8023);
 
 int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr,
@@ -644,70 +644,75 @@ int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr,
 }
 EXPORT_SYMBOL(ieee80211_data_from_8023);
 
+static struct sk_buff *
+__ieee80211_amsdu_copy(struct sk_buff *skb, unsigned int hlen,
+                      int offset, int len)
+{
+       struct sk_buff *frame;
+
+       if (skb->len - offset < len)
+               return NULL;
+
+       /*
+        * Allocate and reserve two bytes more for payload
+        * alignment since sizeof(struct ethhdr) is 14.
+        */
+       frame = dev_alloc_skb(hlen + sizeof(struct ethhdr) + 2 + len);
+
+       skb_reserve(frame, hlen + sizeof(struct ethhdr) + 2);
+       skb_copy_bits(skb, offset, skb_put(frame, len), len);
+
+       return frame;
+}
 
 void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list,
                              const u8 *addr, enum nl80211_iftype iftype,
                              const unsigned int extra_headroom,
                              bool has_80211_header)
 {
+       unsigned int hlen = ALIGN(extra_headroom, 4);
        struct sk_buff *frame = NULL;
        u16 ethertype;
        u8 *payload;
-       const struct ethhdr *eth;
-       int remaining, err;
-       u8 dst[ETH_ALEN], src[ETH_ALEN];
+       int offset = 0, remaining, err;
+       struct ethhdr eth;
+       bool reuse_skb = true;
+       bool last = false;
 
        if (has_80211_header) {
-               err = ieee80211_data_to_8023(skb, addr, iftype);
+               err = __ieee80211_data_to_8023(skb, &eth, addr, iftype);
                if (err)
                        goto out;
-
-               /* skip the wrapping header */
-               eth = (struct ethhdr *) skb_pull(skb, sizeof(struct ethhdr));
-               if (!eth)
-                       goto out;
-       } else {
-               eth = (struct ethhdr *) skb->data;
        }
 
-       while (skb != frame) {
+       while (!last) {
+               unsigned int subframe_len;
+               int len;
                u8 padding;
-               __be16 len = eth->h_proto;
-               unsigned int subframe_len = sizeof(struct ethhdr) + ntohs(len);
-
-               remaining = skb->len;
-               memcpy(dst, eth->h_dest, ETH_ALEN);
-               memcpy(src, eth->h_source, ETH_ALEN);
 
+               skb_copy_bits(skb, offset, &eth, sizeof(eth));
+               len = ntohs(eth.h_proto);
+               subframe_len = sizeof(struct ethhdr) + len;
                padding = (4 - subframe_len) & 0x3;
+
                /* the last MSDU has no padding */
+               remaining = skb->len - offset;
                if (subframe_len > remaining)
                        goto purge;
 
-               skb_pull(skb, sizeof(struct ethhdr));
+               offset += sizeof(struct ethhdr);
                /* reuse skb for the last subframe */
-               if (remaining <= subframe_len + padding)
+               last = remaining <= subframe_len + padding;
+               if (!skb_is_nonlinear(skb) && last) {
+                       skb_pull(skb, offset);
                        frame = skb;
-               else {
-                       unsigned int hlen = ALIGN(extra_headroom, 4);
-                       /*
-                        * Allocate and reserve two bytes more for payload
-                        * alignment since sizeof(struct ethhdr) is 14.
-                        */
-                       frame = dev_alloc_skb(hlen + subframe_len + 2);
+                       reuse_skb = true;
+               } else {
+                       frame = __ieee80211_amsdu_copy(skb, hlen, offset, len);
                        if (!frame)
                                goto purge;
 
-                       skb_reserve(frame, hlen + sizeof(struct ethhdr) + 2);
-                       memcpy(skb_put(frame, ntohs(len)), skb->data,
-                               ntohs(len));
-
-                       eth = (struct ethhdr *)skb_pull(skb, ntohs(len) +
-                                                       padding);
-                       if (!eth) {
-                               dev_kfree_skb(frame);
-                               goto purge;
-                       }
+                       offset += len + padding;
                }
 
                skb_reset_network_header(frame);
@@ -716,24 +721,20 @@ void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list,
 
                payload = frame->data;
                ethertype = (payload[6] << 8) | payload[7];
-
                if (likely((ether_addr_equal(payload, rfc1042_header) &&
                            ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) ||
                           ether_addr_equal(payload, bridge_tunnel_header))) {
-                       /* remove RFC1042 or Bridge-Tunnel
-                        * encapsulation and replace EtherType */
-                       skb_pull(frame, 6);
-                       memcpy(skb_push(frame, ETH_ALEN), src, ETH_ALEN);
-                       memcpy(skb_push(frame, ETH_ALEN), dst, ETH_ALEN);
-               } else {
-                       memcpy(skb_push(frame, sizeof(__be16)), &len,
-                               sizeof(__be16));
-                       memcpy(skb_push(frame, ETH_ALEN), src, ETH_ALEN);
-                       memcpy(skb_push(frame, ETH_ALEN), dst, ETH_ALEN);
+                       eth.h_proto = htons(ethertype);
+                       skb_pull(frame, ETH_ALEN + 2);
                }
+
+               memcpy(skb_push(frame, sizeof(eth)), &eth, sizeof(eth));
                __skb_queue_tail(list, frame);
        }
 
+       if (!reuse_skb)
+               dev_kfree_skb(skb);
+
        return;
 
  purge: