#define WLAN_EID_EXT_EHT_CAPABILITIES 108
#define WLAN_EID_EXT_TID_TO_LINK_MAPPING 109
#define WLAN_EID_EXT_MULTI_LINK_TRAFFIC_INDICATION 110
+#define WLAN_EID_EXT_QOS_CHARACTERISTICS 113
#define WLAN_EID_EXT_AKM_SUITE_SELECTOR 114
/* Extended Capabilities field */
SCS_REQ_CHANGE = 2,
};
+/*
+ * IEEE P802.11be/D4.0, 9.4.2.316 QoS Characteristics element,
+ * Table 9-404s (Direction subfield encoding)
+ */
+enum scs_direction {
+ SCS_DIRECTION_UP = 0,
+ SCS_DIRECTION_DOWN = 1,
+ SCS_DIRECTION_DIRECT = 2,
+};
+
+/*
+ * IEEE P802.11be/D4.0, 9.4.2.316 QoS Characteristics element,
+ * Figure 9-1001av (Control Info field format)
+ */
+#define EHT_QOS_CONTROL_INFO_DIRECTION_OFFSET 0
+#define EHT_QOS_CONTROL_INFO_TID_OFFSET 2
+#define EHT_QOS_CONTROL_INFO_USER_PRIORITY_OFFSET 6
+#define EHT_QOS_CONTROL_INFO_PRESENCE_MASK_OFFSET 9
+#define EHT_QOS_CONTROL_INFO_LINK_ID_OFFSET 25
+
/* Optional subelement IDs for MSCS Descriptor element */
enum mscs_description_subelem {
MCSC_SUBELEM_STATUS = 1,
* [scs_id=<decimal number>] <add|remove|change> [scs_up=<0-7>]
* [classifier_type=<4|10>]
* [classifier params based on classifier type]
- * [tclas_processing=<0|1>] [scs_id=<decimal number>] ...
+ * [tclas_processing=<0|1>]
+ * [qos_characteristics] <up/down/direct> [min_si=<decimal number>]
+ * [max_si=<decimal number>] [min_data_rate=<decimal number>]
+ * [delay_bound=<decimal number>]
+ * [scs_id=<decimal number>] ...
*/
pos1 = os_strstr(cmd, "scs_id=");
if (!pos1) {
struct active_scs_elem *active_scs_desc;
char *next_scs_desc;
unsigned int num_tclas_elem = 0;
- bool scsid_active = false;
+ bool scsid_active = false, tclas_present = false;
+ struct qos_characteristics *qos_elem = &desc_elem.qos_char_elem;
desc_elem.scs_id = atoi(pos1 + 7);
pos1 += 7;
pos = os_strstr(pos1, "classifier_type=");
if (!pos) {
wpa_printf(MSG_ERROR, "classifier type empty");
- goto free_scs_desc;
+ goto qos_characteristics;
}
+ tclas_present = true;
while (pos) {
struct tclas_element elem = { 0 }, *n;
desc_elem.tclas_processing = val;
}
+ qos_characteristics:
+ pos1 = os_strstr(pos1, "qos_characteristics");
+ if (!pos1 && !tclas_present)
+ goto free_scs_desc;
+ if (!pos1)
+ goto scs_desc_end;
+
+ qos_elem->available = true;
+ if (os_strstr(pos1, "up ")) {
+ qos_elem->direction = SCS_DIRECTION_UP;
+ if (tclas_present) {
+ wpa_printf(MSG_ERROR,
+ "TCLAS with direction:UP not allowed");
+ goto free_scs_desc;
+ }
+ } else if (os_strstr(pos1, "down ")) {
+ qos_elem->direction = SCS_DIRECTION_DOWN;
+ } else if (os_strstr(pos1, "direct ")) {
+ qos_elem->direction = SCS_DIRECTION_DIRECT;
+ }
+
+ pos1 = os_strstr(pos1, "min_si=");
+ if (!pos1) {
+ wpa_printf(MSG_ERROR, "Min SI is required");
+ goto free_scs_desc;
+ }
+ qos_elem->min_si = atoi(pos1 + 7);
+
+ pos1 = os_strstr(pos1, "max_si=");
+ if (!pos1) {
+ wpa_printf(MSG_ERROR, "Max SI is required");
+ goto free_scs_desc;
+ }
+ qos_elem->max_si = atoi(pos1 + 7);
+
+ if (qos_elem->min_si && qos_elem->max_si &&
+ qos_elem->max_si < qos_elem->min_si) {
+ wpa_printf(MSG_ERROR, "Invalid Max SI");
+ goto free_scs_desc;
+ }
+
+ pos1 = os_strstr(pos1, "min_data_rate=");
+ if (!pos1) {
+ wpa_printf(MSG_ERROR, "Min data rate is required");
+ goto free_scs_desc;
+ }
+ qos_elem->min_data_rate = atoi(pos1 + 14);
+
+ pos1 = os_strstr(pos1, "delay_bound=");
+ if (!pos1) {
+ wpa_printf(MSG_ERROR, "Delay Bound is required");
+ goto free_scs_desc;
+ }
+ qos_elem->delay_bound = atoi(pos1 + 12);
+
+ if (qos_elem->min_data_rate >= BIT(24) ||
+ qos_elem->delay_bound >= BIT(24)) {
+ wpa_printf(MSG_ERROR,
+ "Invalid min_data_rate or delay_bound");
+ goto free_scs_desc;
+ }
+
scs_desc_end:
n1 = os_realloc(scs_data->scs_desc_elems, (num_scs_desc + 1) *
sizeof(struct scs_desc_elem));
}
+static bool tclas_elem_required(const struct qos_characteristics *qos_elem)
+{
+ if (!qos_elem || !qos_elem->available)
+ return true;
+
+ if (qos_elem->direction == SCS_DIRECTION_DOWN)
+ return true;
+
+ return false;
+}
+
+
static int wpas_populate_scs_descriptor_ie(struct scs_desc_elem *desc_elem,
- struct wpabuf *buf)
+ struct wpabuf *buf,
+ bool allow_scs_traffic_desc)
{
u8 *len, *len1;
struct tclas_element *tclas_elem;
unsigned int i;
+ struct qos_characteristics *qos_elem;
+ u32 control_info = 0;
/* SCS Descriptor element */
wpabuf_put_u8(buf, WLAN_EID_SCS_DESCRIPTOR);
if (desc_elem->request_type == SCS_REQ_REMOVE)
goto end;
+ if (!tclas_elem_required(&desc_elem->qos_char_elem))
+ goto skip_tclas_elem;
+
if (desc_elem->intra_access_priority || desc_elem->scs_up_avail) {
wpabuf_put_u8(buf, WLAN_EID_INTRA_ACCESS_CATEGORY_PRIORITY);
wpabuf_put_u8(buf, 1);
wpabuf_put_u8(buf, desc_elem->tclas_processing);
}
+skip_tclas_elem:
+ if (allow_scs_traffic_desc && desc_elem->qos_char_elem.available) {
+ qos_elem = &desc_elem->qos_char_elem;
+ /* Element ID, Length, and Element ID Extension */
+ wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
+ len1 = wpabuf_put(buf, 1);
+ wpabuf_put_u8(buf, WLAN_EID_EXT_QOS_CHARACTERISTICS);
+
+ /* IEEE P802.11be/D4.0, 9.4.2.316 QoS Characteristics element,
+ * Figure 9-1001av (Control Info field format)
+ */
+ control_info = ((u32) qos_elem->direction <<
+ EHT_QOS_CONTROL_INFO_DIRECTION_OFFSET);
+ control_info |= ((u32) desc_elem->intra_access_priority <<
+ EHT_QOS_CONTROL_INFO_TID_OFFSET);
+ control_info |= ((u32) desc_elem->intra_access_priority <<
+ EHT_QOS_CONTROL_INFO_USER_PRIORITY_OFFSET);
+ control_info |= ((u32) qos_elem->mask <<
+ EHT_QOS_CONTROL_INFO_PRESENCE_MASK_OFFSET);
+
+ /* Control Info */
+ wpabuf_put_le32(buf, control_info);
+ /* Minimum Service Interval */
+ wpabuf_put_le32(buf, qos_elem->min_si);
+ /* Maximum Service Interval */
+ wpabuf_put_le32(buf, qos_elem->max_si);
+ /* Minimum Data Rate */
+ wpabuf_put_le24(buf, qos_elem->min_data_rate);
+ /* Delay Bound */
+ wpabuf_put_le24(buf, qos_elem->delay_bound);
+
+ *len1 = (u8 *) wpabuf_put(buf, 0) - len1 - 1;
+ }
+
end:
*len = (u8 *) wpabuf_put(buf, 0) - len - 1;
return 0;
}
+static size_t qos_char_len(const struct qos_characteristics *qos_elem)
+{
+ size_t buf_len = 0;
+
+ buf_len += 1 + /* Element ID */
+ 1 + /* Length */
+ 1 + /* Element ID Extension */
+ 4 + /* Control Info */
+ 4 + /* Minimum Service Interval */
+ 4 + /* Maximum Service Interval */
+ 3 + /* Minimum Data Rate */
+ 3; /* Delay Bound */
+
+ return buf_len;
+}
+
+
static struct wpabuf * allocate_scs_buf(struct scs_desc_elem *desc_elem,
- unsigned int num_scs_desc)
+ unsigned int num_scs_desc,
+ bool allow_scs_traffic_desc)
{
struct wpabuf *buf;
size_t buf_len = 0;
if (desc_elem->request_type == SCS_REQ_REMOVE)
continue;
+ if (allow_scs_traffic_desc &&
+ desc_elem->qos_char_elem.available)
+ buf_len += qos_char_len(&desc_elem->qos_char_elem);
+
+ if (!tclas_elem_required(&desc_elem->qos_char_elem))
+ continue;
+
if (desc_elem->intra_access_priority || desc_elem->scs_up_avail)
buf_len += 3;
{
struct wpabuf *buf = NULL;
struct scs_desc_elem *desc_elem = NULL;
+ const struct ieee80211_eht_capabilities *eht;
+ const u8 *eht_ie;
int ret = -1;
unsigned int i;
+ bool allow_scs_traffic_desc = false;
if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_s->current_ssid)
return -1;
if (!desc_elem)
return -1;
+ if (wpa_is_non_eht_scs_traffic_desc_supported(wpa_s->current_bss))
+ allow_scs_traffic_desc = true;
+
+ /* Allow SCS Traffic descriptor support for EHT connection */
+ eht_ie = wpa_bss_get_ie_ext(wpa_s->current_bss,
+ WLAN_EID_EXT_EHT_CAPABILITIES);
+ if (wpa_s->connection_eht && eht_ie &&
+ eht_ie[1] >= 1 + IEEE80211_EHT_CAPAB_MIN_LEN) {
+ eht = (const struct ieee80211_eht_capabilities *) &eht_ie[3];
+ if (eht->mac_cap & EHT_MACCAP_SCS_TRAFFIC_DESC)
+ allow_scs_traffic_desc = true;
+ }
+
+ if (!allow_scs_traffic_desc && desc_elem->qos_char_elem.available) {
+ wpa_dbg(wpa_s, MSG_INFO,
+ "Connection does not support EHT/non-EHT SCS Traffic Description - could not send SCS Request with QoS Characteristics");
+ return -1;
+ }
+
buf = allocate_scs_buf(desc_elem,
- wpa_s->scs_robust_av_req.num_scs_desc);
+ wpa_s->scs_robust_av_req.num_scs_desc,
+ allow_scs_traffic_desc);
if (!buf)
return -1;
for (i = 0; i < wpa_s->scs_robust_av_req.num_scs_desc;
i++, desc_elem++) {
/* SCS Descriptor element */
- if (wpas_populate_scs_descriptor_ie(desc_elem, buf) < 0)
+ if (wpas_populate_scs_descriptor_ie(desc_elem, buf,
+ allow_scs_traffic_desc) < 0)
goto end;
}