mac80211: allow station add/remove to sleep
[safe/jmp/linux-2.6] / net / mac80211 / util.c
index 55be3ef..3af439a 100644 (file)
 #include <linux/skbuff.h>
 #include <linux/etherdevice.h>
 #include <linux/if_arp.h>
-#include <linux/wireless.h>
 #include <linux/bitmap.h>
+#include <linux/crc32.h>
 #include <net/net_namespace.h>
 #include <net/cfg80211.h>
 #include <net/rtnetlink.h>
 
 #include "ieee80211_i.h"
+#include "driver-ops.h"
 #include "rate.h"
 #include "mesh.h"
 #include "wme.h"
+#include "led.h"
+#include "wep.h"
 
 /* privid for wiphys to determine whether they belong to us or not */
 void *mac80211_wiphy_privid = &mac80211_wiphy_privid;
 
-/* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */
-/* Ethernet-II snap header (RFC1042 for most EtherTypes) */
-const unsigned char rfc1042_header[] __aligned(2) =
-       { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
-
-/* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */
-const unsigned char bridge_tunnel_header[] __aligned(2) =
-       { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 };
+struct ieee80211_hw *wiphy_to_ieee80211_hw(struct wiphy *wiphy)
+{
+       struct ieee80211_local *local;
+       BUG_ON(!wiphy);
 
+       local = wiphy_priv(wiphy);
+       return &local->hw;
+}
+EXPORT_SYMBOL(wiphy_to_ieee80211_hw);
 
 u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len,
-                       enum ieee80211_if_types type)
+                       enum nl80211_iftype type)
 {
        __le16 fc = hdr->frame_control;
 
@@ -77,10 +80,10 @@ u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len,
 
                if (ieee80211_is_back_req(fc)) {
                        switch (type) {
-                       case IEEE80211_IF_TYPE_STA:
+                       case NL80211_IFTYPE_STATION:
                                return hdr->addr2;
-                       case IEEE80211_IF_TYPE_AP:
-                       case IEEE80211_IF_TYPE_VLAN:
+                       case NL80211_IFTYPE_AP:
+                       case NL80211_IFTYPE_AP_VLAN:
                                return hdr->addr1;
                        default:
                                break; /* fall through to the return */
@@ -91,84 +94,15 @@ u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len,
        return NULL;
 }
 
-unsigned int ieee80211_hdrlen(__le16 fc)
-{
-       unsigned int hdrlen = 24;
-
-       if (ieee80211_is_data(fc)) {
-               if (ieee80211_has_a4(fc))
-                       hdrlen = 30;
-               if (ieee80211_is_data_qos(fc))
-                       hdrlen += IEEE80211_QOS_CTL_LEN;
-               goto out;
-       }
-
-       if (ieee80211_is_ctl(fc)) {
-               /*
-                * ACK and CTS are 10 bytes, all others 16. To see how
-                * to get this condition consider
-                *   subtype mask:   0b0000000011110000 (0x00F0)
-                *   ACK subtype:    0b0000000011010000 (0x00D0)
-                *   CTS subtype:    0b0000000011000000 (0x00C0)
-                *   bits that matter:         ^^^      (0x00E0)
-                *   value of those: 0b0000000011000000 (0x00C0)
-                */
-               if ((fc & cpu_to_le16(0x00E0)) == cpu_to_le16(0x00C0))
-                       hdrlen = 10;
-               else
-                       hdrlen = 16;
-       }
-out:
-       return hdrlen;
-}
-EXPORT_SYMBOL(ieee80211_hdrlen);
-
-unsigned int ieee80211_get_hdrlen_from_skb(const struct sk_buff *skb)
-{
-       const struct ieee80211_hdr *hdr = (const struct ieee80211_hdr *)skb->data;
-       unsigned int hdrlen;
-
-       if (unlikely(skb->len < 10))
-               return 0;
-       hdrlen = ieee80211_hdrlen(hdr->frame_control);
-       if (unlikely(hdrlen > skb->len))
-               return 0;
-       return hdrlen;
-}
-EXPORT_SYMBOL(ieee80211_get_hdrlen_from_skb);
-
-int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr)
-{
-       int ae = meshhdr->flags & IEEE80211S_FLAGS_AE;
-       /* 7.1.3.5a.2 */
-       switch (ae) {
-       case 0:
-               return 6;
-       case 1:
-               return 12;
-       case 2:
-               return 18;
-       case 3:
-               return 24;
-       default:
-               return 6;
-       }
-}
-
 void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx)
 {
-       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data;
+       struct sk_buff *skb = tx->skb;
+       struct ieee80211_hdr *hdr;
 
-       hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
-       if (tx->extra_frag) {
-               struct ieee80211_hdr *fhdr;
-               int i;
-               for (i = 0; i < tx->num_extra_frag; i++) {
-                       fhdr = (struct ieee80211_hdr *)
-                               tx->extra_frag[i]->data;
-                       fhdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
-               }
-       }
+       do {
+               hdr = (struct ieee80211_hdr *) skb->data;
+               hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
+       } while ((skb = skb->next));
 }
 
 int ieee80211_frame_duration(struct ieee80211_local *local, size_t len,
@@ -231,16 +165,21 @@ __le16 ieee80211_generic_frame_duration(struct ieee80211_hw *hw,
                                        struct ieee80211_rate *rate)
 {
        struct ieee80211_local *local = hw_to_local(hw);
-       struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+       struct ieee80211_sub_if_data *sdata;
        u16 dur;
        int erp;
+       bool short_preamble = false;
 
        erp = 0;
-       if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
-               erp = rate->flags & IEEE80211_RATE_ERP_G;
+       if (vif) {
+               sdata = vif_to_sdata(vif);
+               short_preamble = sdata->vif.bss_conf.use_short_preamble;
+               if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
+                       erp = rate->flags & IEEE80211_RATE_ERP_G;
+       }
 
        dur = ieee80211_frame_duration(local, frame_len, rate->bitrate, erp,
-                                      sdata->bss_conf.use_short_preamble);
+                                      short_preamble);
 
        return cpu_to_le16(dur);
 }
@@ -252,7 +191,7 @@ __le16 ieee80211_rts_duration(struct ieee80211_hw *hw,
 {
        struct ieee80211_local *local = hw_to_local(hw);
        struct ieee80211_rate *rate;
-       struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+       struct ieee80211_sub_if_data *sdata;
        bool short_preamble;
        int erp;
        u16 dur;
@@ -260,13 +199,17 @@ __le16 ieee80211_rts_duration(struct ieee80211_hw *hw,
 
        sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
 
-       short_preamble = sdata->bss_conf.use_short_preamble;
+       short_preamble = false;
 
        rate = &sband->bitrates[frame_txctl->control.rts_cts_rate_idx];
 
        erp = 0;
-       if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
-               erp = rate->flags & IEEE80211_RATE_ERP_G;
+       if (vif) {
+               sdata = vif_to_sdata(vif);
+               short_preamble = sdata->vif.bss_conf.use_short_preamble;
+               if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
+                       erp = rate->flags & IEEE80211_RATE_ERP_G;
+       }
 
        /* CTS duration */
        dur = ieee80211_frame_duration(local, 10, rate->bitrate,
@@ -289,7 +232,7 @@ __le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw,
 {
        struct ieee80211_local *local = hw_to_local(hw);
        struct ieee80211_rate *rate;
-       struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+       struct ieee80211_sub_if_data *sdata;
        bool short_preamble;
        int erp;
        u16 dur;
@@ -297,12 +240,16 @@ __le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw,
 
        sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
 
-       short_preamble = sdata->bss_conf.use_short_preamble;
+       short_preamble = false;
 
        rate = &sband->bitrates[frame_txctl->control.rts_cts_rate_idx];
        erp = 0;
-       if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
-               erp = rate->flags & IEEE80211_RATE_ERP_G;
+       if (vif) {
+               sdata = vif_to_sdata(vif);
+               short_preamble = sdata->vif.bss_conf.use_short_preamble;
+               if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
+                       erp = rate->flags & IEEE80211_RATE_ERP_G;
+       }
 
        /* Data frame duration */
        dur = ieee80211_frame_duration(local, frame_len, rate->bitrate,
@@ -317,49 +264,193 @@ __le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw,
 }
 EXPORT_SYMBOL(ieee80211_ctstoself_duration);
 
-void ieee80211_wake_queue(struct ieee80211_hw *hw, int queue)
+static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue,
+                                  enum queue_stop_reason reason)
 {
        struct ieee80211_local *local = hw_to_local(hw);
+       struct ieee80211_sub_if_data *sdata;
+
+       if (WARN_ON(queue >= hw->queues))
+               return;
+
+       __clear_bit(reason, &local->queue_stop_reasons[queue]);
+
+       if (local->queue_stop_reasons[queue] != 0)
+               /* someone still has this queue stopped */
+               return;
 
-       if (test_bit(queue, local->queues_pending)) {
-               set_bit(queue, local->queues_pending_run);
+       if (!skb_queue_empty(&local->pending[queue]))
                tasklet_schedule(&local->tx_pending_tasklet);
-       } else {
-               netif_wake_subqueue(local->mdev, queue);
-       }
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(sdata, &local->interfaces, list)
+               netif_tx_wake_queue(netdev_get_tx_queue(sdata->dev, queue));
+       rcu_read_unlock();
+}
+
+void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue,
+                                   enum queue_stop_reason reason)
+{
+       struct ieee80211_local *local = hw_to_local(hw);
+       unsigned long flags;
+
+       spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+       __ieee80211_wake_queue(hw, queue, reason);
+       spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+}
+
+void ieee80211_wake_queue(struct ieee80211_hw *hw, int queue)
+{
+       ieee80211_wake_queue_by_reason(hw, queue,
+                                      IEEE80211_QUEUE_STOP_REASON_DRIVER);
 }
 EXPORT_SYMBOL(ieee80211_wake_queue);
 
-void ieee80211_stop_queue(struct ieee80211_hw *hw, int queue)
+static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue,
+                                  enum queue_stop_reason reason)
+{
+       struct ieee80211_local *local = hw_to_local(hw);
+       struct ieee80211_sub_if_data *sdata;
+
+       if (WARN_ON(queue >= hw->queues))
+               return;
+
+       __set_bit(reason, &local->queue_stop_reasons[queue]);
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(sdata, &local->interfaces, list)
+               netif_tx_stop_queue(netdev_get_tx_queue(sdata->dev, queue));
+       rcu_read_unlock();
+}
+
+void ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue,
+                                   enum queue_stop_reason reason)
 {
        struct ieee80211_local *local = hw_to_local(hw);
+       unsigned long flags;
 
-       netif_stop_subqueue(local->mdev, queue);
+       spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+       __ieee80211_stop_queue(hw, queue, reason);
+       spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+}
+
+void ieee80211_stop_queue(struct ieee80211_hw *hw, int queue)
+{
+       ieee80211_stop_queue_by_reason(hw, queue,
+                                      IEEE80211_QUEUE_STOP_REASON_DRIVER);
 }
 EXPORT_SYMBOL(ieee80211_stop_queue);
 
-void ieee80211_stop_queues(struct ieee80211_hw *hw)
+void ieee80211_add_pending_skb(struct ieee80211_local *local,
+                              struct sk_buff *skb)
+{
+       struct ieee80211_hw *hw = &local->hw;
+       unsigned long flags;
+       int queue = skb_get_queue_mapping(skb);
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+
+       if (WARN_ON(!info->control.vif)) {
+               kfree_skb(skb);
+               return;
+       }
+
+       spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+       __ieee80211_stop_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_SKB_ADD);
+       __skb_queue_tail(&local->pending[queue], skb);
+       __ieee80211_wake_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_SKB_ADD);
+       spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+}
+
+int ieee80211_add_pending_skbs(struct ieee80211_local *local,
+                              struct sk_buff_head *skbs)
+{
+       struct ieee80211_hw *hw = &local->hw;
+       struct sk_buff *skb;
+       unsigned long flags;
+       int queue, ret = 0, i;
+
+       spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+       for (i = 0; i < hw->queues; i++)
+               __ieee80211_stop_queue(hw, i,
+                       IEEE80211_QUEUE_STOP_REASON_SKB_ADD);
+
+       while ((skb = skb_dequeue(skbs))) {
+               struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+
+               if (WARN_ON(!info->control.vif)) {
+                       kfree_skb(skb);
+                       continue;
+               }
+
+               ret++;
+               queue = skb_get_queue_mapping(skb);
+               __skb_queue_tail(&local->pending[queue], skb);
+       }
+
+       for (i = 0; i < hw->queues; i++)
+               __ieee80211_wake_queue(hw, i,
+                       IEEE80211_QUEUE_STOP_REASON_SKB_ADD);
+       spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+
+       return ret;
+}
+
+void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw,
+                                   enum queue_stop_reason reason)
 {
+       struct ieee80211_local *local = hw_to_local(hw);
+       unsigned long flags;
        int i;
 
-       for (i = 0; i < ieee80211_num_queues(hw); i++)
-               ieee80211_stop_queue(hw, i);
+       spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+
+       for (i = 0; i < hw->queues; i++)
+               __ieee80211_stop_queue(hw, i, reason);
+
+       spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+}
+
+void ieee80211_stop_queues(struct ieee80211_hw *hw)
+{
+       ieee80211_stop_queues_by_reason(hw,
+                                       IEEE80211_QUEUE_STOP_REASON_DRIVER);
 }
 EXPORT_SYMBOL(ieee80211_stop_queues);
 
 int ieee80211_queue_stopped(struct ieee80211_hw *hw, int queue)
 {
        struct ieee80211_local *local = hw_to_local(hw);
-       return __netif_subqueue_stopped(local->mdev, queue);
+       unsigned long flags;
+       int ret;
+
+       if (WARN_ON(queue >= hw->queues))
+               return true;
+
+       spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+       ret = !!local->queue_stop_reasons[queue];
+       spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+       return ret;
 }
 EXPORT_SYMBOL(ieee80211_queue_stopped);
 
-void ieee80211_wake_queues(struct ieee80211_hw *hw)
+void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
+                                    enum queue_stop_reason reason)
 {
+       struct ieee80211_local *local = hw_to_local(hw);
+       unsigned long flags;
        int i;
 
-       for (i = 0; i < hw->queues + hw->ampdu_queues; i++)
-               ieee80211_wake_queue(hw, i);
+       spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+
+       for (i = 0; i < hw->queues; i++)
+               __ieee80211_wake_queue(hw, i, reason);
+
+       spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+}
+
+void ieee80211_wake_queues(struct ieee80211_hw *hw)
+{
+       ieee80211_wake_queues_by_reason(hw, IEEE80211_QUEUE_STOP_REASON_DRIVER);
 }
 EXPORT_SYMBOL(ieee80211_wake_queues);
 
@@ -372,27 +463,28 @@ void ieee80211_iterate_active_interfaces(
        struct ieee80211_local *local = hw_to_local(hw);
        struct ieee80211_sub_if_data *sdata;
 
-       rtnl_lock();
+       mutex_lock(&local->iflist_mtx);
 
        list_for_each_entry(sdata, &local->interfaces, list) {
                switch (sdata->vif.type) {
-               case IEEE80211_IF_TYPE_INVALID:
-               case IEEE80211_IF_TYPE_MNTR:
-               case IEEE80211_IF_TYPE_VLAN:
+               case __NL80211_IFTYPE_AFTER_LAST:
+               case NL80211_IFTYPE_UNSPECIFIED:
+               case NL80211_IFTYPE_MONITOR:
+               case NL80211_IFTYPE_AP_VLAN:
                        continue;
-               case IEEE80211_IF_TYPE_AP:
-               case IEEE80211_IF_TYPE_STA:
-               case IEEE80211_IF_TYPE_IBSS:
-               case IEEE80211_IF_TYPE_WDS:
-               case IEEE80211_IF_TYPE_MESH_POINT:
+               case NL80211_IFTYPE_AP:
+               case NL80211_IFTYPE_STATION:
+               case NL80211_IFTYPE_ADHOC:
+               case NL80211_IFTYPE_WDS:
+               case NL80211_IFTYPE_MESH_POINT:
                        break;
                }
-               if (netif_running(sdata->dev))
-                       iterator(data, sdata->dev->dev_addr,
+               if (ieee80211_sdata_running(sdata))
+                       iterator(data, sdata->vif.addr,
                                 &sdata->vif);
        }
 
-       rtnl_unlock();
+       mutex_unlock(&local->iflist_mtx);
 }
 EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces);
 
@@ -409,19 +501,20 @@ void ieee80211_iterate_active_interfaces_atomic(
 
        list_for_each_entry_rcu(sdata, &local->interfaces, list) {
                switch (sdata->vif.type) {
-               case IEEE80211_IF_TYPE_INVALID:
-               case IEEE80211_IF_TYPE_MNTR:
-               case IEEE80211_IF_TYPE_VLAN:
+               case __NL80211_IFTYPE_AFTER_LAST:
+               case NL80211_IFTYPE_UNSPECIFIED:
+               case NL80211_IFTYPE_MONITOR:
+               case NL80211_IFTYPE_AP_VLAN:
                        continue;
-               case IEEE80211_IF_TYPE_AP:
-               case IEEE80211_IF_TYPE_STA:
-               case IEEE80211_IF_TYPE_IBSS:
-               case IEEE80211_IF_TYPE_WDS:
-               case IEEE80211_IF_TYPE_MESH_POINT:
+               case NL80211_IFTYPE_AP:
+               case NL80211_IFTYPE_STATION:
+               case NL80211_IFTYPE_ADHOC:
+               case NL80211_IFTYPE_WDS:
+               case NL80211_IFTYPE_MESH_POINT:
                        break;
                }
-               if (netif_running(sdata->dev))
-                       iterator(data, sdata->dev->dev_addr,
+               if (ieee80211_sdata_running(sdata))
+                       iterator(data, sdata->vif.addr,
                                 &sdata->vif);
        }
 
@@ -429,11 +522,59 @@ void ieee80211_iterate_active_interfaces_atomic(
 }
 EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_atomic);
 
+/*
+ * Nothing should have been stuffed into the workqueue during
+ * the suspend->resume cycle. If this WARN is seen then there
+ * is a bug with either the driver suspend or something in
+ * mac80211 stuffing into the workqueue which we haven't yet
+ * cleared during mac80211's suspend cycle.
+ */
+static bool ieee80211_can_queue_work(struct ieee80211_local *local)
+{
+       if (WARN(local->suspended && !local->resuming,
+                "queueing ieee80211 work while going to suspend\n"))
+               return false;
+
+       return true;
+}
+
+void ieee80211_queue_work(struct ieee80211_hw *hw, struct work_struct *work)
+{
+       struct ieee80211_local *local = hw_to_local(hw);
+
+       if (!ieee80211_can_queue_work(local))
+               return;
+
+       queue_work(local->workqueue, work);
+}
+EXPORT_SYMBOL(ieee80211_queue_work);
+
+void ieee80211_queue_delayed_work(struct ieee80211_hw *hw,
+                                 struct delayed_work *dwork,
+                                 unsigned long delay)
+{
+       struct ieee80211_local *local = hw_to_local(hw);
+
+       if (!ieee80211_can_queue_work(local))
+               return;
+
+       queue_delayed_work(local->workqueue, dwork, delay);
+}
+EXPORT_SYMBOL(ieee80211_queue_delayed_work);
+
 void ieee802_11_parse_elems(u8 *start, size_t len,
                            struct ieee802_11_elems *elems)
 {
+       ieee802_11_parse_elems_crc(start, len, elems, 0, 0);
+}
+
+u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,
+                              struct ieee802_11_elems *elems,
+                              u64 filter, u32 crc)
+{
        size_t left = len;
        u8 *pos = start;
+       bool calc_crc = filter != 0;
 
        memset(elems, 0, sizeof(*elems));
        elems->ie_start = start;
@@ -447,7 +588,10 @@ void ieee802_11_parse_elems(u8 *start, size_t len,
                left -= 2;
 
                if (elen > left)
-                       return;
+                       break;
+
+               if (calc_crc && id < 64 && (filter & (1ULL << id)))
+                       crc = crc32_be(crc, pos - 2, elen + 2);
 
                switch (id) {
                case WLAN_EID_SSID:
@@ -471,8 +615,10 @@ void ieee802_11_parse_elems(u8 *start, size_t len,
                        elems->cf_params_len = elen;
                        break;
                case WLAN_EID_TIM:
-                       elems->tim = pos;
-                       elems->tim_len = elen;
+                       if (elen >= sizeof(struct ieee80211_tim_ie)) {
+                               elems->tim = (void *)pos;
+                               elems->tim_len = elen;
+                       }
                        break;
                case WLAN_EID_IBSS_PARAMS:
                        elems->ibss_params = pos;
@@ -482,15 +628,20 @@ void ieee802_11_parse_elems(u8 *start, size_t len,
                        elems->challenge = pos;
                        elems->challenge_len = elen;
                        break;
-               case WLAN_EID_WPA:
+               case WLAN_EID_VENDOR_SPECIFIC:
                        if (elen >= 4 && pos[0] == 0x00 && pos[1] == 0x50 &&
                            pos[2] == 0xf2) {
                                /* Microsoft OUI (00:50:F2) */
+
+                               if (calc_crc)
+                                       crc = crc32_be(crc, pos - 2, elen + 2);
+
                                if (pos[3] == 1) {
                                        /* OUI Type 1 - WPA IE */
                                        elems->wpa = pos;
                                        elems->wpa_len = elen;
                                } else if (elen >= 5 && pos[3] == 2) {
+                                       /* OUI Type 2 - WMM IE */
                                        if (pos[4] == 0) {
                                                elems->wmm_info = pos;
                                                elems->wmm_info_len = elen;
@@ -514,20 +665,20 @@ void ieee802_11_parse_elems(u8 *start, size_t len,
                        elems->ext_supp_rates_len = elen;
                        break;
                case WLAN_EID_HT_CAPABILITY:
-                       elems->ht_cap_elem = pos;
-                       elems->ht_cap_elem_len = elen;
+                       if (elen >= sizeof(struct ieee80211_ht_cap))
+                               elems->ht_cap_elem = (void *)pos;
                        break;
-               case WLAN_EID_HT_EXTRA_INFO:
-                       elems->ht_info_elem = pos;
-                       elems->ht_info_elem_len = elen;
+               case WLAN_EID_HT_INFORMATION:
+                       if (elen >= sizeof(struct ieee80211_ht_info))
+                               elems->ht_info_elem = (void *)pos;
                        break;
                case WLAN_EID_MESH_ID:
                        elems->mesh_id = pos;
                        elems->mesh_id_len = elen;
                        break;
                case WLAN_EID_MESH_CONFIG:
-                       elems->mesh_config = pos;
-                       elems->mesh_config_len = elen;
+                       if (elen >= sizeof(struct ieee80211_meshconf_ie))
+                               elems->mesh_config = (void *)pos;
                        break;
                case WLAN_EID_PEER_LINK:
                        elems->peer_link = pos;
@@ -545,6 +696,10 @@ void ieee802_11_parse_elems(u8 *start, size_t len,
                        elems->perr = pos;
                        elems->perr_len = elen;
                        break;
+               case WLAN_EID_RANN:
+                       if (elen >= sizeof(struct ieee80211_rann_ie))
+                               elems->rann = (void *)pos;
+                       break;
                case WLAN_EID_CHANNEL_SWITCH:
                        elems->ch_switch_elem = pos;
                        elems->ch_switch_elem_len = elen;
@@ -564,6 +719,10 @@ void ieee802_11_parse_elems(u8 *start, size_t len,
                        elems->pwr_constr_elem = pos;
                        elems->pwr_constr_elem_len = elen;
                        break;
+               case WLAN_EID_TIMEOUT_INTERVAL:
+                       elems->timeout_int = pos;
+                       elems->timeout_int_len = elen;
+                       break;
                default:
                        break;
                }
@@ -571,30 +730,630 @@ void ieee802_11_parse_elems(u8 *start, size_t len,
                left -= elen;
                pos += elen;
        }
+
+       return crc;
 }
 
 void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata)
 {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_tx_queue_params qparam;
-       int i;
+       int queue;
+       bool use_11b;
+       int aCWmin, aCWmax;
 
        if (!local->ops->conf_tx)
                return;
 
        memset(&qparam, 0, sizeof(qparam));
 
-       qparam.aifs = 2;
+       use_11b = (local->hw.conf.channel->band == IEEE80211_BAND_2GHZ) &&
+                !(sdata->flags & IEEE80211_SDATA_OPERATING_GMODE);
+
+       for (queue = 0; queue < local_to_hw(local)->queues; queue++) {
+               /* Set defaults according to 802.11-2007 Table 7-37 */
+               aCWmax = 1023;
+               if (use_11b)
+                       aCWmin = 31;
+               else
+                       aCWmin = 15;
+
+               switch (queue) {
+               case 3: /* AC_BK */
+                       qparam.cw_max = aCWmax;
+                       qparam.cw_min = aCWmin;
+                       qparam.txop = 0;
+                       qparam.aifs = 7;
+                       break;
+               default: /* never happens but let's not leave undefined */
+               case 2: /* AC_BE */
+                       qparam.cw_max = aCWmax;
+                       qparam.cw_min = aCWmin;
+                       qparam.txop = 0;
+                       qparam.aifs = 3;
+                       break;
+               case 1: /* AC_VI */
+                       qparam.cw_max = aCWmin;
+                       qparam.cw_min = (aCWmin + 1) / 2 - 1;
+                       if (use_11b)
+                               qparam.txop = 6016/32;
+                       else
+                               qparam.txop = 3008/32;
+                       qparam.aifs = 2;
+                       break;
+               case 0: /* AC_VO */
+                       qparam.cw_max = (aCWmin + 1) / 2 - 1;
+                       qparam.cw_min = (aCWmin + 1) / 4 - 1;
+                       if (use_11b)
+                               qparam.txop = 3264/32;
+                       else
+                               qparam.txop = 1504/32;
+                       qparam.aifs = 2;
+                       break;
+               }
+
+               qparam.uapsd = false;
+
+               drv_conf_tx(local, queue, &qparam);
+       }
+}
+
+void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata,
+                                 const size_t supp_rates_len,
+                                 const u8 *supp_rates)
+{
+       struct ieee80211_local *local = sdata->local;
+       int i, have_higher_than_11mbit = 0;
+
+       /* cf. IEEE 802.11 9.2.12 */
+       for (i = 0; i < supp_rates_len; i++)
+               if ((supp_rates[i] & 0x7f) * 5 > 110)
+                       have_higher_than_11mbit = 1;
 
        if (local->hw.conf.channel->band == IEEE80211_BAND_2GHZ &&
-           !(sdata->flags & IEEE80211_SDATA_OPERATING_GMODE))
-               qparam.cw_min = 31;
+           have_higher_than_11mbit)
+               sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE;
        else
-               qparam.cw_min = 15;
+               sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE;
+
+       ieee80211_set_wmm_default(sdata);
+}
+
+u32 ieee80211_mandatory_rates(struct ieee80211_local *local,
+                             enum ieee80211_band band)
+{
+       struct ieee80211_supported_band *sband;
+       struct ieee80211_rate *bitrates;
+       u32 mandatory_rates;
+       enum ieee80211_rate_flags mandatory_flag;
+       int i;
+
+       sband = local->hw.wiphy->bands[band];
+       if (!sband) {
+               WARN_ON(1);
+               sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
+       }
+
+       if (band == IEEE80211_BAND_2GHZ)
+               mandatory_flag = IEEE80211_RATE_MANDATORY_B;
+       else
+               mandatory_flag = IEEE80211_RATE_MANDATORY_A;
+
+       bitrates = sband->bitrates;
+       mandatory_rates = 0;
+       for (i = 0; i < sband->n_bitrates; i++)
+               if (bitrates[i].flags & mandatory_flag)
+                       mandatory_rates |= BIT(i);
+       return mandatory_rates;
+}
+
+void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
+                        u16 transaction, u16 auth_alg,
+                        u8 *extra, size_t extra_len, const u8 *bssid,
+                        const u8 *key, u8 key_len, u8 key_idx)
+{
+       struct ieee80211_local *local = sdata->local;
+       struct sk_buff *skb;
+       struct ieee80211_mgmt *mgmt;
+       int err;
+
+       skb = dev_alloc_skb(local->hw.extra_tx_headroom +
+                           sizeof(*mgmt) + 6 + extra_len);
+       if (!skb) {
+               printk(KERN_DEBUG "%s: failed to allocate buffer for auth "
+                      "frame\n", sdata->name);
+               return;
+       }
+       skb_reserve(skb, local->hw.extra_tx_headroom);
+
+       mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24 + 6);
+       memset(mgmt, 0, 24 + 6);
+       mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+                                         IEEE80211_STYPE_AUTH);
+       memcpy(mgmt->da, bssid, ETH_ALEN);
+       memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
+       memcpy(mgmt->bssid, bssid, ETH_ALEN);
+       mgmt->u.auth.auth_alg = cpu_to_le16(auth_alg);
+       mgmt->u.auth.auth_transaction = cpu_to_le16(transaction);
+       mgmt->u.auth.status_code = cpu_to_le16(0);
+       if (extra)
+               memcpy(skb_put(skb, extra_len), extra, extra_len);
+
+       if (auth_alg == WLAN_AUTH_SHARED_KEY && transaction == 3) {
+               mgmt->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
+               err = ieee80211_wep_encrypt(local, skb, key, key_len, key_idx);
+               WARN_ON(err);
+       }
+
+       IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
+       ieee80211_tx_skb(sdata, skb);
+}
+
+int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
+                            const u8 *ie, size_t ie_len,
+                            enum ieee80211_band band)
+{
+       struct ieee80211_supported_band *sband;
+       u8 *pos;
+       size_t offset = 0, noffset;
+       int supp_rates_len, i;
+
+       sband = local->hw.wiphy->bands[band];
+
+       pos = buffer;
+
+       supp_rates_len = min_t(int, sband->n_bitrates, 8);
+
+       *pos++ = WLAN_EID_SUPP_RATES;
+       *pos++ = supp_rates_len;
+
+       for (i = 0; i < supp_rates_len; i++) {
+               int rate = sband->bitrates[i].bitrate;
+               *pos++ = (u8) (rate / 5);
+       }
+
+       /* insert "request information" if in custom IEs */
+       if (ie && ie_len) {
+               static const u8 before_extrates[] = {
+                       WLAN_EID_SSID,
+                       WLAN_EID_SUPP_RATES,
+                       WLAN_EID_REQUEST,
+               };
+               noffset = ieee80211_ie_split(ie, ie_len,
+                                            before_extrates,
+                                            ARRAY_SIZE(before_extrates),
+                                            offset);
+               memcpy(pos, ie + offset, noffset - offset);
+               pos += noffset - offset;
+               offset = noffset;
+       }
+
+       if (sband->n_bitrates > i) {
+               *pos++ = WLAN_EID_EXT_SUPP_RATES;
+               *pos++ = sband->n_bitrates - i;
+
+               for (; i < sband->n_bitrates; i++) {
+                       int rate = sband->bitrates[i].bitrate;
+                       *pos++ = (u8) (rate / 5);
+               }
+       }
+
+       /* insert custom IEs that go before HT */
+       if (ie && ie_len) {
+               static const u8 before_ht[] = {
+                       WLAN_EID_SSID,
+                       WLAN_EID_SUPP_RATES,
+                       WLAN_EID_REQUEST,
+                       WLAN_EID_EXT_SUPP_RATES,
+                       WLAN_EID_DS_PARAMS,
+                       WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
+               };
+               noffset = ieee80211_ie_split(ie, ie_len,
+                                            before_ht, ARRAY_SIZE(before_ht),
+                                            offset);
+               memcpy(pos, ie + offset, noffset - offset);
+               pos += noffset - offset;
+               offset = noffset;
+       }
+
+       if (sband->ht_cap.ht_supported) {
+               u16 cap = sband->ht_cap.cap;
+               __le16 tmp;
+
+               if (ieee80211_disable_40mhz_24ghz &&
+                   sband->band == IEEE80211_BAND_2GHZ) {
+                       cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+                       cap &= ~IEEE80211_HT_CAP_SGI_40;
+               }
+
+               *pos++ = WLAN_EID_HT_CAPABILITY;
+               *pos++ = sizeof(struct ieee80211_ht_cap);
+               memset(pos, 0, sizeof(struct ieee80211_ht_cap));
+               tmp = cpu_to_le16(cap);
+               memcpy(pos, &tmp, sizeof(u16));
+               pos += sizeof(u16);
+               *pos++ = sband->ht_cap.ampdu_factor |
+                        (sband->ht_cap.ampdu_density <<
+                               IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT);
+               memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs));
+               pos += sizeof(sband->ht_cap.mcs);
+               pos += 2 + 4 + 1; /* ext info, BF cap, antsel */
+       }
+
+       /*
+        * If adding more here, adjust code in main.c
+        * that calculates local->scan_ies_len.
+        */
+
+       /* add any remaining custom IEs */
+       if (ie && ie_len) {
+               noffset = ie_len;
+               memcpy(pos, ie + offset, noffset - offset);
+               pos += noffset - offset;
+       }
+
+       return pos - buffer;
+}
+
+void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
+                             const u8 *ssid, size_t ssid_len,
+                             const u8 *ie, size_t ie_len)
+{
+       struct ieee80211_local *local = sdata->local;
+       struct sk_buff *skb;
+       struct ieee80211_mgmt *mgmt;
+       size_t buf_len;
+       u8 *buf;
+
+       /* FIXME: come up with a proper value */
+       buf = kmalloc(200 + ie_len, GFP_KERNEL);
+       if (!buf) {
+               printk(KERN_DEBUG "%s: failed to allocate temporary IE "
+                      "buffer\n", sdata->name);
+               return;
+       }
+
+       buf_len = ieee80211_build_preq_ies(local, buf, ie, ie_len,
+                                          local->hw.conf.channel->band);
+
+       skb = ieee80211_probereq_get(&local->hw, &sdata->vif,
+                                    ssid, ssid_len,
+                                    buf, buf_len);
+
+       if (dst) {
+               mgmt = (struct ieee80211_mgmt *) skb->data;
+               memcpy(mgmt->da, dst, ETH_ALEN);
+               memcpy(mgmt->bssid, dst, ETH_ALEN);
+       }
+
+       IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
+       ieee80211_tx_skb(sdata, skb);
+       kfree(buf);
+}
+
+u32 ieee80211_sta_get_rates(struct ieee80211_local *local,
+                           struct ieee802_11_elems *elems,
+                           enum ieee80211_band band)
+{
+       struct ieee80211_supported_band *sband;
+       struct ieee80211_rate *bitrates;
+       size_t num_rates;
+       u32 supp_rates;
+       int i, j;
+       sband = local->hw.wiphy->bands[band];
+
+       if (!sband) {
+               WARN_ON(1);
+               sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
+       }
+
+       bitrates = sband->bitrates;
+       num_rates = sband->n_bitrates;
+       supp_rates = 0;
+       for (i = 0; i < elems->supp_rates_len +
+                    elems->ext_supp_rates_len; i++) {
+               u8 rate = 0;
+               int own_rate;
+               if (i < elems->supp_rates_len)
+                       rate = elems->supp_rates[i];
+               else if (elems->ext_supp_rates)
+                       rate = elems->ext_supp_rates
+                               [i - elems->supp_rates_len];
+               own_rate = 5 * (rate & 0x7f);
+               for (j = 0; j < num_rates; j++)
+                       if (bitrates[j].bitrate == own_rate)
+                               supp_rates |= BIT(j);
+       }
+       return supp_rates;
+}
+
+void ieee80211_stop_device(struct ieee80211_local *local)
+{
+       ieee80211_led_radio(local, false);
+
+       cancel_work_sync(&local->reconfig_filter);
+
+       flush_workqueue(local->workqueue);
+       drv_stop(local);
+}
+
+int ieee80211_reconfig(struct ieee80211_local *local)
+{
+       struct ieee80211_hw *hw = &local->hw;
+       struct ieee80211_sub_if_data *sdata;
+       struct sta_info *sta;
+       int res;
+
+       if (local->suspended)
+               local->resuming = true;
+
+       /* restart hardware */
+       if (local->open_count) {
+               /*
+                * Upon resume hardware can sometimes be goofy due to
+                * various platform / driver / bus issues, so restarting
+                * the device may at times not work immediately. Propagate
+                * the error.
+                */
+               res = drv_start(local);
+               if (res) {
+                       WARN(local->suspended, "Harware became unavailable "
+                            "upon resume. This is could be a software issue"
+                            "prior to suspend or a hardware issue\n");
+                       return res;
+               }
+
+               ieee80211_led_radio(local, true);
+       }
+
+       /* add interfaces */
+       list_for_each_entry(sdata, &local->interfaces, list) {
+               if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
+                   sdata->vif.type != NL80211_IFTYPE_MONITOR &&
+                   ieee80211_sdata_running(sdata))
+                       res = drv_add_interface(local, &sdata->vif);
+       }
+
+       /* add STAs back */
+       mutex_lock(&local->sta_mtx);
+       list_for_each_entry(sta, &local->sta_list, list) {
+               if (sta->uploaded) {
+                       sdata = sta->sdata;
+                       if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+                               sdata = container_of(sdata->bss,
+                                            struct ieee80211_sub_if_data,
+                                            u.ap);
+
+                       WARN_ON(drv_sta_add(local, sdata, &sta->sta));
+               }
+       }
+       mutex_unlock(&local->sta_mtx);
+
+       /* Clear Suspend state so that ADDBA requests can be processed */
+
+       rcu_read_lock();
+
+       if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) {
+               list_for_each_entry_rcu(sta, &local->sta_list, list) {
+                       clear_sta_flags(sta, WLAN_STA_SUSPEND);
+               }
+       }
+
+       rcu_read_unlock();
+
+       /* setup RTS threshold */
+       drv_set_rts_threshold(local, hw->wiphy->rts_threshold);
+
+       /* reconfigure hardware */
+       ieee80211_hw_config(local, ~0);
+
+       ieee80211_configure_filter(local);
+
+       /* Finally also reconfigure all the BSS information */
+       list_for_each_entry(sdata, &local->interfaces, list) {
+               u32 changed = ~0;
+               if (!ieee80211_sdata_running(sdata))
+                       continue;
+               switch (sdata->vif.type) {
+               case NL80211_IFTYPE_STATION:
+                       /* disable beacon change bits */
+                       changed &= ~(BSS_CHANGED_BEACON |
+                                    BSS_CHANGED_BEACON_ENABLED);
+                       /* fall through */
+               case NL80211_IFTYPE_ADHOC:
+               case NL80211_IFTYPE_AP:
+               case NL80211_IFTYPE_MESH_POINT:
+                       ieee80211_bss_info_change_notify(sdata, changed);
+                       break;
+               case NL80211_IFTYPE_WDS:
+                       break;
+               case NL80211_IFTYPE_AP_VLAN:
+               case NL80211_IFTYPE_MONITOR:
+                       /* ignore virtual */
+                       break;
+               case NL80211_IFTYPE_UNSPECIFIED:
+               case __NL80211_IFTYPE_AFTER_LAST:
+                       WARN_ON(1);
+                       break;
+               }
+       }
+
+       /* add back keys */
+       list_for_each_entry(sdata, &local->interfaces, list)
+               if (ieee80211_sdata_running(sdata))
+                       ieee80211_enable_keys(sdata);
+
+       ieee80211_wake_queues_by_reason(hw,
+                       IEEE80211_QUEUE_STOP_REASON_SUSPEND);
+
+       /*
+        * If this is for hw restart things are still running.
+        * We may want to change that later, however.
+        */
+       if (!local->suspended)
+               return 0;
+
+#ifdef CONFIG_PM
+       /* first set suspended false, then resuming */
+       local->suspended = false;
+       mb();
+       local->resuming = false;
+
+       list_for_each_entry(sdata, &local->interfaces, list) {
+               switch(sdata->vif.type) {
+               case NL80211_IFTYPE_STATION:
+                       ieee80211_sta_restart(sdata);
+                       break;
+               case NL80211_IFTYPE_ADHOC:
+                       ieee80211_ibss_restart(sdata);
+                       break;
+               case NL80211_IFTYPE_MESH_POINT:
+                       ieee80211_mesh_restart(sdata);
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       add_timer(&local->sta_cleanup);
+
+       mutex_lock(&local->sta_mtx);
+       list_for_each_entry(sta, &local->sta_list, list)
+               mesh_plink_restart(sta);
+       mutex_unlock(&local->sta_mtx);
+#else
+       WARN_ON(1);
+#endif
+       return 0;
+}
+
+static int check_mgd_smps(struct ieee80211_if_managed *ifmgd,
+                         enum ieee80211_smps_mode *smps_mode)
+{
+       if (ifmgd->associated) {
+               *smps_mode = ifmgd->ap_smps;
+
+               if (*smps_mode == IEEE80211_SMPS_AUTOMATIC) {
+                       if (ifmgd->powersave)
+                               *smps_mode = IEEE80211_SMPS_DYNAMIC;
+                       else
+                               *smps_mode = IEEE80211_SMPS_OFF;
+               }
+
+               return 1;
+       }
+
+       return 0;
+}
+
+/* must hold iflist_mtx */
+void ieee80211_recalc_smps(struct ieee80211_local *local,
+                          struct ieee80211_sub_if_data *forsdata)
+{
+       struct ieee80211_sub_if_data *sdata;
+       enum ieee80211_smps_mode smps_mode = IEEE80211_SMPS_OFF;
+       int count = 0;
+
+       if (forsdata)
+               WARN_ON(!mutex_is_locked(&forsdata->u.mgd.mtx));
+
+       WARN_ON(!mutex_is_locked(&local->iflist_mtx));
+
+       /*
+        * This function could be improved to handle multiple
+        * interfaces better, but right now it makes any
+        * non-station interfaces force SM PS to be turned
+        * off. If there are multiple station interfaces it
+        * could also use the best possible mode, e.g. if
+        * one is in static and the other in dynamic then
+        * dynamic is ok.
+        */
+
+       list_for_each_entry(sdata, &local->interfaces, list) {
+               if (!netif_running(sdata->dev))
+                       continue;
+               if (sdata->vif.type != NL80211_IFTYPE_STATION)
+                       goto set;
+               if (sdata != forsdata) {
+                       /*
+                        * This nested is ok -- we are holding the iflist_mtx
+                        * so can't get here twice or so. But it's required
+                        * since normally we acquire it first and then the
+                        * iflist_mtx.
+                        */
+                       mutex_lock_nested(&sdata->u.mgd.mtx, SINGLE_DEPTH_NESTING);
+                       count += check_mgd_smps(&sdata->u.mgd, &smps_mode);
+                       mutex_unlock(&sdata->u.mgd.mtx);
+               } else
+                       count += check_mgd_smps(&sdata->u.mgd, &smps_mode);
+
+               if (count > 1) {
+                       smps_mode = IEEE80211_SMPS_OFF;
+                       break;
+               }
+       }
+
+       if (smps_mode == local->smps_mode)
+               return;
+
+ set:
+       local->smps_mode = smps_mode;
+       /* changed flag is auto-detected for this */
+       ieee80211_hw_config(local, 0);
+}
+
+static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id)
+{
+       int i;
+
+       for (i = 0; i < n_ids; i++)
+               if (ids[i] == id)
+                       return true;
+       return false;
+}
+
+/**
+ * ieee80211_ie_split - split an IE buffer according to ordering
+ *
+ * @ies: the IE buffer
+ * @ielen: the length of the IE buffer
+ * @ids: an array with element IDs that are allowed before
+ *     the split
+ * @n_ids: the size of the element ID array
+ * @offset: offset where to start splitting in the buffer
+ *
+ * This function splits an IE buffer by updating the @offset
+ * variable to point to the location where the buffer should be
+ * split.
+ *
+ * It assumes that the given IE buffer is well-formed, this
+ * has to be guaranteed by the caller!
+ *
+ * It also assumes that the IEs in the buffer are ordered
+ * correctly, if not the result of using this function will not
+ * be ordered correctly either, i.e. it does no reordering.
+ *
+ * The function returns the offset where the next part of the
+ * buffer starts, which may be @ielen if the entire (remainder)
+ * of the buffer should be used.
+ */
+size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
+                         const u8 *ids, int n_ids, size_t offset)
+{
+       size_t pos = offset;
+
+       while (pos < ielen && ieee80211_id_in_list(ids, n_ids, ies[pos]))
+               pos += 2 + ies[pos + 1];
+
+       return pos;
+}
+
+size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset)
+{
+       size_t pos = offset;
 
-       qparam.cw_max = 1023;
-       qparam.txop = 0;
+       while (pos < ielen && ies[pos] != WLAN_EID_VENDOR_SPECIFIC)
+               pos += 2 + ies[pos + 1];
 
-       for (i = 0; i < local_to_hw(local)->queues; i++)
-               local->ops->conf_tx(local_to_hw(local), i, &qparam);
+       return pos;
 }