Files
linux-nv-oot/drivers/net/wireless/realtek/rtl8852ce/phl/phl_sta.c
Shobek Attupurath 04fa635c7d rtl8852ce: Add Nvidia changes to v1.19.16.1-0-g1fe335ba1.20240815_PC
1. Update makefiles to add Nvidia source path
2. Change file permissions to 0644
3. Delete files with .orig extensions
4. Fix compilation issue due to enum-int mismatch

Bug 4667769
Bug 4667981

Change-Id: I0df6f3073780daf6879c4165cd97fd62fd0a4b65
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3195602
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
Reviewed-by: Revanth Kumar Uppala <ruppala@nvidia.com>
Reviewed-by: Ashutosh Jha <ajha@nvidia.com>
Tested-by: Shobek Attupurath <sattupurath@nvidia.com>
2025-07-24 10:19:09 +00:00

4989 lines
141 KiB
C

/******************************************************************************
*
* 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_STA_C_
#include "phl_headers.h"
/*********** macid ctrl section ***********/
enum rtw_phl_status
phl_macid_ctrl_init(struct phl_info_t *phl)
{
struct rtw_hal_com_t *hal_com = rtw_hal_get_halcom(phl->hal);
struct rtw_phl_com_t *phl_com = phl->phl_com;
struct macid_ctl_t *macid_ctl = phl_to_mac_ctrl(phl);
enum rtw_phl_status phl_status = RTW_PHL_STATUS_FAILURE;
u8 i = 0;
u8 lidx = 0;
/* check invalid value or not */
if (hal_com->dev_hw_cap.macid_num == 0) {
PHL_ERR("Cannot get macid_num of hal\n");
goto exit;
}
_os_spinlock_init(phl_to_drvpriv(phl), &macid_ctl->lock);
macid_ctl->max_num = MIN(hal_com->dev_hw_cap.macid_num,
phl_com->dev_sw_cap.macid_num);
PHL_INFO("%s macid max_num:%d\n", __func__, macid_ctl->max_num);
for (i = 0; i < MAX_WIFI_ROLE_NUMBER; i++)
for (lidx = 0; lidx < RTW_RLINK_MAX; lidx++)
macid_ctl->wrole_bmc[i][lidx] = macid_ctl->max_num;
phl_status = RTW_PHL_STATUS_SUCCESS;
exit:
return phl_status;
}
enum rtw_phl_status
phl_macid_ctrl_deinit(struct phl_info_t *phl)
{
struct macid_ctl_t *macid_ctl = phl_to_mac_ctrl(phl);
_os_spinlock_free(phl_to_drvpriv(phl), &macid_ctl->lock);
macid_ctl->max_num = 0;
return RTW_PHL_STATUS_SUCCESS;
}
static u8
_phl_macid_is_used(u32 *map, const u16 id)
{
int map_idx = (int)id / 32;
if (map[map_idx] & BIT(id % 32))
return true;
else
return false;
}
static void _phl_wrole_bcmc_id_set(struct macid_ctl_t *macid_ctl,
struct rtw_wifi_role_t *wrole,
struct rtw_wifi_role_link_t *rlink,
const u16 id)
{
macid_ctl->wrole_bmc[wrole->id][rlink->id] = id;
}
static u16
_phl_alloc_mld_macid(struct phl_info_t *phl_info, u16 main_id)
{
struct rtw_phl_com_t *phl_com = phl_info->phl_com;
struct macid_ctl_t *mc = phl_to_mac_ctrl(phl_info);
u16 max_macid_num = mc->max_num;
u16 max_mld_num = phl_com->dev_cap.max_mld_num;
u8 max_link_num = phl_com->dev_cap.max_link_num, lidx = 0;
u16 mid = 0;
if (max_macid_num == main_id) {
if (RTW_ONE_LINK == phl_com->dev_cap.max_link_num) {
for(mid = 0; mid < max_macid_num; mid++) {
if (!_phl_macid_is_used(mc->used_map, mid))
return mid;
}
} else {
for(mid = 0; mid < max_mld_num; mid++) {
if (_phl_macid_is_used(mc->used_mld_map, mid))
continue;
if (_phl_macid_is_used(mc->used_legacy_map, mid))
continue;
phl_macid_map_set(mc->used_mld_map, mid);
return mid;
}
}
} else {
main_id = main_id % max_mld_num;
for (lidx = 0; lidx < max_link_num; lidx++) {
mid = main_id + (max_mld_num * lidx);
if (!_phl_macid_is_used(mc->used_map, mid))
return mid;
}
}
return max_macid_num;
}
static u16
_phl_alloc_legacy_macid(struct phl_info_t *phl_info)
{
struct rtw_phl_com_t *phl_com = phl_info->phl_com;
struct macid_ctl_t *mc = phl_to_mac_ctrl(phl_info);
u16 max_macid_num = mc->max_num;
u16 avail_macid = mc->max_num;
u16 max_mld_num = phl_com->dev_cap.max_mld_num;
u8 max_link_num = phl_com->dev_cap.max_link_num;
u16 mid = 0, another_mid = 0;
u16 rsvd_macid = 0, lidx = 0;
if (RTW_ONE_LINK == phl_com->dev_cap.max_link_num) {
for(mid = 0; mid < max_macid_num; mid++)
if (!_phl_macid_is_used(mc->used_map, mid))
return mid;
} else {
rsvd_macid = max_mld_num * max_link_num;
if ((rsvd_macid > 0) && (max_macid_num > rsvd_macid)) {
for(mid = rsvd_macid; mid < max_macid_num; mid++)
if (!_phl_macid_is_used(mc->used_map, mid))
return mid;
}
for(mid = 0; mid < max_mld_num; mid++) {
if (_phl_macid_is_used(mc->used_mld_map, mid))
continue;
if (!_phl_macid_is_used(mc->used_legacy_map, mid)) {
if (avail_macid == max_macid_num)
avail_macid = mid;
continue;
}
for (lidx = 0; lidx < max_link_num; lidx++) {
another_mid = mid + (max_mld_num * lidx);
if (!_phl_macid_is_used(mc->used_map, another_mid))
return another_mid;
}
}
}
if (avail_macid != max_macid_num)
phl_macid_map_set(mc->used_legacy_map, avail_macid);
return avail_macid;
}
static enum rtw_phl_status
_phl_alloc_macid(struct phl_info_t *phl_info,
enum rtw_device_type dtype,
u16 main_id,
struct rtw_phl_stainfo_t *phl_sta)
{
struct macid_ctl_t *mc = phl_to_mac_ctrl(phl_info);
struct rtw_wifi_role_t *wrole = phl_sta->wrole;
struct rtw_wifi_role_link_t *rlink = phl_sta->rlink;
u8 bc_addr[MAC_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
u16 mid = 0;
u16 max_macid_num = 0;
bool bmc_sta = false;
if (wrole == NULL) {
PHL_ERR("%s wrole=NULL!\n", __func__);
return RTW_PHL_STATUS_FAILURE;
}
if (_os_mem_cmp(phl_to_drvpriv(phl_info),
bc_addr, phl_sta->mac_addr, MAC_ALEN) == 0)
bmc_sta = true;
/* TODO
if (wrole->type == PHL_RTYPE_STATION)
else if (wrole->type == PHL_RTYPE_AP)*/
max_macid_num = mc->max_num;
_os_spinlock(phl_to_drvpriv(phl_info), &mc->lock, _bh, NULL);
if (DEV_TYPE_MLD == dtype)
mid = _phl_alloc_mld_macid(phl_info, main_id);
else
mid = _phl_alloc_legacy_macid(phl_info);
if (mid == max_macid_num) {
phl_sta->macid = max_macid_num;
PHL_ERR("%s cannot get macid\n", __func__);
_os_spinunlock(phl_to_drvpriv(phl_info), &mc->lock, _bh, NULL);
return RTW_PHL_STATUS_FAILURE;
}
phl_macid_map_set(mc->used_map, mid);
phl_macid_map_set(&mc->wifi_role_usedmap[wrole->id][0], mid);
mc->sta[mid] = phl_sta;
if (bmc_sta) {
phl_macid_map_set(mc->bmc_map, mid);
_phl_wrole_bcmc_id_set(mc, wrole, rlink, mid);
}
_os_spinunlock(phl_to_drvpriv(phl_info), &mc->lock, _bh, NULL);
phl_sta->macid = mid;
PHL_INFO("%s allocate %02x:%02x:%02x:%02x:%02x:%02x for macid:%u\n", __func__,
phl_sta->mac_addr[0], phl_sta->mac_addr[1], phl_sta->mac_addr[2],
phl_sta->mac_addr[3], phl_sta->mac_addr[4], phl_sta->mac_addr[5],
phl_sta->macid);
return RTW_PHL_STATUS_SUCCESS;
}
static enum rtw_phl_status
_phl_release_macid(struct phl_info_t *phl_info,
struct rtw_phl_stainfo_t *phl_sta)
{
struct rtw_phl_com_t *phl_com = phl_info->phl_com;
struct macid_ctl_t *macid_ctl = phl_to_mac_ctrl(phl_info);
enum rtw_phl_status phl_status = RTW_PHL_STATUS_FAILURE;
struct rtw_wifi_role_t *wrole = phl_sta->wrole;
u8 bc_addr[MAC_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
u16 invalid_macid = macid_ctl->max_num;
u16 max_mld_num = phl_com->dev_cap.max_mld_num;
u8 max_link_num = phl_com->dev_cap.max_link_num, lidx = 0;
u16 mid = 0;
u16 another_mid = 0;
if (phl_sta->macid >= invalid_macid) {
PHL_ERR("_phl_release_macid macid error (%d\n)", phl_sta->macid);
phl_status = RTW_PHL_STATUS_FAILURE;
goto exit;
}
_os_spinlock(phl_to_drvpriv(phl_info), &macid_ctl->lock, _bh, NULL);
if (!_phl_macid_is_used(macid_ctl->used_map, phl_sta->macid)) {
PHL_WARN("_phl_release_macid macid unused (%d\n)", phl_sta->macid);
_os_spinunlock(phl_to_drvpriv(phl_info), &macid_ctl->lock, _bh, NULL);
phl_status = RTW_PHL_STATUS_FAILURE;
goto exit;
}
phl_macid_map_clr(macid_ctl->used_map, phl_sta->macid);
phl_macid_map_clr(&macid_ctl->wifi_role_usedmap[wrole->id][0], phl_sta->macid);
macid_ctl->sta[phl_sta->macid] = NULL;
if (_os_mem_cmp(phl_to_drvpriv(phl_info),
bc_addr, phl_sta->mac_addr, MAC_ALEN) == 0)
phl_macid_map_clr(macid_ctl->bmc_map, phl_sta->macid);
if (max_mld_num != 0) {
mid = phl_sta->macid % max_mld_num;
for (lidx = 0; lidx < max_link_num; lidx++) {
another_mid = mid + (max_mld_num * lidx);
if (_phl_macid_is_used(macid_ctl->used_map, another_mid))
break;
}
if (lidx == max_link_num) {
phl_macid_map_clr(macid_ctl->used_mld_map, mid);
phl_macid_map_clr(macid_ctl->used_legacy_map, mid);
}
}
phl_status = RTW_PHL_STATUS_SUCCESS;
_os_spinunlock(phl_to_drvpriv(phl_info), &macid_ctl->lock, _bh, NULL);
exit:
PHL_INFO("%s release macid:%d - %02x:%02x:%02x:%02x:%02x:%02x \n",
__func__,
phl_sta->macid,
phl_sta->mac_addr[0], phl_sta->mac_addr[1], phl_sta->mac_addr[2],
phl_sta->mac_addr[3], phl_sta->mac_addr[4], phl_sta->mac_addr[5]);
phl_sta->macid = invalid_macid;
return phl_status;
}
u16 _phl_get_macid(struct phl_info_t *phl_info,
struct rtw_phl_stainfo_t *phl_sta)
{
/* TODO: macid management */
return phl_sta->macid;
}
void
phl_macid_map_clr(u32 *map, u16 id)
{
int map_idx = (int)id / 32;
map[map_idx] &= ~BIT(id % 32);
}
void
phl_macid_map_set(u32 *map, u16 id)
{
int map_idx = (int)id / 32;
map[map_idx] |= BIT(id % 32);
}
/**
* This function export to core layer use
* to get phl role bmc macid
* @phl: see phl_info_t
* @wrole: wifi role
*/
u16
rtw_phl_wrole_bcmc_id_get(void *phl,
struct rtw_wifi_role_t *wrole,
struct rtw_wifi_role_link_t *rlink)
{
struct phl_info_t *phl_info = (struct phl_info_t *)phl;
struct macid_ctl_t *macid_ctl = phl_to_mac_ctrl(phl_info);
return macid_ctl->wrole_bmc[wrole->id][rlink->id];
}
/**
* This function export to core layer use
* to get maximum macid number
* @phl: see phl_info_t
*/
u16
rtw_phl_get_macid_max_num(void *phl)
{
struct phl_info_t *phl_info = (struct phl_info_t *)phl;
struct macid_ctl_t *macid_ctl = phl_to_mac_ctrl(phl_info);
return macid_ctl->max_num;
}
/**
* This function export to core layer use
* to check macid is bmc or not
* @phl: see phl_info_t
* @macid: macid
*/
u8
rtw_phl_macid_is_bmc(void *phl, u16 macid)
{
struct phl_info_t *phl_info = (struct phl_info_t *)phl;
struct macid_ctl_t *macid_ctl = phl_to_mac_ctrl(phl_info);
if (macid >= macid_ctl->max_num) {
PHL_ERR("%s macid(%d) is invalid\n", __func__, macid);
return true;
}
return _phl_macid_is_used(macid_ctl->bmc_map, macid);
}
/**
* This function export to core layer use
* to check macid is used or not
* @phl: see phl_info_t
* @macid: macid
*/
u8
rtw_phl_macid_is_used(void *phl, u16 macid)
{
struct phl_info_t *phl_info = (struct phl_info_t *)phl;
struct macid_ctl_t *macid_ctl = phl_to_mac_ctrl(phl_info);
if (macid >= macid_ctl->max_num) {
PHL_ERR("%s macid(%d) is invalid\n", __func__, macid);
return true;
}
return _phl_macid_is_used(macid_ctl->used_map, macid);
}
/**
* This function is used to
* pause/unpause hw of macid
* @phl: see phl_info_t
* @macid: macid
* @pause: pause or unpause
*/
static enum rtw_phl_status
_phl_set_macid_pause(struct phl_info_t *phl_info, u16 macid, bool pause)
{
enum rtw_phl_status phl_sts = RTW_PHL_STATUS_FAILURE;
if(RTW_HAL_STATUS_SUCCESS ==
rtw_hal_set_macid_pause(phl_info->hal, macid, pause))
phl_sts = RTW_PHL_STATUS_SUCCESS;
return phl_sts;
}
/**
* This function is used to pause/unpause macid AC hw
* @phl: see phl_info_t
* @macid: macid
* @pause: pause or unpause
*/
static enum rtw_phl_status
_phl_set_macid_pause_ac(struct phl_info_t *phl_info, u16 macid, bool pause)
{
enum rtw_phl_status phl_sts = RTW_PHL_STATUS_FAILURE;
if (RTW_HAL_STATUS_SUCCESS ==
rtw_hal_set_macid_pause_ac(phl_info->hal, macid, pause))
phl_sts = RTW_PHL_STATUS_SUCCESS;
return phl_sts;
}
/**
* This function export is used to
* drop hw packets of macid
* @phl: see phl_info_t
* @macid: macid
* @sel: mac definition in mac_def.h: enum mac_ax_pkt_drop_sel
*/
static enum rtw_phl_status
_phl_set_macid_pkt_drop(struct phl_info_t *phl_info, u16 macid, u8 sel, u8 band,
u8 port, u8 mbssid)
{
enum rtw_phl_status phl_sts = RTW_PHL_STATUS_FAILURE;
if(RTW_HAL_STATUS_SUCCESS ==
rtw_hal_set_macid_pkt_drop(phl_info->hal, macid, sel, band, port,
mbssid))
phl_sts = RTW_PHL_STATUS_SUCCESS;
return phl_sts;
}
#ifdef CONFIG_CMD_DISP
struct cmd_set_macid_pause_param {
struct rtw_phl_stainfo_t *phl_sta;
bool pause;
};
struct cmd_set_macid_pkt_drop_param {
struct rtw_phl_stainfo_t *phl_sta;
u8 sel;
};
enum rtw_phl_status
phl_cmd_set_macid_pause_hdl(struct phl_info_t *phl_info, u8 *param)
{
struct cmd_set_macid_pause_param *p =
(struct cmd_set_macid_pause_param *)param;
return _phl_set_macid_pause(phl_info, p->phl_sta->macid, p->pause);
}
enum rtw_phl_status
phl_cmd_set_macid_pause_ac_hdl(struct phl_info_t *phl_info, u8 *param)
{
struct cmd_set_macid_pause_param *p =
(struct cmd_set_macid_pause_param *)param;
return _phl_set_macid_pause_ac(phl_info, p->phl_sta->macid, p->pause);
}
enum rtw_phl_status
phl_cmd_set_macid_pkt_drop_hdl(struct phl_info_t *phl_info, u8 *param)
{
struct cmd_set_macid_pkt_drop_param *p =
(struct cmd_set_macid_pkt_drop_param *)param;
u8 hw_band = p->phl_sta->rlink->hw_band;
u8 hw_port = p->phl_sta->rlink->hw_port;
#ifdef RTW_PHL_BCN
u8 hw_mbssid = p->phl_sta->rlink->hw_mbssid;
#else
u8 hw_mbssid = 0;
#endif
return _phl_set_macid_pkt_drop(phl_info,
p->phl_sta->macid,
p->sel,
hw_band,
hw_port,
hw_mbssid);
}
static void
_phl_cmd_set_macid_pause_done(void *drv_priv,
u8 *cmd,
u32 cmd_len,
enum rtw_phl_status status)
{
if (cmd) {
_os_kmem_free(drv_priv, cmd, cmd_len);
cmd = NULL;
PHL_INFO("%s.....\n", __func__);
}
}
static void _phl_cmd_set_macid_pause_ac_done(void *drv_priv,
u8 *cmd,
u32 cmd_len,
enum rtw_phl_status status)
{
if (cmd) {
_os_kmem_free(drv_priv, cmd, cmd_len);
cmd = NULL;
}
}
static void
_phl_cmd_set_macid_pkt_drop_done(void *drv_priv,
u8 *cmd,
u32 cmd_len,
enum rtw_phl_status status)
{
if (cmd) {
_os_kmem_free(drv_priv, cmd, cmd_len);
cmd = NULL;
PHL_INFO("%s.....\n", __func__);
}
}
enum rtw_phl_status
rtw_phl_cmd_set_macid_pause(struct rtw_wifi_role_t *wifi_role,
struct rtw_phl_stainfo_t *phl_sta, bool pause,
enum phl_cmd_type cmd_type,
u32 cmd_timeout)
{
struct phl_info_t *phl_info = wifi_role->phl_com->phl_priv;
void *drv = wifi_role->phl_com->drv_priv;
enum rtw_phl_status psts = RTW_PHL_STATUS_FAILURE;
struct cmd_set_macid_pause_param *param = NULL;
u32 param_len;
if (cmd_type == PHL_CMD_DIRECTLY) {
psts = _phl_set_macid_pause(phl_info, phl_sta->macid, pause);
goto _exit;
}
param_len = sizeof(struct cmd_set_macid_pause_param);
param = _os_kmem_alloc(drv, param_len);
if (param == NULL) {
PHL_ERR("%s: alloc param failed!\n", __func__);
goto _exit;
}
param->phl_sta = phl_sta;
param->pause = pause;
psts = phl_cmd_enqueue(phl_info,
phl_sta->rlink->hw_band,
MSG_EVT_SET_MACID_PAUSE,
(u8 *)param,
param_len,
_phl_cmd_set_macid_pause_done,
cmd_type,
cmd_timeout);
if (is_cmd_failure(psts)) {
/* Send cmd success, but wait cmd fail*/
psts = RTW_PHL_STATUS_FAILURE;
} else if (psts != RTW_PHL_STATUS_SUCCESS) {
/* Send cmd fail */
_os_kmem_free(drv, param, param_len);
psts = RTW_PHL_STATUS_FAILURE;
}
_exit:
return psts;
}
enum rtw_phl_status
rtw_phl_cmd_set_macid_pause_ac(struct rtw_wifi_role_t *wifi_role,
struct rtw_phl_stainfo_t *phl_sta, bool pause,
enum phl_cmd_type cmd_type,
u32 cmd_timeout)
{
struct phl_info_t *phl_info = wifi_role->phl_com->phl_priv;
void *drv = wifi_role->phl_com->drv_priv;
enum rtw_phl_status psts = RTW_PHL_STATUS_FAILURE;
struct cmd_set_macid_pause_param *param = NULL;
u32 param_len;
if (cmd_type == PHL_CMD_DIRECTLY) {
psts = _phl_set_macid_pause_ac(phl_info, phl_sta->macid, pause);
goto _exit;
}
param_len = sizeof(struct cmd_set_macid_pause_param);
param = _os_kmem_alloc(drv, param_len);
if (param == NULL) {
PHL_ERR("%s: alloc param failed!\n", __func__);
goto _exit;
}
param->phl_sta = phl_sta;
param->pause = pause;
psts = phl_cmd_enqueue(phl_info,
phl_sta->rlink->hw_band,
MSG_EVT_SET_MACID_PAUSE_AC,
(u8 *)param,
param_len,
_phl_cmd_set_macid_pause_ac_done,
cmd_type,
cmd_timeout);
if (is_cmd_failure(psts)) {
/* Send cmd success, but wait cmd fail*/
psts = RTW_PHL_STATUS_FAILURE;
} else if (psts != RTW_PHL_STATUS_SUCCESS) {
/* Send cmd fail */
_os_kmem_free(drv, param, param_len);
psts = RTW_PHL_STATUS_FAILURE;
}
_exit:
return psts;
}
enum rtw_phl_status
rtw_phl_cmd_set_macid_pkt_drop(struct rtw_wifi_role_t *wifi_role,
struct rtw_phl_stainfo_t *phl_sta, u8 sel,
enum phl_cmd_type cmd_type,
u32 cmd_timeout)
{
struct phl_info_t *phl_info = wifi_role->phl_com->phl_priv;
void *drv = wifi_role->phl_com->drv_priv;
enum rtw_phl_status psts = RTW_PHL_STATUS_FAILURE;
struct cmd_set_macid_pkt_drop_param *param = NULL;
u32 param_len;
u8 hw_band = phl_sta->rlink->hw_band;
u8 hw_port = phl_sta->rlink->hw_port;
#ifdef RTW_PHL_BCN
u8 hw_mbssid = phl_sta->rlink->hw_mbssid;
#else
u8 hw_mbssid = 0;
#endif
if (cmd_type == PHL_CMD_DIRECTLY) {
psts = _phl_set_macid_pkt_drop(phl_info,
phl_sta->macid,
sel,
hw_band,
hw_port,
hw_mbssid);
goto _exit;
}
param_len = sizeof(struct cmd_set_macid_pkt_drop_param);
param = _os_kmem_alloc(drv, param_len);
if (param == NULL) {
PHL_ERR("%s: alloc param failed!\n", __func__);
goto _exit;
}
param->phl_sta = phl_sta;
param->sel = sel;
psts = phl_cmd_enqueue(phl_info,
hw_band,
MSG_EVT_SET_MACID_PKT_DROP,
(u8 *)param,
param_len,
_phl_cmd_set_macid_pkt_drop_done,
cmd_type,
cmd_timeout);
if (is_cmd_failure(psts)) {
/* Send cmd success, but wait cmd fail*/
psts = RTW_PHL_STATUS_FAILURE;
} else if (psts != RTW_PHL_STATUS_SUCCESS) {
/* Send cmd fail */
_os_kmem_free(drv, param, param_len);
psts = RTW_PHL_STATUS_FAILURE;
}
_exit:
return psts;
}
#endif
/**
* This function is used to
* check macid shared by all wifi role
* @phl: see phl_info_t
* @macid: macid
*/
u8
rtw_phl_macid_is_wrole_shared(void *phl, u16 macid)
{
struct phl_info_t *phl_info = (struct phl_info_t *)phl;
struct macid_ctl_t *macid_ctl = phl_to_mac_ctrl(phl_info);
int i = 0;
u8 iface_bmp = 0;
if (macid >= macid_ctl->max_num) {
PHL_ERR("%s macid(%d) is invalid\n", __func__, macid);
return false;
}
for (i = 0; i < MAX_WIFI_ROLE_NUMBER; i++) {
if (_phl_macid_is_used(&macid_ctl->wifi_role_usedmap[i][0], macid)) {
if (iface_bmp)
return true;
iface_bmp |= BIT(i);
}
}
return false;
}
/**
* This function is used to
* check macid not shared by all wifi role
* and belong to wifi role
* @phl: see phl_info_t
* @macid: macid
* @wrole: check id belong to this wifi role
*/
u8
rtw_phl_macid_is_wrole_specific(void *phl,
u16 macid, struct rtw_wifi_role_t *wrole)
{
struct phl_info_t *phl_info = (struct phl_info_t *)phl;
struct macid_ctl_t *macid_ctl = phl_to_mac_ctrl(phl_info);
int i = 0;
u8 iface_bmp = 0;
if (macid >= macid_ctl->max_num) {
PHL_ERR("%s macid(%d) invalid\n", __func__, macid);
return false;
}
for (i = 0; i < MAX_WIFI_ROLE_NUMBER; i++) {
if (_phl_macid_is_used(&macid_ctl->wifi_role_usedmap[i][0], macid)) {
if (iface_bmp || i != wrole->id)
return false;
iface_bmp |= BIT(i);
}
}
return iface_bmp ? true : false;
}
/*********** stainfo_ctrl section ***********/
static enum rtw_phl_status
_phl_stainfo_init(struct phl_info_t *phl_info,
struct rtw_phl_stainfo_t *phl_sta)
{
void *drv = phl_to_drvpriv(phl_info);
INIT_LIST_HEAD(&phl_sta->list);
_os_spinlock_init(drv, &phl_sta->tid_rx_lock);
_os_mem_set(drv, phl_sta->tid_rx, 0, sizeof(phl_sta->tid_rx));
_os_event_init(drv, &phl_sta->comp_sync);
_os_init_timer(drv, &phl_sta->reorder_timer,
phl_sta_rx_reorder_timer_expired, phl_sta, "reorder_timer");
_os_atomic_set(drv, &phl_sta->ps_sta, 0);
if (rtw_hal_stainfo_init(phl_info->hal, phl_sta) !=
RTW_HAL_STATUS_SUCCESS) {
PHL_ERR("hal_stainfo_init failed\n");
FUNCOUT();
return RTW_PHL_STATUS_FAILURE;
}
phl_sta->active = false;
return RTW_PHL_STATUS_SUCCESS;
}
enum rtw_phl_status
_phl_stainfo_deinit(struct phl_info_t *phl_info,
struct rtw_phl_stainfo_t *phl_sta)
{
void *drv = phl_to_drvpriv(phl_info);
_os_release_timer(drv, &phl_sta->reorder_timer);
_os_spinlock_free(phl_to_drvpriv(phl_info), &phl_sta->tid_rx_lock);
_os_event_free(drv, &phl_sta->comp_sync);
if (rtw_hal_stainfo_deinit(phl_info->hal, phl_sta)!=
RTW_HAL_STATUS_SUCCESS) {
PHL_ERR("hal_stainfo_deinit failed\n");
FUNCOUT();
return RTW_PHL_STATUS_FAILURE;
}
return RTW_PHL_STATUS_SUCCESS;
}
enum rtw_phl_status
phl_stainfo_enqueue(struct phl_info_t *phl_info,
struct phl_queue *sta_queue,
struct rtw_phl_stainfo_t *psta)
{
void *drv = phl_to_drvpriv(phl_info);
if (!psta)
return RTW_PHL_STATUS_FAILURE;
_os_spinlock(drv, &sta_queue->lock, _bh, NULL);
list_add_tail(&psta->list, &sta_queue->queue);
sta_queue->cnt++;
_os_spinunlock(drv, &sta_queue->lock, _bh, NULL);
return RTW_PHL_STATUS_SUCCESS;
}
struct rtw_phl_stainfo_t *
phl_stainfo_dequeue(struct phl_info_t *phl_info,
struct phl_queue *sta_queue)
{
struct rtw_phl_stainfo_t *psta = NULL;
void *drv = phl_to_drvpriv(phl_info);
_os_spinlock(drv, &sta_queue->lock, _bh, NULL);
if (list_empty(&sta_queue->queue)) {
psta = NULL;
} else {
psta = list_first_entry(&sta_queue->queue,
struct rtw_phl_stainfo_t, list);
list_del(&psta->list);
sta_queue->cnt--;
}
_os_spinunlock(drv, &sta_queue->lock, _bh, NULL);
return psta;
}
enum rtw_phl_status
phl_stainfo_queue_del(struct phl_info_t *phl_info,
struct phl_queue *sta_queue,
struct rtw_phl_stainfo_t *psta)
{
void *drv = phl_to_drvpriv(phl_info);
if (!psta)
return RTW_PHL_STATUS_FAILURE;
_os_spinlock(drv, &sta_queue->lock, _bh, NULL);
if (sta_queue->cnt) {
list_del(&psta->list);
sta_queue->cnt--;
}
_os_spinunlock(drv, &sta_queue->lock, _bh, NULL);
return RTW_PHL_STATUS_SUCCESS;
}
struct rtw_phl_stainfo_t *
phl_stainfo_queue_search(struct phl_info_t *phl_info,
struct phl_queue *sta_queue,
u8 *addr)
{
struct rtw_phl_stainfo_t *sta = NULL;
_os_list *sta_list = &sta_queue->queue;
void *drv = phl_to_drvpriv(phl_info);
bool sta_found = false;
_os_spinlock(drv, &sta_queue->lock, _bh, NULL);
if (list_empty(sta_list) == true)
goto _exit;
phl_list_for_loop(sta, struct rtw_phl_stainfo_t, sta_list, list) {
if (_os_mem_cmp(phl_to_drvpriv(phl_info),
sta->mac_addr, addr, MAC_ALEN) == 0) {
sta_found = true;
break;
}
if (sta->mld == NULL)
continue;
/* search MLD mac address */
if (_os_mem_cmp(phl_to_drvpriv(phl_info),
sta->mld->mac_addr, addr, MAC_ALEN) == 0) {
sta_found = true;
break;
}
}
_exit:
_os_spinunlock(drv, &sta_queue->lock, _bh, NULL);
if (sta_found == false)
sta = NULL;
return sta;
}
struct rtw_phl_stainfo_t *
phl_stainfo_queue_get_first(struct phl_info_t *phl_info,
struct phl_queue *sta_queue)
{
_os_list *sta_list = &sta_queue->queue;
void *drv = phl_to_drvpriv(phl_info);
struct rtw_phl_stainfo_t *sta = NULL;
/* first sta info in assoc_sta_queu is self sta info */
_os_spinlock(drv, &sta_queue->lock, _bh, NULL);
if (list_empty(sta_list) == true)
goto _exit;
sta = list_first_entry(sta_list, struct rtw_phl_stainfo_t, list);
_exit :
_os_spinunlock(drv, &sta_queue->lock, _bh, NULL);
return sta;
}
enum rtw_phl_status
phl_stainfo_ctrl_deinit(struct phl_info_t *phl_info)
{
struct stainfo_ctl_t *sta_ctrl = phl_to_sta_ctrl(phl_info);
void *drv = phl_to_drvpriv(phl_info);
struct rtw_phl_stainfo_t *psta = NULL;
struct phl_queue *fsta_queue = &sta_ctrl->free_sta_queue;
FUNCIN();
do {
psta = phl_stainfo_dequeue(phl_info, fsta_queue);
if (psta)
_phl_stainfo_deinit(phl_info, psta);
}while (psta != NULL);
pq_deinit(drv, fsta_queue);
if (sta_ctrl->allocated_stainfo_buf)
_os_mem_free(drv, sta_ctrl->allocated_stainfo_buf,
sta_ctrl->allocated_stainfo_sz);
FUNCOUT();
return RTW_PHL_STATUS_SUCCESS;
}
enum rtw_phl_status
phl_stainfo_ctrl_init(struct phl_info_t *phl_info)
{
struct stainfo_ctl_t *sta_ctrl = phl_to_sta_ctrl(phl_info);
void *drv = phl_to_drvpriv(phl_info);
struct rtw_phl_stainfo_t *psta = NULL;
enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE;
struct phl_queue *fsta_queue = NULL;
u16 i;
bool sta_init_fail = false;
FUNCIN();
sta_ctrl->phl_info = phl_info;
sta_ctrl->allocated_stainfo_sz = sizeof(struct rtw_phl_stainfo_t) * PHL_MAX_STA_NUM;
#ifdef MEM_ALIGNMENT
sta_ctrl->allocated_stainfo_sz += MEM_ALIGNMENT_OFFSET;
#endif
sta_ctrl->allocated_stainfo_buf =
_os_mem_alloc(drv, sta_ctrl->allocated_stainfo_sz);
if (!sta_ctrl->allocated_stainfo_buf) {
PHL_ERR("allocate stainfo buf failed\n");
goto _exit;
}
sta_ctrl->stainfo_buf = sta_ctrl->allocated_stainfo_buf;
#ifdef MEM_ALIGNMENT
if (sta_ctrl->stainfo_buf & MEM_ALIGNMENT_PADDING)
sta_ctrl->stainfo_buf += MEM_ALIGNMENT_OFFSET -
(sta_ctrl->stainfo_buf & MEM_ALIGNMENT_PADDING);
#endif
fsta_queue = &sta_ctrl->free_sta_queue;
pq_init(drv, fsta_queue);
psta = (struct rtw_phl_stainfo_t *)(sta_ctrl->stainfo_buf);
for (i = 0; i < PHL_MAX_STA_NUM; i++) {
if (_phl_stainfo_init(phl_info, psta) != RTW_PHL_STATUS_SUCCESS) {
sta_init_fail = true;
break;
}
phl_stainfo_enqueue(phl_info, fsta_queue, psta);
psta++;
}
if (sta_init_fail == true) {
PHL_ERR("sta_init failed\n");
phl_stainfo_ctrl_deinit(phl_info);
goto _exit;
}
PHL_DUMP_STACTRL_EX(phl_info);
pstatus = RTW_PHL_STATUS_SUCCESS;
_exit:
FUNCOUT();
return pstatus;
}
/*********** phl stainfo section ***********/
#ifdef DBG_PHL_STAINFO
void
phl_dump_stactrl(const char *caller, const int line, bool show_caller,
struct phl_info_t *phl_info)
{
struct rtw_phl_com_t *phl_com = phl_info->phl_com;
u8 ridx = MAX_WIFI_ROLE_NUMBER;
struct rtw_wifi_role_t *role;
struct stainfo_ctl_t *sta_ctrl = NULL;
u8 idx = 0;
struct rtw_wifi_role_link_t *rlink = NULL;
sta_ctrl = phl_to_sta_ctrl(phl_info);
if (show_caller)
PHL_INFO("[PSTA] ###### FUN - %s LINE - %d #######\n", caller, line);
PHL_INFO("[PSTA] PHL_MAX_STA_NUM:%d\n", PHL_MAX_STA_NUM);
PHL_INFO("[PSTA] sta_ctrl - q_cnt :%d\n", sta_ctrl->free_sta_queue.cnt);
for (ridx = 0; ridx < MAX_WIFI_ROLE_NUMBER; ridx++) {
role = &(phl_com->wifi_roles[ridx]);
for (idx = 0; idx < role->rlink_num; idx++) {
rlink = get_rlink(role, idx);
PHL_INFO("[PSTA] wrole_%d link_%d asoc_q cnt :%d\n",
ridx, idx, rlink->assoc_sta_queue.cnt);
}
}
if (show_caller)
PHL_INFO("#################################\n");
}
static void _phl_dump_stainfo(struct rtw_phl_stainfo_t *phl_sta)
{
PHL_INFO("\t[STA] MAC-ID:%d, AID:%d, MAC-ADDR:%02x-%02x-%02x-%02x-%02x-%02x, Active:%s\n",
phl_sta->macid, phl_sta->aid,
phl_sta->mac_addr[0],phl_sta->mac_addr[1],phl_sta->mac_addr[2],
phl_sta->mac_addr[3],phl_sta->mac_addr[4],phl_sta->mac_addr[5],
(phl_sta->active) ? "Y" : "N");
PHL_INFO("\t[STA] WROLE-IDX:%d R-IDX:%d HW-Band-IDX:%d, wlan_mode:0x%02x\n",
phl_sta->wrole->id, phl_sta->rlink->id, phl_sta->rlink->hw_band, phl_sta->wmode);
PHL_DUMP_CHAN_DEF(&phl_sta->chandef);
/****** statistic ******/
PHL_INFO("\t[STA] TP -[Tx:%d Rx :%d BI:N/A] (KBits)\n",
phl_sta->stats.tx_tp_kbits, phl_sta->stats.rx_tp_kbits);
PHL_INFO("\t[STA] Total -[Tx:%llu Rx :%llu BI:N/A] (Bytes)\n",
phl_sta->stats.tx_byte_total, phl_sta->stats.rx_byte_total);
/****** asoc_cap ******/
PHL_INFO("\t[STA] assoc_cap - nss_tx:%d\n",
phl_sta->asoc_cap.nss_tx);
PHL_INFO("\t[STA] assoc_cap - nss_rx:%d stbc ht_rx:%d, vht_rx:%d, he_rx:%d\n",
phl_sta->asoc_cap.nss_rx, phl_sta->asoc_cap.stbc_ht_rx,
phl_sta->asoc_cap.stbc_vht_rx, phl_sta->asoc_cap.stbc_he_rx);
/****** protect ******/
/****** sec_mode ******/
/****** rssi_stat ******/
PHL_INFO("\t\t[HAL STA] rssi:%d assoc_rssi:%d, ofdm:%d, cck:%d, rssi_ma:%d, ma_rssi:%d\n",
(phl_sta->hal_sta->rssi_stat.rssi >> 1), phl_sta->hal_sta->rssi_stat.assoc_rssi,
(phl_sta->hal_sta->rssi_stat.rssi_ofdm >> 1), (phl_sta->hal_sta->rssi_stat.rssi_cck >> 1),
(phl_sta->hal_sta->rssi_stat.rssi_ma >> 5), phl_sta->hal_sta->rssi_stat.ma_rssi);
/****** ra_info ******/
PHL_INFO("\t\t[HAL STA] - RA info\n");
PHL_INFO("\t\t[HAL STA] Tx rate:0x%04x ra_bw_mode:%d, curr_tx_bw:%d\n",
phl_sta->hal_sta->ra_info.curr_tx_rate,
phl_sta->hal_sta->ra_info.ra_bw_mode,
phl_sta->hal_sta->ra_info.curr_tx_bw);
PHL_INFO("\t\t[HAL STA] dis_ra:%s ra_registered:%s\n",
(phl_sta->hal_sta->ra_info.dis_ra) ? "Y" : "N",
(phl_sta->hal_sta->ra_info.ra_registered) ? "Y" : "N");
PHL_INFO("\t\t[HAL STA] ra_mask:0x%08llx cur_ra_mask:0x%08llx, retry_ratio:%d\n",
phl_sta->hal_sta->ra_info.ra_mask,
phl_sta->hal_sta->ra_info.cur_ra_mask,
phl_sta->hal_sta->ra_info.curr_retry_ratio);
/****** ra_info - Report ******/
PHL_INFO("\t\t[HAL STA] RA Report: gi_ltf:%d rate_mode:%d, bw:%d, mcs_ss_idx:%d\n",
phl_sta->hal_sta->ra_info.rpt_rt_i.gi_ltf,
phl_sta->hal_sta->ra_info.rpt_rt_i.mode,
phl_sta->hal_sta->ra_info.rpt_rt_i.bw,
phl_sta->hal_sta->ra_info.rpt_rt_i.mcs_ss_idx);
PHL_INFO("\t\t[HAL STA] HAL rx_ok_cnt:%d rx_err_cnt:%d, rx_rate_plurality:%d\n\n",
phl_sta->hal_sta->trx_stat.rx_ok_cnt,
phl_sta->hal_sta->trx_stat.rx_err_cnt,
phl_sta->hal_sta->trx_stat.rx_rate_plurality);
}
void phl_dump_stainfo_all(const char *caller, const int line, bool show_caller,
struct phl_info_t *phl_info)
{
struct macid_ctl_t *macid_ctl = phl_to_mac_ctrl(phl_info);
struct rtw_hal_com_t *hal_com = rtw_hal_get_halcom(phl_info->hal);
struct rtw_phl_stainfo_t *phl_sta = NULL;
u16 max_macid_num = 0;
u16 mid = 0;
u8 assoc_sta_cnt_all, assoc_sta_cnt_b0, assoc_sta_cnt_b1;
if (show_caller)
PHL_INFO("###### FUN - %s LINE - %d #######\n", caller, line);
assoc_sta_cnt_all = hal_com->assoc_sta_cnt;
assoc_sta_cnt_b0 = hal_com->band[HW_BAND_0].assoc_sta_cnt;
assoc_sta_cnt_b1 = hal_com->band[HW_BAND_1].assoc_sta_cnt;
PHL_INFO("[assoc_sta_cnt] all:%d, B0:%d, B1:%d\n",
assoc_sta_cnt_all, assoc_sta_cnt_b0, assoc_sta_cnt_b1);
max_macid_num = macid_ctl->max_num;
PHL_INFO("max_macid_num:%d\n", max_macid_num);
_os_spinlock(phl_to_drvpriv(phl_info), &macid_ctl->lock, _bh, NULL);
for(mid = 0; mid < max_macid_num; mid++) {
if (_phl_macid_is_used(macid_ctl->used_map, mid)) {
phl_sta = macid_ctl->sta[mid];
if (phl_sta)
_phl_dump_stainfo(phl_sta);
}
}
_os_spinunlock(phl_to_drvpriv(phl_info), &macid_ctl->lock, _bh, NULL);
if (show_caller)
PHL_INFO("#################################\n");
}
const char *const _rtype_str[] = {
"NONE",
"STA",
"AP",
"VAP",
"ADHOC",
"MASTER",
"MESH",
"MONITOR",
"PD",
"GC",
"GO",
"TDLS",
"NAN",
"NONE"
};
void phl_dump_stainfo_per_role(const char *caller, const int line, bool show_caller,
struct phl_info_t *phl_info, struct rtw_wifi_role_t *wrole)
{
void *drv = phl_to_drvpriv(phl_info);
struct rtw_phl_stainfo_t *sta = NULL;
int sta_cnt = 0;
u8 idx = 0;
struct rtw_wifi_role_link_t *rlink = NULL;
if (show_caller)
PHL_INFO("[STA] ###### FUN - %s LINE - %d #######\n", caller, line);
PHL_INFO("WR_IDX:%d RTYPE:%s, mac-addr:%02x-%02x-%02x-%02x-%02x-%02x\n",
wrole->id,
_rtype_str[wrole->type],
wrole->mac_addr[0], wrole->mac_addr[1], wrole->mac_addr[2],
wrole->mac_addr[3], wrole->mac_addr[4], wrole->mac_addr[5]);
for (idx = 0; idx < wrole->rlink_num; idx++) {
rlink = get_rlink(wrole, idx);
_os_spinlock(drv, &rlink->assoc_sta_queue.lock, _bh, NULL);
if ((wrole->type == PHL_RTYPE_STATION || wrole->type == PHL_RTYPE_P2P_GC)
&& rlink->mstate == MLME_LINKED)
sta_cnt = 1;
else if (wrole->type == PHL_RTYPE_TDLS)
sta_cnt = rlink->assoc_sta_queue.cnt;
else
sta_cnt = rlink->assoc_sta_queue.cnt - 1;
PHL_INFO("link %d assoced STA num: %d\n", idx, sta_cnt);
phl_list_for_loop(sta, struct rtw_phl_stainfo_t, &rlink->assoc_sta_queue.queue, list) {
if (sta)
_phl_dump_stainfo(sta);
}
_os_spinunlock(drv, &rlink->assoc_sta_queue.lock, _bh, NULL);
}
if (show_caller)
PHL_INFO("#################################\n");
}
void rtw_phl_sta_dump_info(void *phl, bool show_caller, struct rtw_wifi_role_t *wr, u8 mode)
{
struct phl_info_t *phl_info = (struct phl_info_t *)phl;
if (mode == 1) {
if (show_caller) {
PHL_DUMP_STACTRL_EX(phl_info);
} else {
PHL_DUMP_STACTRL(phl_info);
}
} else if (mode == 2) {
if (show_caller) {
PHL_DUMP_STAINFO_EX(phl_info);
} else {
PHL_DUMP_STAINFO(phl_info);
}
} else if (mode == 3) {
if (show_caller) {
PHL_DUMP_ROLE_STAINFO_EX(phl_info, wr);
} else {
PHL_DUMP_ROLE_STAINFO(phl_info, wr);
}
} else {
if (show_caller) {
PHL_DUMP_STACTRL_EX(phl_info);
PHL_DUMP_STAINFO_EX(phl_info);
PHL_DUMP_ROLE_STAINFO_EX(phl_info, wr);
}
else {
PHL_DUMP_STACTRL(phl_info);
PHL_DUMP_STAINFO(phl_info);
PHL_DUMP_ROLE_STAINFO(phl_info, wr);
}
}
}
#endif /*DBG_PHL_STAINFO*/
static bool
_phl_self_stainfo_chk(struct phl_info_t *phl_info,
struct rtw_wifi_role_t *wrole,
struct rtw_wifi_role_link_t *rlink,
struct rtw_phl_stainfo_t *sta)
{
void *drv = phl_to_drvpriv(phl_info);
bool is_self = false;
switch (wrole->type) {
case PHL_RTYPE_STATION:
case PHL_RTYPE_P2P_GC:
_os_mem_cpy(drv, sta->mac_addr, rlink->mac_addr, MAC_ALEN);
is_self = true;
break;
case PHL_RTYPE_AP:
#ifdef CONFIG_RTW_SUPPORT_MBSSID_VAP
case PHL_RTYPE_VAP:
#endif /* CONFIG_RTW_SUPPORT_MBSSID_VAP */
case PHL_RTYPE_MESH:
case PHL_RTYPE_P2P_GO:
case PHL_RTYPE_TDLS:
if (_os_mem_cmp(drv, rlink->mac_addr, sta->mac_addr, MAC_ALEN) == 0)
is_self = true;
break;
case PHL_RTYPE_NONE:
#ifndef CONFIG_RTW_SUPPORT_MBSSID_VAP
case PHL_RTYPE_VAP:
#endif /* CONFIG_RTW_SUPPORT_MBSSID_VAP */
case PHL_RTYPE_ADHOC:
case PHL_RTYPE_ADHOC_MASTER:
case PHL_RTYPE_MONITOR:
case PHL_RTYPE_P2P_DEVICE:
case PHL_RTYPE_NAN:
case PHL_MLME_MAX:
PHL_TRACE(COMP_PHL_DBG, _PHL_ERR_, "_phl_self_stainfo_chk(): Unsupported case:%d, please check it\n",
wrole->type);
break;
default:
PHL_TRACE(COMP_PHL_DBG, _PHL_ERR_, "_phl_self_stainfo_chk(): role-type(%d) not recognize\n",
wrole->type);
break;
}
return is_self;
}
enum rtw_phl_status
phl_free_stainfo_sw(struct phl_info_t *phl_info, struct rtw_phl_stainfo_t *sta)
{
enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE;
if(sta == NULL) {
PHL_ERR("%s sta is NULL\n", __func__);
return RTW_PHL_STATUS_FAILURE;
}
phl_free_rx_reorder(phl_info, sta);
pstatus = phl_deregister_tx_ring((void *)phl_info, sta->macid);
if (pstatus != RTW_PHL_STATUS_SUCCESS) {
/* For 1 tx ring case, only macid 0 registers tx ring,
but here we deregister tx ring anyway, to prevent possible memory leak */
if (!phl_info->use_onetxring)
PHL_ERR("macid(%d) phl_deregister_tx_ring failed\n", sta->macid);
}
/* release macid for used_map */
pstatus = _phl_release_macid(phl_info, sta);
if (pstatus != RTW_PHL_STATUS_SUCCESS)
PHL_ERR("_phl_release_macid failed\n");
return pstatus;
}
enum rtw_phl_status
__phl_free_stainfo_sw(struct phl_info_t *phl_info, struct rtw_phl_stainfo_t *sta)
{
enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE;
struct stainfo_ctl_t *sta_ctrl = phl_to_sta_ctrl(phl_info);
struct rtw_wifi_role_t *wrole = NULL;
struct rtw_wifi_role_link_t *rlink = NULL;
FUNCIN();
if(sta == NULL) {
PHL_ERR("%s sta is NULL\n", __func__);
goto _exit;
}
wrole = sta->wrole;
rlink = sta->rlink;
if (!is_broadcast_mac_addr(sta->mac_addr)) {
if (_phl_self_stainfo_chk(phl_info, wrole, rlink, sta) == true)
{
pstatus = RTW_PHL_STATUS_SUCCESS;
goto _exit;
}
}
pstatus = phl_stainfo_queue_del(phl_info, &rlink->assoc_sta_queue, sta);
if (pstatus != RTW_PHL_STATUS_SUCCESS) {
PHL_ERR("phl_stainfo_queue_del failed\n");
}
pstatus = phl_free_stainfo_sw(phl_info, sta);
if (pstatus != RTW_PHL_STATUS_SUCCESS) {
PHL_ERR("macid(%d) _phl_free_stainfo_sw failed\n", sta->macid);
}
pstatus = phl_stainfo_enqueue(phl_info, &sta_ctrl->free_sta_queue, sta);
if (pstatus != RTW_PHL_STATUS_SUCCESS)
PHL_ERR("phl_stainfo_enqueue to free queue failed\n");
_exit:
PHL_DUMP_STACTRL_EX(phl_info);
FUNCOUT();
return pstatus;
}
enum rtw_phl_status
rtw_phl_free_stainfo_sw(void *phl, struct rtw_phl_stainfo_t *sta)
{
return __phl_free_stainfo_sw((struct phl_info_t *)phl, sta);
}
enum rtw_phl_status
phl_free_stainfo_hw(struct phl_info_t *phl_info,
struct rtw_phl_stainfo_t *sta)
{
enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE;
if (sta == NULL) {
PHL_ERR("%s sta == NULL\n", __func__);
goto _exit;
}
phl_pkt_ofld_del_entry(phl_info, sta->macid);
sta->active = false;
if (rtw_hal_del_sta_entry(phl_info->hal, sta) == RTW_HAL_STATUS_SUCCESS)
pstatus = RTW_PHL_STATUS_SUCCESS;
else
PHL_ERR("rtw_hal_del_sta_entry failed\n");
_exit:
return pstatus;
}
enum rtw_phl_status
__phl_free_stainfo_hw(struct phl_info_t *phl_info, struct rtw_phl_stainfo_t *sta)
{
struct rtw_wifi_role_t *wrole = sta->wrole;
struct rtw_wifi_role_link_t *rlink = sta->rlink;
if (!is_broadcast_mac_addr(sta->mac_addr)) {
if (_phl_self_stainfo_chk(phl_info, wrole, rlink, sta) == true)
return RTW_PHL_STATUS_SUCCESS;
}
return phl_free_stainfo_hw(phl_info, sta);
}
static enum rtw_phl_status
__phl_free_stainfo(struct phl_info_t *phl, struct rtw_phl_stainfo_t *sta)
{
enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE;
struct rtw_wifi_role_t *wrole = sta->wrole;
u16 macid = sta->macid;
bool notify = false;
if (rtw_phl_role_is_ap_category(wrole) &&
_phl_self_stainfo_chk(phl, wrole, sta->rlink, sta) == false)
notify = true;
pstatus = __phl_free_stainfo_hw(phl, sta);
if (pstatus != RTW_PHL_STATUS_SUCCESS)
PHL_ERR("__phl_free_stainfo_hw failed\n");
pstatus = __phl_free_stainfo_sw(phl, sta);
if (pstatus != RTW_PHL_STATUS_SUCCESS)
PHL_ERR("__phl_free_stainfo_sw failed\n");
else if (notify)
phl_role_ap_client_notify(phl, wrole, MLME_NO_LINK, macid);
return pstatus;
}
u8 _phl_tx_ring_register_decision(struct phl_info_t *phl_info,
struct rtw_phl_stainfo_t *sta)
{
/* In one TXQ case, only register 1 shared tring to save memory usage */
if (sta->macid != 0 && phl_info->use_onetxring) {
return false;
}
return true;
}
static enum rtw_phl_status
_phl_alloc_stainfo_sw(struct phl_info_t *phl_info,
enum rtw_device_type dtype,
u16 main_id,
struct rtw_phl_stainfo_t *sta)
{
enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE;
struct rtw_wifi_role_link_t *rlink = sta->rlink;
pstatus = _phl_alloc_macid(phl_info, dtype, main_id, sta);
if (pstatus != RTW_PHL_STATUS_SUCCESS) {
PHL_ERR("%s allocate macid failure!\n", __func__);
goto error_alloc_macid;
}
if (_phl_tx_ring_register_decision(phl_info, sta) == true) {
pstatus = phl_register_tx_ring(phl_info,
sta->macid,
rlink->hw_band,
rlink->hw_wmm,
rlink->hw_port);
if (pstatus != RTW_PHL_STATUS_SUCCESS) {
PHL_ERR("%s register_tx_ring failure!\n", __func__);
goto error_register_tx_ring;
}
}
pstatus = RTW_PHL_STATUS_SUCCESS;
return pstatus;
error_register_tx_ring:
_phl_release_macid(phl_info, sta);
error_alloc_macid:
return pstatus;
}
static void _phl_sta_set_default_value(struct phl_info_t *phl_info,
struct rtw_phl_stainfo_t *phl_sta)
{
phl_sta->bcn_hit_cond = 0; /* beacon:A3 probersp: A1 & A3 */
phl_sta->rts_en = 0;
phl_sta->cts2self = 0;
phl_sta->hw_rts_en = 0;
phl_sta->rts_cca_mode = 0;
phl_sta->rts_sel = 0;
/* fit rule
* 0: A1 & A2
* 1: A1 & A3
*
* Rule 0 should be used for both AP and STA modes.
*
* For STA, A3 is source address(SA) which can be any peer on the LAN.
*
* For AP, A3 is destination address(DA) which can also be any node
* on the LAN. A1 & A2 match find the address CAM entry that contains the
* correct security CAM ID and MAC ID.
*/
phl_sta->hit_rule = 0;
phl_sta->flag_pwr_diff_large = false;
phl_sta->tx_bw_mode = CHANNEL_WIDTH_MAX;
}
struct rtw_phl_stainfo_t *
phl_alloc_stainfo_sw(struct phl_info_t *phl_info,
u8 *sta_addr,
struct rtw_wifi_role_t *wrole,
enum rtw_device_type dtype,
u16 main_id,
struct rtw_wifi_role_link_t *rlink)
{
struct stainfo_ctl_t *sta_ctrl = phl_to_sta_ctrl(phl_info);
struct rtw_phl_stainfo_t *phl_sta = NULL;
void *drv = phl_to_drvpriv(phl_info);
bool bmc_sta = false;
FUNCIN();
if (is_broadcast_mac_addr(sta_addr))
bmc_sta = true;
/* if sta_addr is bmc addr, allocate new sta_info */
if ((wrole->type == PHL_RTYPE_STATION || wrole->type == PHL_RTYPE_P2P_GC)
&& (!bmc_sta)) {
phl_sta = rtw_phl_get_stainfo_self(phl_info, rlink);
if (phl_sta) {
_os_mem_cpy(drv, phl_sta->mac_addr, sta_addr, MAC_ALEN);
goto _exit;
}
}
/* check station info exist */
phl_sta = rtw_phl_get_stainfo_by_addr(phl_info,
wrole,
rlink,
sta_addr);
if (phl_sta) {
PHL_INFO("%s phl_sta(%02x:%02x:%02x:%02x:%02x:%02x) exist\n",
__func__, sta_addr[0], sta_addr[1], sta_addr[2],
sta_addr[3], sta_addr[4], sta_addr[5]);
goto _exit;
}
phl_sta = phl_stainfo_dequeue(phl_info, &sta_ctrl->free_sta_queue);
if (phl_sta == NULL) {
PHL_ERR("allocate phl_sta failure!\n");
goto _exit;
}
_os_mem_cpy(drv, phl_sta->mac_addr, sta_addr, MAC_ALEN);
phl_sta->wrole = wrole;
phl_sta->rlink = rlink;
/* initialize link ID, STA mode may change link ID while doing association */
phl_sta->link_id = rlink->id;
phl_sta->tid_ul_map = 0xff; /* All tid are enabled for uplink */
phl_sta->tid_dl_map = 0xff; /* All tid are enabled for downlink */
if (_phl_alloc_stainfo_sw(phl_info, dtype, main_id, phl_sta) != RTW_PHL_STATUS_SUCCESS) {
PHL_ERR("_phl_alloc_stainfo_sw failed\n");
goto error_alloc_sta;
}
_phl_sta_set_default_value(phl_info, phl_sta);
phl_stainfo_enqueue(phl_info, &rlink->assoc_sta_queue, phl_sta);
_exit:
PHL_DUMP_STACTRL_EX(phl_info);
FUNCOUT();
return phl_sta;
error_alloc_sta:
phl_stainfo_enqueue(phl_info, &sta_ctrl->free_sta_queue, phl_sta);
phl_sta = NULL;
PHL_DUMP_STACTRL_EX(phl_info);
FUNCOUT();
return phl_sta;
}
bool
phl_sta_latency_sensitive_chk(struct phl_info_t *phl,
struct rtw_phl_stainfo_t *sta,
enum rtw_lat_sen_chk type)
{
bool ret = false;
if (!TEST_STATUS_FLAG(sta->wrole->status, WR_STATUS_LAT_SEN))
return ret;
#ifdef CONFIG_POWER_SAVE
if (LAT_SEN_CHK_LPS_TX == type) {
if (phl_ps_sta_in_lps(phl, sta))
ret = true;
} else
#endif /* CONFIG_POWER_SAVE */
if (LAT_SEN_CHK_BCN_TRACKING == type) {
ret = true;
} else {
ret = false;
}
return ret;
}
struct rtw_phl_stainfo_t *
rtw_phl_alloc_stainfo_sw(void *phl,
u8 *sta_addr,
struct rtw_wifi_role_t *wrole,
enum rtw_device_type dtype,
u16 main_id,
struct rtw_wifi_role_link_t *rlink)
{
return phl_alloc_stainfo_sw((struct phl_info_t *)phl,
sta_addr,
wrole,
dtype,
main_id,
rlink);
}
enum rtw_phl_status
phl_alloc_stainfo_hw(struct phl_info_t *phl_info, struct rtw_phl_stainfo_t *sta)
{
enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE;
if (sta == NULL) {
PHL_ERR("%s sta == NULL\n", __func__);
goto _exit;
}
if (rtw_hal_add_sta_entry(phl_info->hal, sta) != RTW_HAL_STATUS_SUCCESS) {
PHL_ERR("%s rtw_hal_add_sta_entry failure!\n", __func__);
goto _exit;
}
sta->active = true;
pstatus = phl_pkt_ofld_add_entry(phl_info, sta->macid);
if (RTW_PHL_STATUS_SUCCESS != pstatus)
PHL_ERR("%s phl_pkt_ofld_add_entry failure!\n", __func__);
_exit:
return pstatus;
}
enum rtw_phl_status
__phl_alloc_stainfo_hw(struct phl_info_t *phl_info,
struct rtw_wifi_role_t *wrole,
struct rtw_wifi_role_link_t *rlink,
struct rtw_phl_stainfo_t *sta)
{
enum rtw_phl_status psts = RTW_PHL_STATUS_SUCCESS;
psts = phl_alloc_stainfo_hw(phl_info, sta);
if (psts == RTW_PHL_STATUS_FAILURE) {
PHL_ERR("phl_alloc_stainfo_hw failed\n");
goto _exit;
}
if(rtw_phl_role_is_ap_category(wrole) &&
_phl_self_stainfo_chk(phl_info, wrole, rlink, sta) == false)
phl_role_ap_client_notify(phl_info, wrole, MLME_LINKING, sta->macid);
_exit :
return psts;
}
static enum rtw_phl_status
__phl_alloc_stainfo(struct phl_info_t *phl,
struct rtw_phl_stainfo_t **sta,
u8 *sta_addr,
struct rtw_wifi_role_t *wrole,
enum rtw_device_type dtype,
u16 main_id,
struct rtw_wifi_role_link_t *rlink)
{
struct rtw_phl_stainfo_t *alloc_sta = NULL;
enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE;
alloc_sta = phl_alloc_stainfo_sw(phl,
sta_addr,
wrole,
dtype,
main_id,
rlink);
if (alloc_sta == NULL) {
PHL_ERR("%s can't alloc stainfo\n", __func__);
*sta = alloc_sta;
goto _exit;
}
if (alloc_sta->active == false) {
pstatus = __phl_alloc_stainfo_hw(phl, wrole, rlink, alloc_sta);
if (pstatus != RTW_PHL_STATUS_SUCCESS) {
PHL_ERR("__phl_alloc_stainfo_hw failed\n");
goto _err_alloc_sta_hw;
}
}
PHL_INFO("%s success - macid:%u %02x:%02x:%02x:%02x:%02x:%02x\n",
__func__, alloc_sta->macid,
alloc_sta->mac_addr[0], alloc_sta->mac_addr[1], alloc_sta->mac_addr[2],
alloc_sta->mac_addr[3], alloc_sta->mac_addr[4], alloc_sta->mac_addr[5]);
*sta = alloc_sta;
return RTW_PHL_STATUS_SUCCESS;
_err_alloc_sta_hw:
__phl_free_stainfo_sw(phl, alloc_sta);
*sta = alloc_sta = NULL;
_exit:
return RTW_PHL_STATUS_FAILURE;
}
static enum rtw_phl_status
_phl_alloc_stainfo(struct phl_info_t *phl,
struct rtw_phl_stainfo_t **sta,
u8 *sta_addr,
struct rtw_wifi_role_t *wrole,
enum rtw_device_type dtype,
u16 main_id,
struct rtw_wifi_role_link_t *rlink,
bool alloc,
bool only_hw)
{
enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE;
if (alloc) {
if (only_hw)
pstatus = __phl_alloc_stainfo_hw(phl, wrole, rlink, *sta);
else
pstatus = __phl_alloc_stainfo(phl, sta, sta_addr, wrole, dtype, main_id, rlink);
} else {
if (only_hw)
pstatus = __phl_free_stainfo_hw(phl, *sta);
else
pstatus = __phl_free_stainfo(phl, *sta);
}
return pstatus;
}
enum rtw_phl_status
phl_update_stainfo_sw(struct phl_info_t *phl_info, struct rtw_phl_stainfo_t *sta)
{
enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE;
u8 hw_band = sta->rlink->hw_band;
u8 hw_wmm = sta->rlink->hw_wmm;
u8 hw_port = sta->rlink->hw_port;
#ifdef DBG_DBCC_MONITOR_TIME
u32 start_t = 0;
PHL_FUN_MON_START(&start_t);
#endif /* DBG_DBCC_MONITOR_TIME */
pstatus = phl_re_register_tx_ring(phl_info,
sta->macid,
hw_band,
hw_wmm,
hw_port);
if (pstatus != RTW_PHL_STATUS_SUCCESS) {
PHL_ERR("%s reregister_tx_ring failure!\n", __func__);
}
#ifdef DBG_DBCC_MONITOR_TIME
PHL_FUNC_MON_END(phl_info->phl_com, &start_t, TIME_PHL_MAX);
#endif /* DBG_DBCC_MONITOR_TIME */
return pstatus;
}
#ifdef CONFIG_CMD_DISP
struct cmd_stainfo_param {
struct rtw_phl_stainfo_t **sta;
u8 sta_addr[MAC_ALEN];
struct rtw_wifi_role_t *wrole;
enum rtw_device_type dtype;
u16 main_id;
struct rtw_wifi_role_link_t *rlink;
bool alloc;
bool only_hw;
};
static void
_phl_cmd_alloc_stainfo_done(void *drv_priv,
u8 *cmd,
u32 cmd_len,
enum rtw_phl_status status)
{
if (cmd)
_os_kmem_free(drv_priv, cmd, cmd_len);
}
static enum rtw_phl_status
_phl_cmd_alloc_stainfo(struct phl_info_t *phl_info,
struct rtw_phl_stainfo_t **sta,
u8 *sta_addr,
struct rtw_wifi_role_t *wrole,
enum rtw_device_type dtype,
u16 main_id,
struct rtw_wifi_role_link_t *rlink,
bool alloc,
bool only_hw,
enum phl_cmd_type cmd_type,
u32 cmd_timeout)
{
void *drv = phl_to_drvpriv(phl_info);
enum rtw_phl_status psts = RTW_PHL_STATUS_FAILURE;
struct cmd_stainfo_param *param = NULL;
u32 param_len = 0;
u8 hw_band = rlink->hw_band;
if (cmd_type == PHL_CMD_DIRECTLY) {
psts = _phl_alloc_stainfo(phl_info,
sta,
sta_addr,
wrole,
dtype,
main_id,
rlink,
alloc,
only_hw);
goto _exit;
}
param_len = sizeof(struct cmd_stainfo_param);
param = _os_kmem_alloc(drv, param_len);
if (param == NULL) {
PHL_ERR("%s: alloc param failed!\n", __func__);
psts = RTW_PHL_STATUS_RESOURCE;
goto _exit;
}
_os_mem_set(drv, param, 0, param_len);
param->sta = sta;
_os_mem_cpy(drv, param->sta_addr, sta_addr, MAC_ALEN);
param->wrole = wrole;
param->rlink = rlink;
param->dtype = dtype;
param->main_id = main_id;
param->alloc = alloc;
param->only_hw = only_hw;
psts = phl_cmd_enqueue(phl_info,
hw_band,
MSG_EVT_STA_INFO_CTRL,
(u8 *)param,
param_len,
_phl_cmd_alloc_stainfo_done,
cmd_type,
cmd_timeout);
if (is_cmd_failure(psts)) {
/* Send cmd success, but wait cmd fail*/
psts = RTW_PHL_STATUS_FAILURE;
} else if (psts != RTW_PHL_STATUS_SUCCESS) {
/* Send cmd fail */
psts = RTW_PHL_STATUS_FAILURE;
_os_kmem_free(drv, param, param_len);
}
_exit:
return psts;
}
enum rtw_phl_status
phl_cmd_alloc_stainfo_hdl(struct phl_info_t *phl_info, u8 *param)
{
struct cmd_stainfo_param *cmd_sta_param = (struct cmd_stainfo_param *)param;
return _phl_alloc_stainfo(phl_info,
cmd_sta_param->sta,
cmd_sta_param->sta_addr,
cmd_sta_param->wrole,
cmd_sta_param->dtype,
cmd_sta_param->main_id,
cmd_sta_param->rlink,
cmd_sta_param->alloc,
cmd_sta_param->only_hw);
}
#endif /* CONFIG_CMD_DISP */
enum rtw_phl_status
rtw_phl_cmd_alloc_stainfo(void *phl,
struct rtw_phl_stainfo_t **sta,
u8 *sta_addr,
struct rtw_wifi_role_t *wrole,
enum rtw_device_type dtype,
u16 main_id,
struct rtw_wifi_role_link_t *rlink,
bool alloc,
bool only_hw,
enum phl_cmd_type cmd_type,
u32 cmd_timeout)
{
#ifdef CONFIG_CMD_DISP
return _phl_cmd_alloc_stainfo(phl,
sta,
sta_addr,
wrole,
dtype,
main_id,
rlink,
alloc,
only_hw,
cmd_type,
cmd_timeout);
#else
PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_, "%s: not support alloc stainfo cmd\n",
__func__);
return _phl_alloc_stainfo((struct phl_info_t *)phl,
sta,
sta_addr,
wrole,
dtype,
main_id,
rlink,
alloc,
only_hw);
#endif /* CONFIG_CMD_DISP */
}
enum rtw_phl_status
phl_wifi_role_free_stainfo_hw(struct phl_info_t *phl_info,
struct rtw_wifi_role_t *wrole)
{
struct macid_ctl_t *mc = phl_to_mac_ctrl(phl_info);
u16 max_macid_num = mc->max_num;
struct rtw_phl_stainfo_t *sta = NULL;
u32 *used_map;
u16 mid;
used_map = &mc->wifi_role_usedmap[wrole->id][0];
for(mid = 0; mid < max_macid_num; mid++) {
if (_phl_macid_is_used(used_map, mid)) {
sta = mc->sta[mid];
if (sta) {
PHL_INFO("%s [WR-%d] free sta_info(MID:%d)\n",
__func__, wrole->id, sta->macid);
rtw_hal_disconnect_drop_all_pkt(phl_info->hal, sta);
phl_free_stainfo_hw(phl_info, sta);
}
}
}
return RTW_PHL_STATUS_SUCCESS;
}
enum rtw_phl_status
phl_wifi_role_free_stainfo_sw(struct phl_info_t *phl_info,
struct rtw_wifi_role_t *role)
{
struct rtw_phl_stainfo_t *phl_sta = NULL;
struct stainfo_ctl_t *sta_ctrl = phl_to_sta_ctrl(phl_info);
u8 lidx;
struct rtw_wifi_role_link_t *rlink;
PHL_DUMP_STACTRL_EX(phl_info);
for (lidx = 0; lidx < role->rlink_num; lidx++) {
rlink = get_rlink(role, lidx);
do {
phl_sta = phl_stainfo_dequeue(phl_info, &rlink->assoc_sta_queue);
if (phl_sta) {
phl_free_stainfo_sw(phl_info, phl_sta);
phl_stainfo_enqueue(phl_info,
&sta_ctrl->free_sta_queue,
phl_sta);
}
} while(phl_sta != NULL);
}
return RTW_PHL_STATUS_SUCCESS;
}
enum rtw_phl_status
phl_wifi_role_free_stainfo(struct phl_info_t *phl_info,
struct rtw_wifi_role_t *role)
{
struct rtw_phl_stainfo_t *phl_sta = NULL;
struct stainfo_ctl_t *sta_ctrl = phl_to_sta_ctrl(phl_info);
u8 lidx;
struct rtw_wifi_role_link_t *rlink;
PHL_DUMP_STACTRL_EX(phl_info);
for (lidx = 0; lidx < role->rlink_num; lidx++) {
rlink = get_rlink(role, lidx);
do {
phl_sta = phl_stainfo_dequeue(phl_info, &rlink->assoc_sta_queue);
if (phl_sta) {
phl_free_stainfo_hw(phl_info, phl_sta);
phl_free_stainfo_sw(phl_info, phl_sta);
phl_stainfo_enqueue(phl_info,
&sta_ctrl->free_sta_queue,
phl_sta);
}
} while(phl_sta != NULL);
}
return RTW_PHL_STATUS_SUCCESS;
}
/**
* According to 802.11 spec 26.5.2.3.2
* We shall not transmit HE TB PPDU with RU-26 on DFS channel
*/
static void
_phl_set_dfs_tb_ctrl(struct phl_info_t *phl_info,
struct rtw_wifi_role_link_t *rlink)
{
bool is_dfs = rlink->chandef.is_dfs;
rtw_hal_set_dfs_tb_ctrl(phl_info->hal, is_dfs);
}
static void
_phl_no_link_reset_sta_info(struct phl_info_t *phl_info, struct rtw_phl_stainfo_t *sta)
{
void *drv = phl_to_drvpriv(phl_info);
/* asoc cap */
_os_mem_set(drv, &sta->asoc_cap, 0, sizeof(struct protocol_cap_t));
/* other capabilities under stainfo need to reset with default value */
sta->tf_trs = 0;
/* protection mode */
sta->protect = RTW_PROTECT_DISABLE;
}
/* If all rlink->mstate == mstate, return TRUE */
static bool
_phl_chk_all_rlink_mstate(struct rtw_wifi_role_t *wrole, enum mlme_state mstate)
{
bool ret = true;
u8 idx = 0;
struct rtw_wifi_role_link_t *rlink = NULL;
for (idx = 0; idx < wrole->rlink_num; idx++) {
rlink = get_rlink(wrole,idx);
if (rlink->mstate != mstate) {
ret = false;
break;
}
}
return ret;
}
static enum rtw_phl_status
phl_update_media_status(struct phl_info_t *phl_info, struct rtw_phl_stainfo_t *sta,
u8 *sta_addr, bool is_connect)
{
struct rtw_wifi_role_t *wrole = sta->wrole;
struct rtw_wifi_role_link_t *rlink = sta->rlink;
void *drv = phl_to_drvpriv(phl_info);
enum rtw_hal_status hstatus = RTW_HAL_STATUS_FAILURE;
enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE;
bool is_sta_linked = false;
u8 rsc_cfg = 0x0;
bool rrsr_ref_rate_sel = true;
struct rtw_phl_com_t *phl_com = phl_info->phl_com;
#ifdef CONFIG_DIG_TDMA
struct rtw_phl_stainfo_t *self_sta = rtw_phl_get_stainfo_self(phl_info, rlink);
#endif
is_sta_linked = rtw_hal_is_sta_linked(phl_info->hal, sta);
if (is_connect == true && is_sta_linked == true) {
PHL_ERR("%s STA (MAC_ID:%d) had connected\n", __func__, sta->macid);
goto _exit;
}
if (is_connect == false && is_sta_linked == false) {
/* handle connect abort case */
if (rlink->mstate == MLME_LINKING) {
PHL_INFO("%s MAC_ID(%d) connect abort\n", __func__, sta->macid);
pstatus = RTW_PHL_STATUS_SUCCESS;
} else {
PHL_ERR("%s MAC_ID(%d) had disconnected\n", __func__, sta->macid);
}
if (wrole->type == PHL_RTYPE_STATION || wrole->type == PHL_RTYPE_P2P_GC)
rlink->mstate = MLME_NO_LINK;
goto _exit;
}
/* reset trx statistics */
if (is_connect == false) {
phl_reset_tx_stats(&sta->stats);
phl_reset_rx_stats(&sta->stats);
_phl_no_link_reset_sta_info(phl_info, sta);
CLEAR_STATUS_FLAG(rlink->status, RLINK_STATUS_TSF_SYNC);
} else {
phl_clean_sta_bcn_info(phl_info, sta);
#ifdef BCN_TRACKING_TEST
PHL_ERR("%s: Overwritre bcv_intvl from %d to %d for BCN_TRACKING_TEST\n",
__func__, sta->asoc_cap.bcn_interval, T_B_INTVL);
sta->asoc_cap.bcn_interval = T_B_INTVL;
#endif /* BCN_TRACKING_TEST */
}
/* Configure address cam, including net_type and sync_tsf */
if ((wrole->type == PHL_RTYPE_STATION) || (wrole->type == PHL_RTYPE_P2P_GC)
#ifdef CONFIG_PHL_TDLS
/* STA disconnects with the associated AP before tearing down with TDLS peers */
|| ((wrole->type == PHL_RTYPE_TDLS) && (!sta_addr))
#endif
) {
if (is_connect) {
rlink->mstate = MLME_LINKED;
_os_mem_cpy(drv, sta->mac_addr, sta_addr, MAC_ALEN);
_phl_set_dfs_tb_ctrl(phl_info, rlink);
} else {
rlink->mstate = MLME_NO_LINK;
}
}
hstatus = rtw_hal_update_sta_entry(phl_com, phl_info->hal, sta, is_connect);
if (hstatus != RTW_HAL_STATUS_SUCCESS) {
PHL_ERR("rtw_hal_update_sta_entry failure!\n");
goto _exit;
}
if (PHL_RTYPE_STATION == wrole->type) {
if (is_connect) {
if (rlink->chandef.band == BAND_ON_6G) {
rsc_cfg = phl_com->phy_sw_cap[rlink->hw_band].rsc_mode.rsc_6g;
rrsr_ref_rate_sel = false;
}
else if (rlink->chandef.band == BAND_ON_5G) {
rsc_cfg = phl_com->phy_sw_cap[rlink->hw_band].rsc_mode.rsc_5g;
rrsr_ref_rate_sel = false;
}
else {
rsc_cfg = phl_com->phy_sw_cap[rlink->hw_band].rsc_mode.rsc_2g;
rrsr_ref_rate_sel = false;
}
hstatus = rtw_hal_cfg_rsc(phl_info->hal, sta, rsc_cfg);
if (hstatus != RTW_HAL_STATUS_SUCCESS)
PHL_ERR("rtw_hal_cfg_rsc failed\n");
hstatus = rtw_hal_cfg_rrsr_ref_rate_sel(phl_info->hal, sta, rrsr_ref_rate_sel);
if (hstatus != RTW_HAL_STATUS_SUCCESS)
PHL_ERR("rtw_hal_cfg_rsc failed\n");
} else {
rsc_cfg = 0x0;
hstatus = rtw_hal_cfg_rsc(phl_info->hal, sta, rsc_cfg);
if (hstatus != RTW_HAL_STATUS_SUCCESS)
PHL_ERR("rtw_hal_cfg_rsc failed\n");
rrsr_ref_rate_sel = false;
hstatus = rtw_hal_cfg_rrsr_ref_rate_sel(phl_info->hal, sta, rrsr_ref_rate_sel);
if (hstatus != RTW_HAL_STATUS_SUCCESS)
PHL_ERR("rtw_hal_cfg_rsc failed\n");
}
} else {
PHL_INFO("%s: no need to modify rsc config, role = %d\n", __FUNCTION__, wrole->type);
}
/* For P2P GO SOURCE or GO SINK(one-to-one link), need to notify BB to restore DIG after connect complete*/
#ifdef CONFIG_DIG_TDMA
if ((self_sta->p2p_session == RTW_P2P_APP_GO_SRC) || (self_sta->p2p_session == RTW_P2P_APP_GO_SINK)
|| (self_sta->p2p_session == RTW_P2P_APP_GO_SRC_SINK)) {
if (is_connect) {
rtw_hal_notification(phl_info->hal, MSG_EVT_P2P_SESSION_LINKED, HW_BAND_0);
PHL_TRACE(COMP_PHL_P2PPS, _PHL_INFO_, "%s: notify MSG_EVT_P2P_SESSION_LINKED\n",
__FUNCTION__);
} else {
rtw_hal_notification(phl_info->hal, MSG_EVT_P2P_SESSION_NO_LINK, HW_BAND_0);
PHL_TRACE(COMP_PHL_P2PPS, _PHL_INFO_, "%s: notify MSG_EVT_P2P_SESSION_NO_LINK\n",
__FUNCTION__);
}
}
#endif
if (wrole->type == PHL_RTYPE_STATION || wrole->type == PHL_RTYPE_P2P_GC
#ifdef CONFIG_PHL_TDLS
/* STA disconnects with the associated AP before tearing down with TDLS peers */
|| ((wrole->type == PHL_RTYPE_TDLS) && (!sta_addr))
#endif
) {
hstatus = rtw_hal_role_cfg(phl_info->hal, wrole, rlink);
if (hstatus != RTW_HAL_STATUS_SUCCESS) {
PHL_ERR("rtw_hal_role_cfg failure!\n");
goto _exit;
}
}
pstatus = RTW_PHL_STATUS_SUCCESS;
/* TODO: Configure RCR */
_exit:
/* Check wrole->mstate
* If all rlink->mstate is MLME_NO_LINK, wrole->mstate is MLME_NO_LINK
* If any rlink->mstate is MLME_LINKED, wrole->mstate is MLME_LINKED
*/
if (_phl_chk_all_rlink_mstate(wrole, MLME_NO_LINK) == true)
wrole->mstate = MLME_NO_LINK;
else
wrole->mstate = MLME_LINKED;
/*PHL_DUMP_STAINFO_EX(phl_info);*/
return pstatus;
}
#ifdef CONFIG_CMD_DISP
struct sta_media_param {
struct rtw_phl_stainfo_t *sta;
u8 sta_addr[MAC_ALEN];
bool is_connect;
};
enum rtw_phl_status
phl_update_media_status_hdl(struct phl_info_t *phl_info, u8 *param)
{
struct sta_media_param *media_sts = (struct sta_media_param *)param;
return phl_update_media_status(phl_info,
media_sts->sta, media_sts->sta_addr, media_sts->is_connect);
}
void phl_update_media_status_done(void *drv_priv, u8 *cmd, u32 cmd_len,
enum rtw_phl_status status)
{
if (cmd) {
_os_kmem_free(drv_priv, cmd, cmd_len);
cmd = NULL;
}
}
#endif
enum rtw_phl_status
rtw_phl_cmd_update_media_status(void *phl,
struct rtw_phl_stainfo_t *sta,
u8 *sta_addr,
bool is_connect,
enum phl_cmd_type cmd_type,
u32 cmd_timeout)
{
#ifdef CONFIG_CMD_DISP
enum rtw_phl_status psts = RTW_PHL_STATUS_FAILURE;
struct phl_info_t *phl_info = (struct phl_info_t *)phl;
void *drv = phl_to_drvpriv(phl_info);
struct rtw_wifi_role_t *wrole = NULL;
struct sta_media_param *sta_ms = NULL;
u32 sta_ms_len = 0;
if (cmd_type == PHL_CMD_DIRECTLY) {
psts = phl_update_media_status(phl_info, sta, sta_addr, is_connect);
goto _exit;
}
sta_ms_len = sizeof(struct sta_media_param);
sta_ms = _os_kmem_alloc(drv, sta_ms_len);
if (sta_ms == NULL) {
PHL_ERR("%s: alloc sta media status param failed!\n", __func__);
psts = RTW_PHL_STATUS_RESOURCE;
goto _exit;
}
_os_mem_set(drv, sta_ms, 0, sta_ms_len);
sta_ms->sta = sta;
sta_ms->is_connect = is_connect;
if (is_connect && sta_addr)
_os_mem_cpy(drv, sta_ms->sta_addr, sta_addr, MAC_ALEN);
wrole = sta->wrole;
psts = phl_cmd_enqueue(phl_info,
sta->rlink->hw_band,
MSG_EVT_STA_MEDIA_STATUS_UPT,
(u8*)sta_ms,
sta_ms_len,
phl_update_media_status_done,
cmd_type,
cmd_timeout);
if (is_cmd_failure(psts)) {
/* Send cmd success, but wait cmd fail*/
psts = RTW_PHL_STATUS_FAILURE;
} else if (psts != RTW_PHL_STATUS_SUCCESS) {
/* Send cmd fail */
psts = RTW_PHL_STATUS_FAILURE;
_os_kmem_free(drv, sta_ms, sta_ms_len);
}
_exit:
return psts;
#else
PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_, "%s: not support cmd to update media status\n",
__func__);
return phl_update_media_status((struct phl_info_t *)phl, sta, sta_addr, is_connect);
#endif
}
/**
* This function is called once station info changed
* (BW/NSS/RAMASK/SEC/ROLE/MACADDR........)
* @phl: see phl_info_t
* @stainfo: information is updated through phl_station_info
* @mode: see phl_upd_mode
*/
enum rtw_phl_status
phl_change_stainfo(struct phl_info_t *phl_info, struct rtw_phl_stainfo_t *sta,
enum phl_upd_mode mode)
{
enum rtw_hal_status hstatus = RTW_HAL_STATUS_FAILURE;
hstatus = rtw_hal_change_sta_entry(phl_info->hal, sta, mode);
if (hstatus != RTW_HAL_STATUS_SUCCESS) {
PHL_ERR("rtw_hal_change_sta_entry failure!\n");
return RTW_PHL_STATUS_FAILURE;
}
return RTW_PHL_STATUS_SUCCESS;
}
static enum rtw_phl_status
_change_stainfo(struct phl_info_t *phl_info,
struct rtw_phl_stainfo_t *sta, enum sta_chg_id chg_id, u8 *chg_info, u8 chg_info_len)
{
enum phl_upd_mode mode = PHL_UPD_STA_INFO_CHANGE;
switch (chg_id) {
case STA_CHG_BW:
case STA_CHG_NSS:
case STA_CHG_RAMASK:
case STA_CHG_VCS:
{
PHL_INFO("%s MACID:%d %02x:%02x:%02x:%02x:%02x:%02x update bw\n",
__func__, sta->macid,
sta->mac_addr[0], sta->mac_addr[1], sta->mac_addr[2],
sta->mac_addr[3], sta->mac_addr[4], sta->mac_addr[5]);
}
break;
case STA_CHG_SEC_MODE:
sta->sec_mode = *((u8*)chg_info);
break;
case STA_CHG_MBSSID:
sta->addr_sel = 1;
sta->addr_msk = *((u8*)chg_info);
break;
case STA_CHG_RA_GILTF:
sta->hal_sta->ra_info.cal_giltf = *((u8*)chg_info);
sta->hal_sta->ra_info.fix_giltf_en = true;
PHL_INFO("%s: Config RA GI LTF = %d\n", __FUNCTION__, *((u8*)chg_info));
break;
case STA_CHG_MAX:
PHL_TRACE(COMP_PHL_DBG, _PHL_DEBUG_, "rtw_phl_change_stainfo(): Unsupported case:%d, please check it\n",
chg_id);
break;
default:
PHL_TRACE(COMP_PHL_DBG, _PHL_DEBUG_, "rtw_phl_change_stainfo(): Unrecognize case:%d, please check it\n",
chg_id);
break;
}
return phl_change_stainfo(phl_info, sta, mode);
}
#ifdef CONFIG_CMD_DISP
struct sta_chg_param {
struct rtw_phl_stainfo_t *sta;
enum sta_chg_id id;
u8 *info;
u8 info_len;
};
enum rtw_phl_status
phl_cmd_change_stainfo_hdl(struct phl_info_t *phl_info, u8 *param)
{
struct sta_chg_param *sta_param = (struct sta_chg_param *)param;
return _change_stainfo(phl_info,
sta_param->sta, sta_param->id,
sta_param->info, sta_param->info_len);
}
static void
_phl_cmd_change_stainfo_done(void *drv_priv, u8 *cmd, u32 cmd_len,
enum rtw_phl_status status)
{
struct sta_chg_param *sta_chg_info = NULL;
if (cmd == NULL || cmd_len == 0) {
PHL_ERR("%s buf == NULL || buf_len == 0\n", __func__);
_os_warn_on(1);
return;
}
sta_chg_info = (struct sta_chg_param *)cmd;
PHL_INFO("%s - id:%d .....\n", __func__, sta_chg_info->id);
if (sta_chg_info->info && sta_chg_info->info_len > 0)
_os_kmem_free(drv_priv, sta_chg_info->info, sta_chg_info->info_len);
_os_kmem_free(drv_priv, cmd, cmd_len);
cmd = NULL;
}
static enum rtw_phl_status
_phl_cmd_change_stainfo(struct phl_info_t *phl_info,
struct rtw_phl_stainfo_t *sta, enum sta_chg_id chg_id,
u8 *chg_info, u8 chg_info_len,
enum phl_cmd_type cmd_type, u32 cmd_timeout)
{
void *drv = phl_to_drvpriv(phl_info);
enum rtw_phl_status psts = RTW_PHL_STATUS_FAILURE;
struct sta_chg_param *param = NULL;
u8 param_len = 0;
if (cmd_type == PHL_CMD_DIRECTLY) {
psts = _change_stainfo(phl_info, sta, chg_id, chg_info, chg_info_len);
goto _exit;
}
param_len = sizeof(struct sta_chg_param);
param = _os_kmem_alloc(drv, param_len);
if (param == NULL) {
PHL_ERR("%s: alloc param failed!\n", __func__);
psts = RTW_PHL_STATUS_RESOURCE;
goto _exit;
}
_os_mem_set(drv, param, 0, param_len);
param->sta = sta;
param->id = chg_id;
param->info_len = chg_info_len;
if (chg_info_len > 0) {
param->info = _os_kmem_alloc(drv, chg_info_len);
if (param->info == NULL) {
PHL_ERR("%s: alloc param->info failed!\n", __func__);
psts = RTW_PHL_STATUS_RESOURCE;
goto _err_info;
}
_os_mem_set(drv, param->info, 0, chg_info_len);
_os_mem_cpy(drv, param->info, chg_info, chg_info_len);
} else {
param->info = NULL;
}
psts = phl_cmd_enqueue(phl_info,
sta->rlink->hw_band,
MSG_EVT_STA_CHG_STAINFO,
(u8 *)param,
param_len,
_phl_cmd_change_stainfo_done,
cmd_type,
cmd_timeout);
if (is_cmd_failure(psts)) {
/* Send cmd success, but wait cmd fail*/
psts = RTW_PHL_STATUS_FAILURE;
} else if (psts != RTW_PHL_STATUS_SUCCESS) {
/* Send cmd fail */
psts = RTW_PHL_STATUS_FAILURE;
goto _err_cmd;
}
return psts;
_err_cmd:
if (param->info)
_os_kmem_free(drv, param->info, param->info_len);
_err_info:
if (param)
_os_kmem_free(drv, param, param_len);
_exit:
return psts;
}
#endif
enum rtw_phl_status
rtw_phl_cmd_change_stainfo(void *phl,
struct rtw_phl_stainfo_t *sta, enum sta_chg_id chg_id,
u8 *chg_info, u8 chg_info_len,
enum phl_cmd_type cmd_type, u32 cmd_timeout)
{
struct phl_info_t *phl_info = (struct phl_info_t *)phl;
#ifdef CONFIG_CMD_DISP
return _phl_cmd_change_stainfo(phl_info, sta, chg_id, chg_info, chg_info_len,
cmd_type, cmd_timeout);
#else
PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_, "%s: not support alloc stainfo cmd\n",
__func__);
return _change_stainfo(phl_info, sta, chg_id, chg_info, chg_info_len);
#endif /* CONFIG_CMD_DISP */
}
/**
* This function updates tx/rx traffic status of each active station info
*/
void
phl_sta_trx_tfc_upd(struct phl_info_t *phl_info)
{
struct macid_ctl_t *macid_ctl = phl_to_mac_ctrl(phl_info);
struct rtw_phl_stainfo_t *phl_sta = NULL;
struct rtw_stats *sta_stats = NULL;
u16 max_macid_num = 0;
u16 mid = 0;
max_macid_num = macid_ctl->max_num;
_os_spinlock(phl_to_drvpriv(phl_info), &macid_ctl->lock, _bh, NULL);
for(mid = 0; mid < max_macid_num; mid++) {
if (_phl_macid_is_used(macid_ctl->used_map, mid)) {
phl_sta = macid_ctl->sta[mid];
if (phl_sta) {
#ifdef CONFIG_PHL_RA_TXSTS_DBG
/* issue H2C to get ra txsts report */
if (rtw_hal_is_sta_linked(phl_info->hal, phl_sta))
rtw_phl_txsts_rpt_config(phl_info, phl_sta);
#endif
sta_stats = &phl_sta->stats;
phl_tx_traffic_upd(sta_stats);
phl_rx_traffic_upd(sta_stats);
}
}
}
_os_spinunlock(phl_to_drvpriv(phl_info), &macid_ctl->lock, _bh, NULL);
}
/**
* This function is used to get phl sta info
* by macid
* @phl: see phl_info_t
* @macid: macid
*/
struct rtw_phl_stainfo_t *
rtw_phl_get_stainfo_by_macid(void *phl, u16 macid)
{
struct phl_info_t *phl_info = (struct phl_info_t *)phl;
struct macid_ctl_t *macid_ctl = phl_to_mac_ctrl(phl_info);
struct rtw_phl_stainfo_t *phl_sta = NULL;
if (macid >= macid_ctl->max_num) {
PHL_ERR("%s macid(%d) is invalid\n", __func__, macid);
return NULL;
}
_os_spinlock(phl_to_drvpriv(phl_info), &macid_ctl->lock, _bh, NULL);
if (_phl_macid_is_used(macid_ctl->used_map, macid))
phl_sta = macid_ctl->sta[macid];
if (phl_sta == NULL) {
PHL_TRACE(COMP_PHL_DBG, _PHL_DEBUG_,"%s sta info (macid:%d) is NULL\n", __func__, macid);
#if defined(CONFIG_PHL_RELEASE_RPT_ENABLE) || defined(CONFIG_PCI_HCI)
/* comment temporarily since release report may report unused macid */
/* and trigger call tracing */
/* _os_warn_on(1); */
#else
#ifdef CONFIG_RTW_DEBUG
if (_PHL_DEBUG_ <= phl_log_level)
_os_warn_on(1);
#endif /*CONFIG_RTW_DEBUG*/
#endif /* CONFIG_PHL_RELEASE_RPT_ENABLE */
}
_os_spinunlock(phl_to_drvpriv(phl_info), &macid_ctl->lock, _bh, NULL);
return phl_sta;
}
struct rtw_phl_stainfo_t *
rtw_phl_get_stainfo_by_addr_ex(void *phl, u8 *addr)
{
struct phl_info_t *phl_info = (struct phl_info_t *)phl;
struct macid_ctl_t *mc = phl_to_mac_ctrl(phl_info);
struct rtw_phl_stainfo_t *sta = NULL;
u16 mid = 0;
u16 max_macid_num = mc->max_num;
bool sta_found = false;
_os_spinlock(phl_to_drvpriv(phl_info), &mc->lock, _bh, NULL);
for(mid = 0; mid < max_macid_num; mid++) {
if (_phl_macid_is_used(mc->used_map, mid)) {
sta = mc->sta[mid];
if (_os_mem_cmp(phl_to_drvpriv(phl_info),
sta->mac_addr, addr, MAC_ALEN) == 0) {
sta_found = true;
break;
}
}
}
_os_spinunlock(phl_to_drvpriv(phl_info), &mc->lock, _bh, NULL);
if (sta_found == false)
sta = NULL;
return sta;
}
u16 rtw_phl_get_macid_by_addr(void *phl, u8 *addr)
{
struct phl_info_t *phl_info = (struct phl_info_t *)phl;
struct macid_ctl_t *mc = phl_to_mac_ctrl(phl_info);
struct rtw_phl_stainfo_t *sta = NULL;
sta = rtw_phl_get_stainfo_by_addr_ex(phl, addr);
if (sta)
return sta->macid;
return mc->max_num;
}
/**
* This function is called to create phl_station_info
* return pointer to rtw_phl_stainfo_t
* @phl: see phl_info_t
* @roleidx: index of wifi role(linux) port nubmer(windows)
* @addr: current address of this station
*/
struct rtw_phl_stainfo_t *
rtw_phl_get_stainfo_by_addr(void *phl,
struct rtw_wifi_role_t *wrole,
struct rtw_wifi_role_link_t *rlink,
u8 *addr)
{
struct phl_info_t *phl_info = (struct phl_info_t *)phl;
struct macid_ctl_t *macid_ctl = phl_to_mac_ctrl(phl_info);
struct rtw_phl_stainfo_t *sta = NULL;
if (is_broadcast_mac_addr(addr)) {
u16 macid = macid_ctl->wrole_bmc[wrole->id][rlink->id];
if (macid >= macid_ctl->max_num)
sta = NULL;
else
sta = macid_ctl->sta[macid];
goto _exit;
}
sta = phl_stainfo_queue_search(phl_info,
&(rlink->assoc_sta_queue), addr);
_exit:
return sta;
}
struct rtw_phl_stainfo_t *
rtw_phl_get_stainfo_self(void *phl,
struct rtw_wifi_role_link_t *rlink)
{
struct phl_info_t *phl_info = (struct phl_info_t *)phl;
struct rtw_phl_stainfo_t *sta = NULL;
#if 0
if ((wrole->type == PHL_RTYPE_STATION) &&
(wrole->mstate == MLME_LINKED))
//????
else
sta = phl_stainfo_queue_search(phl_info,
&wrole->assoc_sta_queue, wrole->mac_addr);
}
#else
sta = phl_stainfo_queue_get_first(phl_info, &rlink->assoc_sta_queue);
if (sta == NULL)
PHL_ERR("%s sta == NULL\n", __func__);
#endif
return sta;
}
u8
rtw_phl_get_sta_rssi(struct rtw_phl_stainfo_t *sta)
{
u8 rssi = rtw_hal_get_sta_rssi(sta);
return rssi;
}
u8 phl_get_min_rssi_bcn(struct phl_info_t *phl_info)
{
struct macid_ctl_t *macid_ctl = phl_to_mac_ctrl(phl_info);
struct rtw_phl_stainfo_t *sta = NULL;
u8 rssi_bcn_min = 0xFF;
u16 i = 0;
u8 rssi = 0;
for (i = 0; i < macid_ctl->max_num; i++) {
if (!_phl_macid_is_used(macid_ctl->used_map, i))
continue;
sta = rtw_phl_get_stainfo_by_macid(phl_info, i);
if (NULL == sta)
continue;
rssi = rtw_hal_get_sta_rssi_bcn(sta);
PHL_DBG("%s macid(%d) with rssi_bcn = %d\n",
__func__, i, rssi);
if (rssi == 0)
continue;
rssi_bcn_min = MIN(rssi, rssi_bcn_min);
}
return rssi_bcn_min;
}
enum rtw_phl_status
rtw_phl_query_rainfo(void *phl, struct rtw_phl_stainfo_t *phl_sta,
struct rtw_phl_rainfo *ra_info)
{
struct phl_info_t *phl_info = (struct phl_info_t *)phl;
enum rtw_phl_status phl_sts = RTW_PHL_STATUS_FAILURE;
do {
if (NULL == phl_sta) {
PHL_TRACE(COMP_PHL_XMIT, _PHL_ERR_,
"%s : phl_sta is NULL\n",
__func__);
break;
}
if (NULL == ra_info) {
PHL_TRACE(COMP_PHL_XMIT, _PHL_ERR_,
"%s : Input parameter is NULL\n",
__func__);
break;
}
if (RTW_HAL_STATUS_SUCCESS ==
rtw_hal_query_rainfo(phl_info->hal, phl_sta->hal_sta,
ra_info)) {
phl_sts = RTW_PHL_STATUS_SUCCESS;
break;
} else {
break;
}
} while (false);
return phl_sts;
}
enum rtw_phl_status
rtw_phl_get_rx_stat(void *phl, struct rtw_phl_stainfo_t *phl_sta,
u16 *rx_rate, u8 *bw, u8 *gi_ltf)
{
enum rtw_phl_status phl_sts = RTW_PHL_STATUS_FAILURE;
struct rtw_hal_stainfo_t *hal_sta;
if(phl_sta) {
hal_sta = phl_sta->hal_sta;
*rx_rate = hal_sta->trx_stat.rx_rate;
*gi_ltf = hal_sta->trx_stat.rx_gi_ltf;
*bw = hal_sta->trx_stat.rx_bw;
phl_sts = RTW_PHL_STATUS_SUCCESS;
}
return phl_sts;
}
int rtw_phl_get_sta_inact_ms(void *phl, struct rtw_phl_stainfo_t *phl_sta)
{
if (phl_sta)
return _os_get_cur_time_ms() - phl_sta->stats.last_rx_time_ms;
return -1;
}
/**
* rtw_phl_txsts_rpt_config() - issue h2c for txok and tx retry info
* @phl: struct phl_info_t *
* @phl_sta: indicate the first macid that you want to query.
* Return rtw_phl_txsts_rpt_config's return value in enum rtw_phl_status type.
*/
enum rtw_phl_status
rtw_phl_txsts_rpt_config(void *phl, struct rtw_phl_stainfo_t *phl_sta)
{
struct phl_info_t *phl_info = (struct phl_info_t *)phl;
enum rtw_phl_status phl_sts = RTW_PHL_STATUS_FAILURE;
if (phl_sta) {
if (RTW_HAL_STATUS_SUCCESS == rtw_hal_query_txsts_rpt(phl_info->hal, phl_sta->macid))
phl_sts = RTW_PHL_STATUS_SUCCESS;
}
return phl_sts;
}
/**
* rtw_phl_get_tx_ok_rpt() - get txok info.
* @phl: struct phl_info_t *
* @phl_sta: information is updated through phl_station_info.
* @tx_ok_cnt: buffer address that we used to store tx ok statistics.
* @qsel indicate which AC queue, or fetch all by PHL_AC_QUEUE_TOTAL
*
* Return rtw_phl_get_tx_ok_rpt's return value in enum rtw_phl_status type.
*/
enum rtw_phl_status
rtw_phl_get_tx_ok_rpt(void *phl, struct rtw_phl_stainfo_t *phl_sta, u32 *tx_ok_cnt,
enum phl_ac_queue qsel)
{
#if defined(CONFIG_PHL_RELEASE_RPT_ENABLE) || defined(CONFIG_PCI_HCI)
struct phl_info_t *phl_info = (struct phl_info_t *)phl;
enum rtw_phl_status phl_sts = RTW_PHL_STATUS_SUCCESS;
struct rtw_hal_stainfo_t *hal_sta;
struct rtw_wp_rpt_stats *rpt_stats;
if(phl_sta) {
hal_sta = phl_sta->hal_sta;
rpt_stats = hal_sta->trx_stat.wp_rpt_stats;
if (rpt_stats == NULL) {
phl_sts = RTW_PHL_STATUS_FAILURE;
PHL_ERR("rtp_stats NULL\n");
goto fail;
}
if (tx_ok_cnt && qsel <= PHL_AC_QUEUE_TOTAL) {
if (qsel == PHL_AC_QUEUE_TOTAL) {
/* copy all AC counter */
tx_ok_cnt[PHL_BE_QUEUE_SEL] = rpt_stats[PHL_BE_QUEUE_SEL].tx_ok_cnt;
tx_ok_cnt[PHL_BK_QUEUE_SEL] = rpt_stats[PHL_BK_QUEUE_SEL].tx_ok_cnt;
tx_ok_cnt[PHL_VI_QUEUE_SEL] = rpt_stats[PHL_VI_QUEUE_SEL].tx_ok_cnt;
tx_ok_cnt[PHL_VO_QUEUE_SEL] = rpt_stats[PHL_VO_QUEUE_SEL].tx_ok_cnt;
/* reset all counter */
_os_spinlock(phl_to_drvpriv(phl_info), &hal_sta->trx_stat.tx_sts_lock, _bh, NULL);
rpt_stats[PHL_BE_QUEUE_SEL].tx_ok_cnt = 0;
rpt_stats[PHL_BK_QUEUE_SEL].tx_ok_cnt = 0;
rpt_stats[PHL_VI_QUEUE_SEL].tx_ok_cnt = 0;
rpt_stats[PHL_VO_QUEUE_SEL].tx_ok_cnt = 0;
_os_spinunlock(phl_to_drvpriv(phl_info), &hal_sta->trx_stat.tx_sts_lock, _bh, NULL);
} else {
/*copy target AC queue counter*/
*tx_ok_cnt = rpt_stats[qsel].tx_ok_cnt;
/* reset target AC queue counter */
_os_spinlock(phl_to_drvpriv(phl_info), &hal_sta->trx_stat.tx_sts_lock, _bh, NULL);
rpt_stats[qsel].tx_ok_cnt = 0;
_os_spinunlock(phl_to_drvpriv(phl_info), &hal_sta->trx_stat.tx_sts_lock, _bh, NULL);
}
} else {
phl_sts = RTW_PHL_STATUS_FAILURE;
PHL_ERR("tx_ok_cnt = %p, qsel = %d\n", tx_ok_cnt, qsel);
}
} else {
phl_sts = RTW_PHL_STATUS_FAILURE;
PHL_ERR("PHL STA NULL.\n");
}
fail:
return phl_sts;
#else
PHL_WARN("%s not support\n", __func__);
return RTW_PHL_STATUS_FAILURE;
#endif
}
static u32 rtw_phl_get_hw_tx_fail_cnt(struct rtw_hal_stainfo_t *hal_sta,
enum phl_ac_queue qsel) {
#if defined(CONFIG_PHL_RELEASE_RPT_ENABLE) || defined(CONFIG_PCI_HCI)
struct rtw_wp_rpt_stats *rpt_stats;
u32 total = 0;
if (hal_sta) {
rpt_stats = hal_sta->trx_stat.wp_rpt_stats;
if (rpt_stats == NULL) {
PHL_ERR("rtp_stats NULL\n");
return total;
}
total = rpt_stats[qsel].rty_fail_cnt\
+ rpt_stats[qsel].lifetime_drop_cnt \
+ rpt_stats[qsel].macid_drop_cnt;
}
return total;
#else
PHL_WARN("%s not support\n", __func__);
return 0;
#endif
}
static void rtw_phl_reset_tx_fail_cnt(struct phl_info_t *phl_info,
struct rtw_hal_stainfo_t *hal_sta, enum phl_ac_queue qsel) {
#if defined(CONFIG_PHL_RELEASE_RPT_ENABLE) || defined(CONFIG_PCI_HCI)
struct rtw_wp_rpt_stats *rpt_stats;
if (hal_sta) {
rpt_stats = hal_sta->trx_stat.wp_rpt_stats;
if (rpt_stats == NULL) {
PHL_ERR("rtp_stats NULL\n");
return;
}
_os_spinlock(phl_to_drvpriv(phl_info), &hal_sta->trx_stat.tx_sts_lock, _bh, NULL);
rpt_stats[qsel].rty_fail_cnt = 0;
rpt_stats[qsel].lifetime_drop_cnt = 0;
rpt_stats[qsel].macid_drop_cnt = 0;
_os_spinunlock(phl_to_drvpriv(phl_info), &hal_sta->trx_stat.tx_sts_lock, _bh, NULL);
}
#else
PHL_WARN("%s not support\n", __func__);
#endif
}
/**
* rtw_phl_get_tx_fail_rpt() - get tx fail info.
* @phl: struct phl_info_t *
* @phl_sta: information is updated through phl_station_info.
* @tx_fail_cnt: buffer address that we used to store tx fail statistics.
* @qsel indicate which AC queue, or fetch all by PHL_AC_QUEUE_TOTAL
*
* Return rtw_phl_get_tx_fail_rpt's return value in enum rtw_phl_status type.
*/
enum rtw_phl_status
rtw_phl_get_tx_fail_rpt(void *phl, struct rtw_phl_stainfo_t *phl_sta, u32 *tx_fail_cnt,
enum phl_ac_queue qsel)
{
#if defined(CONFIG_PHL_RELEASE_RPT_ENABLE) || defined(CONFIG_PCI_HCI)
struct phl_info_t *phl_info = (struct phl_info_t *)phl;
enum rtw_phl_status phl_sts = RTW_PHL_STATUS_SUCCESS;
struct rtw_hal_stainfo_t *hal_sta;
if(phl_sta) {
hal_sta = phl_sta->hal_sta;
if (tx_fail_cnt && qsel <= PHL_AC_QUEUE_TOTAL) {
if (qsel == PHL_AC_QUEUE_TOTAL) {
/* copy all AC counter */
tx_fail_cnt[PHL_BE_QUEUE_SEL] = rtw_phl_get_hw_tx_fail_cnt(hal_sta, PHL_BE_QUEUE_SEL);
tx_fail_cnt[PHL_BK_QUEUE_SEL] = rtw_phl_get_hw_tx_fail_cnt(hal_sta, PHL_BK_QUEUE_SEL);
tx_fail_cnt[PHL_VI_QUEUE_SEL] = rtw_phl_get_hw_tx_fail_cnt(hal_sta, PHL_VI_QUEUE_SEL);
tx_fail_cnt[PHL_VO_QUEUE_SEL] = rtw_phl_get_hw_tx_fail_cnt(hal_sta, PHL_VO_QUEUE_SEL);
/* reset all counter */
rtw_phl_reset_tx_fail_cnt(phl_info, hal_sta, PHL_BE_QUEUE_SEL);
rtw_phl_reset_tx_fail_cnt(phl_info, hal_sta, PHL_BK_QUEUE_SEL);
rtw_phl_reset_tx_fail_cnt(phl_info, hal_sta, PHL_VI_QUEUE_SEL);
rtw_phl_reset_tx_fail_cnt(phl_info, hal_sta, PHL_VO_QUEUE_SEL);
} else {
/*copy target AC queue counter*/
tx_fail_cnt[qsel] = rtw_phl_get_hw_tx_fail_cnt(hal_sta, qsel);
/* reset target AC queue counter */
rtw_phl_reset_tx_fail_cnt(phl_info, hal_sta, qsel);
}
} else {
phl_sts = RTW_PHL_STATUS_FAILURE;
PHL_ERR("tx_fail_cnt = %p, qsel = %d\n", tx_fail_cnt, qsel);
}
} else {
phl_sts = RTW_PHL_STATUS_FAILURE;
PHL_ERR("PHL STA NULL.\n");
}
return phl_sts;
#else
PHL_WARN("%s not support\n", __func__);
return RTW_PHL_STATUS_FAILURE;
#endif
}
/**
* rtw_phl_get_tx_ra_retry_rpt() - get tx retry info.
* @phl: struct phl_info_t *
* @phl_sta: information is updated through phl_station_info.
* @tx_retry_cnt: buffer address that we used to store tx fail statistics.
* @qsel: indicate which AC queue, or fetch all by PHL_AC_QUEUE_TOTAL
* @reset: decide to reset counter or not
*
* Return rtw_phl_get_tx_retry_rpt's return value in enum rtw_phl_status type.
*/
enum rtw_phl_status
rtw_phl_get_tx_ra_retry_rpt(void *phl, struct rtw_phl_stainfo_t *phl_sta,
u32 *tx_retry_cnt, enum phl_ac_queue qsel,
u8 reset)
{
#if defined(CONFIG_PHL_RELEASE_RPT_ENABLE) || defined(CONFIG_PCI_HCI)
struct phl_info_t *phl_info = (struct phl_info_t *)phl;
void *drv = phl_to_drvpriv(phl_info);
enum rtw_phl_status phl_sts = RTW_PHL_STATUS_SUCCESS;
struct rtw_hal_stainfo_t *hal_sta;
if (phl_sta) {
hal_sta = phl_sta->hal_sta;
if (tx_retry_cnt && qsel <= PHL_AC_QUEUE_TOTAL) {
if (qsel == PHL_AC_QUEUE_TOTAL) {
/* copy all AC counter */
_os_mem_cpy(drv, tx_retry_cnt, hal_sta->ra_info.tx_retry_cnt,
sizeof(u32)*PHL_AC_QUEUE_TOTAL);
/* reset all counter */
/* TODO: Here needs lock, and so does halbb_get_txsts_rpt */
if (reset)
_os_mem_set(drv, hal_sta->ra_info.tx_retry_cnt, 0,
sizeof(u32)*PHL_AC_QUEUE_TOTAL);
} else {
/*copy target AC queue counter*/
*tx_retry_cnt = hal_sta->ra_info.tx_retry_cnt[qsel];
/* reset target AC queue counter */
/* TODO: Here needs lock, and so does halbb_get_txsts_rpt */
if (reset)
hal_sta->ra_info.tx_retry_cnt[qsel] = 0;
}
} else {
phl_sts = RTW_PHL_STATUS_FAILURE;
PHL_ERR("tx_retry_cnt = %p, qsel = %d\n", tx_retry_cnt, qsel);
}
} else {
phl_sts = RTW_PHL_STATUS_FAILURE;
PHL_ERR("PHL STA NULL.\n");
}
return phl_sts;
#else
PHL_WARN("%s not support\n", __func__);
return RTW_PHL_STATUS_FAILURE;
#endif
}
#ifdef BCN_TRACKING_TEST
#define TEST_BCN_NUM 130
static void
_bcn_tracking_test(struct rtw_bcn_pkt_info *info, u16 *bcn_intvl)
{
static u8 idx = 0;
u8 t_case = T_B_CASE;
u64 bcn1[TEST_BCN_NUM] = {
0x00000019B3E6B016, 0x00000019B3E84016, 0x00000019B3FB001E,
0x00000019B3FC9016, 0x00000019B4014016, 0x00000019B402D044,
0x00000019B4046556, 0x00000019B405F016, 0x00000019B407801E,
0x00000019B4091054, 0x00000019B40AAE80, 0x00000019B40C302D,
0x00000019B40DC016, 0x00000019B40F508B, 0x00000019B410E01E,
0x00000019B4127016, 0x00000019B414001E, 0x00000019B4159389,
0x00000019B417201E, 0x00000019B418B059, 0x00000019B41A401E,
0x00000019B41BD047, 0x00000019B41D6016, 0x00000019B41EF01E,
0x00000019B420801E, 0x00000019B4302016, 0x00000019B434D01E,
0x00000019B43B1032, 0x00000019B44AB01E, 0x00000019B44C4075,
0x00000019B458CB0E, 0x00000019B45BE4B1, 0x00000019B45F0093,
0x00000019B46EA016, 0x00000019B47E4016, 0x00000019B486105C,
0x00000019B492AE10, 0x00000019B495B810, 0x00000019B49BF6EF,
0x00000019B4AB901E, 0x00000019B4B4F050, 0x00000019B4C49144,
0x00000019B4C94084, 0x00000019B4CDF057, 0x00000019B4DC0043,
0x00000019B4EBA01B, 0x00000019B4F37250, 0x00000019B4F69016,
0x00000019B50310A1, 0x00000019B5063CEE, 0x00000019B5095069,
0x00000019B50C7AB0, 0x00000019B50F915A, 0x00000019B512B544,
0x00000019B53B5016, 0x00000019B54B03EA, 0x00000019B54C8453,
0x00000019B552C089, 0x00000019B554501C, 0x00000019B555E01E,
0x00000019B557701E, 0x00000019B559003D, 0x00000019B55A901E,
0x00000019B55C2591, 0x00000019B55DB01E, 0x00000019B55F4016,
0x00000019B560D016, 0x00000019B5626043, 0x00000019B563F016,
0x00000019B5658248, 0x00000019B5671016, 0x00000019B568A016,
0x00000019B56A3016, 0x00000019B56BC016, 0x00000019B56D5016,
0x00000019B56EE016, 0x00000019B5707016, 0x00000019B5720016,
0x00000019B581A01E, 0x00000019B586506F, 0x00000019B58C9229,
0x00000019B58FB114, 0x00000019B592DEA3,0x00000019B595F71E,
0x00000019B59917A0, 0x00000019B59F52AC, 0x00000019B5AEF063,
0x00000019B5BE9016, 0x00000019B5CCA069, 0x00000019B5D4724F,
0x00000019B5D7906E, 0x00000019B5DC438D, 0x00000019B5DF68EA,
0x00000019B5EBE01E, 0x00000019B5F247A3, 0x00000019B601C016,
0x00000019B60CB081, 0x00000019B61C5047, 0x00000019B61F7090,
0x00000019B6229049, 0x00000019B6290054, 0x00000019B6387016,
0x00000019B6481016, 0x00000019B649AD89, 0x00000019B64CC72F,
0x00000019B651704D, 0x00000019B6549F54, 0x00000019B659412C,
0x00000019B65AD04C, 0x00000019B65C6101, 0x00000019B661123E,
0x00000019B670B016, 0x00000019B67D304F, 0x00000019B68CD043,
0x00000019B68FFBA9, 0x00000019B693104D, 0x00000019B6963127,
0x00000019B69C792C, 0x00000019B6A8F094, 0x00000019B6AC137F,
0x00000019B6BBB072, 0x00000019B6BED070, 0x00000019B6CE7016,
0x00000019B6D001AC, 0x00000019B6D64961, 0x00000019B6D9614B,
0x00000019B6E2C068, 0x00000019B725F2DF, 0x00000019B7278016,
0x00000019B729111D};
u64 bcn2[TEST_BCN_NUM] = {
0x0000001CD8A0CDBE, 0x0000001CD8A26FBF, 0x0000001CD8A4058F,
0x0000001CD8A59636, 0x0000001CD8A8C598, 0x0000001CD8B0BE61,
0x0000001CD8C0AD98, 0x0000001CD8CF0784, 0x0000001CD8DEF599,
0x0000001CD8E23975, 0x0000001CD8F21598, 0x0000001CD9020598,
0x0000001CD9053598, 0x0000001CD9152598, 0x0000001CD921F17E,
0x0000001CD931D598, 0x0000001CD941C598, 0x0000001CD951B598,
0x0000001D30ABAC29, 0x0000001D30B9F59A, 0x0000001D30C9E59A,
0x0000001D30CB7DC1, 0x0000001D30D0459A, 0x0000001D30D51D2A,
0x0000001D30E4FD9A, 0x0000001D30F4ED9A, 0x0000001D3104DD9A,
0x0000001D310681ED, 0x0000001D310811E2, 0x0000001D3117FD9A,
0x0000001D3127ED9A, 0x0000001D3137DD9A, 0x0000001D3147CD9A,
0x0000001D3157BD9A, 0x0000001D3167ADB1, 0x0000001D31779D9A,
0x0000001D317C69E7, 0x0000001D318C559A, 0x0000001D319C459A,
0x0000001D31AC359A, 0x0000001D31BC259A, 0x0000001D31CC159A,
0x0000001D31D5AA2D, 0x000000208038BF29, 0x0000002080426273,
0x000000208048AD9C, 0x00000020804A459C, 0x00000020804BE434,
0x00000020805573A1, 0x0000002080655D9C, 0x0000002080754D9C,
0x0000002080853D9C, 0x00000020808A0E18, 0x000000208099F59C,
0x00000020809D385A, 0x0000002080A84DD5, 0x0000002080AEAD9C,
0x0000002080BE9D9C, 0x0000002080CE8DD2, 0x0000002080DE7D9C,
0x0000002080E9B232, 0x0000002080ECDF25, 0x000000214DDD7DA4,
0x000000214DE3DD9E, 0x000000214DE5759E, 0x000000214DE70D9E,
0x000000214DE8A59E, 0x000000214DEA3E17, 0x000000214DEBD59E,
0x000000214DED6F65, 0x000000214DEF059E, 0x000000214DF09DF1,
0x000000214DF2359E, 0x000000214DF3CD9E, 0x000000214DF5659E,
0x000000214DF6FE42, 0x000000214DF8959E, 0x000000214DFA2D9E,
0x000000214DFBC59E, 0x000000214DFD5D9E, 0x000000214DFEF59E,
0x000000214E008D9D, 0x000000214E03BD9E, 0x000000214E055611,
0x000000214E06ED9E, 0x000000214E08859E, 0x000000214E0A1F2A,
0x000000214E0BB59E, 0x000000214E0D5342, 0x000000214E0EE698,
0x000000214E107D9E, 0x000000214E12159E, 0x000000214E13AD9E,
0x000000214E15459E, 0x000000214E16DD9E, 0x000000214E18759E,
0x000000214E1A0E13, 0x000000214E1BA59E, 0x000000214E1D3D9E,
0x000000214E1ED59E, 0x000000214E206D9E, 0x000000214E22059E,
0x000000214E239D9E, 0x000000214E338D9E, 0x000000214E41F29C,
0x000000214E51D59E, 0x000000214E61C59E, 0x000000214E71B59E,
0x000000214E81A59E, 0x000000214E8CDECA, 0x00000027F885D1CA,
0x00000027F885D697, 0x00000027F885D979, 0x00000027F885E8B1,
0x00000027F8862F3B, 0x00000027F89415A0, 0x00000027F895C705,
0x00000027F898E3EA, 0x00000027F89A80FB, 0x00000027F89C0EEA,
0x00000027F8A0D8EF, 0x00000027F8A271A7, 0x00000027F8A410A1,
0x00000027F8A5ACAC, 0x00000027F8AC00CB, 0x00000027F8AD987B,
0x00000027F8B0C936, 0x00000027F8B26A21, 0x00000027F8B40278,
0x00000027F8B59B83};
if (t_case == 1) {
info->tsf = bcn1[idx];
} else {
info->tsf = bcn2[idx];
}
*bcn_intvl = T_B_INTVL;
PHL_ERR("%s: tsf(0x%08x %08x), Mod(%d), bcn_intvl(%d)\n",
__func__,
(u32)(info->tsf >> 32),
(u32)info->tsf,
(u32)_os_modular64(info->tsf, *bcn_intvl * TU),
*bcn_intvl);
idx++;
if (idx >= TEST_BCN_NUM)
idx = 0;
}
#endif /* BCN_TRACKING_TEST */
/**
* rtw_phl_get_tx_ra_ok_rpt() - get tx ok info from bb ra txsts rpt
* @phl: struct phl_info_t *
* @phl_sta: information is updated through phl_station_info.
* @tx_retry_cnt: buffer address that we used to store tx fail statistics.
* @qsel: indicate which AC queue, or fetch all by PHL_AC_QUEUE_TOTAL
* @reset: decide to reset counter or not
*
* Return rtw_phl_get_tx_ra_ok_rpt's return value in enum rtw_phl_status type.
*/
enum rtw_phl_status
rtw_phl_get_tx_ra_ok_rpt(void *phl, struct rtw_phl_stainfo_t *phl_sta,
u32 *tx_ok_cnt, enum phl_ac_queue qsel,
u8 reset)
{
#if defined(CONFIG_PHL_RELEASE_RPT_ENABLE) || defined(CONFIG_PCI_HCI)
struct phl_info_t *phl_info = (struct phl_info_t *)phl;
void *drv = phl_to_drvpriv(phl_info);
enum rtw_phl_status phl_sts = RTW_PHL_STATUS_SUCCESS;
struct rtw_hal_stainfo_t *hal_sta;
if (phl_sta) {
hal_sta = phl_sta->hal_sta;
if (tx_ok_cnt && qsel <= PHL_AC_QUEUE_TOTAL) {
if (qsel == PHL_AC_QUEUE_TOTAL) {
/* copy all AC counter */
_os_mem_cpy(drv, tx_ok_cnt, hal_sta->ra_info.tx_ok_cnt,
sizeof(u32)*PHL_AC_QUEUE_TOTAL);
/* reset all counter */
/* TODO: Here needs lock, and so does halbb_get_txsts_rpt */
if (reset)
_os_mem_set(drv, hal_sta->ra_info.tx_ok_cnt, 0,
sizeof(u32)*PHL_AC_QUEUE_TOTAL);
} else {
/*copy target AC queue counter*/
*tx_ok_cnt = hal_sta->ra_info.tx_ok_cnt[qsel];
/* reset target AC queue counter */
/* TODO: Here needs lock, and so does halbb_get_txsts_rpt */
if (reset)
hal_sta->ra_info.tx_ok_cnt[qsel] = 0;
}
} else {
phl_sts = RTW_PHL_STATUS_FAILURE;
PHL_ERR("tx_ok_cnt = %p, qsel = %d\n", tx_ok_cnt, qsel);
}
} else {
phl_sts = RTW_PHL_STATUS_FAILURE;
PHL_ERR("PHL STA NULL.\n");
}
return phl_sts;
#else
PHL_WARN("%s not support\n", __func__);
return RTW_PHL_STATUS_FAILURE;
#endif
}
static void
_associated_tsf_error_handle_done(void *drv_priv, u8 *cmd, u32 cmd_len, enum rtw_phl_status status)
{
if (cmd) {
PHL_INFO("%s.....\n", __func__);
}
}
void
_associated_tsf_error_handle(struct phl_info_t *phl,
struct rtw_phl_stainfo_t *sta)
{
enum rtw_phl_status psts = RTW_PHL_STATUS_FAILURE;
psts = phl_cmd_enqueue(phl,
sta->rlink->hw_band,
MSG_EVT_ASSOCIATED_TSF_ERROR,
(u8 *)sta,
sizeof(struct rtw_phl_stainfo_t),
_associated_tsf_error_handle_done,
PHL_CMD_NO_WAIT,
0);
if (psts == RTW_PHL_STATUS_SUCCESS) {
PHL_INFO("%s: Issue cmd ok\n", __func__);
} else {
PHL_INFO("%s: Fail to issue MSG_EVT_ASSOCIATED_TSF_ERROR!! psts(%d)\n",
__func__, psts);
}
}
bool
_error_tsf_detect(struct phl_info_t *phl, struct rtw_rx_bcn_info *bcn_i,
struct rtw_bcn_pkt_info *info)
{
struct rtw_bcn_long_i *bcn_l = &bcn_i->bcn_l_i;
u16 bcn_intvl = bcn_i->bcn_intvl;
bool ret = false;
/* first bcn after association */
if (bcn_l->num == 0)
return ret;
/* Associated AP maybe reset TSF or jump tsf.
* We need to reset previous info and sent msg to other module to handle this event.
* Ex: Need to restart MCC, P2P NOA
*/
/* current tsf < last tsf */
if (info->tsf < bcn_l->info[LONG_BCN_TSF][bcn_l->idx]) {
PHL_ERR("%s: Error, current tsf(0x%08x %08x) < last tsf(0x%08x %08x)\n",
__func__,
(u32)(info->tsf >> 32), (u32)info->tsf,
(u32)(bcn_l->info[LONG_BCN_TSF][bcn_l->idx] >> 32),
(u32)(bcn_l->info[LONG_BCN_TSF][bcn_l->idx]));
ret = true;
} else {
u32 d_tsf = 0, d_sym = 0, d_t = 0, tol = bcn_intvl * 2;
d_sym = phl_get_passing_time_ms(
(u32)bcn_l->info[LONG_BCN_SYS_T][bcn_l->idx]);
d_tsf = (u32)_os_division64(
(info->tsf - bcn_l->info[LONG_BCN_TSF][bcn_l->idx]),
1000);
if (d_tsf > d_sym) {
d_t = d_tsf - d_sym;
} else {
d_t = d_sym - d_tsf;
}
if (d_t > tol) {
PHL_ERR("%s: Error, d_sym_t(%d) d_tsf(%d) don't match\n",
__func__, d_sym, d_tsf);
ret = true;
}
}
if (ret) {
phl_clean_sta_bcn_info(phl, info->sta);
}
return ret;
}
static void _data_sorting(u16 *array, u16 num)
{
u16 idx = 0, jdx = 0;
u16 temp = 0;
for (idx = 1; idx < num; idx++) {
for (jdx = 0; jdx < (num - idx); jdx++) {
if (array[jdx] > array[jdx + 1]) {
temp = array[jdx];
array[jdx] = array[jdx + 1];
array[jdx + 1] = temp;
}
}
}
}
/*
* Get next idx
*/
u8 _get_fidx(u8 num, u8 cur_idx, u8 boundary)
{
u8 idx = 0;
if (num == 0)
idx = cur_idx;
else {
idx = cur_idx + 1;
if (idx >= boundary)
idx = 0;
}
return idx;
}
/*
* Get previous idx
*/
u8 _get_bidx(u8 num, u8 cur_idx)
{
u8 idx = 0;
if (cur_idx == 0) {
idx = num - 1;
} else {
idx = cur_idx - 1;
}
return idx;
}
static u64
_calc_tbtt(u64 tsf, u16 append_t, u16 bcn_intvl)
{
return _os_modular64(tsf - append_t, bcn_intvl * TU);
}
static u32
_min_tbtt_ofst(u32 tbtt1, u32 tbtt2, u32 bcn_intvl)
{
u32 diff_th = (bcn_intvl * TU * 8) / 10;
u32 diff = 0, min = 0;
if (tbtt1 > tbtt2)
diff = tbtt1 - tbtt2;
else
diff = tbtt2 - tbtt1;
if (diff > diff_th) {
if (tbtt1 > tbtt2) {
/* ex: tbtt1 = 92TU, tbtt2 = 2 TU */
if (tbtt2 > (bcn_intvl * TU - tbtt1))
min = tbtt1;
else
min = tbtt2;
} else {
/* ex: tbtt1 = 10TU, tbtt2 = 92 TU */
if (tbtt1 > (bcn_intvl * TU - tbtt2))
min = tbtt2;
else
min = tbtt1;
}
} else {
if (tbtt1 < tbtt2)
min = tbtt1;
else
min = tbtt2;
}
PHL_TRACE(COMP_PHL_DBG, _PHL_DEBUG_, "%s: tbtt1(%d), tbtt2(%d), bcn_intvl(%d), min(%d)\n",
__func__, tbtt1, tbtt2, bcn_intvl, min);
return min;
}
static u32
_min_tbtt(u32 tbtt1, u32 tbtt2, u32 bcn_intvl)
{
u32 diff_th = (bcn_intvl * TU * 85) / 100;
u32 diff = 0, min = 0;
u32 similar_0_th = (bcn_intvl - 3) * TU;
if (tbtt1 > tbtt2) {
diff = tbtt1 - tbtt2;
} else {
diff = tbtt2 - tbtt1;
}
if (diff > diff_th) {
if (tbtt1 > tbtt2)
/* ex: bcn_intvl = 100 TU, tbtt1 = 95TU, tbtt2 = 2 TU
we consider 95 TU to be -5 TU
*/
min = tbtt1;
else
/* ex: tbtt1 = 10TU, tbtt2 = 96 TU */
min = tbtt2;
} else {
/* ex: bcn_intvl = 100 TU, tbtt1 = 98TU, tbtt2 = 30 TU */
if (tbtt1 > tbtt2 && tbtt1 > similar_0_th)
min = tbtt1;
else if (tbtt2 > tbtt1 && tbtt2 > similar_0_th)
min = tbtt2;
else if (tbtt1 < tbtt2)
min = tbtt1;
else
min = tbtt2;
}
PHL_TRACE(COMP_PHL_DBG, _PHL_DEBUG_, "%s: tbtt1(%d), tbtt2(%d), bcn_intvl(%d), min(%d)\n",
__func__, tbtt1, tbtt2, bcn_intvl, min);
return min;
}
void _phl_sta_up_bcn_offset_info(struct phl_info_t *phl,
struct rtw_rx_bcn_info *bcn_i)
{
struct rtw_bcn_long_i *bcn_l = &bcn_i->bcn_l_i;
struct rtw_bcn_offset *ofst_i = &bcn_i->offset_i;
u32 b_intvl = bcn_i->bcn_intvl;
u32 offset = b_intvl * TU;
u16 similar_th = 2;/*Unit: TU*/
u64 diff = 0;
u8 idx = 0, jdx = 0, c_idx = 0, bidx = 0, start_idx = 0;
if (bcn_l->num == 1) {
ofst_i->offset = (u32)bcn_l->info[LONG_BCN_TBTT][bcn_l->idx];
ofst_i->conf_lvl = CONF_LVL_LOW;
PHL_TRACE(COMP_PHL_DBG, _PHL_WARNING_, "%s: bcn_i->num ==1, conf_lvl = CONF_LVL_LOW, offset(%d)\n",
__func__, ofst_i->offset);
goto exit;
}
c_idx = bcn_l->idx;
start_idx = c_idx;
for (idx = 0; idx < bcn_l->num; idx++) {
bidx = c_idx;
for (jdx = 1; jdx < bcn_l->num; jdx++) {
bidx = _get_bidx(bcn_l->num, bidx);
if (start_idx == bidx)
break;
diff = bcn_l->info[LONG_BCN_TSF][c_idx] -
bcn_l->info[LONG_BCN_TSF][bidx];
diff = _os_division64(
_os_modular64(diff, b_intvl * TU), TU);
/*ex: diff = 99, BcnIntvl = 100, It's similar case
* diff = 2, BcnIntvl = 100, It's similar case
*/
if (!((diff < similar_th) ||
((b_intvl - diff) < similar_th))) {
continue;
}
/* init value */
if (offset == b_intvl * TU) {
offset = (u32)bcn_l->info[LONG_BCN_TBTT][c_idx];
} else {
offset = _min_tbtt(offset,
(u32)bcn_l->info[LONG_BCN_TBTT][c_idx],
b_intvl);
}
offset = _min_tbtt(offset,
(u32)bcn_l->info[LONG_BCN_TBTT][bidx],
b_intvl);
}
c_idx = _get_bidx(bcn_l->num, c_idx);
}
if (offset != (b_intvl * TU)) {
ofst_i->conf_lvl = CONF_LVL_HIGH;
if (ofst_i->offset !=
_min_tbtt(offset, ofst_i->offset, b_intvl)) {
ofst_i->offset = offset;
}
goto exit;
}
for (idx = 0; idx < bcn_l->num; idx++) {
if (ofst_i->offset !=
_min_tbtt((u32)bcn_l->info[LONG_BCN_TBTT][idx],
ofst_i->offset, b_intvl)) {
ofst_i->conf_lvl = CONF_LVL_MID;
ofst_i->offset = (u32)bcn_l->info[LONG_BCN_TBTT][idx];
} else if (ofst_i->conf_lvl < CONF_LVL_MID) {
ofst_i->conf_lvl = CONF_LVL_MID;
}
}
exit:
/*
if offset is small, maybe impact by environment, offset < 5% bcn_intvl, we consider offset is 0
*/
if ((ofst_i->offset != 0) &&
(ofst_i->offset < ((b_intvl * TU * 5) / 100))) {
PHL_TRACE(COMP_PHL_DBG, _PHL_WARNING_, "%s: offset(%d) < (%d), set offset = 0\n",
__func__, ofst_i->offset, (b_intvl * TU * 5) / 100);
ofst_i->offset = 0;
}
PHL_TRACE(COMP_PHL_DBG, _PHL_DEBUG_, "%s: bcn num(%d), offset(%d us), conf_lvl(%d), current setting(%d us)\n",
__func__, bcn_l->num, ofst_i->offset, ofst_i->conf_lvl,
ofst_i->cr_tbtt_shift);
return;
}
static void _store_bcn_short_info(struct rtw_rx_bcn_info *bcn_i,
struct rtw_bcn_pkt_info *info)
{
struct rtw_bcn_short_i *bcn_s = &bcn_i->bcn_s_i;
u16 mod = (u16)_os_division64(_os_modular64(info->tsf,
bcn_i->bcn_intvl * TU), TU);
bcn_s->idx = _get_fidx(bcn_s->num, bcn_s->idx, MAX_SHORT_INFO_BCN_NUM);
if (bcn_s->num < MAX_SHORT_INFO_BCN_NUM)
bcn_s->num++;
bcn_s->info[SHORT_BCN_MOD][bcn_s->idx] = mod;
bcn_s->info[SHORT_BCN_RSSI][bcn_s->idx] = info->rssi;
if (bcn_s->num > 1) {
if (mod < bcn_s->min)
bcn_s->min = mod;
if (mod > bcn_s->max)
bcn_s->max = mod;
} else {
bcn_s->min = mod;
bcn_s->max = mod;
}
}
static void _store_bcn_long_info(struct rtw_rx_bcn_info *bcn_i,
struct rtw_bcn_pkt_info *info)
{
struct rtw_bcn_long_i *bcn_l = &bcn_i->bcn_l_i;
bcn_l->idx = _get_fidx(bcn_l->num, bcn_l->idx, MAX_LONG_INFO_BCN_NUM);
if (bcn_l->num < MAX_LONG_INFO_BCN_NUM)
bcn_l->num++;
bcn_l->info[LONG_BCN_TSF][bcn_l->idx] = info->tsf;
bcn_l->info[LONG_BCN_TBTT][bcn_l->idx] =
_calc_tbtt(info->tsf, bcn_i->app_tsf_t, bcn_i->bcn_intvl);
bcn_l->info[LONG_BCN_RX_T][bcn_l->idx] = info->hw_tsf;
bcn_l->info[LONG_BCN_SYS_T][bcn_l->idx] = _os_get_cur_time_ms();
}
#define APP_TSF_T_24G 384
#define APP_TSF_T_5G 52
#define APP_TSF_T_6G 52
void rtw_phl_sta_up_rx_bcn(void *phl, struct rtw_bcn_pkt_info *info)
{
struct rtw_rx_bcn_info *bcn_i = &info->sta->bcn_i;
u16 bcn_intvl = info->sta->asoc_cap.bcn_interval;
#ifndef BCN_TRACKING_TEST
bool error_handle = false;
#endif /* BCN_TRACKING_TEST */
if (info->sta->wrole->mstate != MLME_LINKED) {
PHL_TRACE(COMP_PHL_DBG, _PHL_WARNING_, "mstate(%d) != MLME_LINKED\n",
info->sta->wrole->mstate);
return;
}
if (bcn_intvl == 0) {
PHL_TRACE(COMP_PHL_DBG, _PHL_WARNING_, "bcn_intvl == 0\n");
return;
}
#ifdef BCN_TRACKING_TEST
_bcn_tracking_test(info, &bcn_intvl);
#endif /* BCN_TRACKING_TEST */
#ifndef BCN_TRACKING_TEST
error_handle = _error_tsf_detect(phl, bcn_i, info);
#endif /* BCN_TRACKING_TEST */
bcn_i->pkt_len = info->pkt_len;
bcn_i->rate = info->rate;
if (bcn_i->bcn_intvl != bcn_intvl)
bcn_i->bcn_intvl = bcn_intvl;
if (info->sta->chandef.band == BAND_ON_24G)
bcn_i->app_tsf_t = APP_TSF_T_24G;
else if (info->sta->chandef.band == BAND_ON_5G)
bcn_i->app_tsf_t = APP_TSF_T_5G;
else if (info->sta->chandef.band == BAND_ON_6G)
bcn_i->app_tsf_t = APP_TSF_T_6G;
else
bcn_i->app_tsf_t = APP_TSF_T_24G;
PHL_TRACE(COMP_PHL_DBG, _PHL_DEBUG_, "pkt_len(%d), data_rate(%d), rssi(%d), append_tsf_t(%d), bcn_intvl(%d)\n",
bcn_i->pkt_len, bcn_i->rate, info->rssi, bcn_i->app_tsf_t,
bcn_i->bcn_intvl);
_store_bcn_long_info(bcn_i, info);
_store_bcn_short_info(bcn_i, info);
_phl_sta_up_bcn_offset_info(phl, bcn_i);
bcn_i->num_per_watchdog++;
#ifndef BCN_TRACKING_TEST
if (error_handle)
_associated_tsf_error_handle(phl, info->sta);
#endif /* BCN_TRACKING_TEST */
}
void phl_clean_sta_bcn_info(struct phl_info_t *phl, struct rtw_phl_stainfo_t *sta)
{
void *priv = phl_to_drvpriv(phl);
struct rtw_rx_bcn_info *bcn_i = &sta->bcn_i;
PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_, "phl_clean_sta_bcn_info(): sta->wrole->id(%d)\n",
sta->wrole->id);
_os_mem_set(priv, bcn_i, 0, sizeof(struct rtw_rx_bcn_info));
bcn_i->rate = RTW_DATA_RATE_MAX;
}
void
phl_get_sta_bcn_offset_info(struct phl_info_t *phl,
struct rtw_phl_stainfo_t *sta, struct rtw_bcn_offset *ofst_i)
{
_os_mem_cpy(phl_to_drvpriv(phl), ofst_i, &sta->bcn_i.offset_i,
sizeof(struct rtw_bcn_offset));
}
void phl_sta_up_tbtt_shift_setting(struct rtw_phl_stainfo_t *sta, u32 val)
{
PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_, "%s(): cr_tbtt_shift from %d to %d us\n",
__func__, sta->bcn_i.offset_i.cr_tbtt_shift, val);
sta->bcn_i.offset_i.cr_tbtt_shift = val;
}
void phl_get_sta_bcn_info(struct phl_info_t *phl,
struct rtw_phl_stainfo_t *sta, struct rtw_rx_bcn_info *bcn_i)
{
_os_mem_cpy(phl_to_drvpriv(phl), bcn_i, &sta->bcn_i,
sizeof(struct rtw_rx_bcn_info));
}
static u8 _get_bcn_avg_rssi(struct rtw_bcn_short_i *bcn_s_i, u8 num)
{
u8 idx = 0, jdx = bcn_s_i->idx;
u16 rssi = 0;
if (num > bcn_s_i->num) {
PHL_TRACE(COMP_PHL_DBG, _PHL_ERR_, "%s(): num(%d) > bcn_s_i->num(%d), set num = bcn_s_i->num\n",
__func__, num, bcn_s_i->num);
num = bcn_s_i->num;
}
for (idx = 0; idx < num; idx++) {
rssi += bcn_s_i->info[SHORT_BCN_RSSI][jdx];
jdx = _get_bidx(bcn_s_i->num, jdx);
}
return (u8)(rssi / num);
}
static u8 _get_bcn_dist_pct(struct rtw_bcn_short_i *bcn_s_i, u16 bandary)
{
u8 idx = 0;
u16 cnt = 0;
for (idx = 0; idx < bcn_s_i->num; idx++) {
if (bcn_s_i->info[SHORT_BCN_MOD][idx] <= bandary)
cnt++;
}
return (u8)((cnt * 100) / bcn_s_i->num);
}
static u16 _get_bcn_histogram_bandary(struct rtw_bcn_dist *bcn_dist_i, u8 pct)
{
struct rtw_bcn_histogram *hist_i = &bcn_dist_i->hist_i;
u16 tgt_num = 0, num = 0;
u8 idx = 0;
u16 ret = 0;
tgt_num = (hist_i->num * pct) / 100;
for (idx = 0; idx < MAX_NUM_BIN; idx++) {
num += hist_i->h_array[H_CNT][idx];
if (num > tgt_num) {
ret = hist_i->h_array[H_UPPER][idx];
break;
}
}
return ret;
}
#define PLCP_24G 192 /* us */
#define PLCP_5G 20 /* us */
#define DEF_RX_BCN_T 4 /* ms */
static u16
_get_legacy_rate_val(enum rtw_data_rate rate)
{
if (rate == RTW_DATA_RATE_CCK1)
return 10;
else if (rate == RTW_DATA_RATE_CCK2)
return 20;
else if (rate ==RTW_DATA_RATE_CCK5_5)
return 55;
else if (rate ==RTW_DATA_RATE_CCK11)
return 110;
else if (rate ==RTW_DATA_RATE_OFDM6)
return 60;
else if (rate ==RTW_DATA_RATE_OFDM9)
return 90;
else if (rate ==RTW_DATA_RATE_OFDM12)
return 120;
else if (rate ==RTW_DATA_RATE_OFDM18)
return 180;
else if (rate ==RTW_DATA_RATE_OFDM24)
return 240;
else if (rate ==RTW_DATA_RATE_OFDM36)
return 360;
else if (rate ==RTW_DATA_RATE_OFDM48)
return 480;
else if (rate ==RTW_DATA_RATE_OFDM54)
return 540;
else
return 0;
}
static u16
_get_rx_bcn_t(struct rtw_phl_stainfo_t *sta,
struct rtw_rx_bcn_info *bcn_i)
{
u16 plcp = PLCP_24G;
u16 ret = DEF_RX_BCN_T;
u16 rate = 0;
if ((bcn_i->rate == RTW_DATA_RATE_MAX) ||
(bcn_i->rate > RTW_DATA_RATE_OFDM54))
goto _exit;
if (sta->chandef.band != BAND_ON_5G && sta->chandef.band != BAND_ON_24G)
goto _exit;
if (sta->chandef.band == BAND_ON_5G) {
plcp = PLCP_5G;
}
rate = _get_legacy_rate_val(bcn_i->rate);
if (rate == 0)
goto _exit;
ret = plcp + ((bcn_i->pkt_len * 8 * 10) / rate);
ret = (ret / 1000) + 1; /* us 2 ms and round up */
_exit:
return ret;
}
static u16
_calc_bcn_drift(u16 mod, u16 tbtt, u16 bcn_intvl)
{
if (mod < tbtt) {
/* bcn mod = 20TU, TBTT = 80 */
return bcn_intvl - tbtt + mod;
} else {
/* bcn mod = 50TU, TBTT = 30 */
return mod -= tbtt;
}
}
static void
_get_bcn_drift(struct rtw_rx_bcn_info *bcn_i)
{
u16 tbtt = (u16)(bcn_i->offset_i.offset / TU);
u16 *d = bcn_i->bcn_s_i.bcn_drift;
u16 idx = 0;
for (idx = 0; idx < bcn_i->bcn_s_i.num; idx++) {
d[idx] = _calc_bcn_drift(bcn_i->bcn_s_i.info[SHORT_BCN_MOD][idx],
tbtt, bcn_i->bcn_intvl);
}
_data_sorting(d, bcn_i->bcn_s_i.num);
}
#define BCN_DISPERSION 30 /* More than 30% data is outlier */
#define MIN_BCN 30 /* Min % of rx bcn num in watchdog */
#define TGT_BCN 80 /* Target % of rx bcn in watchdog */
#define ACPT_BCN 70 /* Acceptable % of rx bcn in watchdog */
#define INCREASE_BCN_TIMEOUT 5 /* Increase bcn timeout */
#define AVG_RSSI_NUM 5
#define STG_RSSI 80 /* strong RSSI */
#define IDEAL_MOD 0 /* IDEAL Mod */
#define PRFCT_PCT 90 /* Perfect percentage */
#define COPM_ENV 5 /* compensate of environment */
static void
_get_bcn_tracking_info(struct phl_info_t *phl, struct rtw_phl_stainfo_t *sta,
struct rtw_rx_bcn_info *bcn_i, struct rtw_bcn_tracking_cfg *cfg)
{
struct rtw_bcn_dist *b_dist = &bcn_i->bcn_dist_i;
struct rtw_bcn_short_i *bcn_s = &bcn_i->bcn_s_i;
u16 *b_drift = bcn_s->bcn_drift;
u16 extrm_dispersion = 0;
u16 min_bcn = 0, tgt_bcn = 0, acpt_bcn = 0;
u16 tgt_idx = 0, b_intvl = bcn_i->bcn_intvl;
u8 avg_rssi = 0, pct = 0;
u16 rx_t = 0, tbtt = (u16)(bcn_i->offset_i.offset / TU);
bool comp_env = true;
if (0 == b_intvl) {
PHL_TRACE(COMP_PHL_DBG, _PHL_ERR_, "%s:b_intvl = 0, please check code flow\n",
__func__);
return;
}
min_bcn = ((WDOG_PERIOD / b_intvl) * MIN_BCN) / 100;
PHL_TRACE(COMP_PHL_DBG, _PHL_DEBUG_, "%s(): num_per_watchdog(%d), min_bcn(%d)\n",
__func__, bcn_i->num_per_watchdog, min_bcn);
/* Rx little */
if (bcn_i->num_per_watchdog < min_bcn) {
PHL_TRACE(COMP_PHL_DBG, _PHL_DEBUG_, "%s(): Rx little bcn in watcdog\n",
__func__);
cfg->bcn_timeout = (b_intvl * TGT_BCN) / 100;
goto _cfg;
}
/* Fail to calculate bcn distribution */
if (b_dist->calc_fail) {
PHL_TRACE(COMP_PHL_DBG, _PHL_DEBUG_, "%s(): b_dist->calc_fail\n",
__func__);
cfg->bcn_timeout = (b_intvl * TGT_BCN) / 100;
goto _cfg;
}
/* bcn distribution is extreme dispersion */
extrm_dispersion = bcn_s->num;
extrm_dispersion = (extrm_dispersion * BCN_DISPERSION) / 100;
if (b_dist->outlier_num >= extrm_dispersion) {
PHL_TRACE(COMP_PHL_DBG, _PHL_DEBUG_, "%s(): It's extreme dispersion. outlier_num(%d), extrm_dispersion(%d)\n",
__func__, b_dist->outlier_num, extrm_dispersion);
cfg->bcn_timeout = b_dist->max;
goto _cfg;
}
acpt_bcn = ((WDOG_PERIOD / b_intvl) * ACPT_BCN) / 100;
tgt_bcn = ((WDOG_PERIOD / b_intvl) * TGT_BCN) / 100;
PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_, "%s(): tgt_bcn_wdg(%d), acpt_bcn_wdg(%d)\n",
__func__, tgt_bcn, acpt_bcn);
if (bcn_i->num_per_watchdog < acpt_bcn) {
goto _rx_middle;
}
if (bcn_s->num == MAX_SHORT_INFO_BCN_NUM) {
avg_rssi = _get_bcn_avg_rssi(bcn_s, AVG_RSSI_NUM);
pct = _get_bcn_dist_pct(bcn_s, IDEAL_MOD);
PHL_TRACE(COMP_PHL_DBG, _PHL_DEBUG_, "%s(): avg_rssi(%d), %d percentage of mod <= %d\n",
__func__, avg_rssi, pct, IDEAL_MOD);
if (avg_rssi >= STG_RSSI) {
if (pct >= PRFCT_PCT) {
cfg->bcn_timeout = 0;
comp_env = false;
} else {
cfg->bcn_timeout = _calc_bcn_drift(
_get_bcn_histogram_bandary(b_dist, TGT_BCN),
tbtt, b_intvl);
}
goto _cfg;
}
}
/* rx enough */
tgt_idx = bcn_s->num;
tgt_idx = ((tgt_idx * TGT_BCN) / 100);
cfg->bcn_timeout = b_drift[tgt_idx];
goto _cfg;
/* rx middle */
_rx_middle:
if (bcn_s->num < tgt_bcn) {
/* raw data is not enough */
cfg->bcn_timeout = b_dist->max + INCREASE_BCN_TIMEOUT;
goto _cfg;
}
tgt_bcn = bcn_s->num;
tgt_bcn = (tgt_bcn * TGT_BCN) / 100;
if (b_dist->outlier_num > (bcn_s->num - tgt_bcn)) {
cfg->bcn_timeout = (b_dist->max + b_dist->outlier_h) / 2;
} else {
cfg->bcn_timeout = b_dist->outlier_h + INCREASE_BCN_TIMEOUT;
}
_cfg:
rx_t = _get_rx_bcn_t(sta, bcn_i);
cfg->bcn_timeout += rx_t;
if (comp_env &&
phl_sta_latency_sensitive_chk(phl, sta, LAT_SEN_CHK_BCN_TRACKING)) {
cfg->bcn_timeout += COPM_ENV;
PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_, "%s(): extend timeout for latency_sensitive\n",
__func__);
}
PHL_TRACE(COMP_PHL_DBG, _PHL_DEBUG_, "%s(): pkt_len(%d), rate(%d), rx_pkt_t(%d)\n",
__func__, bcn_i->pkt_len, bcn_i->rate, rx_t);
PHL_TRACE(COMP_PHL_DBG, _PHL_DEBUG_, "%s(): num(%d), tgt_bcn(%d), outlier_num(%d), max(%d)\n",
__func__, bcn_s->num, tgt_bcn, b_dist->outlier_num, b_dist->max);
PHL_TRACE(COMP_PHL_DBG, _PHL_DEBUG_, "%s(): macid(%d), bcn_timeout(%d), bcn_ofset(%d us, %d TU)\n",
__func__, sta->macid, cfg->bcn_timeout, bcn_i->offset_i.offset,
tbtt);
}
static void
_calc_bcn_tracking(struct phl_info_t *phl, struct rtw_phl_stainfo_t *sta,
struct rtw_rx_bcn_info *bcn_i)
{
void *priv = phl_to_drvpriv(phl);
_os_mem_set(priv, &bcn_i->cfg, 0, sizeof(struct rtw_bcn_tracking_cfg));
_get_bcn_tracking_info(phl, sta, bcn_i, &bcn_i->cfg);
}
#define MIN_BCN_NUM_BOX_PLOT 4
#define MIN_OFST_AS_FENCE_L 5
/* reference https://zh.m.wikipedia.org/zh-tw/%E7%AE%B1%E5%BD%A2%E5%9C%96 */
static bool _dist_box_plot(struct rtw_rx_bcn_info *bcn_i)
{
struct rtw_bcn_short_i *bcn_s = &bcn_i->bcn_s_i;
struct rtw_bcn_dist *dist_i = &bcn_i->bcn_dist_i;
u16 q1 = 0, q3 = 0, iqr = 0, fence_l = 0, fence_h = 0;
u16 temp, idx = 0;
if (bcn_s->num < MIN_BCN_NUM_BOX_PLOT) {
PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_, "%s: bcn_s_i->num(%d) < MIN_BCN_NUM_BOX_PLOT(%d), cna't calculate\n",
__func__, bcn_s->num, MIN_BCN_NUM_BOX_PLOT);
return false;
}
dist_i->min = bcn_s->bcn_drift[0];
dist_i->max = bcn_s->bcn_drift[bcn_s->num - 1];
q1 = bcn_s->num / 4;
if ((bcn_s->num % 4) == 0) {
temp = bcn_s->bcn_drift[q1 - 1] +
bcn_s->bcn_drift[q1];
q1 = (temp * 10) / 2;
} else {
q1 = (u16)(bcn_s->bcn_drift[q1] * 10);
}
q3 = (bcn_s->num * 3) / 4;
if (((bcn_s->num * 3) % 4) == 0) {
temp = bcn_s->bcn_drift[q3 - 1] +
bcn_s->bcn_drift[q3];
q3 = (temp * 10) / 2;
} else {
q3 = bcn_s->bcn_drift[q3] * 10;
}
iqr = q3 - q1;
temp = (3 * iqr) / 2;
if (dist_i->min <= MIN_OFST_AS_FENCE_L) {
fence_l = dist_i->min;
} else if (q1 > temp) {
fence_l = (q1 - temp) / 10;
} else {
fence_l = 0;
}
fence_h = ((q3 + temp) / 10);
dist_i->outlier_l = fence_l;
dist_i->outlier_h = fence_h;
for (idx = 0; idx < bcn_s->num; idx++) {
if ((bcn_s->bcn_drift[idx] < dist_i->outlier_l) ||
(bcn_s->bcn_drift[idx] > dist_i->outlier_h))
dist_i->outlier_num++;
}
PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_, "%s: q1(%d), q3(%d), iqr(%d), fence_l(%d), fence_h(%d)\n",
__func__, q1, q3, iqr, fence_l, fence_h);
return true;
}
static void _get_bcn_histogram(struct rtw_rx_bcn_info *bcn_i)
{
struct rtw_bcn_short_i *bcn_s_i = &bcn_i->bcn_s_i;
struct rtw_bcn_histogram *hist_i = &bcn_i->bcn_dist_i.hist_i;
u8 idx = 0, jdx = 0;
u16 th = 0, lower_bound = 0;
hist_i->num = bcn_s_i->num;
for (idx = 0; idx < bcn_s_i->num; idx++) {
/* init the bandary */
for (jdx = 0; jdx < MAX_NUM_BIN; jdx++) {
th = bcn_s_i->min + (BIN_WIDTH * (jdx + 1));
if (jdx == (MAX_NUM_BIN - 1)) {
if (th > bcn_s_i->max)
hist_i->h_array[H_UPPER][jdx] = th;
else
hist_i->h_array[H_UPPER][jdx] = bcn_s_i->max;
} else {
hist_i->h_array[H_UPPER][jdx] = th;
}
}
for (jdx = 0; jdx < MAX_NUM_BIN; jdx++) {
if (jdx == (MAX_NUM_BIN - 1)) {
hist_i->h_array[H_CNT][jdx]++;
break;
}
th = bcn_s_i->min + (BIN_WIDTH * (jdx + 1));
if (bcn_s_i->info[SHORT_BCN_MOD][idx] < th) {
hist_i->h_array[H_CNT][jdx]++;
break;
}
}
}
for (idx = 0; idx < MAX_NUM_BIN; idx++) {
PHL_TRACE(COMP_PHL_DBG, _PHL_DEBUG_, "%s: Mod Histogram: %02d ~ %02d, num: %d\n",
__func__, lower_bound, hist_i->h_array[H_UPPER][idx], hist_i->h_array[H_CNT][idx]);
lower_bound = hist_i->h_array[H_UPPER][idx];
}
}
static void _get_bcn_distribution(struct phl_info_t *phl, struct rtw_rx_bcn_info *bcn_i)
{
void *priv = phl_to_drvpriv(phl);
_os_mem_set(priv, &bcn_i->bcn_dist_i, 0, sizeof(struct rtw_bcn_dist));
if (_dist_box_plot(bcn_i))
PHL_TRACE(COMP_PHL_DBG, _PHL_DEBUG_, "%s: bcn_dist_i: min(%d), max(%d), outlier_num(%d), outlier_l(%d), outlier_h(%d)\n",
__func__, bcn_i->bcn_dist_i.min, bcn_i->bcn_dist_i.max,
bcn_i->bcn_dist_i.outlier_num, bcn_i->bcn_dist_i.outlier_l,
bcn_i->bcn_dist_i.outlier_h);
else
bcn_i->bcn_dist_i.calc_fail = true;
_get_bcn_histogram(bcn_i);
}
void phl_bcn_watchdog_sw(struct phl_info_t *phl)
{
void *priv = phl_to_drvpriv(phl);
u8 ridx = MAX_WIFI_ROLE_NUMBER, lidx = 0;
struct rtw_wifi_role_t *wrole = NULL;
struct rtw_phl_stainfo_t *sta = NULL;
struct rtw_rx_bcn_info bcn_i = {0};
struct rtw_wifi_role_link_t *rlink = NULL;
for (ridx = 0; ridx < MAX_WIFI_ROLE_NUMBER; ridx++) {
wrole = phl_get_wrole_by_ridx(phl, ridx);
if ((wrole == NULL) || (wrole->active == false) ||
(wrole->mstate != MLME_LINKED))
continue;
if (!rtw_phl_role_is_client_category(wrole))
continue;
for (lidx = 0; lidx < wrole->rlink_num; lidx++) {
rlink = get_rlink(wrole, lidx);
sta = rtw_phl_get_stainfo_self(phl, rlink);
phl_get_sta_bcn_info(phl, sta, &bcn_i);
if (0 == bcn_i.num_per_watchdog)
continue;
/* record bcn num of last watchdog */
sta->bcn_i.last_num_per_watchdog = bcn_i.num_per_watchdog;
/* reset cnt */
sta->bcn_i.num_per_watchdog = 0;
_get_bcn_drift(&bcn_i);
_get_bcn_distribution(phl, &bcn_i);
_calc_bcn_tracking(phl, sta, &bcn_i);
/* update bcn_dist_i and bcn_tracking_cfg */
_os_mem_cpy(priv, &sta->bcn_i.bcn_dist_i, &bcn_i.bcn_dist_i,
sizeof(struct rtw_bcn_dist));
_os_mem_cpy(priv, &sta->bcn_i.cfg, &bcn_i.cfg,
sizeof(struct rtw_bcn_tracking_cfg));
PHL_TRACE(COMP_PHL_DBG, _PHL_DEBUG_, "%s: sta bcn_dist_i: min(%d), max(%d), outlier_num(%d), outlier_l(%d), outlier_h(%d)\n",
__func__,
sta->bcn_i.bcn_dist_i.min,
sta->bcn_i.bcn_dist_i.max,
sta->bcn_i.bcn_dist_i.outlier_num,
sta->bcn_i.bcn_dist_i.outlier_l,
sta->bcn_i.bcn_dist_i.outlier_h);
}
}
}
void phl_bcn_watchdog_hw(struct phl_info_t *phl)
{
u8 ridx = MAX_WIFI_ROLE_NUMBER;
struct rtw_wifi_role_t *wrole = NULL;
struct rtw_phl_stainfo_t *sta = NULL;
struct rtw_bcn_offset b_ofst_i = {0};
enum rtw_hal_status hstatus = RTW_HAL_STATUS_SUCCESS;
u8 lidx = 0;
struct rtw_wifi_role_link_t *rlink = NULL;
for (ridx = 0; ridx < MAX_WIFI_ROLE_NUMBER; ridx++) {
wrole = phl_get_wrole_by_ridx(phl, ridx);
if (wrole->active == false)
continue;
if (rtw_phl_role_is_client_category(wrole) && wrole->mstate == MLME_LINKED) {
for (lidx = 0; lidx < wrole->rlink_num; lidx++) {
rlink = get_rlink(wrole, lidx);
sta = rtw_phl_get_stainfo_self(phl, rlink);
phl_get_sta_bcn_offset_info(phl, sta, &b_ofst_i);
if (b_ofst_i.conf_lvl < CONF_LVL_MID ||
b_ofst_i.offset == b_ofst_i.cr_tbtt_shift)
continue;
PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_, "%s(): update bcn offset from %d to %d us\n",
__func__, b_ofst_i.cr_tbtt_shift, b_ofst_i.offset);
hstatus = rtw_hal_tbtt_tuning(phl->hal,
rlink->hw_band,
rlink->hw_port,
b_ofst_i.offset);
if (hstatus == RTW_HAL_STATUS_SUCCESS)
phl_sta_up_tbtt_shift_setting(sta,
b_ofst_i.offset);
else
PHL_ERR("%s(): tbtt cfg fail, status: %d\n",
__func__, hstatus);
}
}
}
}
/*
* calculate the value between current TSF and TBTT
* TSF 0 50 180 150 250
* TBTT ^ ^ ^
* Curr T |
* | 30 |
*
* TSF 0 80 120 180 280
* TBTT ^ ^ ^
* Curr T |
* | 40 |
* @wrole: specific role, we get bcn offset info from the role.
* @cur_t: current TSF
* @ofst: output value, unit: TU
*/
bool phl_calc_offset_from_tbtt(struct phl_info_t *phl,
struct rtw_wifi_role_t *wrole,
struct rtw_wifi_role_link_t *rlink,
u64 cur_t,
u16 *ofst
)
{
struct rtw_phl_stainfo_t *sta = rtw_phl_get_stainfo_self(phl, rlink);
struct rtw_bcn_offset b_ofst_i = {0};
u64 b_ofst = 0, b_intvl = 0;
u32 mod = 0; /*TU*/
phl_get_sta_bcn_offset_info(phl, sta, &b_ofst_i);
b_ofst = b_ofst_i.offset / TU;
b_intvl = phl_role_get_bcn_intvl(phl, wrole, rlink);
if (0 == b_intvl) {
PHL_TRACE(COMP_PHL_DBG, _PHL_ERR_, "phl_calc_offset_from_tbtt(): Fail, b_intvl ==0, wrole->id(%d), rlink->id(%d), type(%d)\n",
wrole->id, rlink->id, wrole->type);
return false;
}
mod = (u32)_os_division64(_os_modular64(cur_t, b_intvl * TU), TU);
if (mod < b_ofst) {
*ofst = (u16)(mod + (b_intvl - b_ofst));
} else {
*ofst = (u16)(mod - b_ofst);
}
PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_, "phl_calc_offset_from_tbtt(): wrole->id(%d), rlink->id(%d), ofst(%d), cur_t: 0x%08x %08x modular(%d, TU), Bcn offset: conf_lvl(%d), offset(%d)\n",
wrole->id, rlink->id, *ofst, (u32)(cur_t >> 32), (u32)cur_t, mod,
b_ofst_i.conf_lvl, (u32)b_ofst);
return true;
}
/*
* Synchronize TBTT of target role with TBTT of sourec role
* Assume TBTT of target role is locate in Mod(Tgt Tsf) = 0
* @sync_ofst: Offset between TBTT of target role and TBTT of sourec role. Unit: TU
* @sync_now_once: Sync once time right now.
* @*diff_t : output diff_tsf. Unit: TU
*/
enum rtw_phl_status rtw_phl_tbtt_sync(struct phl_info_t *phl,
struct rtw_wifi_role_t *src_role,
struct rtw_wifi_role_link_t *src_rlink,
struct rtw_wifi_role_t *tgt_role,
struct rtw_wifi_role_link_t *tgt_rlink,
u16 sync_ofst,
bool sync_now_once,
u16 *diff_t)
{
enum rtw_phl_status status = RTW_PHL_STATUS_FAILURE;
u32 tsf_h = 0, tsf_l = 0;
u64 tsf = 0, tgt_tsf = 0, bcn_intvl = 0;
u16 ofst = 0;
u64 diff_tsf = 0;
enum hal_tsf_sync_act act = sync_now_once ? HAL_TSF_SYNC_NOW_ONCE :
HAL_TSF_EN_SYNC_AUTO;
if (RTW_HAL_STATUS_SUCCESS != rtw_hal_get_tsf(phl->hal,
src_rlink->hw_band, src_rlink->hw_port,
&tsf_h, &tsf_l)) {
PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_, "rtw_phl_tbtt_sync(): Get tsf fail, src_role->id(%d) src_rlink->id(%d)\n",
src_role->id, src_rlink->id);
goto exit;
}
bcn_intvl = phl_role_get_bcn_intvl(phl, tgt_role, tgt_rlink);
if (bcn_intvl == 0) {
PHL_TRACE(COMP_PHL_DBG, _PHL_ERR_, "rtw_phl_tbtt_sync(): bcn_intvl == 0, tgt_role->id(%d) tgt_rlink->id(%d)\n",
tgt_role->id, tgt_rlink->id);
goto exit;
}
tsf = tsf_h;
tsf = tsf << 32;
tsf |= tsf_l;
/*calculate the value between current TSF and TBTT*/
phl_calc_offset_from_tbtt(phl, src_role, src_rlink, tsf, &ofst);
tgt_tsf = (tsf + sync_ofst * TU) - ofst * TU;
/*Find diff_tsf, let Mod((tgt_tsf + diff_tsf), bcn_intvl) = 0*/
diff_tsf = bcn_intvl * TU - _os_modular64(tgt_tsf, bcn_intvl * TU);
diff_tsf = _os_division64(diff_tsf, TU);
PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_, "rtw_phl_tbtt_sync(): diff_tsf(%d), sync_ofst(%d), ofst(%d)\n",
(u32)diff_tsf, sync_ofst, (u32)ofst);
if (RTW_HAL_STATUS_SUCCESS != rtw_hal_tsf_sync(phl->hal,
src_rlink->hw_port, tgt_rlink->hw_port,
src_rlink->hw_band, (s32)diff_tsf,
act)) {
PHL_TRACE(COMP_PHL_DBG, _PHL_ERR_, "rtw_phl_tbtt_sync(): Sync tsf fail\n");
goto exit;
}
if (RTW_HAL_STATUS_SUCCESS == rtw_hal_get_tsf(phl->hal,
src_rlink->hw_band, src_rlink->hw_port,
&tsf_h, &tsf_l)) {
PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_, "rtw_phl_tbtt_sync(): tsf_src(0x%08x %08x)\n",
tsf_h, tsf_l);
}
if (RTW_HAL_STATUS_SUCCESS == rtw_hal_get_tsf(phl->hal,
tgt_rlink->hw_band, tgt_rlink->hw_port,
&tsf_h, &tsf_l)) {
PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_, "rtw_phl_tbtt_sync(): tsf_tgt(0x%08x %08x)\n",
tsf_h, tsf_l);
}
*diff_t = (u16)diff_tsf;
status = RTW_PHL_STATUS_SUCCESS;
exit:
return status;
}
bool phl_self_stainfo_chk(struct phl_info_t *phl_info,
struct rtw_wifi_role_t *wrole,
struct rtw_wifi_role_link_t *rlink,
struct rtw_phl_stainfo_t *sta)
{
return _phl_self_stainfo_chk(phl_info, wrole, rlink, sta);
}
static void _phl_client_ps_annc_notify_done(void *drv_priv, u8 *cmd,
u32 cmd_len, enum rtw_phl_status status)
{
if (cmd) {
_os_kmem_free(drv_priv, cmd, cmd_len);
cmd = NULL;
PHL_TRACE(COMP_PHL_TWT, _PHL_INFO_, "%s\n", __func__);
}
}
enum rtw_phl_status
phl_send_client_ps_annc_ntfy_cmd(struct phl_info_t *phl,
struct rtw_phl_stainfo_t *sta, enum link_state lstate)
{
enum rtw_phl_status psts = RTW_PHL_STATUS_FAILURE;
void *drv = phl_to_drvpriv(phl);
u32 param_len = sizeof(struct link_ntfy);
struct link_ntfy *param = _os_kmem_alloc(drv, param_len);
if (param == NULL) {
PHL_ERR("%s: alloc param failed!\n", __func__);
goto _exit;
}
param->lstate = lstate;
param->rsvd[0].ptr = (void *)sta;
psts = phl_cmd_enqueue(phl,
sta->rlink->hw_band,
MSG_EVT_CLIENT_PS_ANNC,
(u8 *)param,
param_len,
_phl_client_ps_annc_notify_done,
PHL_CMD_NO_WAIT,
0);
if (psts != RTW_PHL_STATUS_SUCCESS) {
PHL_INFO("%s: Fail to issue MSG_EVT_CLIENT_PS_ANNC!!\n",
__func__);
if (!is_cmd_failure(psts)) {
/* Send cmd fail */
_os_kmem_free(drv, param, param_len);
psts = RTW_PHL_STATUS_FAILURE;
}
goto _exit;
}
_exit:
PHL_INFO("%s: Issue cmd, status(%d)\n", __func__, psts);
return psts;
}
enum rtw_phl_status
rtw_phl_sta_tsf_sync_done(void *phl, struct rtw_phl_stainfo_t *sta)
{
enum rtw_phl_status psts = RTW_PHL_STATUS_FAILURE;
PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_, "Send tsf sync done event!\n");
psts = phl_cmd_enqueue((struct phl_info_t *)phl, sta->rlink->hw_band,
MSG_EVT_TSF_SYNC_DONE, (u8 *)sta, 8,
NULL, PHL_CMD_NO_WAIT, 0);
if (psts != RTW_PHL_STATUS_SUCCESS)
PHL_ERR("%s: Dispr send msg fail!\n", __func__);
return psts;
}
enum rtw_phl_status
rtw_phl_sta_assoc_cap_process(struct rtw_phl_stainfo_t *sta,
bool backup)
{
if (backup) {
sta->asoc_cap.nss_rx_bk = sta->asoc_cap.nss_rx;
sta->asoc_cap.stbc_ht_rx_bk = sta->asoc_cap.stbc_ht_rx;
sta->asoc_cap.stbc_vht_rx_bk = sta->asoc_cap.stbc_vht_rx;
sta->asoc_cap.stbc_he_rx_bk = sta->asoc_cap.stbc_he_rx;
}
else {/*restore*/
sta->asoc_cap.nss_rx = sta->asoc_cap.nss_rx_bk;
sta->asoc_cap.stbc_ht_rx = sta->asoc_cap.stbc_ht_rx_bk;
sta->asoc_cap.stbc_vht_rx = sta->asoc_cap.stbc_vht_rx_bk;
sta->asoc_cap.stbc_he_rx = sta->asoc_cap.stbc_he_rx_bk;
}
return RTW_PHL_STATUS_SUCCESS;
}
/*********** phl mld_ctrl section ***********/
static enum rtw_phl_status
_phl_mld_init(struct phl_info_t *phl_info,
struct rtw_phl_mld_t *mld)
{
INIT_LIST_HEAD(&mld->list);
mld->type = DEV_TYPE_INACTIVE;
mld->sta_num = 0;
return RTW_PHL_STATUS_SUCCESS;
}
enum rtw_phl_status
phl_mld_enqueue(struct phl_info_t *phl_info,
struct phl_queue *mld_queue,
struct rtw_phl_mld_t *mld)
{
void *drv = phl_to_drvpriv(phl_info);
if (!mld)
return RTW_PHL_STATUS_FAILURE;
_os_spinlock(drv, &mld_queue->lock, _bh, NULL);
list_add_tail(&mld->list, &mld_queue->queue);
mld_queue->cnt++;
_os_spinunlock(drv, &mld_queue->lock, _bh, NULL);
return RTW_PHL_STATUS_SUCCESS;
}
struct rtw_phl_mld_t *
phl_mld_dequeue(struct phl_info_t *phl_info,
struct phl_queue *mld_queue)
{
struct rtw_phl_mld_t *mld = NULL;
void *drv = phl_to_drvpriv(phl_info);
_os_spinlock(drv, &mld_queue->lock, _bh, NULL);
if (list_empty(&mld_queue->queue)) {
mld = NULL;
} else {
mld = list_first_entry(&mld_queue->queue,
struct rtw_phl_mld_t, list);
list_del(&mld->list);
mld_queue->cnt--;
}
_os_spinunlock(drv, &mld_queue->lock, _bh, NULL);
return mld;
}
enum rtw_phl_status
phl_mld_queue_del(struct phl_info_t *phl_info,
struct phl_queue *mld_queue,
struct rtw_phl_mld_t *mld)
{
void *drv = phl_to_drvpriv(phl_info);
if (!mld)
return RTW_PHL_STATUS_FAILURE;
_os_spinlock(drv, &mld_queue->lock, _bh, NULL);
if (mld_queue->cnt) {
list_del(&mld->list);
mld_queue->cnt--;
}
_os_spinunlock(drv, &mld_queue->lock, _bh, NULL);
return RTW_PHL_STATUS_SUCCESS;
}
struct rtw_phl_mld_t *
phl_mld_queue_search(struct phl_info_t *phl_info,
struct phl_queue *mld_queue,
u8 *addr) /* MLD or non-MLD mac address */
{
struct rtw_phl_mld_t *mld = NULL;
_os_list *mld_list = &mld_queue->queue;
void *drv = phl_to_drvpriv(phl_info);
bool mld_found = false;
_os_spinlock(drv, &mld_queue->lock, _bh, NULL);
if (list_empty(mld_list) == true)
goto _exit;
phl_list_for_loop(mld, struct rtw_phl_mld_t, mld_list, list) {
if (_os_mem_cmp(phl_to_drvpriv(phl_info),
mld->mac_addr, addr, MAC_ALEN) == 0) {
mld_found = true;
break;
}
}
_exit:
_os_spinunlock(drv, &mld_queue->lock, _bh, NULL);
if (mld_found == false)
mld = NULL;
return mld;
}
struct rtw_phl_mld_t *
phl_mld_queue_get_first(struct phl_info_t *phl_info,
struct phl_queue *mld_queue)
{
_os_list *mld_list = &mld_queue->queue;
void *drv = phl_to_drvpriv(phl_info);
struct rtw_phl_mld_t *mld = NULL;
/* first mld in assoc_mld_queue is self mld */
_os_spinlock(drv, &mld_queue->lock, _bh, NULL);
if (list_empty(mld_list) == true)
goto _exit;
mld = list_first_entry(mld_list, struct rtw_phl_mld_t, list);
_exit :
_os_spinunlock(drv, &mld_queue->lock, _bh, NULL);
return mld;
}
enum rtw_phl_status
phl_mld_ctrl_deinit(struct phl_info_t *phl_info)
{
struct mld_ctl_t *mld_ctrl = phl_to_mld_ctrl(phl_info);
void *drv = phl_to_drvpriv(phl_info);
struct rtw_phl_mld_t *mld = NULL;
struct phl_queue *free_mld_queue = &mld_ctrl->free_mld_queue;
FUNCIN();
do {
mld = phl_mld_dequeue(phl_info, free_mld_queue);
} while (mld != NULL);
pq_deinit(drv, free_mld_queue);
if (mld_ctrl->allocated_mld_buf)
_os_mem_free(drv, mld_ctrl->allocated_mld_buf,
mld_ctrl->allocated_mld_sz);
FUNCOUT();
return RTW_PHL_STATUS_SUCCESS;
}
enum rtw_phl_status
phl_mld_ctrl_init(struct phl_info_t *phl_info)
{
struct mld_ctl_t *mld_ctrl = phl_to_mld_ctrl(phl_info);
void *drv = phl_to_drvpriv(phl_info);
struct rtw_phl_mld_t *mld = NULL;
enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE;
struct phl_queue *free_mld_queue = NULL;
u16 i;
bool mld_init_fail = false;
FUNCIN();
mld_ctrl->phl_info = phl_info;
mld_ctrl->allocated_mld_sz = sizeof(struct rtw_phl_mld_t) * PHL_MAX_MLD_NUM;
#ifdef MEM_ALIGNMENT
mld_ctrl->allocated_mld_sz += MEM_ALIGNMENT_OFFSET;
#endif
mld_ctrl->allocated_mld_buf =
_os_mem_alloc(drv, mld_ctrl->allocated_mld_sz);
if (!mld_ctrl->allocated_mld_buf) {
PHL_ERR("allocate mld buf failed\n");
goto _exit;
}
mld_ctrl->mld_buf = mld_ctrl->allocated_mld_buf;
#ifdef MEM_ALIGNMENT
if (mld_ctrl->mld_buf & MEM_ALIGNMENT_PADDING)
mld_ctrl->mld_buf += MEM_ALIGNMENT_OFFSET -
(mld_ctrl->mld_buf & MEM_ALIGNMENT_PADDING);
#endif
free_mld_queue = &mld_ctrl->free_mld_queue;
pq_init(drv, free_mld_queue);
mld = (struct rtw_phl_mld_t *)(mld_ctrl->mld_buf);
for (i = 0; i < PHL_MAX_MLD_NUM; i++) {
if (_phl_mld_init(phl_info, mld) != RTW_PHL_STATUS_SUCCESS) {
mld_init_fail = true;
break;
}
phl_mld_enqueue(phl_info, free_mld_queue, mld);
mld++;
}
if (mld_init_fail == true) {
PHL_ERR("mld init failed\n");
phl_mld_ctrl_deinit(phl_info);
goto _exit;
}
//PHL_DUMP_MLD_EX(phl_info);
pstatus = RTW_PHL_STATUS_SUCCESS;
_exit:
FUNCOUT();
return pstatus;
}
/*********** phl mld section ***********/
static bool _phl_self_mld_chk(struct phl_info_t *phl_info,
struct rtw_wifi_role_t *wrole,
struct rtw_phl_mld_t *mld)
{
void *drv = phl_to_drvpriv(phl_info);
bool is_self = false;
switch (wrole->type) {
case PHL_RTYPE_STATION:
case PHL_RTYPE_P2P_GC:
_os_mem_cpy(drv, mld->mac_addr, wrole->mac_addr, MAC_ALEN);
is_self = true;
break;
case PHL_RTYPE_AP:
case PHL_RTYPE_MESH:
case PHL_RTYPE_P2P_GO:
case PHL_RTYPE_TDLS:
if (_os_mem_cmp(drv, wrole->mac_addr, mld->mac_addr, MAC_ALEN) == 0)
is_self = true;
break;
case PHL_RTYPE_NONE:
case PHL_RTYPE_VAP:
case PHL_RTYPE_ADHOC:
case PHL_RTYPE_ADHOC_MASTER:
case PHL_RTYPE_MONITOR:
case PHL_RTYPE_P2P_DEVICE:
case PHL_RTYPE_NAN:
case PHL_MLME_MAX:
PHL_TRACE(COMP_PHL_DBG, _PHL_ERR_,
"_phl_self_mld_chk(): Unsupported case:%d, please check it\n",
wrole->type);
break;
default:
PHL_TRACE(COMP_PHL_DBG, _PHL_ERR_,
"_phl_self_mld_chk(): role-type(%d) not recognize\n",
wrole->type);
break;
}
return is_self;
}
enum rtw_phl_status
_phl_link_mld_stainfo(struct rtw_phl_mld_t *mld,
struct rtw_phl_stainfo_t *phl_sta,
u8 lidx)
{
mld->phl_sta[lidx] = phl_sta;
phl_sta->mld = mld;
return RTW_PHL_STATUS_SUCCESS;
}
enum rtw_phl_status
phl_free_mld(struct phl_info_t *phl_info, struct rtw_phl_mld_t *mld)
{
enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE;
struct mld_ctl_t *mld_ctrl = phl_to_mld_ctrl(phl_info);
struct rtw_wifi_role_t *wrole = NULL;
FUNCIN();
if(mld == NULL) {
PHL_ERR("%s mld is NULL\n", __func__);
goto _exit;
}
wrole = mld->wrole;
if (!is_broadcast_mac_addr(mld->mac_addr)) {
if (_phl_self_mld_chk(phl_info, wrole, mld) == true) {
pstatus = RTW_PHL_STATUS_SUCCESS;
goto _exit;
}
}
pstatus = phl_mld_queue_del(phl_info, &wrole->assoc_mld_queue, mld);
if (pstatus != RTW_PHL_STATUS_SUCCESS) {
PHL_ERR("phl_mld_queue_del failed\n");
}
pstatus = phl_mld_enqueue(phl_info, &mld_ctrl->free_mld_queue, mld);
if (pstatus != RTW_PHL_STATUS_SUCCESS)
PHL_ERR("phl_mld_enqueue to free queue failed\n");
if (pstatus == RTW_PHL_STATUS_SUCCESS)
mld->type = DEV_TYPE_INACTIVE;
_exit:
//PHL_DUMP_MLD_EX(phl_info);
FUNCOUT();
return pstatus;
}
struct rtw_phl_mld_t *
phl_alloc_mld(struct phl_info_t *phl_info,
struct rtw_wifi_role_t *wrole,
u8 *mac_addr,
enum rtw_device_type type)
{
struct mld_ctl_t *mld_ctrl = phl_to_mld_ctrl(phl_info);
struct rtw_phl_mld_t *mld = NULL;
void *drv = phl_to_drvpriv(phl_info);
bool bmc_sta = false;
u8 lidx;
FUNCIN();
if (is_broadcast_mac_addr(mac_addr))
bmc_sta = true;
/* if sta_addr is bmc addr, allocate new sta_info */
if ((wrole->type == PHL_RTYPE_STATION || wrole->type == PHL_RTYPE_P2P_GC)
&& (!bmc_sta)) {
mld = rtw_phl_get_mld_self(phl_info, wrole);
if (mld) {
_os_mem_cpy(drv, mld->mac_addr, mac_addr, MAC_ALEN);
goto _exit;
}
}
/* check mld exist */
mld = rtw_phl_get_mld_by_addr(phl_info, wrole, mac_addr);
if (mld) {
PHL_INFO("%s mld (%02x:%02x:%02x:%02x:%02x:%02x) exist\n",
__func__,
mac_addr[0], mac_addr[1], mac_addr[2],
mac_addr[3], mac_addr[4], mac_addr[5]);
goto _exit;
}
mld = phl_mld_dequeue(phl_info, &mld_ctrl->free_mld_queue);
if (mld == NULL) {
PHL_ERR("allocate mld failure!\n");
goto _exit;
}
_os_mem_cpy(drv, mld->mac_addr, mac_addr, MAC_ALEN);
mld->wrole = wrole;
mld->type = type;
mld->sta_num = 0;
for (lidx = 0; lidx < RTW_RLINK_MAX; lidx++) {
mld->phl_sta[lidx] = NULL;
mld->assoc_status[lidx].used = 0;
mld->assoc_status[lidx].status = 0;
mld->assoc_status[lidx].link_id = 0;
}
phl_mld_enqueue(phl_info, &wrole->assoc_mld_queue, mld);
_exit:
//PHL_DUMP_MLD_EX(phl_info);
FUNCOUT();
return mld;
}
enum rtw_phl_status
phl_wifi_role_free_mld(struct phl_info_t *phl_info,
struct rtw_wifi_role_t *role)
{
struct rtw_phl_mld_t *mld = NULL;
struct mld_ctl_t *mld_ctrl = phl_to_mld_ctrl(phl_info);
//PHL_DUMP_MLD_EX(phl_info);
do {
mld = phl_mld_dequeue(phl_info, &role->assoc_mld_queue);
if (mld) {
phl_mld_enqueue(phl_info,
&mld_ctrl->free_mld_queue, mld);
mld->type = DEV_TYPE_INACTIVE;
}
} while(mld != NULL);
return RTW_PHL_STATUS_SUCCESS;
}
enum rtw_phl_status
rtw_phl_free_mld(void *phl, struct rtw_phl_mld_t *mld)
{
struct phl_info_t *phl_info = (struct phl_info_t *)phl;
return phl_free_mld(phl_info, mld);
}
struct rtw_phl_mld_t *
rtw_phl_alloc_mld(void *phl,
struct rtw_wifi_role_t *wrole,
u8 *mac_addr,
enum rtw_device_type type)
{
struct phl_info_t *phl_info = (struct phl_info_t *)phl;
return phl_alloc_mld(phl_info, wrole, mac_addr, type);
}
enum rtw_phl_status
rtw_phl_link_mld_stainfo(struct rtw_phl_mld_t *mld,
struct rtw_phl_stainfo_t *phl_sta)
{
enum rtw_phl_status pstatus = RTW_PHL_STATUS_SUCCESS;
struct rtw_wifi_role_t *wrole = mld->wrole;
if (mld->type == DEV_TYPE_INACTIVE) {
PHL_ERR("%s link fails: map->type = INACTIVE!\n", __func__);
pstatus = RTW_PHL_STATUS_FAILURE;
}
else if (mld->sta_num >= wrole->rlink_num) {
PHL_ERR("%s link fails: mld only support %d links!\n",
__func__, wrole->rlink_num);
pstatus = RTW_PHL_STATUS_FAILURE;
}
else if (mld->type == DEV_TYPE_LEGACY) {
if (mld->sta_num > 0) {
PHL_ERR("%s LEGACY already have a sta entry and overwrite it!\n",
__func__);
}
_phl_link_mld_stainfo(mld, phl_sta, RTW_RLINK_PRIMARY);
mld->sta_num = 1;
}
else {
/* AP MLD type: the first STA link would be the PRIMARY one */
if (rtw_phl_role_is_ap_category(wrole)) {
if (mld->sta_num < wrole->rlink_num) {
_phl_link_mld_stainfo(mld, phl_sta, mld->sta_num);
mld->sta_num++;
}
}
/* non-AP MLD type: TODO: change primary link? */
else if (rtw_phl_role_is_client_category(wrole)) {
if (mld->sta_num < wrole->rlink_num) {
_phl_link_mld_stainfo(mld, phl_sta, mld->sta_num);
mld->sta_num++;
}
}
}
return pstatus;
}
enum rtw_phl_status
rtw_phl_unlink_mld_stainfo(struct rtw_phl_mld_t *mld,
struct rtw_phl_stainfo_t *phl_sta)
{
enum rtw_phl_status pstatus = RTW_PHL_STATUS_SUCCESS;
u8 lidx;
u8 i;
for (lidx = 0; lidx < mld->sta_num; lidx++) {
if (phl_sta != mld->phl_sta[lidx])
continue;
for (i = lidx + 1; i < mld->sta_num; i++)
_phl_link_mld_stainfo(mld, mld->phl_sta[i], i - 1);
mld->phl_sta[i-1] = NULL;
mld->sta_num--;
break;
}
return pstatus;
}
struct rtw_phl_mld_t *
rtw_phl_get_mld_by_addr(void *phl,
struct rtw_wifi_role_t *wrole,
u8 *addr)
{
struct phl_info_t *phl_info = (struct phl_info_t *)phl;
// struct macid_ctl_t *macid_ctl = phl_to_mac_ctrl(phl_info);
struct rtw_phl_mld_t *mld = NULL;
/* TODO: broadcast ? */
mld = phl_mld_queue_search(phl_info, &wrole->assoc_mld_queue, addr);
return mld;
}
struct rtw_phl_mld_t *
rtw_phl_get_mld_self(void *phl, struct rtw_wifi_role_t *wrole)
{
struct phl_info_t *phl_info = (struct phl_info_t *)phl;
struct rtw_phl_mld_t *mld = NULL;
mld = phl_mld_queue_get_first(phl_info, &wrole->assoc_mld_queue);
if (mld == NULL)
PHL_ERR("%s mld == NULL\n", __func__);
return mld;
}
/*
* asoc_cap.tid2link_ul[TID 0] :
* +------------+------------+---+-----------+-----------+
* | LINK_ID 14 | LINK_ID 13 |...| LINK_ID 1 | LINK_ID 0 |
* +------------+------------+---+-----------+-----------+
*
* tid_ul_map:
* +-------+-------+---+-------+-------+
* | TID 7 | TID 6 |...| TID 1 | TID 0 |
* +-------+-------+---+-------+-------+
*
*/
void
phl_mld_link2tid(struct rtw_phl_stainfo_t *phl_sta)
{
struct rtw_phl_mld_t *mld = phl_sta->mld;
struct rtw_phl_stainfo_t *sta = NULL;
u8 tidx = 0, lidx = 0;
for (lidx = 0; lidx < mld->sta_num; lidx++) {
sta = mld->phl_sta[lidx];
sta->tid_ul_map = 0;
sta->tid_dl_map = 0;
for(tidx = 0; tidx < WMM_AC_TID_NUM ; tidx++) {
if(phl_sta->asoc_cap.tid2link_ul[tidx] & BIT(sta->link_id))
sta->tid_ul_map |= BIT(tidx);
if(phl_sta->asoc_cap.tid2link_dl[tidx] & BIT(sta->link_id))
sta->tid_dl_map |= BIT(tidx);
}
}
/* TODO:Update halmac api */
}
struct rtw_phl_stainfo_t *
rtw_phl_get_stainfo_by_mld(struct rtw_phl_mld_t *mld, u8 lidx)
{
struct rtw_phl_stainfo_t *phl_sta;
u8 i;
for (i = 0; i < mld->sta_num; i++) {
phl_sta = mld->phl_sta[i];
if (phl_sta && phl_sta->rlink->id == lidx)
return phl_sta;
}
return NULL;
}
/* MLD level setting */
void
rtw_phl_mld_apply_links(struct rtw_phl_stainfo_t *sta)
{
struct rtw_phl_stainfo_t *phl_sta = NULL;
u8 idx = 0;
for (idx = 0; idx < sta->mld->sta_num; idx++) {
phl_sta = sta->mld->phl_sta[idx];
if(phl_sta != sta) {
phl_sta->asoc_cap = sta->asoc_cap;
}
}
}
#ifdef CONFIG_CMD_DISP
static void _phl_set_fw_ul_fixinfo_done(void *drv_priv, u8 *buf, u32 buf_len,
enum rtw_phl_status status)
{
if (buf) {
_os_kmem_free(drv_priv, buf, buf_len);
buf = NULL;
PHL_INFO("%s.....\n", __func__);
}
}
#endif
enum rtw_phl_status
phl_set_fw_ul_fixinfo_hdl(struct phl_info_t *phl_info,
struct rtw_phl_ax_ul_fixinfo *ul_fixinfo)
{
enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE;
if (RTW_HAL_STATUS_SUCCESS ==
rtw_hal_set_fw_ul_fixinfo(phl_info->hal, ul_fixinfo))
pstatus = RTW_PHL_STATUS_SUCCESS;
return pstatus;
}
enum rtw_phl_status
rtw_phl_cmd_set_fw_ul_fixinfo(void *phl,
struct rtw_wifi_role_t *wifi_role,
struct rtw_phl_ax_ul_fixinfo *ul_fixinfo,
enum phl_cmd_type cmd_type,
u32 cmd_timeout)
{
struct phl_info_t *phl_info = (struct phl_info_t *)phl;
void *drv = wifi_role->phl_com->drv_priv;
enum rtw_phl_status psts = RTW_PHL_STATUS_FAILURE;
struct rtw_phl_ax_ul_fixinfo *param = NULL;
u32 param_len;
if (!ul_fixinfo)
goto _exit;
#ifdef CONFIG_CMD_DISP
if (cmd_type == PHL_CMD_DIRECTLY) {
psts = phl_set_fw_ul_fixinfo_hdl(phl_info, ul_fixinfo);
goto _exit;
}
param_len = sizeof(struct rtw_phl_ax_ul_fixinfo);
param = _os_kmem_alloc(drv, param_len);
if (param == NULL) {
PHL_ERR("%s: alloc param failed!\n", __func__);
goto _exit;
}
_os_mem_cpy(drv, param, ul_fixinfo, param_len);
psts = phl_cmd_enqueue(phl_info,
wifi_role->rlink[RTW_RLINK_PRIMARY].hw_band,
MSG_EVT_SET_UL_FIXINFO,
(u8 *)param,
param_len,
_phl_set_fw_ul_fixinfo_done,
cmd_type,
cmd_timeout);
if (is_cmd_failure(psts)) {
/* Send cmd success, but wait cmd fail*/
psts = RTW_PHL_STATUS_FAILURE;
} else if (psts != RTW_PHL_STATUS_SUCCESS) {
/* Send cmd fail */
psts = RTW_PHL_STATUS_FAILURE;
_os_kmem_free(drv, (u8 *)param, param_len);
}
#else
PHL_TRACE(COMP_PHL_DBG, _PHL_DEBUG_,"%s CMD_DISP not supported, call directly\n", __func__);
psts = phl_set_fw_ul_fixinfo_hdl(phl_info, ul_fixinfo);
#endif
_exit:
return psts;
}
#ifdef CONFIG_CMD_DISP
enum rtw_phl_status
phl_cmd_set_seciv_hdl(struct phl_info_t *phl_info, u8 *param)
{
struct rtw_phl_stainfo_t *sta = (struct rtw_phl_stainfo_t *)param;
return rtw_hal_set_dctrl_tbl_seciv((void *)phl_info->hal, sta, sta->sec_iv);
}
#endif
enum rtw_phl_status
rtw_phl_cmd_set_sta_seciv(void *phl,
struct rtw_wifi_role_t *wifi_role,
struct rtw_phl_stainfo_t *sta,
u64 sec_iv,
enum phl_cmd_type cmd_type,
u32 cmd_timeout)
{
enum rtw_phl_status sts = RTW_PHL_STATUS_FAILURE;
if (NULL == sta)
return RTW_PHL_STATUS_FAILURE;
sta->sec_iv = sec_iv;
#ifdef CONFIG_CMD_DISP
sts = phl_cmd_enqueue(phl,
wifi_role->rlink[RTW_RLINK_PRIMARY].hw_band,
MSG_EVT_SET_STA_SEC_IV,
(u8 *)sta,
0,
NULL,
cmd_type,
cmd_timeout);
if (is_cmd_failure(sts)) {
PHL_ERR("%s : cmd fail (0x%x)!\n", __func__, sts);
sts = RTW_PHL_STATUS_FAILURE;
} else {
sts = RTW_PHL_STATUS_SUCCESS;
}
return sts;
#else
PHL_ERR("%s : CMD_DISP not set for MSG_EVT_SET_STA_SEC_IV\n", __func__);
sts = phl_cmd_cfg_sec_iv_hdl(phl, sta);
return sts;
#endif
}
const char *rtw_phl_get_lstate_str(enum link_state lstate)
{
switch (lstate) {
/* config period */
case PHL_EN_LINK_START:
return "PHL_EN_LINK_START";
case PHL_EN_LINK_DONE:
return "PHL_EN_LINK_DONE";
case PHL_DIS_LINK_START:
return "PHL_DIS_LINK_START";
case PHL_DIS_LINK_DONE:
return "PHL_DIS_LINK_DONE";
case PHL_EN_DBCC_START:
return "PHL_EN_DBCC_START";
case PHL_EN_DBCC_DONE:
return "PHL_EN_DBCC_DONE";
case PHL_DIS_DBCC_START:
return "PHL_DIS_DBCC_START";
case PHL_DIS_DBCC_DONE:
return "PHL_DIS_DBCC_DONE";
/* state */
case PHL_LINK_STARTED:
return "PHL_LINK_STARTED";
case PHL_LINK_STOPPED:
return "PHL_LINK_STOPPED";
case PHL_ClIENT_JOINING:
return "PHL_ClIENT_JOINING";
case PHL_ClIENT_LEFT:
return "PHL_ClIENT_LEFT";
case PHL_LINK_UP_NOA:
return "PHL_LINK_UP_NOA";
case PHL_LINK_CHG_CH:
return "PHL_LINK_CHG_CH";
case PHL_LINK_UNKNOWN:
default:
return "PHL_LINK_UNKNOWN";
}
}