sctp: fix sctp_setsockopt_autoclose compile warning
[safe/jmp/linux-2.6] / net / mac80211 / util.c
index 2cde9bb..d09f78b 100644 (file)
 #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;
@@ -102,70 +95,6 @@ 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 sk_buff *skb = tx->skb;
@@ -346,16 +275,12 @@ static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue,
 
        __clear_bit(reason, &local->queue_stop_reasons[queue]);
 
-       if (!skb_queue_empty(&local->pending[queue]) &&
-           local->queue_stop_reasons[queue] ==
-                               BIT(IEEE80211_QUEUE_STOP_REASON_PENDING))
-               tasklet_schedule(&local->tx_pending_tasklet);
-
        if (local->queue_stop_reasons[queue] != 0)
                /* someone still has this queue stopped */
                return;
 
-       netif_wake_subqueue(local->mdev, queue);
+       if (!skb_queue_empty(&local->pending[queue]))
+               tasklet_schedule(&local->tx_pending_tasklet);
 }
 
 void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue,
@@ -384,14 +309,6 @@ static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue,
        if (WARN_ON(queue >= hw->queues))
                return;
 
-       /*
-        * Only stop if it was previously running, this is necessary
-        * for correct pending packets handling because there we may
-        * start (but not wake) the queue and rely on that.
-        */
-       if (!local->queue_stop_reasons[queue])
-               netif_stop_subqueue(local->mdev, queue);
-
        __set_bit(reason, &local->queue_stop_reasons[queue]);
 }
 
@@ -413,6 +330,60 @@ void ieee80211_stop_queue(struct ieee80211_hw *hw, int queue)
 }
 EXPORT_SYMBOL(ieee80211_stop_queue);
 
+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)
 {
@@ -438,11 +409,16 @@ EXPORT_SYMBOL(ieee80211_stop_queues);
 int ieee80211_queue_stopped(struct ieee80211_hw *hw, int queue)
 {
        struct ieee80211_local *local = hw_to_local(hw);
+       unsigned long flags;
+       int ret;
 
        if (WARN_ON(queue >= hw->queues))
                return true;
 
-       return __netif_subqueue_stopped(local->mdev, queue);
+       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);
 
@@ -535,6 +511,46 @@ 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)
 {
@@ -650,8 +666,8 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,
                        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;
@@ -669,6 +685,10 @@ u32 ieee802_11_parse_elems_crc(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;
@@ -707,26 +727,62 @@ 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;
-
-       if (local->hw.conf.channel->band == IEEE80211_BAND_2GHZ &&
-           !(sdata->flags & IEEE80211_SDATA_OPERATING_GMODE))
-               qparam.cw_min = 31;
-       else
-               qparam.cw_min = 15;
+       use_11b = (local->hw.conf.channel->band == IEEE80211_BAND_2GHZ) &&
+                !(sdata->flags & IEEE80211_SDATA_OPERATING_GMODE);
 
-       qparam.cw_max = 1023;
-       qparam.txop = 0;
+       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;
+               }
 
-       for (i = 0; i < local_to_hw(local)->queues; i++)
-               local->ops->conf_tx(local_to_hw(local), i, &qparam);
+               drv_conf_tx(local, queue, &qparam);
+       }
 }
 
 void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata,
@@ -750,45 +806,6 @@ void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata,
        ieee80211_set_wmm_default(sdata);
 }
 
-void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
-                     int encrypt)
-{
-       skb->dev = sdata->local->mdev;
-       skb_set_mac_header(skb, 0);
-       skb_set_network_header(skb, 0);
-       skb_set_transport_header(skb, 0);
-
-       skb->iif = sdata->dev->ifindex;
-       skb->do_not_encrypt = !encrypt;
-
-       dev_queue_xmit(skb);
-}
-
-int ieee80211_set_freq(struct ieee80211_sub_if_data *sdata, int freqMHz)
-{
-       int ret = -EINVAL;
-       struct ieee80211_channel *chan;
-       struct ieee80211_local *local = sdata->local;
-
-       chan = ieee80211_get_channel(local->hw.wiphy, freqMHz);
-
-       if (chan && !(chan->flags & IEEE80211_CHAN_DISABLED)) {
-               if (sdata->vif.type == NL80211_IFTYPE_ADHOC &&
-                   chan->flags & IEEE80211_CHAN_NO_IBSS)
-                       return ret;
-               local->oper_channel = chan;
-               local->oper_channel_type = NL80211_CHAN_NO_HT;
-
-               if (local->sw_scanning || local->hw_scanning)
-                       ret = 0;
-               else
-                       ret = ieee80211_hw_config(
-                               local, IEEE80211_CONF_CHANGE_CHANNEL);
-       }
-
-       return ret;
-}
-
 u32 ieee80211_mandatory_rates(struct ieee80211_local *local,
                              enum ieee80211_band band)
 {
@@ -819,12 +836,13 @@ u32 ieee80211_mandatory_rates(struct ieee80211_local *local,
 
 void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
                         u16 transaction, u16 auth_alg,
-                        u8 *extra, size_t extra_len,
-                        const u8 *bssid, int encrypt)
+                        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);
@@ -839,8 +857,6 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
        memset(mgmt, 0, 24 + 6);
        mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
                                          IEEE80211_STYPE_AUTH);
-       if (encrypt)
-               mgmt->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
        memcpy(mgmt->da, bssid, ETH_ALEN);
        memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
        memcpy(mgmt->bssid, bssid, ETH_ALEN);
@@ -850,17 +866,25 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
        if (extra)
                memcpy(skb_put(skb, extra_len), extra, extra_len);
 
-       ieee80211_tx_skb(sdata, skb, encrypt);
+       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)
+                            const u8 *ie, size_t ie_len,
+                            enum ieee80211_band band)
 {
        struct ieee80211_supported_band *sband;
        u8 *pos, *supp_rates_len, *esupp_rates_len = NULL;
        int i;
 
-       sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
+       sband = local->hw.wiphy->bands[band];
 
        pos = buffer;
 
@@ -948,9 +972,11 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
        memcpy(pos, ssid, ssid_len);
        pos += ssid_len;
 
-       skb_put(skb, ieee80211_build_preq_ies(local, pos, ie, ie_len));
+       skb_put(skb, ieee80211_build_preq_ies(local, pos, ie, ie_len,
+                                             local->hw.conf.channel->band));
 
-       ieee80211_tx_skb(sdata, skb, 0);
+       IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
+       ieee80211_tx_skb(sdata, skb);
 }
 
 u32 ieee80211_sta_get_rates(struct ieee80211_local *local,
@@ -989,6 +1015,16 @@ u32 ieee80211_sta_get_rates(struct ieee80211_local *local,
        return supp_rates;
 }
 
+void ieee80211_stop_device(struct ieee80211_local *local)
+{
+       ieee80211_led_radio(local, false);
+
+       cancel_work_sync(&local->reconfig_filter);
+       drv_stop(local);
+
+       flush_workqueue(local->workqueue);
+}
+
 int ieee80211_reconfig(struct ieee80211_local *local)
 {
        struct ieee80211_hw *hw = &local->hw;
@@ -998,11 +1034,14 @@ int ieee80211_reconfig(struct ieee80211_local *local)
        unsigned long flags;
        int res;
 
+       if (local->suspended)
+               local->resuming = true;
+
        /* restart hardware */
        if (local->open_count) {
-               res = local->ops->start(hw);
+               res = drv_start(local);
 
-               ieee80211_led_radio(local, hw->conf.radio_enabled);
+               ieee80211_led_radio(local, true);
        }
 
        /* add interfaces */
@@ -1013,7 +1052,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
                        conf.vif = &sdata->vif;
                        conf.type = sdata->vif.type;
                        conf.mac_addr = sdata->dev->dev_addr;
-                       res = local->ops->add_interface(hw, &conf);
+                       res = drv_add_interface(local, &conf);
                }
        }
 
@@ -1021,13 +1060,14 @@ int ieee80211_reconfig(struct ieee80211_local *local)
        if (local->ops->sta_notify) {
                spin_lock_irqsave(&local->sta_lock, flags);
                list_for_each_entry(sta, &local->sta_list, list) {
+                       sdata = sta->sdata;
                        if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
                                sdata = container_of(sdata->bss,
                                             struct ieee80211_sub_if_data,
                                             u.ap);
 
-                       local->ops->sta_notify(hw, &sdata->vif,
-                               STA_NOTIFY_ADD, &sta->sta);
+                       drv_sta_notify(local, &sdata->vif, STA_NOTIFY_ADD,
+                                      &sta->sta);
                }
                spin_unlock_irqrestore(&local->sta_lock, flags);
        }
@@ -1045,15 +1085,12 @@ int ieee80211_reconfig(struct ieee80211_local *local)
        rcu_read_unlock();
 
        /* setup RTS threshold */
-       if (local->ops->set_rts_threshold)
-               local->ops->set_rts_threshold(hw, hw->wiphy->rts_threshold);
+       drv_set_rts_threshold(local, hw->wiphy->rts_threshold);
 
        /* reconfigure hardware */
        ieee80211_hw_config(local, ~0);
 
-       netif_addr_lock_bh(local->mdev);
        ieee80211_configure_filter(local);
-       netif_addr_unlock_bh(local->mdev);
 
        /* Finally also reconfigure all the BSS information */
        list_for_each_entry(sdata, &local->interfaces, list) {
@@ -1092,5 +1129,44 @@ int ieee80211_reconfig(struct ieee80211_local *local)
        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);
+
+       spin_lock_irqsave(&local->sta_lock, flags);
+       list_for_each_entry(sta, &local->sta_list, list)
+               mesh_plink_restart(sta);
+       spin_unlock_irqrestore(&local->sta_lock, flags);
+#else
+       WARN_ON(1);
+#endif
        return 0;
 }
+