Patchwork [v3] hostap: Support ht-cap over-rides.

login
register
mail settings
Submitter Ben Greear
Date Nov. 22, 2011, 8:37 p.m.
Message ID <1321994260-20421-1-git-send-email-greearb@candelatech.com>
Download mbox | patch
Permalink /patch/127163/
State Superseded
Headers show

Comments

Ben Greear - Nov. 22, 2011, 8:37 p.m.
From: Ben Greear <greearb@candelatech.com>

This allows ht-capabilities over-rides on kernels that
support these features.

MCS Rates can be disabled (ie, to force to slower
speeds when using /n).  Rates cannot be forced
higher.

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

HT40 can be disabled.

MAX-AMSDU can be disabled
AMPDU-Factor and AMPDU Density can be modified.

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

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

v3:  Sync'd with upstream netlink attributes
     Moved configurables into the network {} sections.
     Compile out the code by default.

Still needs some additional documentation, but I want to make sure
the code is at least mostly right before doing that.

:100644 100644 3014762... 860396f... M	src/common/ieee802_11_defs.h
:100644 100644 ce25d03... 78fedea... M	src/drivers/driver.h
:100644 100644 7943595... 9b8df69... M	src/drivers/driver_nl80211.c
:100644 100644 f9261c2... a8028dd... M	src/drivers/nl80211_copy.h
:100644 100644 5ec7207... 7dda785... M	wpa_supplicant/Makefile
:100644 100644 b446a3f... 35f446d... M	wpa_supplicant/config.c
:100644 100644 ae496ff... 899aecb... M	wpa_supplicant/config.h
:100644 100644 8419f43... 2f4193f... M	wpa_supplicant/config_ssid.h
:100644 100644 03a4223... 862f645... M	wpa_supplicant/defconfig
:100644 100644 3416d68... 9e4044f... M	wpa_supplicant/sme.c
:100644 100644 2b3140e... ceee620... M	wpa_supplicant/wpa_supplicant.c
:100644 100644 59fc9a3... 6d4643d... M	wpa_supplicant/wpa_supplicant_i.h
 src/common/ieee802_11_defs.h      |    1 +
 src/drivers/driver.h              |   16 ++++
 src/drivers/driver_nl80211.c      |   20 +++++
 src/drivers/nl80211_copy.h        |    5 +
 wpa_supplicant/Makefile           |    4 +
 wpa_supplicant/config.c           |   19 +++++
 wpa_supplicant/config.h           |    5 +
 wpa_supplicant/config_ssid.h      |   49 +++++++++++
 wpa_supplicant/defconfig          |    3 +
 wpa_supplicant/sme.c              |    1 +
 wpa_supplicant/wpa_supplicant.c   |  159 +++++++++++++++++++++++++++++++++++++
 wpa_supplicant/wpa_supplicant_i.h |   27 ++++++
 12 files changed, 309 insertions(+), 0 deletions(-)
Jouni Malinen - Dec. 18, 2011, 6:49 p.m.
On Tue, Nov 22, 2011 at 12:37:40PM -0800, greearb@candelatech.com wrote:
> This allows ht-capabilities over-rides on kernels that
> support these features.

> diff --git a/src/drivers/driver.h b/src/drivers/driver.h
> @@ -26,6 +26,7 @@
> +#include "common/ieee802_11_defs.h"

I would prefer to not do this. driver.h tries to live mostly on its own
without pulling in many of the header files. Some driver wrappers live
outside the main hostap.git repository and the less dependencies there
are between them and some header files I consider more internal to the
core hostapd/wpa_supplicant, the better in general.

> @@ -516,6 +517,21 @@ struct wpa_driver_associate_params {
> +#ifdef CONFIG_HT_OVERRIDES
> +	struct ieee80211_ht_capabilities htcaps;
> +	struct ieee80211_ht_capabilities htcaps_mask;
> +#endif

i.e., these could be just const u8 *htcaps, *htcaps_mask and I would
drop the #ifdef from this case to keep driver.h API more independent of
the build configuration.


> diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
> index 7943595..9b8df69 100644
> --- a/src/drivers/driver_nl80211.c
> +++ b/src/drivers/driver_nl80211.c
> @@ -5617,6 +5617,16 @@ skip_auth_type:
> +#ifdef CONFIG_HT_OVERRIDES
> +	if (params->disable_ht)
> +		NLA_PUT_FLAG(msg, NL80211_ATTR_DISABLE_HT);
> +
> +	NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY, sizeof(params->htcaps),
> +		&params->htcaps);
> +	NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY_MASK, sizeof(params->htcaps_mask),
> +		&params->htcaps_mask);
> +#endif

I would drop the ifdef once params->htcaps != NULL can be used to see
whether the value is set. Obviously, same for
wpa_driver_nl80211_associate().


> diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h

No need to include nl80211_copy.h changes in submissions. I'll sync this
directly with wireless-testing.git anyway.

> diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
> index b446a3f..35f446d 100644
> --- a/wpa_supplicant/config.c
> +++ b/wpa_supplicant/config.c
> @@ -1864,6 +1875,14 @@ void wpa_config_set_network_defaults(struct wpa_ssid *ssid)
> +#ifdef CONFIG_HT_OVERRIDES
> +	ssid->ht_mcs = strdup("");

Why? Wouldn't NULL work here?

> diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
> index 2b3140e..ceee620 100644
> --- a/wpa_supplicant/wpa_supplicant.c
> +++ b/wpa_supplicant/wpa_supplicant.c
> @@ -2165,6 +2167,163 @@ static struct wpa_supplicant * wpa_supplicant_alloc(void)
> +#ifdef CONFIG_HT_OVERRIDES
> +int wpa_set_htcap_mcs(struct wpa_supplicant *wpa_s,
> +int wpa_disable_max_amsdu(struct wpa_supplicant *wpa_s,
> +int wpa_set_ampdu_factor(struct wpa_supplicant *wpa_s,
> +int wpa_set_ampdu_density(struct wpa_supplicant *wpa_s,
> +int wpa_set_disable_ht40(struct wpa_supplicant *wpa_s,

Shouldn't these be static? Only the following
wpa_supplicant_apply_ht_overrides() calls them.

> diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
> @@ -36,6 +36,8 @@ struct scan_info;
> +struct ieee80211_ht_capabilities;

Should be able to drop that with these functions becoming static and
going away from this header file:

> @@ -514,6 +516,31 @@ struct wpa_supplicant {
> +#ifdef CONFIG_HT_OVERRIDES
> +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);
> +int wpa_disable_max_amsdu(struct wpa_supplicant *wpa_s,
> +			  struct ieee80211_ht_capabilities *htcaps,
> +			  struct ieee80211_ht_capabilities *htcaps_mask,
> +			  int disabled);
> +int wpa_set_ampdu_factor(struct wpa_supplicant *wpa_s,
> +			 struct ieee80211_ht_capabilities *htcaps,
> +			 struct ieee80211_ht_capabilities *htcaps_mask,
> +			 int factor);
> +int wpa_set_ampdu_density(struct wpa_supplicant *wpa_s,
> +			  struct ieee80211_ht_capabilities *htcaps,
> +			  struct ieee80211_ht_capabilities *htcaps_mask,
> +			  int density);
> +int wpa_set_disable_ht40(struct wpa_supplicant *wpa_s,
> +			 struct ieee80211_ht_capabilities *htcaps,
> +			 struct ieee80211_ht_capabilities *htcaps_mask,
> +			 int disabled);
> +#endif
Ben Greear - Dec. 19, 2011, 2:43 a.m.
On 12/18/2011 10:49 AM, Jouni Malinen wrote:
> On Tue, Nov 22, 2011 at 12:37:40PM -0800, greearb@candelatech.com wrote:
>> This allows ht-capabilities over-rides on kernels that
>> support these features.
>
>> diff --git a/src/drivers/driver.h b/src/drivers/driver.h
>> @@ -26,6 +26,7 @@
>> +#include "common/ieee802_11_defs.h"
>
> I would prefer to not do this. driver.h tries to live mostly on its own
> without pulling in many of the header files. Some driver wrappers live
> outside the main hostap.git repository and the less dependencies there
> are between them and some header files I consider more internal to the
> core hostapd/wpa_supplicant, the better in general.
>
>> @@ -516,6 +517,21 @@ struct wpa_driver_associate_params {
>> +#ifdef CONFIG_HT_OVERRIDES
>> +	struct ieee80211_ht_capabilities htcaps;
>> +	struct ieee80211_ht_capabilities htcaps_mask;
>> +#endif
>
> i.e., these could be just const u8 *htcaps, *htcaps_mask and I would
> drop the #ifdef from this case to keep driver.h API more independent of
> the build configuration.

That just adds more void* pointers effectively, and that just
makes it easier to introduce bugs that the compiler could otherwise
easily catch.

I added all the ifdef stuff to make the code smaller per your previous
comments.  Making them pointers instead of members adds some extra coding
overhead to deal with properly freeing memory, but if you really want
them to be pointers, I will make the changes.

>> diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
>> index 7943595..9b8df69 100644
>> --- a/src/drivers/driver_nl80211.c
>> +++ b/src/drivers/driver_nl80211.c
>> @@ -5617,6 +5617,16 @@ skip_auth_type:
>> +#ifdef CONFIG_HT_OVERRIDES
>> +	if (params->disable_ht)
>> +		NLA_PUT_FLAG(msg, NL80211_ATTR_DISABLE_HT);
>> +
>> +	NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY, sizeof(params->htcaps),
>> +		&params->htcaps);
>> +	NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY_MASK, sizeof(params->htcaps_mask),
>> +		&params->htcaps_mask);
>> +#endif
>
> I would drop the ifdef once params->htcaps != NULL can be used to see
> whether the value is set. Obviously, same for
> wpa_driver_nl80211_associate().


>> diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
>
> No need to include nl80211_copy.h changes in submissions. I'll sync this
> directly with wireless-testing.git anyway.
>
>> diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
>> index b446a3f..35f446d 100644
>> --- a/wpa_supplicant/config.c
>> +++ b/wpa_supplicant/config.c
>> @@ -1864,6 +1875,14 @@ void wpa_config_set_network_defaults(struct wpa_ssid *ssid)
>> +#ifdef CONFIG_HT_OVERRIDES
>> +	ssid->ht_mcs = strdup("");
>
> Why? Wouldn't NULL work here?

Probably..but makes the parsing slightly harder if you have to check
for null as well as empty string.  I can change it to null if you want.

I won't be able to work on this until first of January, but will
post a new patch then that attempts to address your comments.

Thanks,
Ben
Jouni Malinen - Dec. 19, 2011, 9:49 a.m.
On Sun, Dec 18, 2011 at 06:43:00PM -0800, Ben Greear wrote:
> On 12/18/2011 10:49 AM, Jouni Malinen wrote:
> > On Tue, Nov 22, 2011 at 12:37:40PM -0800, greearb@candelatech.com wrote:
> >> @@ -516,6 +517,21 @@ struct wpa_driver_associate_params {
> >> +#ifdef CONFIG_HT_OVERRIDES
> >> +	struct ieee80211_ht_capabilities htcaps;
> >> +	struct ieee80211_ht_capabilities htcaps_mask;
> >> +#endif
> >
> > i.e., these could be just const u8 *htcaps, *htcaps_mask and I would
> > drop the #ifdef from this case to keep driver.h API more independent of
> > the build configuration.
> 
> That just adds more void* pointers effectively, and that just
> makes it easier to introduce bugs that the compiler could otherwise
> easily catch.

Not sure I would really agree in this case. These are just payload of an
information element that gets copied in driver_nl80211.c to the driver
as-is.

> I added all the ifdef stuff to make the code smaller per your previous
> comments.  Making them pointers instead of members adds some extra coding
> overhead to deal with properly freeing memory, but if you really want
> them to be pointers, I will make the changes.

I understand the ifdefs that actually change code size - this particular
one does not (it changes the size of a short lived memory area). It is
not like these const u8 * values would use separately allocated memory
that would need explicit freeing (it would be a stack variable in
sme_associate() and wpa_supplicant_associate()).

> >> diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
> >> @@ -1864,6 +1875,14 @@ void wpa_config_set_network_defaults(struct wpa_ssid *ssid)
> >> +#ifdef CONFIG_HT_OVERRIDES
> >> +	ssid->ht_mcs = strdup("");
> >
> > Why? Wouldn't NULL work here?
> 
> Probably..but makes the parsing slightly harder if you have to check
> for null as well as empty string.  I can change it to null if you want.

Why would you need to check for an empty string? It just sounds odd to
allocate an empty buffer here (and by the way, that strdup() can return
NULL anyway)..

> I won't be able to work on this until first of January, but will
> post a new patch then that attempts to address your comments.

OK.

Patch

diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index 3014762..860396f 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -519,6 +519,7 @@  struct ieee80211_mgmt {
 } STRUCT_PACKED;
 
 
+#define IEEE80211_HT_MCS_MASK_LEN               10
 struct ieee80211_ht_capabilities {
 	le16 ht_capabilities_info;
 	u8 a_mpdu_params;
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index ce25d03..78fedea 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -26,6 +26,7 @@ 
 #define WPA_SUPPLICANT_DRIVER_VERSION 4
 
 #include "common/defs.h"
+#include "common/ieee802_11_defs.h"
 
 #define HOSTAPD_CHAN_DISABLED 0x00000001
 #define HOSTAPD_CHAN_PASSIVE_SCAN 0x00000002
@@ -516,6 +517,21 @@  struct wpa_driver_associate_params {
 	 * STA mode: bits 0..3 UAPSD enabled for VO,VI,BK,BE
 	 */
 	int uapsd;
+
+#ifdef CONFIG_HT_OVERRIDES
+	/**
+	 * disable_ht - Disable HT (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.
+	 */
+	struct ieee80211_ht_capabilities htcaps;
+	struct ieee80211_ht_capabilities htcaps_mask;
+#endif
 };
 
 enum hide_ssid {
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 7943595..9b8df69 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -5617,6 +5617,16 @@  skip_auth_type:
 		NLA_PUT_U32(msg, NL80211_ATTR_AKM_SUITES, mgmt);
 	}
 
+#ifdef CONFIG_HT_OVERRIDES
+	if (params->disable_ht)
+		NLA_PUT_FLAG(msg, NL80211_ATTR_DISABLE_HT);
+
+	NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY, sizeof(params->htcaps),
+		&params->htcaps);
+	NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY_MASK, sizeof(params->htcaps_mask),
+		&params->htcaps_mask);
+#endif
+
 	ret = nl80211_set_conn_keys(params, msg);
 	if (ret)
 		goto nla_put_failure;
@@ -5764,6 +5774,16 @@  static int wpa_driver_nl80211_associate(
 			params->prev_bssid);
 	}
 
+#ifdef CONFIG_HT_OVERRIDES
+	if (params->disable_ht)
+		NLA_PUT_FLAG(msg, NL80211_ATTR_DISABLE_HT);
+
+	NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY, sizeof(params->htcaps),
+		&params->htcaps);
+	NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY_MASK, sizeof(params->htcaps_mask),
+		&params->htcaps_mask);
+#endif
+
 	if (params->p2p)
 		wpa_printf(MSG_DEBUG, "  * P2P group");
 
diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
index f9261c2..a8028dd 100644
--- a/src/drivers/nl80211_copy.h
+++ b/src/drivers/nl80211_copy.h
@@ -1408,6 +1408,11 @@  enum nl80211_attrs {
 
 	NL80211_ATTR_PROBE_RESP,
 
+	NL80211_ATTR_DFS_REGION,
+
+	NL80211_ATTR_DISABLE_HT,
+	NL80211_ATTR_HT_CAPABILITY_MASK,
+
 	/* add attributes here, update the policy in nl80211.c */
 
 	__NL80211_ATTR_AFTER_LAST,
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index 5ec7207..7dda785 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -111,6 +111,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
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index b446a3f..35f446d 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -1511,6 +1511,14 @@  static const struct parse_data ssid_fields[] = {
 	{ INT_RANGE(frequency, 0, 10000) },
 	{ INT(wpa_ptk_rekey) },
 	{ STR(bgscan) },
+#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
 };
 
 #undef OFFSET
@@ -1677,6 +1685,9 @@  void wpa_config_free_ssid(struct wpa_ssid *ssid)
 	os_free(ssid->scan_freq);
 	os_free(ssid->freq_list);
 	os_free(ssid->bgscan);
+#ifdef CONFIG_HT_OVERRIDES
+	os_free(ssid->ht_mcs);
+#endif
 	os_free(ssid);
 }
 
@@ -1864,6 +1875,14 @@  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;
+	ssid->ht_mcs = strdup("");
+#endif
 }
 
 
diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h
index ae496ff..899aecb 100644
--- a/wpa_supplicant/config.h
+++ b/wpa_supplicant/config.h
@@ -15,6 +15,11 @@ 
 #ifndef CONFIG_H
 #define CONFIG_H
 
+#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 */
 #define DEFAULT_EAPOL_VERSION 1
 #ifdef CONFIG_NO_SCAN_PROCESSING
 #define DEFAULT_AP_SCAN 2
diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h
index 8419f43..2f4193f 100644
--- a/wpa_supplicant/config_ssid.h
+++ b/wpa_supplicant/config_ssid.h
@@ -408,6 +408,55 @@  struct wpa_ssid {
 	 * WPS or similar so that they may be exported.
 	 */
 	int export_keys;
+
+
+#ifdef CONFIG_HT_OVERRIDES
+	/**
+	 * disable_ht - Disable HT (802.11n) for this interface
+	 *
+	 * 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 HT-40 for this interface
+	 *
+	 * 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 AMSDU
+	 *
+	 * AMDSU 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 section 7.3.2.56.3 of the 802.11n-2009 spec.
+	 */
+	int ampdu_factor;
+
+	/**
+	 * ampdu_density - Minum A-MPDU Start Spacing
+	 *
+	 * Value: 0-7, see section 7.3.2.56.3 of the 802.11n-2009 spec.
+	 */
+	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
 };
 
 #endif /* CONFIG_SSID_H */
diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig
index 03a4223..862f645 100644
--- a/wpa_supplicant/defconfig
+++ b/wpa_supplicant/defconfig
@@ -217,6 +217,9 @@  CONFIG_SMARTCARD=y
 # Enable this if EAP-SIM or EAP-AKA is included
 #CONFIG_PCSC=y
 
+# Support HT overrides (disable-ht40, mcs rates, etc)
+# CONFIG_HT_OVERRIDES=y
+
 # Development testing
 #CONFIG_EAPOL_TEST=y
 
diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
index 3416d68..9e4044f 100644
--- a/wpa_supplicant/sme.c
+++ b/wpa_supplicant/sme.c
@@ -386,6 +386,7 @@  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);
+	wpa_supplicant_apply_ht_overrides(wpa_s, &params);
 #ifdef CONFIG_IEEE80211R
 	if (auth_type == WLAN_AUTH_FT && wpa_s->sme.ft_ies) {
 		params.wpa_ie = wpa_s->sme.ft_ies;
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index 2b3140e..ceee620 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -1425,6 +1425,8 @@  void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
 	else
 		params.uapsd = -1;
 
+	wpa_supplicant_apply_ht_overrides(wpa_s, &params);
+
 	ret = wpa_drv_associate(wpa_s, &params);
 	if (ret < 0) {
 		wpa_msg(wpa_s, MSG_INFO, "Association request to the driver "
@@ -2165,6 +2167,163 @@  static struct wpa_supplicant * wpa_supplicant_alloc(void)
 	return wpa_s;
 }
 
+#ifdef CONFIG_HT_OVERRIDES
+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;
+
+	/* 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_ERROR, "set_htcap, ht_mcs -:%s:-\n", 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_ERROR, "htcap value[%i]: %ld end: %p  tmp: %p\n",
+				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 (i) {
+		os_memset(&htcaps_mask->supported_mcs_set, 0xff, IEEE80211_HT_MCS_MASK_LEN-1);
+		htcaps_mask->supported_mcs_set[IEEE80211_HT_MCS_MASK_LEN-1] = 0x1f; /* skip the 3 reserved bits */
+	}
+
+	return 0;
+}
+
+
+int wpa_disable_max_amsdu(struct wpa_supplicant *wpa_s,
+			  struct ieee80211_ht_capabilities *htcaps,
+			  struct ieee80211_ht_capabilities *htcaps_mask,
+			  int disabled)
+{
+	wpa_msg(wpa_s, MSG_DEBUG, "wpa: set_disable_max_amsdu: %d\n",
+		disabled);
+
+	if (disabled == -1)
+		return 0;
+
+	u16 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;
+}
+
+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, "wpa: set_ampdu_factor: %d\n",
+		factor);
+
+	if (factor == -1)
+		return 0;
+
+	if (factor < 0 || factor > 3) {
+		wpa_msg(wpa_s, MSG_ERROR,
+			"wpa: ampdu_factor: %d out of range.  Must be 0-3 or -1\n",
+			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;
+}
+
+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, "wpa: set_ampdu_density: %d\n",
+		density);
+
+	if (density == -1)
+		return 0;
+
+	if (density < 0 || density > 7) {
+		wpa_msg(wpa_s, MSG_ERROR,
+			"wpa: ampdu_density: %d out of range.  Must be 0-7 or -1.\n",
+			density);
+		return -EINVAL;
+	}
+
+	htcaps_mask->a_mpdu_params |= 0x1C;
+	htcaps->a_mpdu_params &= ~(0x1C);
+	htcaps->a_mpdu_params |= (density << 2) & 0x1C;
+	return 0;
+}
+
+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 HT-40 */
+	u16 msk = host_to_le16(HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET |
+			       HT_CAP_INFO_SHORT_GI40MHZ);
+
+	wpa_msg(wpa_s, MSG_DEBUG, "wpa: set_disable_ht40: %d\n",
+		disabled);
+
+	if (disabled)
+		htcaps->ht_capabilities_info &= ~msk;
+	else
+		htcaps->ht_capabilities_info |= msk;
+
+	htcaps_mask->ht_capabilities_info |= msk;
+	return 0;
+}
+#endif
+
+
+void wpa_supplicant_apply_ht_overrides(struct wpa_supplicant *wpa_s,
+				       struct wpa_driver_associate_params *params)
+{
+#ifdef CONFIG_HT_OVERRIDES
+	struct wpa_ssid *ssid = wpa_s->current_ssid;
+	if (ssid) {
+		params->disable_ht = ssid->disable_ht;
+
+		wpa_set_htcap_mcs(wpa_s, &params->htcaps, &params->htcaps_mask, ssid->ht_mcs);
+		wpa_disable_max_amsdu(wpa_s, &params->htcaps, &params->htcaps_mask,
+				      ssid->disable_max_amsdu);
+		wpa_set_ampdu_factor(wpa_s, &params->htcaps, &params->htcaps_mask,
+				     ssid->ampdu_factor);
+		wpa_set_ampdu_density(wpa_s, &params->htcaps, &params->htcaps_mask,
+				      ssid->ampdu_density);
+		wpa_set_disable_ht40(wpa_s, &params->htcaps, &params->htcaps_mask,
+				     ssid->disable_ht40);
+	}
+#endif
+}
 
 static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s,
 				     struct wpa_interface *iface)
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index 59fc9a3..6d4643d 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -36,6 +36,8 @@  struct scan_info;
 struct wpa_bss;
 struct wpa_scan_results;
 struct hostapd_hw_modes;
+struct ieee80211_ht_capabilities;
+struct wpa_driver_associate_params;
 
 /*
  * Forward declarations of private structures used within the ctrl_iface
@@ -514,6 +516,31 @@  struct wpa_supplicant {
 
 
 /* wpa_supplicant.c */
+#ifdef CONFIG_HT_OVERRIDES
+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);
+int wpa_disable_max_amsdu(struct wpa_supplicant *wpa_s,
+			  struct ieee80211_ht_capabilities *htcaps,
+			  struct ieee80211_ht_capabilities *htcaps_mask,
+			  int disabled);
+int wpa_set_ampdu_factor(struct wpa_supplicant *wpa_s,
+			 struct ieee80211_ht_capabilities *htcaps,
+			 struct ieee80211_ht_capabilities *htcaps_mask,
+			 int factor);
+int wpa_set_ampdu_density(struct wpa_supplicant *wpa_s,
+			  struct ieee80211_ht_capabilities *htcaps,
+			  struct ieee80211_ht_capabilities *htcaps_mask,
+			  int density);
+int wpa_set_disable_ht40(struct wpa_supplicant *wpa_s,
+			 struct ieee80211_ht_capabilities *htcaps,
+			 struct ieee80211_ht_capabilities *htcaps_mask,
+			 int disabled);
+#endif
+void wpa_supplicant_apply_ht_overrides(struct wpa_supplicant *wpa_s,
+			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);