2025-05-10 21:58:58 +08:00

570 lines
16 KiB
C

/******************************************************************************
*
* Copyright(c) 2019 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 _HAL_WOW_C_
#include "hal_headers.h"
#ifdef CONFIG_WOWLAN
#define case_rsn(rsn) \
case MAC_AX_WOW_##rsn: return RTW_WOW_RSN_##rsn
u8 _trans_wake_rsn(u8 mac_rsn)
{
switch (mac_rsn) {
case_rsn(RX_PAIRWISEKEY);
case_rsn(RX_GTK);
case_rsn(RX_FOURWAY_HANDSHAKE);
case_rsn(RX_DISASSOC);
case_rsn(RX_DEAUTH);
case_rsn(RX_ARP_REQUEST);
case_rsn(RX_NS);
case_rsn(RX_EAPREQ_IDENTIFY);
case_rsn(FW_DECISION_DISCONNECT);
case_rsn(RX_MAGIC_PKT);
case_rsn(RX_UNICAST_PKT);
case_rsn(RX_PATTERN_PKT);
case_rsn(RTD3_SSID_MATCH);
case_rsn(RX_DATA_PKT);
case_rsn(RX_SSDP_MATCH);
case_rsn(RX_WSD_MATCH);
case_rsn(RX_SLP_MATCH);
case_rsn(RX_LLTD_MATCH);
case_rsn(RX_MDNS_MATCH);
case_rsn(RX_REALWOW_V2_WAKEUP_PKT);
case_rsn(RX_REALWOW_V2_ACK_LOST);
case_rsn(RX_REALWOW_V2_TX_KAPKT);
case_rsn(ENABLE_FAIL_DMA_IDLE);
case_rsn(ENABLE_FAIL_DMA_PAUSE);
case_rsn(RTIME_FAIL_DMA_IDLE);
case_rsn(RTIME_FAIL_DMA_PAUSE);
case_rsn(RX_SNMP_MISMATCHED_PKT);
case_rsn(RX_DESIGNATED_MAC_PKT);
case_rsn(NLO_SSID_MACH);
case_rsn(AP_OFFLOAD_WAKEUP);
case_rsn(DMAC_ERROR_OCCURRED);
case_rsn(EXCEPTION_OCCURRED);
case_rsn(L0_TO_L1_ERROR_OCCURRED);
case_rsn(ASSERT_OCCURRED);
case_rsn(L2_ERROR_OCCURRED);
case_rsn(WDT_TIMEOUT_WAKE);
case_rsn(RX_ACTION);
case_rsn(CLK_32K_UNLOCK);
case_rsn(CLK_32K_LOCK);
default:
return RTW_WOW_RSN_MAX;
}
}
#define MAX_POLLING_WOW_FW_STS_CNT 1000 /* 50ms */
static enum rtw_hal_status _hal_check_wow_fw_ready(void *hal, u8 func_en)
{
struct hal_info_t *hal_info = (struct hal_info_t *)hal;
enum rtw_hal_status hstats = RTW_HAL_STATUS_FAILURE;
void* drv_priv = hal_to_drvpriv(hal_info);
u8 fw_status = 0;
u32 poll_cnt = 0;
PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] %s : start polling... (func_en %d)\n", __func__, func_en);
/* polling fw status */
while (1) {
if (poll_cnt >= MAX_POLLING_WOW_FW_STS_CNT) {
PHL_ERR("%s polling fw status timeout !!!\n", __func__);
hstats = RTW_HAL_STATUS_FAILURE;
break;
}
hstats = rtw_hal_mac_get_wow_fw_status(hal_info, &fw_status,
func_en);
if (RTW_HAL_STATUS_SUCCESS != hstats) {
PHL_ERR("%s : status %u\n", __func__, fw_status);
break;
}
if (fw_status) {
hstats = RTW_HAL_STATUS_SUCCESS;
PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_,
"%s : polling count %u\n", __func__, poll_cnt);
break;
} else {
_os_delay_us(drv_priv, 50);
}
poll_cnt++;
}
PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] %s : finish polling... (func_en %d)\n", __func__, func_en);
return hstats;
}
enum rtw_hal_status rtw_hal_get_wake_rsn(void *hal, enum rtw_wow_wake_reason *wake_rsn, u8 *reset)
{
struct hal_info_t *hal_info = (struct hal_info_t *)hal;
enum rtw_hal_status hstats = RTW_HAL_STATUS_FAILURE;
u8 mac_rsn = 0;
hstats = rtw_hal_mac_get_wake_rsn(hal_info, &mac_rsn, reset);
PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "%s : wake rsn from mac in hex 0x%x (reset %d).\n",
__func__, mac_rsn, *reset);
if (RTW_HAL_STATUS_SUCCESS != hstats) {
PHL_ERR("%s : rtw_hal_mac_get_wake_rsn failed.\n", __func__);
} else {
*wake_rsn = (mac_rsn == 0) ? RTW_WOW_RSN_UNKNOWN : _trans_wake_rsn(mac_rsn) ;
PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "%s : wake reason %u.\n", __func__, *wake_rsn);
}
return hstats;
}
enum rtw_hal_status rtw_hal_cfg_wow_sleep(void *hal, u8 sleep)
{
struct hal_info_t *hal_info = (struct hal_info_t *)hal;
enum rtw_hal_status hstats = RTW_HAL_STATUS_FAILURE;
hstats = rtw_hal_mac_cfg_wow_sleep(hal_info, sleep);
if (RTW_HAL_STATUS_SUCCESS != hstats)
PHL_ERR("%s : sleep %u\n", __func__, sleep);
return hstats;
}
enum rtw_hal_status rtw_hal_get_wow_aoac_rpt(void *hal, struct rtw_aoac_report *aoac_info, u8 rx_ready)
{
struct hal_info_t *hal_info = (struct hal_info_t *)hal;
enum rtw_hal_status hstatus = RTW_HAL_STATUS_FAILURE;
FUNCIN();
/* get aoac report */
hstatus = rtw_hal_mac_get_aoac_rpt(hal_info, aoac_info, rx_ready);
if (RTW_HAL_STATUS_SUCCESS != hstatus)
PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] %s(): failed with status(%u)\n", __func__, hstatus);
FUNCOUT();
return hstatus;
}
enum rtw_hal_status rtw_hal_reset_pkt_ofld_state(void *hal)
{
struct hal_info_t *hal_info = (struct hal_info_t *)hal;
enum rtw_hal_status hstats = RTW_HAL_STATUS_FAILURE;
hstats = rtw_hal_mac_reset_pkt_ofld_state(hal_info);
if (RTW_HAL_STATUS_SUCCESS != hstats)
PHL_ERR("%s : failed \n", __func__);
return hstats;
}
#ifdef CONFIG_PCI_HCI
enum rtw_hal_status rtw_hal_wow_cfg_txdma(void *hal, u8 en)
{
struct hal_info_t *hal_info = (struct hal_info_t *)hal;
struct hal_trx_ops *trx_ops = hal_info->trx_ops;
PHL_TRACE(COMP_PHL_DBG, _PHL_DEBUG_, "%s : enable %d.\n", __func__, en);
trx_ops->cfg_wow_txdma(hal_info, en);
return RTW_HAL_STATUS_SUCCESS;
}
#endif
enum rtw_hal_status
rtw_hal_wow_cfg_nlo(void *hal, enum SCAN_OFLD_OP op, u16 mac_id,
u8 hw_band, u8 hw_port, struct rtw_nlo_info *cfg)
{
struct hal_info_t *hal_info = (struct hal_info_t *)hal;
enum rtw_hal_status hstatus = RTW_HAL_STATUS_FAILURE;
struct scan_ofld_info info = {0};
u64 tsf;
u32 tsf_h = 0, tsf_l = 0;
if (cfg->delay != 0) {
hstatus = rtw_hal_mac_get_tsf(hal, &hw_port, &tsf_h, &tsf_l);
if (hstatus == RTW_HAL_STATUS_SUCCESS) {
tsf = ((u64)tsf_h << 32) | tsf_l;
tsf += cfg->delay * 1000;
tsf_h = tsf >> 32;
tsf_l = (u32)tsf;
info.tsf_high = tsf_h;
info.tsf_low = tsf_l;
} else {
PHL_WARN("%s Getting TSF failed \n", __func__);
}
}
/* configure scan offload */
info.operation = op;
info.probe_req_pkt_id = cfg->probe_req_id;
info.period = cfg->period;
info.slow_period = cfg->slow_period;
info.cycle = cfg->cycle;
if (info.period == 0 && info.slow_period == 0) {
info.mode = SCAN_OFLD_MD_ONCE;
} else if (info.period == 0) {
info.mode = SCAN_OFLD_MD_SEEMLESS;
} else if (info.slow_period == 0) {
info.mode = SCAN_OFLD_MD_PD;
} else {
info.mode = SCAN_OFLD_MD_PD_SLOW;
}
hstatus = rtw_hal_mac_scan_ofld(hal_info, (u8)mac_id,
hw_band, hw_port, &info);
if (hstatus != RTW_HAL_STATUS_SUCCESS)
return hstatus;
return hstatus;
}
enum rtw_hal_status
rtw_hal_wow_cfg_nlo_chnl_list(void *hal, struct rtw_nlo_info *cfg)
{
struct hal_info_t *hal_info = (struct hal_info_t *)hal;
enum rtw_hal_status hstatus = RTW_HAL_STATUS_FAILURE;
u8 i = 0;
if (cfg->channel_num == 0)
return RTW_HAL_STATUS_FAILURE;
for (i = 0; i < cfg->channel_num; i++) {
/* set channel to mac */
hstatus = rtw_hal_mac_scan_ofld_add_ch(hal_info,
&cfg->channel_list[i],
i == (cfg->channel_num-1) ? true : false);
if (hstatus != RTW_HAL_STATUS_SUCCESS)
return hstatus;
}
return hstatus;
}
enum rtw_hal_status rtw_hal_wow_init(struct rtw_phl_com_t *phl_com, void *hal,
struct rtw_phl_stainfo_t *sta)
{
struct hal_info_t *hal_info = (struct hal_info_t *)hal;
enum rtw_hal_status hal_status = RTW_HAL_STATUS_FAILURE;
struct hal_ops_t *hal_ops = hal_get_ops(hal_info);
FUNCIN_WSTS(hal_status);
/* download wowlan fw and do related tasks needed after redownload fw */
hal_status = hal_ops->hal_wow_init(phl_com, hal_info, sta);
if (hal_status != RTW_HAL_STATUS_SUCCESS)
return hal_status;
PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "%s successfully done.\n", __func__);
FUNCOUT_WSTS(hal_status);
return hal_status;
}
enum rtw_hal_status rtw_hal_wow_deinit(struct rtw_phl_com_t *phl_com, void *hal,
struct rtw_phl_stainfo_t *sta)
{
struct hal_info_t *hal_info = (struct hal_info_t *)hal;
enum rtw_hal_status hal_status = RTW_HAL_STATUS_FAILURE;
struct hal_ops_t *hal_ops = hal_get_ops(hal_info);
FUNCIN_WSTS(hal_status);
/* download wowlan fw and do related tasks needed after redownload fw */
hal_status = hal_ops->hal_wow_deinit(phl_com, hal_info, sta);
if (hal_status != RTW_HAL_STATUS_SUCCESS)
return hal_status;
PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "%s successfully done.\n", __func__);
FUNCOUT_WSTS(hal_status);
return hal_status;
}
enum rtw_hal_status
rtw_hal_wow_func_en(struct rtw_phl_com_t *phl_com, void *hal, u16 macid,
struct rtw_hal_wow_cfg *cfg)
{
struct hal_info_t *hal_info = (struct hal_info_t *)hal;
enum rtw_hal_status hstatus = RTW_HAL_STATUS_FAILURE;
FUNCIN();
do {
/* config wow cam : pattern match */
#ifndef RTW_WKARD_WOW_SKIP_WOW_CAM_CONFIG
hstatus = rtw_hal_mac_cfg_wow_cam(hal_info, macid, true, cfg->pattern_match_info);
if (RTW_HAL_STATUS_SUCCESS != hstatus)
break;
#endif
/* gtk offload */
if (cfg->gtk_ofld_cfg->gtk_en) {
hstatus = rtw_hal_mac_cfg_gtk_ofld(hal_info, macid, true,
cfg->gtk_ofld_cfg);
if (RTW_HAL_STATUS_SUCCESS != hstatus)
break;
}
/* arp offload */
if (cfg->arp_ofld_cfg->arp_en) {
hstatus = rtw_hal_mac_cfg_arp_ofld(hal_info, macid, true,
cfg->arp_ofld_cfg);
if (RTW_HAL_STATUS_SUCCESS != hstatus)
break;
}
/* ndp offload */
if (cfg->ndp_ofld_cfg->ndp_en) {
hstatus = rtw_hal_mac_cfg_ndp_ofld(hal_info, macid, true,
cfg->ndp_ofld_cfg);
if (RTW_HAL_STATUS_SUCCESS != hstatus)
break;
}
/* config keep alive */
if (cfg->keep_alive_cfg->keep_alive_en) {
hstatus = rtw_hal_mac_cfg_keep_alive(hal_info, macid, true,
cfg->keep_alive_cfg);
if (RTW_HAL_STATUS_SUCCESS != hstatus)
break;
}
/* config disconnect detection */
if (cfg->disc_det_cfg->disc_det_en) {
hstatus = rtw_hal_mac_cfg_disc_dec(hal_info, macid, true,
cfg->disc_det_cfg);
if (RTW_HAL_STATUS_SUCCESS != hstatus)
break;
}
/* realwow offload */
if (cfg->realwow_cfg->realwow_en) {
hstatus = rtw_hal_mac_cfg_realwow(hal_info, macid, true,
cfg->realwow_cfg);
if (RTW_HAL_STATUS_SUCCESS != hstatus)
break;
}
/* config gpio */
if (cfg->wow_gpio->d2h_gpio_info.dev2hst_gpio_en) {
hstatus = rtw_hal_mac_cfg_dev2hst_gpio(hal_info, true, cfg->wow_gpio);
if (RTW_HAL_STATUS_SUCCESS != hstatus)
break;
}
/* network list offload */
if (cfg->nlo_cfg->nlo_en) {
hstatus = rtw_hal_mac_cfg_nlo(hal_info, macid, true, cfg->nlo_cfg);
if (RTW_HAL_STATUS_SUCCESS != hstatus)
break;
}
} while(0);
PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] %s status(%u).\n", __func__, hstatus);
FUNCOUT();
return hstatus;
}
enum rtw_hal_status
rtw_hal_wow_func_dis(struct rtw_phl_com_t *phl_com, void *hal, u16 macid,
struct rtw_hal_wow_cfg *cfg)
{
struct hal_info_t *hal_info = (struct hal_info_t *)hal;
enum rtw_hal_status hstatus = RTW_HAL_STATUS_FAILURE;
FUNCIN();
/* cancel pattern match */
#ifndef RTW_WKARD_WOW_SKIP_WOW_CAM_CONFIG
hstatus = rtw_hal_mac_cfg_wow_cam(hal_info, macid, false, NULL);
if (RTW_HAL_STATUS_SUCCESS != hstatus)
PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] rtw_hal_mac_cfg_wow_cam failed \n");
#endif
/* cancel gtk offload */
if (cfg->gtk_ofld_cfg->gtk_en) {
hstatus = rtw_hal_mac_cfg_gtk_ofld(hal_info, macid, false, NULL);
if (RTW_HAL_STATUS_SUCCESS != hstatus)
PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] rtw_hal_mac_cfg_gtk_ofld failed \n");
}
/* cancel arp offload */
if (cfg->arp_ofld_cfg->arp_en) {
hstatus = rtw_hal_mac_cfg_arp_ofld(hal_info, macid, false, NULL);
if (RTW_HAL_STATUS_SUCCESS != hstatus)
PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] rtw_hal_mac_cfg_arp_ofld failed \n");
}
/* cancel ndp offload */
if (cfg->ndp_ofld_cfg->ndp_en) {
hstatus = rtw_hal_mac_cfg_ndp_ofld(hal_info, macid, false, NULL);
if (RTW_HAL_STATUS_SUCCESS != hstatus)
PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] rtw_hal_mac_cfg_ndp_ofld failed \n");
}
/* disable keep alive */
if (cfg->keep_alive_cfg->keep_alive_en) {
hstatus = rtw_hal_mac_cfg_keep_alive(hal_info, macid, false, NULL);
if (RTW_HAL_STATUS_SUCCESS != hstatus)
PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] rtw_hal_mac_cfg_keep_alive failed \n");
}
/* disable disconect detection */
if (cfg->disc_det_cfg->disc_det_en) {
hstatus = rtw_hal_mac_cfg_disc_dec(hal_info, macid, false, NULL);
if (RTW_HAL_STATUS_SUCCESS != hstatus)
PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] rtw_hal_mac_cfg_disc_dec failed \n");
}
/* realwow offload */
if (cfg->realwow_cfg->realwow_en) {
hstatus = rtw_hal_mac_cfg_realwow(hal_info, macid, false, NULL);
if (RTW_HAL_STATUS_SUCCESS != hstatus)
PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] rtw_hal_mac_cfg_realwow_ofld failed \n");
}
/* config gpio */
if (cfg->wow_gpio->d2h_gpio_info.dev2hst_gpio_en) {
hstatus = rtw_hal_mac_cfg_dev2hst_gpio(hal_info, false, NULL);
if (RTW_HAL_STATUS_SUCCESS != hstatus)
PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] rtw_hal_mac_cfg_dev2hst_gpio failed \n");
}
/* nlo */
if (cfg->nlo_cfg->nlo_en) {
hstatus = rtw_hal_mac_cfg_nlo(hal_info, macid, false, NULL);
if (RTW_HAL_STATUS_SUCCESS != hstatus)
PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] rtw_hal_mac_cfg_nlo failed \n");
}
PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] %s status(%u).\n", __func__, hstatus);
FUNCOUT();
return RTW_HAL_STATUS_SUCCESS;
}
enum rtw_hal_status rtw_hal_wow_func_start(struct rtw_phl_com_t *phl_com, void *hal, u16 macid, struct rtw_hal_wow_cfg *cfg)
{
struct hal_info_t *hal_info = (struct hal_info_t *)hal;
enum rtw_hal_status hstatus = RTW_HAL_STATUS_FAILURE;
do {
hstatus = rtw_hal_mac_cfg_wow_wake(hal_info, macid, true, cfg->wow_wake_cfg);
if (RTW_HAL_STATUS_SUCCESS != hstatus)
break;
/* poll fw status */
hstatus = _hal_check_wow_fw_ready(hal_info, 1);
if (RTW_HAL_STATUS_SUCCESS != hstatus)
break;
} while(0);
PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] %s status(%u).\n", __func__, hstatus);
return hstatus;
}
enum rtw_hal_status rtw_hal_wow_func_stop(struct rtw_phl_com_t *phl_com, void *hal, u16 macid)
{
struct hal_info_t *hal_info = (struct hal_info_t *)hal;
enum rtw_hal_status hstatus = RTW_HAL_STATUS_FAILURE;
/* config wow ctrl */
hstatus = rtw_hal_mac_cfg_wow_wake(hal_info, macid, false, NULL);
if (RTW_HAL_STATUS_SUCCESS != hstatus)
PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] rtw_hal_mac_cfg_wow_wake failed \n");
hstatus = _hal_check_wow_fw_ready(hal_info, 0);
if (RTW_HAL_STATUS_SUCCESS != hstatus)
PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] _hal_poll_wow_fw_status failed \n");
PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] %s status(%u).\n", __func__, hstatus);
return hstatus;
}
enum rtw_hal_status rtw_hal_set_wowlan(struct rtw_phl_com_t *phl_com, void *hal, u8 enter)
{
struct hal_info_t *hal_info = (struct hal_info_t *)hal;
enum rtw_hal_status hal_status = RTW_HAL_STATUS_FAILURE;
hal_status = rtw_hal_mac_set_wowlan(hal_info, enter);
PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "%s : status(%u).\n", __func__, hal_status);
return hal_status;
}
static enum rtw_hal_status _wow_chk_txq_empty(struct hal_info_t *hal_info, u8 *empty)
{
enum rtw_hal_status hal_status = RTW_HAL_STATUS_FAILURE;
hal_status = rtw_hal_mac_wow_chk_txq_empty(hal_info, empty);
PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "%s : status(%u).\n", __func__, hal_status);
return hal_status;
}
static enum rtw_hal_status _wow_wde_drop(struct hal_info_t *hal_info, u8 band)
{
enum rtw_hal_status hal_status = RTW_HAL_STATUS_FAILURE;
hal_status = rtw_hal_mac_wow_wde_drop(hal_info, band);
PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "%s : status(%u).\n", __func__, hal_status);
return hal_status;
}
#define MAX_WOW_DROP_HWTX_TRYCNT 3
enum rtw_hal_status rtw_hal_wow_drop_tx(void *hal, u8 band)
{
enum rtw_hal_status hal_status = RTW_HAL_STATUS_FAILURE;
struct hal_info_t *hal_info = (struct hal_info_t *)hal;
u8 empty = 0;
u8 i = 0;
for (i = 0; i < MAX_WOW_DROP_HWTX_TRYCNT; i++) {
PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] %s : chk/drop for the %d time\n", __func__, i + 1);
if (RTW_HAL_STATUS_SUCCESS != _wow_chk_txq_empty(hal_info, &empty))
PHL_WARN("[wow] _wow_chk_txq_empty failed.\n");
/* force drop wde if txq is not empty */
if (!empty)
_wow_wde_drop(hal_info, band);
else
break;
}
if (!empty) {
PHL_WARN("[wow] %s : chk/drop fail!\n", __func__);
} else {
PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] %s : chk/drop ok.\n", __func__);
hal_status = RTW_HAL_STATUS_SUCCESS;
}
return hal_status;
}
#endif /* CONFIG_WOWLAN */