]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
Support HT capability overrides
authorBen Greear <greearb@candelatech.com>
Sun, 29 Jan 2012 19:01:31 +0000 (21:01 +0200)
committerJouni Malinen <j@w1.fi>
Sun, 29 Jan 2012 19:01:31 +0000 (21:01 +0200)
This allows HT capabilities overrides on kernels that
support these features.

MCS Rates can be disabled to force to slower speeds when using HT.
Rates cannot be forced higher.

HT can be disabled, forcing an 802.11a/b/g/n station to act like
an 802.11a/b/g station.

HT40 can be disabled.

MAX A-MSDU can be disabled.
A-MPDU Factor and A-MPDU Density can be modified.

Please note that these are suggestions to the kernel. Only mac80211
drivers will work at all. The A-MPDU Factor can only be decreased and
the A-MPDU Density can only be increased currently.

Signed-hostap: Ben Greear <greearb@candelatech.com>

src/common/ieee802_11_defs.h
src/drivers/driver.h
src/drivers/driver_nl80211.c
wpa_supplicant/Makefile
wpa_supplicant/config.c
wpa_supplicant/config_ssid.h
wpa_supplicant/defconfig
wpa_supplicant/sme.c
wpa_supplicant/wpa_supplicant.c
wpa_supplicant/wpa_supplicant_i.h

index 66801fd5dfe11b5ed0087737661e0909558e08e0..25bc5c6a446399ceb3de693f05ec6ccc4f63965b 100644 (file)
@@ -519,6 +519,9 @@ struct ieee80211_mgmt {
 } STRUCT_PACKED;
 
 
+/* Rx MCS bitmask is in the first 77 bits of supported_mcs_set */
+#define IEEE80211_HT_MCS_MASK_LEN 10
+
 struct ieee80211_ht_capabilities {
        le16 ht_capabilities_info;
        u8 a_mpdu_params;
index f56c41b91b3190c8bbde0ea085b3aacb8d055991..8bfb12b74f2be5359a838790d0da903f93ea3edf 100644 (file)
@@ -523,6 +523,19 @@ struct wpa_driver_associate_params {
         * 0 = Do not fix BSSID.
         */
        int fixed_bssid;
+
+       /**
+        * disable_ht - Disable HT (IEEE 802.11n) for this connection
+        */
+       int disable_ht;
+
+       /**
+        * HT Capabilities over-rides. Only bits set in the mask will be used,
+        * and not all values are used by the kernel anyway. Currently, MCS,
+        * MPDU and MSDU fields are used.
+        */
+       const u8 *htcaps;       /* struct ieee80211_ht_capabilities * */
+       const u8 *htcaps_mask;  /* struct ieee80211_ht_capabilities * */
 };
 
 enum hide_ssid {
index 0bf1dca8bc45158f624f6283ed0a0eb962d3b7e9..381be66565333c5de9cf5db322c145506ff265cb 100644 (file)
@@ -6648,6 +6648,16 @@ skip_auth_type:
                NLA_PUT_U32(msg, NL80211_ATTR_AKM_SUITES, mgmt);
        }
 
+       if (params->disable_ht)
+               NLA_PUT_FLAG(msg, NL80211_ATTR_DISABLE_HT);
+
+       if (params->htcaps && params->htcaps_mask) {
+               int sz = sizeof(struct ieee80211_ht_capabilities);
+               NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY, sz, params->htcaps);
+               NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY_MASK, sz,
+                       params->htcaps_mask);
+       }
+
        ret = nl80211_set_conn_keys(params, msg);
        if (ret)
                goto nla_put_failure;
@@ -6795,6 +6805,16 @@ static int wpa_driver_nl80211_associate(
                        params->prev_bssid);
        }
 
+       if (params->disable_ht)
+               NLA_PUT_FLAG(msg, NL80211_ATTR_DISABLE_HT);
+
+       if (params->htcaps && params->htcaps_mask) {
+               int sz = sizeof(struct ieee80211_ht_capabilities);
+               NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY, sz, params->htcaps);
+               NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY_MASK, sz,
+                       params->htcaps_mask);
+       }
+
        if (params->p2p)
                wpa_printf(MSG_DEBUG, "  * P2P group");
 
index fd1c5d3b8d3886f5dccb9033e85de703c7bdf604..5eef855600d7a146623a10a672280812c7c090dc 100644 (file)
@@ -113,6 +113,10 @@ ifdef CONFIG_EAPOL_TEST
 CFLAGS += -Werror -DEAPOL_TEST
 endif
 
+ifdef CONFIG_HT_OVERRIDES
+CFLAGS += -DCONFIG_HT_OVERRIDES
+endif
+
 ifndef CONFIG_BACKEND
 CONFIG_BACKEND=file
 endif
index 0fd1f3e0d6f0dcb4e5267e44a7bf18f94a93a219..89fba408a7b0c2667a8e2ad67c713380646d2df3 100644 (file)
@@ -1598,6 +1598,14 @@ static const struct parse_data ssid_fields[] = {
 #ifdef CONFIG_P2P
        { FUNC(p2p_client_list) },
 #endif /* CONFIG_P2P */
+#ifdef CONFIG_HT_OVERRIDES
+       { INT_RANGE(disable_ht, 0, 1) },
+       { INT_RANGE(disable_ht40, -1, 1) },
+       { INT_RANGE(disable_max_amsdu, -1, 1) },
+       { INT_RANGE(ampdu_factor, -1, 3) },
+       { INT_RANGE(ampdu_density, -1, 7) },
+       { STR(ht_mcs) },
+#endif /* CONFIG_HT_OVERRIDES */
 };
 
 #undef OFFSET
@@ -1765,6 +1773,9 @@ void wpa_config_free_ssid(struct wpa_ssid *ssid)
        os_free(ssid->freq_list);
        os_free(ssid->bgscan);
        os_free(ssid->p2p_client_list);
+#ifdef CONFIG_HT_OVERRIDES
+       os_free(ssid->ht_mcs);
+#endif /* CONFIG_HT_OVERRIDES */
        os_free(ssid);
 }
 
@@ -1952,6 +1963,13 @@ void wpa_config_set_network_defaults(struct wpa_ssid *ssid)
        ssid->eap_workaround = DEFAULT_EAP_WORKAROUND;
        ssid->eap.fragment_size = DEFAULT_FRAGMENT_SIZE;
 #endif /* IEEE8021X_EAPOL */
+#ifdef CONFIG_HT_OVERRIDES
+       ssid->disable_ht = DEFAULT_DISABLE_HT;
+       ssid->disable_ht40 = DEFAULT_DISABLE_HT40;
+       ssid->disable_max_amsdu = DEFAULT_DISABLE_MAX_AMSDU;
+       ssid->ampdu_factor = DEFAULT_AMPDU_FACTOR;
+       ssid->ampdu_density = DEFAULT_AMPDU_DENSITY;
+#endif /* CONFIG_HT_OVERRIDES */
 }
 
 
index 8a47c0b9d75a702403a8f9c6c9cbc6d59e315418..9fa7f8bdafa2ad3435d10eb7e0321fa651e1776a 100644 (file)
                       WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40)
 #define DEFAULT_FRAGMENT_SIZE 1398
 
+#define DEFAULT_DISABLE_HT 0
+#define DEFAULT_DISABLE_HT40 0
+#define DEFAULT_DISABLE_MAX_AMSDU -1 /* no change */
+#define DEFAULT_AMPDU_FACTOR -1 /* no change */
+#define DEFAULT_AMPDU_DENSITY -1 /* no change */
+
 /**
  * struct wpa_ssid - Network configuration data
  *
@@ -422,6 +428,54 @@ struct wpa_ssid {
         * WPS or similar so that they may be exported.
         */
        int export_keys;
+
+#ifdef CONFIG_HT_OVERRIDES
+       /**
+        * disable_ht - Disable HT (IEEE 802.11n) for this network
+        *
+        * By default, use it if it is available, but this can be configured
+        * to 1 to have it disabled.
+        */
+       int disable_ht;
+
+       /**
+        * disable_ht40 - Disable HT40 for this network
+        *
+        * By default, use it if it is available, but this can be configured
+        * to 1 to have it disabled.
+        */
+       int disable_ht40;
+
+       /**
+        * disable_max_amsdu - Disable MAX A-MSDU
+        *
+        * A-MDSU will be 3839 bytes when disabled, or 7935
+        * when enabled (assuming it is otherwise supported)
+        * -1 (default) means do not apply any settings to the kernel.
+        */
+       int disable_max_amsdu;
+
+       /**
+        * ampdu_factor - Maximum A-MPDU Length Exponent
+        *
+        * Value: 0-3, see 7.3.2.56.3 in IEEE Std 802.11n-2009.
+        */
+       int ampdu_factor;
+
+       /**
+        * ampdu_density - Minimum A-MPDU Start Spacing
+        *
+        * Value: 0-7, see 7.3.2.56.3 in IEEE Std 802.11n-2009.
+        */
+       int ampdu_density;
+
+       /**
+        * ht_mcs - Allowed HT-MCS rates, in ASCII hex: ffff0000...
+        *
+        * By default (empty string): Use whatever the OS has configured.
+        */
+       char *ht_mcs;
+#endif /* CONFIG_HT_OVERRIDES */
 };
 
 #endif /* CONFIG_SSID_H */
index cff25d6c83679b3e444cf684e727d67740b95cda..69a86252b4a313a58db041e55f503eea9861d202 100644 (file)
@@ -217,6 +217,9 @@ CONFIG_SMARTCARD=y
 # Enable this if EAP-SIM or EAP-AKA is included
 #CONFIG_PCSC=y
 
+# Support HT overrides (disable HT/HT40, mask MCS rates, etc.)
+#CONFIG_HT_OVERRIDES=y
+
 # Development testing
 #CONFIG_EAPOL_TEST=y
 
index fc283477b715351c2589eb1d04d76cc05f1e45be..c07b7a6316fdfebe38cf630cc4922f42ef7a7436 100644 (file)
@@ -367,6 +367,10 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode,
 {
        struct wpa_driver_associate_params params;
        struct ieee802_11_elems elems;
+#ifdef CONFIG_HT_OVERRIDES
+       struct ieee80211_ht_capabilities htcaps;
+       struct ieee80211_ht_capabilities htcaps_mask;
+#endif /* CONFIG_HT_OVERRIDES */
 
        os_memset(&params, 0, sizeof(params));
        params.bssid = bssid;
@@ -378,6 +382,13 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode,
        params.wpa_ie_len = wpa_s->sme.assoc_req_ie_len;
        params.pairwise_suite = cipher_suite2driver(wpa_s->pairwise_cipher);
        params.group_suite = cipher_suite2driver(wpa_s->group_cipher);
+#ifdef CONFIG_HT_OVERRIDES
+       os_memset(&htcaps, 0, sizeof(htcaps));
+       os_memset(&htcaps_mask, 0, sizeof(htcaps_mask));
+       params.htcaps = (u8 *) &htcaps;
+       params.htcaps_mask = (u8 *) &htcaps_mask;
+       wpa_supplicant_apply_ht_overrides(wpa_s, wpa_s->current_ssid, &params);
+#endif /* CONFIG_HT_OVERRIDES */
 #ifdef CONFIG_IEEE80211R
        if (auth_type == WLAN_AUTH_FT && wpa_s->sme.ft_ies) {
                params.wpa_ie = wpa_s->sme.ft_ies;
index 304ae098aaff5fbaed02ef06a7288a5fdb501f6c..4d3e30735329fc069f9e7ccd3f0584b0796f9d60 100644 (file)
@@ -1110,6 +1110,10 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
        struct wpa_driver_capa capa;
        int assoc_failed = 0;
        struct wpa_ssid *old_ssid;
+#ifdef CONFIG_HT_OVERRIDES
+       struct ieee80211_ht_capabilities htcaps;
+       struct ieee80211_ht_capabilities htcaps_mask;
+#endif /* CONFIG_HT_OVERRIDES */
 
 #ifdef CONFIG_IBSS_RSN
        ibss_rsn_deinit(wpa_s->ibss_rsn);
@@ -1411,6 +1415,14 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
        else
                params.uapsd = -1;
 
+#ifdef CONFIG_HT_OVERRIDES
+       os_memset(&htcaps, 0, sizeof(htcaps));
+       os_memset(&htcaps_mask, 0, sizeof(htcaps_mask));
+       params.htcaps = (u8 *) &htcaps;
+       params.htcaps_mask = (u8 *) &htcaps_mask;
+       wpa_supplicant_apply_ht_overrides(wpa_s, ssid, &params);
+#endif /* CONFIG_HT_OVERRIDES */
+
        ret = wpa_drv_associate(wpa_s, &params);
        if (ret < 0) {
                wpa_msg(wpa_s, MSG_INFO, "Association request to the driver "
@@ -2165,6 +2177,183 @@ static struct wpa_supplicant * wpa_supplicant_alloc(void)
 }
 
 
+#ifdef CONFIG_HT_OVERRIDES
+
+static int wpa_set_htcap_mcs(struct wpa_supplicant *wpa_s,
+                            struct ieee80211_ht_capabilities *htcaps,
+                            struct ieee80211_ht_capabilities *htcaps_mask,
+                            const char *ht_mcs)
+{
+       /* parse ht_mcs into hex array */
+       int i;
+       const char *tmp = ht_mcs;
+       char *end = NULL;
+
+       /* If ht_mcs is null, do not set anything */
+       if (!ht_mcs)
+               return 0;
+
+       /* This is what we are setting in the kernel */
+       os_memset(&htcaps->supported_mcs_set, 0, IEEE80211_HT_MCS_MASK_LEN);
+
+       wpa_msg(wpa_s, MSG_DEBUG, "set_htcap, ht_mcs -:%s:-", ht_mcs);
+
+       for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) {
+               errno = 0;
+               long v = strtol(tmp, &end, 16);
+               if (errno == 0) {
+                       wpa_msg(wpa_s, MSG_DEBUG,
+                               "htcap value[%i]: %ld end: %p  tmp: %p",
+                               i, v, end, tmp);
+                       if (end == tmp)
+                               break;
+
+                       htcaps->supported_mcs_set[i] = v;
+                       tmp = end;
+               } else {
+                       wpa_msg(wpa_s, MSG_ERROR,
+                               "Failed to parse ht-mcs: %s, error: %s\n",
+                               ht_mcs, strerror(errno));
+                       return -1;
+               }
+       }
+
+       /*
+        * If we were able to parse any values, then set mask for the MCS set.
+        */
+       if (i) {
+               os_memset(&htcaps_mask->supported_mcs_set, 0xff,
+                         IEEE80211_HT_MCS_MASK_LEN - 1);
+               /* skip the 3 reserved bits */
+               htcaps_mask->supported_mcs_set[IEEE80211_HT_MCS_MASK_LEN - 1] =
+                       0x1f;
+       }
+
+       return 0;
+}
+
+
+static int wpa_disable_max_amsdu(struct wpa_supplicant *wpa_s,
+                                struct ieee80211_ht_capabilities *htcaps,
+                                struct ieee80211_ht_capabilities *htcaps_mask,
+                                int disabled)
+{
+       u16 msk;
+
+       wpa_msg(wpa_s, MSG_DEBUG, "set_disable_max_amsdu: %d", disabled);
+
+       if (disabled == -1)
+               return 0;
+
+       msk = host_to_le16(HT_CAP_INFO_MAX_AMSDU_SIZE);
+       htcaps_mask->ht_capabilities_info |= msk;
+       if (disabled)
+               htcaps->ht_capabilities_info &= msk;
+       else
+               htcaps->ht_capabilities_info |= msk;
+
+       return 0;
+}
+
+
+static int wpa_set_ampdu_factor(struct wpa_supplicant *wpa_s,
+                               struct ieee80211_ht_capabilities *htcaps,
+                               struct ieee80211_ht_capabilities *htcaps_mask,
+                               int factor)
+{
+       wpa_msg(wpa_s, MSG_DEBUG, "set_ampdu_factor: %d", factor);
+
+       if (factor == -1)
+               return 0;
+
+       if (factor < 0 || factor > 3) {
+               wpa_msg(wpa_s, MSG_ERROR, "ampdu_factor: %d out of range. "
+                       "Must be 0-3 or -1", factor);
+               return -EINVAL;
+       }
+
+       htcaps_mask->a_mpdu_params |= 0x3; /* 2 bits for factor */
+       htcaps->a_mpdu_params &= ~0x3;
+       htcaps->a_mpdu_params |= factor & 0x3;
+
+       return 0;
+}
+
+
+static int wpa_set_ampdu_density(struct wpa_supplicant *wpa_s,
+                                struct ieee80211_ht_capabilities *htcaps,
+                                struct ieee80211_ht_capabilities *htcaps_mask,
+                                int density)
+{
+       wpa_msg(wpa_s, MSG_DEBUG, "set_ampdu_density: %d", density);
+
+       if (density == -1)
+               return 0;
+
+       if (density < 0 || density > 7) {
+               wpa_msg(wpa_s, MSG_ERROR,
+                       "ampdu_density: %d out of range. Must be 0-7 or -1.",
+                       density);
+               return -EINVAL;
+       }
+
+       htcaps_mask->a_mpdu_params |= 0x1C;
+       htcaps->a_mpdu_params &= ~(0x1C);
+       htcaps->a_mpdu_params |= (density << 2) & 0x1C;
+
+       return 0;
+}
+
+
+static int wpa_set_disable_ht40(struct wpa_supplicant *wpa_s,
+                               struct ieee80211_ht_capabilities *htcaps,
+                               struct ieee80211_ht_capabilities *htcaps_mask,
+                               int disabled)
+{
+       /* Masking these out disables HT40 */
+       u16 msk = host_to_le16(HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET |
+                              HT_CAP_INFO_SHORT_GI40MHZ);
+
+       wpa_msg(wpa_s, MSG_DEBUG, "set_disable_ht40: %d", disabled);
+
+       if (disabled)
+               htcaps->ht_capabilities_info &= ~msk;
+       else
+               htcaps->ht_capabilities_info |= msk;
+
+       htcaps_mask->ht_capabilities_info |= msk;
+
+       return 0;
+}
+
+
+void wpa_supplicant_apply_ht_overrides(
+       struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+       struct wpa_driver_associate_params *params)
+{
+       struct ieee80211_ht_capabilities *htcaps;
+       struct ieee80211_ht_capabilities *htcaps_mask;
+
+       if (!ssid)
+               return;
+
+       params->disable_ht = ssid->disable_ht;
+       if (!params->htcaps || !params->htcaps_mask)
+               return;
+
+       htcaps = (struct ieee80211_ht_capabilities *) params->htcaps;
+       htcaps_mask = (struct ieee80211_ht_capabilities *) params->htcaps_mask;
+       wpa_set_htcap_mcs(wpa_s, htcaps, htcaps_mask, ssid->ht_mcs);
+       wpa_disable_max_amsdu(wpa_s, htcaps, htcaps_mask,
+                             ssid->disable_max_amsdu);
+       wpa_set_ampdu_factor(wpa_s, htcaps, htcaps_mask, ssid->ampdu_factor);
+       wpa_set_ampdu_density(wpa_s, htcaps, htcaps_mask, ssid->ampdu_density);
+       wpa_set_disable_ht40(wpa_s, htcaps, htcaps_mask, ssid->disable_ht40);
+}
+
+#endif /* CONFIG_HT_OVERRIDES */
+
+
 static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s,
                                     struct wpa_interface *iface)
 {
index 91982e5819aa45ede94eb796f6cdc8b6cc76ac5e..b4d8b280a0bfb925ab96f9b0c38ff85c577ae1f7 100644 (file)
@@ -36,6 +36,7 @@ struct scan_info;
 struct wpa_bss;
 struct wpa_scan_results;
 struct hostapd_hw_modes;
+struct wpa_driver_associate_params;
 
 /*
  * Forward declarations of private structures used within the ctrl_iface
@@ -528,6 +529,10 @@ struct wpa_supplicant {
 
 
 /* wpa_supplicant.c */
+void wpa_supplicant_apply_ht_overrides(
+       struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+       struct wpa_driver_associate_params *params);
+
 int wpa_set_wep_keys(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
 
 int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s);