SCS: Add support for QoS Characteristics in SCS request
Add support to configure the mandatory QoS Characteristics parameters per IEEE P802.11be/D4.0, 9.4.2.316 (QoS Characteristics element), in SCS request: - Minimum Service Interval - Maximum Service Interval - Minimum Data Rate - Delay Bound Enable STA SCS traffic descriptor support for EHT connection when the connected EHT AP advertises SCS traffic descriptor capability in the EHT Capabilities element. Signed-off-by: Purushottam Kushwaha <quic_pkushwah@quicinc.com>
This commit is contained in:
parent
c437665041
commit
33da386553
4 changed files with 216 additions and 7 deletions
|
@ -501,6 +501,7 @@
|
||||||
#define WLAN_EID_EXT_EHT_CAPABILITIES 108
|
#define WLAN_EID_EXT_EHT_CAPABILITIES 108
|
||||||
#define WLAN_EID_EXT_TID_TO_LINK_MAPPING 109
|
#define WLAN_EID_EXT_TID_TO_LINK_MAPPING 109
|
||||||
#define WLAN_EID_EXT_MULTI_LINK_TRAFFIC_INDICATION 110
|
#define WLAN_EID_EXT_MULTI_LINK_TRAFFIC_INDICATION 110
|
||||||
|
#define WLAN_EID_EXT_QOS_CHARACTERISTICS 113
|
||||||
#define WLAN_EID_EXT_AKM_SUITE_SELECTOR 114
|
#define WLAN_EID_EXT_AKM_SUITE_SELECTOR 114
|
||||||
|
|
||||||
/* Extended Capabilities field */
|
/* Extended Capabilities field */
|
||||||
|
@ -2768,6 +2769,26 @@ enum scs_request_type {
|
||||||
SCS_REQ_CHANGE = 2,
|
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 */
|
/* Optional subelement IDs for MSCS Descriptor element */
|
||||||
enum mscs_description_subelem {
|
enum mscs_description_subelem {
|
||||||
MCSC_SUBELEM_STATUS = 1,
|
MCSC_SUBELEM_STATUS = 1,
|
||||||
|
|
|
@ -11528,7 +11528,11 @@ static int wpas_ctrl_iface_configure_scs(struct wpa_supplicant *wpa_s,
|
||||||
* [scs_id=<decimal number>] <add|remove|change> [scs_up=<0-7>]
|
* [scs_id=<decimal number>] <add|remove|change> [scs_up=<0-7>]
|
||||||
* [classifier_type=<4|10>]
|
* [classifier_type=<4|10>]
|
||||||
* [classifier params based on classifier type]
|
* [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=");
|
pos1 = os_strstr(cmd, "scs_id=");
|
||||||
if (!pos1) {
|
if (!pos1) {
|
||||||
|
@ -11543,7 +11547,8 @@ static int wpas_ctrl_iface_configure_scs(struct wpa_supplicant *wpa_s,
|
||||||
struct active_scs_elem *active_scs_desc;
|
struct active_scs_elem *active_scs_desc;
|
||||||
char *next_scs_desc;
|
char *next_scs_desc;
|
||||||
unsigned int num_tclas_elem = 0;
|
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);
|
desc_elem.scs_id = atoi(pos1 + 7);
|
||||||
pos1 += 7;
|
pos1 += 7;
|
||||||
|
@ -11619,8 +11624,9 @@ static int wpas_ctrl_iface_configure_scs(struct wpa_supplicant *wpa_s,
|
||||||
pos = os_strstr(pos1, "classifier_type=");
|
pos = os_strstr(pos1, "classifier_type=");
|
||||||
if (!pos) {
|
if (!pos) {
|
||||||
wpa_printf(MSG_ERROR, "classifier type empty");
|
wpa_printf(MSG_ERROR, "classifier type empty");
|
||||||
goto free_scs_desc;
|
goto qos_characteristics;
|
||||||
}
|
}
|
||||||
|
tclas_present = true;
|
||||||
|
|
||||||
while (pos) {
|
while (pos) {
|
||||||
struct tclas_element elem = { 0 }, *n;
|
struct tclas_element elem = { 0 }, *n;
|
||||||
|
@ -11684,6 +11690,68 @@ static int wpas_ctrl_iface_configure_scs(struct wpa_supplicant *wpa_s,
|
||||||
desc_elem.tclas_processing = val;
|
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:
|
scs_desc_end:
|
||||||
n1 = os_realloc(scs_data->scs_desc_elems, (num_scs_desc + 1) *
|
n1 = os_realloc(scs_data->scs_desc_elems, (num_scs_desc + 1) *
|
||||||
sizeof(struct scs_desc_elem));
|
sizeof(struct scs_desc_elem));
|
||||||
|
|
|
@ -97,12 +97,27 @@ static int wpas_populate_type10_classifier(struct type10_params *type10_param,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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,
|
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;
|
u8 *len, *len1;
|
||||||
struct tclas_element *tclas_elem;
|
struct tclas_element *tclas_elem;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
struct qos_characteristics *qos_elem;
|
||||||
|
u32 control_info = 0;
|
||||||
|
|
||||||
/* SCS Descriptor element */
|
/* SCS Descriptor element */
|
||||||
wpabuf_put_u8(buf, WLAN_EID_SCS_DESCRIPTOR);
|
wpabuf_put_u8(buf, WLAN_EID_SCS_DESCRIPTOR);
|
||||||
|
@ -112,6 +127,9 @@ static int wpas_populate_scs_descriptor_ie(struct scs_desc_elem *desc_elem,
|
||||||
if (desc_elem->request_type == SCS_REQ_REMOVE)
|
if (desc_elem->request_type == SCS_REQ_REMOVE)
|
||||||
goto end;
|
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) {
|
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, WLAN_EID_INTRA_ACCESS_CATEGORY_PRIORITY);
|
||||||
wpabuf_put_u8(buf, 1);
|
wpabuf_put_u8(buf, 1);
|
||||||
|
@ -164,6 +182,40 @@ static int wpas_populate_scs_descriptor_ie(struct scs_desc_elem *desc_elem,
|
||||||
wpabuf_put_u8(buf, desc_elem->tclas_processing);
|
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:
|
end:
|
||||||
*len = (u8 *) wpabuf_put(buf, 0) - len - 1;
|
*len = (u8 *) wpabuf_put(buf, 0) - len - 1;
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -273,8 +325,26 @@ static size_t tclas_elem_len(const struct tclas_element *elem)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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,
|
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;
|
struct wpabuf *buf;
|
||||||
size_t buf_len = 0;
|
size_t buf_len = 0;
|
||||||
|
@ -292,6 +362,13 @@ static struct wpabuf * allocate_scs_buf(struct scs_desc_elem *desc_elem,
|
||||||
if (desc_elem->request_type == SCS_REQ_REMOVE)
|
if (desc_elem->request_type == SCS_REQ_REMOVE)
|
||||||
continue;
|
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)
|
if (desc_elem->intra_access_priority || desc_elem->scs_up_avail)
|
||||||
buf_len += 3;
|
buf_len += 3;
|
||||||
|
|
||||||
|
@ -369,8 +446,11 @@ int wpas_send_scs_req(struct wpa_supplicant *wpa_s)
|
||||||
{
|
{
|
||||||
struct wpabuf *buf = NULL;
|
struct wpabuf *buf = NULL;
|
||||||
struct scs_desc_elem *desc_elem = NULL;
|
struct scs_desc_elem *desc_elem = NULL;
|
||||||
|
const struct ieee80211_eht_capabilities *eht;
|
||||||
|
const u8 *eht_ie;
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
bool allow_scs_traffic_desc = false;
|
||||||
|
|
||||||
if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_s->current_ssid)
|
if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_s->current_ssid)
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -385,8 +465,28 @@ int wpas_send_scs_req(struct wpa_supplicant *wpa_s)
|
||||||
if (!desc_elem)
|
if (!desc_elem)
|
||||||
return -1;
|
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,
|
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)
|
if (!buf)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
@ -400,7 +500,8 @@ int wpas_send_scs_req(struct wpa_supplicant *wpa_s)
|
||||||
for (i = 0; i < wpa_s->scs_robust_av_req.num_scs_desc;
|
for (i = 0; i < wpa_s->scs_robust_av_req.num_scs_desc;
|
||||||
i++, desc_elem++) {
|
i++, desc_elem++) {
|
||||||
/* SCS Descriptor element */
|
/* 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;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -596,6 +596,24 @@ struct tclas_element {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct qos_characteristics {
|
||||||
|
bool available;
|
||||||
|
|
||||||
|
/* Control Info Direction */
|
||||||
|
u8 direction;
|
||||||
|
/* Presence Bitmap Of Additional Parameters */
|
||||||
|
u16 mask;
|
||||||
|
/* Minimum Service Interval */
|
||||||
|
u32 min_si;
|
||||||
|
/* Maximum Service Interval */
|
||||||
|
u32 max_si;
|
||||||
|
/* Minimum Data Rate */
|
||||||
|
u32 min_data_rate;
|
||||||
|
/* Delay Bound */
|
||||||
|
u32 delay_bound;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
struct scs_desc_elem {
|
struct scs_desc_elem {
|
||||||
u8 scs_id;
|
u8 scs_id;
|
||||||
enum scs_request_type request_type;
|
enum scs_request_type request_type;
|
||||||
|
@ -604,6 +622,7 @@ struct scs_desc_elem {
|
||||||
struct tclas_element *tclas_elems;
|
struct tclas_element *tclas_elems;
|
||||||
unsigned int num_tclas_elem;
|
unsigned int num_tclas_elem;
|
||||||
u8 tclas_processing;
|
u8 tclas_processing;
|
||||||
|
struct qos_characteristics qos_char_elem;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue