diff mbox series

[08/10] ap: include WMM parameter IE

Message ID 20221220214318.2041986-8-prestwoj@gmail.com (mailing list archive)
State New
Headers show
Series [01/10] ap: make supported rates a common builder. | expand

Checks

Context Check Description
tedd_an/pre-ci_am success Success
prestwoj/iwd-ci-gitlint success GitLint

Commit Message

James Prestwood Dec. 20, 2022, 9:43 p.m. UTC
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 | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 60 insertions(+)

Comments

Denis Kenzior Dec. 27, 2022, 6:09 p.m. UTC | #1
Hi James,

On 12/20/22 15:43, James Prestwood wrote:
> 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 | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>   1 file changed, 60 insertions(+)
> 

<snip>

> @@ -842,6 +846,56 @@ 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 {
> +	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;

Since this bitfield is multiple bytes, you have to be careful about endianness. 
  Compiler will store these bits differently depending on native endian order. 
See src/mpdu.h (mmpdu_field_capability definition) for an example of how we have 
solved this in the past.

> +	__le16 txop_limit;
> +} __attribute__((packed));
> +

Regards,
-Denis
diff mbox series

Patch

diff --git a/src/ap.c b/src/ap.c
index 595c71c9..d3188d1d 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,56 @@  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 {
+	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;
+	__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 +907,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 +3312,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;