diff mbox

[v2] hostap: Support ht-cap over-rides.

Message ID 1321663789-7989-1-git-send-email-greearb@candelatech.com
State Superseded
Headers show

Commit Message

Ben Greear Nov. 19, 2011, 12:49 a.m. UTC
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>
---
:100644 100644 3014762... 860396f... M	src/common/ieee802_11_defs.h
:100644 100644 bd0c611... ca9c698... M	src/drivers/driver.h
:100644 100644 89775d7... 6d60030... M	src/drivers/driver_nl80211.c
:100644 100644 9d797f2... 688bd3f... M	src/drivers/nl80211_copy.h
:100644 100644 b446a3f... a457c1e... M	wpa_supplicant/config.c
:100644 100644 ae496ff... 7586209... M	wpa_supplicant/config.h
:100644 100644 3416d68... b41b7c2... M	wpa_supplicant/sme.c
:100644 100644 2b3140e... 704fab1... M	wpa_supplicant/wpa_supplicant.c
:100644 100644 59fc9a3... ebc4d64... M	wpa_supplicant/wpa_supplicant_i.h
 src/common/ieee802_11_defs.h      |    1 +
 src/drivers/driver.h              |   19 +++++
 src/drivers/driver_nl80211.c      |   16 ++++
 src/drivers/nl80211_copy.h        |   13 +++
 wpa_supplicant/config.c           |   12 +++
 wpa_supplicant/config.h           |   51 ++++++++++++
 wpa_supplicant/sme.c              |   13 +++
 wpa_supplicant/wpa_supplicant.c   |  154 ++++++++++++++++++++++++++++++++++++-
 wpa_supplicant/wpa_supplicant_i.h |   21 +++++
 9 files changed, 299 insertions(+), 1 deletions(-)

Comments

Jouni Malinen Nov. 19, 2011, 9:05 a.m. UTC | #1
On Fri, Nov 18, 2011 at 04:49:49PM -0800, greearb@candelatech.com wrote:
> This allows ht-capabilities over-rides on kernels that
> support these features.

These look quite complex.. I could kind of understand disabling HT
completely for some cases where there are interop issues, but where
would normal users need any of the more detailed overrides?
Ben Greear Nov. 19, 2011, 9:48 a.m. UTC | #2
On 11/19/2011 01:05 AM, Jouni Malinen wrote:
> On Fri, Nov 18, 2011 at 04:49:49PM -0800, greearb@candelatech.com wrote:
>> This allows ht-capabilities over-rides on kernels that
>> support these features.
>
> These look quite complex.. I could kind of understand disabling HT
> completely for some cases where there are interop issues, but where
> would normal users need any of the more detailed overrides?

I'm not sure...my use-case is for testing APs primarily.

Maybe setting rates to a lower value would help reliability
in some cases, and tuning the aggregation may also allow
users to decrease latency (at a cost of throughput)?

Is there any particular bit that seems complex to you?

Thanks,
Ben
Jouni Malinen Nov. 19, 2011, 11:34 a.m. UTC | #3
On Sat, Nov 19, 2011 at 01:48:52AM -0800, Ben Greear wrote:
> I'm not sure...my use-case is for testing APs primarily.
> 
> Maybe setting rates to a lower value would help reliability
> in some cases, and tuning the aggregation may also allow
> users to decrease latency (at a cost of throughput)?

Why would users need to do that? Isn't this something that the rate
control algorithm should take care of automatically?

> Is there any particular bit that seems complex to you?

Well, more or less all of that patch.. ;-) The problem here is that your
use case is very special and it increases the size of the wpa_supplicant
binary without really providing useful functionality for most users as
far as I can tell. Binary size may not matter much in many cases, but
wpa_supplicant is used in various limited resources embedded devices and
there is desire to avoid unnecessary size increases. Sure, this
particular patch may not be that large of an increase on its own, but
these things pile up and are difficult to get rid of once applied.
Ben Greear Nov. 19, 2011, 4:03 p.m. UTC | #4
On 11/19/2011 03:34 AM, Jouni Malinen wrote:
> On Sat, Nov 19, 2011 at 01:48:52AM -0800, Ben Greear wrote:
>> I'm not sure...my use-case is for testing APs primarily.
>>
>> Maybe setting rates to a lower value would help reliability
>> in some cases, and tuning the aggregation may also allow
>> users to decrease latency (at a cost of throughput)?
>
> Why would users need to do that? Isn't this something that the rate
> control algorithm should take care of automatically?

Sure, but the rate algorithms may not optimize perfectly for every
user.  If packet loss is very painful (maybe for VOIP?), then
it may be worth running at a lower rate for less packet loss?

Why did we allow setting rates in the a/b/g modes (even if not
through hostap) ?

>> Is there any particular bit that seems complex to you?
>
> Well, more or less all of that patch.. ;-) The problem here is that your
> use case is very special and it increases the size of the wpa_supplicant
> binary without really providing useful functionality for most users as
> far as I can tell. Binary size may not matter much in many cases, but
> wpa_supplicant is used in various limited resources embedded devices and
> there is desire to avoid unnecessary size increases. Sure, this
> particular patch may not be that large of an increase on its own, but
> these things pile up and are difficult to get rid of once applied.

Would it help if I made it something that is only compiled in when
the user selects the option in the config file?

Thanks,
Ben
Jouni Malinen Nov. 19, 2011, 4:42 p.m. UTC | #5
On Sat, Nov 19, 2011 at 08:03:48AM -0800, Ben Greear wrote:
> Sure, but the rate algorithms may not optimize perfectly for every
> user.  If packet loss is very painful (maybe for VOIP?), then
> it may be worth running at a lower rate for less packet loss?

Sure, but that should not really have anything to do with
wpa_supplicant.

> Why did we allow setting rates in the a/b/g modes (even if not
> through hostap) ?

As you point out, that is not in wpa_supplicant.. And neither should
these HT capability overrides.

> Would it help if I made it something that is only compiled in when
> the user selects the option in the config file?

Some, but it does leave the extra maintenance effort and makes code
uglier.

Unfortunately, it looks like the HT disabling capability in nl80211 was
added in a way that in practice requires changes in wpa_supplicant. It
would have been much easier from my view point if this could have been
done with iw without having to make wpa_supplicant aware of all the HT
details. It looks like your patch for wpa_supplicant does not actually
match with this design since it uses global configuration parameters
instead of per-network configuration. It is difficult to see why anyone
would want to use that type of configuration.

I could see some reasonable use cases for being able to disable HT in a
network (though, more likely as a per-network rather than global
parameter). However, anything going beyond that does not sound
justifiable since we do not provide configuration options for any other
parameters (like supported rate sets) either for this type of low-level
radio parameters in wpa_supplicant.
Ben Greear Nov. 19, 2011, 5:47 p.m. UTC | #6
On 11/19/2011 08:42 AM, Jouni Malinen wrote:
> On Sat, Nov 19, 2011 at 08:03:48AM -0800, Ben Greear wrote:
>> Sure, but the rate algorithms may not optimize perfectly for every
>> user.  If packet loss is very painful (maybe for VOIP?), then
>> it may be worth running at a lower rate for less packet loss?
>
> Sure, but that should not really have anything to do with
> wpa_supplicant.
>
>> Why did we allow setting rates in the a/b/g modes (even if not
>> through hostap) ?
>
> As you point out, that is not in wpa_supplicant.. And neither should
> these HT capability overrides.

Well, as it happens, I agree, but Johannes insisted that I make this
part of the association logic.  If he concurs with moving it back
as an interface setting, I can move all of this to 'iw'.

>> Would it help if I made it something that is only compiled in when
>> the user selects the option in the config file?
>
> Some, but it does leave the extra maintenance effort and makes code
> uglier.
>
> Unfortunately, it looks like the HT disabling capability in nl80211 was
> added in a way that in practice requires changes in wpa_supplicant. It
> would have been much easier from my view point if this could have been
> done with iw without having to make wpa_supplicant aware of all the HT
> details. It looks like your patch for wpa_supplicant does not actually
> match with this design since it uses global configuration parameters
> instead of per-network configuration. It is difficult to see why anyone
> would want to use that type of configuration.
>
> I could see some reasonable use cases for being able to disable HT in a
> network (though, more likely as a per-network rather than global
> parameter). However, anything going beyond that does not sound
> justifiable since we do not provide configuration options for any other
> parameters (like supported rate sets) either for this type of low-level
> radio parameters in wpa_supplicant.

Well, I can move the configurables out of the global portion of the
config file, but that doesn't address the rest of your concerns.

Thanks,
Ben
diff mbox

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 bd0c611..ca9c698 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,24 @@  struct wpa_driver_associate_params {
 	 * STA mode: bits 0..3 UAPSD enabled for VO,VI,BK,BE
 	 */
 	int uapsd;
+
+	/**
+	 * disable_ht - Disable HT (802.11n) for this connection.
+	 */
+	int disable_ht;
+
+	/**
+	 * disable_ht40 - Disable HT40 (802.11n) for this connection.
+	 */
+	int disable_ht40;
+
+	/**
+	 * 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;
 };
 
 enum hide_ssid {
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 89775d7..6d60030 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -5567,6 +5567,14 @@  skip_auth_type:
 		NLA_PUT_U32(msg, NL80211_ATTR_AKM_SUITES, mgmt);
 	}
 
+	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);
+
 	ret = nl80211_set_conn_keys(params, msg);
 	if (ret)
 		goto nla_put_failure;
@@ -5714,6 +5722,14 @@  static int wpa_driver_nl80211_associate(
 			params->prev_bssid);
 	}
 
+	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);
+
 	if (params->p2p)
 		wpa_printf(MSG_DEBUG, "  * P2P group");
 
diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
index 9d797f2..688bd3f 100644
--- a/src/drivers/nl80211_copy.h
+++ b/src/drivers/nl80211_copy.h
@@ -1337,6 +1337,19 @@  enum nl80211_attrs {
 	NL80211_ATTR_TDLS_SUPPORT,
 	NL80211_ATTR_TDLS_EXTERNAL_SETUP,
 
+	NL80211_ATTR_DEVICE_AP_SME,
+
+	NL80211_ATTR_DONT_WAIT_FOR_ACK,
+
+	NL80211_ATTR_FEATURE_FLAGS,
+
+	NL80211_ATTR_PROBE_RESP_OFFLOAD,
+
+	NL80211_ATTR_PROBE_RESP,
+
+	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/config.c b/wpa_supplicant/config.c
index b446a3f..a457c1e 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -1732,6 +1732,7 @@  void wpa_config_free(struct wpa_config *config)
 	os_free(config->home_ca_cert);
 	os_free(config->home_imsi);
 	os_free(config->home_milenage);
+	os_free(config->ht_mcs);
 	os_free(config);
 }
 
@@ -2201,6 +2202,11 @@  struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface,
 	config->bss_expiration_scan_count = DEFAULT_BSS_EXPIRATION_SCAN_COUNT;
 	config->max_num_sta = DEFAULT_MAX_NUM_STA;
 	config->access_network_type = DEFAULT_ACCESS_NETWORK_TYPE;
+	config->disable_ht = DEFAULT_DISABLE_HT;
+	config->disable_max_amsdu = DEFAULT_DISABLE_MAX_AMSDU;
+	config->ampdu_factor = DEFAULT_AMPDU_FACTOR;
+	config->ampdu_density = DEFAULT_AMPDU_DENSITY;
+	config->ht_mcs = strdup("");
 
 	if (ctrl_interface)
 		config->ctrl_interface = os_strdup(ctrl_interface);
@@ -2495,6 +2501,12 @@  static const struct global_parse_data global_fields[] = {
 	{ STR(home_imsi), 0 },
 	{ STR(home_milenage), 0 },
 	{ INT_RANGE(interworking, 0, 1), 0 },
+	{ INT(disable_ht), 0 },
+	{ INT(disable_ht40), 0 },
+	{ INT(disable_max_amsdu), 0 },
+	{ INT(ampdu_factor), 0 },
+	{ INT(ampdu_density), 0 },
+	{ STR(ht_mcs), 0 },
 	{ FUNC(hessid), 0 },
 	{ INT_RANGE(access_network_type, 0, 15), 0 }
 };
diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h
index ae496ff..7586209 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
@@ -192,6 +197,52 @@  struct wpa_config {
 	int fast_reauth;
 
 	/**
+	 * disable_ht - Disable HT (802.11n) for this interface
+	 *
+	 * By default, use it if it is available, but this can be configured
+	 * to a non-zero value 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 a non-zero value 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:  use whatever the OS has configured.
+	 */
+	char *ht_mcs;
+
+	/**
 	 * opensc_engine_path - Path to the OpenSSL engine for opensc
 	 *
 	 * This is an OpenSSL specific configuration option for loading OpenSC
diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
index 3416d68..b41b7c2 100644
--- a/wpa_supplicant/sme.c
+++ b/wpa_supplicant/sme.c
@@ -386,6 +386,19 @@  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);
+	params.disable_ht = wpa_s->conf->disable_ht;
+	params.disable_ht40 = wpa_s->conf->disable_ht40;
+
+	wpa_set_htcap_mcs(wpa_s, &params.htcaps, &params.htcaps_mask, wpa_s->conf->ht_mcs);
+	wpa_disable_max_amsdu(wpa_s, &params.htcaps, &params.htcaps_mask,
+			      wpa_s->conf->disable_max_amsdu);
+	wpa_set_ampdu_factor(wpa_s, &params.htcaps, &params.htcaps_mask,
+			     wpa_s->conf->ampdu_factor);
+	wpa_set_ampdu_density(wpa_s, &params.htcaps, &params.htcaps_mask,
+			      wpa_s->conf->ampdu_density);
+	wpa_set_disable_ht40(wpa_s, &params.htcaps, &params.htcaps_mask,
+			     wpa_s->conf->disable_ht40);
+
 #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..704fab1 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -742,7 +742,13 @@  int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s)
 	}
 
 	eapol_sm_invalidate_cached_session(wpa_s->eapol);
-	if (wpa_s->current_ssid) {
+	if (wpa_s->current_ssid ||
+	    (conf->disable_ht40 != wpa_s->conf->disable_ht40) ||
+	    (conf->disable_max_amsdu != wpa_s->conf->disable_max_amsdu) ||
+	    (conf->ampdu_factor != wpa_s->conf->ampdu_factor) ||
+	    (conf->ampdu_density != wpa_s->conf->ampdu_density) ||
+	    (conf->disable_ht != wpa_s->conf->disable_ht) ||
+	    (strcmp(conf->ht_mcs, wpa_s->conf->ht_mcs) != 0)) {
 		wpa_supplicant_deauthenticate(wpa_s,
 					      WLAN_REASON_DEAUTH_LEAVING);
 	}
@@ -1424,6 +1430,18 @@  void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
 		params.uapsd = wpa_s->parent->sta_uapsd;
 	else
 		params.uapsd = -1;
+	params.disable_ht = wpa_s->conf->disable_ht;
+	params.disable_ht40 = wpa_s->conf->disable_ht40;
+
+	wpa_set_htcap_mcs(wpa_s, &params.htcaps, &params.htcaps_mask, wpa_s->conf->ht_mcs);
+	wpa_disable_max_amsdu(wpa_s, &params.htcaps, &params.htcaps_mask,
+			      wpa_s->conf->disable_max_amsdu);
+	wpa_set_ampdu_factor(wpa_s, &params.htcaps, &params.htcaps_mask,
+			     wpa_s->conf->ampdu_factor);
+	wpa_set_ampdu_density(wpa_s, &params.htcaps, &params.htcaps_mask,
+			      wpa_s->conf->ampdu_density);
+	wpa_set_disable_ht40(wpa_s, &params.htcaps, &params.htcaps_mask,
+			     wpa_s->conf->disable_ht40);
 
 	ret = wpa_drv_associate(wpa_s, &params);
 	if (ret < 0) {
@@ -2165,6 +2183,140 @@  static struct wpa_supplicant * wpa_supplicant_alloc(void)
 	return wpa_s;
 }
 
+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;
+}
+
 
 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..ebc4d64 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -36,6 +36,7 @@  struct scan_info;
 struct wpa_bss;
 struct wpa_scan_results;
 struct hostapd_hw_modes;
+struct ieee80211_ht_capabilities;
 
 /*
  * Forward declarations of private structures used within the ctrl_iface
@@ -514,6 +515,26 @@  struct wpa_supplicant {
 
 
 /* wpa_supplicant.c */
+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);
 int wpa_set_wep_keys(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
 
 int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s);