mac80211: refactor association
[safe/jmp/linux-2.6] / net / mac80211 / work.c
index 8743459..c03c22d 100644 (file)
@@ -100,6 +100,102 @@ static int ieee80211_compatible_rates(const u8 *supp_rates, int supp_rates_len,
 
 /* frame sending functions */
 
+static void ieee80211_add_ht_ie(struct sk_buff *skb, const u8 *ht_info_ie,
+                               struct ieee80211_supported_band *sband,
+                               struct ieee80211_channel *channel,
+                               enum ieee80211_smps_mode smps)
+{
+       struct ieee80211_ht_info *ht_info;
+       u8 *pos;
+       u32 flags = channel->flags;
+       u16 cap = sband->ht_cap.cap;
+       __le16 tmp;
+
+       if (!sband->ht_cap.ht_supported)
+               return;
+
+       if (!ht_info_ie)
+               return;
+
+       if (ht_info_ie[1] < sizeof(struct ieee80211_ht_info))
+               return;
+
+       ht_info = (struct ieee80211_ht_info *)(ht_info_ie + 2);
+
+       /* determine capability flags */
+
+       if (ieee80211_disable_40mhz_24ghz &&
+           sband->band == IEEE80211_BAND_2GHZ) {
+               cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+               cap &= ~IEEE80211_HT_CAP_SGI_40;
+       }
+
+       switch (ht_info->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
+       case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
+               if (flags & IEEE80211_CHAN_NO_HT40PLUS) {
+                       cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+                       cap &= ~IEEE80211_HT_CAP_SGI_40;
+               }
+               break;
+       case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
+               if (flags & IEEE80211_CHAN_NO_HT40MINUS) {
+                       cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+                       cap &= ~IEEE80211_HT_CAP_SGI_40;
+               }
+               break;
+       }
+
+       /* set SM PS mode properly */
+       cap &= ~IEEE80211_HT_CAP_SM_PS;
+       switch (smps) {
+       case IEEE80211_SMPS_AUTOMATIC:
+       case IEEE80211_SMPS_NUM_MODES:
+               WARN_ON(1);
+       case IEEE80211_SMPS_OFF:
+               cap |= WLAN_HT_CAP_SM_PS_DISABLED <<
+                       IEEE80211_HT_CAP_SM_PS_SHIFT;
+               break;
+       case IEEE80211_SMPS_STATIC:
+               cap |= WLAN_HT_CAP_SM_PS_STATIC <<
+                       IEEE80211_HT_CAP_SM_PS_SHIFT;
+               break;
+       case IEEE80211_SMPS_DYNAMIC:
+               cap |= WLAN_HT_CAP_SM_PS_DYNAMIC <<
+                       IEEE80211_HT_CAP_SM_PS_SHIFT;
+               break;
+       }
+
+       /* reserve and fill IE */
+
+       pos = skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2);
+       *pos++ = WLAN_EID_HT_CAPABILITY;
+       *pos++ = sizeof(struct ieee80211_ht_cap);
+       memset(pos, 0, sizeof(struct ieee80211_ht_cap));
+
+       /* capability flags */
+       tmp = cpu_to_le16(cap);
+       memcpy(pos, &tmp, sizeof(u16));
+       pos += sizeof(u16);
+
+       /* AMPDU parameters */
+       *pos++ = sband->ht_cap.ampdu_factor |
+                (sband->ht_cap.ampdu_density <<
+                       IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT);
+
+       /* MCS set */
+       memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs));
+       pos += sizeof(sband->ht_cap.mcs);
+
+       /* extended capabilities */
+       pos += sizeof(__le16);
+
+       /* BF capabilities */
+       pos += sizeof(__le32);
+
+       /* antenna selection */
+       pos += sizeof(u8);
+}
+
 static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
                                 struct ieee80211_work *wk)
 {
@@ -107,15 +203,34 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
        struct sk_buff *skb;
        struct ieee80211_mgmt *mgmt;
        u8 *pos;
-       const u8 *ies, *ht_ie;
+       const u8 *ies;
        int i, len, count, rates_len, supp_rates_len;
        u16 capab;
        struct ieee80211_supported_band *sband;
        u32 rates = 0;
 
-       skb = dev_alloc_skb(local->hw.extra_tx_headroom +
-                           sizeof(*mgmt) + 200 + wk->ie_len +
-                           wk->assoc.ssid_len);
+       sband = local->hw.wiphy->bands[wk->chan->band];
+
+       /*
+        * Get all rates supported by the device and the AP as
+        * some APs don't like getting a superset of their rates
+        * in the association request (e.g. D-Link DAP 1353 in
+        * b-only mode)...
+        */
+       rates_len = ieee80211_compatible_rates(wk->assoc.supp_rates,
+                                              wk->assoc.supp_rates_len,
+                                              sband, &rates);
+
+       skb = alloc_skb(local->hw.extra_tx_headroom +
+                       sizeof(*mgmt) + /* bit too much but doesn't matter */
+                       2 + wk->assoc.ssid_len + /* SSID */
+                       4 + rates_len + /* (extended) rates */
+                       4 + /* power capability */
+                       2 + 2 * sband->n_channels + /* supported channels */
+                       2 + sizeof(struct ieee80211_ht_cap) + /* HT */
+                       wk->ie_len + /* extra IEs */
+                       9, /* WMM */
+                       GFP_KERNEL);
        if (!skb) {
                printk(KERN_DEBUG "%s: failed to allocate buffer for assoc "
                       "frame\n", sdata->name);
@@ -123,8 +238,6 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
        }
        skb_reserve(skb, local->hw.extra_tx_headroom);
 
-       sband = local->hw.wiphy->bands[wk->chan->band];
-
        capab = WLAN_CAPABILITY_ESS;
 
        if (sband->band == IEEE80211_BAND_2GHZ) {
@@ -137,16 +250,6 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
        if (wk->assoc.capability & WLAN_CAPABILITY_PRIVACY)
                capab |= WLAN_CAPABILITY_PRIVACY;
 
-       /*
-        * Get all rates supported by the device and the AP as
-        * some APs don't like getting a superset of their rates
-        * in the association request (e.g. D-Link DAP 1353 in
-        * b-only mode)...
-        */
-       rates_len = ieee80211_compatible_rates(wk->assoc.supp_rates,
-                                              wk->assoc.supp_rates_len,
-                                              sband, &rates);
-
        if ((wk->assoc.capability & WLAN_CAPABILITY_SPECTRUM_MGMT) &&
            (local->hw.flags & IEEE80211_HW_SPECTRUM_MGMT))
                capab |= WLAN_CAPABILITY_SPECTRUM_MGMT;
@@ -220,7 +323,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
                *pos++ = WLAN_EID_PWR_CAPABILITY;
                *pos++ = 2;
                *pos++ = 0; /* min tx power */
-               *pos++ = local->hw.conf.channel->max_power; /* max tx power */
+               *pos++ = wk->chan->max_power; /* max tx power */
 
                /* 2. supported channels */
                /* TODO: get this in reg domain format */
@@ -234,11 +337,21 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
                }
        }
 
+       /*
+        * XXX: These IEs could contain (vendor-specified)
+        *      IEs that belong after HT -- the buffer may
+        *      need to be split up.
+        */
        if (wk->ie_len && wk->ie) {
                pos = skb_put(skb, wk->ie_len);
                memcpy(pos, wk->ie, wk->ie_len);
        }
 
+       if (wk->assoc.use_11n && wk->assoc.wmm_used &&
+           local->hw.queues >= 4)
+               ieee80211_add_ht_ie(skb, wk->assoc.ht_information_ie,
+                                   sband, wk->chan, wk->assoc.smps);
+
        if (wk->assoc.wmm_used && local->hw.queues >= 4) {
                pos = skb_put(skb, 9);
                *pos++ = WLAN_EID_VENDOR_SPECIFIC;
@@ -252,98 +365,6 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
                *pos++ = 0;
        }
 
-       /* wmm support is a must to HT */
-       /*
-        * IEEE802.11n does not allow TKIP/WEP as pairwise
-        * ciphers in HT mode. We still associate in non-ht
-        * mode (11a/b/g) if any one of these ciphers is
-        * configured as pairwise.
-        */
-       if (wk->assoc.use_11n && wk->assoc.wmm_used &&
-           (local->hw.queues >= 4) &&
-           sband->ht_cap.ht_supported &&
-           (ht_ie = wk->assoc.ht_information_ie) &&
-           ht_ie[1] >= sizeof(struct ieee80211_ht_info)) {
-               struct ieee80211_ht_info *ht_info =
-                       (struct ieee80211_ht_info *)(ht_ie + 2);
-               u16 cap = sband->ht_cap.cap;
-               __le16 tmp;
-               u32 flags = local->hw.conf.channel->flags;
-
-               /* determine capability flags */
-
-               if (ieee80211_disable_40mhz_24ghz &&
-                   sband->band == IEEE80211_BAND_2GHZ) {
-                       cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
-                       cap &= ~IEEE80211_HT_CAP_SGI_40;
-               }
-
-               switch (ht_info->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
-               case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
-                       if (flags & IEEE80211_CHAN_NO_HT40PLUS) {
-                               cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
-                               cap &= ~IEEE80211_HT_CAP_SGI_40;
-                       }
-                       break;
-               case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
-                       if (flags & IEEE80211_CHAN_NO_HT40MINUS) {
-                               cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
-                               cap &= ~IEEE80211_HT_CAP_SGI_40;
-                       }
-                       break;
-               }
-
-               /* set SM PS mode properly */
-               cap &= ~IEEE80211_HT_CAP_SM_PS;
-               switch (wk->assoc.smps) {
-               case IEEE80211_SMPS_AUTOMATIC:
-               case IEEE80211_SMPS_NUM_MODES:
-                       WARN_ON(1);
-               case IEEE80211_SMPS_OFF:
-                       cap |= WLAN_HT_CAP_SM_PS_DISABLED <<
-                               IEEE80211_HT_CAP_SM_PS_SHIFT;
-                       break;
-               case IEEE80211_SMPS_STATIC:
-                       cap |= WLAN_HT_CAP_SM_PS_STATIC <<
-                               IEEE80211_HT_CAP_SM_PS_SHIFT;
-                       break;
-               case IEEE80211_SMPS_DYNAMIC:
-                       cap |= WLAN_HT_CAP_SM_PS_DYNAMIC <<
-                               IEEE80211_HT_CAP_SM_PS_SHIFT;
-                       break;
-               }
-
-               /* reserve and fill IE */
-
-               pos = skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2);
-               *pos++ = WLAN_EID_HT_CAPABILITY;
-               *pos++ = sizeof(struct ieee80211_ht_cap);
-               memset(pos, 0, sizeof(struct ieee80211_ht_cap));
-
-               /* capability flags */
-               tmp = cpu_to_le16(cap);
-               memcpy(pos, &tmp, sizeof(u16));
-               pos += sizeof(u16);
-
-               /* AMPDU parameters */
-               *pos++ = sband->ht_cap.ampdu_factor |
-                        (sband->ht_cap.ampdu_density <<
-                               IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT);
-
-               /* MCS set */
-               memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs));
-               pos += sizeof(sband->ht_cap.mcs);
-
-               /* extended capabilities */
-               pos += sizeof(__le16);
-
-               /* BF capabilities */
-               pos += sizeof(__le32);
-
-               /* antenna selection */
-               pos += sizeof(u8);
-       }
-
        IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
        ieee80211_tx_skb(sdata, skb);
 }