mac80211: adding 802.11n IEs handling
[safe/jmp/linux-2.6] / net / mac80211 / ieee80211_sta.c
index 231a663..1f47afe 100644 (file)
@@ -90,7 +90,8 @@ struct ieee802_11_elems {
        u8 *ext_supp_rates;
        u8 *wmm_info;
        u8 *wmm_param;
-
+       u8 *ht_cap_elem;
+       u8 *ht_info_elem;
        /* length of them, respectively */
        u8 ssid_len;
        u8 supp_rates_len;
@@ -106,6 +107,8 @@ struct ieee802_11_elems {
        u8 ext_supp_rates_len;
        u8 wmm_info_len;
        u8 wmm_param_len;
+       u8 ht_cap_elem_len;
+       u8 ht_info_elem_len;
 };
 
 static void ieee802_11_parse_elems(u8 *start, size_t len,
@@ -190,6 +193,14 @@ static void ieee802_11_parse_elems(u8 *start, size_t len,
                        elems->ext_supp_rates = pos;
                        elems->ext_supp_rates_len = elen;
                        break;
+               case WLAN_EID_HT_CAPABILITY:
+                       elems->ht_cap_elem = pos;
+                       elems->ht_cap_elem_len = elen;
+                       break;
+               case WLAN_EID_HT_EXTRA_INFO:
+                       elems->ht_info_elem = pos;
+                       elems->ht_info_elem_len = elen;
+                       break;
                default:
                        break;
                }
@@ -332,6 +343,51 @@ static void ieee80211_handle_erp_ie(struct net_device *dev, u8 erp_value)
                ieee80211_erp_info_change_notify(dev, changes);
 }
 
+int ieee80211_ht_cap_ie_to_ht_info(struct ieee80211_ht_cap *ht_cap_ie,
+                                  struct ieee80211_ht_info *ht_info)
+{
+
+       if (ht_info == NULL)
+               return -EINVAL;
+
+       memset(ht_info, 0, sizeof(*ht_info));
+
+       if (ht_cap_ie) {
+               u8 ampdu_info = ht_cap_ie->ampdu_params_info;
+
+               ht_info->ht_supported = 1;
+               ht_info->cap = le16_to_cpu(ht_cap_ie->cap_info);
+               ht_info->ampdu_factor =
+                       ampdu_info & IEEE80211_HT_CAP_AMPDU_FACTOR;
+               ht_info->ampdu_density =
+                       (ampdu_info & IEEE80211_HT_CAP_AMPDU_DENSITY) >> 2;
+               memcpy(ht_info->supp_mcs_set, ht_cap_ie->supp_mcs_set, 16);
+       } else
+               ht_info->ht_supported = 0;
+
+       return 0;
+}
+
+int ieee80211_ht_addt_info_ie_to_ht_bss_info(
+                       struct ieee80211_ht_addt_info *ht_add_info_ie,
+                       struct ieee80211_ht_bss_info *bss_info)
+{
+       if (bss_info == NULL)
+               return -EINVAL;
+
+       memset(bss_info, 0, sizeof(*bss_info));
+
+       if (ht_add_info_ie) {
+               u16 op_mode;
+               op_mode = le16_to_cpu(ht_add_info_ie->operation_mode);
+
+               bss_info->primary_channel = ht_add_info_ie->control_chan;
+               bss_info->bss_cap = ht_add_info_ie->ht_param;
+               bss_info->bss_op_mode = (u8)(op_mode & 0xff);
+       }
+
+       return 0;
+}
 
 static void ieee80211_sta_send_associnfo(struct net_device *dev,
                                         struct ieee80211_if_sta *ifsta)
@@ -630,6 +686,19 @@ static void ieee80211_send_assoc(struct net_device *dev,
                *pos++ = 1; /* WME ver */
                *pos++ = 0;
        }
+       /* wmm support is a must to HT */
+       if (wmm && mode->ht_info.ht_supported) {
+               __le16 tmp = cpu_to_le16(mode->ht_info.cap);
+               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));
+               memcpy(pos, &tmp, sizeof(u16));
+               pos += sizeof(u16);
+               *pos++ = (mode->ht_info.ampdu_factor |
+                               (mode->ht_info.ampdu_density << 2));
+               memcpy(pos, mode->ht_info.supp_mcs_set, 16);
+       }
 
        kfree(ifsta->assocreq_ies);
        ifsta->assocreq_ies_len = (skb->data + skb->len) - ies;
@@ -1380,6 +1449,7 @@ static void ieee80211_rx_bss_free(struct ieee80211_sta_bss *bss)
        kfree(bss->wpa_ie);
        kfree(bss->rsn_ie);
        kfree(bss->wmm_ie);
+       kfree(bss->ht_ie);
        kfree(bss);
 }
 
@@ -1637,7 +1707,22 @@ static void ieee80211_rx_bss_info(struct net_device *dev,
                bss->wmm_ie = NULL;
                bss->wmm_ie_len = 0;
        }
-
+       if (elems.ht_cap_elem &&
+           (!bss->ht_ie || bss->ht_ie_len != elems.ht_cap_elem_len ||
+            memcmp(bss->ht_ie, elems.ht_cap_elem, elems.ht_cap_elem_len))) {
+               kfree(bss->ht_ie);
+               bss->ht_ie = kmalloc(elems.ht_cap_elem_len + 2, GFP_ATOMIC);
+               if (bss->ht_ie) {
+                       memcpy(bss->ht_ie, elems.ht_cap_elem - 2,
+                              elems.ht_cap_elem_len + 2);
+                       bss->ht_ie_len = elems.ht_cap_elem_len + 2;
+               } else
+                       bss->ht_ie_len = 0;
+       } else if (!elems.ht_cap_elem && bss->ht_ie) {
+               kfree(bss->ht_ie);
+               bss->ht_ie = NULL;
+               bss->ht_ie_len = 0;
+       }
 
        bss->hw_mode = rx_status->phymode;
        bss->freq = rx_status->freq;