Merge tag 'mac80211-next-for-davem-2016-09-16' of git://git.kernel.org/pub/scm/linux...
authorDavid S. Miller <davem@davemloft.net>
Mon, 19 Sep 2016 02:29:08 +0000 (22:29 -0400)
committerDavid S. Miller <davem@davemloft.net>
Mon, 19 Sep 2016 02:29:08 +0000 (22:29 -0400)
Johannes Berg says:

====================
This time we have various things - all across the board:
 * MU-MIMO sniffer support in mac80211
 * a create_singlethread_workqueue() cleanup
 * interface dump filtering that was documented but not implemented
 * support for the new radiotap timestamp field
 * send delBA in two unexpected conditions (as required by the spec)
 * connect keys cleanups - allow only WEP with index 0-3
 * per-station aggregation limit to work around broken APs
 * debugfs improvement for the integrated codel algorithm
and various other small improvements and cleanups.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
34 files changed:
drivers/net/wireless/mac80211_hwsim.c
include/net/cfg80211.h
include/net/ieee80211_radiotap.h
include/net/mac80211.h
net/mac80211/agg-rx.c
net/mac80211/cfg.c
net/mac80211/debugfs.c
net/mac80211/debugfs_netdev.c
net/mac80211/debugfs_sta.c
net/mac80211/driver-ops.c
net/mac80211/driver-ops.h
net/mac80211/ieee80211_i.h
net/mac80211/iface.c
net/mac80211/main.c
net/mac80211/mlme.c
net/mac80211/pm.c
net/mac80211/rx.c
net/mac80211/scan.c
net/mac80211/sta_info.c
net/mac80211/sta_info.h
net/mac80211/status.c
net/mac80211/tx.c
net/mac80211/util.c
net/wireless/core.c
net/wireless/core.h
net/wireless/ibss.c
net/wireless/mlme.c
net/wireless/nl80211.c
net/wireless/scan.c
net/wireless/sme.c
net/wireless/sysfs.c
net/wireless/util.c
net/wireless/wext-compat.c
net/wireless/wext-sme.c

index 8c35ac8..431f13b 100644 (file)
@@ -487,7 +487,7 @@ static const struct ieee80211_iface_combination hwsim_if_comb_p2p_dev[] = {
 };
 
 static spinlock_t hwsim_radio_lock;
-static struct list_head hwsim_radios;
+static LIST_HEAD(hwsim_radios);
 static int hwsim_radio_idx;
 
 static struct platform_driver mac80211_hwsim_driver = {
@@ -3376,7 +3376,6 @@ static int __init init_mac80211_hwsim(void)
                mac80211_hwsim_unassign_vif_chanctx;
 
        spin_lock_init(&hwsim_radio_lock);
-       INIT_LIST_HEAD(&hwsim_radios);
 
        err = register_pernet_device(&hwsim_net_ops);
        if (err)
index beb7610..bd26cc6 100644 (file)
@@ -2432,7 +2432,8 @@ struct cfg80211_qos_map {
  *     cases, the result of roaming is indicated with a call to
  *     cfg80211_roamed() or cfg80211_roamed_bss().
  *     (invoked with the wireless_dev mutex held)
- * @disconnect: Disconnect from the BSS/ESS.
+ * @disconnect: Disconnect from the BSS/ESS. Once done, call
+ *     cfg80211_disconnected().
  *     (invoked with the wireless_dev mutex held)
  *
  * @join_ibss: Join the specified IBSS (or create if necessary). Once done, call
@@ -3954,6 +3955,34 @@ void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list,
 unsigned int cfg80211_classify8021d(struct sk_buff *skb,
                                    struct cfg80211_qos_map *qos_map);
 
+/**
+ * cfg80211_find_ie_match - match information element and byte array in data
+ *
+ * @eid: element ID
+ * @ies: data consisting of IEs
+ * @len: length of data
+ * @match: byte array to match
+ * @match_len: number of bytes in the match array
+ * @match_offset: offset in the IE where the byte array should match.
+ *     If match_len is zero, this must also be set to zero.
+ *     Otherwise this must be set to 2 or more, because the first
+ *     byte is the element id, which is already compared to eid, and
+ *     the second byte is the IE length.
+ *
+ * Return: %NULL if the element ID could not be found or if
+ * the element is invalid (claims to be longer than the given
+ * data) or if the byte array doesn't match, or a pointer to the first
+ * byte of the requested element, that is the byte containing the
+ * element ID.
+ *
+ * Note: There are no checks on the element length other than
+ * having to fit into the given data and being large enough for the
+ * byte array to match.
+ */
+const u8 *cfg80211_find_ie_match(u8 eid, const u8 *ies, int len,
+                                const u8 *match, int match_len,
+                                int match_offset);
+
 /**
  * cfg80211_find_ie - find information element in data
  *
@@ -3969,7 +3998,10 @@ unsigned int cfg80211_classify8021d(struct sk_buff *skb,
  * Note: There are no checks on the element length other than
  * having to fit into the given data.
  */
-const u8 *cfg80211_find_ie(u8 eid, const u8 *ies, int len);
+static inline const u8 *cfg80211_find_ie(u8 eid, const u8 *ies, int len)
+{
+       return cfg80211_find_ie_match(eid, ies, len, NULL, 0, 0);
+}
 
 /**
  * cfg80211_find_vendor_ie - find vendor specific information element in data
index b0fd947..ba07b9d 100644 (file)
@@ -190,6 +190,10 @@ struct ieee80211_radiotap_header {
  * IEEE80211_RADIOTAP_VHT      u16, u8, u8, u8[4], u8, u8, u16
  *
  *     Contains VHT information about this frame.
+ *
+ * IEEE80211_RADIOTAP_TIMESTAMP                u64, u16, u8, u8        variable
+ *
+ *     Contains timestamp information for this frame.
  */
 enum ieee80211_radiotap_type {
        IEEE80211_RADIOTAP_TSFT = 0,
@@ -214,6 +218,7 @@ enum ieee80211_radiotap_type {
        IEEE80211_RADIOTAP_MCS = 19,
        IEEE80211_RADIOTAP_AMPDU_STATUS = 20,
        IEEE80211_RADIOTAP_VHT = 21,
+       IEEE80211_RADIOTAP_TIMESTAMP = 22,
 
        /* valid in every it_present bitmap, even vendor namespaces */
        IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE = 29,
@@ -321,6 +326,22 @@ enum ieee80211_radiotap_type {
 #define IEEE80211_RADIOTAP_CODING_LDPC_USER2                   0x04
 #define IEEE80211_RADIOTAP_CODING_LDPC_USER3                   0x08
 
+/* For IEEE80211_RADIOTAP_TIMESTAMP */
+#define IEEE80211_RADIOTAP_TIMESTAMP_UNIT_MASK                 0x000F
+#define IEEE80211_RADIOTAP_TIMESTAMP_UNIT_MS                   0x0000
+#define IEEE80211_RADIOTAP_TIMESTAMP_UNIT_US                   0x0001
+#define IEEE80211_RADIOTAP_TIMESTAMP_UNIT_NS                   0x0003
+#define IEEE80211_RADIOTAP_TIMESTAMP_SPOS_MASK                 0x00F0
+#define IEEE80211_RADIOTAP_TIMESTAMP_SPOS_BEGIN_MDPU           0x0000
+#define IEEE80211_RADIOTAP_TIMESTAMP_SPOS_EO_MPDU              0x0010
+#define IEEE80211_RADIOTAP_TIMESTAMP_SPOS_EO_PPDU              0x0020
+#define IEEE80211_RADIOTAP_TIMESTAMP_SPOS_PLCP_SIG_ACQ         0x0030
+#define IEEE80211_RADIOTAP_TIMESTAMP_SPOS_UNKNOWN              0x00F0
+
+#define IEEE80211_RADIOTAP_TIMESTAMP_FLAG_64BIT                        0x00
+#define IEEE80211_RADIOTAP_TIMESTAMP_FLAG_32BIT                        0x01
+#define IEEE80211_RADIOTAP_TIMESTAMP_FLAG_ACCURACY             0x02
+
 /* helpers */
 static inline int ieee80211_get_radiotap_len(unsigned char *data)
 {
index cca510a..5296100 100644 (file)
@@ -1735,6 +1735,9 @@ struct ieee80211_sta_rates {
  * @supp_rates: Bitmap of supported rates (per band)
  * @ht_cap: HT capabilities of this STA; restricted to our own capabilities
  * @vht_cap: VHT capabilities of this STA; restricted to our own capabilities
+ * @max_rx_aggregation_subframes: maximal amount of frames in a single AMPDU
+ *     that this station is allowed to transmit to us.
+ *     Can be modified by driver.
  * @wme: indicates whether the STA supports QoS/WME (if local devices does,
  *     otherwise always false)
  * @drv_priv: data area for driver use, will always be aligned to
@@ -1775,6 +1778,7 @@ struct ieee80211_sta {
        u16 aid;
        struct ieee80211_sta_ht_cap ht_cap;
        struct ieee80211_sta_vht_cap vht_cap;
+       u8 max_rx_aggregation_subframes;
        bool wme;
        u8 uapsd_queues;
        u8 max_sp;
@@ -2014,6 +2018,11 @@ struct ieee80211_txq {
  * @IEEE80211_HW_TX_FRAG_LIST: Hardware (or driver) supports sending frag_list
  *     skbs, needed for zero-copy software A-MSDU.
  *
+ * @IEEE80211_HW_REPORTS_LOW_ACK: The driver (or firmware) reports low ack event
+ *     by ieee80211_report_low_ack() based on its own algorithm. For such
+ *     drivers, mac80211 packet loss mechanism will not be triggered and driver
+ *     is completely depending on firmware event for station kickout.
+ *
  * @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays
  */
 enum ieee80211_hw_flags {
@@ -2054,6 +2063,7 @@ enum ieee80211_hw_flags {
        IEEE80211_HW_USES_RSS,
        IEEE80211_HW_TX_AMSDU,
        IEEE80211_HW_TX_FRAG_LIST,
+       IEEE80211_HW_REPORTS_LOW_ACK,
 
        /* keep last, obviously */
        NUM_IEEE80211_HW_FLAGS
@@ -2141,6 +2151,14 @@ enum ieee80211_hw_flags {
  *     the default is _GI | _BANDWIDTH.
  *     Use the %IEEE80211_RADIOTAP_VHT_KNOWN_* values.
  *
+ * @radiotap_timestamp: Information for the radiotap timestamp field; if the
+ *     'units_pos' member is set to a non-negative value it must be set to
+ *     a combination of a IEEE80211_RADIOTAP_TIMESTAMP_UNIT_* and a
+ *     IEEE80211_RADIOTAP_TIMESTAMP_SPOS_* value, and then the timestamp
+ *     field will be added and populated from the &struct ieee80211_rx_status
+ *     device_timestamp. If the 'accuracy' member is non-negative, it's put
+ *     into the accuracy radiotap field and the accuracy known flag is set.
+ *
  * @netdev_features: netdev features to be set in each netdev created
  *     from this HW. Note that not all features are usable with mac80211,
  *     other features will be rejected during HW registration.
@@ -2184,6 +2202,10 @@ struct ieee80211_hw {
        u8 offchannel_tx_hw_queue;
        u8 radiotap_mcs_details;
        u16 radiotap_vht_details;
+       struct {
+               int units_pos;
+               s16 accuracy;
+       } radiotap_timestamp;
        netdev_features_t netdev_features;
        u8 uapsd_queues;
        u8 uapsd_max_sp_len;
@@ -3085,11 +3107,8 @@ enum ieee80211_reconfig_type {
  *
  * @sta_add_debugfs: Drivers can use this callback to add debugfs files
  *     when a station is added to mac80211's station list. This callback
- *     and @sta_remove_debugfs should be within a CONFIG_MAC80211_DEBUGFS
- *     conditional. This callback can sleep.
- *
- * @sta_remove_debugfs: Remove the debugfs files which were added using
- *     @sta_add_debugfs. This callback can sleep.
+ *     should be within a CONFIG_MAC80211_DEBUGFS conditional. This
+ *     callback can sleep.
  *
  * @sta_notify: Notifies low level driver about power state transition of an
  *     associated station, AP,  IBSS/WDS/mesh peer etc. For a VIF operating
@@ -3485,10 +3504,6 @@ struct ieee80211_ops {
                                struct ieee80211_vif *vif,
                                struct ieee80211_sta *sta,
                                struct dentry *dir);
-       void (*sta_remove_debugfs)(struct ieee80211_hw *hw,
-                                  struct ieee80211_vif *vif,
-                                  struct ieee80211_sta *sta,
-                                  struct dentry *dir);
 #endif
        void (*sta_notify)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                        enum sta_notify_cmd, struct ieee80211_sta *sta);
index a9aff60..a5d69df 100644 (file)
@@ -298,10 +298,13 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta,
                buf_size = IEEE80211_MAX_AMPDU_BUF;
 
        /* make sure the size doesn't exceed the maximum supported by the hw */
-       if (buf_size > local->hw.max_rx_aggregation_subframes)
-               buf_size = local->hw.max_rx_aggregation_subframes;
+       if (buf_size > sta->sta.max_rx_aggregation_subframes)
+               buf_size = sta->sta.max_rx_aggregation_subframes;
        params.buf_size = buf_size;
 
+       ht_dbg(sta->sdata, "AddBA Req buf_size=%d for %pM\n",
+              buf_size, sta->sta.addr);
+
        /* examine state machine */
        mutex_lock(&sta->ampdu_mlme.mtx);
 
@@ -406,8 +409,10 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta,
        }
 
 end:
-       if (status == WLAN_STATUS_SUCCESS)
+       if (status == WLAN_STATUS_SUCCESS) {
                __set_bit(tid, sta->ampdu_mlme.agg_session_valid);
+               __clear_bit(tid, sta->ampdu_mlme.unexpected_agg);
+       }
        mutex_unlock(&sta->ampdu_mlme.mtx);
 
 end_no_lock:
index 543b1d4..e29ff57 100644 (file)
@@ -39,7 +39,7 @@ static struct wireless_dev *ieee80211_add_iface(struct wiphy *wiphy,
 
        if (type == NL80211_IFTYPE_MONITOR && flags) {
                sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
-               sdata->u.mntr_flags = *flags;
+               sdata->u.mntr.flags = *flags;
        }
 
        return wdev;
@@ -73,8 +73,29 @@ static int ieee80211_change_iface(struct wiphy *wiphy,
                sdata->u.mgd.use_4addr = params->use_4addr;
        }
 
-       if (sdata->vif.type == NL80211_IFTYPE_MONITOR && flags) {
+       if (sdata->vif.type == NL80211_IFTYPE_MONITOR) {
                struct ieee80211_local *local = sdata->local;
+               struct ieee80211_sub_if_data *monitor_sdata;
+               u32 mu_mntr_cap_flag = NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER;
+
+               monitor_sdata = rtnl_dereference(local->monitor_sdata);
+               if (monitor_sdata &&
+                   wiphy_ext_feature_isset(wiphy, mu_mntr_cap_flag)) {
+                       memcpy(monitor_sdata->vif.bss_conf.mu_group.membership,
+                              params->vht_mumimo_groups, WLAN_MEMBERSHIP_LEN);
+                       memcpy(monitor_sdata->vif.bss_conf.mu_group.position,
+                              params->vht_mumimo_groups + WLAN_MEMBERSHIP_LEN,
+                              WLAN_USER_POSITION_LEN);
+                       monitor_sdata->vif.mu_mimo_owner = true;
+                       ieee80211_bss_info_change_notify(monitor_sdata,
+                                                        BSS_CHANGED_MU_GROUPS);
+
+                       ether_addr_copy(monitor_sdata->u.mntr.mu_follow_addr,
+                                       params->macaddr);
+               }
+
+               if (!flags)
+                       return 0;
 
                if (ieee80211_sdata_running(sdata)) {
                        u32 mask = MONITOR_FLAG_COOK_FRAMES |
@@ -89,11 +110,11 @@ static int ieee80211_change_iface(struct wiphy *wiphy,
                         *      cooked_mntrs, monitor and all fif_* counters
                         *      reconfigure hardware
                         */
-                       if ((*flags & mask) != (sdata->u.mntr_flags & mask))
+                       if ((*flags & mask) != (sdata->u.mntr.flags & mask))
                                return -EBUSY;
 
                        ieee80211_adjust_monitor_flags(sdata, -1);
-                       sdata->u.mntr_flags = *flags;
+                       sdata->u.mntr.flags = *flags;
                        ieee80211_adjust_monitor_flags(sdata, 1);
 
                        ieee80211_configure_filter(local);
@@ -103,7 +124,7 @@ static int ieee80211_change_iface(struct wiphy *wiphy,
                         * and ieee80211_do_open take care of "everything"
                         * mentioned in the comment above.
                         */
-                       sdata->u.mntr_flags = *flags;
+                       sdata->u.mntr.flags = *flags;
                }
        }
 
@@ -2940,10 +2961,6 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
        }
 
        chanctx = container_of(conf, struct ieee80211_chanctx, conf);
-       if (!chanctx) {
-               err = -EBUSY;
-               goto out;
-       }
 
        ch_switch.timestamp = 0;
        ch_switch.device_timestamp = 0;
index 2906c10..8ca62b6 100644 (file)
@@ -71,138 +71,39 @@ DEBUGFS_READONLY_FILE(wep_iv, "%#08x",
 DEBUGFS_READONLY_FILE(rate_ctrl_alg, "%s",
        local->rate_ctrl ? local->rate_ctrl->ops->name : "hw/driver");
 
-struct aqm_info {
-       struct ieee80211_local *local;
-       size_t size;
-       size_t len;
-       unsigned char buf[0];
-};
-
-#define AQM_HDR_LEN 200
-#define AQM_HW_ENTRY_LEN 40
-#define AQM_TXQ_ENTRY_LEN 110
-
-static int aqm_open(struct inode *inode, struct file *file)
+static ssize_t aqm_read(struct file *file,
+                       char __user *user_buf,
+                       size_t count,
+                       loff_t *ppos)
 {
-       struct ieee80211_local *local = inode->i_private;
-       struct ieee80211_sub_if_data *sdata;
-       struct sta_info *sta;
-       struct txq_info *txqi;
+       struct ieee80211_local *local = file->private_data;
        struct fq *fq = &local->fq;
-       struct aqm_info *info = NULL;
+       char buf[200];
        int len = 0;
-       int i;
-
-       if (!local->ops->wake_tx_queue)
-               return -EOPNOTSUPP;
-
-       len += AQM_HDR_LEN;
-       len += 6 * AQM_HW_ENTRY_LEN;
-
-       rcu_read_lock();
-       list_for_each_entry_rcu(sdata, &local->interfaces, list)
-               len += AQM_TXQ_ENTRY_LEN;
-       list_for_each_entry_rcu(sta, &local->sta_list, list)
-               len += AQM_TXQ_ENTRY_LEN * ARRAY_SIZE(sta->sta.txq);
-       rcu_read_unlock();
-
-       info = vmalloc(len);
-       if (!info)
-               return -ENOMEM;
 
        spin_lock_bh(&local->fq.lock);
        rcu_read_lock();
 
-       file->private_data = info;
-       info->local = local;
-       info->size = len;
-       len = 0;
-
-       len += scnprintf(info->buf + len, info->size - len,
-                        "* hw\n"
-                        "access name value\n"
-                        "R fq_flows_cnt %u\n"
-                        "R fq_backlog %u\n"
-                        "R fq_overlimit %u\n"
-                        "R fq_collisions %u\n"
-                        "RW fq_limit %u\n"
-                        "RW fq_quantum %u\n",
-                        fq->flows_cnt,
-                        fq->backlog,
-                        fq->overlimit,
-                        fq->collisions,
-                        fq->limit,
-                        fq->quantum);
-
-       len += scnprintf(info->buf + len,
-                        info->size - len,
-                        "* vif\n"
-                        "ifname addr ac backlog-bytes backlog-packets flows overlimit collisions tx-bytes tx-packets\n");
-
-       list_for_each_entry_rcu(sdata, &local->interfaces, list) {
-               txqi = to_txq_info(sdata->vif.txq);
-               len += scnprintf(info->buf + len, info->size - len,
-                                "%s %pM %u %u %u %u %u %u %u %u\n",
-                                sdata->name,
-                                sdata->vif.addr,
-                                txqi->txq.ac,
-                                txqi->tin.backlog_bytes,
-                                txqi->tin.backlog_packets,
-                                txqi->tin.flows,
-                                txqi->tin.overlimit,
-                                txqi->tin.collisions,
-                                txqi->tin.tx_bytes,
-                                txqi->tin.tx_packets);
-       }
-
-       len += scnprintf(info->buf + len,
-                        info->size - len,
-                        "* sta\n"
-                        "ifname addr tid ac backlog-bytes backlog-packets flows overlimit collisions tx-bytes tx-packets\n");
-
-       list_for_each_entry_rcu(sta, &local->sta_list, list) {
-               sdata = sta->sdata;
-               for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) {
-                       txqi = to_txq_info(sta->sta.txq[i]);
-                       len += scnprintf(info->buf + len, info->size - len,
-                                        "%s %pM %d %d %u %u %u %u %u %u %u\n",
-                                        sdata->name,
-                                        sta->sta.addr,
-                                        txqi->txq.tid,
-                                        txqi->txq.ac,
-                                        txqi->tin.backlog_bytes,
-                                        txqi->tin.backlog_packets,
-                                        txqi->tin.flows,
-                                        txqi->tin.overlimit,
-                                        txqi->tin.collisions,
-                                        txqi->tin.tx_bytes,
-                                        txqi->tin.tx_packets);
-               }
-       }
-
-       info->len = len;
+       len = scnprintf(buf, sizeof(buf),
+                       "access name value\n"
+                       "R fq_flows_cnt %u\n"
+                       "R fq_backlog %u\n"
+                       "R fq_overlimit %u\n"
+                       "R fq_collisions %u\n"
+                       "RW fq_limit %u\n"
+                       "RW fq_quantum %u\n",
+                       fq->flows_cnt,
+                       fq->backlog,
+                       fq->overlimit,
+                       fq->collisions,
+                       fq->limit,
+                       fq->quantum);
 
        rcu_read_unlock();
        spin_unlock_bh(&local->fq.lock);
 
-       return 0;
-}
-
-static int aqm_release(struct inode *inode, struct file *file)
-{
-       vfree(file->private_data);
-       return 0;
-}
-
-static ssize_t aqm_read(struct file *file,
-                       char __user *user_buf,
-                       size_t count,
-                       loff_t *ppos)
-{
-       struct aqm_info *info = file->private_data;
-
        return simple_read_from_buffer(user_buf, count, ppos,
-                                      info->buf, info->len);
+                                      buf, len);
 }
 
 static ssize_t aqm_write(struct file *file,
@@ -210,8 +111,7 @@ static ssize_t aqm_write(struct file *file,
                         size_t count,
                         loff_t *ppos)
 {
-       struct aqm_info *info = file->private_data;
-       struct ieee80211_local *local = info->local;
+       struct ieee80211_local *local = file->private_data;
        char buf[100];
        size_t len;
 
@@ -237,8 +137,7 @@ static ssize_t aqm_write(struct file *file,
 static const struct file_operations aqm_ops = {
        .write = aqm_write,
        .read = aqm_read,
-       .open = aqm_open,
-       .release = aqm_release,
+       .open = simple_open,
        .llseek = default_llseek,
 };
 
@@ -302,6 +201,7 @@ static const char *hw_flag_names[] = {
        FLAG(USES_RSS),
        FLAG(TX_AMSDU),
        FLAG(TX_FRAG_LIST),
+       FLAG(REPORTS_LOW_ACK),
 #undef FLAG
 };
 
@@ -428,7 +328,9 @@ void debugfs_hw_add(struct ieee80211_local *local)
        DEBUGFS_ADD(hwflags);
        DEBUGFS_ADD(user_power);
        DEBUGFS_ADD(power);
-       DEBUGFS_ADD_MODE(aqm, 0600);
+
+       if (local->ops->wake_tx_queue)
+               DEBUGFS_ADD_MODE(aqm, 0600);
 
        statsd = debugfs_create_dir("statistics", phyd);
 
index a5ba739..5d35c0f 100644 (file)
@@ -30,7 +30,7 @@ static ssize_t ieee80211_if_read(
        size_t count, loff_t *ppos,
        ssize_t (*format)(const struct ieee80211_sub_if_data *, char *, int))
 {
-       char buf[70];
+       char buf[200];
        ssize_t ret = -EINVAL;
 
        read_lock(&dev_base_lock);
@@ -486,6 +486,38 @@ static ssize_t ieee80211_if_fmt_num_buffered_multicast(
 }
 IEEE80211_IF_FILE_R(num_buffered_multicast);
 
+static ssize_t ieee80211_if_fmt_aqm(
+       const struct ieee80211_sub_if_data *sdata, char *buf, int buflen)
+{
+       struct ieee80211_local *local = sdata->local;
+       struct txq_info *txqi = to_txq_info(sdata->vif.txq);
+       int len;
+
+       spin_lock_bh(&local->fq.lock);
+       rcu_read_lock();
+
+       len = scnprintf(buf,
+                       buflen,
+                       "ac backlog-bytes backlog-packets new-flows drops marks overlimit collisions tx-bytes tx-packets\n"
+                       "%u %u %u %u %u %u %u %u %u %u\n",
+                       txqi->txq.ac,
+                       txqi->tin.backlog_bytes,
+                       txqi->tin.backlog_packets,
+                       txqi->tin.flows,
+                       txqi->cstats.drop_count,
+                       txqi->cstats.ecn_mark,
+                       txqi->tin.overlimit,
+                       txqi->tin.collisions,
+                       txqi->tin.tx_bytes,
+                       txqi->tin.tx_packets);
+
+       rcu_read_unlock();
+       spin_unlock_bh(&local->fq.lock);
+
+       return len;
+}
+IEEE80211_IF_FILE_R(aqm);
+
 /* IBSS attributes */
 static ssize_t ieee80211_if_fmt_tsf(
        const struct ieee80211_sub_if_data *sdata, char *buf, int buflen)
@@ -618,6 +650,9 @@ static void add_common_files(struct ieee80211_sub_if_data *sdata)
        DEBUGFS_ADD(rc_rateidx_vht_mcs_mask_2ghz);
        DEBUGFS_ADD(rc_rateidx_vht_mcs_mask_5ghz);
        DEBUGFS_ADD(hw_queues);
+
+       if (sdata->local->ops->wake_tx_queue)
+               DEBUGFS_ADD(aqm);
 }
 
 static void add_sta_files(struct ieee80211_sub_if_data *sdata)
index fd33413..a2fcdb4 100644 (file)
@@ -133,6 +133,55 @@ static ssize_t sta_last_seq_ctrl_read(struct file *file, char __user *userbuf,
 }
 STA_OPS(last_seq_ctrl);
 
+#define AQM_TXQ_ENTRY_LEN 130
+
+static ssize_t sta_aqm_read(struct file *file, char __user *userbuf,
+                       size_t count, loff_t *ppos)
+{
+       struct sta_info *sta = file->private_data;
+       struct ieee80211_local *local = sta->local;
+       size_t bufsz = AQM_TXQ_ENTRY_LEN*(IEEE80211_NUM_TIDS+1);
+       char *buf = kzalloc(bufsz, GFP_KERNEL), *p = buf;
+       struct txq_info *txqi;
+       ssize_t rv;
+       int i;
+
+       if (!buf)
+               return -ENOMEM;
+
+       spin_lock_bh(&local->fq.lock);
+       rcu_read_lock();
+
+       p += scnprintf(p,
+                      bufsz+buf-p,
+                      "tid ac backlog-bytes backlog-packets new-flows drops marks overlimit collisions tx-bytes tx-packets\n");
+
+       for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
+               txqi = to_txq_info(sta->sta.txq[i]);
+               p += scnprintf(p, bufsz+buf-p,
+                              "%d %d %u %u %u %u %u %u %u %u %u\n",
+                              txqi->txq.tid,
+                              txqi->txq.ac,
+                              txqi->tin.backlog_bytes,
+                              txqi->tin.backlog_packets,
+                              txqi->tin.flows,
+                              txqi->cstats.drop_count,
+                              txqi->cstats.ecn_mark,
+                              txqi->tin.overlimit,
+                              txqi->tin.collisions,
+                              txqi->tin.tx_bytes,
+                              txqi->tin.tx_packets);
+       }
+
+       rcu_read_unlock();
+       spin_unlock_bh(&local->fq.lock);
+
+       rv = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
+       kfree(buf);
+       return rv;
+}
+STA_OPS(aqm);
+
 static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf,
                                        size_t count, loff_t *ppos)
 {
@@ -478,6 +527,9 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta)
        DEBUGFS_ADD_COUNTER(rx_fragments, rx_stats.fragments);
        DEBUGFS_ADD_COUNTER(tx_filtered, status_stats.filtered);
 
+       if (local->ops->wake_tx_queue)
+               DEBUGFS_ADD(aqm);
+
        if (sizeof(sta->driver_buffered_tids) == sizeof(u32))
                debugfs_create_x32("driver_buffered_tids", 0400,
                                   sta->debugfs_dir,
@@ -492,10 +544,6 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta)
 
 void ieee80211_sta_debugfs_remove(struct sta_info *sta)
 {
-       struct ieee80211_local *local = sta->local;
-       struct ieee80211_sub_if_data *sdata = sta->sdata;
-
-       drv_sta_remove_debugfs(local, sdata, &sta->sta, sta->debugfs_dir);
        debugfs_remove_recursive(sta->debugfs_dir);
        sta->debugfs_dir = NULL;
 }
index c258f10..c701b64 100644 (file)
@@ -62,7 +62,7 @@ int drv_add_interface(struct ieee80211_local *local,
        if (WARN_ON(sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
                    (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
                     !ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF) &&
-                    !(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE))))
+                    !(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE))))
                return -EINVAL;
 
        trace_drv_add_interface(local, sdata);
index 42a41ae..fe35a1c 100644 (file)
@@ -162,7 +162,8 @@ static inline void drv_bss_info_changed(struct ieee80211_local *local,
                return;
 
        if (WARN_ON_ONCE(sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE ||
-                        sdata->vif.type == NL80211_IFTYPE_MONITOR))
+                        (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
+                         !sdata->vif.mu_mimo_owner)))
                return;
 
        if (!check_sdata_in_driver(sdata))
@@ -498,21 +499,6 @@ static inline void drv_sta_add_debugfs(struct ieee80211_local *local,
                local->ops->sta_add_debugfs(&local->hw, &sdata->vif,
                                            sta, dir);
 }
-
-static inline void drv_sta_remove_debugfs(struct ieee80211_local *local,
-                                         struct ieee80211_sub_if_data *sdata,
-                                         struct ieee80211_sta *sta,
-                                         struct dentry *dir)
-{
-       might_sleep();
-
-       sdata = get_bss_sdata(sdata);
-       check_sdata_in_driver(sdata);
-
-       if (local->ops->sta_remove_debugfs)
-               local->ops->sta_remove_debugfs(&local->hw, &sdata->vif,
-                                              sta, dir);
-}
 #endif
 
 static inline void drv_sta_pre_rcu_remove(struct ieee80211_local *local,
index f56d342..c71c735 100644 (file)
@@ -3,7 +3,7 @@
  * Copyright 2005, Devicescape Software, Inc.
  * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
  * Copyright 2007-2010 Johannes Berg <johannes@sipsolutions.net>
- * Copyright 2013-2014  Intel Mobile Communications GmbH
+ * Copyright 2013-2015  Intel Mobile Communications 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
@@ -818,12 +818,18 @@ struct txq_info {
        struct fq_tin tin;
        struct fq_flow def_flow;
        struct codel_vars def_cvars;
+       struct codel_stats cstats;
        unsigned long flags;
 
        /* keep last! */
        struct ieee80211_txq txq;
 };
 
+struct ieee80211_if_mntr {
+       u32 flags;
+       u8 mu_follow_addr[ETH_ALEN] __aligned(2);
+};
+
 struct ieee80211_sub_if_data {
        struct list_head list;
 
@@ -922,7 +928,7 @@ struct ieee80211_sub_if_data {
                struct ieee80211_if_ibss ibss;
                struct ieee80211_if_mesh mesh;
                struct ieee80211_if_ocb ocb;
-               u32 mntr_flags;
+               struct ieee80211_if_mntr mntr;
        } u;
 
 #ifdef CONFIG_MAC80211_DEBUGFS
@@ -1112,7 +1118,6 @@ struct ieee80211_local {
        struct fq fq;
        struct codel_vars *cvars;
        struct codel_params cparams;
-       struct codel_stats cstats;
 
        const struct ieee80211_ops *ops;
 
index b123a9e..b0abddc 100644 (file)
@@ -43,6 +43,8 @@
  * by either the RTNL, the iflist_mtx or RCU.
  */
 
+static void ieee80211_iface_work(struct work_struct *work);
+
 bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata)
 {
        struct ieee80211_chanctx_conf *chanctx_conf;
@@ -188,7 +190,7 @@ static int ieee80211_verify_mac(struct ieee80211_sub_if_data *sdata, u8 *addr,
                        continue;
 
                if (iter->vif.type == NL80211_IFTYPE_MONITOR &&
-                   !(iter->u.mntr_flags & MONITOR_FLAG_ACTIVE))
+                   !(iter->u.mntr.flags & MONITOR_FLAG_ACTIVE))
                        continue;
 
                m = iter->vif.addr;
@@ -217,7 +219,7 @@ static int ieee80211_change_mac(struct net_device *dev, void *addr)
                return -EBUSY;
 
        if (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
-           !(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE))
+           !(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE))
                check_dup = false;
 
        ret = ieee80211_verify_mac(sdata, sa->sa_data, check_dup);
@@ -357,7 +359,7 @@ void ieee80211_adjust_monitor_flags(struct ieee80211_sub_if_data *sdata,
                                    const int offset)
 {
        struct ieee80211_local *local = sdata->local;
-       u32 flags = sdata->u.mntr_flags;
+       u32 flags = sdata->u.mntr.flags;
 
 #define ADJUST(_f, _s) do {                                    \
        if (flags & MONITOR_FLAG_##_f)                          \
@@ -448,6 +450,9 @@ int ieee80211_add_virtual_monitor(struct ieee80211_local *local)
                return ret;
        }
 
+       skb_queue_head_init(&sdata->skb_queue);
+       INIT_WORK(&sdata->work, ieee80211_iface_work);
+
        return 0;
 }
 
@@ -589,12 +594,12 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
                }
                break;
        case NL80211_IFTYPE_MONITOR:
-               if (sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES) {
+               if (sdata->u.mntr.flags & MONITOR_FLAG_COOK_FRAMES) {
                        local->cooked_mntrs++;
                        break;
                }
 
-               if (sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE) {
+               if (sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE) {
                        res = drv_add_interface(local, sdata);
                        if (res)
                                goto err_stop;
@@ -926,7 +931,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
                /* no need to tell driver */
                break;
        case NL80211_IFTYPE_MONITOR:
-               if (sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES) {
+               if (sdata->u.mntr.flags & MONITOR_FLAG_COOK_FRAMES) {
                        local->cooked_mntrs--;
                        break;
                }
@@ -1012,7 +1017,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
                ieee80211_recalc_idle(local);
                mutex_unlock(&local->mtx);
 
-               if (!(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE))
+               if (!(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE))
                        break;
 
                /* fall through */
@@ -1444,7 +1449,7 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
        case NL80211_IFTYPE_MONITOR:
                sdata->dev->type = ARPHRD_IEEE80211_RADIOTAP;
                sdata->dev->netdev_ops = &ieee80211_monitorif_ops;
-               sdata->u.mntr_flags = MONITOR_FLAG_CONTROL |
+               sdata->u.mntr.flags = MONITOR_FLAG_CONTROL |
                                      MONITOR_FLAG_OTHER_BSS;
                break;
        case NL80211_IFTYPE_WDS:
index d00ea9b..ac053a9 100644 (file)
@@ -660,6 +660,9 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,
 
        ieee80211_roc_setup(local);
 
+       local->hw.radiotap_timestamp.units_pos = -1;
+       local->hw.radiotap_timestamp.accuracy = -1;
+
        return &local->hw;
  err_free:
        wiphy_free(wiphy);
index 8d426f6..7486f2d 100644 (file)
@@ -1672,11 +1672,15 @@ __ieee80211_sta_handle_tspec_ac_params(struct ieee80211_sub_if_data *sdata)
                             non_acm_ac++)
                                if (!(sdata->wmm_acm & BIT(7 - 2 * non_acm_ac)))
                                        break;
-                       /* The loop will result in using BK even if it requires
-                        * admission control, such configuration makes no sense
-                        * and we have to transmit somehow - the AC selection
-                        * does the same thing.
+                       /* Usually the loop will result in using BK even if it
+                        * requires admission control, but such a configuration
+                        * makes no sense and we have to transmit somehow - the
+                        * AC selection does the same thing.
+                        * If we started out trying to downgrade from BK, then
+                        * the extra condition here might be needed.
                         */
+                       if (non_acm_ac >= IEEE80211_NUM_ACS)
+                               non_acm_ac = IEEE80211_AC_BK;
                        if (drv_conf_tx(local, sdata, ac,
                                        &sdata->tx_conf[non_acm_ac]))
                                sdata_err(sdata,
index 00a43a7..28a3a09 100644 (file)
@@ -178,8 +178,7 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
        WARN_ON(!list_empty(&local->chanctx_list));
 
        /* stop hardware - this must stop RX */
-       if (local->open_count)
-               ieee80211_stop_device(local);
+       ieee80211_stop_device(local);
 
  suspend:
        local->suspended = true;
index 9dce3b1..e796060 100644 (file)
@@ -180,6 +180,11 @@ ieee80211_rx_radiotap_hdrlen(struct ieee80211_local *local,
                len += 12;
        }
 
+       if (local->hw.radiotap_timestamp.units_pos >= 0) {
+               len = ALIGN(len, 8);
+               len += 12;
+       }
+
        if (status->chains) {
                /* antenna and antenna signal fields */
                len += 2 * hweight8(status->chains);
@@ -447,6 +452,31 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
                pos += 2;
        }
 
+       if (local->hw.radiotap_timestamp.units_pos >= 0) {
+               u16 accuracy = 0;
+               u8 flags = IEEE80211_RADIOTAP_TIMESTAMP_FLAG_32BIT;
+
+               rthdr->it_present |=
+                       cpu_to_le32(1 << IEEE80211_RADIOTAP_TIMESTAMP);
+
+               /* ensure 8 byte alignment */
+               while ((pos - (u8 *)rthdr) & 7)
+                       pos++;
+
+               put_unaligned_le64(status->device_timestamp, pos);
+               pos += sizeof(u64);
+
+               if (local->hw.radiotap_timestamp.accuracy >= 0) {
+                       accuracy = local->hw.radiotap_timestamp.accuracy;
+                       flags |= IEEE80211_RADIOTAP_TIMESTAMP_FLAG_ACCURACY;
+               }
+               put_unaligned_le16(accuracy, pos);
+               pos += sizeof(u16);
+
+               *pos++ = local->hw.radiotap_timestamp.units_pos;
+               *pos++ = flags;
+       }
+
        for_each_set_bit(chain, &chains, IEEE80211_MAX_CHAINS) {
                *pos++ = status->chain_signal[chain];
                *pos++ = chain;
@@ -485,6 +515,9 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
        struct net_device *prev_dev = NULL;
        int present_fcs_len = 0;
        unsigned int rtap_vendor_space = 0;
+       struct ieee80211_mgmt *mgmt;
+       struct ieee80211_sub_if_data *monitor_sdata =
+               rcu_dereference(local->monitor_sdata);
 
        if (unlikely(status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA)) {
                struct ieee80211_vendor_radiotap *rtap = (void *)origskb->data;
@@ -567,7 +600,7 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
                if (sdata->vif.type != NL80211_IFTYPE_MONITOR)
                        continue;
 
-               if (sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES)
+               if (sdata->u.mntr.flags & MONITOR_FLAG_COOK_FRAMES)
                        continue;
 
                if (!ieee80211_sdata_running(sdata))
@@ -585,6 +618,23 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
                ieee80211_rx_stats(sdata->dev, skb->len);
        }
 
+       mgmt = (void *)skb->data;
+       if (monitor_sdata &&
+           skb->len >= IEEE80211_MIN_ACTION_SIZE + 1 + VHT_MUMIMO_GROUPS_DATA_LEN &&
+           ieee80211_is_action(mgmt->frame_control) &&
+           mgmt->u.action.category == WLAN_CATEGORY_VHT &&
+           mgmt->u.action.u.vht_group_notif.action_code == WLAN_VHT_ACTION_GROUPID_MGMT &&
+           is_valid_ether_addr(monitor_sdata->u.mntr.mu_follow_addr) &&
+           ether_addr_equal(mgmt->da, monitor_sdata->u.mntr.mu_follow_addr)) {
+               struct sk_buff *mu_skb = skb_copy(skb, GFP_ATOMIC);
+
+               if (mu_skb) {
+                       mu_skb->pkt_type = IEEE80211_SDATA_QUEUE_TYPE_FRAME;
+                       skb_queue_tail(&monitor_sdata->skb_queue, mu_skb);
+                       ieee80211_queue_work(&local->hw, &monitor_sdata->work);
+               }
+       }
+
        if (prev_dev) {
                skb->dev = prev_dev;
                netif_receive_skb(skb);
@@ -1072,8 +1122,15 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx,
        tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK;
 
        tid_agg_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[tid]);
-       if (!tid_agg_rx)
+       if (!tid_agg_rx) {
+               if (ack_policy == IEEE80211_QOS_CTL_ACK_POLICY_BLOCKACK &&
+                   !test_bit(tid, rx->sta->ampdu_mlme.agg_session_valid) &&
+                   !test_and_set_bit(tid, rx->sta->ampdu_mlme.unexpected_agg))
+                       ieee80211_send_delba(rx->sdata, rx->sta->sta.addr, tid,
+                                            WLAN_BACK_RECIPIENT,
+                                            WLAN_REASON_QSTA_REQUIRE_SETUP);
                goto dont_reorder;
+       }
 
        /* qos null data frames are excluded */
        if (unlikely(hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_NULLFUNC)))
@@ -2535,6 +2592,12 @@ ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx, struct sk_buff_head *frames)
 
                tid = le16_to_cpu(bar_data.control) >> 12;
 
+               if (!test_bit(tid, rx->sta->ampdu_mlme.agg_session_valid) &&
+                   !test_and_set_bit(tid, rx->sta->ampdu_mlme.unexpected_agg))
+                       ieee80211_send_delba(rx->sdata, rx->sta->sta.addr, tid,
+                                            WLAN_BACK_RECIPIENT,
+                                            WLAN_REASON_QSTA_REQUIRE_SETUP);
+
                tid_agg_rx = rcu_dereference(rx->sta->ampdu_mlme.tid_rx[tid]);
                if (!tid_agg_rx)
                        return RX_DROP_MONITOR;
@@ -3147,7 +3210,7 @@ static void ieee80211_rx_cooked_monitor(struct ieee80211_rx_data *rx,
                        continue;
 
                if (sdata->vif.type != NL80211_IFTYPE_MONITOR ||
-                   !(sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES))
+                   !(sdata->u.mntr.flags & MONITOR_FLAG_COOK_FRAMES))
                        continue;
 
                if (prev_dev) {
index 070b40f..23d8ac8 100644 (file)
@@ -420,7 +420,7 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw,
 {
        struct ieee80211_local *local = hw_to_local(hw);
 
-       trace_api_scan_completed(local, info);
+       trace_api_scan_completed(local, info->aborted);
 
        set_bit(SCAN_COMPLETED, &local->scanning);
        if (info->aborted)
index 19f14c9..1b1b28f 100644 (file)
@@ -340,6 +340,9 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
 
        memcpy(sta->addr, addr, ETH_ALEN);
        memcpy(sta->sta.addr, addr, ETH_ALEN);
+       sta->sta.max_rx_aggregation_subframes =
+               local->hw.max_rx_aggregation_subframes;
+
        sta->local = local;
        sta->sdata = sdata;
        sta->rx_stats.last_rx = jiffies;
@@ -687,7 +690,7 @@ static void __sta_info_recalc_tim(struct sta_info *sta, bool ignore_pending)
        }
 
        /* No need to do anything if the driver does all */
-       if (ieee80211_hw_check(&local->hw, AP_LINK_PS))
+       if (!local->ops->set_tim)
                return;
 
        if (sta->dead)
index 0556be3..530231b 100644 (file)
@@ -230,6 +230,8 @@ struct tid_ampdu_rx {
  * @tid_rx_stop_requested:  bitmap indicating which BA sessions per TID the
  *     driver requested to close until the work for it runs
  * @agg_session_valid: bitmap indicating which TID has a rx BA session open on
+ * @unexpected_agg: bitmap indicating which TID already sent a delBA due to
+ *     unexpected aggregation related frames outside a session
  * @work: work struct for starting/stopping aggregation
  * @tid_tx: aggregation info for Tx per TID
  * @tid_start_tx: sessions where start was requested
@@ -244,6 +246,7 @@ struct sta_ampdu_mlme {
        unsigned long tid_rx_timer_expired[BITS_TO_LONGS(IEEE80211_NUM_TIDS)];
        unsigned long tid_rx_stop_requested[BITS_TO_LONGS(IEEE80211_NUM_TIDS)];
        unsigned long agg_session_valid[BITS_TO_LONGS(IEEE80211_NUM_TIDS)];
+       unsigned long unexpected_agg[BITS_TO_LONGS(IEEE80211_NUM_TIDS)];
        /* tx */
        struct work_struct work;
        struct tid_ampdu_tx __rcu *tid_tx[IEEE80211_NUM_TIDS];
index a2a6826..ea39f8a 100644 (file)
@@ -557,6 +557,12 @@ static void ieee80211_report_used_skb(struct ieee80211_local *local,
 static void ieee80211_lost_packet(struct sta_info *sta,
                                  struct ieee80211_tx_info *info)
 {
+       /* If driver relies on its own algorithm for station kickout, skip
+        * mac80211 packet loss mechanism.
+        */
+       if (ieee80211_hw_check(&sta->local->hw, REPORTS_LOW_ACK))
+               return;
+
        /* This packet was aggregated but doesn't carry status info */
        if ((info->flags & IEEE80211_TX_CTL_AMPDU) &&
            !(info->flags & IEEE80211_TX_STAT_AMPDU))
@@ -709,7 +715,7 @@ void ieee80211_tx_monitor(struct ieee80211_local *local, struct sk_buff *skb,
                        if (!ieee80211_sdata_running(sdata))
                                continue;
 
-                       if ((sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES) &&
+                       if ((sdata->u.mntr.flags & MONITOR_FLAG_COOK_FRAMES) &&
                            !send_to_cooked)
                                continue;
 
index 1d0746d..61d302d 100644 (file)
@@ -1343,7 +1343,7 @@ static struct sk_buff *fq_tin_dequeue_func(struct fq *fq,
        local = container_of(fq, struct ieee80211_local, fq);
        txqi = container_of(tin, struct txq_info, tin);
        cparams = &local->cparams;
-       cstats = &local->cstats;
+       cstats = &txqi->cstats;
 
        if (flow == &txqi->def_flow)
                cvars = &txqi->def_cvars;
@@ -1403,6 +1403,7 @@ void ieee80211_txq_init(struct ieee80211_sub_if_data *sdata,
        fq_tin_init(&txqi->tin);
        fq_flow_init(&txqi->def_flow);
        codel_vars_init(&txqi->def_cvars);
+       codel_stats_init(&txqi->cstats);
 
        txqi->txq.vif = &sdata->vif;
 
@@ -1441,7 +1442,6 @@ int ieee80211_txq_setup_flows(struct ieee80211_local *local)
                return ret;
 
        codel_params_init(&local->cparams);
-       codel_stats_init(&local->cstats);
        local->cparams.interval = MS2TIME(100);
        local->cparams.target = MS2TIME(20);
        local->cparams.ecn = true;
@@ -1643,7 +1643,7 @@ static bool __ieee80211_tx(struct ieee80211_local *local,
 
        switch (sdata->vif.type) {
        case NL80211_IFTYPE_MONITOR:
-               if (sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE) {
+               if (sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE) {
                        vif = &sdata->vif;
                        break;
                }
@@ -2263,15 +2263,9 @@ static int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata,
        case NL80211_IFTYPE_STATION:
                if (sdata->wdev.wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) {
                        sta = sta_info_get(sdata, skb->data);
-                       if (sta) {
-                               bool tdls_peer, tdls_auth;
-
-                               tdls_peer = test_sta_flag(sta,
-                                                         WLAN_STA_TDLS_PEER);
-                               tdls_auth = test_sta_flag(sta,
-                                               WLAN_STA_TDLS_PEER_AUTH);
-
-                               if (tdls_peer && tdls_auth) {
+                       if (sta && test_sta_flag(sta, WLAN_STA_TDLS_PEER)) {
+                               if (test_sta_flag(sta,
+                                                 WLAN_STA_TDLS_PEER_AUTH)) {
                                        *sta_out = sta;
                                        return 0;
                                }
@@ -2283,8 +2277,7 @@ static int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata,
                                 * after a TDLS sta is removed due to being
                                 * unreachable.
                                 */
-                               if (tdls_peer && !tdls_auth &&
-                                   !ieee80211_is_tdls_setup(skb))
+                               if (!ieee80211_is_tdls_setup(skb))
                                        return -EINVAL;
                        }
 
index 42bf0b6..b6865d8 100644 (file)
@@ -598,7 +598,7 @@ static void __iterate_interfaces(struct ieee80211_local *local,
        list_for_each_entry_rcu(sdata, &local->interfaces, list) {
                switch (sdata->vif.type) {
                case NL80211_IFTYPE_MONITOR:
-                       if (!(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE))
+                       if (!(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE))
                                continue;
                        break;
                case NL80211_IFTYPE_AP_VLAN:
@@ -2555,7 +2555,6 @@ int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata,
 
                if (need_basic && basic_rates & BIT(i))
                        basic = 0x80;
-               rate = sband->bitrates[i].bitrate;
                rate = DIV_ROUND_UP(sband->bitrates[i].bitrate,
                                    5 * (1 << shift));
                *pos++ = basic | (u8) rate;
index 2029b49..4911cd9 100644 (file)
@@ -1252,7 +1252,7 @@ static int __init cfg80211_init(void)
        if (err)
                goto out_fail_reg;
 
-       cfg80211_wq = create_singlethread_workqueue("cfg80211");
+       cfg80211_wq = alloc_ordered_workqueue("cfg80211", WQ_MEM_RECLAIM);
        if (!cfg80211_wq) {
                err = -ENOMEM;
                goto out_fail_wq;
index eee9144..5555e3c 100644 (file)
@@ -249,9 +249,9 @@ struct cfg80211_event {
 };
 
 struct cfg80211_cached_keys {
-       struct key_params params[6];
-       u8 data[6][WLAN_MAX_KEY_LEN];
-       int def, defmgmt;
+       struct key_params params[4];
+       u8 data[4][WLAN_KEY_LEN_WEP104];
+       int def;
 };
 
 enum cfg80211_chan_mode {
index 4a4dda5..eafdfa5 100644 (file)
@@ -114,6 +114,9 @@ static int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
                }
        }
 
+       if (WARN_ON(connkeys && connkeys->def < 0))
+               return -EINVAL;
+
        if (WARN_ON(wdev->connect_keys))
                kzfree(wdev->connect_keys);
        wdev->connect_keys = connkeys;
@@ -284,18 +287,16 @@ int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev,
        if (!netif_running(wdev->netdev))
                return 0;
 
-       if (wdev->wext.keys) {
+       if (wdev->wext.keys)
                wdev->wext.keys->def = wdev->wext.default_key;
-               wdev->wext.keys->defmgmt = wdev->wext.default_mgmt_key;
-       }
 
        wdev->wext.ibss.privacy = wdev->wext.default_key != -1;
 
-       if (wdev->wext.keys) {
+       if (wdev->wext.keys && wdev->wext.keys->def != -1) {
                ck = kmemdup(wdev->wext.keys, sizeof(*ck), GFP_KERNEL);
                if (!ck)
                        return -ENOMEM;
-               for (i = 0; i < 6; i++)
+               for (i = 0; i < 4; i++)
                        ck->params[i].key = ck->data[i];
        }
        err = __cfg80211_join_ibss(rdev, wdev->netdev,
index c284d88..d6abb07 100644 (file)
@@ -222,7 +222,7 @@ int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
        ASSERT_WDEV_LOCK(wdev);
 
        if (auth_type == NL80211_AUTHTYPE_SHARED_KEY)
-               if (!key || !key_len || key_idx < 0 || key_idx > 4)
+               if (!key || !key_len || key_idx < 0 || key_idx > 3)
                        return -EINVAL;
 
        if (wdev->current_bss &&
index 4997857..887c4c1 100644 (file)
@@ -848,13 +848,21 @@ nl80211_parse_connkeys(struct cfg80211_registered_device *rdev,
        struct nlattr *key;
        struct cfg80211_cached_keys *result;
        int rem, err, def = 0;
+       bool have_key = false;
+
+       nla_for_each_nested(key, keys, rem) {
+               have_key = true;
+               break;
+       }
+
+       if (!have_key)
+               return NULL;
 
        result = kzalloc(sizeof(*result), GFP_KERNEL);
        if (!result)
                return ERR_PTR(-ENOMEM);
 
        result->def = -1;
-       result->defmgmt = -1;
 
        nla_for_each_nested(key, keys, rem) {
                memset(&parse, 0, sizeof(parse));
@@ -866,7 +874,7 @@ nl80211_parse_connkeys(struct cfg80211_registered_device *rdev,
                err = -EINVAL;
                if (!parse.p.key)
                        goto error;
-               if (parse.idx < 0 || parse.idx > 4)
+               if (parse.idx < 0 || parse.idx > 3)
                        goto error;
                if (parse.def) {
                        if (def)
@@ -881,16 +889,24 @@ nl80211_parse_connkeys(struct cfg80211_registered_device *rdev,
                                                     parse.idx, false, NULL);
                if (err)
                        goto error;
+               if (parse.p.cipher != WLAN_CIPHER_SUITE_WEP40 &&
+                   parse.p.cipher != WLAN_CIPHER_SUITE_WEP104) {
+                       err = -EINVAL;
+                       goto error;
+               }
                result->params[parse.idx].cipher = parse.p.cipher;
                result->params[parse.idx].key_len = parse.p.key_len;
                result->params[parse.idx].key = result->data[parse.idx];
                memcpy(result->data[parse.idx], parse.p.key, parse.p.key_len);
 
-               if (parse.p.cipher == WLAN_CIPHER_SUITE_WEP40 ||
-                   parse.p.cipher == WLAN_CIPHER_SUITE_WEP104) {
-                       if (no_ht)
-                               *no_ht = true;
-               }
+               /* must be WEP key if we got here */
+               if (no_ht)
+                       *no_ht = true;
+       }
+
+       if (result->def < 0) {
+               err = -EINVAL;
+               goto error;
        }
 
        return result;
@@ -2525,10 +2541,35 @@ static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *
        int if_idx = 0;
        int wp_start = cb->args[0];
        int if_start = cb->args[1];
+       int filter_wiphy = -1;
        struct cfg80211_registered_device *rdev;
        struct wireless_dev *wdev;
 
        rtnl_lock();
+       if (!cb->args[2]) {
+               struct nl80211_dump_wiphy_state state = {
+                       .filter_wiphy = -1,
+               };
+               int ret;
+
+               ret = nl80211_dump_wiphy_parse(skb, cb, &state);
+               if (ret)
+                       return ret;
+
+               filter_wiphy = state.filter_wiphy;
+
+               /*
+                * if filtering, set cb->args[2] to +1 since 0 is the default
+                * value needed to determine that parsing is necessary.
+                */
+               if (filter_wiphy >= 0)
+                       cb->args[2] = filter_wiphy + 1;
+               else
+                       cb->args[2] = -1;
+       } else if (cb->args[2] > 0) {
+               filter_wiphy = cb->args[2] - 1;
+       }
+
        list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
                if (!net_eq(wiphy_net(&rdev->wiphy), sock_net(skb->sk)))
                        continue;
@@ -2536,6 +2577,10 @@ static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *
                        wp_idx++;
                        continue;
                }
+
+               if (filter_wiphy >= 0 && filter_wiphy != rdev->wiphy_idx)
+                       continue;
+
                if_idx = 0;
 
                list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
@@ -7359,7 +7404,7 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
                    (key.p.cipher != WLAN_CIPHER_SUITE_WEP104 ||
                     key.p.key_len != WLAN_KEY_LEN_WEP104))
                        return -EINVAL;
-               if (key.idx > 4)
+               if (key.idx > 3)
                        return -EINVAL;
        } else {
                key.p.key_len = 0;
@@ -7977,6 +8022,8 @@ __cfg80211_alloc_vendor_skb(struct cfg80211_registered_device *rdev,
        }
 
        data = nla_nest_start(skb, attr);
+       if (!data)
+               goto nla_put_failure;
 
        ((void **)skb->cb)[0] = rdev;
        ((void **)skb->cb)[1] = hdr;
@@ -9406,18 +9453,27 @@ static int nl80211_send_wowlan_nd(struct sk_buff *msg,
        if (!freqs)
                return -ENOBUFS;
 
-       for (i = 0; i < req->n_channels; i++)
-               nla_put_u32(msg, i, req->channels[i]->center_freq);
+       for (i = 0; i < req->n_channels; i++) {
+               if (nla_put_u32(msg, i, req->channels[i]->center_freq))
+                       return -ENOBUFS;
+       }
 
        nla_nest_end(msg, freqs);
 
        if (req->n_match_sets) {
                matches = nla_nest_start(msg, NL80211_ATTR_SCHED_SCAN_MATCH);
+               if (!matches)
+                       return -ENOBUFS;
+
                for (i = 0; i < req->n_match_sets; i++) {
                        match = nla_nest_start(msg, i);
-                       nla_put(msg, NL80211_SCHED_SCAN_MATCH_ATTR_SSID,
-                               req->match_sets[i].ssid.ssid_len,
-                               req->match_sets[i].ssid.ssid);
+                       if (!match)
+                               return -ENOBUFS;
+
+                       if (nla_put(msg, NL80211_SCHED_SCAN_MATCH_ATTR_SSID,
+                                   req->match_sets[i].ssid.ssid_len,
+                                   req->match_sets[i].ssid.ssid))
+                               return -ENOBUFS;
                        nla_nest_end(msg, match);
                }
                nla_nest_end(msg, matches);
@@ -9429,6 +9485,9 @@ static int nl80211_send_wowlan_nd(struct sk_buff *msg,
 
        for (i = 0; i < req->n_scan_plans; i++) {
                scan_plan = nla_nest_start(msg, i + 1);
+               if (!scan_plan)
+                       return -ENOBUFS;
+
                if (!scan_plan ||
                    nla_put_u32(msg, NL80211_SCHED_SCAN_PLAN_INTERVAL,
                                req->scan_plans[i].interval) ||
index 0358e12..b5bd58d 100644 (file)
@@ -352,52 +352,48 @@ void cfg80211_bss_expire(struct cfg80211_registered_device *rdev)
        __cfg80211_bss_expire(rdev, jiffies - IEEE80211_SCAN_RESULT_EXPIRE);
 }
 
-const u8 *cfg80211_find_ie(u8 eid, const u8 *ies, int len)
+const u8 *cfg80211_find_ie_match(u8 eid, const u8 *ies, int len,
+                                const u8 *match, int match_len,
+                                int match_offset)
 {
-       while (len > 2 && ies[0] != eid) {
+       /* match_offset can't be smaller than 2, unless match_len is
+        * zero, in which case match_offset must be zero as well.
+        */
+       if (WARN_ON((match_len && match_offset < 2) ||
+                   (!match_len && match_offset)))
+               return NULL;
+
+       while (len >= 2 && len >= ies[1] + 2) {
+               if ((ies[0] == eid) &&
+                   (ies[1] + 2 >= match_offset + match_len) &&
+                   !memcmp(ies + match_offset, match, match_len))
+                       return ies;
+
                len -= ies[1] + 2;
                ies += ies[1] + 2;
        }
-       if (len < 2)
-               return NULL;
-       if (len < 2 + ies[1])
-               return NULL;
-       return ies;
+
+       return NULL;
 }
-EXPORT_SYMBOL(cfg80211_find_ie);
+EXPORT_SYMBOL(cfg80211_find_ie_match);
 
 const u8 *cfg80211_find_vendor_ie(unsigned int oui, int oui_type,
                                  const u8 *ies, int len)
 {
-       struct ieee80211_vendor_ie *ie;
-       const u8 *pos = ies, *end = ies + len;
-       int ie_oui;
+       const u8 *ie;
+       u8 match[] = { oui >> 16, oui >> 8, oui, oui_type };
+       int match_len = (oui_type < 0) ? 3 : sizeof(match);
 
        if (WARN_ON(oui_type > 0xff))
                return NULL;
 
-       while (pos < end) {
-               pos = cfg80211_find_ie(WLAN_EID_VENDOR_SPECIFIC, pos,
-                                      end - pos);
-               if (!pos)
-                       return NULL;
-
-               ie = (struct ieee80211_vendor_ie *)pos;
-
-               /* make sure we can access ie->len */
-               BUILD_BUG_ON(offsetof(struct ieee80211_vendor_ie, len) != 1);
+       ie = cfg80211_find_ie_match(WLAN_EID_VENDOR_SPECIFIC, ies, len,
+                                   match, match_len, 2);
 
-               if (ie->len < sizeof(*ie))
-                       goto cont;
+       if (ie && (ie[1] < 4))
+               return NULL;
 
-               ie_oui = ie->oui[0] << 16 | ie->oui[1] << 8 | ie->oui[2];
-               if (ie_oui == oui &&
-                   (oui_type < 0 || ie->oui_type == oui_type))
-                       return pos;
-cont:
-               pos += 2 + ie->len;
-       }
-       return NULL;
+       return ie;
 }
 EXPORT_SYMBOL(cfg80211_find_vendor_ie);
 
index add6824..c08a3b5 100644 (file)
@@ -1043,6 +1043,9 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev,
                                connect->crypto.ciphers_pairwise[0] = cipher;
                        }
                }
+       } else {
+               if (WARN_ON(connkeys))
+                       return -EINVAL;
        }
 
        wdev->connect_keys = connkeys;
index e46469b..0082f4b 100644 (file)
@@ -57,7 +57,7 @@ static ssize_t addresses_show(struct device *dev,
                return sprintf(buf, "%pM\n", wiphy->perm_addr);
 
        for (i = 0; i < wiphy->n_addresses; i++)
-               buf += sprintf(buf, "%pM\n", &wiphy->addresses[i].addr);
+               buf += sprintf(buf, "%pM\n", wiphy->addresses[i].addr);
 
        return buf - start;
 }
index 0675f51..9e6e2aa 100644 (file)
@@ -218,7 +218,7 @@ int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev,
                                   struct key_params *params, int key_idx,
                                   bool pairwise, const u8 *mac_addr)
 {
-       if (key_idx > 5)
+       if (key_idx < 0 || key_idx > 5)
                return -EINVAL;
 
        if (!pairwise && mac_addr && !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN))
@@ -249,7 +249,13 @@ int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev,
                /* Disallow BIP (group-only) cipher as pairwise cipher */
                if (pairwise)
                        return -EINVAL;
+               if (key_idx < 4)
+                       return -EINVAL;
                break;
+       case WLAN_CIPHER_SUITE_WEP40:
+       case WLAN_CIPHER_SUITE_WEP104:
+               if (key_idx > 3)
+                       return -EINVAL;
        default:
                break;
        }
@@ -906,7 +912,7 @@ void cfg80211_upload_connect_keys(struct wireless_dev *wdev)
        if (!wdev->connect_keys)
                return;
 
-       for (i = 0; i < 6; i++) {
+       for (i = 0; i < 4; i++) {
                if (!wdev->connect_keys->params[i].cipher)
                        continue;
                if (rdev_add_key(rdev, dev, i, false, NULL,
@@ -919,9 +925,6 @@ void cfg80211_upload_connect_keys(struct wireless_dev *wdev)
                                netdev_err(dev, "failed to set defkey %d\n", i);
                                continue;
                        }
-               if (wdev->connect_keys->defmgmt == i)
-                       if (rdev_set_default_mgmt_key(rdev, dev, i))
-                               netdev_err(dev, "failed to set mgtdef %d\n", i);
        }
 
        kzfree(wdev->connect_keys);
index 9f27221..7b97d43 100644 (file)
@@ -408,10 +408,10 @@ static int __cfg80211_set_encryption(struct cfg80211_registered_device *rdev,
 
        if (!wdev->wext.keys) {
                wdev->wext.keys = kzalloc(sizeof(*wdev->wext.keys),
-                                             GFP_KERNEL);
+                                         GFP_KERNEL);
                if (!wdev->wext.keys)
                        return -ENOMEM;
-               for (i = 0; i < 6; i++)
+               for (i = 0; i < 4; i++)
                        wdev->wext.keys->params[i].key =
                                wdev->wext.keys->data[i];
        }
@@ -460,7 +460,7 @@ static int __cfg80211_set_encryption(struct cfg80211_registered_device *rdev,
                if (err == -ENOENT)
                        err = 0;
                if (!err) {
-                       if (!addr) {
+                       if (!addr && idx < 4) {
                                memset(wdev->wext.keys->data[idx], 0,
                                       sizeof(wdev->wext.keys->data[idx]));
                                wdev->wext.keys->params[idx].key_len = 0;
@@ -487,6 +487,9 @@ static int __cfg80211_set_encryption(struct cfg80211_registered_device *rdev,
        err = 0;
        if (wdev->current_bss)
                err = rdev_add_key(rdev, dev, idx, pairwise, addr, params);
+       else if (params->cipher != WLAN_CIPHER_SUITE_WEP40 &&
+                params->cipher != WLAN_CIPHER_SUITE_WEP104)
+               return -EINVAL;
        if (err)
                return err;
 
index a4e8af3..88f1f69 100644 (file)
@@ -35,7 +35,6 @@ int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev,
 
        if (wdev->wext.keys) {
                wdev->wext.keys->def = wdev->wext.default_key;
-               wdev->wext.keys->defmgmt = wdev->wext.default_mgmt_key;
                if (wdev->wext.default_key != -1)
                        wdev->wext.connect.privacy = true;
        }
@@ -43,11 +42,11 @@ int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev,
        if (!wdev->wext.connect.ssid_len)
                return 0;
 
-       if (wdev->wext.keys) {
+       if (wdev->wext.keys && wdev->wext.keys->def != -1) {
                ck = kmemdup(wdev->wext.keys, sizeof(*ck), GFP_KERNEL);
                if (!ck)
                        return -ENOMEM;
-               for (i = 0; i < 6; i++)
+               for (i = 0; i < 4; i++)
                        ck->params[i].key = ck->data[i];
        }