]> git.karo-electronics.de Git - linux-beck.git/commitdiff
Merge tag 'mac80211-next-for-davem-2015-08-14' mac80211-next.git
authorKalle Valo <kvalo@codeaurora.org>
Tue, 18 Aug 2015 05:44:22 +0000 (08:44 +0300)
committerKalle Valo <kvalo@codeaurora.org>
Tue, 18 Aug 2015 05:44:22 +0000 (08:44 +0300)
iwlwifi needs new mac80211 patches so merge mac80211-next.git to
wireless-drivers-next.git.

54 files changed:
drivers/net/wireless/iwlwifi/dvm/dev.h
drivers/net/wireless/iwlwifi/dvm/main.c
drivers/net/wireless/iwlwifi/dvm/rx.c
drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h
drivers/net/wireless/iwlwifi/mvm/mvm.h
drivers/net/wireless/iwlwifi/mvm/ops.c
drivers/net/wireless/iwlwifi/mvm/rx.c
drivers/net/wireless/iwlwifi/mvm/sta.c
drivers/net/wireless/mac80211_hwsim.c
include/linux/average.h
include/linux/ieee80211.h
include/net/cfg80211.h
include/net/mac80211.h
net/mac80211/Kconfig
net/mac80211/Makefile
net/mac80211/aes_cmac.c
net/mac80211/cfg.c
net/mac80211/chan.c
net/mac80211/debugfs.c
net/mac80211/debugfs_key.c
net/mac80211/debugfs_netdev.c
net/mac80211/driver-ops.c [new file with mode: 0644]
net/mac80211/driver-ops.h
net/mac80211/ieee80211_i.h
net/mac80211/iface.c
net/mac80211/key.c
net/mac80211/key.h
net/mac80211/main.c
net/mac80211/mesh.c
net/mac80211/mesh_hwmp.c
net/mac80211/mesh_plink.c
net/mac80211/mesh_ps.c
net/mac80211/mesh_sync.c
net/mac80211/mlme.c
net/mac80211/ocb.c
net/mac80211/rate.c
net/mac80211/rate.h
net/mac80211/rc80211_minstrel_ht.c
net/mac80211/rx.c
net/mac80211/sta_info.c
net/mac80211/sta_info.h
net/mac80211/status.c
net/mac80211/tdls.c
net/mac80211/tx.c
net/mac80211/util.c
net/mac80211/vht.c
net/mac80211/wpa.c
net/rfkill/Kconfig
net/wireless/core.c
net/wireless/core.h
net/wireless/mlme.c
net/wireless/nl80211.c
net/wireless/rdev-ops.h
net/wireless/reg.c

index 1f51757f4a93f43ae0d9db9da1daaace43b0a933..0ba3e56d6015b16feb34acc71c36a81d42453eb6 100644 (file)
@@ -669,6 +669,8 @@ struct iwl_priv {
        /* ieee device used by generic ieee processing code */
        struct ieee80211_hw *hw;
 
+       struct napi_struct *napi;
+
        struct list_head calib_results;
 
        struct workqueue_struct *workqueue;
index 234e30f498b2dde18f3f355d5cfdcf13db6dc185..644819563cf0e85272b3780243d160a26ced3e82 100644 (file)
@@ -2037,7 +2037,8 @@ static void iwl_napi_add(struct iwl_op_mode *op_mode,
 {
        struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
 
-       ieee80211_napi_add(priv->hw, napi, napi_dev, poll, weight);
+       netif_napi_add(napi_dev, napi, poll, weight);
+       priv->napi = napi;
 }
 
 static const struct iwl_op_mode_ops iwl_dvm_ops = {
index c91374fe9a5859e251f2252aa2f9965afb1cf4a7..15176981dc8f62c8754a6f939989fdc1863f0368 100644 (file)
@@ -763,7 +763,7 @@ static void iwlagn_pass_packet_to_mac80211(struct iwl_priv *priv,
 
        memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats));
 
-       ieee80211_rx(priv->hw, skb);
+       ieee80211_rx_napi(priv->hw, skb, priv->napi);
 }
 
 static u32 iwlagn_translate_rx_status(struct iwl_priv *priv, u32 decrypt_in)
index 21dd5b771660f0f689798b650fbb4acd1eed3d21..493a8bdfbc9e4bb89180bc75bdd462d88b67255a 100644 (file)
@@ -366,8 +366,8 @@ struct iwl_mvm_rm_sta_cmd {
  * ( MGMT_MCAST_KEY = 0x1f )
  * @ctrl_flags: %iwl_sta_key_flag
  * @IGTK:
- * @K1: IGTK master key
- * @K2: IGTK sub key
+ * @K1: unused
+ * @K2: unused
  * @sta_id: station ID that support IGTK
  * @key_id:
  * @receive_seq_cnt: initial RSC/PN needed for replay check
index 95f326dc0b1ff4b373a8362899403a0c3d875e66..6b0956a9129a5aec7966770770db693ec453780c 100644 (file)
@@ -563,6 +563,7 @@ struct iwl_mvm {
        const struct iwl_cfg *cfg;
        struct iwl_phy_db *phy_db;
        struct ieee80211_hw *hw;
+       struct napi_struct *napi;
 
        /* for protecting access to iwl_mvm */
        struct mutex mutex;
index 6957d026e4bd1cd442637001551b44dc28d24101..c8327f1a077a7518eddbe859bacc94d0a0bfd281 100644 (file)
@@ -1326,7 +1326,8 @@ static void iwl_mvm_napi_add(struct iwl_op_mode *op_mode,
 {
        struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
 
-       ieee80211_napi_add(mvm->hw, napi, napi_dev, poll, weight);
+       netif_napi_add(napi_dev, napi, poll, weight);
+       mvm->napi = napi;
 }
 
 static const struct iwl_op_mode_ops iwl_mvm_ops = {
index 65746145273e17dd49323fa97edc8f1be7b61e94..a0c27cc19759015c1148c97880755f9f0e4c0d89 100644 (file)
@@ -127,7 +127,7 @@ static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm,
                                fraglen, rxb->truesize);
        }
 
-       ieee80211_rx(mvm->hw, skb);
+       ieee80211_rx_napi(mvm->hw, skb, mvm->napi);
 }
 
 /*
index 2531aa3d6754a318df3045ceed55c521d99da4c1..df216cd0c98f4659d1c00d7d07c8adb31c8244c6 100644 (file)
@@ -1290,8 +1290,6 @@ static int iwl_mvm_send_sta_igtk(struct iwl_mvm *mvm,
                const u8 *pn;
 
                memcpy(igtk_cmd.IGTK, keyconf->key, keyconf->keylen);
-               ieee80211_aes_cmac_calculate_k1_k2(keyconf,
-                                                  igtk_cmd.K1, igtk_cmd.K2);
                ieee80211_get_key_rx_seq(keyconf, 0, &seq);
                pn = seq.aes_cmac.pn;
                igtk_cmd.receive_seq_cnt = cpu_to_le64(((u64) pn[5] << 0) |
index 99e873dc86847c80de5a4d27cfdf84c62d83a519..dbb46ece6f5246b948c8c0cdff3ab40b4719a2b4 100644 (file)
@@ -2399,6 +2399,7 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
        ieee80211_hw_set(hw, AMPDU_AGGREGATION);
        ieee80211_hw_set(hw, MFP_CAPABLE);
        ieee80211_hw_set(hw, SIGNAL_DBM);
+       ieee80211_hw_set(hw, TDLS_WIDER_BW);
        if (rctbl)
                ieee80211_hw_set(hw, SUPPORTS_RC_TABLE);
 
@@ -3120,8 +3121,10 @@ static int hwsim_init_netlink(void)
                goto failure;
 
        rc = netlink_register_notifier(&hwsim_netlink_notifier);
-       if (rc)
+       if (rc) {
+               genl_unregister_family(&hwsim_genl_family);
                goto failure;
+       }
 
        return 0;
 
index c6028fd742c1a18309a323a016e3be076d9b13be..60f8226c5652986f87b8d638538d9d935037d1aa 100644 (file)
@@ -27,4 +27,43 @@ static inline unsigned long ewma_read(const struct ewma *avg)
        return avg->internal >> avg->factor;
 }
 
+#define DECLARE_EWMA(name, _factor, _weight)                           \
+       struct ewma_##name {                                            \
+               unsigned long internal;                                 \
+       };                                                              \
+       static inline void ewma_##name##_init(struct ewma_##name *e)    \
+       {                                                               \
+               BUILD_BUG_ON(!__builtin_constant_p(_factor));           \
+               BUILD_BUG_ON(!__builtin_constant_p(_weight));           \
+               BUILD_BUG_ON_NOT_POWER_OF_2(_factor);                   \
+               BUILD_BUG_ON_NOT_POWER_OF_2(_weight);                   \
+               e->internal = 0;                                        \
+       }                                                               \
+       static inline unsigned long                                     \
+       ewma_##name##_read(struct ewma_##name *e)                       \
+       {                                                               \
+               BUILD_BUG_ON(!__builtin_constant_p(_factor));           \
+               BUILD_BUG_ON(!__builtin_constant_p(_weight));           \
+               BUILD_BUG_ON_NOT_POWER_OF_2(_factor);                   \
+               BUILD_BUG_ON_NOT_POWER_OF_2(_weight);                   \
+               return e->internal >> ilog2(_factor);                   \
+       }                                                               \
+       static inline void ewma_##name##_add(struct ewma_##name *e,     \
+                                            unsigned long val)         \
+       {                                                               \
+               unsigned long internal = ACCESS_ONCE(e->internal);      \
+               unsigned long weight = ilog2(_weight);                  \
+               unsigned long factor = ilog2(_factor);                  \
+                                                                       \
+               BUILD_BUG_ON(!__builtin_constant_p(_factor));           \
+               BUILD_BUG_ON(!__builtin_constant_p(_weight));           \
+               BUILD_BUG_ON_NOT_POWER_OF_2(_factor);                   \
+               BUILD_BUG_ON_NOT_POWER_OF_2(_weight);                   \
+                                                                       \
+               ACCESS_ONCE(e->internal) = internal ?                   \
+                       (((internal << weight) - internal) +            \
+                               (val << factor)) >> weight :            \
+                       (val << factor);                                \
+       }
+
 #endif /* _LINUX_AVERAGE_H */
index b9c7897dc5668c3fe59c30ab8505d334a6689168..cfa906f28b7a277b480f5bf154bb8cf76e467ad5 100644 (file)
@@ -2074,8 +2074,8 @@ enum ieee80211_tdls_actioncode {
 #define WLAN_EXT_CAPA5_TDLS_PROHIBITED BIT(6)
 #define WLAN_EXT_CAPA5_TDLS_CH_SW_PROHIBITED   BIT(7)
 
+#define WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED    BIT(5)
 #define WLAN_EXT_CAPA8_OPMODE_NOTIF    BIT(6)
-#define WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED    BIT(7)
 
 /* TDLS specific payload type in the LLC/SNAP header */
 #define WLAN_TDLS_SNAP_RFTYPE  0x2
index 883fe1e7c5a17e982651a68cfb17cbc0bbe5b367..f0889a2476439553f055b31b6c577b6eb03e4d55 100644 (file)
@@ -2369,8 +2369,7 @@ struct cfg80211_qos_map {
  *     method returns 0.)
  *
  * @mgmt_frame_register: Notify driver that a management frame type was
- *     registered. Note that this callback may not sleep, and cannot run
- *     concurrently with itself.
+ *     registered. The callback is allowed to sleep.
  *
  * @set_antenna: Set antenna configuration (tx_ant, rx_ant) on the device.
  *     Parameters are bitmaps of allowed antennas to use for TX/RX. Drivers may
index 6b1077c2a63faaaafe60a7d080024662daeb8f54..e3314e516681ed0733ec212d7464c393222428ef 100644 (file)
@@ -973,6 +973,10 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info)
  * @RX_FLAG_IV_STRIPPED: The IV/ICV are stripped from this frame.
  *     If this flag is set, the stack cannot do any replay detection
  *     hence the driver or hardware will have to do that.
+ * @RX_FLAG_PN_VALIDATED: Currently only valid for CCMP/GCMP frames, this
+ *     flag indicates that the PN was verified for replay protection.
+ *     Note that this flag is also currently only supported when a frame
+ *     is also decrypted (ie. @RX_FLAG_DECRYPTED must be set)
  * @RX_FLAG_FAILED_FCS_CRC: Set this flag if the FCS check failed on
  *     the frame.
  * @RX_FLAG_FAILED_PLCP_CRC: Set this flag if the PCLP check failed on
@@ -997,9 +1001,6 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info)
  * @RX_FLAG_AMPDU_DETAILS: A-MPDU details are known, in particular the reference
  *     number (@ampdu_reference) must be populated and be a distinct number for
  *     each A-MPDU
- * @RX_FLAG_AMPDU_REPORT_ZEROLEN: driver reports 0-length subframes
- * @RX_FLAG_AMPDU_IS_ZEROLEN: This is a zero-length subframe, for
- *     monitoring purposes only
  * @RX_FLAG_AMPDU_LAST_KNOWN: last subframe is known, should be set on all
  *     subframes of a single A-MPDU
  * @RX_FLAG_AMPDU_IS_LAST: this subframe is the last subframe of the A-MPDU
@@ -1039,8 +1040,8 @@ enum mac80211_rx_flags {
        RX_FLAG_NO_SIGNAL_VAL           = BIT(12),
        RX_FLAG_HT_GF                   = BIT(13),
        RX_FLAG_AMPDU_DETAILS           = BIT(14),
-       RX_FLAG_AMPDU_REPORT_ZEROLEN    = BIT(15),
-       RX_FLAG_AMPDU_IS_ZEROLEN        = BIT(16),
+       RX_FLAG_PN_VALIDATED            = BIT(15),
+       /* bit 16 free */
        RX_FLAG_AMPDU_LAST_KNOWN        = BIT(17),
        RX_FLAG_AMPDU_IS_LAST           = BIT(18),
        RX_FLAG_AMPDU_DELIM_CRC_ERROR   = BIT(19),
@@ -1491,8 +1492,10 @@ enum ieee80211_key_flags {
  *     - Temporal Authenticator Rx MIC Key (64 bits)
  * @icv_len: The ICV length for this key type
  * @iv_len: The IV length for this key type
+ * @drv_priv: pointer for driver use
  */
 struct ieee80211_key_conf {
+       void *drv_priv;
        atomic64_t tx_pn;
        u32 cipher;
        u8 icv_len;
@@ -1675,7 +1678,6 @@ struct ieee80211_sta_rates {
  * @tdls: indicates whether the STA is a TDLS peer
  * @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.
  * @txq: per-TID data TX queues (if driver uses the TXQ abstraction)
  */
 struct ieee80211_sta {
@@ -1693,7 +1695,6 @@ struct ieee80211_sta {
        struct ieee80211_sta_rates __rcu *rates;
        bool tdls;
        bool tdls_initiator;
-       bool mfp;
 
        struct ieee80211_txq *txq[IEEE80211_NUM_TIDS];
 
@@ -1888,6 +1889,9 @@ struct ieee80211_txq {
  * @IEEE80211_HW_SINGLE_SCAN_ON_ALL_BANDS: The HW supports scanning on all bands
  *     in one command, mac80211 doesn't have to run separate scans per band.
  *
+ * @IEEE80211_HW_TDLS_WIDER_BW: The device/driver supports wider bandwidth
+ *     than then BSS bandwidth for a TDLS link on the base channel.
+ *
  * @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays
  */
 enum ieee80211_hw_flags {
@@ -1920,6 +1924,7 @@ enum ieee80211_hw_flags {
        IEEE80211_HW_CHANCTX_STA_CSA,
        IEEE80211_HW_SUPPORTS_CLONED_SKBS,
        IEEE80211_HW_SINGLE_SCAN_ON_ALL_BANDS,
+       IEEE80211_HW_TDLS_WIDER_BW,
 
        /* keep last, obviously */
        NUM_IEEE80211_HW_FLAGS
@@ -3696,20 +3701,28 @@ void ieee80211_free_hw(struct ieee80211_hw *hw);
 void ieee80211_restart_hw(struct ieee80211_hw *hw);
 
 /**
- * ieee80211_napi_add - initialize mac80211 NAPI context
- * @hw: the hardware to initialize the NAPI context on
- * @napi: the NAPI context to initialize
- * @napi_dev: dummy NAPI netdevice, here to not waste the space if the
- *     driver doesn't use NAPI
- * @poll: poll function
- * @weight: default weight
+ * ieee80211_rx_napi - receive frame from NAPI context
+ *
+ * Use this function to hand received frames to mac80211. The receive
+ * buffer in @skb must start with an IEEE 802.11 header. In case of a
+ * paged @skb is used, the driver is recommended to put the ieee80211
+ * header of the frame on the linear part of the @skb to avoid memory
+ * allocation and/or memcpy by the stack.
+ *
+ * This function may not be called in IRQ context. Calls to this function
+ * for a single hardware must be synchronized against each other. Calls to
+ * this function, ieee80211_rx_ni() and ieee80211_rx_irqsafe() may not be
+ * mixed for a single hardware. Must not run concurrently with
+ * ieee80211_tx_status() or ieee80211_tx_status_ni().
+ *
+ * This function must be called with BHs disabled.
  *
- * See also netif_napi_add().
+ * @hw: the hardware this frame came in on
+ * @skb: the buffer to receive, owned by mac80211 after this call
+ * @napi: the NAPI context
  */
-void ieee80211_napi_add(struct ieee80211_hw *hw, struct napi_struct *napi,
-                       struct net_device *napi_dev,
-                       int (*poll)(struct napi_struct *, int),
-                       int weight);
+void ieee80211_rx_napi(struct ieee80211_hw *hw, struct sk_buff *skb,
+                      struct napi_struct *napi);
 
 /**
  * ieee80211_rx - receive frame
@@ -3731,7 +3744,10 @@ void ieee80211_napi_add(struct ieee80211_hw *hw, struct napi_struct *napi,
  * @hw: the hardware this frame came in on
  * @skb: the buffer to receive, owned by mac80211 after this call
  */
-void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb);
+static inline void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb)
+{
+       ieee80211_rx_napi(hw, skb, NULL);
+}
 
 /**
  * ieee80211_rx_irqsafe - receive frame
@@ -4314,19 +4330,6 @@ void ieee80211_get_tkip_rx_p1k(struct ieee80211_key_conf *keyconf,
 void ieee80211_get_tkip_p2k(struct ieee80211_key_conf *keyconf,
                            struct sk_buff *skb, u8 *p2k);
 
-/**
- * ieee80211_aes_cmac_calculate_k1_k2 - calculate the AES-CMAC sub keys
- *
- * This function computes the two AES-CMAC sub-keys, based on the
- * previously installed master key.
- *
- * @keyconf: the parameter passed with the set key
- * @k1: a buffer to be filled with the 1st sub-key
- * @k2: a buffer to be filled with the 2nd sub-key
- */
-void ieee80211_aes_cmac_calculate_k1_k2(struct ieee80211_key_conf *keyconf,
-                                       u8 *k1, u8 *k2);
-
 /**
  * ieee80211_get_key_tx_seq - get key TX sequence counter
  *
index 086de496a4c197bb98a5e0b550a9b67d516d4902..3891cbd2adeab7bdcb062be3dfb36926cf91d23c 100644 (file)
@@ -7,7 +7,6 @@ config MAC80211
        select CRYPTO_CCM
        select CRYPTO_GCM
        select CRC32
-       select AVERAGE
        ---help---
          This option enables the hardware independent IEEE 802.11
          networking stack.
index 3275f01881bee8a53a046e117873347fe04877c8..783e891b7525c537bcab70b1be7c603e5f6d1f0d 100644 (file)
@@ -3,6 +3,7 @@ obj-$(CONFIG_MAC80211) += mac80211.o
 # mac80211 objects
 mac80211-y := \
        main.o status.o \
+       driver-ops.o \
        sta_info.o \
        wep.o \
        wpa.o \
index 4192806be3d36884d22ce5830a43cae63d54745d..bdf0790d89cca6fe3f64c097c84299ac35e1f3d0 100644 (file)
@@ -145,20 +145,3 @@ void ieee80211_aes_cmac_key_free(struct crypto_cipher *tfm)
 {
        crypto_free_cipher(tfm);
 }
-
-void ieee80211_aes_cmac_calculate_k1_k2(struct ieee80211_key_conf *keyconf,
-                                       u8 *k1, u8 *k2)
-{
-       u8 l[AES_BLOCK_SIZE] = {};
-       struct ieee80211_key *key =
-               container_of(keyconf, struct ieee80211_key, conf);
-
-       crypto_cipher_encrypt_one(key->u.aes_cmac.tfm, l, l);
-
-       memcpy(k1, l, AES_BLOCK_SIZE);
-       gf_mulx(k1);
-
-       memcpy(k2, k1, AES_BLOCK_SIZE);
-       gf_mulx(k2);
-}
-EXPORT_SYMBOL(ieee80211_aes_cmac_calculate_k1_k2);
index bf7023f6c3278289f1100f7ce6fa4f56d72caa15..685ec13ed7c2b0a2dcdcf82d7388d1c44d041a26 100644 (file)
@@ -1019,6 +1019,65 @@ static int sta_apply_auth_flags(struct ieee80211_local *local,
        return 0;
 }
 
+static void sta_apply_mesh_params(struct ieee80211_local *local,
+                                 struct sta_info *sta,
+                                 struct station_parameters *params)
+{
+#ifdef CONFIG_MAC80211_MESH
+       struct ieee80211_sub_if_data *sdata = sta->sdata;
+       u32 changed = 0;
+
+       if (params->sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE) {
+               switch (params->plink_state) {
+               case NL80211_PLINK_ESTAB:
+                       if (sta->mesh->plink_state != NL80211_PLINK_ESTAB)
+                               changed = mesh_plink_inc_estab_count(sdata);
+                       sta->mesh->plink_state = params->plink_state;
+
+                       ieee80211_mps_sta_status_update(sta);
+                       changed |= ieee80211_mps_set_sta_local_pm(sta,
+                                     sdata->u.mesh.mshcfg.power_mode);
+                       break;
+               case NL80211_PLINK_LISTEN:
+               case NL80211_PLINK_BLOCKED:
+               case NL80211_PLINK_OPN_SNT:
+               case NL80211_PLINK_OPN_RCVD:
+               case NL80211_PLINK_CNF_RCVD:
+               case NL80211_PLINK_HOLDING:
+                       if (sta->mesh->plink_state == NL80211_PLINK_ESTAB)
+                               changed = mesh_plink_dec_estab_count(sdata);
+                       sta->mesh->plink_state = params->plink_state;
+
+                       ieee80211_mps_sta_status_update(sta);
+                       changed |= ieee80211_mps_set_sta_local_pm(sta,
+                                       NL80211_MESH_POWER_UNKNOWN);
+                       break;
+               default:
+                       /*  nothing  */
+                       break;
+               }
+       }
+
+       switch (params->plink_action) {
+       case NL80211_PLINK_ACTION_NO_ACTION:
+               /* nothing */
+               break;
+       case NL80211_PLINK_ACTION_OPEN:
+               changed |= mesh_plink_open(sta);
+               break;
+       case NL80211_PLINK_ACTION_BLOCK:
+               changed |= mesh_plink_block(sta);
+               break;
+       }
+
+       if (params->local_pm)
+               changed |= ieee80211_mps_set_sta_local_pm(sta,
+                                                         params->local_pm);
+
+       ieee80211_mbss_info_change_notify(sdata, changed);
+#endif
+}
+
 static int sta_apply_parameters(struct ieee80211_local *local,
                                struct sta_info *sta,
                                struct station_parameters *params)
@@ -1076,7 +1135,6 @@ static int sta_apply_parameters(struct ieee80211_local *local,
        }
 
        if (mask & BIT(NL80211_STA_FLAG_MFP)) {
-               sta->sta.mfp = !!(set & BIT(NL80211_STA_FLAG_MFP));
                if (set & BIT(NL80211_STA_FLAG_MFP))
                        set_sta_flag(sta, WLAN_STA_MFP);
                else
@@ -1097,6 +1155,12 @@ static int sta_apply_parameters(struct ieee80211_local *local,
            params->ext_capab[3] & WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH)
                set_sta_flag(sta, WLAN_STA_TDLS_CHAN_SWITCH);
 
+       if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) &&
+           ieee80211_hw_check(&local->hw, TDLS_WIDER_BW) &&
+           params->ext_capab_len >= 8 &&
+           params->ext_capab[7] & WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED)
+               set_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW);
+
        if (params->sta_modify_mask & STATION_PARAM_APPLY_UAPSD) {
                sta->sta.uapsd_queues = params->uapsd_queues;
                sta->sta.max_sp = params->max_sp;
@@ -1144,62 +1208,8 @@ static int sta_apply_parameters(struct ieee80211_local *local,
                                              band, false);
        }
 
-       if (ieee80211_vif_is_mesh(&sdata->vif)) {
-#ifdef CONFIG_MAC80211_MESH
-               u32 changed = 0;
-
-               if (params->sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE) {
-                       switch (params->plink_state) {
-                       case NL80211_PLINK_ESTAB:
-                               if (sta->plink_state != NL80211_PLINK_ESTAB)
-                                       changed = mesh_plink_inc_estab_count(
-                                                       sdata);
-                               sta->plink_state = params->plink_state;
-
-                               ieee80211_mps_sta_status_update(sta);
-                               changed |= ieee80211_mps_set_sta_local_pm(sta,
-                                             sdata->u.mesh.mshcfg.power_mode);
-                               break;
-                       case NL80211_PLINK_LISTEN:
-                       case NL80211_PLINK_BLOCKED:
-                       case NL80211_PLINK_OPN_SNT:
-                       case NL80211_PLINK_OPN_RCVD:
-                       case NL80211_PLINK_CNF_RCVD:
-                       case NL80211_PLINK_HOLDING:
-                               if (sta->plink_state == NL80211_PLINK_ESTAB)
-                                       changed = mesh_plink_dec_estab_count(
-                                                       sdata);
-                               sta->plink_state = params->plink_state;
-
-                               ieee80211_mps_sta_status_update(sta);
-                               changed |= ieee80211_mps_set_sta_local_pm(sta,
-                                               NL80211_MESH_POWER_UNKNOWN);
-                               break;
-                       default:
-                               /*  nothing  */
-                               break;
-                       }
-               }
-
-               switch (params->plink_action) {
-               case NL80211_PLINK_ACTION_NO_ACTION:
-                       /* nothing */
-                       break;
-               case NL80211_PLINK_ACTION_OPEN:
-                       changed |= mesh_plink_open(sta);
-                       break;
-               case NL80211_PLINK_ACTION_BLOCK:
-                       changed |= mesh_plink_block(sta);
-                       break;
-               }
-
-               if (params->local_pm)
-                       changed |=
-                             ieee80211_mps_set_sta_local_pm(sta,
-                                                            params->local_pm);
-               ieee80211_mbss_info_change_notify(sdata, changed);
-#endif
-       }
+       if (ieee80211_vif_is_mesh(&sdata->vif))
+               sta_apply_mesh_params(local, sta, params);
 
        /* set the STA state after all sta info from usermode has been set */
        if (test_sta_flag(sta, WLAN_STA_TDLS_PEER)) {
@@ -2358,6 +2368,8 @@ int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata,
        const u8 *ap;
        enum ieee80211_smps_mode old_req;
        int err;
+       struct sta_info *sta;
+       bool tdls_peer_found = false;
 
        lockdep_assert_held(&sdata->wdev.mtx);
 
@@ -2382,11 +2394,22 @@ int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata,
 
        ap = sdata->u.mgd.associated->bssid;
 
+       rcu_read_lock();
+       list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) {
+               if (!sta->sta.tdls || sta->sdata != sdata || !sta->uploaded ||
+                   !test_sta_flag(sta, WLAN_STA_AUTHORIZED))
+                       continue;
+
+               tdls_peer_found = true;
+               break;
+       }
+       rcu_read_unlock();
+
        if (smps_mode == IEEE80211_SMPS_AUTOMATIC) {
-               if (sdata->u.mgd.powersave)
-                       smps_mode = IEEE80211_SMPS_DYNAMIC;
-               else
+               if (tdls_peer_found || !sdata->u.mgd.powersave)
                        smps_mode = IEEE80211_SMPS_OFF;
+               else
+                       smps_mode = IEEE80211_SMPS_DYNAMIC;
        }
 
        /* send SM PS frame to AP */
@@ -2394,6 +2417,8 @@ int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata,
                                         ap, ap);
        if (err)
                sdata->u.mgd.req_smps = old_req;
+       else if (smps_mode != IEEE80211_SMPS_OFF && tdls_peer_found)
+               ieee80211_teardown_tdls_peers(sdata);
 
        return err;
 }
@@ -2479,16 +2504,26 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy,
                sdata->rc_rateidx_mask[i] = mask->control[i].legacy;
                memcpy(sdata->rc_rateidx_mcs_mask[i], mask->control[i].ht_mcs,
                       sizeof(mask->control[i].ht_mcs));
+               memcpy(sdata->rc_rateidx_vht_mcs_mask[i],
+                      mask->control[i].vht_mcs,
+                      sizeof(mask->control[i].vht_mcs));
 
                sdata->rc_has_mcs_mask[i] = false;
+               sdata->rc_has_vht_mcs_mask[i] = false;
                if (!sband)
                        continue;
 
-               for (j = 0; j < IEEE80211_HT_MCS_MASK_LEN; j++)
-                       if (~sdata->rc_rateidx_mcs_mask[i][j]) {
+               for (j = 0; j < IEEE80211_HT_MCS_MASK_LEN; j++) {
+                       if (~sdata->rc_rateidx_mcs_mask[i][j])
                                sdata->rc_has_mcs_mask[i] = true;
+
+                       if (~sdata->rc_rateidx_vht_mcs_mask[i][j])
+                               sdata->rc_has_vht_mcs_mask[i] = true;
+
+                       if (sdata->rc_has_mcs_mask[i] &&
+                           sdata->rc_has_vht_mcs_mask[i])
                                break;
-                       }
+               }
        }
 
        return 0;
index f01c18a3160e11d72dae9e2a0939530ec805f6a5..1d1b9b7bdefe74ac851ca6d01554d663fae39541 100644 (file)
@@ -190,7 +190,7 @@ ieee80211_find_reservation_chanctx(struct ieee80211_local *local,
        return NULL;
 }
 
-static enum nl80211_chan_width ieee80211_get_sta_bw(struct ieee80211_sta *sta)
+enum nl80211_chan_width ieee80211_get_sta_bw(struct ieee80211_sta *sta)
 {
        switch (sta->bandwidth) {
        case IEEE80211_STA_RX_BW_20:
@@ -264,9 +264,17 @@ ieee80211_get_chanctx_max_required_bw(struct ieee80211_local *local,
                case NL80211_IFTYPE_AP_VLAN:
                        width = ieee80211_get_max_required_bw(sdata);
                        break;
+               case NL80211_IFTYPE_STATION:
+                       /*
+                        * The ap's sta->bandwidth is not set yet at this
+                        * point, so take the width from the chandef, but
+                        * account also for TDLS peers
+                        */
+                       width = max(vif->bss_conf.chandef.width,
+                                   ieee80211_get_max_required_bw(sdata));
+                       break;
                case NL80211_IFTYPE_P2P_DEVICE:
                        continue;
-               case NL80211_IFTYPE_STATION:
                case NL80211_IFTYPE_ADHOC:
                case NL80211_IFTYPE_WDS:
                case NL80211_IFTYPE_MESH_POINT:
@@ -554,12 +562,13 @@ static void ieee80211_free_chanctx(struct ieee80211_local *local,
        kfree_rcu(ctx, rcu_head);
 }
 
-static void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local,
-                                             struct ieee80211_chanctx *ctx)
+void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local,
+                                      struct ieee80211_chanctx *ctx)
 {
        struct ieee80211_chanctx_conf *conf = &ctx->conf;
        struct ieee80211_sub_if_data *sdata;
        const struct cfg80211_chan_def *compat = NULL;
+       struct sta_info *sta;
 
        lockdep_assert_held(&local->chanctx_mtx);
 
@@ -581,6 +590,20 @@ static void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local,
                if (WARN_ON_ONCE(!compat))
                        break;
        }
+
+       /* TDLS peers can sometimes affect the chandef width */
+       list_for_each_entry_rcu(sta, &local->sta_list, list) {
+               if (!sta->uploaded ||
+                   !test_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW) ||
+                   !test_sta_flag(sta, WLAN_STA_AUTHORIZED) ||
+                   !sta->tdls_chandef.chan)
+                       continue;
+
+               compat = cfg80211_chandef_compatible(&sta->tdls_chandef,
+                                                    compat);
+               if (WARN_ON_ONCE(!compat))
+                       break;
+       }
        rcu_read_unlock();
 
        if (!compat)
index 3ea8b7de963368faf6effceec8ad0e73c8d7fb54..ced6bf3be8d6cf5d3d9fc80b6c46f48c4e567aef 100644 (file)
@@ -122,6 +122,7 @@ static const char *hw_flag_names[NUM_IEEE80211_HW_FLAGS + 1] = {
        FLAG(CHANCTX_STA_CSA),
        FLAG(SUPPORTS_CLONED_SKBS),
        FLAG(SINGLE_SCAN_ON_ALL_BANDS),
+       FLAG(TDLS_WIDER_BW),
 
        /* keep last for the build bug below */
        (void *)0x1
@@ -277,7 +278,6 @@ void debugfs_hw_add(struct ieee80211_local *local)
        DEBUGFS_STATS_ADD(rx_handlers_queued);
        DEBUGFS_STATS_ADD(rx_handlers_drop_nullfunc);
        DEBUGFS_STATS_ADD(rx_handlers_drop_defrag);
-       DEBUGFS_STATS_ADD(rx_handlers_drop_short);
        DEBUGFS_STATS_ADD(tx_expand_skb_head);
        DEBUGFS_STATS_ADD(tx_expand_skb_head_cloned);
        DEBUGFS_STATS_ADD(rx_expand_skb_head_defrag);
index e82bf1e9d7a83e32566bf9d96f8f461bd4f141ab..702ca122c498938691842d95db7732d6d8c6d6bb 100644 (file)
@@ -57,7 +57,6 @@ KEY_CONF_FILE(keylen, D);
 KEY_CONF_FILE(keyidx, D);
 KEY_CONF_FILE(hw_key_idx, D);
 KEY_FILE(flags, X);
-KEY_FILE(tx_rx_count, D);
 KEY_READ(ifindex, sdata->name, "%s\n");
 KEY_OPS(ifindex);
 
@@ -310,7 +309,6 @@ void ieee80211_debugfs_key_add(struct ieee80211_key *key)
        DEBUGFS_ADD(flags);
        DEBUGFS_ADD(keyidx);
        DEBUGFS_ADD(hw_key_idx);
-       DEBUGFS_ADD(tx_rx_count);
        DEBUGFS_ADD(algorithm);
        DEBUGFS_ADD(tx_spec);
        DEBUGFS_ADD(rx_spec);
index c09c0131bfa227e99346b180f501cc6fcf64644a..1021e87c051f35168eef1274a38a4d720054359e 100644 (file)
@@ -186,6 +186,38 @@ IEEE80211_IF_FILE(rc_rateidx_mcs_mask_2ghz,
 IEEE80211_IF_FILE(rc_rateidx_mcs_mask_5ghz,
                  rc_rateidx_mcs_mask[IEEE80211_BAND_5GHZ], HEXARRAY);
 
+static ssize_t ieee80211_if_fmt_rc_rateidx_vht_mcs_mask_2ghz(
+                               const struct ieee80211_sub_if_data *sdata,
+                               char *buf, int buflen)
+{
+       int i, len = 0;
+       const u16 *mask = sdata->rc_rateidx_vht_mcs_mask[IEEE80211_BAND_2GHZ];
+
+       for (i = 0; i < NL80211_VHT_NSS_MAX; i++)
+               len += scnprintf(buf + len, buflen - len, "%04x ", mask[i]);
+       len += scnprintf(buf + len, buflen - len, "\n");
+
+       return len;
+}
+
+IEEE80211_IF_FILE_R(rc_rateidx_vht_mcs_mask_2ghz);
+
+static ssize_t ieee80211_if_fmt_rc_rateidx_vht_mcs_mask_5ghz(
+                               const struct ieee80211_sub_if_data *sdata,
+                               char *buf, int buflen)
+{
+       int i, len = 0;
+       const u16 *mask = sdata->rc_rateidx_vht_mcs_mask[IEEE80211_BAND_5GHZ];
+
+       for (i = 0; i < NL80211_VHT_NSS_MAX; i++)
+               len += scnprintf(buf + len, buflen - len, "%04x ", mask[i]);
+       len += scnprintf(buf + len, buflen - len, "\n");
+
+       return len;
+}
+
+IEEE80211_IF_FILE_R(rc_rateidx_vht_mcs_mask_5ghz);
+
 IEEE80211_IF_FILE(flags, flags, HEX);
 IEEE80211_IF_FILE(state, state, LHEX);
 IEEE80211_IF_FILE(txpower, vif.bss_conf.txpower, DEC);
@@ -565,6 +597,8 @@ static void add_common_files(struct ieee80211_sub_if_data *sdata)
        DEBUGFS_ADD(rc_rateidx_mask_5ghz);
        DEBUGFS_ADD(rc_rateidx_mcs_mask_2ghz);
        DEBUGFS_ADD(rc_rateidx_mcs_mask_5ghz);
+       DEBUGFS_ADD(rc_rateidx_vht_mcs_mask_2ghz);
+       DEBUGFS_ADD(rc_rateidx_vht_mcs_mask_5ghz);
        DEBUGFS_ADD(hw_queues);
 }
 
diff --git a/net/mac80211/driver-ops.c b/net/mac80211/driver-ops.c
new file mode 100644 (file)
index 0000000..267c3b1
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <net/mac80211.h>
+#include "ieee80211_i.h"
+#include "trace.h"
+#include "driver-ops.h"
+
+__must_check
+int drv_sta_state(struct ieee80211_local *local,
+                 struct ieee80211_sub_if_data *sdata,
+                 struct sta_info *sta,
+                 enum ieee80211_sta_state old_state,
+                 enum ieee80211_sta_state new_state)
+{
+       int ret = 0;
+
+       might_sleep();
+
+       sdata = get_bss_sdata(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return -EIO;
+
+       trace_drv_sta_state(local, sdata, &sta->sta, old_state, new_state);
+       if (local->ops->sta_state) {
+               ret = local->ops->sta_state(&local->hw, &sdata->vif, &sta->sta,
+                                           old_state, new_state);
+       } else if (old_state == IEEE80211_STA_AUTH &&
+                  new_state == IEEE80211_STA_ASSOC) {
+               ret = drv_sta_add(local, sdata, &sta->sta);
+               if (ret == 0)
+                       sta->uploaded = true;
+       } else if (old_state == IEEE80211_STA_ASSOC &&
+                  new_state == IEEE80211_STA_AUTH) {
+               drv_sta_remove(local, sdata, &sta->sta);
+       }
+       trace_drv_return_int(local, ret);
+       return ret;
+}
index 32a2e707e2226355235907c956632d6f03aa5af5..02d91332d7dddbe4459246308d605146be4430d7 100644 (file)
@@ -573,37 +573,12 @@ static inline void drv_sta_pre_rcu_remove(struct ieee80211_local *local,
        trace_drv_return_void(local);
 }
 
-static inline __must_check
+__must_check
 int drv_sta_state(struct ieee80211_local *local,
                  struct ieee80211_sub_if_data *sdata,
                  struct sta_info *sta,
                  enum ieee80211_sta_state old_state,
-                 enum ieee80211_sta_state new_state)
-{
-       int ret = 0;
-
-       might_sleep();
-
-       sdata = get_bss_sdata(sdata);
-       if (!check_sdata_in_driver(sdata))
-               return -EIO;
-
-       trace_drv_sta_state(local, sdata, &sta->sta, old_state, new_state);
-       if (local->ops->sta_state) {
-               ret = local->ops->sta_state(&local->hw, &sdata->vif, &sta->sta,
-                                           old_state, new_state);
-       } else if (old_state == IEEE80211_STA_AUTH &&
-                  new_state == IEEE80211_STA_ASSOC) {
-               ret = drv_sta_add(local, sdata, &sta->sta);
-               if (ret == 0)
-                       sta->uploaded = true;
-       } else if (old_state == IEEE80211_STA_ASSOC &&
-                  new_state == IEEE80211_STA_AUTH) {
-               drv_sta_remove(local, sdata, &sta->sta);
-       }
-       trace_drv_return_int(local, ret);
-       return ret;
-}
+                 enum ieee80211_sta_state new_state);
 
 static inline void drv_sta_rc_update(struct ieee80211_local *local,
                                     struct ieee80211_sub_if_data *sdata,
index b12f61507f9f9a4f84eaad69f98ac209af90361e..6e52659f923f72a6ab451e73b75338e4f4e7de13 100644 (file)
@@ -84,13 +84,13 @@ struct ieee80211_local;
 #define IEEE80211_DEAUTH_FRAME_LEN     (24 /* hdr */ + 2 /* reason */)
 
 struct ieee80211_fragment_entry {
-       unsigned long first_frag_time;
-       unsigned int seq;
-       unsigned int rx_queue;
-       unsigned int last_frag;
-       unsigned int extra_len;
        struct sk_buff_head skb_list;
-       int ccmp; /* Whether fragments were encrypted with CCMP */
+       unsigned long first_frag_time;
+       u16 seq;
+       u16 extra_len;
+       u16 last_frag;
+       u8 rx_queue;
+       bool ccmp; /* Whether fragments were encrypted with CCMP */
        u8 last_pn[6]; /* PN of the last fragment if CCMP was used */
 };
 
@@ -181,7 +181,6 @@ typedef unsigned __bitwise__ ieee80211_rx_result;
 
 /**
  * enum ieee80211_packet_rx_flags - packet RX flags
- * @IEEE80211_RX_FRAGMENTED: fragmented frame
  * @IEEE80211_RX_AMSDU: a-MSDU packet
  * @IEEE80211_RX_MALFORMED_ACTION_FRM: action frame is malformed
  * @IEEE80211_RX_DEFERRED_RELEASE: frame was subjected to receive reordering
@@ -190,7 +189,6 @@ typedef unsigned __bitwise__ ieee80211_rx_result;
  * @rx_flags field of &struct ieee80211_rx_status.
  */
 enum ieee80211_packet_rx_flags {
-       IEEE80211_RX_FRAGMENTED                 = BIT(2),
        IEEE80211_RX_AMSDU                      = BIT(3),
        IEEE80211_RX_MALFORMED_ACTION_FRM       = BIT(4),
        IEEE80211_RX_DEFERRED_RELEASE           = BIT(5),
@@ -202,8 +200,6 @@ enum ieee80211_packet_rx_flags {
  * @IEEE80211_RX_CMNTR: received on cooked monitor already
  * @IEEE80211_RX_BEACON_REPORTED: This frame was already reported
  *     to cfg80211_report_obss_beacon().
- * @IEEE80211_RX_REORDER_TIMER: this frame is released by the
- *     reorder buffer timeout timer, not the normal RX path
  *
  * These flags are used across handling multiple interfaces
  * for a single frame.
@@ -211,10 +207,10 @@ enum ieee80211_packet_rx_flags {
 enum ieee80211_rx_flags {
        IEEE80211_RX_CMNTR              = BIT(0),
        IEEE80211_RX_BEACON_REPORTED    = BIT(1),
-       IEEE80211_RX_REORDER_TIMER      = BIT(2),
 };
 
 struct ieee80211_rx_data {
+       struct napi_struct *napi;
        struct sk_buff *skb;
        struct ieee80211_local *local;
        struct ieee80211_sub_if_data *sdata;
@@ -725,6 +721,7 @@ struct ieee80211_if_mesh {
  *     back to wireless media and to the local net stack.
  * @IEEE80211_SDATA_DISCONNECT_RESUME: Disconnect after resume.
  * @IEEE80211_SDATA_IN_DRIVER: indicates interface was added to driver
+ * @IEEE80211_SDATA_MU_MIMO_OWNER: indicates interface owns MU-MIMO capability
  */
 enum ieee80211_sub_if_data_flags {
        IEEE80211_SDATA_ALLMULTI                = BIT(0),
@@ -732,6 +729,7 @@ enum ieee80211_sub_if_data_flags {
        IEEE80211_SDATA_DONT_BRIDGE_PACKETS     = BIT(3),
        IEEE80211_SDATA_DISCONNECT_RESUME       = BIT(4),
        IEEE80211_SDATA_IN_DRIVER               = BIT(5),
+       IEEE80211_SDATA_MU_MIMO_OWNER           = BIT(6),
 };
 
 /**
@@ -903,6 +901,9 @@ struct ieee80211_sub_if_data {
        bool rc_has_mcs_mask[IEEE80211_NUM_BANDS];
        u8  rc_rateidx_mcs_mask[IEEE80211_NUM_BANDS][IEEE80211_HT_MCS_MASK_LEN];
 
+       bool rc_has_vht_mcs_mask[IEEE80211_NUM_BANDS];
+       u16 rc_rateidx_vht_mcs_mask[IEEE80211_NUM_BANDS][NL80211_VHT_NSS_MAX];
+
        union {
                struct ieee80211_if_ap ap;
                struct ieee80211_if_wds wds;
@@ -1010,7 +1011,6 @@ enum sdata_queue_type {
        IEEE80211_SDATA_QUEUE_AGG_STOP          = 2,
        IEEE80211_SDATA_QUEUE_RX_AGG_START      = 3,
        IEEE80211_SDATA_QUEUE_RX_AGG_STOP       = 4,
-       IEEE80211_SDATA_QUEUE_TDLS_CHSW         = 5,
 };
 
 enum {
@@ -1286,7 +1286,6 @@ struct ieee80211_local {
        unsigned int rx_handlers_queued;
        unsigned int rx_handlers_drop_nullfunc;
        unsigned int rx_handlers_drop_defrag;
-       unsigned int rx_handlers_drop_short;
        unsigned int tx_expand_skb_head;
        unsigned int tx_expand_skb_head_cloned;
        unsigned int rx_expand_skb_head_defrag;
@@ -1348,14 +1347,16 @@ struct ieee80211_local {
 
        struct ieee80211_sub_if_data __rcu *p2p_sdata;
 
-       struct napi_struct *napi;
-
        /* virtual monitor interface */
        struct ieee80211_sub_if_data __rcu *monitor_sdata;
        struct cfg80211_chan_def monitor_chandef;
 
        /* extended capabilities provided by mac80211 */
        u8 ext_capa[8];
+
+       /* TDLS channel switch */
+       struct work_struct tdls_chsw_work;
+       struct sk_buff_head skb_queue_tdls_chsw;
 };
 
 static inline struct ieee80211_sub_if_data *
@@ -1715,6 +1716,8 @@ void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
                                 enum ieee80211_band band, bool nss_only);
 void ieee80211_apply_vhtcap_overrides(struct ieee80211_sub_if_data *sdata,
                                      struct ieee80211_sta_vht_cap *vht_cap);
+void ieee80211_get_vht_mask_from_cap(__le16 vht_cap,
+                                    u16 vht_mask[NL80211_VHT_NSS_MAX]);
 
 /* Spectrum management */
 void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
@@ -1763,8 +1766,6 @@ static inline int __ieee80211_resume(struct ieee80211_hw *hw)
 
 /* utility functions/constants */
 extern const void *const mac80211_wiphy_privid; /* for wiphy privid */
-u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len,
-                       enum nl80211_iftype type);
 int ieee80211_frame_duration(enum ieee80211_band band, size_t len,
                             int rate, int erp, int short_preamble,
                             int shift);
@@ -2042,6 +2043,9 @@ int ieee80211_check_combinations(struct ieee80211_sub_if_data *sdata,
                                 enum ieee80211_chanctx_mode chanmode,
                                 u8 radar_detect);
 int ieee80211_max_num_channels(struct ieee80211_local *local);
+enum nl80211_chan_width ieee80211_get_sta_bw(struct ieee80211_sta *sta);
+void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local,
+                                      struct ieee80211_chanctx *ctx);
 
 /* TDLS */
 int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
@@ -2058,8 +2062,8 @@ int ieee80211_tdls_channel_switch(struct wiphy *wiphy, struct net_device *dev,
 void ieee80211_tdls_cancel_channel_switch(struct wiphy *wiphy,
                                          struct net_device *dev,
                                          const u8 *addr);
-void ieee80211_process_tdls_channel_switch(struct ieee80211_sub_if_data *sdata,
-                                          struct sk_buff *skb);
+void ieee80211_teardown_tdls_peers(struct ieee80211_sub_if_data *sdata);
+void ieee80211_tdls_chsw_work(struct work_struct *wk);
 
 extern const struct ethtool_ops ieee80211_ethtool_ops;
 
index 553ac6dd4867480048aed3ca0d430948f928d3c7..6964fc6a8ea2c7b46149e8be5e79c6f373f50d40 100644 (file)
@@ -1242,8 +1242,6 @@ static void ieee80211_iface_work(struct work_struct *work)
                                                        WLAN_BACK_RECIPIENT, 0,
                                                        false);
                        mutex_unlock(&local->sta_mtx);
-               } else if (skb->pkt_type == IEEE80211_SDATA_QUEUE_TDLS_CHSW) {
-                       ieee80211_process_tdls_channel_switch(sdata, skb);
                } else if (ieee80211_is_action(mgmt->frame_control) &&
                           mgmt->u.action.category == WLAN_CATEGORY_BACK) {
                        int len = skb->len;
@@ -1790,13 +1788,23 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
                sband = local->hw.wiphy->bands[i];
                sdata->rc_rateidx_mask[i] =
                        sband ? (1 << sband->n_bitrates) - 1 : 0;
-               if (sband)
+               if (sband) {
+                       __le16 cap;
+                       u16 *vht_rate_mask;
+
                        memcpy(sdata->rc_rateidx_mcs_mask[i],
                               sband->ht_cap.mcs.rx_mask,
                               sizeof(sdata->rc_rateidx_mcs_mask[i]));
-               else
+
+                       cap = sband->vht_cap.vht_mcs.rx_mcs_map;
+                       vht_rate_mask = sdata->rc_rateidx_vht_mcs_mask[i];
+                       ieee80211_get_vht_mask_from_cap(cap, vht_rate_mask);
+               } else {
                        memset(sdata->rc_rateidx_mcs_mask[i], 0,
                               sizeof(sdata->rc_rateidx_mcs_mask[i]));
+                       memset(sdata->rc_rateidx_vht_mcs_mask[i], 0,
+                              sizeof(sdata->rc_rateidx_vht_mcs_mask[i]));
+               }
        }
 
        ieee80211_set_default_queues(sdata);
index b22df3a79a417c9d182647f3126e004490d3de93..44388d6a1d8e628f4324f64e95c9de675e861830 100644 (file)
@@ -336,7 +336,6 @@ static void ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
                        ieee80211_check_fast_xmit(sta);
                } else {
                        rcu_assign_pointer(sta->gtk[idx], new);
-                       sta->gtk_idx = idx;
                }
        } else {
                defunikey = old &&
index 3f4f9eaac14003d1a2f2f655215fd0ed3fcca499..9951ef06323e743d2c33156d15e5a21478584cd9 100644 (file)
@@ -115,9 +115,6 @@ struct ieee80211_key {
                } gen;
        } u;
 
-       /* number of times this key has been used */
-       int tx_rx_count;
-
 #ifdef CONFIG_MAC80211_DEBUGFS
        struct {
                struct dentry *stalink;
index 3c63468b4dfb530d0e28a5f97dddf5065543ba6f..ff79a13d231db0d4197c80a67a9d119d4c870e68 100644 (file)
@@ -629,6 +629,8 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,
        INIT_WORK(&local->sched_scan_stopped_work,
                  ieee80211_sched_scan_stopped_work);
 
+       INIT_WORK(&local->tdls_chsw_work, ieee80211_tdls_chsw_work);
+
        spin_lock_init(&local->ack_status_lock);
        idr_init(&local->ack_status_frames);
 
@@ -645,6 +647,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,
 
        skb_queue_head_init(&local->skb_queue);
        skb_queue_head_init(&local->skb_queue_unreliable);
+       skb_queue_head_init(&local->skb_queue_tdls_chsw);
 
        ieee80211_alloc_led_names(local);
 
@@ -1132,18 +1135,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
 }
 EXPORT_SYMBOL(ieee80211_register_hw);
 
-void ieee80211_napi_add(struct ieee80211_hw *hw, struct napi_struct *napi,
-                       struct net_device *napi_dev,
-                       int (*poll)(struct napi_struct *, int),
-                       int weight)
-{
-       struct ieee80211_local *local = hw_to_local(hw);
-
-       netif_napi_add(napi_dev, napi, poll, weight);
-       local->napi = napi;
-}
-EXPORT_SYMBOL_GPL(ieee80211_napi_add);
-
 void ieee80211_unregister_hw(struct ieee80211_hw *hw)
 {
        struct ieee80211_local *local = hw_to_local(hw);
@@ -1173,6 +1164,7 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
 
        cancel_work_sync(&local->restart_work);
        cancel_work_sync(&local->reconfig_filter);
+       cancel_work_sync(&local->tdls_chsw_work);
        flush_work(&local->sched_scan_stopped_work);
 
        ieee80211_clear_tx_pending(local);
@@ -1183,6 +1175,7 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
                wiphy_warn(local->hw.wiphy, "skb_queue not empty\n");
        skb_queue_purge(&local->skb_queue);
        skb_queue_purge(&local->skb_queue_unreliable);
+       skb_queue_purge(&local->skb_queue_tdls_chsw);
 
        destroy_workqueue(local->workqueue);
        wiphy_unregister(local->hw.wiphy);
index 817098add1d6736e1632ba7f142a9b81465cd71f..e06a5ca7c9a996b311b524c37f93629ccc928a61 100644 (file)
@@ -158,7 +158,7 @@ void mesh_sta_cleanup(struct sta_info *sta)
        changed = mesh_accept_plinks_update(sdata);
        if (!sdata->u.mesh.user_mpm) {
                changed |= mesh_plink_deactivate(sta);
-               del_timer_sync(&sta->plink_timer);
+               del_timer_sync(&sta->mesh->plink_timer);
        }
 
        if (changed)
index 085edc1d056bf7adfe1f7fba5a500733bf72927a..d80e0a4c16cf98eb386bb61c3513f20619693556 100644 (file)
 
 #define MAX_PREQ_QUEUE_LEN     64
 
-/* Destination only */
-#define MP_F_DO        0x1
-/* Reply and forward */
-#define MP_F_RF        0x2
-/* Unknown Sequence Number */
-#define MP_F_USN    0x01
-/* Reason code Present */
-#define MP_F_RCODE  0x02
-
 static void mesh_queue_preq(struct mesh_path *, u8);
 
 static inline u32 u32_field_get(const u8 *preq_elem, int offset, bool ae)
@@ -79,6 +70,12 @@ static inline u16 u16_field_get(const u8 *preq_elem, int offset, bool ae)
 #define MSEC_TO_TU(x) (x*1000/1024)
 #define SN_GT(x, y) ((s32)(y - x) < 0)
 #define SN_LT(x, y) ((s32)(x - y) < 0)
+#define MAX_SANE_SN_DELTA 32
+
+static inline u32 SN_DELTA(u32 x, u32 y)
+{
+       return x >= y ? x - y : y - x;
+}
 
 #define net_traversal_jiffies(s) \
        msecs_to_jiffies(s->u.mesh.mshcfg.dot11MeshHWMPnetDiameterTraversalTime)
@@ -279,15 +276,10 @@ int mesh_path_error_tx(struct ieee80211_sub_if_data *sdata,
        *pos++ = ttl;
        /* number of destinations */
        *pos++ = 1;
-       /*
-        * flags bit, bit 1 is unset if we know the sequence number and
-        * bit 2 is set if we have a reason code
+       /* Flags field has AE bit only as defined in
+        * sec 8.4.2.117 IEEE802.11-2012
         */
        *pos = 0;
-       if (!target_sn)
-               *pos |= MP_F_USN;
-       if (target_rcode)
-               *pos |= MP_F_RCODE;
        pos++;
        memcpy(pos, target, ETH_ALEN);
        pos += ETH_ALEN;
@@ -316,8 +308,9 @@ void ieee80211s_update_metric(struct ieee80211_local *local,
        failed = !(txinfo->flags & IEEE80211_TX_STAT_ACK);
 
        /* moving average, scaled to 100 */
-       sta->fail_avg = ((80 * sta->fail_avg + 5) / 100 + 20 * failed);
-       if (sta->fail_avg > 95)
+       sta->mesh->fail_avg =
+               ((80 * sta->mesh->fail_avg + 5) / 100 + 20 * failed);
+       if (sta->mesh->fail_avg > 95)
                mesh_plink_broken(sta);
 }
 
@@ -333,7 +326,7 @@ static u32 airtime_link_metric_get(struct ieee80211_local *local,
        u32 tx_time, estimated_retx;
        u64 result;
 
-       if (sta->fail_avg >= 100)
+       if (sta->mesh->fail_avg >= 100)
                return MAX_METRIC;
 
        sta_set_rate_info_tx(sta, &sta->last_tx_rate, &rinfo);
@@ -341,7 +334,7 @@ static u32 airtime_link_metric_get(struct ieee80211_local *local,
        if (WARN_ON(!rate))
                return MAX_METRIC;
 
-       err = (sta->fail_avg << ARITH_SHIFT) / 100;
+       err = (sta->mesh->fail_avg << ARITH_SHIFT) / 100;
 
        /* bitrate is in units of 100 Kbps, while we need rate in units of
         * 1Mbps. This will be corrected on tx_time computation.
@@ -441,6 +434,26 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata,
                                        process = false;
                                        fresh_info = false;
                                }
+                       } else if (!(mpath->flags & MESH_PATH_ACTIVE)) {
+                               bool have_sn, newer_sn, bounced;
+
+                               have_sn = mpath->flags & MESH_PATH_SN_VALID;
+                               newer_sn = have_sn && SN_GT(orig_sn, mpath->sn);
+                               bounced = have_sn &&
+                                         (SN_DELTA(orig_sn, mpath->sn) >
+                                                       MAX_SANE_SN_DELTA);
+
+                               if (!have_sn || newer_sn) {
+                                       /* if SN is newer than what we had
+                                        * then we can take it */;
+                               } else if (bounced) {
+                                       /* if SN is way different than what
+                                        * we had then assume the other side
+                                        * rebooted or restarted */;
+                               } else {
+                                       process = false;
+                                       fresh_info = false;
+                               }
                        }
                } else {
                        mpath = mesh_path_add(sdata, orig_addr);
@@ -570,15 +583,13 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata,
                                        SN_LT(mpath->sn, target_sn)) {
                                mpath->sn = target_sn;
                                mpath->flags |= MESH_PATH_SN_VALID;
-                       } else if ((!(target_flags & MP_F_DO)) &&
+                       } else if ((!(target_flags & IEEE80211_PREQ_TO_FLAG)) &&
                                        (mpath->flags & MESH_PATH_ACTIVE)) {
                                reply = true;
                                target_metric = mpath->metric;
                                target_sn = mpath->sn;
-                               if (target_flags & MP_F_RF)
-                                       target_flags |= MP_F_DO;
-                               else
-                                       forward = false;
+                               /* Case E2 of sec 13.10.9.3 IEEE 802.11-2012*/
+                               target_flags |= IEEE80211_PREQ_TO_FLAG;
                        }
                }
                rcu_read_unlock();
@@ -736,9 +747,12 @@ static void hwmp_perr_frame_process(struct ieee80211_sub_if_data *sdata,
                if (mpath->flags & MESH_PATH_ACTIVE &&
                    ether_addr_equal(ta, sta->sta.addr) &&
                    (!(mpath->flags & MESH_PATH_SN_VALID) ||
-                   SN_GT(target_sn, mpath->sn))) {
+                   SN_GT(target_sn, mpath->sn)  || target_sn == 0)) {
                        mpath->flags &= ~MESH_PATH_ACTIVE;
-                       mpath->sn = target_sn;
+                       if (target_sn != 0)
+                               mpath->sn = target_sn;
+                       else
+                               mpath->sn += 1;
                        spin_unlock_bh(&mpath->state_lock);
                        if (!ifmsh->mshcfg.dot11MeshForwarding)
                                goto endperr;
@@ -862,7 +876,7 @@ void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata,
 
        rcu_read_lock();
        sta = sta_info_get(sdata, mgmt->sa);
-       if (!sta || sta->plink_state != NL80211_PLINK_ESTAB) {
+       if (!sta || sta->mesh->plink_state != NL80211_PLINK_ESTAB) {
                rcu_read_unlock();
                return;
        }
@@ -974,7 +988,7 @@ void mesh_path_start_discovery(struct ieee80211_sub_if_data *sdata)
        struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
        struct mesh_preq_queue *preq_node;
        struct mesh_path *mpath;
-       u8 ttl, target_flags;
+       u8 ttl, target_flags = 0;
        const u8 *da;
        u32 lifetime;
 
@@ -1033,9 +1047,9 @@ void mesh_path_start_discovery(struct ieee80211_sub_if_data *sdata)
        }
 
        if (preq_node->flags & PREQ_Q_F_REFRESH)
-               target_flags = MP_F_DO;
+               target_flags |= IEEE80211_PREQ_TO_FLAG;
        else
-               target_flags = MP_F_RF;
+               target_flags &= ~IEEE80211_PREQ_TO_FLAG;
 
        spin_unlock_bh(&mpath->state_lock);
        da = (mpath->is_root) ? mpath->rann_snd_addr : broadcast_addr;
@@ -1176,7 +1190,9 @@ void mesh_path_timer(unsigned long data)
                spin_unlock_bh(&mpath->state_lock);
                mesh_queue_preq(mpath, 0);
        } else {
-               mpath->flags = 0;
+               mpath->flags &= ~(MESH_PATH_RESOLVING |
+                                 MESH_PATH_RESOLVED |
+                                 MESH_PATH_REQ_QUEUED);
                mpath->exp_time = jiffies;
                spin_unlock_bh(&mpath->state_lock);
                if (!mpath->is_gate && mesh_gate_num(sdata) > 0) {
index 3b59099413fb1770e2ee2228065899bc5f9eb302..58384642e03c52bb6427fc809a9149769855e9c1 100644 (file)
 #include "rate.h"
 #include "mesh.h"
 
+#define PLINK_CNF_AID(mgmt) ((mgmt)->u.action.u.self_prot.variable + 2)
 #define PLINK_GET_LLID(p) (p + 2)
 #define PLINK_GET_PLID(p) (p + 4)
 
-#define mod_plink_timer(s, t) (mod_timer(&s->plink_timer, \
+#define mod_plink_timer(s, t) (mod_timer(&s->mesh->plink_timer, \
                                jiffies + msecs_to_jiffies(t)))
 
 enum plink_event {
@@ -53,18 +54,13 @@ static const char * const mplevents[] = {
        [CLS_IGNR] = "CLS_IGNR"
 };
 
-static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
-                              enum ieee80211_self_protected_actioncode action,
-                              u8 *da, u16 llid, u16 plid, u16 reason);
-
-
 /* We only need a valid sta if user configured a minimum rssi_threshold. */
 static bool rssi_threshold_check(struct ieee80211_sub_if_data *sdata,
                                 struct sta_info *sta)
 {
        s32 rssi_threshold = sdata->u.mesh.mshcfg.rssi_threshold;
        return rssi_threshold == 0 ||
-              (sta && (s8) -ewma_read(&sta->avg_signal) > rssi_threshold);
+              (sta && (s8) -ewma_signal_read(&sta->avg_signal) > rssi_threshold);
 }
 
 /**
@@ -72,14 +68,14 @@ static bool rssi_threshold_check(struct ieee80211_sub_if_data *sdata,
  *
  * @sta: mesh peer link to restart
  *
- * Locking: this function must be called holding sta->plink_lock
+ * Locking: this function must be called holding sta->mesh->plink_lock
  */
 static inline void mesh_plink_fsm_restart(struct sta_info *sta)
 {
-       lockdep_assert_held(&sta->plink_lock);
-       sta->plink_state = NL80211_PLINK_LISTEN;
-       sta->llid = sta->plid = sta->reason = 0;
-       sta->plink_retries = 0;
+       lockdep_assert_held(&sta->mesh->plink_lock);
+       sta->mesh->plink_state = NL80211_PLINK_LISTEN;
+       sta->mesh->llid = sta->mesh->plid = sta->mesh->reason = 0;
+       sta->mesh->plink_retries = 0;
 }
 
 /*
@@ -119,7 +115,7 @@ static u32 mesh_set_short_slot_time(struct ieee80211_sub_if_data *sdata)
        rcu_read_lock();
        list_for_each_entry_rcu(sta, &local->sta_list, list) {
                if (sdata != sta->sdata ||
-                   sta->plink_state != NL80211_PLINK_ESTAB)
+                   sta->mesh->plink_state != NL80211_PLINK_ESTAB)
                        continue;
 
                short_slot = false;
@@ -169,7 +165,7 @@ static u32 mesh_set_ht_prot_mode(struct ieee80211_sub_if_data *sdata)
        rcu_read_lock();
        list_for_each_entry_rcu(sta, &local->sta_list, list) {
                if (sdata != sta->sdata ||
-                   sta->plink_state != NL80211_PLINK_ESTAB)
+                   sta->mesh->plink_state != NL80211_PLINK_ESTAB)
                        continue;
 
                if (sta->sta.bandwidth > IEEE80211_STA_RX_BW_20)
@@ -204,59 +200,8 @@ static u32 mesh_set_ht_prot_mode(struct ieee80211_sub_if_data *sdata)
        return BSS_CHANGED_HT;
 }
 
-/**
- * __mesh_plink_deactivate - deactivate mesh peer link
- *
- * @sta: mesh peer link to deactivate
- *
- * All mesh paths with this peer as next hop will be flushed
- * Returns beacon changed flag if the beacon content changed.
- *
- * Locking: the caller must hold sta->plink_lock
- */
-static u32 __mesh_plink_deactivate(struct sta_info *sta)
-{
-       struct ieee80211_sub_if_data *sdata = sta->sdata;
-       u32 changed = 0;
-
-       lockdep_assert_held(&sta->plink_lock);
-
-       if (sta->plink_state == NL80211_PLINK_ESTAB)
-               changed = mesh_plink_dec_estab_count(sdata);
-       sta->plink_state = NL80211_PLINK_BLOCKED;
-       mesh_path_flush_by_nexthop(sta);
-
-       ieee80211_mps_sta_status_update(sta);
-       changed |= ieee80211_mps_set_sta_local_pm(sta,
-                       NL80211_MESH_POWER_UNKNOWN);
-
-       return changed;
-}
-
-/**
- * mesh_plink_deactivate - deactivate mesh peer link
- *
- * @sta: mesh peer link to deactivate
- *
- * All mesh paths with this peer as next hop will be flushed
- */
-u32 mesh_plink_deactivate(struct sta_info *sta)
-{
-       struct ieee80211_sub_if_data *sdata = sta->sdata;
-       u32 changed;
-
-       spin_lock_bh(&sta->plink_lock);
-       changed = __mesh_plink_deactivate(sta);
-       sta->reason = WLAN_REASON_MESH_PEER_CANCELED;
-       mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE,
-                           sta->sta.addr, sta->llid, sta->plid,
-                           sta->reason);
-       spin_unlock_bh(&sta->plink_lock);
-
-       return changed;
-}
-
 static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
+                              struct sta_info *sta,
                               enum ieee80211_self_protected_actioncode action,
                               u8 *da, u16 llid, u16 plid, u16 reason)
 {
@@ -306,7 +251,7 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
                if (action == WLAN_SP_MESH_PEERING_CONFIRM) {
                        /* AID */
                        pos = skb_put(skb, 2);
-                       put_unaligned_le16(plid, pos);
+                       put_unaligned_le16(sta->sta.aid, pos);
                }
                if (ieee80211_add_srates_ie(sdata, skb, true, band) ||
                    ieee80211_add_ext_srates_ie(sdata, skb, true, band) ||
@@ -375,6 +320,58 @@ free:
        return err;
 }
 
+/**
+ * __mesh_plink_deactivate - deactivate mesh peer link
+ *
+ * @sta: mesh peer link to deactivate
+ *
+ * All mesh paths with this peer as next hop will be flushed
+ * Returns beacon changed flag if the beacon content changed.
+ *
+ * Locking: the caller must hold sta->mesh->plink_lock
+ */
+static u32 __mesh_plink_deactivate(struct sta_info *sta)
+{
+       struct ieee80211_sub_if_data *sdata = sta->sdata;
+       u32 changed = 0;
+
+       lockdep_assert_held(&sta->mesh->plink_lock);
+
+       if (sta->mesh->plink_state == NL80211_PLINK_ESTAB)
+               changed = mesh_plink_dec_estab_count(sdata);
+       sta->mesh->plink_state = NL80211_PLINK_BLOCKED;
+       mesh_path_flush_by_nexthop(sta);
+
+       ieee80211_mps_sta_status_update(sta);
+       changed |= ieee80211_mps_set_sta_local_pm(sta,
+                       NL80211_MESH_POWER_UNKNOWN);
+
+       return changed;
+}
+
+/**
+ * mesh_plink_deactivate - deactivate mesh peer link
+ *
+ * @sta: mesh peer link to deactivate
+ *
+ * All mesh paths with this peer as next hop will be flushed
+ */
+u32 mesh_plink_deactivate(struct sta_info *sta)
+{
+       struct ieee80211_sub_if_data *sdata = sta->sdata;
+       u32 changed;
+
+       spin_lock_bh(&sta->mesh->plink_lock);
+       changed = __mesh_plink_deactivate(sta);
+       sta->mesh->reason = WLAN_REASON_MESH_PEER_CANCELED;
+       mesh_plink_frame_tx(sdata, sta, WLAN_SP_MESH_PEERING_CLOSE,
+                           sta->sta.addr, sta->mesh->llid, sta->mesh->plid,
+                           sta->mesh->reason);
+       spin_unlock_bh(&sta->mesh->plink_lock);
+
+       return changed;
+}
+
 static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata,
                               struct sta_info *sta,
                               struct ieee802_11_elems *elems, bool insert)
@@ -388,13 +385,14 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata,
        sband = local->hw.wiphy->bands[band];
        rates = ieee80211_sta_get_rates(sdata, elems, band, &basic_rates);
 
-       spin_lock_bh(&sta->plink_lock);
+       spin_lock_bh(&sta->mesh->plink_lock);
        sta->last_rx = jiffies;
 
        /* rates and capabilities don't change during peering */
-       if (sta->plink_state == NL80211_PLINK_ESTAB && sta->processed_beacon)
+       if (sta->mesh->plink_state == NL80211_PLINK_ESTAB &&
+           sta->mesh->processed_beacon)
                goto out;
-       sta->processed_beacon = true;
+       sta->mesh->processed_beacon = true;
 
        if (sta->sta.supp_rates[band] != rates)
                changed |= IEEE80211_RC_SUPP_RATES_CHANGED;
@@ -421,23 +419,57 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata,
        else
                rate_control_rate_update(local, sband, sta, changed);
 out:
-       spin_unlock_bh(&sta->plink_lock);
+       spin_unlock_bh(&sta->mesh->plink_lock);
+}
+
+static int mesh_allocate_aid(struct ieee80211_sub_if_data *sdata)
+{
+       struct sta_info *sta;
+       unsigned long *aid_map;
+       int aid;
+
+       aid_map = kcalloc(BITS_TO_LONGS(IEEE80211_MAX_AID + 1),
+                         sizeof(*aid_map), GFP_KERNEL);
+       if (!aid_map)
+               return -ENOMEM;
+
+       /* reserve aid 0 for mcast indication */
+       __set_bit(0, aid_map);
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(sta, &sdata->local->sta_list, list)
+               __set_bit(sta->sta.aid, aid_map);
+       rcu_read_unlock();
+
+       aid = find_first_zero_bit(aid_map, IEEE80211_MAX_AID + 1);
+       kfree(aid_map);
+
+       if (aid > IEEE80211_MAX_AID)
+               return -ENOBUFS;
+
+       return aid;
 }
 
 static struct sta_info *
 __mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *hw_addr)
 {
        struct sta_info *sta;
+       int aid;
 
        if (sdata->local->num_sta >= MESH_MAX_PLINKS)
                return NULL;
 
+       aid = mesh_allocate_aid(sdata);
+       if (aid < 0)
+               return NULL;
+
        sta = sta_info_alloc(sdata, hw_addr, GFP_KERNEL);
        if (!sta)
                return NULL;
 
-       sta->plink_state = NL80211_PLINK_LISTEN;
+       sta->mesh->plink_state = NL80211_PLINK_LISTEN;
        sta->sta.wme = true;
+       sta->sta.aid = aid;
 
        sta_info_pre_move_state(sta, IEEE80211_STA_AUTH);
        sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC);
@@ -524,7 +556,7 @@ void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata,
                goto out;
 
        if (mesh_peer_accepts_plinks(elems) &&
-           sta->plink_state == NL80211_PLINK_LISTEN &&
+           sta->mesh->plink_state == NL80211_PLINK_LISTEN &&
            sdata->u.mesh.accepting_plinks &&
            sdata->u.mesh.mshcfg.auto_open_plinks &&
            rssi_threshold_check(sdata, sta))
@@ -554,52 +586,52 @@ static void mesh_plink_timer(unsigned long data)
        if (sta->sdata->local->quiescing)
                return;
 
-       spin_lock_bh(&sta->plink_lock);
+       spin_lock_bh(&sta->mesh->plink_lock);
 
        /* If a timer fires just before a state transition on another CPU,
         * we may have already extended the timeout and changed state by the
         * time we've acquired the lock and arrived  here.  In that case,
         * skip this timer and wait for the new one.
         */
-       if (time_before(jiffies, sta->plink_timer.expires)) {
+       if (time_before(jiffies, sta->mesh->plink_timer.expires)) {
                mpl_dbg(sta->sdata,
                        "Ignoring timer for %pM in state %s (timer adjusted)",
-                       sta->sta.addr, mplstates[sta->plink_state]);
-               spin_unlock_bh(&sta->plink_lock);
+                       sta->sta.addr, mplstates[sta->mesh->plink_state]);
+               spin_unlock_bh(&sta->mesh->plink_lock);
                return;
        }
 
        /* del_timer() and handler may race when entering these states */
-       if (sta->plink_state == NL80211_PLINK_LISTEN ||
-           sta->plink_state == NL80211_PLINK_ESTAB) {
+       if (sta->mesh->plink_state == NL80211_PLINK_LISTEN ||
+           sta->mesh->plink_state == NL80211_PLINK_ESTAB) {
                mpl_dbg(sta->sdata,
                        "Ignoring timer for %pM in state %s (timer deleted)",
-                       sta->sta.addr, mplstates[sta->plink_state]);
-               spin_unlock_bh(&sta->plink_lock);
+                       sta->sta.addr, mplstates[sta->mesh->plink_state]);
+               spin_unlock_bh(&sta->mesh->plink_lock);
                return;
        }
 
        mpl_dbg(sta->sdata,
                "Mesh plink timer for %pM fired on state %s\n",
-               sta->sta.addr, mplstates[sta->plink_state]);
+               sta->sta.addr, mplstates[sta->mesh->plink_state]);
        sdata = sta->sdata;
        mshcfg = &sdata->u.mesh.mshcfg;
 
-       switch (sta->plink_state) {
+       switch (sta->mesh->plink_state) {
        case NL80211_PLINK_OPN_RCVD:
        case NL80211_PLINK_OPN_SNT:
                /* retry timer */
-               if (sta->plink_retries < mshcfg->dot11MeshMaxRetries) {
+               if (sta->mesh->plink_retries < mshcfg->dot11MeshMaxRetries) {
                        u32 rand;
                        mpl_dbg(sta->sdata,
                                "Mesh plink for %pM (retry, timeout): %d %d\n",
-                               sta->sta.addr, sta->plink_retries,
-                               sta->plink_timeout);
+                               sta->sta.addr, sta->mesh->plink_retries,
+                               sta->mesh->plink_timeout);
                        get_random_bytes(&rand, sizeof(u32));
-                       sta->plink_timeout = sta->plink_timeout +
-                                            rand % sta->plink_timeout;
-                       ++sta->plink_retries;
-                       mod_plink_timer(sta, sta->plink_timeout);
+                       sta->mesh->plink_timeout = sta->mesh->plink_timeout +
+                                            rand % sta->mesh->plink_timeout;
+                       ++sta->mesh->plink_retries;
+                       mod_plink_timer(sta, sta->mesh->plink_timeout);
                        action = WLAN_SP_MESH_PEERING_OPEN;
                        break;
                }
@@ -609,31 +641,31 @@ static void mesh_plink_timer(unsigned long data)
                /* confirm timer */
                if (!reason)
                        reason = WLAN_REASON_MESH_CONFIRM_TIMEOUT;
-               sta->plink_state = NL80211_PLINK_HOLDING;
+               sta->mesh->plink_state = NL80211_PLINK_HOLDING;
                mod_plink_timer(sta, mshcfg->dot11MeshHoldingTimeout);
                action = WLAN_SP_MESH_PEERING_CLOSE;
                break;
        case NL80211_PLINK_HOLDING:
                /* holding timer */
-               del_timer(&sta->plink_timer);
+               del_timer(&sta->mesh->plink_timer);
                mesh_plink_fsm_restart(sta);
                break;
        default:
                break;
        }
-       spin_unlock_bh(&sta->plink_lock);
+       spin_unlock_bh(&sta->mesh->plink_lock);
        if (action)
-               mesh_plink_frame_tx(sdata, action, sta->sta.addr,
-                                   sta->llid, sta->plid, reason);
+               mesh_plink_frame_tx(sdata, sta, action, sta->sta.addr,
+                                   sta->mesh->llid, sta->mesh->plid, reason);
 }
 
 static inline void mesh_plink_timer_set(struct sta_info *sta, u32 timeout)
 {
-       sta->plink_timer.expires = jiffies + msecs_to_jiffies(timeout);
-       sta->plink_timer.data = (unsigned long) sta;
-       sta->plink_timer.function = mesh_plink_timer;
-       sta->plink_timeout = timeout;
-       add_timer(&sta->plink_timer);
+       sta->mesh->plink_timer.expires = jiffies + msecs_to_jiffies(timeout);
+       sta->mesh->plink_timer.data = (unsigned long) sta;
+       sta->mesh->plink_timer.function = mesh_plink_timer;
+       sta->mesh->plink_timeout = timeout;
+       add_timer(&sta->mesh->plink_timer);
 }
 
 static bool llid_in_use(struct ieee80211_sub_if_data *sdata,
@@ -645,7 +677,7 @@ static bool llid_in_use(struct ieee80211_sub_if_data *sdata,
 
        rcu_read_lock();
        list_for_each_entry_rcu(sta, &local->sta_list, list) {
-               if (!memcmp(&sta->llid, &llid, sizeof(llid))) {
+               if (!memcmp(&sta->mesh->llid, &llid, sizeof(llid))) {
                        in_use = true;
                        break;
                }
@@ -661,8 +693,6 @@ static u16 mesh_get_new_llid(struct ieee80211_sub_if_data *sdata)
 
        do {
                get_random_bytes(&llid, sizeof(llid));
-               /* for mesh PS we still only have the AID range for TIM bits */
-               llid = (llid % IEEE80211_MAX_AID) + 1;
        } while (llid_in_use(sdata, llid));
 
        return llid;
@@ -676,16 +706,16 @@ u32 mesh_plink_open(struct sta_info *sta)
        if (!test_sta_flag(sta, WLAN_STA_AUTH))
                return 0;
 
-       spin_lock_bh(&sta->plink_lock);
-       sta->llid = mesh_get_new_llid(sdata);
-       if (sta->plink_state != NL80211_PLINK_LISTEN &&
-           sta->plink_state != NL80211_PLINK_BLOCKED) {
-               spin_unlock_bh(&sta->plink_lock);
+       spin_lock_bh(&sta->mesh->plink_lock);
+       sta->mesh->llid = mesh_get_new_llid(sdata);
+       if (sta->mesh->plink_state != NL80211_PLINK_LISTEN &&
+           sta->mesh->plink_state != NL80211_PLINK_BLOCKED) {
+               spin_unlock_bh(&sta->mesh->plink_lock);
                return 0;
        }
-       sta->plink_state = NL80211_PLINK_OPN_SNT;
+       sta->mesh->plink_state = NL80211_PLINK_OPN_SNT;
        mesh_plink_timer_set(sta, sdata->u.mesh.mshcfg.dot11MeshRetryTimeout);
-       spin_unlock_bh(&sta->plink_lock);
+       spin_unlock_bh(&sta->mesh->plink_lock);
        mpl_dbg(sdata,
                "Mesh plink: starting establishment with %pM\n",
                sta->sta.addr);
@@ -693,8 +723,8 @@ u32 mesh_plink_open(struct sta_info *sta)
        /* set the non-peer mode to active during peering */
        changed = ieee80211_mps_local_status_update(sdata);
 
-       mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_OPEN,
-                           sta->sta.addr, sta->llid, 0, 0);
+       mesh_plink_frame_tx(sdata, sta, WLAN_SP_MESH_PEERING_OPEN,
+                           sta->sta.addr, sta->mesh->llid, 0, 0);
        return changed;
 }
 
@@ -702,10 +732,10 @@ u32 mesh_plink_block(struct sta_info *sta)
 {
        u32 changed;
 
-       spin_lock_bh(&sta->plink_lock);
+       spin_lock_bh(&sta->mesh->plink_lock);
        changed = __mesh_plink_deactivate(sta);
-       sta->plink_state = NL80211_PLINK_BLOCKED;
-       spin_unlock_bh(&sta->plink_lock);
+       sta->mesh->plink_state = NL80211_PLINK_BLOCKED;
+       spin_unlock_bh(&sta->mesh->plink_lock);
 
        return changed;
 }
@@ -715,12 +745,11 @@ static void mesh_plink_close(struct ieee80211_sub_if_data *sdata,
                             enum plink_event event)
 {
        struct mesh_config *mshcfg = &sdata->u.mesh.mshcfg;
-
        u16 reason = (event == CLS_ACPT) ?
                     WLAN_REASON_MESH_CLOSE : WLAN_REASON_MESH_CONFIG;
 
-       sta->reason = reason;
-       sta->plink_state = NL80211_PLINK_HOLDING;
+       sta->mesh->reason = reason;
+       sta->mesh->plink_state = NL80211_PLINK_HOLDING;
        mod_plink_timer(sta, mshcfg->dot11MeshHoldingTimeout);
 }
 
@@ -730,8 +759,8 @@ static u32 mesh_plink_establish(struct ieee80211_sub_if_data *sdata,
        struct mesh_config *mshcfg = &sdata->u.mesh.mshcfg;
        u32 changed = 0;
 
-       del_timer(&sta->plink_timer);
-       sta->plink_state = NL80211_PLINK_ESTAB;
+       del_timer(&sta->mesh->plink_timer);
+       sta->mesh->plink_state = NL80211_PLINK_ESTAB;
        changed |= mesh_plink_inc_estab_count(sdata);
        changed |= mesh_set_ht_prot_mode(sdata);
        changed |= mesh_set_short_slot_time(sdata);
@@ -758,18 +787,18 @@ static u32 mesh_plink_fsm(struct ieee80211_sub_if_data *sdata,
        u32 changed = 0;
 
        mpl_dbg(sdata, "peer %pM in state %s got event %s\n", sta->sta.addr,
-               mplstates[sta->plink_state], mplevents[event]);
+               mplstates[sta->mesh->plink_state], mplevents[event]);
 
-       spin_lock_bh(&sta->plink_lock);
-       switch (sta->plink_state) {
+       spin_lock_bh(&sta->mesh->plink_lock);
+       switch (sta->mesh->plink_state) {
        case NL80211_PLINK_LISTEN:
                switch (event) {
                case CLS_ACPT:
                        mesh_plink_fsm_restart(sta);
                        break;
                case OPN_ACPT:
-                       sta->plink_state = NL80211_PLINK_OPN_RCVD;
-                       sta->llid = mesh_get_new_llid(sdata);
+                       sta->mesh->plink_state = NL80211_PLINK_OPN_RCVD;
+                       sta->mesh->llid = mesh_get_new_llid(sdata);
                        mesh_plink_timer_set(sta,
                                             mshcfg->dot11MeshRetryTimeout);
 
@@ -791,11 +820,11 @@ static u32 mesh_plink_fsm(struct ieee80211_sub_if_data *sdata,
                        break;
                case OPN_ACPT:
                        /* retry timer is left untouched */
-                       sta->plink_state = NL80211_PLINK_OPN_RCVD;
+                       sta->mesh->plink_state = NL80211_PLINK_OPN_RCVD;
                        action = WLAN_SP_MESH_PEERING_CONFIRM;
                        break;
                case CNF_ACPT:
-                       sta->plink_state = NL80211_PLINK_CNF_RCVD;
+                       sta->mesh->plink_state = NL80211_PLINK_CNF_RCVD;
                        mod_plink_timer(sta, mshcfg->dot11MeshConfirmTimeout);
                        break;
                default:
@@ -855,7 +884,7 @@ static u32 mesh_plink_fsm(struct ieee80211_sub_if_data *sdata,
        case NL80211_PLINK_HOLDING:
                switch (event) {
                case CLS_ACPT:
-                       del_timer(&sta->plink_timer);
+                       del_timer(&sta->mesh->plink_timer);
                        mesh_plink_fsm_restart(sta);
                        break;
                case OPN_ACPT:
@@ -874,17 +903,18 @@ static u32 mesh_plink_fsm(struct ieee80211_sub_if_data *sdata,
                 */
                break;
        }
-       spin_unlock_bh(&sta->plink_lock);
+       spin_unlock_bh(&sta->mesh->plink_lock);
        if (action) {
-               mesh_plink_frame_tx(sdata, action, sta->sta.addr,
-                                   sta->llid, sta->plid, sta->reason);
+               mesh_plink_frame_tx(sdata, sta, action, sta->sta.addr,
+                                   sta->mesh->llid, sta->mesh->plid,
+                                   sta->mesh->reason);
 
                /* also send confirm in open case */
                if (action == WLAN_SP_MESH_PEERING_OPEN) {
-                       mesh_plink_frame_tx(sdata,
+                       mesh_plink_frame_tx(sdata, sta,
                                            WLAN_SP_MESH_PEERING_CONFIRM,
-                                           sta->sta.addr, sta->llid,
-                                           sta->plid, 0);
+                                           sta->sta.addr, sta->mesh->llid,
+                                           sta->mesh->plid, 0);
                }
        }
 
@@ -939,7 +969,7 @@ mesh_plink_get_event(struct ieee80211_sub_if_data *sdata,
                        mpl_dbg(sdata, "Mesh plink: Action frame from non-authed peer\n");
                        goto out;
                }
-               if (sta->plink_state == NL80211_PLINK_BLOCKED)
+               if (sta->mesh->plink_state == NL80211_PLINK_BLOCKED)
                        goto out;
        }
 
@@ -954,7 +984,7 @@ mesh_plink_get_event(struct ieee80211_sub_if_data *sdata,
                if (!matches_local)
                        event = OPN_RJCT;
                if (!mesh_plink_free_count(sdata) ||
-                   (sta->plid && sta->plid != plid))
+                   (sta->mesh->plid && sta->mesh->plid != plid))
                        event = OPN_IGNR;
                else
                        event = OPN_ACPT;
@@ -963,14 +993,14 @@ mesh_plink_get_event(struct ieee80211_sub_if_data *sdata,
                if (!matches_local)
                        event = CNF_RJCT;
                if (!mesh_plink_free_count(sdata) ||
-                   sta->llid != llid ||
-                   (sta->plid && sta->plid != plid))
+                   sta->mesh->llid != llid ||
+                   (sta->mesh->plid && sta->mesh->plid != plid))
                        event = CNF_IGNR;
                else
                        event = CNF_ACPT;
                break;
        case WLAN_SP_MESH_PEERING_CLOSE:
-               if (sta->plink_state == NL80211_PLINK_ESTAB)
+               if (sta->mesh->plink_state == NL80211_PLINK_ESTAB)
                        /* Do not check for llid or plid. This does not
                         * follow the standard but since multiple plinks
                         * per sta are not supported, it is necessary in
@@ -981,9 +1011,9 @@ mesh_plink_get_event(struct ieee80211_sub_if_data *sdata,
                         * restarted.
                         */
                        event = CLS_ACPT;
-               else if (sta->plid != plid)
+               else if (sta->mesh->plid != plid)
                        event = CLS_IGNR;
-               else if (ie_len == 8 && sta->llid != llid)
+               else if (ie_len == 8 && sta->mesh->llid != llid)
                        event = CLS_IGNR;
                else
                        event = CLS_ACPT;
@@ -1070,9 +1100,9 @@ mesh_process_plink_frame(struct ieee80211_sub_if_data *sdata,
                        mpl_dbg(sdata, "Mesh plink: failed to init peer!\n");
                        goto unlock_rcu;
                }
-               sta->plid = plid;
+               sta->mesh->plid = plid;
        } else if (!sta && event == OPN_RJCT) {
-               mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE,
+               mesh_plink_frame_tx(sdata, NULL, WLAN_SP_MESH_PEERING_CLOSE,
                                    mgmt->sa, 0, plid,
                                    WLAN_REASON_MESH_CONFIG);
                goto unlock_rcu;
@@ -1081,9 +1111,13 @@ mesh_process_plink_frame(struct ieee80211_sub_if_data *sdata,
                goto unlock_rcu;
        }
 
-       /* 802.11-2012 13.3.7.2 - update plid on CNF if not set */
-       if (!sta->plid && event == CNF_ACPT)
-               sta->plid = plid;
+       if (event == CNF_ACPT) {
+               /* 802.11-2012 13.3.7.2 - update plid on CNF if not set */
+               if (!sta->mesh->plid)
+                       sta->mesh->plid = plid;
+
+               sta->mesh->aid = get_unaligned_le16(PLINK_CNF_AID(mgmt));
+       }
 
        changed |= mesh_plink_fsm(sdata, sta, event);
 
index ad8b377b4b9f6cfa5d5e222a3c0aff169f4d72d1..90a268abea171aebc5d5907929db0804a2b0f7b0 100644 (file)
@@ -92,16 +92,16 @@ u32 ieee80211_mps_local_status_update(struct ieee80211_sub_if_data *sdata)
                if (sdata != sta->sdata)
                        continue;
 
-               switch (sta->plink_state) {
+               switch (sta->mesh->plink_state) {
                case NL80211_PLINK_OPN_SNT:
                case NL80211_PLINK_OPN_RCVD:
                case NL80211_PLINK_CNF_RCVD:
                        peering = true;
                        break;
                case NL80211_PLINK_ESTAB:
-                       if (sta->local_pm == NL80211_MESH_POWER_LIGHT_SLEEP)
+                       if (sta->mesh->local_pm == NL80211_MESH_POWER_LIGHT_SLEEP)
                                light_sleep_cnt++;
-                       else if (sta->local_pm == NL80211_MESH_POWER_DEEP_SLEEP)
+                       else if (sta->mesh->local_pm == NL80211_MESH_POWER_DEEP_SLEEP)
                                deep_sleep_cnt++;
                        break;
                default:
@@ -153,19 +153,19 @@ u32 ieee80211_mps_set_sta_local_pm(struct sta_info *sta,
 {
        struct ieee80211_sub_if_data *sdata = sta->sdata;
 
-       if (sta->local_pm == pm)
+       if (sta->mesh->local_pm == pm)
                return 0;
 
        mps_dbg(sdata, "local STA operates in mode %d with %pM\n",
                pm, sta->sta.addr);
 
-       sta->local_pm = pm;
+       sta->mesh->local_pm = pm;
 
        /*
         * announce peer-specific power mode transition
         * (see IEEE802.11-2012 13.14.3.2 and 13.14.3.3)
         */
-       if (sta->plink_state == NL80211_PLINK_ESTAB)
+       if (sta->mesh->plink_state == NL80211_PLINK_ESTAB)
                mps_qos_null_tx(sta);
 
        return ieee80211_mps_local_status_update(sdata);
@@ -197,8 +197,8 @@ void ieee80211_mps_set_frame_flags(struct ieee80211_sub_if_data *sdata,
 
        if (is_unicast_ether_addr(hdr->addr1) &&
            ieee80211_is_data_qos(hdr->frame_control) &&
-           sta->plink_state == NL80211_PLINK_ESTAB)
-               pm = sta->local_pm;
+           sta->mesh->plink_state == NL80211_PLINK_ESTAB)
+               pm = sta->mesh->local_pm;
        else
                pm = sdata->u.mesh.nonpeer_pm;
 
@@ -241,16 +241,16 @@ void ieee80211_mps_sta_status_update(struct sta_info *sta)
         * use peer-specific power mode if peering is established and the
         * peer's power mode is known
         */
-       if (sta->plink_state == NL80211_PLINK_ESTAB &&
-           sta->peer_pm != NL80211_MESH_POWER_UNKNOWN)
-               pm = sta->peer_pm;
+       if (sta->mesh->plink_state == NL80211_PLINK_ESTAB &&
+           sta->mesh->peer_pm != NL80211_MESH_POWER_UNKNOWN)
+               pm = sta->mesh->peer_pm;
        else
-               pm = sta->nonpeer_pm;
+               pm = sta->mesh->nonpeer_pm;
 
        do_buffer = (pm != NL80211_MESH_POWER_ACTIVE);
 
        /* clear the MPSP flags for non-peers or active STA */
-       if (sta->plink_state != NL80211_PLINK_ESTAB) {
+       if (sta->mesh->plink_state != NL80211_PLINK_ESTAB) {
                clear_sta_flag(sta, WLAN_STA_MPSP_OWNER);
                clear_sta_flag(sta, WLAN_STA_MPSP_RECIPIENT);
        } else if (!do_buffer) {
@@ -296,13 +296,13 @@ static void mps_set_sta_peer_pm(struct sta_info *sta,
                pm = NL80211_MESH_POWER_ACTIVE;
        }
 
-       if (sta->peer_pm == pm)
+       if (sta->mesh->peer_pm == pm)
                return;
 
        mps_dbg(sta->sdata, "STA %pM enters mode %d\n",
                sta->sta.addr, pm);
 
-       sta->peer_pm = pm;
+       sta->mesh->peer_pm = pm;
 
        ieee80211_mps_sta_status_update(sta);
 }
@@ -317,13 +317,13 @@ static void mps_set_sta_nonpeer_pm(struct sta_info *sta,
        else
                pm = NL80211_MESH_POWER_ACTIVE;
 
-       if (sta->nonpeer_pm == pm)
+       if (sta->mesh->nonpeer_pm == pm)
                return;
 
        mps_dbg(sta->sdata, "STA %pM sets non-peer mode to %d\n",
                sta->sta.addr, pm);
 
-       sta->nonpeer_pm = pm;
+       sta->mesh->nonpeer_pm = pm;
 
        ieee80211_mps_sta_status_update(sta);
 }
@@ -552,7 +552,7 @@ void ieee80211_mpsp_trigger_process(u8 *qc, struct sta_info *sta,
        } else {
                if (eosp)
                        clear_sta_flag(sta, WLAN_STA_MPSP_RECIPIENT);
-               else if (sta->local_pm != NL80211_MESH_POWER_ACTIVE)
+               else if (sta->mesh->local_pm != NL80211_MESH_POWER_ACTIVE)
                        set_sta_flag(sta, WLAN_STA_MPSP_RECIPIENT);
 
                if (rspi && !test_and_set_sta_flag(sta, WLAN_STA_MPSP_OWNER))
@@ -577,9 +577,9 @@ void ieee80211_mps_frame_release(struct sta_info *sta,
        int ac, buffer_local = 0;
        bool has_buffered = false;
 
-       if (sta->plink_state == NL80211_PLINK_ESTAB)
+       if (sta->mesh->plink_state == NL80211_PLINK_ESTAB)
                has_buffered = ieee80211_check_tim(elems->tim, elems->tim_len,
-                                                  sta->llid);
+                                                  sta->mesh->aid);
 
        if (has_buffered)
                mps_dbg(sta->sdata, "%pM indicates buffered frames\n",
@@ -598,7 +598,7 @@ void ieee80211_mps_frame_release(struct sta_info *sta,
        if (!has_buffered && !buffer_local)
                return;
 
-       if (sta->plink_state == NL80211_PLINK_ESTAB)
+       if (sta->mesh->plink_state == NL80211_PLINK_ESTAB)
                mpsp_trigger_send(sta, has_buffered, !buffer_local);
        else
                mps_frame_deliver(sta, 1);
index 09625d6205c31418edba53a62ee027245f232050..64bc22ad94965c4615eaaa94aae02b45891204e7 100644 (file)
@@ -127,14 +127,14 @@ static void mesh_sync_offset_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
 
        /* Timing offset calculation (see 13.13.2.2.2) */
        t_t = le64_to_cpu(mgmt->u.beacon.timestamp);
-       sta->t_offset = t_t - t_r;
+       sta->mesh->t_offset = t_t - t_r;
 
        if (test_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN)) {
-               s64 t_clockdrift = sta->t_offset_setpoint - sta->t_offset;
+               s64 t_clockdrift = sta->mesh->t_offset_setpoint - sta->mesh->t_offset;
                msync_dbg(sdata,
-                         "STA %pM : sta->t_offset=%lld, sta->t_offset_setpoint=%lld, t_clockdrift=%lld\n",
-                         sta->sta.addr, (long long) sta->t_offset,
-                         (long long) sta->t_offset_setpoint,
+                         "STA %pM : t_offset=%lld, t_offset_setpoint=%lld, t_clockdrift=%lld\n",
+                         sta->sta.addr, (long long) sta->mesh->t_offset,
+                         (long long) sta->mesh->t_offset_setpoint,
                          (long long) t_clockdrift);
 
                if (t_clockdrift > TOFFSET_MAXIMUM_ADJUSTMENT ||
@@ -152,12 +152,12 @@ static void mesh_sync_offset_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
                        ifmsh->sync_offset_clockdrift_max = t_clockdrift;
                spin_unlock_bh(&ifmsh->sync_offset_lock);
        } else {
-               sta->t_offset_setpoint = sta->t_offset - TOFFSET_SET_MARGIN;
+               sta->mesh->t_offset_setpoint = sta->mesh->t_offset - TOFFSET_SET_MARGIN;
                set_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN);
                msync_dbg(sdata,
-                         "STA %pM : offset was invalid, sta->t_offset=%lld\n",
+                         "STA %pM : offset was invalid, t_offset=%lld\n",
                          sta->sta.addr,
-                         (long long) sta->t_offset);
+                         (long long) sta->mesh->t_offset);
        }
 
 no_sync:
index 9b2cc278ac2afc60920ebec3083bebc35c497b61..705ef1d040edfb70042fdd9cd25f050b19dab4c0 100644 (file)
@@ -6,6 +6,7 @@
  * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
  * Copyright 2007, Michael Wu <flamingice@sourmilk.net>
  * 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
@@ -538,11 +539,16 @@ static void ieee80211_add_ht_ie(struct ieee80211_sub_if_data *sdata,
        ieee80211_ie_build_ht_cap(pos, &ht_cap, cap);
 }
 
+/* This function determines vht capability flags for the association
+ * and builds the IE.
+ * Note - the function may set the owner of the MU-MIMO capability
+ */
 static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata,
                                 struct sk_buff *skb,
                                 struct ieee80211_supported_band *sband,
                                 struct ieee80211_vht_cap *ap_vht_cap)
 {
+       struct ieee80211_local *local = sdata->local;
        u8 *pos;
        u32 cap;
        struct ieee80211_sta_vht_cap vht_cap;
@@ -576,7 +582,34 @@ static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata,
         */
        if (!(ap_vht_cap->vht_cap_info &
                        cpu_to_le32(IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE)))
-               cap &= ~IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
+               cap &= ~(IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE |
+                        IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE);
+       else if (!(ap_vht_cap->vht_cap_info &
+                       cpu_to_le32(IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE)))
+               cap &= ~IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
+
+       /*
+        * If some other vif is using the MU-MIMO capablity we cannot associate
+        * using MU-MIMO - this will lead to contradictions in the group-id
+        * mechanism.
+        * Ownership is defined since association request, in order to avoid
+        * simultaneous associations with MU-MIMO.
+        */
+       if (cap & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE) {
+               bool disable_mu_mimo = false;
+               struct ieee80211_sub_if_data *other;
+
+               list_for_each_entry_rcu(other, &local->interfaces, list) {
+                       if (other->flags & IEEE80211_SDATA_MU_MIMO_OWNER) {
+                               disable_mu_mimo = true;
+                               break;
+                       }
+               }
+               if (disable_mu_mimo)
+                       cap &= ~IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
+               else
+                       sdata->flags |= IEEE80211_SDATA_MU_MIMO_OWNER;
+       }
 
        mask = IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK;
 
@@ -1096,24 +1129,6 @@ static void ieee80211_chswitch_timer(unsigned long data)
        ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.chswitch_work);
 }
 
-static void ieee80211_teardown_tdls_peers(struct ieee80211_sub_if_data *sdata)
-{
-       struct sta_info *sta;
-       u16 reason = WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED;
-
-       rcu_read_lock();
-       list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) {
-               if (!sta->sta.tdls || sta->sdata != sdata || !sta->uploaded ||
-                   !test_sta_flag(sta, WLAN_STA_AUTHORIZED))
-                       continue;
-
-               ieee80211_tdls_oper_request(&sdata->vif, sta->sta.addr,
-                                           NL80211_TDLS_TEARDOWN, reason,
-                                           GFP_ATOMIC);
-       }
-       rcu_read_unlock();
-}
-
 static void
 ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
                                 u64 timestamp, u32 device_timestamp,
@@ -2076,6 +2091,7 @@ 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));
+       sdata->flags &= ~IEEE80211_SDATA_MU_MIMO_OWNER;
 
        sdata->ap_power_level = IEEE80211_UNSET_POWER_LEVEL;
 
@@ -2538,6 +2554,7 @@ static void ieee80211_destroy_assoc_data(struct ieee80211_sub_if_data *sdata,
                eth_zero_addr(sdata->u.mgd.bssid);
                ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID);
                sdata->u.mgd.flags = 0;
+               sdata->flags &= ~IEEE80211_SDATA_MU_MIMO_OWNER;
                mutex_lock(&sdata->local->mtx);
                ieee80211_vif_release_channel(sdata);
                mutex_unlock(&sdata->local->mtx);
@@ -3034,12 +3051,8 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
 
        rate_control_rate_init(sta);
 
-       if (ifmgd->flags & IEEE80211_STA_MFP_ENABLED) {
+       if (ifmgd->flags & IEEE80211_STA_MFP_ENABLED)
                set_sta_flag(sta, WLAN_STA_MFP);
-               sta->sta.mfp = true;
-       } else {
-               sta->sta.mfp = false;
-       }
 
        sta->sta.wme = elems.wmm_param && local->hw.queues >= IEEE80211_NUM_ACS;
 
index 358d5f9d820788acef2f439dee6ac88250724edc..573b81a1fb2d882487f75ccc24493cc1b9deee5e 100644 (file)
@@ -179,7 +179,7 @@ int ieee80211_ocb_join(struct ieee80211_sub_if_data *sdata,
 {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_if_ocb *ifocb = &sdata->u.ocb;
-       u32 changed = BSS_CHANGED_OCB;
+       u32 changed = BSS_CHANGED_OCB | BSS_CHANGED_BSSID;
        int err;
 
        if (ifocb->joined == true)
index fda33f961d83ce44f05ab3298113877d01768fb2..9857693b91ec721ff71e3f3cd1087ccc289912e1 100644 (file)
@@ -29,6 +29,65 @@ module_param(ieee80211_default_rc_algo, charp, 0644);
 MODULE_PARM_DESC(ieee80211_default_rc_algo,
                 "Default rate control algorithm for mac80211 to use");
 
+void rate_control_rate_init(struct sta_info *sta)
+{
+       struct ieee80211_local *local = sta->sdata->local;
+       struct rate_control_ref *ref = sta->rate_ctrl;
+       struct ieee80211_sta *ista = &sta->sta;
+       void *priv_sta = sta->rate_ctrl_priv;
+       struct ieee80211_supported_band *sband;
+       struct ieee80211_chanctx_conf *chanctx_conf;
+
+       ieee80211_sta_set_rx_nss(sta);
+
+       if (!ref)
+               return;
+
+       rcu_read_lock();
+
+       chanctx_conf = rcu_dereference(sta->sdata->vif.chanctx_conf);
+       if (WARN_ON(!chanctx_conf)) {
+               rcu_read_unlock();
+               return;
+       }
+
+       sband = local->hw.wiphy->bands[chanctx_conf->def.chan->band];
+
+       spin_lock_bh(&sta->rate_ctrl_lock);
+       ref->ops->rate_init(ref->priv, sband, &chanctx_conf->def, ista,
+                           priv_sta);
+       spin_unlock_bh(&sta->rate_ctrl_lock);
+       rcu_read_unlock();
+       set_sta_flag(sta, WLAN_STA_RATE_CONTROL);
+}
+
+void rate_control_rate_update(struct ieee80211_local *local,
+                                   struct ieee80211_supported_band *sband,
+                                   struct sta_info *sta, u32 changed)
+{
+       struct rate_control_ref *ref = local->rate_ctrl;
+       struct ieee80211_sta *ista = &sta->sta;
+       void *priv_sta = sta->rate_ctrl_priv;
+       struct ieee80211_chanctx_conf *chanctx_conf;
+
+       if (ref && ref->ops->rate_update) {
+               rcu_read_lock();
+
+               chanctx_conf = rcu_dereference(sta->sdata->vif.chanctx_conf);
+               if (WARN_ON(!chanctx_conf)) {
+                       rcu_read_unlock();
+                       return;
+               }
+
+               spin_lock_bh(&sta->rate_ctrl_lock);
+               ref->ops->rate_update(ref->priv, sband, &chanctx_conf->def,
+                                     ista, priv_sta, changed);
+               spin_unlock_bh(&sta->rate_ctrl_lock);
+               rcu_read_unlock();
+       }
+       drv_sta_rc_update(local, sta->sdata, &sta->sta, changed);
+}
+
 int ieee80211_rate_control_register(const struct rate_control_ops *ops)
 {
        struct rate_control_alg *alg;
@@ -294,39 +353,37 @@ bool rate_control_send_low(struct ieee80211_sta *pubsta,
 }
 EXPORT_SYMBOL(rate_control_send_low);
 
-static bool rate_idx_match_legacy_mask(struct ieee80211_tx_rate *rate,
-                                      int n_bitrates, u32 mask)
+static bool rate_idx_match_legacy_mask(s8 *rate_idx, int n_bitrates, u32 mask)
 {
        int j;
 
        /* See whether the selected rate or anything below it is allowed. */
-       for (j = rate->idx; j >= 0; j--) {
+       for (j = *rate_idx; j >= 0; j--) {
                if (mask & (1 << j)) {
                        /* Okay, found a suitable rate. Use it. */
-                       rate->idx = j;
+                       *rate_idx = j;
                        return true;
                }
        }
 
        /* Try to find a higher rate that would be allowed */
-       for (j = rate->idx + 1; j < n_bitrates; j++) {
+       for (j = *rate_idx + 1; j < n_bitrates; j++) {
                if (mask & (1 << j)) {
                        /* Okay, found a suitable rate. Use it. */
-                       rate->idx = j;
+                       *rate_idx = j;
                        return true;
                }
        }
        return false;
 }
 
-static bool rate_idx_match_mcs_mask(struct ieee80211_tx_rate *rate,
-                                   u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN])
+static bool rate_idx_match_mcs_mask(s8 *rate_idx, u8 *mcs_mask)
 {
        int i, j;
        int ridx, rbit;
 
-       ridx = rate->idx / 8;
-       rbit = rate->idx % 8;
+       ridx = *rate_idx / 8;
+       rbit = *rate_idx % 8;
 
        /* sanity check */
        if (ridx < 0 || ridx >= IEEE80211_HT_MCS_MASK_LEN)
@@ -336,20 +393,20 @@ static bool rate_idx_match_mcs_mask(struct ieee80211_tx_rate *rate,
        for (i = ridx; i >= 0; i--) {
                for (j = rbit; j >= 0; j--)
                        if (mcs_mask[i] & BIT(j)) {
-                               rate->idx = i * 8 + j;
+                               *rate_idx = i * 8 + j;
                                return true;
                        }
                rbit = 7;
        }
 
        /* Try to find a higher rate that would be allowed */
-       ridx = (rate->idx + 1) / 8;
-       rbit = (rate->idx + 1) % 8;
+       ridx = (*rate_idx + 1) / 8;
+       rbit = (*rate_idx + 1) % 8;
 
        for (i = ridx; i < IEEE80211_HT_MCS_MASK_LEN; i++) {
                for (j = rbit; j < 8; j++)
                        if (mcs_mask[i] & BIT(j)) {
-                               rate->idx = i * 8 + j;
+                               *rate_idx = i * 8 + j;
                                return true;
                        }
                rbit = 0;
@@ -357,37 +414,93 @@ static bool rate_idx_match_mcs_mask(struct ieee80211_tx_rate *rate,
        return false;
 }
 
+static bool rate_idx_match_vht_mcs_mask(s8 *rate_idx, u16 *vht_mask)
+{
+       int i, j;
+       int ridx, rbit;
+
+       ridx = *rate_idx >> 4;
+       rbit = *rate_idx & 0xf;
+
+       if (ridx < 0 || ridx >= NL80211_VHT_NSS_MAX)
+               return false;
+
+       /* See whether the selected rate or anything below it is allowed. */
+       for (i = ridx; i >= 0; i--) {
+               for (j = rbit; j >= 0; j--) {
+                       if (vht_mask[i] & BIT(j)) {
+                               *rate_idx = (i << 4) | j;
+                               return true;
+                       }
+               }
+               rbit = 15;
+       }
 
+       /* Try to find a higher rate that would be allowed */
+       ridx = (*rate_idx + 1) >> 4;
+       rbit = (*rate_idx + 1) & 0xf;
 
-static void rate_idx_match_mask(struct ieee80211_tx_rate *rate,
+       for (i = ridx; i < NL80211_VHT_NSS_MAX; i++) {
+               for (j = rbit; j < 16; j++) {
+                       if (vht_mask[i] & BIT(j)) {
+                               *rate_idx = (i << 4) | j;
+                               return true;
+                       }
+               }
+               rbit = 0;
+       }
+       return false;
+}
+
+static void rate_idx_match_mask(s8 *rate_idx, u16 *rate_flags,
                                struct ieee80211_supported_band *sband,
                                enum nl80211_chan_width chan_width,
                                u32 mask,
-                               u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN])
+                               u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN],
+                               u16 vht_mask[NL80211_VHT_NSS_MAX])
 {
-       struct ieee80211_tx_rate alt_rate;
+       if (*rate_flags & IEEE80211_TX_RC_VHT_MCS) {
+               /* handle VHT rates */
+               if (rate_idx_match_vht_mcs_mask(rate_idx, vht_mask))
+                       return;
+
+               *rate_idx = 0;
+               /* keep protection flags */
+               *rate_flags &= (IEEE80211_TX_RC_USE_RTS_CTS |
+                               IEEE80211_TX_RC_USE_CTS_PROTECT |
+                               IEEE80211_TX_RC_USE_SHORT_PREAMBLE);
 
-       /* handle HT rates */
-       if (rate->flags & IEEE80211_TX_RC_MCS) {
-               if (rate_idx_match_mcs_mask(rate, mcs_mask))
+               *rate_flags |= IEEE80211_TX_RC_MCS;
+               if (chan_width == NL80211_CHAN_WIDTH_40)
+                       *rate_flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
+
+               if (rate_idx_match_mcs_mask(rate_idx, mcs_mask))
                        return;
 
                /* also try the legacy rates. */
-               alt_rate.idx = 0;
+               *rate_flags &= ~(IEEE80211_TX_RC_MCS |
+                                IEEE80211_TX_RC_40_MHZ_WIDTH);
+               if (rate_idx_match_legacy_mask(rate_idx, sband->n_bitrates,
+                                              mask))
+                       return;
+       } else if (*rate_flags & IEEE80211_TX_RC_MCS) {
+               /* handle HT rates */
+               if (rate_idx_match_mcs_mask(rate_idx, mcs_mask))
+                       return;
+
+               /* also try the legacy rates. */
+               *rate_idx = 0;
                /* keep protection flags */
-               alt_rate.flags = rate->flags &
-                                (IEEE80211_TX_RC_USE_RTS_CTS |
-                                 IEEE80211_TX_RC_USE_CTS_PROTECT |
-                                 IEEE80211_TX_RC_USE_SHORT_PREAMBLE);
-               alt_rate.count = rate->count;
-               if (rate_idx_match_legacy_mask(&alt_rate,
-                                              sband->n_bitrates, mask)) {
-                       *rate = alt_rate;
+               *rate_flags &= (IEEE80211_TX_RC_USE_RTS_CTS |
+                               IEEE80211_TX_RC_USE_CTS_PROTECT |
+                               IEEE80211_TX_RC_USE_SHORT_PREAMBLE);
+               if (rate_idx_match_legacy_mask(rate_idx, sband->n_bitrates,
+                                              mask))
                        return;
-               }
-       } else if (!(rate->flags & IEEE80211_TX_RC_VHT_MCS)) {
+       } else {
                /* handle legacy rates */
-               if (rate_idx_match_legacy_mask(rate, sband->n_bitrates, mask))
+               if (rate_idx_match_legacy_mask(rate_idx, sband->n_bitrates,
+                                              mask))
                        return;
 
                /* if HT BSS, and we handle a data frame, also try HT rates */
@@ -400,23 +513,19 @@ static void rate_idx_match_mask(struct ieee80211_tx_rate *rate,
                        break;
                }
 
-               alt_rate.idx = 0;
+               *rate_idx = 0;
                /* keep protection flags */
-               alt_rate.flags = rate->flags &
-                                (IEEE80211_TX_RC_USE_RTS_CTS |
-                                 IEEE80211_TX_RC_USE_CTS_PROTECT |
-                                 IEEE80211_TX_RC_USE_SHORT_PREAMBLE);
-               alt_rate.count = rate->count;
+               *rate_flags &= (IEEE80211_TX_RC_USE_RTS_CTS |
+                               IEEE80211_TX_RC_USE_CTS_PROTECT |
+                               IEEE80211_TX_RC_USE_SHORT_PREAMBLE);
 
-               alt_rate.flags |= IEEE80211_TX_RC_MCS;
+               *rate_flags |= IEEE80211_TX_RC_MCS;
 
                if (chan_width == NL80211_CHAN_WIDTH_40)
-                       alt_rate.flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
+                       *rate_flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
 
-               if (rate_idx_match_mcs_mask(&alt_rate, mcs_mask)) {
-                       *rate = alt_rate;
+               if (rate_idx_match_mcs_mask(rate_idx, mcs_mask))
                        return;
-               }
        }
 
        /*
@@ -569,18 +678,92 @@ static void rate_control_fill_sta_table(struct ieee80211_sta *sta,
        }
 }
 
+static bool rate_control_cap_mask(struct ieee80211_sub_if_data *sdata,
+                                 struct ieee80211_supported_band *sband,
+                                 struct ieee80211_sta *sta, u32 *mask,
+                                 u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN],
+                                 u16 vht_mask[NL80211_VHT_NSS_MAX])
+{
+       u32 i, flags;
+
+       *mask = sdata->rc_rateidx_mask[sband->band];
+       flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef);
+       for (i = 0; i < sband->n_bitrates; i++) {
+               if ((flags & sband->bitrates[i].flags) != flags)
+                       *mask &= ~BIT(i);
+       }
+
+       if (*mask == (1 << sband->n_bitrates) - 1 &&
+           !sdata->rc_has_mcs_mask[sband->band] &&
+           !sdata->rc_has_vht_mcs_mask[sband->band])
+               return false;
+
+       if (sdata->rc_has_mcs_mask[sband->band])
+               memcpy(mcs_mask, sdata->rc_rateidx_mcs_mask[sband->band],
+                      IEEE80211_HT_MCS_MASK_LEN);
+       else
+               memset(mcs_mask, 0xff, IEEE80211_HT_MCS_MASK_LEN);
+
+       if (sdata->rc_has_vht_mcs_mask[sband->band])
+               memcpy(vht_mask, sdata->rc_rateidx_vht_mcs_mask[sband->band],
+                      sizeof(u16) * NL80211_VHT_NSS_MAX);
+       else
+               memset(vht_mask, 0xff, sizeof(u16) * NL80211_VHT_NSS_MAX);
+
+       if (sta) {
+               __le16 sta_vht_cap;
+               u16 sta_vht_mask[NL80211_VHT_NSS_MAX];
+
+               /* Filter out rates that the STA does not support */
+               *mask &= sta->supp_rates[sband->band];
+               for (i = 0; i < sizeof(mcs_mask); i++)
+                       mcs_mask[i] &= sta->ht_cap.mcs.rx_mask[i];
+
+               sta_vht_cap = sta->vht_cap.vht_mcs.rx_mcs_map;
+               ieee80211_get_vht_mask_from_cap(sta_vht_cap, sta_vht_mask);
+               for (i = 0; i < NL80211_VHT_NSS_MAX; i++)
+                       vht_mask[i] &= sta_vht_mask[i];
+       }
+
+       return true;
+}
+
+static void
+rate_control_apply_mask_ratetbl(struct sta_info *sta,
+                               struct ieee80211_supported_band *sband,
+                               struct ieee80211_sta_rates *rates)
+{
+       int i;
+       u32 mask;
+       u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN];
+       u16 vht_mask[NL80211_VHT_NSS_MAX];
+       enum nl80211_chan_width chan_width;
+
+       if (!rate_control_cap_mask(sta->sdata, sband, &sta->sta, &mask,
+                                  mcs_mask, vht_mask))
+               return;
+
+       chan_width = sta->sdata->vif.bss_conf.chandef.width;
+       for (i = 0; i < IEEE80211_TX_RATE_TABLE_SIZE; i++) {
+               if (rates->rate[i].idx < 0)
+                       break;
+
+               rate_idx_match_mask(&rates->rate[i].idx, &rates->rate[i].flags,
+                                   sband, chan_width, mask, mcs_mask,
+                                   vht_mask);
+       }
+}
+
 static void rate_control_apply_mask(struct ieee80211_sub_if_data *sdata,
                                    struct ieee80211_sta *sta,
                                    struct ieee80211_supported_band *sband,
-                                   struct ieee80211_tx_info *info,
                                    struct ieee80211_tx_rate *rates,
                                    int max_rates)
 {
        enum nl80211_chan_width chan_width;
        u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN];
-       bool has_mcs_mask;
        u32 mask;
-       u32 rate_flags;
+       u16 rate_flags, vht_mask[NL80211_VHT_NSS_MAX];
        int i;
 
        /*
@@ -588,30 +771,10 @@ static void rate_control_apply_mask(struct ieee80211_sub_if_data *sdata,
         * default mask (allow all rates) is used to save some processing for
         * the common case.
         */
-       mask = sdata->rc_rateidx_mask[info->band];
-       has_mcs_mask = sdata->rc_has_mcs_mask[info->band];
-       rate_flags =
-               ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef);
-       for (i = 0; i < sband->n_bitrates; i++)
-               if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
-                       mask &= ~BIT(i);
-
-       if (mask == (1 << sband->n_bitrates) - 1 && !has_mcs_mask)
+       if (!rate_control_cap_mask(sdata, sband, sta, &mask, mcs_mask,
+                                  vht_mask))
                return;
 
-       if (has_mcs_mask)
-               memcpy(mcs_mask, sdata->rc_rateidx_mcs_mask[info->band],
-                      sizeof(mcs_mask));
-       else
-               memset(mcs_mask, 0xff, sizeof(mcs_mask));
-
-       if (sta) {
-               /* Filter out rates that the STA does not support */
-               mask &= sta->supp_rates[info->band];
-               for (i = 0; i < sizeof(mcs_mask); i++)
-                       mcs_mask[i] &= sta->ht_cap.mcs.rx_mask[i];
-       }
-
        /*
         * Make sure the rate index selected for each TX rate is
         * included in the configured mask and change the rate indexes
@@ -623,8 +786,10 @@ static void rate_control_apply_mask(struct ieee80211_sub_if_data *sdata,
                if (rates[i].idx < 0)
                        break;
 
-               rate_idx_match_mask(&rates[i], sband, chan_width, mask,
-                                   mcs_mask);
+               rate_flags = rates[i].flags;
+               rate_idx_match_mask(&rates[i].idx, &rate_flags, sband,
+                                   chan_width, mask, mcs_mask, vht_mask);
+               rates[i].flags = rate_flags;
        }
 }
 
@@ -648,7 +813,7 @@ void ieee80211_get_tx_rates(struct ieee80211_vif *vif,
        sband = sdata->local->hw.wiphy->bands[info->band];
 
        if (ieee80211_is_data(hdr->frame_control))
-               rate_control_apply_mask(sdata, sta, sband, info, dest, max_rates);
+               rate_control_apply_mask(sdata, sta, sband, dest, max_rates);
 
        if (dest[0].idx < 0)
                __rate_control_send_low(&sdata->local->hw, sband, sta, info,
@@ -705,7 +870,10 @@ int rate_control_set_rates(struct ieee80211_hw *hw,
 {
        struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
        struct ieee80211_sta_rates *old;
+       struct ieee80211_supported_band *sband;
 
+       sband = hw->wiphy->bands[ieee80211_get_sdata_band(sta->sdata)];
+       rate_control_apply_mask_ratetbl(sta, sband, rates);
        /*
         * mac80211 guarantees that this function will not be called
         * concurrently, so the following RCU access is safe, even without
index 25c9be5dd7fd811b32d13c792cf50c661f6f1e44..624fe5b81615e6afa04138cfdb375c8894d133c0 100644 (file)
@@ -71,64 +71,10 @@ rate_control_tx_status_noskb(struct ieee80211_local *local,
        spin_unlock_bh(&sta->rate_ctrl_lock);
 }
 
-static inline void rate_control_rate_init(struct sta_info *sta)
-{
-       struct ieee80211_local *local = sta->sdata->local;
-       struct rate_control_ref *ref = sta->rate_ctrl;
-       struct ieee80211_sta *ista = &sta->sta;
-       void *priv_sta = sta->rate_ctrl_priv;
-       struct ieee80211_supported_band *sband;
-       struct ieee80211_chanctx_conf *chanctx_conf;
-
-       ieee80211_sta_set_rx_nss(sta);
-
-       if (!ref)
-               return;
-
-       rcu_read_lock();
-
-       chanctx_conf = rcu_dereference(sta->sdata->vif.chanctx_conf);
-       if (WARN_ON(!chanctx_conf)) {
-               rcu_read_unlock();
-               return;
-       }
-
-       sband = local->hw.wiphy->bands[chanctx_conf->def.chan->band];
-
-       spin_lock_bh(&sta->rate_ctrl_lock);
-       ref->ops->rate_init(ref->priv, sband, &chanctx_conf->def, ista,
-                           priv_sta);
-       spin_unlock_bh(&sta->rate_ctrl_lock);
-       rcu_read_unlock();
-       set_sta_flag(sta, WLAN_STA_RATE_CONTROL);
-}
-
-static inline void rate_control_rate_update(struct ieee80211_local *local,
+void rate_control_rate_init(struct sta_info *sta);
+void rate_control_rate_update(struct ieee80211_local *local,
                                    struct ieee80211_supported_band *sband,
-                                   struct sta_info *sta, u32 changed)
-{
-       struct rate_control_ref *ref = local->rate_ctrl;
-       struct ieee80211_sta *ista = &sta->sta;
-       void *priv_sta = sta->rate_ctrl_priv;
-       struct ieee80211_chanctx_conf *chanctx_conf;
-
-       if (ref && ref->ops->rate_update) {
-               rcu_read_lock();
-
-               chanctx_conf = rcu_dereference(sta->sdata->vif.chanctx_conf);
-               if (WARN_ON(!chanctx_conf)) {
-                       rcu_read_unlock();
-                       return;
-               }
-
-               spin_lock_bh(&sta->rate_ctrl_lock);
-               ref->ops->rate_update(ref->priv, sband, &chanctx_conf->def,
-                                     ista, priv_sta, changed);
-               spin_unlock_bh(&sta->rate_ctrl_lock);
-               rcu_read_unlock();
-       }
-       drv_sta_rc_update(local, sta->sdata, &sta->sta, changed);
-}
+                                   struct sta_info *sta, u32 changed);
 
 static inline void *rate_control_alloc_sta(struct rate_control_ref *ref,
                                           struct sta_info *sta, gfp_t gfp)
index 543b672335353817334b407fbfc1ff3574625ee6..3928dbd24e257e68627aa977cc54a19aaa996339 100644 (file)
@@ -867,7 +867,13 @@ minstrel_ht_set_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
        else
                idx = index % MCS_GROUP_RATES + (group->streams - 1) * 8;
 
-       if (offset > 0) {
+       /* enable RTS/CTS if needed:
+        *  - if station is in dynamic SMPS (and streams > 1)
+        *  - for fallback rates, to increase chances of getting through
+        */
+       if (offset > 0 &&
+           (mi->sta->smps_mode == IEEE80211_SMPS_DYNAMIC &&
+            group->streams > 1)) {
                ratetbl->rate[offset].count = ratetbl->rate[offset].count_rts;
                flags |= IEEE80211_TX_RC_USE_RTS_CTS;
        }
index 5dae166cb7f56b7cb9d9097496db9e8fa8d755ce..5bc0b88d9eb1331a0dd47aab05ffa01c7a0bc845 100644 (file)
@@ -42,6 +42,51 @@ static inline void ieee80211_rx_stats(struct net_device *dev, u32 len)
        u64_stats_update_end(&tstats->syncp);
 }
 
+static u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len,
+                              enum nl80211_iftype type)
+{
+       __le16 fc = hdr->frame_control;
+
+       if (ieee80211_is_data(fc)) {
+               if (len < 24) /* drop incorrect hdr len (data) */
+                       return NULL;
+
+               if (ieee80211_has_a4(fc))
+                       return NULL;
+               if (ieee80211_has_tods(fc))
+                       return hdr->addr1;
+               if (ieee80211_has_fromds(fc))
+                       return hdr->addr2;
+
+               return hdr->addr3;
+       }
+
+       if (ieee80211_is_mgmt(fc)) {
+               if (len < 24) /* drop incorrect hdr len (mgmt) */
+                       return NULL;
+               return hdr->addr3;
+       }
+
+       if (ieee80211_is_ctl(fc)) {
+               if (ieee80211_is_pspoll(fc))
+                       return hdr->addr1;
+
+               if (ieee80211_is_back_req(fc)) {
+                       switch (type) {
+                       case NL80211_IFTYPE_STATION:
+                               return hdr->addr2;
+                       case NL80211_IFTYPE_AP:
+                       case NL80211_IFTYPE_AP_VLAN:
+                               return hdr->addr1;
+                       default:
+                               break; /* fall through to the return */
+                       }
+               }
+       }
+
+       return NULL;
+}
+
 /*
  * monitor mode reception
  *
@@ -77,8 +122,7 @@ 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_AMPDU_IS_ZEROLEN))
+                           RX_FLAG_FAILED_PLCP_CRC))
                return true;
 
        if (unlikely(skb->len < 16 + present_fcs_len + rtap_vendor_space))
@@ -346,10 +390,6 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
                        cpu_to_le32(1 << IEEE80211_RADIOTAP_AMPDU_STATUS);
                put_unaligned_le32(status->ampdu_reference, pos);
                pos += 4;
-               if (status->flag & RX_FLAG_AMPDU_REPORT_ZEROLEN)
-                       flags |= IEEE80211_RADIOTAP_AMPDU_REPORT_ZEROLEN;
-               if (status->flag & RX_FLAG_AMPDU_IS_ZEROLEN)
-                       flags |= IEEE80211_RADIOTAP_AMPDU_IS_ZEROLEN;
                if (status->flag & RX_FLAG_AMPDU_LAST_KNOWN)
                        flags |= IEEE80211_RADIOTAP_AMPDU_LAST_KNOWN;
                if (status->flag & RX_FLAG_AMPDU_IS_LAST)
@@ -1093,11 +1133,6 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx)
 {
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
 
-       if (unlikely(rx->skb->len < 16)) {
-               I802_DEBUG_INC(rx->local->rx_handlers_drop_short);
-               return RX_DROP_MONITOR;
-       }
-
        /* Drop disallowed frame classes based on STA auth/assoc state;
         * IEEE 802.11, Chap 5.5.
         *
@@ -1240,22 +1275,22 @@ static void sta_ps_end(struct sta_info *sta)
        ieee80211_sta_ps_deliver_wakeup(sta);
 }
 
-int ieee80211_sta_ps_transition(struct ieee80211_sta *sta, bool start)
+int ieee80211_sta_ps_transition(struct ieee80211_sta *pubsta, bool start)
 {
-       struct sta_info *sta_inf = container_of(sta, struct sta_info, sta);
+       struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
        bool in_ps;
 
-       WARN_ON(!ieee80211_hw_check(&sta_inf->local->hw, AP_LINK_PS));
+       WARN_ON(!ieee80211_hw_check(&sta->local->hw, AP_LINK_PS));
 
        /* Don't let the same PS state be set twice */
-       in_ps = test_sta_flag(sta_inf, WLAN_STA_PS_STA);
+       in_ps = test_sta_flag(sta, WLAN_STA_PS_STA);
        if ((start && in_ps) || (!start && !in_ps))
                return -EINVAL;
 
        if (start)
-               sta_ps_start(sta_inf);
+               sta_ps_start(sta);
        else
-               sta_ps_end(sta_inf);
+               sta_ps_end(sta);
 
        return 0;
 }
@@ -1393,7 +1428,7 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
        sta->rx_bytes += rx->skb->len;
        if (!(status->flag & RX_FLAG_NO_SIGNAL_VAL)) {
                sta->last_signal = status->signal;
-               ewma_add(&sta->avg_signal, -status->signal);
+               ewma_signal_add(&sta->avg_signal, -status->signal);
        }
 
        if (status->chains) {
@@ -1405,7 +1440,7 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
                                continue;
 
                        sta->chain_signal_last[i] = signal;
-                       ewma_add(&sta->chain_signal_avg[i], -signal);
+                       ewma_signal_add(&sta->chain_signal_avg[i], -signal);
                }
        }
 
@@ -1647,7 +1682,6 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
                if (unlikely(rx->key->flags & KEY_FLAG_TAINTED))
                        return RX_DROP_MONITOR;
 
-               rx->key->tx_rx_count++;
                /* TODO: add threshold stuff again */
        } else {
                return RX_DROP_MONITOR;
@@ -1883,7 +1917,6 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
 
        /* Complete frame has been reassembled - process it now */
        status = IEEE80211_SKB_RXCB(rx->skb);
-       status->rx_flags |= IEEE80211_RX_FRAGMENTED;
 
  out:
        ieee80211_led_rx(rx->local);
@@ -2108,9 +2141,8 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
                /* deliver to local stack */
                skb->protocol = eth_type_trans(skb, dev);
                memset(skb->cb, 0, sizeof(skb->cb));
-               if (!(rx->flags & IEEE80211_RX_REORDER_TIMER) &&
-                   rx->local->napi)
-                       napi_gro_receive(rx->local->napi, skb);
+               if (rx->napi)
+                       napi_gro_receive(rx->napi, skb);
                else
                        netif_receive_skb(skb);
        }
@@ -2378,9 +2410,8 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
                    tf->category == WLAN_CATEGORY_TDLS &&
                    (tf->action_code == WLAN_TDLS_CHANNEL_SWITCH_REQUEST ||
                     tf->action_code == WLAN_TDLS_CHANNEL_SWITCH_RESPONSE)) {
-                       rx->skb->pkt_type = IEEE80211_SDATA_QUEUE_TDLS_CHSW;
-                       skb_queue_tail(&sdata->skb_queue, rx->skb);
-                       ieee80211_queue_work(&rx->local->hw, &sdata->work);
+                       skb_queue_tail(&local->skb_queue_tdls_chsw, rx->skb);
+                       schedule_work(&local->tdls_chsw_work);
                        if (rx->sta)
                                rx->sta->rx_packets++;
 
@@ -3004,7 +3035,6 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx)
        return RX_QUEUED;
 }
 
-/* TODO: use IEEE80211_RX_FRAGMENTED */
 static void ieee80211_rx_cooked_monitor(struct ieee80211_rx_data *rx,
                                        struct ieee80211_rate *rate)
 {
@@ -3216,7 +3246,7 @@ void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid)
                /* This is OK -- must be QoS data frame */
                .security_idx = tid,
                .seqno_idx = tid,
-               .flags = IEEE80211_RX_REORDER_TIMER,
+               .napi = NULL, /* must be NULL to not have races */
        };
        struct tid_ampdu_rx *tid_agg_rx;
 
@@ -3286,7 +3316,7 @@ static bool ieee80211_accept_frame(struct ieee80211_rx_data *rx)
        case NL80211_IFTYPE_OCB:
                if (!bssid)
                        return false;
-               if (ieee80211_is_beacon(hdr->frame_control))
+               if (!ieee80211_is_data_present(hdr->frame_control))
                        return false;
                if (!is_broadcast_ether_addr(bssid))
                        return false;
@@ -3393,7 +3423,8 @@ static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx,
  * be called with rcu_read_lock protection.
  */
 static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
-                                        struct sk_buff *skb)
+                                        struct sk_buff *skb,
+                                        struct napi_struct *napi)
 {
        struct ieee80211_local *local = hw_to_local(hw);
        struct ieee80211_sub_if_data *sdata;
@@ -3409,6 +3440,7 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
        memset(&rx, 0, sizeof(rx));
        rx.skb = skb;
        rx.local = local;
+       rx.napi = napi;
 
        if (ieee80211_is_data(fc) || ieee80211_is_mgmt(fc))
                I802_DEBUG_INC(local->dot11ReceivedFragmentCount);
@@ -3510,7 +3542,8 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
  * This is the receive path handler. It is called by a low level driver when an
  * 802.11 MPDU is received from the hardware.
  */
-void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb)
+void ieee80211_rx_napi(struct ieee80211_hw *hw, struct sk_buff *skb,
+                      struct napi_struct *napi)
 {
        struct ieee80211_local *local = hw_to_local(hw);
        struct ieee80211_rate *rate = NULL;
@@ -3609,7 +3642,7 @@ void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb)
        ieee80211_tpt_led_trig_rx(local,
                        ((struct ieee80211_hdr *)skb->data)->frame_control,
                        skb->len);
-       __ieee80211_rx_handle_packet(hw, skb);
+       __ieee80211_rx_handle_packet(hw, skb, napi);
 
        rcu_read_unlock();
 
@@ -3617,7 +3650,7 @@ void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb)
  drop:
        kfree_skb(skb);
 }
-EXPORT_SYMBOL(ieee80211_rx);
+EXPORT_SYMBOL(ieee80211_rx_napi);
 
 /* This is a version of the rx handler that can be called from hard irq
  * context. Post the skb on the queue and schedule the tasklet */
index 666ddac3c87c67a63ed685efe83221cf7d52428d..64f1936350c66e48fb076beb7b418d55f42c8753 100644 (file)
@@ -68,7 +68,7 @@ static const struct rhashtable_params sta_rht_params = {
        .nelem_hint = 3, /* start small */
        .automatic_shrinking = true,
        .head_offset = offsetof(struct sta_info, hash_node),
-       .key_offset = offsetof(struct sta_info, sta.addr),
+       .key_offset = offsetof(struct sta_info, addr),
        .key_len = ETH_ALEN,
        .hashfn = sta_addr_hash,
        .max_size = CONFIG_MAC80211_STA_HASH_MAX_SIZE,
@@ -249,6 +249,9 @@ void sta_info_free(struct ieee80211_local *local, struct sta_info *sta)
        if (sta->sta.txq[0])
                kfree(to_txq_info(sta->sta.txq[0]));
        kfree(rcu_dereference_raw(sta->sta.rates));
+#ifdef CONFIG_MAC80211_MESH
+       kfree(sta->mesh);
+#endif
        kfree(sta);
 }
 
@@ -313,13 +316,19 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
        INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work);
        mutex_init(&sta->ampdu_mlme.mtx);
 #ifdef CONFIG_MAC80211_MESH
-       spin_lock_init(&sta->plink_lock);
-       if (ieee80211_vif_is_mesh(&sdata->vif) &&
-           !sdata->u.mesh.user_mpm)
-               init_timer(&sta->plink_timer);
-       sta->nonpeer_pm = NL80211_MESH_POWER_ACTIVE;
+       if (ieee80211_vif_is_mesh(&sdata->vif)) {
+               sta->mesh = kzalloc(sizeof(*sta->mesh), gfp);
+               if (!sta->mesh)
+                       goto free;
+               spin_lock_init(&sta->mesh->plink_lock);
+               if (ieee80211_vif_is_mesh(&sdata->vif) &&
+                   !sdata->u.mesh.user_mpm)
+                       init_timer(&sta->mesh->plink_timer);
+               sta->mesh->nonpeer_pm = NL80211_MESH_POWER_ACTIVE;
+       }
 #endif
 
+       memcpy(sta->addr, addr, ETH_ALEN);
        memcpy(sta->sta.addr, addr, ETH_ALEN);
        sta->local = local;
        sta->sdata = sdata;
@@ -332,9 +341,9 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
 
        ktime_get_ts(&uptime);
        sta->last_connected = uptime.tv_sec;
-       ewma_init(&sta->avg_signal, 1024, 8);
+       ewma_signal_init(&sta->avg_signal);
        for (i = 0; i < ARRAY_SIZE(sta->chain_signal_avg); i++)
-               ewma_init(&sta->chain_signal_avg[i], 1024, 8);
+               ewma_signal_init(&sta->chain_signal_avg[i]);
 
        if (local->ops->wake_tx_queue) {
                void *txq_data;
@@ -405,6 +414,9 @@ free_txq:
        if (sta->sta.txq[0])
                kfree(to_txq_info(sta->sta.txq[0]));
 free:
+#ifdef CONFIG_MAC80211_MESH
+       kfree(sta->mesh);
+#endif
        kfree(sta);
        return NULL;
 }
@@ -623,7 +635,7 @@ static void __sta_info_recalc_tim(struct sta_info *sta, bool ignore_pending)
        bool indicate_tim = false;
        u8 ignore_for_tim = sta->sta.uapsd_queues;
        int ac;
-       u16 id;
+       u16 id = sta->sta.aid;
 
        if (sta->sdata->vif.type == NL80211_IFTYPE_AP ||
            sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
@@ -631,12 +643,9 @@ static void __sta_info_recalc_tim(struct sta_info *sta, bool ignore_pending)
                        return;
 
                ps = &sta->sdata->bss->ps;
-               id = sta->sta.aid;
 #ifdef CONFIG_MAC80211_MESH
        } else if (ieee80211_vif_is_mesh(&sta->sdata->vif)) {
                ps = &sta->sdata->u.mesh.ps;
-               /* TIM map only for 1 <= PLID <= IEEE80211_MAX_AID */
-               id = sta->plid % (IEEE80211_MAX_AID + 1);
 #endif
        } else {
                return;
@@ -1887,7 +1896,8 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
                }
 
                if (!(sinfo->filled & BIT(NL80211_STA_INFO_SIGNAL_AVG))) {
-                       sinfo->signal_avg = (s8) -ewma_read(&sta->avg_signal);
+                       sinfo->signal_avg =
+                               (s8) -ewma_signal_read(&sta->avg_signal);
                        sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL_AVG);
                }
        }
@@ -1902,7 +1912,7 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
                for (i = 0; i < ARRAY_SIZE(sinfo->chain_signal); i++) {
                        sinfo->chain_signal[i] = sta->chain_signal_last[i];
                        sinfo->chain_signal_avg[i] =
-                               (s8) -ewma_read(&sta->chain_signal_avg[i]);
+                               (s8) -ewma_signal_read(&sta->chain_signal_avg[i]);
                }
        }
 
@@ -1956,16 +1966,16 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
                                 BIT(NL80211_STA_INFO_PEER_PM) |
                                 BIT(NL80211_STA_INFO_NONPEER_PM);
 
-               sinfo->llid = sta->llid;
-               sinfo->plid = sta->plid;
-               sinfo->plink_state = sta->plink_state;
+               sinfo->llid = sta->mesh->llid;
+               sinfo->plid = sta->mesh->plid;
+               sinfo->plink_state = sta->mesh->plink_state;
                if (test_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN)) {
                        sinfo->filled |= BIT(NL80211_STA_INFO_T_OFFSET);
-                       sinfo->t_offset = sta->t_offset;
+                       sinfo->t_offset = sta->mesh->t_offset;
                }
-               sinfo->local_pm = sta->local_pm;
-               sinfo->peer_pm = sta->peer_pm;
-               sinfo->nonpeer_pm = sta->nonpeer_pm;
+               sinfo->local_pm = sta->mesh->local_pm;
+               sinfo->peer_pm = sta->mesh->peer_pm;
+               sinfo->nonpeer_pm = sta->mesh->nonpeer_pm;
 #endif
        }
 
index 226f8ca47ad6737ff54c6dc3a8bc4036189e9fbb..b087c71ff7fe4ef99ed3869cc2c7eecc174b95c0 100644 (file)
@@ -53,6 +53,8 @@
  * @WLAN_STA_TDLS_CHAN_SWITCH: This TDLS peer supports TDLS channel-switching
  * @WLAN_STA_TDLS_OFF_CHANNEL: The local STA is currently off-channel with this
  *     TDLS peer
+ * @WLAN_STA_TDLS_WIDER_BW: This TDLS peer supports working on a wider bw on
+ *     the BSS base channel.
  * @WLAN_STA_UAPSD: Station requested unscheduled SP while driver was
  *     keeping station in power-save mode, reply when the driver
  *     unblocks the station.
@@ -84,6 +86,7 @@ enum ieee80211_sta_info_flags {
        WLAN_STA_TDLS_INITIATOR,
        WLAN_STA_TDLS_CHAN_SWITCH,
        WLAN_STA_TDLS_OFF_CHANNEL,
+       WLAN_STA_TDLS_WIDER_BW,
        WLAN_STA_UAPSD,
        WLAN_STA_SP,
        WLAN_STA_4ADDR_EVENT,
@@ -269,6 +272,56 @@ struct ieee80211_fast_tx {
        struct rcu_head rcu_head;
 };
 
+/**
+ * struct mesh_sta - mesh STA information
+ * @plink_lock: serialize access to plink fields
+ * @llid: Local link ID
+ * @plid: Peer link ID
+ * @aid: local aid supplied by peer
+ * @reason: Cancel reason on PLINK_HOLDING state
+ * @plink_retries: Retries in establishment
+ * @plink_state: peer link state
+ * @plink_timeout: timeout of peer link
+ * @plink_timer: peer link watch timer
+ * @t_offset: timing offset relative to this host
+ * @t_offset_setpoint: reference timing offset of this sta to be used when
+ *     calculating clockdrift
+ * @local_pm: local link-specific power save mode
+ * @peer_pm: peer-specific power save mode towards local STA
+ * @nonpeer_pm: STA power save mode towards non-peer neighbors
+ * @processed_beacon: set to true after peer rates and capabilities are
+ *     processed
+ * @fail_avg: moving percentage of failed MSDUs
+ */
+struct mesh_sta {
+       struct timer_list plink_timer;
+
+       s64 t_offset;
+       s64 t_offset_setpoint;
+
+       spinlock_t plink_lock;
+       u16 llid;
+       u16 plid;
+       u16 aid;
+       u16 reason;
+       u8 plink_retries;
+
+       bool processed_beacon;
+
+       enum nl80211_plink_state plink_state;
+       u32 plink_timeout;
+
+       /* mesh power save */
+       enum nl80211_mesh_power_mode local_pm;
+       enum nl80211_mesh_power_mode peer_pm;
+       enum nl80211_mesh_power_mode nonpeer_pm;
+
+       /* moving percentage of failed MSDUs */
+       unsigned int fail_avg;
+};
+
+DECLARE_EWMA(signal, 1024, 8)
+
 /**
  * struct sta_info - STA information
  *
@@ -278,12 +331,13 @@ struct ieee80211_fast_tx {
  * @list: global linked list entry
  * @free_list: list entry for keeping track of stations to free
  * @hash_node: hash node for rhashtable
+ * @addr: station's MAC address - duplicated from public part to
+ *     let the hash table work with just a single cacheline
  * @local: pointer to the global information
  * @sdata: virtual interface this station belongs to
  * @ptk: peer keys negotiated with this station, if any
  * @ptk_idx: last installed peer key index
  * @gtk: group keys negotiated with this station, if any
- * @gtk_idx: last installed group key index
  * @rate_ctrl: rate control algorithm reference
  * @rate_ctrl_lock: spinlock used to protect rate control data
  *     (data inside the algorithm, so serializes calls there)
@@ -318,30 +372,17 @@ struct ieee80211_fast_tx {
  * @last_signal: signal of last received frame from this STA
  * @avg_signal: moving average of signal of received frames from this STA
  * @last_ack_signal: signal of last received Ack frame from this STA
- * @last_seq_ctrl: last received seq/frag number from this STA (per RX queue)
+ * @last_seq_ctrl: last received seq/frag number from this STA (per TID
+ *     plus one for non-QoS frames)
  * @tx_filtered_count: number of frames the hardware filtered for this STA
  * @tx_retry_failed: number of frames that failed retry
  * @tx_retry_count: total number of retries for frames to this STA
- * @fail_avg: moving percentage of failed MSDUs
  * @tx_packets: number of RX/TX MSDUs
  * @tx_bytes: number of bytes transmitted to this STA
  * @tid_seq: per-TID sequence numbers for sending to this STA
  * @ampdu_mlme: A-MPDU state machine state
  * @timer_to_tid: identity mapping to ID timers
- * @plink_lock: serialize access to plink fields
- * @llid: Local link ID
- * @plid: Peer link ID
- * @reason: Cancel reason on PLINK_HOLDING state
- * @plink_retries: Retries in establishment
- * @plink_state: peer link state
- * @plink_timeout: timeout of peer link
- * @plink_timer: peer link watch timer
- * @t_offset: timing offset relative to this host
- * @t_offset_setpoint: reference timing offset of this sta to be used when
- *     calculating clockdrift
- * @local_pm: local link-specific power save mode
- * @peer_pm: peer-specific power save mode towards local STA
- * @nonpeer_pm: STA power save mode towards non-peer neighbors
+ * @mesh: mesh STA information
  * @debugfs: debug filesystem info
  * @dead: set to true when sta is unlinked
  * @uploaded: set to true when sta is uploaded to the driver
@@ -369,19 +410,19 @@ struct ieee80211_fast_tx {
  * @rx_msdu: MSDUs received from this station, using IEEE80211_NUM_TID
  *     entry for non-QoS frames
  * @fast_tx: TX fastpath information
- * @processed_beacon: set to true after peer rates and capabilities are
- *     processed
+ * @tdls_chandef: a TDLS peer can have a wider chandef that is compatible to
+ *     the BSS one.
  */
 struct sta_info {
        /* General information, mostly static */
        struct list_head list, free_list;
        struct rcu_head rcu_head;
        struct rhash_head hash_node;
+       u8 addr[ETH_ALEN];
        struct ieee80211_local *local;
        struct ieee80211_sub_if_data *sdata;
        struct ieee80211_key __rcu *gtk[NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS];
        struct ieee80211_key __rcu *ptk[NUM_DEFAULT_KEYS];
-       u8 gtk_idx;
        u8 ptk_idx;
        struct rate_control_ref *rate_ctrl;
        void *rate_ctrl_priv;
@@ -390,6 +431,10 @@ struct sta_info {
 
        struct ieee80211_fast_tx __rcu *fast_tx;
 
+#ifdef CONFIG_MAC80211_MESH
+       struct mesh_sta *mesh;
+#endif
+
        struct work_struct drv_deliver_wk;
 
        u16 listen_interval;
@@ -419,12 +464,12 @@ struct sta_info {
        unsigned long rx_fragments;
        unsigned long rx_dropped;
        int last_signal;
-       struct ewma avg_signal;
+       struct ewma_signal avg_signal;
        int last_ack_signal;
 
        u8 chains;
        s8 chain_signal_last[IEEE80211_MAX_CHAINS];
-       struct ewma chain_signal_avg[IEEE80211_MAX_CHAINS];
+       struct ewma_signal chain_signal_avg[IEEE80211_MAX_CHAINS];
 
        /* Plus 1 for non-QoS frames */
        __le16 last_seq_ctrl[IEEE80211_NUM_TIDS + 1];
@@ -432,8 +477,6 @@ struct sta_info {
        /* Updated from TX status path only, no locking requirements */
        unsigned long tx_filtered_count;
        unsigned long tx_retry_failed, tx_retry_count;
-       /* moving percentage of failed MSDUs */
-       unsigned int fail_avg;
 
        /* Updated from TX path only, no locking requirements */
        u64 tx_packets[IEEE80211_NUM_ACS];
@@ -455,29 +498,6 @@ struct sta_info {
        struct sta_ampdu_mlme ampdu_mlme;
        u8 timer_to_tid[IEEE80211_NUM_TIDS];
 
-#ifdef CONFIG_MAC80211_MESH
-       /*
-        * Mesh peer link attributes, protected by plink_lock.
-        * TODO: move to a sub-structure that is referenced with pointer?
-        */
-       spinlock_t plink_lock;
-       u16 llid;
-       u16 plid;
-       u16 reason;
-       u8 plink_retries;
-       enum nl80211_plink_state plink_state;
-       u32 plink_timeout;
-       struct timer_list plink_timer;
-
-       s64 t_offset;
-       s64 t_offset_setpoint;
-       /* mesh power save */
-       enum nl80211_mesh_power_mode local_pm;
-       enum nl80211_mesh_power_mode peer_pm;
-       enum nl80211_mesh_power_mode nonpeer_pm;
-       bool processed_beacon;
-#endif
-
 #ifdef CONFIG_MAC80211_DEBUGFS
        struct sta_info_debugfsdentries {
                struct dentry *dir;
@@ -498,6 +518,8 @@ struct sta_info {
 
        u8 reserved_tid;
 
+       struct cfg80211_chan_def tdls_chandef;
+
        /* keep last! */
        struct ieee80211_sta sta;
 };
@@ -505,7 +527,7 @@ struct sta_info {
 static inline enum nl80211_plink_state sta_plink_state(struct sta_info *sta)
 {
 #ifdef CONFIG_MAC80211_MESH
-       return sta->plink_state;
+       return sta->mesh->plink_state;
 #endif
        return NL80211_PLINK_LISTEN;
 }
@@ -608,7 +630,7 @@ u32 sta_addr_hash(const void *key, u32 length, u32 seed);
                               _sta_bucket_idx(tbl, _addr),             \
                               hash_node)                               \
        /* compare address and run code only if it matches */           \
-       if (ether_addr_equal(_sta->sta.addr, (_addr)))
+       if (ether_addr_equal(_sta->addr, (_addr)))
 
 /*
  * Get STA info by index, BROKEN!
index 45628f37c083aa72575fcc0a0241b1aefc0cb5ad..8ba5832435095f10e94f782e07d92a4f742cad3a 100644 (file)
@@ -515,7 +515,7 @@ static void ieee80211_report_used_skb(struct ieee80211_local *local,
 
                if (!sdata) {
                        skb->dev = NULL;
-               } else if (info->flags & IEEE80211_TX_INTFL_MLME_CONN_TX) {
+               } else {
                        unsigned int hdr_size =
                                ieee80211_hdrlen(hdr->frame_control);
 
@@ -529,9 +529,6 @@ static void ieee80211_report_used_skb(struct ieee80211_local *local,
                                ieee80211_mgd_conn_tx_status(sdata,
                                                             hdr->frame_control,
                                                             acked);
-               } else {
-                       /* we assign ack frame ID for the others */
-                       WARN_ON(1);
                }
 
                rcu_read_unlock();
index 8db6e2994bbc59bb7cf38c36848a7d92b8e4a0e5..aee701a5649e59ebd03ef300f25e33eadfc280d5 100644 (file)
@@ -4,6 +4,7 @@
  * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
  * Copyright 2014, Intel Corporation
  * Copyright 2014  Intel Mobile Communications GmbH
+ * Copyright 2015  Intel Deutschland GmbH
  *
  * This file is GPLv2 as found in COPYING.
  */
@@ -11,6 +12,7 @@
 #include <linux/ieee80211.h>
 #include <linux/log2.h>
 #include <net/cfg80211.h>
+#include <linux/rtnetlink.h>
 #include "ieee80211_i.h"
 #include "driver-ops.h"
 
@@ -35,20 +37,28 @@ void ieee80211_tdls_peer_del_work(struct work_struct *wk)
        mutex_unlock(&local->mtx);
 }
 
-static void ieee80211_tdls_add_ext_capab(struct ieee80211_local *local,
+static void ieee80211_tdls_add_ext_capab(struct ieee80211_sub_if_data *sdata,
                                         struct sk_buff *skb)
 {
-       u8 *pos = (void *)skb_put(skb, 7);
+       struct ieee80211_local *local = sdata->local;
        bool chan_switch = local->hw.wiphy->features &
                           NL80211_FEATURE_TDLS_CHANNEL_SWITCH;
+       bool wider_band = ieee80211_hw_check(&local->hw, TDLS_WIDER_BW);
+       enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
+       struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band];
+       bool vht = sband && sband->vht_cap.vht_supported;
+       u8 *pos = (void *)skb_put(skb, 10);
 
        *pos++ = WLAN_EID_EXT_CAPABILITY;
-       *pos++ = 5; /* len */
+       *pos++ = 8; /* len */
        *pos++ = 0x0;
        *pos++ = 0x0;
        *pos++ = 0x0;
        *pos++ = chan_switch ? WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH : 0;
        *pos++ = WLAN_EXT_CAPA5_TDLS_ENABLED;
+       *pos++ = 0;
+       *pos++ = 0;
+       *pos++ = (vht && wider_band) ? WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED : 0;
 }
 
 static u8
@@ -283,6 +293,60 @@ static void ieee80211_tdls_add_wmm_param_ie(struct ieee80211_sub_if_data *sdata,
        }
 }
 
+static void
+ieee80211_tdls_chandef_vht_upgrade(struct ieee80211_sub_if_data *sdata,
+                                  struct sta_info *sta)
+{
+       /* IEEE802.11ac-2013 Table E-4 */
+       u16 centers_80mhz[] = { 5210, 5290, 5530, 5610, 5690, 5775 };
+       struct cfg80211_chan_def uc = sta->tdls_chandef;
+       enum nl80211_chan_width max_width = ieee80211_get_sta_bw(&sta->sta);
+       int i;
+
+       /* only support upgrading non-narrow channels up to 80Mhz */
+       if (max_width == NL80211_CHAN_WIDTH_5 ||
+           max_width == NL80211_CHAN_WIDTH_10)
+               return;
+
+       if (max_width > NL80211_CHAN_WIDTH_80)
+               max_width = NL80211_CHAN_WIDTH_80;
+
+       if (uc.width == max_width)
+               return;
+       /*
+        * Channel usage constrains in the IEEE802.11ac-2013 specification only
+        * allow expanding a 20MHz channel to 80MHz in a single way. In
+        * addition, there are no 40MHz allowed channels that are not part of
+        * the allowed 80MHz range in the 5GHz spectrum (the relevant one here).
+        */
+       for (i = 0; i < ARRAY_SIZE(centers_80mhz); i++)
+               if (abs(uc.chan->center_freq - centers_80mhz[i]) <= 30) {
+                       uc.center_freq1 = centers_80mhz[i];
+                       uc.width = NL80211_CHAN_WIDTH_80;
+                       break;
+               }
+
+       if (!uc.center_freq1)
+               return;
+
+       /* proceed to downgrade the chandef until usable or the same */
+       while (uc.width > max_width &&
+              !cfg80211_reg_can_beacon(sdata->local->hw.wiphy,
+                                       &uc, sdata->wdev.iftype))
+               ieee80211_chandef_downgrade(&uc);
+
+       if (!cfg80211_chandef_identical(&uc, &sta->tdls_chandef)) {
+               tdls_dbg(sdata, "TDLS ch width upgraded %d -> %d\n",
+                        sta->tdls_chandef.width, uc.width);
+
+               /*
+                * the station is not yet authorized when BW upgrade is done,
+                * locking is not required
+                */
+               sta->tdls_chandef = uc;
+       }
+}
+
 static void
 ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
                                   struct sk_buff *skb, const u8 *peer,
@@ -320,7 +384,7 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
                offset = noffset;
        }
 
-       ieee80211_tdls_add_ext_capab(local, skb);
+       ieee80211_tdls_add_ext_capab(sdata, skb);
 
        /* add the QoS element if we support it */
        if (local->hw.queues >= IEEE80211_NUM_ACS &&
@@ -350,15 +414,17 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
                offset = noffset;
        }
 
-       rcu_read_lock();
+       mutex_lock(&local->sta_mtx);
 
        /* we should have the peer STA if we're already responding */
        if (action_code == WLAN_TDLS_SETUP_RESPONSE) {
                sta = sta_info_get(sdata, peer);
                if (WARN_ON_ONCE(!sta)) {
-                       rcu_read_unlock();
+                       mutex_unlock(&local->sta_mtx);
                        return;
                }
+
+               sta->tdls_chandef = sdata->vif.bss_conf.chandef;
        }
 
        ieee80211_tdls_add_oper_classes(sdata, skb);
@@ -384,10 +450,6 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
                ieee80211_ie_build_ht_cap(pos, &ht_cap, ht_cap.cap);
        } else if (action_code == WLAN_TDLS_SETUP_RESPONSE &&
                   ht_cap.ht_supported && sta->sta.ht_cap.ht_supported) {
-               /* disable SMPS in TDLS responder */
-               sta->sta.ht_cap.cap |= WLAN_HT_CAP_SM_PS_DISABLED
-                                       << IEEE80211_HT_CAP_SM_PS_SHIFT;
-
                /* the peer caps are already intersected with our own */
                memcpy(&ht_cap, &sta->sta.ht_cap, sizeof(ht_cap));
 
@@ -448,9 +510,16 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
 
                pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2);
                ieee80211_ie_build_vht_cap(pos, &vht_cap, vht_cap.cap);
+
+               /*
+                * if both peers support WIDER_BW, we can expand the chandef to
+                * a wider compatible one, up to 80MHz
+                */
+               if (test_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW))
+                       ieee80211_tdls_chandef_vht_upgrade(sdata, sta);
        }
 
-       rcu_read_unlock();
+       mutex_unlock(&local->sta_mtx);
 
        /* add any remaining IEs */
        if (extra_ies_len) {
@@ -474,15 +543,17 @@ ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata,
        enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
        u8 *pos;
 
-       rcu_read_lock();
+       mutex_lock(&local->sta_mtx);
 
        sta = sta_info_get(sdata, peer);
        ap_sta = sta_info_get(sdata, ifmgd->bssid);
        if (WARN_ON_ONCE(!sta || !ap_sta)) {
-               rcu_read_unlock();
+               mutex_unlock(&local->sta_mtx);
                return;
        }
 
+       sta->tdls_chandef = sdata->vif.bss_conf.chandef;
+
        /* add any custom IEs that go before the QoS IE */
        if (extra_ies_len) {
                static const u8 before_qos[] = {
@@ -530,12 +601,19 @@ ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata,
 
        /* only include VHT-operation if not on the 2.4GHz band */
        if (band != IEEE80211_BAND_2GHZ && sta->sta.vht_cap.vht_supported) {
+               /*
+                * if both peers support WIDER_BW, we can expand the chandef to
+                * a wider compatible one, up to 80MHz
+                */
+               if (test_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW))
+                       ieee80211_tdls_chandef_vht_upgrade(sdata, sta);
+
                pos = skb_put(skb, 2 + sizeof(struct ieee80211_vht_operation));
                ieee80211_ie_build_vht_oper(pos, &sta->sta.vht_cap,
-                                           &sdata->vif.bss_conf.chandef);
+                                           &sta->tdls_chandef);
        }
 
-       rcu_read_unlock();
+       mutex_unlock(&local->sta_mtx);
 
        /* add any remaining IEs */
        if (extra_ies_len) {
@@ -784,7 +862,7 @@ ieee80211_tdls_build_mgmt_packet_data(struct ieee80211_sub_if_data *sdata,
                               max(sizeof(struct ieee80211_mgmt),
                                   sizeof(struct ieee80211_tdls_data)) +
                               50 + /* supported rates */
-                              7 + /* ext capab */
+                              10 + /* ext capab */
                               26 + /* max(WMM-info, WMM-param) */
                               2 + max(sizeof(struct ieee80211_ht_cap),
                                       sizeof(struct ieee80211_ht_operation)) +
@@ -983,8 +1061,17 @@ ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev,
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        struct ieee80211_local *local = sdata->local;
+       enum ieee80211_smps_mode smps_mode = sdata->u.mgd.driver_smps_mode;
        int ret;
 
+       /* don't support setup with forced SMPS mode that's not off */
+       if (smps_mode != IEEE80211_SMPS_AUTOMATIC &&
+           smps_mode != IEEE80211_SMPS_OFF) {
+               tdls_dbg(sdata, "Aborting TDLS setup due to SMPS mode %d\n",
+                        smps_mode);
+               return -ENOTSUPP;
+       }
+
        mutex_lock(&local->mtx);
 
        /* we don't support concurrent TDLS peer setups */
@@ -1146,6 +1233,22 @@ int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
        return ret;
 }
 
+static void iee80211_tdls_recalc_chanctx(struct ieee80211_sub_if_data *sdata)
+{
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_chanctx_conf *conf;
+       struct ieee80211_chanctx *ctx;
+
+       mutex_lock(&local->chanctx_mtx);
+       conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
+                                        lockdep_is_held(&local->chanctx_mtx));
+       if (conf) {
+               ctx = container_of(conf, struct ieee80211_chanctx, conf);
+               ieee80211_recalc_chanctx_chantype(local, ctx);
+       }
+       mutex_unlock(&local->chanctx_mtx);
+}
+
 int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
                        const u8 *peer, enum nl80211_tdls_operation oper)
 {
@@ -1182,6 +1285,8 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
                        break;
                }
 
+               iee80211_tdls_recalc_chanctx(sdata);
+
                rcu_read_lock();
                sta = sta_info_get(sdata, peer);
                if (!sta) {
@@ -1213,6 +1318,7 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
                ieee80211_flush_queues(local, sdata, false);
 
                ret = sta_info_destroy_addr(sdata, peer);
+               iee80211_tdls_recalc_chanctx(sdata);
                break;
        default:
                ret = -ENOTSUPP;
@@ -1224,6 +1330,10 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
                eth_zero_addr(sdata->u.mgd.tdls_peer);
        }
 
+       if (ret == 0)
+               ieee80211_queue_work(&sdata->local->hw,
+                                    &sdata->u.mgd.request_smps_work);
+
        mutex_unlock(&local->mtx);
        return ret;
 }
@@ -1627,6 +1737,31 @@ ieee80211_process_tdls_channel_switch_req(struct ieee80211_sub_if_data *sdata,
                return -EINVAL;
        }
 
+       if (!elems.sec_chan_offs) {
+               chan_type = NL80211_CHAN_HT20;
+       } else {
+               switch (elems.sec_chan_offs->sec_chan_offs) {
+               case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
+                       chan_type = NL80211_CHAN_HT40PLUS;
+                       break;
+               case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
+                       chan_type = NL80211_CHAN_HT40MINUS;
+                       break;
+               default:
+                       chan_type = NL80211_CHAN_HT20;
+                       break;
+               }
+       }
+
+       cfg80211_chandef_create(&chandef, chan, chan_type);
+
+       /* we will be active on the TDLS link */
+       if (!cfg80211_reg_can_beacon_relax(sdata->local->hw.wiphy, &chandef,
+                                          sdata->wdev.iftype)) {
+               tdls_dbg(sdata, "TDLS chan switch to forbidden channel\n");
+               return -EINVAL;
+       }
+
        mutex_lock(&local->sta_mtx);
        sta = sta_info_get(sdata, tf->sa);
        if (!sta || !test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH)) {
@@ -1647,27 +1782,15 @@ ieee80211_process_tdls_channel_switch_req(struct ieee80211_sub_if_data *sdata,
                goto out;
        }
 
-       if (!sta->sta.ht_cap.ht_supported) {
-               chan_type = NL80211_CHAN_NO_HT;
-       } else if (!elems.sec_chan_offs) {
-               chan_type = NL80211_CHAN_HT20;
-       } else {
-               switch (elems.sec_chan_offs->sec_chan_offs) {
-               case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
-                       chan_type = NL80211_CHAN_HT40PLUS;
-                       break;
-               case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
-                       chan_type = NL80211_CHAN_HT40MINUS;
-                       break;
-               default:
-                       chan_type = NL80211_CHAN_HT20;
-                       break;
-               }
+       /* peer should have known better */
+       if (!sta->sta.ht_cap.ht_supported && elems.sec_chan_offs &&
+           elems.sec_chan_offs->sec_chan_offs) {
+               tdls_dbg(sdata, "TDLS chan switch - wide chan unsupported\n");
+               ret = -ENOTSUPP;
+               goto out;
        }
 
-       cfg80211_chandef_create(&chandef, chan, chan_type);
        params.chandef = &chandef;
-
        params.switch_time = le16_to_cpu(elems.ch_sw_timing->switch_time);
        params.switch_timeout = le16_to_cpu(elems.ch_sw_timing->switch_timeout);
 
@@ -1691,12 +1814,15 @@ out:
        return ret;
 }
 
-void ieee80211_process_tdls_channel_switch(struct ieee80211_sub_if_data *sdata,
-                                          struct sk_buff *skb)
+static void
+ieee80211_process_tdls_channel_switch(struct ieee80211_sub_if_data *sdata,
+                                     struct sk_buff *skb)
 {
        struct ieee80211_tdls_data *tf = (void *)skb->data;
        struct wiphy *wiphy = sdata->local->hw.wiphy;
 
+       ASSERT_RTNL();
+
        /* make sure the driver supports it */
        if (!(wiphy->features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH))
                return;
@@ -1720,3 +1846,47 @@ void ieee80211_process_tdls_channel_switch(struct ieee80211_sub_if_data *sdata,
                return;
        }
 }
+
+void ieee80211_teardown_tdls_peers(struct ieee80211_sub_if_data *sdata)
+{
+       struct sta_info *sta;
+       u16 reason = WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) {
+               if (!sta->sta.tdls || sta->sdata != sdata || !sta->uploaded ||
+                   !test_sta_flag(sta, WLAN_STA_AUTHORIZED))
+                       continue;
+
+               ieee80211_tdls_oper_request(&sdata->vif, sta->sta.addr,
+                                           NL80211_TDLS_TEARDOWN, reason,
+                                           GFP_ATOMIC);
+       }
+       rcu_read_unlock();
+}
+
+void ieee80211_tdls_chsw_work(struct work_struct *wk)
+{
+       struct ieee80211_local *local =
+               container_of(wk, struct ieee80211_local, tdls_chsw_work);
+       struct ieee80211_sub_if_data *sdata;
+       struct sk_buff *skb;
+       struct ieee80211_tdls_data *tf;
+
+       rtnl_lock();
+       while ((skb = skb_dequeue(&local->skb_queue_tdls_chsw))) {
+               tf = (struct ieee80211_tdls_data *)skb->data;
+               list_for_each_entry(sdata, &local->interfaces, list) {
+                       if (!ieee80211_sdata_running(sdata) ||
+                           sdata->vif.type != NL80211_IFTYPE_STATION ||
+                           !ether_addr_equal(tf->da, sdata->vif.addr))
+                               continue;
+
+                       ieee80211_process_tdls_channel_switch(sdata, skb);
+                       break;
+               }
+
+               kfree_skb(skb);
+       }
+       rtnl_unlock();
+}
index b8233505bf9fd3bb4945c126eebee069f2b94878..84e0e8c7fb236952dfc1cfcb23204623e80d7867 100644 (file)
@@ -311,9 +311,6 @@ ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx)
        if (tx->sdata->vif.type == NL80211_IFTYPE_WDS)
                return TX_CONTINUE;
 
-       if (tx->sdata->vif.type == NL80211_IFTYPE_MESH_POINT)
-               return TX_CONTINUE;
-
        if (tx->flags & IEEE80211_TX_PS_BUFFERED)
                return TX_CONTINUE;
 
@@ -610,7 +607,6 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)
        if (tx->key) {
                bool skip_hw = false;
 
-               tx->key->tx_rx_count++;
                /* TODO: add threshold stuff again */
 
                switch (tx->key->conf.cipher) {
@@ -690,7 +686,8 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)
 
        txrc.bss = (tx->sdata->vif.type == NL80211_IFTYPE_AP ||
                    tx->sdata->vif.type == NL80211_IFTYPE_MESH_POINT ||
-                   tx->sdata->vif.type == NL80211_IFTYPE_ADHOC);
+                   tx->sdata->vif.type == NL80211_IFTYPE_ADHOC ||
+                   tx->sdata->vif.type == NL80211_IFTYPE_OCB);
 
        /* set up RTS protection if desired */
        if (len > tx->local->hw.wiphy->rts_threshold) {
@@ -2777,7 +2774,11 @@ static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
                sdata->sequence_number += 0x10;
        }
 
-       sta->tx_msdu[tid]++;
+       if (skb_shinfo(skb)->gso_size)
+               sta->tx_msdu[tid] +=
+                       DIV_ROUND_UP(skb->len, skb_shinfo(skb)->gso_size);
+       else
+               sta->tx_msdu[tid]++;
 
        info->hw_queue = sdata->vif.hw_queue[skb_get_queue_mapping(skb)];
 
@@ -3213,6 +3214,16 @@ static void ieee80211_set_csa(struct ieee80211_sub_if_data *sdata,
        rcu_read_unlock();
 }
 
+static u8 __ieee80211_csa_update_counter(struct beacon_data *beacon)
+{
+       beacon->csa_current_counter--;
+
+       /* the counter should never reach 0 */
+       WARN_ON_ONCE(!beacon->csa_current_counter);
+
+       return beacon->csa_current_counter;
+}
+
 u8 ieee80211_csa_update_counter(struct ieee80211_vif *vif)
 {
        struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
@@ -3231,11 +3242,7 @@ u8 ieee80211_csa_update_counter(struct ieee80211_vif *vif)
        if (!beacon)
                goto unlock;
 
-       beacon->csa_current_counter--;
-
-       /* the counter should never reach 0 */
-       WARN_ON_ONCE(!beacon->csa_current_counter);
-       count = beacon->csa_current_counter;
+       count = __ieee80211_csa_update_counter(beacon);
 
 unlock:
        rcu_read_unlock();
@@ -3335,7 +3342,7 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw,
                if (beacon) {
                        if (beacon->csa_counter_offsets[0]) {
                                if (!is_template)
-                                       ieee80211_csa_update_counter(vif);
+                                       __ieee80211_csa_update_counter(beacon);
 
                                ieee80211_set_csa(sdata, beacon);
                        }
@@ -3381,7 +3388,7 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw,
 
                if (beacon->csa_counter_offsets[0]) {
                        if (!is_template)
-                               ieee80211_csa_update_counter(vif);
+                               __ieee80211_csa_update_counter(beacon);
 
                        ieee80211_set_csa(sdata, beacon);
                }
@@ -3411,7 +3418,7 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw,
                                 * for now we leave it consistent with overall
                                 * mac80211's behavior.
                                 */
-                               ieee80211_csa_update_counter(vif);
+                               __ieee80211_csa_update_counter(beacon);
 
                        ieee80211_set_csa(sdata, beacon);
                }
index 43e5aadd7a894f04b7b2d7cf4b3d456ff7228400..1104421bc525598aae491c587f29eb4b385fc220 100644 (file)
@@ -47,55 +47,6 @@ struct ieee80211_hw *wiphy_to_ieee80211_hw(struct wiphy *wiphy)
 }
 EXPORT_SYMBOL(wiphy_to_ieee80211_hw);
 
-u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len,
-                       enum nl80211_iftype type)
-{
-       __le16 fc = hdr->frame_control;
-
-        /* drop ACK/CTS frames and incorrect hdr len (ctrl) */
-       if (len < 16)
-               return NULL;
-
-       if (ieee80211_is_data(fc)) {
-               if (len < 24) /* drop incorrect hdr len (data) */
-                       return NULL;
-
-               if (ieee80211_has_a4(fc))
-                       return NULL;
-               if (ieee80211_has_tods(fc))
-                       return hdr->addr1;
-               if (ieee80211_has_fromds(fc))
-                       return hdr->addr2;
-
-               return hdr->addr3;
-       }
-
-       if (ieee80211_is_mgmt(fc)) {
-               if (len < 24) /* drop incorrect hdr len (mgmt) */
-                       return NULL;
-               return hdr->addr3;
-       }
-
-       if (ieee80211_is_ctl(fc)) {
-               if (ieee80211_is_pspoll(fc))
-                       return hdr->addr1;
-
-               if (ieee80211_is_back_req(fc)) {
-                       switch (type) {
-                       case NL80211_IFTYPE_STATION:
-                               return hdr->addr2;
-                       case NL80211_IFTYPE_AP:
-                       case NL80211_IFTYPE_AP_VLAN:
-                               return hdr->addr1;
-                       default:
-                               break; /* fall through to the return */
-                       }
-               }
-       }
-
-       return NULL;
-}
-
 void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx)
 {
        struct sk_buff *skb;
@@ -752,7 +703,12 @@ EXPORT_SYMBOL_GPL(wdev_to_ieee80211_vif);
 
 struct wireless_dev *ieee80211_vif_to_wdev(struct ieee80211_vif *vif)
 {
-       struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+       struct ieee80211_sub_if_data *sdata;
+
+       if (!vif)
+               return NULL;
+
+       sdata = vif_to_sdata(vif);
 
        if (!ieee80211_sdata_running(sdata) ||
            !(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
@@ -1709,6 +1665,7 @@ static void ieee80211_handle_reconfig_failure(struct ieee80211_local *local)
        local->resuming = false;
        local->suspended = false;
        local->started = false;
+       local->in_reconfig = false;
 
        /* scheduled scan clearly can't be running any more, but tell
         * cfg80211 and clear local state
@@ -1759,16 +1716,24 @@ int ieee80211_reconfig(struct ieee80211_local *local)
        struct ieee80211_sub_if_data *sched_scan_sdata;
        struct cfg80211_sched_scan_request *sched_scan_req;
        bool sched_scan_stopped = false;
+       bool suspended = local->suspended;
 
        /* nothing to do if HW shouldn't run */
        if (!local->open_count)
                goto wake_up;
 
 #ifdef CONFIG_PM
-       if (local->suspended)
+       if (suspended)
                local->resuming = true;
 
        if (local->wowlan) {
+               /*
+                * In the wowlan case, both mac80211 and the device
+                * are functional when the resume op is called, so
+                * clear local->suspended so the device could operate
+                * normally (e.g. pass rx frames).
+                */
+               local->suspended = false;
                res = drv_resume(local);
                local->wowlan = false;
                if (res < 0) {
@@ -1781,8 +1746,10 @@ int ieee80211_reconfig(struct ieee80211_local *local)
                /*
                 * res is 1, which means the driver requested
                 * to go through a regular reset on wakeup.
+                * restore local->suspended in this case.
                 */
                reconfig_due_to_wowlan = true;
+               local->suspended = true;
        }
 #endif
 
@@ -1794,7 +1761,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
         */
        res = drv_start(local);
        if (res) {
-               if (local->suspended)
+               if (suspended)
                        WARN(1, "Hardware became unavailable upon resume. This could be a software issue prior to suspend or a hardware issue.\n");
                else
                        WARN(1, "Hardware became unavailable during restart.\n");
@@ -2088,10 +2055,10 @@ int ieee80211_reconfig(struct ieee80211_local *local)
         * If this is for hw restart things are still running.
         * We may want to change that later, however.
         */
-       if (local->open_count && (!local->suspended || reconfig_due_to_wowlan))
+       if (local->open_count && (!suspended || reconfig_due_to_wowlan))
                drv_reconfig_complete(local, IEEE80211_RECONFIG_TYPE_RESTART);
 
-       if (!local->suspended)
+       if (!suspended)
                return 0;
 
 #ifdef CONFIG_PM
index 80694d55db7404079212761ff23083bb9aa35169..834ccdbc74be1ccd518aaa952ad854810d94f4ca 100644 (file)
@@ -308,11 +308,15 @@ enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta)
 {
        struct ieee80211_sub_if_data *sdata = sta->sdata;
        enum ieee80211_sta_rx_bandwidth bw;
+       enum nl80211_chan_width bss_width = sdata->vif.bss_conf.chandef.width;
 
-       bw = ieee80211_chan_width_to_rx_bw(sdata->vif.bss_conf.chandef.width);
-       bw = min(bw, ieee80211_sta_cap_rx_bw(sta));
+       bw = ieee80211_sta_cap_rx_bw(sta);
        bw = min(bw, sta->cur_max_bandwidth);
 
+       /* do not cap the BW of TDLS WIDER_BW peers by the bss */
+       if (!test_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW))
+               bw = min(bw, ieee80211_chan_width_to_rx_bw(bss_width));
+
        return bw;
 }
 
@@ -422,3 +426,29 @@ void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
        if (changed > 0)
                rate_control_rate_update(local, sband, sta, changed);
 }
+
+void ieee80211_get_vht_mask_from_cap(__le16 vht_cap,
+                                    u16 vht_mask[NL80211_VHT_NSS_MAX])
+{
+       int i;
+       u16 mask, cap = le16_to_cpu(vht_cap);
+
+       for (i = 0; i < NL80211_VHT_NSS_MAX; i++) {
+               mask = (cap >> i * 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED;
+               switch (mask) {
+               case IEEE80211_VHT_MCS_SUPPORT_0_7:
+                       vht_mask[i] = 0x00FF;
+                       break;
+               case IEEE80211_VHT_MCS_SUPPORT_0_8:
+                       vht_mask[i] = 0x01FF;
+                       break;
+               case IEEE80211_VHT_MCS_SUPPORT_0_9:
+                       vht_mask[i] = 0x03FF;
+                       break;
+               case IEEE80211_VHT_MCS_NOT_SUPPORTED:
+               default:
+                       vht_mask[i] = 0;
+                       break;
+               }
+       }
+}
index 943f7606527e25b4cad0bfea2a38755f1f7f69d9..feb547dc8643ab286fa0c64f19b01e2b4766898e 100644 (file)
@@ -516,31 +516,34 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx,
                        return RX_DROP_UNUSABLE;
        }
 
-       ccmp_hdr2pn(pn, skb->data + hdrlen);
+       if (!(status->flag & RX_FLAG_PN_VALIDATED)) {
+               ccmp_hdr2pn(pn, skb->data + hdrlen);
 
-       queue = rx->security_idx;
+               queue = rx->security_idx;
 
-       if (memcmp(pn, key->u.ccmp.rx_pn[queue], IEEE80211_CCMP_PN_LEN) <= 0) {
-               key->u.ccmp.replays++;
-               return RX_DROP_UNUSABLE;
-       }
+               if (memcmp(pn, key->u.ccmp.rx_pn[queue],
+                          IEEE80211_CCMP_PN_LEN) <= 0) {
+                       key->u.ccmp.replays++;
+                       return RX_DROP_UNUSABLE;
+               }
 
-       if (!(status->flag & RX_FLAG_DECRYPTED)) {
-               u8 aad[2 * AES_BLOCK_SIZE];
-               u8 b_0[AES_BLOCK_SIZE];
-               /* hardware didn't decrypt/verify MIC */
-               ccmp_special_blocks(skb, pn, b_0, aad);
+               if (!(status->flag & RX_FLAG_DECRYPTED)) {
+                       u8 aad[2 * AES_BLOCK_SIZE];
+                       u8 b_0[AES_BLOCK_SIZE];
+                       /* hardware didn't decrypt/verify MIC */
+                       ccmp_special_blocks(skb, pn, b_0, aad);
+
+                       if (ieee80211_aes_ccm_decrypt(
+                                   key->u.ccmp.tfm, b_0, aad,
+                                   skb->data + hdrlen + IEEE80211_CCMP_HDR_LEN,
+                                   data_len,
+                                   skb->data + skb->len - mic_len, mic_len))
+                               return RX_DROP_UNUSABLE;
+               }
 
-               if (ieee80211_aes_ccm_decrypt(
-                           key->u.ccmp.tfm, b_0, aad,
-                           skb->data + hdrlen + IEEE80211_CCMP_HDR_LEN,
-                           data_len,
-                           skb->data + skb->len - mic_len, mic_len))
-                       return RX_DROP_UNUSABLE;
+               memcpy(key->u.ccmp.rx_pn[queue], pn, IEEE80211_CCMP_PN_LEN);
        }
 
-       memcpy(key->u.ccmp.rx_pn[queue], pn, IEEE80211_CCMP_PN_LEN);
-
        /* Remove CCMP header and MIC */
        if (pskb_trim(skb, skb->len - mic_len))
                return RX_DROP_UNUSABLE;
@@ -739,31 +742,35 @@ ieee80211_crypto_gcmp_decrypt(struct ieee80211_rx_data *rx)
                        return RX_DROP_UNUSABLE;
        }
 
-       gcmp_hdr2pn(pn, skb->data + hdrlen);
+       if (!(status->flag & RX_FLAG_PN_VALIDATED)) {
+               gcmp_hdr2pn(pn, skb->data + hdrlen);
 
-       queue = rx->security_idx;
+               queue = rx->security_idx;
 
-       if (memcmp(pn, key->u.gcmp.rx_pn[queue], IEEE80211_GCMP_PN_LEN) <= 0) {
-               key->u.gcmp.replays++;
-               return RX_DROP_UNUSABLE;
-       }
+               if (memcmp(pn, key->u.gcmp.rx_pn[queue],
+                          IEEE80211_GCMP_PN_LEN) <= 0) {
+                       key->u.gcmp.replays++;
+                       return RX_DROP_UNUSABLE;
+               }
 
-       if (!(status->flag & RX_FLAG_DECRYPTED)) {
-               u8 aad[2 * AES_BLOCK_SIZE];
-               u8 j_0[AES_BLOCK_SIZE];
-               /* hardware didn't decrypt/verify MIC */
-               gcmp_special_blocks(skb, pn, j_0, aad);
+               if (!(status->flag & RX_FLAG_DECRYPTED)) {
+                       u8 aad[2 * AES_BLOCK_SIZE];
+                       u8 j_0[AES_BLOCK_SIZE];
+                       /* hardware didn't decrypt/verify MIC */
+                       gcmp_special_blocks(skb, pn, j_0, aad);
+
+                       if (ieee80211_aes_gcm_decrypt(
+                                   key->u.gcmp.tfm, j_0, aad,
+                                   skb->data + hdrlen + IEEE80211_GCMP_HDR_LEN,
+                                   data_len,
+                                   skb->data + skb->len -
+                                   IEEE80211_GCMP_MIC_LEN))
+                               return RX_DROP_UNUSABLE;
+               }
 
-               if (ieee80211_aes_gcm_decrypt(
-                           key->u.gcmp.tfm, j_0, aad,
-                           skb->data + hdrlen + IEEE80211_GCMP_HDR_LEN,
-                           data_len,
-                           skb->data + skb->len - IEEE80211_GCMP_MIC_LEN))
-                       return RX_DROP_UNUSABLE;
+               memcpy(key->u.gcmp.rx_pn[queue], pn, IEEE80211_GCMP_PN_LEN);
        }
 
-       memcpy(key->u.gcmp.rx_pn[queue], pn, IEEE80211_GCMP_PN_LEN);
-
        /* Remove GCMP header and MIC */
        if (pskb_trim(skb, skb->len - IEEE80211_GCMP_MIC_LEN))
                return RX_DROP_UNUSABLE;
index 4c10e7e6c9f6ae53291d3f72d82b6c9b0539b552..598d374f6a35f714db4753efa7ab28d6782419a9 100644 (file)
@@ -36,7 +36,8 @@ config RFKILL_REGULATOR
 
 config RFKILL_GPIO
        tristate "GPIO RFKILL driver"
-       depends on RFKILL && GPIOLIB
+       depends on RFKILL
+       depends on GPIOLIB || COMPILE_TEST
        default n
        help
          If you say yes here you get support of a generic gpio RFKILL
index 2a0bbd22854bd97b377139200f9e6b5e0ec2662f..3893409dee95b3ba23c39b369a57c48c6e838d9b 100644 (file)
@@ -407,6 +407,9 @@ use_default_name:
        INIT_LIST_HEAD(&rdev->bss_list);
        INIT_WORK(&rdev->scan_done_wk, __cfg80211_scan_done);
        INIT_WORK(&rdev->sched_scan_results_wk, __cfg80211_sched_scan_results);
+       INIT_LIST_HEAD(&rdev->mlme_unreg);
+       spin_lock_init(&rdev->mlme_unreg_lock);
+       INIT_WORK(&rdev->mlme_unreg_wk, cfg80211_mlme_unreg_wk);
        INIT_DELAYED_WORK(&rdev->dfs_update_channels_wk,
                          cfg80211_dfs_channels_update_work);
 #ifdef CONFIG_CFG80211_WEXT
@@ -802,6 +805,7 @@ void wiphy_unregister(struct wiphy *wiphy)
        cancel_delayed_work_sync(&rdev->dfs_update_channels_wk);
        flush_work(&rdev->destroy_work);
        flush_work(&rdev->sched_scan_stop_wk);
+       flush_work(&rdev->mlme_unreg_wk);
 
 #ifdef CONFIG_PM
        if (rdev->wiphy.wowlan_config && rdev->ops->set_wakeup)
@@ -855,6 +859,7 @@ void cfg80211_unregister_wdev(struct wireless_dev *wdev)
 
        switch (wdev->iftype) {
        case NL80211_IFTYPE_P2P_DEVICE:
+               cfg80211_mlme_purge_registrations(wdev);
                cfg80211_stop_p2p_device(rdev, wdev);
                break;
        default:
index 311eef26bf88b9a0e8125678498583fa19edf688..b9d5bc8c148d32ecb8156d19c2652c49824f41cd 100644 (file)
@@ -59,6 +59,10 @@ struct cfg80211_registered_device {
        struct list_head beacon_registrations;
        spinlock_t beacon_registrations_lock;
 
+       struct list_head mlme_unreg;
+       spinlock_t mlme_unreg_lock;
+       struct work_struct mlme_unreg_wk;
+
        /* protected by RTNL only */
        int num_running_ifaces;
        int num_running_monitor_ifaces;
@@ -348,6 +352,7 @@ void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
 int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_pid,
                                u16 frame_type, const u8 *match_data,
                                int match_len);
+void cfg80211_mlme_unreg_wk(struct work_struct *wk);
 void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlpid);
 void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev);
 int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
index 7aae329e2b4e4a8e3afa943f670852a6e26e89dd..fb44fa3bf4efa750298163a15534572c43229b2e 100644 (file)
@@ -2,6 +2,7 @@
  * cfg80211 MLME SAP interface
  *
  * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2015          Intel Deutschland GmbH
  */
 
 #include <linux/kernel.h>
@@ -389,6 +390,7 @@ void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
 
 struct cfg80211_mgmt_registration {
        struct list_head list;
+       struct wireless_dev *wdev;
 
        u32 nlportid;
 
@@ -399,6 +401,46 @@ struct cfg80211_mgmt_registration {
        u8 match[];
 };
 
+static void
+cfg80211_process_mlme_unregistrations(struct cfg80211_registered_device *rdev)
+{
+       struct cfg80211_mgmt_registration *reg;
+
+       ASSERT_RTNL();
+
+       spin_lock_bh(&rdev->mlme_unreg_lock);
+       while ((reg = list_first_entry_or_null(&rdev->mlme_unreg,
+                                              struct cfg80211_mgmt_registration,
+                                              list))) {
+               list_del(&reg->list);
+               spin_unlock_bh(&rdev->mlme_unreg_lock);
+
+               if (rdev->ops->mgmt_frame_register) {
+                       u16 frame_type = le16_to_cpu(reg->frame_type);
+
+                       rdev_mgmt_frame_register(rdev, reg->wdev,
+                                                frame_type, false);
+               }
+
+               kfree(reg);
+
+               spin_lock_bh(&rdev->mlme_unreg_lock);
+       }
+       spin_unlock_bh(&rdev->mlme_unreg_lock);
+}
+
+void cfg80211_mlme_unreg_wk(struct work_struct *wk)
+{
+       struct cfg80211_registered_device *rdev;
+
+       rdev = container_of(wk, struct cfg80211_registered_device,
+                           mlme_unreg_wk);
+
+       rtnl_lock();
+       cfg80211_process_mlme_unregistrations(rdev);
+       rtnl_unlock();
+}
+
 int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid,
                                u16 frame_type, const u8 *match_data,
                                int match_len)
@@ -449,11 +491,18 @@ int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid,
        nreg->match_len = match_len;
        nreg->nlportid = snd_portid;
        nreg->frame_type = cpu_to_le16(frame_type);
+       nreg->wdev = wdev;
        list_add(&nreg->list, &wdev->mgmt_registrations);
+       spin_unlock_bh(&wdev->mgmt_registrations_lock);
+
+       /* process all unregistrations to avoid driver confusion */
+       cfg80211_process_mlme_unregistrations(rdev);
 
        if (rdev->ops->mgmt_frame_register)
                rdev_mgmt_frame_register(rdev, wdev, frame_type, true);
 
+       return 0;
+
  out:
        spin_unlock_bh(&wdev->mgmt_registrations_lock);
 
@@ -472,15 +521,12 @@ void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlportid)
                if (reg->nlportid != nlportid)
                        continue;
 
-               if (rdev->ops->mgmt_frame_register) {
-                       u16 frame_type = le16_to_cpu(reg->frame_type);
-
-                       rdev_mgmt_frame_register(rdev, wdev,
-                                                frame_type, false);
-               }
-
                list_del(&reg->list);
-               kfree(reg);
+               spin_lock(&rdev->mlme_unreg_lock);
+               list_add_tail(&reg->list, &rdev->mlme_unreg);
+               spin_unlock(&rdev->mlme_unreg_lock);
+
+               schedule_work(&rdev->mlme_unreg_wk);
        }
 
        spin_unlock_bh(&wdev->mgmt_registrations_lock);
@@ -496,16 +542,15 @@ void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlportid)
 
 void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev)
 {
-       struct cfg80211_mgmt_registration *reg, *tmp;
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
 
        spin_lock_bh(&wdev->mgmt_registrations_lock);
-
-       list_for_each_entry_safe(reg, tmp, &wdev->mgmt_registrations, list) {
-               list_del(&reg->list);
-               kfree(reg);
-       }
-
+       spin_lock(&rdev->mlme_unreg_lock);
+       list_splice_tail_init(&wdev->mgmt_registrations, &rdev->mlme_unreg);
+       spin_unlock(&rdev->mlme_unreg_lock);
        spin_unlock_bh(&wdev->mgmt_registrations_lock);
+
+       cfg80211_process_mlme_unregistrations(rdev);
 }
 
 int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
index 76b41578a838e3bed8596c5950ffbd6881d54a64..5d8748b4c8a2d20f76c38a8394888c7fe56924bf 100644 (file)
@@ -2321,6 +2321,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
                        rdev->wiphy.frag_threshold = old_frag_threshold;
                        rdev->wiphy.rts_threshold = old_rts_threshold;
                        rdev->wiphy.coverage_class = old_coverage_class;
+                       return result;
                }
        }
        return 0;
@@ -7390,7 +7391,8 @@ static int nl80211_set_mcast_rate(struct sk_buff *skb, struct genl_info *info)
        int err;
 
        if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC &&
-           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
+           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT &&
+           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_OCB)
                return -EOPNOTSUPP;
 
        if (!rdev->ops->set_mcast_rate)
index c6e83a7468c0c43baea32bbb0291c312017226f5..c23516d0f80794c7277812dd25dac250da4e5f0c 100644 (file)
@@ -733,6 +733,8 @@ static inline void
 rdev_mgmt_frame_register(struct cfg80211_registered_device *rdev,
                         struct wireless_dev *wdev, u16 frame_type, bool reg)
 {
+       might_sleep();
+
        trace_rdev_mgmt_frame_register(&rdev->wiphy, wdev , frame_type, reg);
        rdev->ops->mgmt_frame_register(&rdev->wiphy, wdev , frame_type, reg);
        trace_rdev_return_void(&rdev->wiphy);
index aa2d75482017e1a258eb0cdb7271b2d615d8782e..b144485946f2e5ce2ec6411cd52462fa278b185e 100644 (file)
@@ -1004,7 +1004,7 @@ static u32 map_regdom_flags(u32 rd_flags)
 
 static const struct ieee80211_reg_rule *
 freq_reg_info_regd(struct wiphy *wiphy, u32 center_freq,
-                  const struct ieee80211_regdomain *regd)
+                  const struct ieee80211_regdomain *regd, u32 bw)
 {
        int i;
        bool band_rule_found = false;
@@ -1028,7 +1028,7 @@ freq_reg_info_regd(struct wiphy *wiphy, u32 center_freq,
                if (!band_rule_found)
                        band_rule_found = freq_in_rule_band(fr, center_freq);
 
-               bw_fits = reg_does_bw_fit(fr, center_freq, MHZ_TO_KHZ(20));
+               bw_fits = reg_does_bw_fit(fr, center_freq, bw);
 
                if (band_rule_found && bw_fits)
                        return rr;
@@ -1040,14 +1040,26 @@ freq_reg_info_regd(struct wiphy *wiphy, u32 center_freq,
        return ERR_PTR(-EINVAL);
 }
 
-const struct ieee80211_reg_rule *freq_reg_info(struct wiphy *wiphy,
-                                              u32 center_freq)
+const struct ieee80211_reg_rule *__freq_reg_info(struct wiphy *wiphy,
+                                                u32 center_freq, u32 min_bw)
 {
-       const struct ieee80211_regdomain *regd;
+       const struct ieee80211_regdomain *regd = reg_get_regdomain(wiphy);
+       const struct ieee80211_reg_rule *reg_rule = NULL;
+       u32 bw;
 
-       regd = reg_get_regdomain(wiphy);
+       for (bw = MHZ_TO_KHZ(20); bw >= min_bw; bw = bw / 2) {
+               reg_rule = freq_reg_info_regd(wiphy, center_freq, regd, bw);
+               if (!IS_ERR(reg_rule))
+                       return reg_rule;
+       }
 
-       return freq_reg_info_regd(wiphy, center_freq, regd);
+       return reg_rule;
+}
+
+const struct ieee80211_reg_rule *freq_reg_info(struct wiphy *wiphy,
+                                              u32 center_freq)
+{
+       return __freq_reg_info(wiphy, center_freq, MHZ_TO_KHZ(20));
 }
 EXPORT_SYMBOL(freq_reg_info);
 
@@ -1176,8 +1188,20 @@ static void handle_channel(struct wiphy *wiphy,
        if (reg_rule->flags & NL80211_RRF_AUTO_BW)
                max_bandwidth_khz = reg_get_max_bandwidth(regd, reg_rule);
 
+       /* If we get a reg_rule we can assume that at least 5Mhz fit */
+       if (!reg_does_bw_fit(freq_range, MHZ_TO_KHZ(chan->center_freq),
+                            MHZ_TO_KHZ(10)))
+               bw_flags |= IEEE80211_CHAN_NO_10MHZ;
+       if (!reg_does_bw_fit(freq_range, MHZ_TO_KHZ(chan->center_freq),
+                            MHZ_TO_KHZ(20)))
+               bw_flags |= IEEE80211_CHAN_NO_20MHZ;
+
+       if (max_bandwidth_khz < MHZ_TO_KHZ(10))
+               bw_flags |= IEEE80211_CHAN_NO_10MHZ;
+       if (max_bandwidth_khz < MHZ_TO_KHZ(20))
+               bw_flags |= IEEE80211_CHAN_NO_20MHZ;
        if (max_bandwidth_khz < MHZ_TO_KHZ(40))
-               bw_flags = IEEE80211_CHAN_NO_HT40;
+               bw_flags |= IEEE80211_CHAN_NO_HT40;
        if (max_bandwidth_khz < MHZ_TO_KHZ(80))
                bw_flags |= IEEE80211_CHAN_NO_80MHZ;
        if (max_bandwidth_khz < MHZ_TO_KHZ(160))
@@ -1695,9 +1719,15 @@ static void handle_channel_custom(struct wiphy *wiphy,
        const struct ieee80211_power_rule *power_rule = NULL;
        const struct ieee80211_freq_range *freq_range = NULL;
        u32 max_bandwidth_khz;
+       u32 bw;
 
-       reg_rule = freq_reg_info_regd(wiphy, MHZ_TO_KHZ(chan->center_freq),
-                                     regd);
+       for (bw = MHZ_TO_KHZ(20); bw >= MHZ_TO_KHZ(5); bw = bw / 2) {
+               reg_rule = freq_reg_info_regd(wiphy,
+                                             MHZ_TO_KHZ(chan->center_freq),
+                                             regd, bw);
+               if (!IS_ERR(reg_rule))
+                       break;
+       }
 
        if (IS_ERR(reg_rule)) {
                REG_DBG_PRINT("Disabling freq %d MHz as custom regd has no rule that fits it\n",
@@ -1721,8 +1751,20 @@ static void handle_channel_custom(struct wiphy *wiphy,
        if (reg_rule->flags & NL80211_RRF_AUTO_BW)
                max_bandwidth_khz = reg_get_max_bandwidth(regd, reg_rule);
 
+       /* If we get a reg_rule we can assume that at least 5Mhz fit */
+       if (!reg_does_bw_fit(freq_range, MHZ_TO_KHZ(chan->center_freq),
+                            MHZ_TO_KHZ(10)))
+               bw_flags |= IEEE80211_CHAN_NO_10MHZ;
+       if (!reg_does_bw_fit(freq_range, MHZ_TO_KHZ(chan->center_freq),
+                            MHZ_TO_KHZ(20)))
+               bw_flags |= IEEE80211_CHAN_NO_20MHZ;
+
+       if (max_bandwidth_khz < MHZ_TO_KHZ(10))
+               bw_flags |= IEEE80211_CHAN_NO_10MHZ;
+       if (max_bandwidth_khz < MHZ_TO_KHZ(20))
+               bw_flags |= IEEE80211_CHAN_NO_20MHZ;
        if (max_bandwidth_khz < MHZ_TO_KHZ(40))
-               bw_flags = IEEE80211_CHAN_NO_HT40;
+               bw_flags |= IEEE80211_CHAN_NO_HT40;
        if (max_bandwidth_khz < MHZ_TO_KHZ(80))
                bw_flags |= IEEE80211_CHAN_NO_80MHZ;
        if (max_bandwidth_khz < MHZ_TO_KHZ(160))
@@ -2079,10 +2121,7 @@ static void reg_process_hint(struct regulatory_request *reg_request)
                reg_process_hint_core(reg_request);
                return;
        case NL80211_REGDOM_SET_BY_USER:
-               treatment = reg_process_hint_user(reg_request);
-               if (treatment == REG_REQ_IGNORE ||
-                   treatment == REG_REQ_ALREADY_SET)
-                       return;
+               reg_process_hint_user(reg_request);
                return;
        case NL80211_REGDOM_SET_BY_DRIVER:
                if (!wiphy)
@@ -2099,7 +2138,9 @@ static void reg_process_hint(struct regulatory_request *reg_request)
                goto out_free;
        }
 
-       /* This is required so that the orig_* parameters are saved */
+       /* This is required so that the orig_* parameters are saved.
+        * NOTE: treatment must be set for any case that reaches here!
+        */
        if (treatment == REG_REQ_ALREADY_SET && wiphy &&
            wiphy->regulatory_flags & REGULATORY_STRICT_REG) {
                wiphy_update_regulatory(wiphy, reg_request->initiator);