hostapd/wpa_s: Use driver's extended capabilities

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>
This commit is contained in:
Johannes Berg 2013-03-31 21:51:44 +03:00 committed by Jouni Malinen
parent 62d58f7ae3
commit 8cd6b7bce8
8 changed files with 160 additions and 58 deletions

View file

@ -274,6 +274,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;

View file

@ -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;

View file

@ -164,10 +164,52 @@ void ieee802_11_sa_query_action(struct hostapd_data *hapd, const u8 *sa,
#endif /* CONFIG_IEEE80211W */
static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx)
{
*pos = 0x00;
switch (idx) {
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 */
#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 */
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) {
/* Bit 39 - TDLS Channel Switching Prohibited */
*pos |= 0x80;
}
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;
}
}
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 +223,21 @@ 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;
for (i = 0; i < len; i++, pos++) {
hostapd_ext_capab_byte(hapd, pos, i);
*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++;
if (len < 4)
return pos;
*pos = 0x00;
#ifdef CONFIG_WNM
*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 (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;
}

View file

@ -897,6 +897,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;
};

View file

@ -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;
@ -2545,6 +2547,7 @@ nla_put_failure:
struct wiphy_info_data {
struct wpa_driver_nl80211_data *drv;
struct wpa_driver_capa *capa;
unsigned int error:1;
@ -2786,6 +2789,7 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
struct wiphy_info_data *info = arg;
struct wpa_driver_capa *capa = info->capa;
struct wpa_driver_nl80211_data *drv = info->drv;
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
@ -2833,6 +2837,30 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
wiphy_info_probe_resp_offload(capa,
tb[NL80211_ATTR_PROBE_RESP_OFFLOAD]);
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;
}
@ -2845,6 +2873,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)
@ -3733,6 +3762,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);
}
@ -7591,6 +7622,11 @@ static int wpa_driver_nl80211_get_capa(void *priv,
if (!drv->has_capability)
return -1;
os_memcpy(capa, &drv->capa, sizeof(*capa));
if (drv->extended_capa && drv->extended_capa_mask) {
capa->extended_capa = drv->extended_capa;
capa->extended_capa_mask = drv->extended_capa_mask;
capa->extended_capa_len = drv->extended_capa_len;
}
return 0;
}

View file

@ -498,6 +498,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) {

View file

@ -1178,28 +1178,58 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
}
static void wpas_ext_capab_byte(struct wpa_supplicant *wpa_s, u8 *pos, int idx)
{
*pos = 0x00;
switch (idx) {
case 0: /* Bits 0-7 */
break;
case 1: /* Bits 8-15 */
break;
case 2: /* Bits 16-23 */
#ifdef CONFIG_WNM
*pos |= 0x02; /* Bit 17 - WNM-Sleep Mode */
*pos |= 0x08; /* Bit 19 - BSS Transition */
#endif /* CONFIG_WNM */
break;
case 3: /* Bits 24-31 */
#ifdef CONFIG_WNM
*pos |= 0x02; /* Bit 25 - SSID List */
#endif /* CONFIG_WNM */
#ifdef CONFIG_INTERWORKING
if (wpa_s->conf->interworking)
*pos |= 0x80; /* Bit 31 - Interworking */
#endif /* CONFIG_INTERWORKING */
break;
case 4: /* Bits 32-39 */
break;
case 5: /* Bits 40-47 */
break;
case 6: /* Bits 48-55 */
break;
}
}
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 */
#ifdef CONFIG_WNM
ext_capab |= BIT(17); /* WNM-Sleep Mode */
ext_capab |= BIT(19); /* BSS Transition */
#endif /* CONFIG_WNM */
if (!ext_capab)
return 0;
if (len < wpa_s->extended_capa_len)
len = wpa_s->extended_capa_len;
*pos++ = WLAN_EID_EXT_CAPAB;
*pos++ = 4;
WPA_PUT_LE32(pos, ext_capab);
pos += 4;
*pos++ = len;
for (i = 0; i < len; i++, pos++) {
wpas_ext_capab_byte(wpa_s, pos, i);
if (i < wpa_s->extended_capa_len) {
*pos &= ~wpa_s->extended_capa_mask[i];
*pos |= wpa_s->extended_capa[i];
}
}
return pos - buf;
}
@ -2904,6 +2934,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;

View file

@ -461,6 +461,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;