/******************************************************************************
|
*
|
* Copyright(c) 2019 - 2021 Realtek Corporation.
|
*
|
* This program is free software; you can redistribute it and/or modify it
|
* under the terms of version 2 of the GNU General Public License as
|
* published by the Free Software Foundation.
|
*
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
* more details.
|
*
|
*****************************************************************************/
|
#define _PHL_IE_C_
|
#include "phl_headers.h"
|
|
u8 _phl_build_ml_common_info(struct rtw_phl_com_t *phl_com,
|
struct rtw_phl_ml_ie_info *info,
|
u8 *pbuf,
|
u8 *ml_ctrl)
|
{
|
struct rtw_phl_stainfo_t *sta = NULL;
|
void *phl = phl_com->phl_priv;
|
void *drv = phl_com->drv_priv;
|
u8 len = 0;
|
u8 *pstart = pbuf;
|
u8 *p = pbuf + 1; /* skip Common Info Length subfield */
|
|
if (info->rlink == NULL) {
|
PHL_WARN("%s: wrong rlink assignment!\n", __func__);
|
goto _exit;
|
}
|
|
sta = rtw_phl_get_stainfo_self(phl, info->rlink);
|
|
/* subfields */
|
switch (GET_ML_ELE_ML_CTRL_TYPE(ml_ctrl)) {
|
case BASIC_ML:
|
/* MLD Mac Address subfield */
|
_os_mem_cpy(drv, p, info->rlink->wrole->mac_addr, ETH_ALEN);
|
p += ETH_ALEN;
|
|
if (GET_ML_ELE_ML_CTRL_LINK_ID_INFO_PRESENT(ml_ctrl)) {
|
SET_ML_ELE_COMMON_INFO_LINK_ID(p, sta->link_id);
|
p += 1;
|
}
|
if (GET_ML_ELE_ML_CTRL_BSS_PARAMS_CHG_CNT_PRESENT(ml_ctrl)) {
|
u8 val = info->rlink->bss_params_chg_cnt;
|
|
SET_ML_ELE_COMMON_INFO_BSS_PARAMS_CHG_CNT(p, val);
|
p += 1;
|
}
|
if (GET_ML_ELE_ML_CTRL_MEDIUM_SYNC_DELAY_INFO_PRESENT(ml_ctrl)) {
|
p += 2;
|
}
|
if (GET_ML_ELE_ML_CTRL_EML_CAP_PRESENT(ml_ctrl)) {
|
p += 3;
|
}
|
if (GET_ML_ELE_ML_CTRL_MLD_CAP_PRESENT(ml_ctrl)) {
|
p += 2;
|
}
|
break;
|
case PROBE_REQUEST_ML:
|
if (GET_ML_ELE_ML_CTRL_MLD_ID_PRESENT(ml_ctrl)) {
|
SET_ML_ELE_COMMON_INFO_MLD_ID(p, info->mld_id);
|
p += 1;
|
}
|
break;
|
default:
|
break;
|
}
|
|
_exit:
|
len = (u8)(p - pstart);
|
|
*pstart = len; /* Common Info Length */
|
|
return len;
|
}
|
|
u8 _phl_build_basic_ml_ie(struct rtw_phl_com_t *phl_com,
|
struct rtw_phl_ml_ie_info *info,
|
u8 *pbuf,
|
u8 *ml_ctrl)
|
{
|
void *drv = phl_com->drv_priv;
|
u8 len = 0;
|
u8 *pstart = pbuf;
|
u8 *p = pbuf;
|
|
/* Element ID Extension field */
|
*p++ = EID_EXT_MULTI_LINK;
|
|
/* ML Control field */
|
_os_mem_cpy(drv, p, ml_ctrl, 2);
|
p += 2;
|
|
/* ML Common Info field */
|
p += _phl_build_ml_common_info(phl_com, info, p, ml_ctrl);
|
|
/* optional subelements */
|
if (info->opt_len) {
|
_os_mem_cpy(drv, p, info->opt, info->opt_len);
|
p += info->opt_len;
|
}
|
|
len = (u8)(p - pstart);
|
|
return len;
|
}
|
|
u8 _phl_build_probe_request_ml_ie(struct rtw_phl_com_t *phl_com,
|
struct rtw_phl_ml_ie_info *info,
|
u8 *pbuf,
|
u8 *ml_ctrl)
|
{
|
void *drv = phl_com->drv_priv;
|
u8 len = 0;
|
u8 *pstart = pbuf;
|
u8 *p = pbuf;
|
|
/* Element ID Extension field */
|
*p++ = EID_EXT_MULTI_LINK;
|
|
/* ML Control field */
|
_os_mem_cpy(drv, p, ml_ctrl, 2);
|
p += 2;
|
|
/* ML Common Info field */
|
p += _phl_build_ml_common_info(phl_com, info, p, ml_ctrl);
|
|
/* optional subelements */
|
if (info->opt_len) {
|
_os_mem_cpy(drv, p, info->opt, info->opt_len);
|
p += info->opt_len;
|
}
|
|
len = (u8)(p - pstart);
|
|
return len;
|
}
|
|
u8 rtw_phl_build_ml_ie(struct rtw_phl_com_t *phl_com,
|
struct rtw_phl_ml_ie_info *info,
|
u8 *pbuf)
|
{
|
u8 len = 0;
|
u8 *pstart = pbuf;
|
u8 *p = pbuf + 2;
|
u8 ml_ctrl[2] = {0};
|
|
/* set ML Control field in advance */
|
switch (info->pkt_type) {
|
case PACKET_BEACON:
|
if (info->critical_update)
|
info->rlink->bss_params_chg_cnt++;
|
fallthrough;
|
case PACKET_PROBE_RESPONSE:
|
case PACKET_PROBE_RESPONSE_ML:
|
SET_ML_ELE_ML_CTRL_TYPE(ml_ctrl, BASIC_ML);
|
SET_ML_ELE_ML_CTRL_LINK_ID_INFO_PRESENT(ml_ctrl, true);
|
SET_ML_ELE_ML_CTRL_BSS_PARAMS_CHG_CNT_PRESENT(ml_ctrl, true);
|
len = _phl_build_basic_ml_ie(phl_com, info, p, ml_ctrl);
|
break;
|
case PACKET_PROBE_REQUEST_ML:
|
SET_ML_ELE_ML_CTRL_TYPE(ml_ctrl, PROBE_REQUEST_ML);
|
if (info->mld_id_present)
|
SET_ML_ELE_ML_CTRL_MLD_ID_PRESENT(ml_ctrl, true);
|
|
len = _phl_build_probe_request_ml_ie(phl_com,
|
info,
|
p,
|
ml_ctrl);
|
break;
|
case PACKET_AUTH:
|
SET_ML_ELE_ML_CTRL_TYPE(ml_ctrl, BASIC_ML);
|
/* LINK_ID_INFO, BSS_PARAMS_CHG_CNT, MEDIUM_SYNC_DELAY_INFO are false */
|
len = _phl_build_basic_ml_ie(phl_com, info, p, ml_ctrl);
|
break;
|
case PACKET_ASSOC_REQUEST:
|
SET_ML_ELE_ML_CTRL_TYPE(ml_ctrl, BASIC_ML);
|
/* LINK_ID_INFO, BSS_PARAMS_CHG_CNT, MEDIUM_SYNC_DELAY_INFO are false */
|
SET_ML_ELE_ML_CTRL_EML_CAP_PRESENT(ml_ctrl, true);
|
SET_ML_ELE_ML_CTRL_MLD_CAP_PRESENT(ml_ctrl, true);
|
len = _phl_build_basic_ml_ie(phl_com, info, p, ml_ctrl);
|
break;
|
case PACKET_ASSOC_RESPONSE:
|
SET_ML_ELE_ML_CTRL_TYPE(ml_ctrl, BASIC_ML);
|
SET_ML_ELE_ML_CTRL_LINK_ID_INFO_PRESENT(ml_ctrl, true);
|
SET_ML_ELE_ML_CTRL_BSS_PARAMS_CHG_CNT_PRESENT(ml_ctrl, true);
|
SET_ML_ELE_ML_CTRL_EML_CAP_PRESENT(ml_ctrl, true);
|
SET_ML_ELE_ML_CTRL_MLD_CAP_PRESENT(ml_ctrl, true);
|
len = _phl_build_basic_ml_ie(phl_com, info, p, ml_ctrl);
|
break;
|
default:
|
break;
|
}
|
|
*pstart = EID_EXTENSION;
|
*(pstart + 1) = len;
|
len += 2;
|
|
return len;
|
}
|
|
u8 _phl_build_sta_info(struct rtw_phl_com_t *phl_com,
|
struct rtw_phl_per_sta_profile_info *info,
|
u8 *pbuf,
|
u8 *sta_ctrl)
|
{
|
void *drv = phl_com->drv_priv;
|
u8 len = 0;
|
u8 *pstart = pbuf;
|
u8 *p = pbuf + 1; /* skip STA Info Length subfield */
|
|
/* subfields */
|
if (GET_STA_CTRL_MAC_ADDR_PRESENT(sta_ctrl)) {
|
_os_mem_cpy(drv, p, info->rlink->mac_addr, ETH_ALEN);
|
p += ETH_ALEN;
|
}
|
if (GET_STA_CTRL_BEACON_INTEREVAL_PRESENT(sta_ctrl)) {
|
#ifdef RTW_PHL_BCN
|
u16 bcn_interval = (u16)info->rlink->bcn_cmn.bcn_interval;
|
#else
|
u16 bcn_interval = 100;
|
#endif
|
|
_os_mem_cpy(drv, p, &(bcn_interval), 2);
|
p += 2;
|
}
|
if (GET_STA_CTRL_DTIM_INFO_PRESENT(sta_ctrl)) {
|
u16 dtim_info = 0;
|
u8 dtim_cnt = 0;
|
u8 dtim_period = 0;
|
|
/* DTIM cnt should be refine later if DTIM period is not 1 */
|
dtim_period = (u8)info->rlink->dtim_period;
|
dtim_info = (dtim_period << 8) | dtim_cnt;
|
_os_mem_cpy(drv, p, &(dtim_info), 2);
|
p += 2;
|
}
|
if (GET_STA_CTRL_NSTR_LINK_PAIR_PRESENT(sta_ctrl)) {
|
if (GET_STA_CTRL_NSTR_BITMAP_SIZE(sta_ctrl) == 0) {
|
/* TODO */
|
p += 1;
|
}
|
else if (GET_STA_CTRL_NSTR_BITMAP_SIZE(sta_ctrl) == 1) {
|
/* TODO */
|
p += 2;
|
}
|
}
|
|
len = (u8)(p - pstart);
|
|
*pstart = len; /* STA Info Length */
|
|
return len;
|
}
|
|
u8 rtw_phl_build_per_sta_profile(struct rtw_phl_com_t *phl_com,
|
struct rtw_phl_per_sta_profile_info *info,
|
u8 *pbuf)
|
{
|
struct rtw_phl_stainfo_t *sta = NULL;
|
void *phl = phl_com->phl_priv;
|
void *drv = phl_com->drv_priv;
|
u8 len = 0, total_len = 0;
|
u8 *pstart = pbuf;
|
u8 *p = pbuf + 2;
|
u8 sta_ctrl[2] = {0};
|
|
if (info->rlink == NULL) {
|
PHL_WARN("%s: wrong rlink assignment!\n", __func__);
|
return total_len;
|
}
|
|
sta = rtw_phl_get_stainfo_self(phl, info->rlink);
|
|
/* STA Control field */
|
switch (info->pkt_type) {
|
case PACKET_BEACON:
|
case PACKET_PROBE_RESPONSE:
|
SET_STA_CTRL_LINK_ID(sta_ctrl, sta->link_id);
|
break;
|
case PACKET_PROBE_RESPONSE_ML:
|
SET_STA_CTRL_LINK_ID(sta_ctrl, sta->link_id);
|
if (info->complete_profile) {
|
SET_STA_CTRL_COMPLETE_PROFILE(sta_ctrl, true);
|
SET_STA_CTRL_MAC_ADDR_PRESENT(sta_ctrl, true);
|
SET_STA_CTRL_BEACON_INTEREVAL_PRESENT(sta_ctrl, true);
|
SET_STA_CTRL_DTIM_INFO_PRESENT(sta_ctrl, true);
|
}
|
break;
|
case PACKET_ASSOC_RESPONSE:
|
SET_STA_CTRL_LINK_ID(sta_ctrl, sta->link_id);
|
SET_STA_CTRL_COMPLETE_PROFILE(sta_ctrl, true);
|
SET_STA_CTRL_MAC_ADDR_PRESENT(sta_ctrl, true);
|
SET_STA_CTRL_BEACON_INTEREVAL_PRESENT(sta_ctrl, true);
|
SET_STA_CTRL_DTIM_INFO_PRESENT(sta_ctrl, true);
|
break;
|
case PACKET_PROBE_REQUEST_ML:
|
SET_STA_CTRL_LINK_ID(sta_ctrl, info->link_id);
|
if (info->complete_profile) {
|
SET_STA_CTRL_COMPLETE_PROFILE(sta_ctrl, true);
|
}
|
break;
|
case PACKET_ASSOC_REQUEST:
|
SET_STA_CTRL_LINK_ID(sta_ctrl, info->link_id);
|
SET_STA_CTRL_COMPLETE_PROFILE(sta_ctrl, true);
|
SET_STA_CTRL_MAC_ADDR_PRESENT(sta_ctrl, true);
|
break;
|
default:
|
break;
|
}
|
|
/* STA Control field */
|
_os_mem_cpy(drv, p, sta_ctrl, 2);
|
p += 2;
|
total_len += 2;
|
|
if (info->pkt_type != PACKET_PROBE_REQUEST_ML) {
|
/* STA Info field */
|
len = _phl_build_sta_info(phl_com, info, p, sta_ctrl);
|
p += len;
|
total_len += len;
|
}
|
|
/* STA Profile field */
|
if (info->sta_profile_len) {
|
_os_mem_cpy(drv, p, info->sta_profile, info->sta_profile_len);
|
total_len += info->sta_profile_len;
|
}
|
|
*pstart = WLAN_SUBEID_PER_STA_PROFILE;
|
*(pstart + 1) = total_len;
|
total_len += 2;
|
|
return total_len;
|
}
|
|
void _set_link_mapping(u8 *ele_pos,
|
u16 *tid2link,
|
u8 *indicator,
|
u8 *total_len)
|
{
|
u8 idx = 0;
|
u8 *ele_linkmap = ele_pos;
|
|
*indicator = 0;
|
for(idx = 0; idx < WMM_AC_TID_NUM; idx++) {
|
if(tid2link[idx]) {
|
SET_LINK_MAPPING_TID(ele_linkmap, tid2link[idx]);
|
ele_linkmap +=2;
|
*total_len += 2;
|
*indicator |= BIT(idx);
|
}
|
}
|
}
|
|
/*
|
* (Re)Association Request/Response
|
* Tid-To-Link Mapping Request/Response action frame
|
*/
|
u8 rtw_phl_build_tid2link(struct rtw_wifi_role_link_t *rlink,
|
u8 *ele_start)
|
{
|
struct protocol_cap_t *protocol_cap = &rlink->protocol_cap;
|
u8 *ele_pos = NULL, *ele_linkmap = 0;
|
u8 def_mapping = true, indicator = 0, idx = 0;
|
u8 total_len = 0;
|
|
/* Element ID */
|
ele_start[0] = EID_EXTENSION;
|
|
/* Element ID Extension field */
|
ele_start[2] = EID_EXT_TID_TO_LINK_MAPPING;
|
|
for(idx = 0; idx < WMM_AC_TID_NUM; idx++) {
|
if(protocol_cap->tid2link_ul[idx] != 0x7fff ||
|
protocol_cap->tid2link_dl[idx] != 0x7fff ) {
|
def_mapping = false;
|
break;
|
}
|
}
|
|
ele_pos = ele_start+3; /* start of tid2link control */
|
if(def_mapping) {
|
SET_TID2LINK_CTRL_DIRECT(ele_pos, 2);
|
SET_TID2LINK_CTRL_DEFAULT(ele_pos, 1);
|
SET_TID2LINK_CTRL_INDIC(ele_pos, indicator); /* indicator is 0 */
|
total_len += 2;
|
}
|
else {
|
/* 0: uplink */
|
SET_TID2LINK_CTRL_DIRECT(ele_pos, 0);
|
SET_TID2LINK_CTRL_DEFAULT(ele_pos, 0);
|
total_len += 2;
|
ele_linkmap = ele_pos+2;
|
_set_link_mapping(ele_linkmap, protocol_cap->tid2link_ul, &indicator, &total_len);
|
SET_TID2LINK_CTRL_INDIC(ele_pos, indicator);
|
ele_pos = ele_pos + total_len;
|
|
/* 1: downlink */
|
SET_TID2LINK_CTRL_DIRECT(ele_pos, 1);
|
SET_TID2LINK_CTRL_DEFAULT(ele_pos, 0);
|
total_len += 2;
|
ele_linkmap = ele_pos+2;
|
_set_link_mapping(ele_linkmap, protocol_cap->tid2link_dl, &indicator, &total_len);
|
SET_TID2LINK_CTRL_INDIC(ele_pos, indicator);
|
}
|
|
ele_start[1]= total_len + 1; /* Ext ID field is included */
|
|
return (total_len +3); /* From Element ID/ Len/ Ext ID */
|
}
|
|
void
|
_dump_tid2link(struct rtw_phl_stainfo_t *sta)
|
{
|
u8 idx =0;
|
PHL_INFO("###### _dump_tid2link #######\n");
|
for (idx = 0; idx < WMM_AC_TID_NUM; idx++) {
|
PHL_INFO("\t[TID-%d] uplink mapp:0x%02X, downlink map:%02X\n",
|
idx, sta->asoc_cap.tid2link_ul[idx],
|
sta->asoc_cap.tid2link_dl[idx]);
|
|
if(!sta->asoc_cap.tid2link_ul[idx] && !sta->asoc_cap.tid2link_dl[idx])
|
PHL_ERR("\t[TID-%d]:: a TID shall be mapped to at least one setup link\n",
|
idx);
|
}
|
}
|
|
void rtw_phl_parse_tid2link(struct rtw_phl_stainfo_t *sta,
|
u8 *ele_start,
|
u16 ele_len)
|
{
|
u8 *ele_pos = NULL;
|
u8 direction = 0, def_mapping = 0, indicator = 0, idx = 0;
|
bool downlink = true, uplink = true;
|
|
ele_pos = ele_start;
|
do {
|
direction = GET_TID2LINK_CTRL_DIRECT(ele_pos);
|
downlink = (direction == 0)? false: true; /* 0: uplink */
|
uplink = (direction == 1)? false: true; /* 1: downlink */
|
def_mapping = GET_TID2LINK_CTRL_DEFAULT(ele_pos);
|
if (!def_mapping) {
|
|
indicator = GET_TID2LINK_CTRL_INDIC(ele_pos);
|
ele_pos += 2; /* skip mapping control */
|
for(idx = 0; idx < WMM_AC_TID_NUM; idx++) {
|
if(BIT(idx) & indicator) {
|
if(uplink)
|
sta->asoc_cap.tid2link_ul[idx] =
|
GET_LINK_MAPPING_TID(ele_pos);
|
if(downlink)
|
sta->asoc_cap.tid2link_dl[idx] =
|
GET_LINK_MAPPING_TID(ele_pos);
|
ele_pos += 2;
|
}
|
}
|
}
|
else {
|
ele_pos += 2; /* skip mapping control */
|
|
/* 35.3.6.1.2 Default mapping mode */
|
for(idx = 0; idx < WMM_AC_TID_NUM; idx++) {
|
/* Link id 15 : if the reported AP is not part of an AP MLD. */
|
sta->asoc_cap.tid2link_ul[idx] = 0x7fff;
|
sta->asoc_cap.tid2link_dl[idx] = 0x7fff;
|
}
|
}
|
} while(ele_pos < (ele_start + ele_len));
|
|
_dump_tid2link(sta);
|
|
phl_mld_link2tid(sta);
|
}
|
|
void rtw_phl_tid2link_not_present(struct rtw_phl_stainfo_t *sta, u8 nego)
|
{
|
struct protocol_cap_t *protocol_cap = &sta->rlink->protocol_cap;
|
u8 idx = 0;
|
u16 link_ul = 0, link_dl = 0;
|
|
for(idx = 0; idx < WMM_AC_TID_NUM; idx++) {
|
link_ul = 0x7fff;
|
link_dl = 0x7fff;
|
|
/* AP accept our tid2link negotiation */
|
if(nego) {
|
link_ul = protocol_cap->tid2link_ul[idx];
|
link_dl = protocol_cap->tid2link_dl[idx];
|
}
|
|
sta->asoc_cap.tid2link_ul[idx] = link_ul;
|
sta->asoc_cap.tid2link_dl[idx] = link_dl;
|
}
|
|
phl_mld_link2tid(sta);
|
}
|
|
void
|
_dump_per_sta_profile(struct rtw_phl_per_sta_profile_element ele)
|
{
|
PHL_INFO("###### _dump_per_sta_profile #######\n");
|
PHL_INFO("%-25s: %d\n", "Link ID", ele.link_id);
|
PHL_INFO("%-25s: %s\n", "Complete Profile", (ele.complete_profile == true)?"Yes":"No");
|
if (ele.mac_addr_present)
|
PHL_INFO("%-25s: %2x:%2x:%2x:%2x:%2x:%2x\n",
|
"MAC address",
|
ele.mac_addr[0],
|
ele.mac_addr[1],
|
ele.mac_addr[2],
|
ele.mac_addr[3],
|
ele.mac_addr[4],
|
ele.mac_addr[5]
|
);
|
else
|
PHL_INFO("%-25s: Not present\n", "MAC address");
|
|
if (ele.bcn_interval_present)
|
PHL_INFO("%-25s: %d\n", "Beacon Interval", ele.bcn_interval);
|
else
|
PHL_INFO("%-25s: Not present\n", "Beacon Interval");
|
|
if (ele.dtim_info_present) {
|
PHL_INFO("%-25s: %d\n", "DTIM Count", ele.dtim_cnt);
|
PHL_INFO("%-25s: %d\n", "DTIM Period", ele.dtim_period);
|
} else {
|
PHL_INFO("%-25s: Not present\n", "DTIM Info");
|
}
|
|
if (ele.nstr_link_pair_present) {
|
PHL_INFO("%-25s: %d\n", "NSTR Bitmap Size", ele.nstr_bitmap_size);
|
PHL_INFO("%-25s: 0x%X\n", "NSTR Indication Bitmap", ele.nstr_indication_bitmap);
|
} else {
|
PHL_INFO("%-25s: Not present\n", "NSTR Link Pair");
|
}
|
|
if (ele.sta_profile_len != 0) {
|
PHL_INFO("%-25s: %d\n", "STA Profile Length", ele.sta_profile_len);
|
PHL_INFO("%-25s: %s\n", "STA Profile Fragment", (ele.sta_profile_frag_len == 0)?"No":"Yes");
|
}
|
}
|
|
/*
|
*
|
* +--------+--------+----------+----------+-------------+
|
* | Subele | Length | STA Ctrl | STA Info | STA Profile |
|
* | ID | | | | |
|
* +--------+--------+----------+----------+-------------+
|
* |
|
* |
|
* ele_len v
|
* ^
|
* | len_before_frag 1 1 len_before_frag - ele_len
|
* +--------+---+----+-----------------+-----+------+-----------------+-------------+
|
* | Subele | Length | Data 0 | FID | FLEN | DATA 1 |
|
* | ID | | | | | |
|
* +--------+--------------------------+-----+------+-------------------------------+
|
* |
|
* v
|
* ele_pos
|
*/
|
void
|
phl_parse_per_sta_profile_ie(struct rtw_phl_com_t *phl_com,
|
u8 *ele_pos,
|
u16 ele_len,
|
u8 *ele_frag,
|
u16 len_before_frag,
|
struct rtw_phl_per_sta_profile_element *ele
|
)
|
{
|
void *d = phlcom_to_drvpriv(phl_com);
|
u8 tmp[STA_CTRL_LEN + MAX_STA_INFO_LEN] = {0};
|
u8 *sta_ctrl = tmp;
|
u8 *sta_info = tmp+2;
|
u8 sta_info_len = 0;
|
u8 sta_info_offset = 0;
|
|
/* Copy STA Ctrl and STA Info field to tmp for parsing */
|
if (len_before_frag == 0) {
|
/* Per-STA profile without fragment */
|
sta_info_len = ele_pos[STA_CTRL_LEN];
|
_os_mem_cpy(d, tmp, ele_pos, (STA_CTRL_LEN + sta_info_len));
|
} else {
|
/* Per-STA profile with fragment */
|
if (len_before_frag <= STA_CTRL_LEN)
|
sta_info_len = ele_frag[STA_CTRL_LEN - len_before_frag];
|
else
|
sta_info_len = ele_pos[STA_CTRL_LEN];
|
|
if (len_before_frag < (STA_CTRL_LEN + sta_info_len)) {
|
_os_mem_cpy(d, tmp, ele_pos, len_before_frag);
|
_os_mem_cpy(d,
|
(tmp + len_before_frag),
|
ele_frag,
|
(STA_CTRL_LEN + sta_info_len - len_before_frag));
|
} else {
|
_os_mem_cpy(d, tmp, ele_pos, (STA_CTRL_LEN + sta_info_len));
|
}
|
}
|
|
sta_info_offset++;
|
|
ele->link_id = (u8)GET_STA_CTRL_LINK_ID(sta_ctrl);
|
|
if (GET_STA_CTRL_COMPLETE_PROFILE(sta_ctrl)) {
|
ele->complete_profile = true;
|
}
|
|
if (GET_STA_CTRL_MAC_ADDR_PRESENT(sta_ctrl)) {
|
ele->mac_addr_present = true;
|
_os_mem_cpy(d, ele->mac_addr, (sta_info+sta_info_offset), MAC_ALEN);
|
sta_info_offset += 6;
|
}
|
|
if (GET_STA_CTRL_BEACON_INTEREVAL_PRESENT(sta_ctrl)) {
|
ele->bcn_interval_present = true;
|
ele->bcn_interval = LE_BITS_TO_2BYTE(sta_info+sta_info_offset, 0, 16);
|
sta_info_offset += 2;
|
}
|
|
if (GET_STA_CTRL_DTIM_INFO_PRESENT(sta_ctrl)) {
|
ele->dtim_info_present = true;
|
ele->dtim_cnt = LE_BITS_TO_1BYTE(sta_info+sta_info_offset, 0, 8);
|
sta_info_offset += 1;
|
ele->dtim_period = LE_BITS_TO_1BYTE(sta_info+sta_info_offset, 0, 8);
|
sta_info_offset += 1;
|
}
|
|
if (GET_STA_CTRL_NSTR_LINK_PAIR_PRESENT(sta_ctrl)) {
|
ele->nstr_link_pair_present = true;
|
if (GET_STA_CTRL_NSTR_BITMAP_SIZE(sta_ctrl)) {
|
ele->nstr_indication_bitmap = LE_BITS_TO_2BYTE(sta_info+sta_info_offset, 0, 16);
|
ele->nstr_bitmap_size = 2;
|
sta_info_offset += 2;
|
} else {
|
ele->nstr_indication_bitmap = LE_BITS_TO_1BYTE(sta_info+sta_info_offset, 0, 8);
|
ele->nstr_bitmap_size = 1;
|
sta_info_offset += 1;
|
}
|
}
|
|
if (len_before_frag == 0) {
|
/* Per-STA profile without fragment */
|
ele->sta_profile = ele_pos + STA_CTRL_LEN + sta_info_len;
|
} else if (len_before_frag <= (STA_CTRL_LEN + sta_info_len)) {
|
/* STA profile located in fragment part */
|
ele->sta_profile = ele_frag + (STA_CTRL_LEN + sta_info_len - len_before_frag + 2);
|
} else {
|
/* STA profile is truncated by fragment */
|
ele->sta_profile = ele_pos + STA_CTRL_LEN + sta_info_len;
|
ele->sta_profile_frag = ele_frag + 2;
|
ele->sta_profile_frag_len = (u8)(len_before_frag - (STA_CTRL_LEN + sta_info_len));
|
}
|
ele->sta_profile_len = (u8)(ele_len - STA_CTRL_LEN - sta_info_len);
|
|
_dump_per_sta_profile(*ele);
|
}
|
|
void
|
_parse_ml_link_info(struct rtw_phl_com_t *phl_com,
|
u8 *ie_buf,
|
u16 ie_len,
|
u16 link_info_offset,
|
struct rtw_phl_ml_element *ml_ele
|
)
|
{
|
u16 offset = link_info_offset;
|
u8 sub_eid = 0;
|
u8 *sub_ele = NULL, *sub_ele_frag = NULL;
|
u16 sub_ele_len = 0, sub_ele_len_before_frag = 0;
|
u16 len_before_frag = 0;
|
/* Exclude the Element ID extension field */
|
u16 next_frag_offset = MAX_ELE_LEN - 1;
|
|
do {
|
if (offset >= (ie_len-1))
|
break;
|
|
sub_ele_frag = NULL;
|
sub_ele_len_before_frag = 0;
|
len_before_frag = 0;
|
|
/* Check fragment */
|
len_before_frag = next_frag_offset - offset;
|
if (len_before_frag == 1) {
|
sub_eid = ie_buf[offset];
|
/* Skip FID and FLEN */
|
sub_ele_len = ie_buf[offset+3];
|
sub_ele = ie_buf + offset + 4;
|
} else if (len_before_frag == 2){
|
sub_eid = ie_buf[offset];
|
sub_ele_len = ie_buf[offset+1];
|
/* Skip FID and FLEN */
|
sub_ele = ie_buf + offset + 4;
|
} else {
|
sub_eid = ie_buf[offset];
|
sub_ele_len = ie_buf[offset+1];
|
sub_ele = ie_buf + offset + 2;
|
if (len_before_frag < (sub_ele_len + 2)) {
|
sub_ele_frag = ie_buf + offset + len_before_frag + 2;
|
sub_ele_len_before_frag = len_before_frag - 2;
|
}
|
}
|
/*
|
* Next fragment offset would be
|
* 255: Max element length
|
* 1: Fragment ID field
|
* 1: Fragment Length field
|
*/
|
if (len_before_frag <= (sub_ele_len + 2))
|
next_frag_offset += (MAX_ELE_LEN + 2);
|
|
if (sub_eid == WLAN_SUBEID_PER_STA_PROFILE) {
|
phl_parse_per_sta_profile_ie(phl_com,
|
sub_ele,
|
sub_ele_len,
|
sub_ele_frag,
|
sub_ele_len_before_frag,
|
&(ml_ele->profile[ml_ele->profile_num]));
|
ml_ele->profile_num++;
|
}
|
|
/* Move offset to the end of the element */
|
if (len_before_frag <= (sub_ele_len + 2)) {
|
/*
|
* Subele ID field (1) +
|
* Subele len field (1) +
|
* Fragment ID field (1) +
|
* Fragment len field (1) +
|
* Subele length
|
*/
|
offset += (sub_ele_len + 2 + 2);
|
} else {
|
/*
|
* Subele ID field (1) +
|
* Subele len field (1) +
|
* Subele length
|
*/
|
offset += (sub_ele_len + 2);
|
}
|
} while(1);
|
}
|
|
void
|
_dump_ml_basic(struct rtw_phl_ml_element ml_ele)
|
{
|
PHL_INFO("###### _dump_ml_basic #######\n");
|
if (ml_ele.common_info.basic_ml.link_id_info_present)
|
PHL_INFO("%-25s: %d\n", "Link ID", ml_ele.common_info.basic_ml.link_id);
|
else
|
PHL_INFO("%-25s: Not present\n", "Link ID");
|
if (ml_ele.common_info.basic_ml.bss_param_chg_cnt_present)
|
PHL_INFO("%-25s: %d\n", "BSS Param Chg Cnt", ml_ele.common_info.basic_ml.bss_param_chg_cnt);
|
else
|
PHL_INFO("%-25s: Not present\n", "BSS Param Chg Cnt");
|
if (ml_ele.common_info.basic_ml.msd_info_present)
|
PHL_INFO("%-25s: Present\n", "MSD Info");
|
else
|
PHL_INFO("%-25s: Not present\n", "MSD Info");
|
if (ml_ele.common_info.basic_ml.eml_cap_present)
|
PHL_INFO("%-25s: Present\n", "EML Capability");
|
else
|
PHL_INFO("%-25s: Not present\n", "EML Capability");
|
if (ml_ele.common_info.basic_ml.mld_cap_present) {
|
PHL_INFO("%-25s =>\n", "MLD Capability");
|
PHL_INFO("%-25s: %d\n", "Max Num of SL",
|
ml_ele.common_info.basic_ml.mld_cap.max_num_sl);
|
PHL_INFO("%-25s: %s\n", "SRS Support",
|
(ml_ele.common_info.basic_ml.mld_cap.srs_support == true)?"True":"False");
|
if (ml_ele.common_info.basic_ml.mld_cap.tid_to_link_nego_support == 0)
|
PHL_INFO("%-25s: %s\n",
|
"TID-To-Link Mapping Nego",
|
"Not support");
|
else if (ml_ele.common_info.basic_ml.mld_cap.tid_to_link_nego_support == 1)
|
PHL_INFO("%-25s: %s\n",
|
"TID-To-Link Mapping Nego",
|
"Same or different link set");
|
else
|
PHL_INFO("%-25s: %s\n",
|
"TID-To-Link Mapping Nego",
|
"Same link set only");
|
if (ml_ele.common_info.basic_ml.mld_cap.freq_sep_for_str)
|
PHL_INFO("%-25s: %dMHz\n", "Freq Separation for STR",
|
((ml_ele.common_info.basic_ml.mld_cap.freq_sep_for_str - 1) * 80));
|
PHL_INFO("%-25s: %s\n", "AAR Support",
|
(ml_ele.common_info.basic_ml.mld_cap.aar_support == true)?"True":"False");
|
} else {
|
PHL_INFO("%-25s: Not present\n", "MLD Capability");
|
}
|
}
|
|
void
|
_parse_ml_basic(struct rtw_phl_com_t *phl_com,
|
u8 *ie_buf,
|
u16 ie_len,
|
struct rtw_phl_ml_element *ml_ele
|
)
|
{
|
struct basic_ml *basic_ml = &(ml_ele->common_info.basic_ml);
|
u8 *ml_ctrl = ie_buf;
|
u8 *common_info = ie_buf+2;
|
u8 common_info_len = 0;
|
u8 common_info_offset = 0;
|
|
common_info_len = common_info[0];
|
common_info_offset++;
|
|
basic_ml->mld_address = common_info+1;
|
common_info_offset += 6;
|
|
if (GET_ML_ELE_ML_CTRL_LINK_ID_INFO_PRESENT(ml_ctrl)) {
|
basic_ml->link_id_info_present = true;
|
basic_ml->link_id = LE_BITS_TO_1BYTE(common_info+common_info_offset, 0, 4);
|
common_info_offset += 1;
|
}
|
|
if (GET_ML_ELE_ML_CTRL_BSS_PARAMS_CHG_CNT_PRESENT(ml_ctrl)) {
|
basic_ml->bss_param_chg_cnt_present = true;
|
basic_ml->bss_param_chg_cnt = LE_BITS_TO_1BYTE(common_info+common_info_offset, 0, 8);
|
common_info_offset += 1;
|
}
|
|
if (GET_ML_ELE_ML_CTRL_MEDIUM_SYNC_DELAY_INFO_PRESENT(ml_ctrl)) {
|
basic_ml->msd_info_present = true;
|
/* TODO: Parse MSD INFO */
|
common_info_offset += 2;
|
}
|
|
if (GET_ML_ELE_ML_CTRL_EML_CAP_PRESENT(ml_ctrl)) {
|
basic_ml->eml_cap_present = true;
|
/* TODO: Parse EML cap */
|
common_info_offset += 3;
|
}
|
|
if (GET_ML_ELE_ML_CTRL_MLD_CAP_PRESENT(ml_ctrl)) {
|
basic_ml->mld_cap_present = true;
|
basic_ml->mld_cap.max_num_sl = GET_MLD_CAP_MAX_NUM_OF_SL(common_info+common_info_offset);
|
basic_ml->mld_cap.srs_support = GET_MLD_CAP_SRS_SUPPORT(common_info+common_info_offset);
|
basic_ml->mld_cap.tid_to_link_nego_support = GET_MLD_CAP_TID_TO_LINK_NEGO_SUPPORT(common_info+common_info_offset);
|
basic_ml->mld_cap.freq_sep_for_str = GET_MLD_CAP_FREQ_SEP_FOR_STR(common_info+common_info_offset);
|
basic_ml->mld_cap.aar_support = GET_MLD_CAP_AAR_SUPPORT(common_info+common_info_offset);
|
common_info_offset += 2;
|
}
|
|
_dump_ml_basic(*ml_ele);
|
|
_parse_ml_link_info(phl_com,
|
ie_buf,
|
ie_len,
|
(2 + common_info_len),
|
ml_ele);
|
}
|
|
void
|
_dump_ml_probe_req(struct rtw_phl_ml_element ml_ele)
|
{
|
PHL_INFO("###### _dump_ml_probe_req #######\n");
|
if (ml_ele.common_info.probe_req_ml.mld_id_present)
|
PHL_INFO("%-25s: %d\n", "MLD ID", ml_ele.common_info.probe_req_ml.mld_id);
|
else
|
PHL_INFO("%-25s: Not present\n", "MLD ID");
|
}
|
|
void
|
_parse_ml_probe_req(struct rtw_phl_com_t *phl_com,
|
u8 *ie_buf,
|
u16 ie_len,
|
struct rtw_phl_ml_element *ml_ele
|
)
|
{
|
struct probe_req_ml *probe_req_ml = &(ml_ele->common_info.probe_req_ml);
|
u8 *ml_ctrl = ie_buf;
|
u8 *common_info = ie_buf+2;
|
u8 common_info_len = 0;
|
u8 common_info_offset = 0;
|
|
common_info_len = common_info[0];
|
common_info_offset++;
|
|
if (GET_ML_ELE_ML_CTRL_MLD_ID_PRESENT(ml_ctrl)) {
|
probe_req_ml->mld_id_present = true;
|
probe_req_ml->mld_id = LE_BITS_TO_1BYTE(common_info+common_info_offset, 0, 8);
|
common_info_offset += 1;
|
}
|
|
_dump_ml_probe_req(*ml_ele);
|
|
_parse_ml_link_info(phl_com,
|
ie_buf,
|
ie_len,
|
(2 + common_info_len),
|
ml_ele);
|
}
|
|
void
|
rtw_phl_parse_ml_ie(struct rtw_phl_com_t *phl_com,
|
u8 *ele_pos,
|
u16 ele_len,
|
struct rtw_phl_ml_element *ml_ele
|
)
|
{
|
if ((ele_pos == NULL) || (ele_len == 0))
|
return;
|
|
if (ml_ele == NULL)
|
return;
|
|
ml_ele->type = GET_ML_ELE_ML_CTRL_TYPE(ele_pos);
|
|
if (ml_ele->type == BASIC_ML) {
|
_parse_ml_basic(phl_com, ele_pos, ele_len, ml_ele);
|
} else if (ml_ele->type == PROBE_REQUEST_ML) {
|
_parse_ml_probe_req(phl_com, ele_pos, ele_len, ml_ele);
|
} else {
|
PHL_WARN("Unknown type!\n");
|
}
|
}
|
/*
|
* rtw_phl_get_ie: Return the total length of the element (include the fragment
|
* element)
|
*
|
* Ex. Element fragmentation without Element ID Extension
|
* ele_len = 255 + 1 + 1 + 255 + 1 + 1 +n
|
* |------------------------------------------|
|
* | |
|
* v 255 1 1 255 1 1 n v
|
* +------------------------------------------------------+
|
* | EID | 255 | Data | FID | 255 | Data | FID | n | Data |
|
* +------------------------------------------------------+
|
* ^
|
* |
|
* out_ele
|
* Ex. Element fragmentation with Element ID Extension
|
* ele_len = 254 + 1 + 1 + 255 + 1 + 1 +n
|
* +----------------+---+---+-----+---+-------+
|
* v v
|
* 1 254 1 1 255 1 1 n
|
* +-----+-----+-----+------+-----+-----+------+-----+---+------+
|
* | EID | 255 | EXT | Data | FID | 255 | Data | FID | n | Data |
|
* +-----+-----+-----+------+-----+-----+------+-----+---+------+
|
* ^
|
* +
|
* out_ele
|
*/
|
u16
|
rtw_phl_get_ie(u8 *ie_start,
|
u16 ies_len,
|
u8 target_id,
|
u8 target_ext_id,
|
u8 **out_ele
|
)
|
{
|
u16 offset = 0, frag_offset = 0;
|
u16 ele_len = 0;
|
u8 tmp_id = 0;
|
u8 tmp_ext_id = 0;
|
u16 tmp_ele_len = 0;
|
u8 frag_len = 0;
|
bool is_fragmentable = false;
|
|
do {
|
if ((offset + 2) >= ies_len)
|
break;
|
|
/* Get current element ID */
|
tmp_id = ie_start[offset];
|
tmp_ele_len = ie_start[offset+1];
|
if (tmp_id == EID_EXTENSION)
|
tmp_ext_id = ie_start[offset+2];
|
is_fragmentable = rtw_phl_is_ie_fragmentable(tmp_id, tmp_ext_id);
|
if (is_fragmentable) {
|
frag_offset = offset + 2 + tmp_ele_len;
|
frag_len = (u8)tmp_ele_len;
|
while (frag_len == 255) {
|
/*
|
* Check there is more data at the end of the IE
|
* and is followed by fragment element
|
*/
|
if (((frag_offset + 2) < ies_len) &&
|
(ie_start[frag_offset] == EID_FRAGMENT)){
|
frag_len = ie_start[frag_offset+1];
|
frag_offset += (2+frag_len);
|
tmp_ele_len += (2+frag_len);
|
}
|
}
|
}
|
|
/* Check element length is valid (ele length + N * frag length) */
|
if ((offset + 2 + tmp_ele_len) > ies_len) {
|
PHL_WARN("%s: Get invalid length!\n", __func__);
|
return 0;
|
}
|
if (target_id == tmp_id) {
|
if (target_id == EID_EXTENSION) {
|
/* Check Extension ID */
|
if (target_ext_id != tmp_ext_id) {
|
/* Extension ID is different */
|
offset += (tmp_ele_len + 2);
|
continue;
|
}
|
/* Return pointer of the first byte after extension ID */
|
*out_ele = ie_start + offset + 3;
|
/* Return length - extension id field (1) */
|
ele_len = tmp_ele_len - 1;
|
break;
|
}
|
/* Return pointer of the first byte after lenght field */
|
*out_ele = ie_start + offset + 2;
|
ele_len = tmp_ele_len;
|
break;
|
} else {
|
/* Element ID is different */
|
offset += (tmp_ele_len + 2);
|
}
|
} while(1);
|
|
return ele_len;
|
}
|
|
bool
|
rtw_phl_is_ie_fragmentable(u32 eid,
|
u32 eid_ext
|
)
|
{
|
bool ret = false;
|
|
if (eid == EID_FILS_INDICATION)
|
ret = true;
|
else if (eid == EID_EXTENSION) {
|
if ((eid_ext == EID_EXT_FILS_KEY_CONFIRM) ||
|
(eid_ext == EID_EXT_FILS_HLP_CONTAINER) ||
|
(eid_ext == EID_EXT_KEY_DELIVERY) ||
|
(eid_ext == EID_EXT_FILS_WRAPPED_DATA) ||
|
(eid_ext == EID_EXT_FILS_PUBLIC_KEY) ||
|
(eid_ext == EID_EXT_CDMG_EXTEND_SCHEDULE) ||
|
(eid_ext == EID_EXT_SSW_REPORT) ||
|
(eid_ext == EID_EXT_SPSH_REPORT) ||
|
(eid_ext == EID_EXT_GAS_EXTENSION) ||
|
(eid_ext == EID_EXT_MULTI_LINK) ||
|
(eid_ext == EID_EXT_TID_TO_LINK_MAPPING))
|
ret = true;
|
}
|
|
return ret;
|
}
|
|
/*
|
* Currently, the reported APs in Reduced Neighbor Report are
|
* affiliated with the same MLD as the reported AP. Therefore,
|
* MLD ID would be 0.
|
*/
|
u8 _build_mld_parameters(void *phl,
|
struct rtw_wifi_role_link_t *rlink,
|
u8 *pbuf)
|
{
|
struct rtw_phl_stainfo_t *sta = rtw_phl_get_stainfo_self(phl, rlink);
|
u8 *p = pbuf;
|
|
SET_MLD_PARAMS_MLD_ID(p, 0);
|
SET_MLD_PARAMS_LINK_ID(p, sta->link_id); /* = rlink->id for AP mode */
|
SET_MLD_PARAMS_BSS_PARAMS_CHG_CNT(p, rlink->bss_params_chg_cnt);
|
|
return 3;
|
}
|
|
/*
|
* Currently, the reported APs in Reduced Neighbor Report are
|
* affiliated with the same MLD as the reported AP.
|
*/
|
u8 rtw_phl_build_reduced_nb_rpt(struct rtw_wifi_role_t *wrole,
|
struct rtw_wifi_role_link_t *rlink,
|
u8 *pbuf)
|
{
|
void *phl = wrole->phl_com->phl_priv;
|
void *drv = wrole->phl_com->drv_priv;
|
u8 tbtt_info_cnt;
|
u8 tbtt_info_len;
|
u8 *pstart = pbuf;
|
u8 *p = pbuf + 2;
|
struct rtw_phl_mld_t *mld = rtw_phl_get_mld_self(phl, wrole);
|
struct rtw_wifi_role_link_t *another_rlink;
|
u8 lidx;
|
|
/* only support MLD currently */
|
if (mld == NULL || mld->type != DEV_TYPE_MLD || wrole->rlink_num == 1)
|
return 0;
|
|
tbtt_info_cnt = 0;
|
tbtt_info_len = 16;
|
|
for (lidx = 0; lidx < wrole->rlink_num; lidx++) {
|
another_rlink = get_rlink(wrole, lidx);
|
if (another_rlink == rlink)
|
continue;
|
|
/* TBTT Information Header */
|
SET_TBTT_INFO_FIELD_TYPE(p, 0);
|
SET_FILTED_NB_AP(p, 0);
|
SET_TBTT_INFO_CNT(p, tbtt_info_cnt);
|
SET_TBTT_INFO_LEN(p, tbtt_info_len);
|
p += 2;
|
|
/* Operating Class */
|
*p++ = rtw_phl_get_operating_class(another_rlink->chandef);
|
/* Channel Number */
|
*p++ = another_rlink->chandef.chan;
|
|
/* TBTT Information Set */
|
*p++ = 0; /* Neighbor AP TBTT Offset */
|
_os_mem_cpy(drv, p, another_rlink->mac_addr, ETH_ALEN); /* BSSID */
|
p += ETH_ALEN;
|
p += 4; /* TODO: Short SSID */
|
p += 1; /* TODO: BSS Parameters */
|
p += 1; /* TODO: 20 MHz PSD */
|
p += _build_mld_parameters(phl, another_rlink, p);
|
}
|
|
*pstart = EID_REDUCED_NEIGHBOR_REPORT;
|
*(pstart + 1) = (u8)(p - pstart - 2);
|
|
return (u8)(p - pstart);
|
}
|
|
void _dump_reduced_nb_rpt(struct rtw_phl_rnb_rpt_element *reduced_nb_rpt)
|
{
|
struct tbtt_info_header *hdr;
|
struct rtw_phl_tbtt_info *tbtt_info;
|
u8 i, j;
|
|
PHL_INFO("###### _dump_reduced_nb_rpt #######\n");
|
for (i = 0; i < reduced_nb_rpt->nb_ap_num; i++) {
|
PHL_INFO("%s - %d\n", "Neighbor AP", i);
|
hdr = &reduced_nb_rpt->nb_aps[i].tbtt_info_hdr;
|
|
if (!hdr->is_legal) {
|
PHL_INFO("%-25s %d %s\n", "Length", hdr->len, "is unrecognized");
|
continue;
|
}
|
|
PHL_INFO("%-25s: %d\n", "Info Field Type", hdr->type);
|
PHL_INFO("%-25s: %s\n", "Filtered Neighbor AP",
|
(hdr->filtered_nb_ap == true ? "True" : "False"));
|
PHL_INFO("%-25s: %d\n", "TBTT Info Count", hdr->cnt);
|
PHL_INFO("%-25s: %d\n", "TBTT Info Length", hdr->len);
|
PHL_INFO("%-25s: %d\n", "Operating Class", reduced_nb_rpt->nb_aps[i].op_class);
|
PHL_INFO("%-25s: %d\n", "Channel", reduced_nb_rpt->nb_aps[i].ch);
|
|
for (j = 0; j < hdr->cnt + 1; j++) {
|
tbtt_info = &reduced_nb_rpt->nb_aps[i].tbtt_infos[j];
|
PHL_INFO("%s - %d\n", "TBTT Info", j);
|
PHL_INFO("%-25s: %d\n", "Neighbor AP Offset", tbtt_info->offset);
|
|
if (hdr->bssid_is_present)
|
PHL_INFO("%-25s: %02x:%02x:%02x:%02x:%02x:%02x\n", "BSSID",
|
tbtt_info->bssid[0], tbtt_info->bssid[1],
|
tbtt_info->bssid[2], tbtt_info->bssid[3],
|
tbtt_info->bssid[4], tbtt_info->bssid[5]);
|
else
|
PHL_INFO("%-25s: Not present\n", "BSSID");
|
|
if (hdr->short_ssid_is_present)
|
PHL_INFO("%-25s: %s\n", "Short SSID", (char *)tbtt_info->short_ssid);
|
else
|
PHL_INFO("%-25s: Not present\n", "Short SSID");
|
|
if (hdr->bss_param_is_present) {
|
PHL_INFO("%-25s: Present\n", "BSS Parameters");
|
PHL_INFO("%-25s: %s\n", "- OCT recommended",
|
(tbtt_info->bss_param.oct_recomm == true ? "True" : "False"));
|
PHL_INFO("%-25s: %s\n", "- Same SSID",
|
(tbtt_info->bss_param.same_ssid == true ? "True" : "False"));
|
PHL_INFO("%-25s: %s\n", "- Multiple BSSID",
|
(tbtt_info->bss_param.multi_bssid == true ? "True" : "False"));
|
PHL_INFO("%-25s: %s\n", "- Transmitted BSSID",
|
(tbtt_info->bss_param.transmitted_bssid == true ? "True" : "False"));
|
PHL_INFO("%-25s: %s\n", "- 24G/5G colocated AP",
|
(tbtt_info->bss_param.mem_24G_5G_colated_ap == true ? "True" : "False"));
|
PHL_INFO("%-25s: %s\n", "- Unsolicited pbrsp active",
|
(tbtt_info->bss_param.unsolicited_probe_resp_act == true ? "True" : "False"));
|
PHL_INFO("%-25s: %s\n", "- Colocated AP",
|
(tbtt_info->bss_param.colated_ap == true ? "True" : "False"));
|
} else
|
PHL_INFO("%-25s: Not present\n", "BSS Parameters");
|
|
if (hdr->max_tx_pwr_is_present)
|
PHL_INFO("%-25s: %d\n", "20MHz PSD", tbtt_info->max_tx_pwr);
|
else
|
PHL_INFO("%-25s: Not present\n", "20MHz PSD");
|
|
if (hdr->mld_param_is_present) {
|
PHL_INFO("%-25s: Present\n", "MLD Parameters");
|
PHL_INFO("%-25s: %d\n", "- MLD ID", tbtt_info->mld_param.mld_id);
|
PHL_INFO("%-25s: %d\n", "- Link ID", tbtt_info->mld_param.link_id);
|
PHL_INFO("%-25s: %d\n", "- BSS Param Chg Cnt",
|
tbtt_info->mld_param.bss_params_chg_cnt);
|
} else
|
PHL_INFO("%-25s: Not present\n", "MLD Parameters");
|
}
|
}
|
PHL_INFO("###### _dump_reduced_nb_rpt #######\n");
|
}
|
|
u8 _parse_tbtt_header(struct rtw_phl_com_t *phl_com,
|
u8 *pos_start,
|
struct tbtt_info_header *hdr)
|
{
|
u8 *pos = pos_start;
|
|
hdr->type = GET_TBTT_INFO_FIELD_TYPE(pos);
|
hdr->filtered_nb_ap = GET_FILTED_NB_AP(pos);
|
hdr->cnt = GET_TBTT_INFO_CNT(pos);
|
hdr->len = GET_TBTT_INFO_LEN(pos);
|
|
if (hdr->len < 1 || hdr->len == 3) {
|
hdr->is_legal = false;
|
pos += hdr->len * hdr->cnt;
|
goto exit;
|
}
|
|
hdr->is_legal = true;
|
pos += 2;
|
|
if (hdr->len >= 7)
|
hdr->bssid_is_present = true;
|
if (hdr->len == 5 || hdr->len == 6 || hdr->len >= 11)
|
hdr->short_ssid_is_present = true;
|
if (hdr->len == 2 || hdr->len == 6 || hdr->len == 8 || hdr->len == 9 || hdr->len >= 12)
|
hdr->bss_param_is_present = true;
|
if (hdr->len == 9 || hdr->len >= 13)
|
hdr->max_tx_pwr_is_present = true;
|
if (hdr->len == 4 || hdr->len == 10 || hdr->len >= 16)
|
hdr->mld_param_is_present = true;
|
|
exit:
|
return (u8)(pos - pos_start);
|
|
}
|
|
u8 _parse_nb_info(struct rtw_phl_com_t *phl_com,
|
u8 *pos_start,
|
struct rtw_phl_neighbor_ap *nb_ap)
|
{
|
void *drv = phl_com->drv_priv;
|
u8 *pos = pos_start;
|
u8 *pos_tbtt_info;
|
struct tbtt_info_header *hdr = &nb_ap->tbtt_info_hdr;
|
struct rtw_phl_tbtt_info *tbtt_info;
|
u8 i = 0;
|
|
/* TBTT Information Header */
|
pos += _parse_tbtt_header(phl_com, pos, hdr);
|
|
if (!hdr->is_legal)
|
goto exit;
|
|
nb_ap->op_class = *pos++;
|
nb_ap->ch = *pos++;
|
|
/* translate to chan_def */
|
if (!rtw_phl_get_chandef_from_operating_class(nb_ap->ch,
|
nb_ap->op_class, &nb_ap->chan_def)) {
|
PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_,
|
"%s: getting channel definition failed !!! \n", __func__);
|
}
|
|
/* TBTT Information Set */
|
do {
|
pos_tbtt_info = pos;
|
tbtt_info = &nb_ap->tbtt_infos[i];
|
|
/* TBTT Offset*/
|
tbtt_info->offset = *pos++;
|
|
/* BSSID */
|
if (hdr->bssid_is_present) {
|
_os_mem_cpy(drv, tbtt_info->bssid, pos, MAC_ALEN);
|
pos += MAC_ALEN;
|
}
|
|
/* Short SSID */
|
if (hdr->short_ssid_is_present) {
|
_os_mem_cpy(drv, tbtt_info->short_ssid, pos, 4);
|
pos += 4;
|
}
|
|
/* BSS Parameters */
|
if (hdr->bss_param_is_present) {
|
tbtt_info->bss_param.oct_recomm =
|
GET_BSS_PARAMS_OCT_RECOMM(pos);
|
tbtt_info->bss_param.same_ssid =
|
GET_BSS_PARAMS_SAME_SSID(pos);
|
tbtt_info->bss_param.multi_bssid =
|
GET_BSS_PARAMS_MULTI_BSSID(pos);
|
tbtt_info->bss_param.transmitted_bssid =
|
GET_BSS_PARAMS_TRANSMITTED_BSSID(pos);
|
tbtt_info->bss_param.mem_24G_5G_colated_ap =
|
GET_BSS_PARAMS_MEM_24G_5G_COLOCATED_AP(pos);
|
tbtt_info->bss_param.unsolicited_probe_resp_act =
|
GET_BSS_PARAMS_UNSOLICITED_PROBE_RESP_ACTIVE(pos);
|
tbtt_info->bss_param.colated_ap =
|
GET_BSS_PARAMS_COLOACTED_AP(pos);
|
pos += 1;
|
}
|
|
/* 20Mhz psd */
|
if (hdr->max_tx_pwr_is_present)
|
tbtt_info->max_tx_pwr = *pos++;
|
|
/* MLD parameters */
|
if (hdr->mld_param_is_present) {
|
tbtt_info->mld_param.mld_id = GET_MLD_PARAMS_MLD_ID(pos);
|
tbtt_info->mld_param.link_id = GET_MLD_PARAMS_LINK_ID(pos);
|
tbtt_info->mld_param.bss_params_chg_cnt = GET_MLD_PARAMS_BSS_PARAMS_CHG_CNT(pos);
|
pos += 3;
|
}
|
|
/* ignore the rest reserved fields */
|
pos = pos_tbtt_info + hdr->len;
|
i++;
|
|
} while (i < hdr->cnt + 1 && i < MAX_TBTT_INFO_NUM);
|
|
exit:
|
return (u8)(pos - pos_start);
|
}
|
|
void rtw_phl_parse_reduced_nb_rpt(struct rtw_phl_com_t *phl_com,
|
u8 *ele_start,
|
u16 ele_len,
|
struct rtw_phl_rnb_rpt_element *reduced_nb_rpt)
|
{
|
u8 *ele_pos = NULL;
|
|
if ((ele_start == NULL) || (ele_len == 0))
|
return;
|
|
if (reduced_nb_rpt == NULL)
|
return;
|
|
ele_pos = ele_start;
|
|
do {
|
if (reduced_nb_rpt->nb_ap_num >= MAX_NEIGHBOR_AP_NUM)
|
break;
|
|
ele_pos += _parse_nb_info(phl_com, ele_pos, &reduced_nb_rpt->nb_aps[reduced_nb_rpt->nb_ap_num]);
|
reduced_nb_rpt->nb_ap_num++;
|
} while(ele_pos < (ele_start + ele_len));
|
|
}
|