Merge git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-2.6
[safe/jmp/linux-2.6] / net / mac80211 / util.c
index 3848140..bc73904 100644 (file)
@@ -18,7 +18,6 @@
 #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>
@@ -480,8 +479,8 @@ void ieee80211_iterate_active_interfaces(
                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);
        }
 
@@ -514,8 +513,8 @@ void ieee80211_iterate_active_interfaces_atomic(
                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);
        }
 
@@ -860,7 +859,7 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
                            sizeof(*mgmt) + 6 + extra_len);
        if (!skb) {
                printk(KERN_DEBUG "%s: failed to allocate buffer for auth "
-                      "frame\n", sdata->dev->name);
+                      "frame\n", sdata->name);
                return;
        }
        skb_reserve(skb, local->hw.extra_tx_headroom);
@@ -870,7 +869,7 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
        mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
                                          IEEE80211_STYPE_AUTH);
        memcpy(mgmt->da, bssid, ETH_ALEN);
-       memcpy(mgmt->sa, sdata->dev->dev_addr, 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);
@@ -893,43 +892,87 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
                             enum ieee80211_band band)
 {
        struct ieee80211_supported_band *sband;
-       u8 *pos, *supp_rates_len, *esupp_rates_len = NULL;
-       int i;
+       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;
-       supp_rates_len = pos;
-       *pos++ = 0;
-
-       for (i = 0; i < sband->n_bitrates; i++) {
-               struct ieee80211_rate *rate = &sband->bitrates[i];
-
-               if (esupp_rates_len) {
-                       *esupp_rates_len += 1;
-               } else if (*supp_rates_len == 8) {
-                       *pos++ = WLAN_EID_EXT_SUPP_RATES;
-                       esupp_rates_len = pos;
-                       *pos++ = 1;
-               } else
-                       *supp_rates_len += 1;
+       *pos++ = supp_rates_len;
 
-               *pos++ = rate->bitrate / 5;
+       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) {
-               __le16 tmp = cpu_to_le16(sband->ht_cap.cap);
+               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);
-               /* TODO: needs a define here for << 2 */
                *pos++ = sband->ht_cap.ampdu_factor |
-                        (sband->ht_cap.ampdu_density << 2);
+                        (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 */
@@ -940,9 +983,11 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
         * that calculates local->scan_ies_len.
         */
 
-       if (ie) {
-               memcpy(pos, ie, ie_len);
-               pos += ie_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;
@@ -961,7 +1006,7 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
                            ie_len);
        if (!skb) {
                printk(KERN_DEBUG "%s: failed to allocate buffer for probe "
-                      "request\n", sdata->dev->name);
+                      "request\n", sdata->name);
                return;
        }
        skb_reserve(skb, local->hw.extra_tx_headroom);
@@ -970,7 +1015,7 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
        memset(mgmt, 0, 24);
        mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
                                          IEEE80211_STYPE_PROBE_REQ);
-       memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
+       memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
        if (dst) {
                memcpy(mgmt->da, dst, ETH_ALEN);
                memcpy(mgmt->bssid, dst, ETH_ALEN);
@@ -1041,7 +1086,6 @@ int ieee80211_reconfig(struct ieee80211_local *local)
 {
        struct ieee80211_hw *hw = &local->hw;
        struct ieee80211_sub_if_data *sdata;
-       struct ieee80211_if_init_conf conf;
        struct sta_info *sta;
        unsigned long flags;
        int res;
@@ -1061,7 +1105,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
                if (res) {
                        WARN(local->suspended, "Harware became unavailable "
                             "upon resume. This is could be a software issue"
-                            "prior to suspend or a harware issue\n");
+                            "prior to suspend or a hardware issue\n");
                        return res;
                }
 
@@ -1072,12 +1116,8 @@ int ieee80211_reconfig(struct ieee80211_local *local)
        list_for_each_entry(sdata, &local->interfaces, list) {
                if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
                    sdata->vif.type != NL80211_IFTYPE_MONITOR &&
-                   netif_running(sdata->dev)) {
-                       conf.vif = &sdata->vif;
-                       conf.type = sdata->vif.type;
-                       conf.mac_addr = sdata->dev->dev_addr;
-                       res = drv_add_interface(local, &conf);
-               }
+                   ieee80211_sdata_running(sdata))
+                       res = drv_add_interface(local, &sdata->vif);
        }
 
        /* add STAs back */
@@ -1090,7 +1130,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
                                             struct ieee80211_sub_if_data,
                                             u.ap);
 
-                       drv_sta_notify(local, &sdata->vif, STA_NOTIFY_ADD,
+                       drv_sta_notify(local, sdata, STA_NOTIFY_ADD,
                                       &sta->sta);
                }
                spin_unlock_irqrestore(&local->sta_lock, flags);
@@ -1119,7 +1159,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
        /* Finally also reconfigure all the BSS information */
        list_for_each_entry(sdata, &local->interfaces, list) {
                u32 changed = ~0;
-               if (!netif_running(sdata->dev))
+               if (!ieee80211_sdata_running(sdata))
                        continue;
                switch (sdata->vif.type) {
                case NL80211_IFTYPE_STATION:
@@ -1147,7 +1187,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
 
        /* add back keys */
        list_for_each_entry(sdata, &local->interfaces, list)
-               if (netif_running(sdata->dev))
+               if (ieee80211_sdata_running(sdata))
                        ieee80211_enable_keys(sdata);
 
        ieee80211_wake_queues_by_reason(hw,
@@ -1194,3 +1234,133 @@ int ieee80211_reconfig(struct ieee80211_local *local)
        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;
+
+       while (pos < ielen && ies[pos] != WLAN_EID_VENDOR_SPECIFIC)
+               pos += 2 + ies[pos + 1];
+
+       return pos;
+}