From e9ea7b45c9b03de2b24ef85a733f618f57278fdc Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Wed, 28 Dec 2022 14:32:29 -0800 Subject: [PATCH] ap: include WMM parameter IE The WMM parameter IE is expected by the linux kernel for any AP supporting HT/VHT etc. IWD won't actually use WMM and its not clear exactly why the kernel uses this restriction, but regardless it must be included to support HT. --- src/ap.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/src/ap.c b/src/ap.c index 595c71c9..b505499d 100644 --- a/src/ap.c +++ b/src/ap.c @@ -111,6 +111,7 @@ struct ap_state { bool in_event : 1; bool free_pending : 1; bool scanning : 1; + bool supports_ht : 1; }; struct sta_state { @@ -834,6 +835,9 @@ static size_t ap_get_extra_ies_len(struct ap_state *ap, len += ap_get_wsc_ie_len(ap, type, client_frame, client_frame_len); + if (ap->supports_ht) + len += 26; + if (ap->ops->get_extra_ies_len) len += ap->ops->get_extra_ies_len(type, client_frame, client_frame_len, @@ -842,6 +846,67 @@ static size_t ap_get_extra_ies_len(struct ap_state *ap, return len; } +/* WMM Specification 2.2.2 WMM Parameter Element */ +struct ap_wmm_ac_record { +#if defined(__LITTLE_ENDIAN_BITFIELD) + uint8_t aifsn : 4; + uint8_t acm : 1; + uint8_t aci : 2; + uint8_t reserved : 1; + uint8_t ecw_min : 4; + uint8_t ecw_max : 4; +#elif defined (__BIG_ENDIAN_BITFIELD) + uint8_t reserved : 1; + uint8_t aci : 2; + uint8_t acm : 1; + uint8_t aifsn : 4; + uint8_t acw_max : 4; + uint8_t acw_min : 4; +#else +#error "Please fix netdev); + + /* + * Linux kernel requires APs include WMM Information element if + * supporting HT/VHT/etc. + * + * The only value we can actually get from the kernel is UAPSD. The + * remaining values (AC parameter records) are made up or defaults + * defined in the WMM spec are used. + */ + *out_buf++ = IE_TYPE_VENDOR_SPECIFIC; + *out_buf++ = 24; + memcpy(out_buf, microsoft_oui, sizeof(microsoft_oui)); + out_buf += sizeof(microsoft_oui); + *out_buf++ = 2; /* WMM OUI Type */ + *out_buf++ = 1; /* WMM Parameter subtype */ + *out_buf++ = 1; /* WMM Version */ + *out_buf++ = wiphy_supports_uapsd(wiphy) ? 1 << 7 : 0; + *out_buf++ = 0; /* reserved */ + + for (i = 0; i < 4; i++) { + struct ap_wmm_ac_record ac = { 0 }; + + ac.aifsn = 2; + ac.acm = 0; + ac.aci = i; + ac.ecw_min = 1; + ac.ecw_max = 15; + l_put_le16(0, &ac.txop_limit); + + memcpy(out_buf + (i * 4), &ac, sizeof(struct ap_wmm_ac_record)); + } + + return 26; +} + static size_t ap_write_extra_ies(struct ap_state *ap, enum mpdu_management_subtype type, const struct mmpdu_header *client_frame, @@ -853,6 +918,9 @@ static size_t ap_write_extra_ies(struct ap_state *ap, len += ap_write_wsc_ie(ap, type, client_frame, client_frame_len, out_buf + len); + if (ap->supports_ht) + len += ap_write_wmm_ies(ap, out_buf + len); + if (ap->ops->write_extra_ies) len += ap->ops->write_extra_ies(type, client_frame, client_frame_len, @@ -3255,6 +3323,9 @@ static int ap_load_config(struct ap_state *ap, const struct l_settings *config, ap->band = BAND_FREQ_2_4_GHZ; } + ap->supports_ht = wiphy_get_ht_capabilities(wiphy, ap->band, + NULL) != NULL; + if (!ap_validate_band_channel(ap)) { l_error("AP Band and Channel combination invalid"); return -EINVAL;