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.
This commit is contained in:
James Prestwood 2022-12-28 14:32:29 -08:00 committed by Denis Kenzior
parent cda4f42a7b
commit e9ea7b45c9
1 changed files with 71 additions and 0 deletions

View File

@ -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 <asm/byteorder.h"
#endif
__le16 txop_limit;
} __attribute__((packed));
static size_t ap_write_wmm_ies(struct ap_state *ap, uint8_t *out_buf)
{
unsigned int i;
struct wiphy *wiphy = netdev_get_wiphy(ap->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;