Patchwork [v2] hostapd/wpa_s: use driver's extended capabilities

login
register
mail settings
Submitter Johannes Berg
Date Feb. 18, 2013, 5:06 p.m.
Message ID <1361207162-5377-1-git-send-email-johannes@sipsolutions.net>
Download mbox | patch
Permalink /patch/221422/
State Superseded
Headers show

Comments

Johannes Berg - Feb. 18, 2013, 5:06 p.m.
From: Johannes Berg <johannes.berg@intel.com>

Some extended capabilities (I'm currently interested in
"Operating Mode Notification" for VHT) are implemented
by the kernel driver and exported in nl80211. Use these
in hostapd/wpa_supplicant.

Signed-hostap: Johannes Berg <johannes.berg@intel.com>
---
 hostapd/main.c                    |  3 ++
 src/ap/hostapd.h                  |  4 ++
 src/ap/ieee802_11_shared.c        | 80 ++++++++++++++++++++-------------------
 src/drivers/driver.h              |  9 +++++
 src/drivers/driver_nl80211.c      | 34 +++++++++++++++++
 wpa_supplicant/ap.c               |  3 ++
 wpa_supplicant/wpa_supplicant.c   | 53 +++++++++++++++++++-------
 wpa_supplicant/wpa_supplicant_i.h |  4 ++
 8 files changed, 137 insertions(+), 53 deletions(-)

Patch

diff --git a/hostapd/main.c b/hostapd/main.c
index d4256d0..b92637b 100644
--- a/hostapd/main.c
+++ b/hostapd/main.c
@@ -273,6 +273,9 @@  static int hostapd_driver_init(struct hostapd_iface *iface)
 	    hapd->driver->get_capa(hapd->drv_priv, &capa) == 0) {
 		iface->drv_flags = capa.flags;
 		iface->probe_resp_offloads = capa.probe_resp_offloads;
+		iface->extended_capa = capa.extended_capa;
+		iface->extended_capa_mask = capa.extended_capa_mask;
+		iface->extended_capa_len = capa.extended_capa_len;
 	}
 
 	return 0;
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index 8ab4f3e..669633d 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -229,6 +229,10 @@  struct hostapd_iface {
 	 */
 	unsigned int probe_resp_offloads;
 
+	/* extended capabilities supported by the driver */
+	const u8 *extended_capa, *extended_capa_mask;
+	unsigned int extended_capa_len;
+
 	struct hostapd_hw_modes *hw_features;
 	int num_hw_features;
 	struct hostapd_hw_modes *current_mode;
diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
index 76f78a7..657e7ee 100644
--- a/src/ap/ieee802_11_shared.c
+++ b/src/ap/ieee802_11_shared.c
@@ -167,7 +167,7 @@  void ieee802_11_sa_query_action(struct hostapd_data *hapd, const u8 *sa,
 u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid)
 {
 	u8 *pos = eid;
-	u8 len = 0;
+	u8 len = 0, i;
 
 	if (hapd->conf->tdls & (TDLS_PROHIBIT | TDLS_PROHIBIT_CHAN_SWITCH))
 		len = 5;
@@ -181,53 +181,55 @@  u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid)
 	if (len < 4)
 		len = 4;
 #endif /* CONFIG_WNM */
+	if (len < hapd->iface->extended_capa_len)
+		len = hapd->iface->extended_capa_len;
 	if (len == 0)
 		return eid;
 
 	*pos++ = WLAN_EID_EXT_CAPAB;
 	*pos++ = len;
-	*pos++ = 0x00;
-	*pos++ = 0x00;
-
-	*pos = 0x00;
-	if (hapd->conf->wnm_sleep_mode)
-		*pos |= 0x02; /* Bit 17 - WNM-Sleep Mode */
-	if (hapd->conf->bss_transition)
-		*pos |= 0x08; /* Bit 19 - BSS Transition */
-	pos++;
+	for (i = 0; i < len; i++, pos++) {
+		*pos = 0x00;
 
-	if (len < 4)
-		return pos;
-	*pos = 0x00;
+		switch (i) {
+		case 0: /* Bits 0-7 */
+			break;
+		case 1: /* Bits 8-15 */
+			break;
+		case 2: /* Bits 16-23 */
+			if (hapd->conf->wnm_sleep_mode)
+				*pos |= 0x02; /* Bit 17 - WNM-Sleep Mode */
+			if (hapd->conf->bss_transition)
+				*pos |= 0x08; /* Bit 19 - BSS Transition */
+			break;
+		case 3: /* Bits 24-31 */
 #ifdef CONFIG_WNM
-	*pos |= 0x02; /* Bit 25 - SSID List */
+			*pos |= 0x02; /* Bit 25 - SSID List */
 #endif /* CONFIG_WNM */
-	if (hapd->conf->time_advertisement == 2)
-		*pos |= 0x08; /* Bit 27 - UTC TSF Offset */
-	if (hapd->conf->interworking)
-		*pos |= 0x80; /* Bit 31 - Interworking */
-	pos++;
-
-	if (len < 5)
-		return pos;
-	*pos = 0x00;
-	if (hapd->conf->tdls & TDLS_PROHIBIT)
-		*pos |= 0x40; /* Bit 38 - TDLS Prohibited */
-	if (hapd->conf->tdls & TDLS_PROHIBIT_CHAN_SWITCH)
-		*pos |= 0x80; /* Bit 39 - TDLS Channel Switching Prohibited */
-	pos++;
-
-	if (len < 6)
-		return pos;
-	*pos = 0x00;
-	pos++;
+			if (hapd->conf->time_advertisement == 2)
+				*pos |= 0x08; /* Bit 27 - UTC TSF Offset */
+			if (hapd->conf->interworking)
+				*pos |= 0x80; /* Bit 31 - Interworking */
+			break;
+		case 4: /* Bits 32-39 */
+			if (hapd->conf->tdls & TDLS_PROHIBIT)
+				*pos |= 0x40; /* Bit 38 - TDLS Prohibited */
+			if (hapd->conf->tdls & TDLS_PROHIBIT_CHAN_SWITCH)
+				*pos |= 0x80; /* Bit 39 - TDLS Channel Switching Prohibited */
+			break;
+		case 5: /* Bits 40-47 */
+			break;
+		case 6: /* Bits 48-55 */
+			if (hapd->conf->ssid.utf8_ssid)
+				*pos |= 0x01; /* Bit 48 - UTF-8 SSID */
+			break;
+		}
 
-	if (len < 7)
-		return pos;
-	*pos = 0x00;
-	if (hapd->conf->ssid.utf8_ssid)
-		*pos |= 0x01; /* Bit 48 - UTF-8 SSID */
-	pos++;
+		if (i < hapd->iface->extended_capa_len) {
+			*pos &= ~hapd->iface->extended_capa_mask[i];
+			*pos |= hapd->iface->extended_capa[i];
+		}
+	}
 
 	return pos;
 }
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 274eeca..bb373be 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -882,6 +882,15 @@  struct wpa_driver_capa {
 /* Driver Probe Response offloading support for IEEE 802.11u (Interworking) */
 #define WPA_DRIVER_PROBE_RESP_OFFLOAD_INTERWORKING	0x00000008
 	unsigned int probe_resp_offloads;
+
+	/**
+	 * extended_capa - extended capabilities in driver/device
+	 *
+	 * Must be allocated and freed by driver and the pointers must be
+	 * valid for the lifetime of the driver, i.e. freed in deinit()
+	 */
+	const u8 *extended_capa, *extended_capa_mask;
+	unsigned int extended_capa_len;
 };
 
 
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 0803b16..fbdafc5 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -220,6 +220,8 @@  struct wpa_driver_nl80211_data {
 	int ignore_if_down_event;
 	struct rfkill_data *rfkill;
 	struct wpa_driver_capa capa;
+	u8 *extended_capa, *extended_capa_mask;
+	unsigned int extended_capa_len;
 	int has_capability;
 
 	int operstate;
@@ -2477,6 +2479,7 @@  nla_put_failure:
 
 
 struct wiphy_info_data {
+	struct wpa_driver_nl80211_data *drv;
 	struct wpa_driver_capa *capa;
 
 	unsigned int error:1;
@@ -2509,6 +2512,7 @@  static int wiphy_info_handler(struct nl_msg *msg, void *arg)
 	struct nlattr *tb[NL80211_ATTR_MAX + 1];
 	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
 	struct wiphy_info_data *info = arg;
+	struct wpa_driver_nl80211_data *drv = info->drv;
 	int p2p_go_supported = 0, p2p_client_supported = 0;
 	int p2p_concurrent = 0, p2p_multichan_concurrent = 0;
 	int auth_supported = 0, connect_supported = 0;
@@ -2729,6 +2733,30 @@  broken_combination:
 			probe_resp_offload_support(protocols);
 	}
 
+	if (tb[NL80211_ATTR_EXT_CAPA] && tb[NL80211_ATTR_EXT_CAPA_MASK] &&
+	    drv->extended_capa == NULL) {
+		drv->extended_capa =
+			os_malloc(nla_len(tb[NL80211_ATTR_EXT_CAPA]));
+		if (drv->extended_capa) {
+			os_memcpy(drv->extended_capa,
+				  nla_data(tb[NL80211_ATTR_EXT_CAPA]),
+				  nla_len(tb[NL80211_ATTR_EXT_CAPA]));
+			drv->extended_capa_len =
+				nla_len(tb[NL80211_ATTR_EXT_CAPA]);
+		}
+		drv->extended_capa_mask =
+			os_malloc(nla_len(tb[NL80211_ATTR_EXT_CAPA]));
+		if (drv->extended_capa_mask) {
+			os_memcpy(drv->extended_capa_mask,
+				  nla_data(tb[NL80211_ATTR_EXT_CAPA]),
+				  nla_len(tb[NL80211_ATTR_EXT_CAPA]));
+		} else {
+			os_free(drv->extended_capa);
+			drv->extended_capa = NULL;
+			drv->extended_capa_len = 0;
+		}
+	}
+
 	return NL_SKIP;
 }
 
@@ -2740,6 +2768,7 @@  static int wpa_driver_nl80211_get_info(struct wpa_driver_nl80211_data *drv,
 
 	os_memset(info, 0, sizeof(*info));
 	info->capa = &drv->capa;
+	info->drv = drv;
 
 	msg = nlmsg_alloc();
 	if (!msg)
@@ -3601,6 +3630,8 @@  static void wpa_driver_nl80211_deinit(struct i802_bss *bss)
 	if (drv->in_interface_list)
 		dl_list_del(&drv->list);
 
+	os_free(drv->extended_capa);
+	os_free(drv->extended_capa_mask);
 	os_free(drv);
 }
 
@@ -7352,6 +7383,9 @@  static int wpa_driver_nl80211_get_capa(void *priv,
 	if (!drv->has_capability)
 		return -1;
 	os_memcpy(capa, &drv->capa, sizeof(*capa));
+	capa->extended_capa = drv->extended_capa;
+	capa->extended_capa_mask = drv->extended_capa_mask;
+	capa->extended_capa_len = drv->extended_capa_len;
 	return 0;
 }
 
diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c
index 82b7e19..fb8d51e 100644
--- a/wpa_supplicant/ap.c
+++ b/wpa_supplicant/ap.c
@@ -486,6 +486,9 @@  int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s,
 	hapd_iface->owner = wpa_s;
 	hapd_iface->drv_flags = wpa_s->drv_flags;
 	hapd_iface->probe_resp_offloads = wpa_s->probe_resp_offloads;
+	hapd_iface->extended_capa = wpa_s->extended_capa;
+	hapd_iface->extended_capa_mask = wpa_s->extended_capa_mask;
+	hapd_iface->extended_capa_len = wpa_s->extended_capa_len;
 
 	wpa_s->ap_iface->conf = conf = hostapd_config_defaults();
 	if (conf == NULL) {
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index ef7a6f0..d32d06e 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -1189,26 +1189,48 @@  int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
 
 int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf)
 {
-	u32 ext_capab = 0;
 	u8 *pos = buf;
+	u8 len = 4, i;
 
-#ifdef CONFIG_INTERWORKING
-	if (wpa_s->conf->interworking)
-		ext_capab |= BIT(31); /* Interworking */
-#endif /* CONFIG_INTERWORKING */
+	if (len < wpa_s->extended_capa_len)
+		len = wpa_s->extended_capa_len;
 
+	*pos++ = WLAN_EID_EXT_CAPAB;
+	*pos++ = len;
+	for (i = 0; i < len; i++, pos++) {
+		*pos = 0x00;
+
+		switch (i) {
+		case 0: /* Bits 0-7 */
+			break;
+		case 1: /* Bits 8-15 */
+			break;
+		case 2: /* Bits 16-23 */
 #ifdef CONFIG_WNM
-	ext_capab |= BIT(17); /* WNM-Sleep Mode */
-	ext_capab |= BIT(19); /* BSS Transition */
+			*pos |= 0x02; /* Bit 17 - WNM-Sleep Mode */
+			*pos |= 0x08; /* Bit 19 - BSS Transition */
+#endif
+			break;
+		case 3: /* Bits 24-31 */
+#ifdef CONFIG_WNM
+			*pos |= 0x02; /* Bit 25 - SSID List */
 #endif /* CONFIG_WNM */
+			if (wpa_s->conf->interworking)
+				*pos |= 0x80; /* Bit 31 - Interworking */
+			break;
+		case 4: /* Bits 32-39 */
+			break;
+		case 5: /* Bits 40-47 */
+			break;
+		case 6: /* Bits 48-55 */
+			break;
+		}
 
-	if (!ext_capab)
-		return 0;
-
-	*pos++ = WLAN_EID_EXT_CAPAB;
-	*pos++ = 4;
-	WPA_PUT_LE32(pos, ext_capab);
-	pos += 4;
+		if (i < wpa_s->extended_capa_len) {
+			*pos &= ~wpa_s->extended_capa_mask[i];
+			*pos |= wpa_s->extended_capa[i];
+		}
+	}
 
 	return pos - buf;
 }
@@ -2866,6 +2888,9 @@  next_driver:
 		wpa_s->max_match_sets = capa.max_match_sets;
 		wpa_s->max_remain_on_chan = capa.max_remain_on_chan;
 		wpa_s->max_stations = capa.max_stations;
+		wpa_s->extended_capa = capa.extended_capa;
+		wpa_s->extended_capa_mask = capa.extended_capa_mask;
+		wpa_s->extended_capa_len = capa.extended_capa_len;
 	}
 	if (wpa_s->max_remain_on_chan == 0)
 		wpa_s->max_remain_on_chan = 1000;
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index 0f51f8e..63808ab 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -459,6 +459,10 @@  struct wpa_supplicant {
 	 */
 	unsigned int probe_resp_offloads;
 
+	/* extended capabilities supported by the driver */
+	const u8 *extended_capa, *extended_capa_mask;
+	unsigned int extended_capa_len;
+
 	int max_scan_ssids;
 	int max_sched_scan_ssids;
 	int sched_scan_supported;