diff mbox

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

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

Commit Message

Ben Greear Nov. 4, 2011, 11:15 p.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 4cbc535... 0f0980a... M	src/common/ieee802_11_defs.h
:100644 100644 ead35c0... c122ab1... M	src/drivers/driver.h
:100644 100644 8901ed7... 4821a38... M	src/drivers/driver_nl80211.c
:100644 100644 9d797f2... 7d04842... 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 da689d2... 6d15458... M	wpa_supplicant/sme.c
:100644 100644 2b3140e... c10f3b6... M	wpa_supplicant/wpa_supplicant.c
:100644 100644 fd04a7d... 4eb401a... M	wpa_supplicant/wpa_supplicant_i.h
 src/common/ieee802_11_defs.h      |    2 +
 src/drivers/driver.h              |   19 ++++++
 src/drivers/driver_nl80211.c      |   23 +++++++
 src/drivers/nl80211_copy.h        |    4 +
 wpa_supplicant/config.c           |   12 ++++
 wpa_supplicant/config.h           |   51 ++++++++++++++
 wpa_supplicant/sme.c              |   11 +++
 wpa_supplicant/wpa_supplicant.c   |  130 ++++++++++++++++++++++++++++++++++++-
 wpa_supplicant/wpa_supplicant_i.h |   17 +++++
 9 files changed, 268 insertions(+), 1 deletions(-)
diff mbox

Patch

diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index 4cbc535..0f0980a 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -519,6 +519,8 @@  struct ieee80211_mgmt {
 } STRUCT_PACKED;
 
 
+#define IEEE80211_HT_CAP_MAX_AMSDU		0x0800
+#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 ead35c0..c122ab1 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 8901ed7..4821a38 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -5586,6 +5586,17 @@  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->disable_ht40)
+		NLA_PUT_FLAG(msg, NL80211_ATTR_DISABLE_HT40);
+
+	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;
@@ -5733,6 +5744,17 @@  static int wpa_driver_nl80211_associate(
 			params->prev_bssid);
 	}
 
+	if (params->disable_ht)
+		NLA_PUT_FLAG(msg, NL80211_ATTR_DISABLE_HT);
+
+	if (params->disable_ht40)
+		NLA_PUT_FLAG(msg, NL80211_ATTR_DISABLE_HT40);
+
+	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");
 
@@ -7114,6 +7136,7 @@  static int wpa_driver_nl80211_deinit_ap(void *priv)
 {
 	struct i802_bss *bss = priv;
 	struct wpa_driver_nl80211_data *drv = bss->drv;
+
 	if (!is_ap_interface(drv->nlmode))
 		return -1;
 	wpa_driver_nl80211_del_beacon(drv);
diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
index 9d797f2..7d04842 100644
--- a/src/drivers/nl80211_copy.h
+++ b/src/drivers/nl80211_copy.h
@@ -1337,6 +1337,10 @@  enum nl80211_attrs {
 	NL80211_ATTR_TDLS_SUPPORT,
 	NL80211_ATTR_TDLS_EXTERNAL_SETUP,
 
+	NL80211_ATTR_DISABLE_HT,
+	NL80211_ATTR_DISABLE_HT40,
+	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 da689d2..6d15458 100644
--- a/wpa_supplicant/sme.c
+++ b/wpa_supplicant/sme.c
@@ -387,6 +387,17 @@  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);
+
 #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..c10f3b6 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,16 @@  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);
 
 	ret = wpa_drv_associate(wpa_s, &params);
 	if (ret < 0) {
@@ -2165,6 +2181,118 @@  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;
+
+	htcaps_mask->ht_capabilities_info |= IEEE80211_HT_CAP_MAX_AMSDU;
+	if (disabled)
+		htcaps->ht_capabilities_info &= ~IEEE80211_HT_CAP_MAX_AMSDU;
+	else
+		htcaps->ht_capabilities_info |= IEEE80211_HT_CAP_MAX_AMSDU;
+	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;
+}
+
 
 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 fd04a7d..4eb401a 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
@@ -512,6 +513,22 @@  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_wep_keys(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
 
 int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s);