/******************************************************************************
 *
 * 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 _PHL_WOW_C_
#include "phl_headers.h"

enum rtw_phl_status phl_wow_mdl_init(struct phl_info_t* phl_info)
{
	enum rtw_phl_status pstatus = RTW_PHL_STATUS_SUCCESS;
#ifdef CONFIG_WOWLAN
	struct phl_wow_info *info = phl_to_wow_info(phl_info);

	info->phl_info = phl_info;
	_os_spinlock_init(phl_to_drvpriv(phl_info), &info->wow_lock);
#endif /* CONFIG_WOWLAN */

	return pstatus;
}

void phl_wow_mdl_deinit(struct phl_info_t* phl_info)
{
#ifdef CONFIG_WOWLAN
	struct phl_wow_info *info = phl_to_wow_info(phl_info);

	_os_spinlock_free(phl_to_drvpriv(phl_info), &info->wow_lock);
#endif /* CONFIG_WOWLAN */
}

#ifdef CONFIG_WOWLAN

/* TO-DO: Confirm the enum strcut of the algo */
u8 _phl_query_iv_len(u8 algo)
{
	u8 len = 0;

	switch(algo) {
	case RTW_ENC_WEP40:
		len = 4;
		break;
	case RTW_ENC_TKIP:
	case RTW_ENC_CCMP:
	case RTW_ENC_GCMP256:
		len = 8;
		break;
	default:
		len = 0;
		break;
	}

	return len;
}

u8 _phl_query_key_desc_ver(struct phl_wow_info *wow_info, u8 algo)
{
	u8 akm_type = wow_info->gtk_ofld_info.akmtype_byte3;

	if (algo == RTW_ENC_TKIP)
		return EAPOLKEY_KEYDESC_VER_1;

	if (akm_type == 1 || akm_type == 2) {
		return EAPOLKEY_KEYDESC_VER_2;
	} else if (akm_type > 2 && akm_type < 7) {
		return EAPOLKEY_KEYDESC_VER_3;
	} else {
		return 0;
	}
}

static void _phl_cfg_pkt_ofld_null_info(
	struct phl_wow_info *wow_info,
	struct rtw_phl_stainfo_t *phl_sta,
	struct rtw_pkt_ofld_null_info *null_info)
{
	void *drv_priv = phl_to_drvpriv(wow_info->phl_info);

	_os_mem_cpy(drv_priv, &(null_info->a1[0]), &(phl_sta->mac_addr[0]), MAC_ADDRESS_LENGTH);
	_os_mem_cpy(drv_priv, &(null_info->a2[0]), &(phl_sta->wrole->mac_addr[0]), MAC_ADDRESS_LENGTH);
	_os_mem_cpy(drv_priv, &(null_info->a3[0]), &(phl_sta->mac_addr[0]), MAC_ADDRESS_LENGTH);

}

static void _phl_cfg_pkt_ofld_arp_rsp_info(struct phl_wow_info *wow_info, struct rtw_phl_stainfo_t *phl_sta,
						struct rtw_pkt_ofld_arp_rsp_info *arp_rsp_info)
{
	void *drv_priv = phl_to_drvpriv(wow_info->phl_info);
	u8 pairwise_algo = get_wow_pairwise_algo_type(wow_info);

	_os_mem_cpy(drv_priv, &(arp_rsp_info->a1[0]), &(phl_sta->mac_addr[0]), MAC_ADDRESS_LENGTH);
	_os_mem_cpy(drv_priv, &(arp_rsp_info->a2[0]), &(phl_sta->wrole->mac_addr[0]), MAC_ADDRESS_LENGTH);
	_os_mem_cpy(drv_priv, &(arp_rsp_info->a3[0]), &(phl_sta->mac_addr[0]), MAC_ADDRESS_LENGTH);
	_os_mem_cpy(drv_priv, &(arp_rsp_info->host_ipv4_addr[0]),
		&(wow_info->arp_ofld_info.arp_ofld_content.host_ipv4_addr[0]),
		IPV4_ADDRESS_LENGTH);
	_os_mem_cpy(drv_priv, &(arp_rsp_info->remote_ipv4_addr[0]),
		&(wow_info->arp_ofld_info.arp_ofld_content.remote_ipv4_addr[0]),
		IPV4_ADDRESS_LENGTH);

	arp_rsp_info->sec_hdr = _phl_query_iv_len(pairwise_algo);
}

static void _phl_cfg_pkt_ofld_na_info(struct phl_wow_info *wow_info, struct rtw_phl_stainfo_t *phl_sta,
					struct rtw_pkt_ofld_na_info *na_info)
{
	void *drv_priv = phl_to_drvpriv(wow_info->phl_info);
	u8 pairwise_algo = get_wow_pairwise_algo_type(wow_info);

	_os_mem_cpy(drv_priv, &(na_info->a1[0]), &(phl_sta->mac_addr[0]), MAC_ADDRESS_LENGTH);
	_os_mem_cpy(drv_priv, &(na_info->a2[0]), &(phl_sta->wrole->mac_addr[0]), MAC_ADDRESS_LENGTH);
	_os_mem_cpy(drv_priv, &(na_info->a3[0]), &(phl_sta->mac_addr[0]), MAC_ADDRESS_LENGTH);

	na_info->sec_hdr = _phl_query_iv_len(pairwise_algo);

}

static void _phl_cfg_pkt_ofld_eapol_key_info(
	struct phl_wow_info *wow_info,
	struct rtw_phl_stainfo_t *phl_sta,
	struct rtw_pkt_ofld_eapol_key_info *eapol_key_info)
{
	void *drv_priv = phl_to_drvpriv(wow_info->phl_info);
	struct rtw_gtk_ofld_info *gtk_ofld_info = &wow_info->gtk_ofld_info;

	u8 pairwise_algo = get_wow_pairwise_algo_type(wow_info);

	_os_mem_cpy(drv_priv, &(eapol_key_info->a1[0]), &(phl_sta->mac_addr[0]),
		MAC_ADDRESS_LENGTH);

	_os_mem_cpy(drv_priv, &(eapol_key_info->a2[0]), &(phl_sta->wrole->mac_addr[0]),
			MAC_ADDRESS_LENGTH);

	_os_mem_cpy(drv_priv, &(eapol_key_info->a3[0]), &(phl_sta->mac_addr[0]),
			MAC_ADDRESS_LENGTH);

	eapol_key_info->sec_hdr = _phl_query_iv_len(pairwise_algo);
	eapol_key_info->key_desc_ver = _phl_query_key_desc_ver(wow_info, pairwise_algo);
	_os_mem_cpy(drv_priv, eapol_key_info->replay_cnt,
				gtk_ofld_info->gtk_ofld_content.replay_cnt, 8);
}

static void _phl_cfg_pkt_ofld_sa_query_info(
	struct phl_wow_info *wow_info,
	struct rtw_phl_stainfo_t *phl_sta,
	struct rtw_pkt_ofld_sa_query_info *sa_query_info)
{
	void *drv_priv = phl_to_drvpriv(wow_info->phl_info);
	u8 pairwise_algo = get_wow_pairwise_algo_type(wow_info);

	_os_mem_cpy(drv_priv, &(sa_query_info->a1[0]), &(phl_sta->mac_addr[0]),
			MAC_ADDRESS_LENGTH);

	_os_mem_cpy(drv_priv, &(sa_query_info->a2[0]), &(phl_sta->wrole->mac_addr[0]),
			MAC_ADDRESS_LENGTH);

	_os_mem_cpy(drv_priv, &(sa_query_info->a3[0]), &(phl_sta->mac_addr[0]),
			MAC_ADDRESS_LENGTH);

	sa_query_info->sec_hdr = _phl_query_iv_len(pairwise_algo);
}

static void _phl_cfg_pkt_ofld_realwow_kapkt_info(
	struct phl_wow_info *wow_info,
	struct rtw_phl_stainfo_t *phl_sta,
	struct rtw_pkt_ofld_realwow_kapkt_info *kapkt_info)
{
	void *drv_priv = phl_to_drvpriv(wow_info->phl_info);

	_os_mem_cpy(drv_priv, &(kapkt_info->keep_alive_pkt_ptrn[0]),
		&(wow_info->realwow_info.realwow_ofld_content.keep_alive_pkt_ptrn[0]),
		wow_info->realwow_info.realwow_ofld_content.keep_alive_pkt_size);

	kapkt_info->keep_alive_pkt_size =
				wow_info->realwow_info.realwow_ofld_content.keep_alive_pkt_size;
}

static void _phl_cfg_pkt_ofld_realwow_ack_info(
	struct phl_wow_info *wow_info,
	struct rtw_pkt_ofld_realwow_ack_info *ack_info)
{
	void *drv_priv = phl_to_drvpriv(wow_info->phl_info);

	_os_mem_cpy(drv_priv, &(ack_info->ack_ptrn[0]),
		&(wow_info->realwow_info.realwow_ofld_content.ack_ptrn[0]),
		wow_info->realwow_info.realwow_ofld_content.ack_ptrn_size);

	ack_info->ack_ptrn_size = wow_info->realwow_info.realwow_ofld_content.ack_ptrn_size;
}

static void _phl_cfg_pkt_ofld_realwow_wp_info(
	struct phl_wow_info *wow_info,
	struct rtw_pkt_ofld_realwow_wp_info *wp_info)
{
	void *drv_priv = phl_to_drvpriv(wow_info->phl_info);

	_os_mem_cpy(drv_priv, &(wp_info->wakeup_ptrn[0]),
		&(wow_info->realwow_info.realwow_ofld_content.wakeup_ptrn[0]),
		wow_info->realwow_info.realwow_ofld_content.wakeup_ptrn_size);

	wp_info->wakeup_ptrn_size = wow_info->realwow_info.realwow_ofld_content.wakeup_ptrn_size;
}

enum rtw_phl_status rtw_phl_cfg_keep_alive_info(void *phl, struct rtw_keep_alive_info *info)
{
	enum rtw_phl_status phl_status = RTW_PHL_STATUS_SUCCESS;
	struct phl_info_t *phl_info = (struct phl_info_t *)phl;
	struct phl_wow_info *wow_info = phl_to_wow_info(phl_info);
	struct rtw_keep_alive_info *keep_alive_info = &wow_info->keep_alive_info;

	FUNCIN();

	keep_alive_info->keep_alive_en = info->keep_alive_en;
	keep_alive_info->keep_alive_period = info->keep_alive_period;

	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] keep_alive_en %d\n", keep_alive_info->keep_alive_en);
	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] keep_alive_period %d\n", keep_alive_info->keep_alive_period);

	return phl_status;
}

enum rtw_phl_status rtw_phl_cfg_disc_det_info(void *phl, struct rtw_disc_det_info *info)
{
	enum rtw_phl_status phl_status = RTW_PHL_STATUS_SUCCESS;
	struct phl_info_t *phl_info = (struct phl_info_t *)phl;
	struct phl_wow_info *wow_info = phl_to_wow_info(phl_info);
	struct rtw_disc_det_info *disc_det_info = &wow_info->disc_det_info;

	FUNCIN();

	disc_det_info->disc_det_en = info->disc_det_en;
	disc_det_info->disc_wake_en = info->disc_wake_en;
	disc_det_info->try_pkt_count = info->try_pkt_count;
	disc_det_info->check_period = info->check_period;
	disc_det_info->cnt_bcn_lost_en = info->cnt_bcn_lost_en;
	disc_det_info->cnt_bcn_lost_limit = info->cnt_bcn_lost_limit;

	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] disc_det_en %d\n", disc_det_info->disc_det_en);
	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] disc_wake_en %d\n", disc_det_info->disc_wake_en);
	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] try_pkt_count %d\n", disc_det_info->try_pkt_count);
	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] check_period %d\n", disc_det_info->check_period);
	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] cnt_bcn_lost_en %d\n", disc_det_info->cnt_bcn_lost_en);
	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] cnt_bcn_lost_limit %d\n", disc_det_info->cnt_bcn_lost_limit);

	return phl_status;
}

enum rtw_phl_status rtw_phl_cfg_nlo_info(void *phl, struct rtw_nlo_info *info)
{
	enum rtw_phl_status phl_status = RTW_PHL_STATUS_SUCCESS;
	struct phl_info_t *phl_info = (struct phl_info_t *)phl;
	struct phl_wow_info *wow_info = phl_to_wow_info(phl_info);
	struct rtw_nlo_info *nlo_info = &wow_info->nlo_info;

	nlo_info->nlo_en = info->nlo_en;

	return phl_status;
}

void rtw_phl_cfg_arp_ofld_info(void *phl, struct rtw_arp_ofld_info *info)
{
	struct phl_info_t *phl_info = (struct phl_info_t *)phl;
	struct phl_wow_info *wow_info = phl_to_wow_info(phl_info);
	struct rtw_arp_ofld_info *arp_ofld_info = &wow_info->arp_ofld_info;
	void *drv_priv = phl_to_drvpriv(phl_info);

	FUNCIN();


	arp_ofld_info->arp_en = info->arp_en;

	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] arp_en %u\n",
			arp_ofld_info->arp_en);

	/* If not enabled, the following actions are not necessary */
	if (false == arp_ofld_info->arp_en)
		return;

	arp_ofld_info->arp_action = info->arp_action;

	_os_mem_cpy(drv_priv,
		&(arp_ofld_info->arp_ofld_content.remote_ipv4_addr[0]),
		&(info->arp_ofld_content.remote_ipv4_addr[0]),
		IPV4_ADDRESS_LENGTH);

	_os_mem_cpy(drv_priv,
		&(arp_ofld_info->arp_ofld_content.host_ipv4_addr[0]),
		&(info->arp_ofld_content.host_ipv4_addr[0]),
		IPV4_ADDRESS_LENGTH);

	_os_mem_cpy(drv_priv,
		&(arp_ofld_info->arp_ofld_content.mac_addr[0]),
		&(info->arp_ofld_content.mac_addr[0]),
		MAC_ADDRESS_LENGTH);

	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] arp_action %u\n",
			arp_ofld_info->arp_action);

	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] arp_remote_ipv4  %u:%u:%u:%u\n",
			arp_ofld_info->arp_ofld_content.remote_ipv4_addr[0],
			arp_ofld_info->arp_ofld_content.remote_ipv4_addr[1],
			arp_ofld_info->arp_ofld_content.remote_ipv4_addr[2],
			arp_ofld_info->arp_ofld_content.remote_ipv4_addr[3]);

	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] arp_host_ipv4  %u:%u:%u:%u\n",
			arp_ofld_info->arp_ofld_content.host_ipv4_addr[0],
			arp_ofld_info->arp_ofld_content.host_ipv4_addr[1],
			arp_ofld_info->arp_ofld_content.host_ipv4_addr[2],
			arp_ofld_info->arp_ofld_content.host_ipv4_addr[3]);

	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] arp_mac_addr  %02x:%02x:%02x:%02x:%02x:%02x \n",
			arp_ofld_info->arp_ofld_content.mac_addr[0],
			arp_ofld_info->arp_ofld_content.mac_addr[1],
			arp_ofld_info->arp_ofld_content.mac_addr[2],
			arp_ofld_info->arp_ofld_content.mac_addr[3],
			arp_ofld_info->arp_ofld_content.mac_addr[4],
			arp_ofld_info->arp_ofld_content.mac_addr[5]);

}


void rtw_phl_cfg_ndp_ofld_info(void *phl, struct rtw_ndp_ofld_info *info)
{
	struct phl_info_t *phl_info = (struct phl_info_t *)phl;
	struct phl_wow_info *wow_info = phl_to_wow_info(phl_info);
	struct rtw_ndp_ofld_info *ndp_ofld_info = &wow_info->ndp_ofld_info;
	struct rtw_ndp_ofld_content *pcontent;
	void *drv_priv = phl_to_drvpriv(phl_info);
	u8 idx = 0;

	FUNCIN();

	ndp_ofld_info->ndp_en = info->ndp_en;

	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] ndp_en %u\n",
			ndp_ofld_info->ndp_en);

	/* If not enabled, the following actions are not necessary */
	if (false == ndp_ofld_info->ndp_en)
		return;

	for (idx = 0; idx < 2; idx++) {

		pcontent = &ndp_ofld_info->ndp_ofld_content[idx];
		pcontent->ndp_en = info->ndp_ofld_content[idx].ndp_en;

		pcontent->chk_remote_ip =
			info->ndp_ofld_content[idx].chk_remote_ip;
		pcontent->num_target_ip =
			info->ndp_ofld_content[idx].num_target_ip;

		_os_mem_cpy(drv_priv, &(pcontent->mac_addr[0]),
			&(info->ndp_ofld_content[idx].mac_addr[0]),
			MAC_ADDRESS_LENGTH);
		_os_mem_cpy(drv_priv, &(pcontent->remote_ipv6_addr[0]),
			&(info->ndp_ofld_content[idx].remote_ipv6_addr[0]),
			IPV6_ADDRESS_LENGTH);
		_os_mem_cpy(drv_priv, &(pcontent->target_ipv6_addr[0][0]),
			&(info->ndp_ofld_content[idx].target_ipv6_addr[0][0]),
			IPV6_ADDRESS_LENGTH*2);

		PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] ndp_chk_remote_ip %u\n",
			pcontent->chk_remote_ip);

		PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] ndp_num_target_ip %u\n",
			pcontent->num_target_ip);

		PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] ndp_mac_addr  %02x:%02x:%02x:%02x:%02x:%02x \n",
			pcontent->mac_addr[0],
			pcontent->mac_addr[1],
			pcontent->mac_addr[2],
			pcontent->mac_addr[3],
			pcontent->mac_addr[4],
			pcontent->mac_addr[5]);

		PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_,
			"[wow] ndp_remote_ipv6  %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x \n",
			pcontent->remote_ipv6_addr[0],
			pcontent->remote_ipv6_addr[1],
			pcontent->remote_ipv6_addr[2],
			pcontent->remote_ipv6_addr[3],
			pcontent->remote_ipv6_addr[4],
			pcontent->remote_ipv6_addr[5],
			pcontent->remote_ipv6_addr[6],
			pcontent->remote_ipv6_addr[7],
			pcontent->remote_ipv6_addr[8],
			pcontent->remote_ipv6_addr[9],
			pcontent->remote_ipv6_addr[10],
			pcontent->remote_ipv6_addr[11],
			pcontent->remote_ipv6_addr[12],
			pcontent->remote_ipv6_addr[13],
			pcontent->remote_ipv6_addr[14],
			pcontent->remote_ipv6_addr[15]);

		PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_,
			"[wow] ndp_target_ipv6_addr  %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x \n",
			pcontent->target_ipv6_addr[0][0],
			pcontent->target_ipv6_addr[0][1],
			pcontent->target_ipv6_addr[0][2],
			pcontent->target_ipv6_addr[0][3],
			pcontent->target_ipv6_addr[0][4],
			pcontent->target_ipv6_addr[0][5],
			pcontent->target_ipv6_addr[0][6],
			pcontent->target_ipv6_addr[0][7],
			pcontent->target_ipv6_addr[0][8],
			pcontent->target_ipv6_addr[0][9],
			pcontent->target_ipv6_addr[0][10],
			pcontent->target_ipv6_addr[0][11],
			pcontent->target_ipv6_addr[0][12],
			pcontent->target_ipv6_addr[0][13],
			pcontent->target_ipv6_addr[0][14],
			pcontent->target_ipv6_addr[0][15]);

		PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_,
			"[wow] ndp_target_ipv6_addr  %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x \n",
			pcontent->target_ipv6_addr[1][0],
			pcontent->target_ipv6_addr[1][1],
			pcontent->target_ipv6_addr[1][2],
			pcontent->target_ipv6_addr[1][3],
			pcontent->target_ipv6_addr[1][4],
			pcontent->target_ipv6_addr[1][5],
			pcontent->target_ipv6_addr[1][6],
			pcontent->target_ipv6_addr[1][7],
			pcontent->target_ipv6_addr[1][8],
			pcontent->target_ipv6_addr[1][9],
			pcontent->target_ipv6_addr[1][10],
			pcontent->target_ipv6_addr[1][11],
			pcontent->target_ipv6_addr[1][12],
			pcontent->target_ipv6_addr[1][13],
			pcontent->target_ipv6_addr[1][14],
			pcontent->target_ipv6_addr[1][15]);

	}

}

u8 _phl_query_free_cam_entry_idx(struct rtw_pattern_match_info *pattern_match_info)
{
	struct rtw_wowcam_upd_info *wowcam_info = pattern_match_info->wowcam_info;
	u8 i = 0;

	for (i = 0; i < MAX_WOW_CAM_NUM; ++i)
		if (wowcam_info[i].valid == 0)
			break;

	return i;
}

u16 _phl_cal_crc16(u8 data, u16 crc)
{
	u8 shift_in, data_bit;
	u8 crc_bit4, crc_bit11, crc_bit15;
	u16 crc_result;
	int index;

	for (index = 0; index < 8; index++) {
		crc_bit15 = ((crc & BIT15) ? 1 : 0);
		data_bit = (data & (BIT0 << index) ? 1 : 0);
		shift_in = crc_bit15 ^ data_bit;
		/*printf("crc_bit15=%d, DataBit=%d, shift_in=%d\n",
		 * crc_bit15, data_bit, shift_in);*/

		crc_result = crc << 1;

		if (shift_in == 0)
			crc_result &= (~BIT0);
		else
			crc_result |= BIT0;
		/*printf("CRC =%x\n",CRC_Result);*/

		crc_bit11 = ((crc & BIT11) ? 1 : 0) ^ shift_in;

		if (crc_bit11 == 0)
			crc_result &= (~BIT12);
		else
			crc_result |= BIT12;

		/*printf("bit12 CRC =%x\n",CRC_Result);*/

		crc_bit4 = ((crc & BIT4) ? 1 : 0) ^ shift_in;

		if (crc_bit4 == 0)
			crc_result &= (~BIT5);
		else
			crc_result |= BIT5;

		/* printf("bit5 CRC =%x\n",CRC_Result); */
		crc = crc_result;
	}
	return crc;
}

u16 _phl_cal_wow_ptrn_crc(u8 *pattern, u32 length)
{
	u16 crc = 0xffff;
	u32 i;

	for (i = 0; i < length; i++)
		crc = _phl_cal_crc16(pattern[i], crc);
	crc = ~crc;

	return crc;
}
/*
 * To get the wake up pattern from the mask.
 * We do not count first 12 bits which means
 * DA[6] and SA[6] in the pattern to match HW design.
 */
u32 _phl_get_ptrn_after_mask(struct rtw_wowcam_upd_info *wowcam_info, u8 *ptrn_after_mask)
{
	u32 ptrn_len_after_mask = 0;
	u32 i;
	u8 da_sa_offset = 12;

	for (i = da_sa_offset; i < wowcam_info->ptrn_len; i++) {
		if (wowcam_info->mask[i / 8] >> (i % 8) & 0x01) {
			ptrn_after_mask[ptrn_len_after_mask] = wowcam_info->ptrn[i];
			ptrn_len_after_mask++;
		}
	}

	return ptrn_len_after_mask;
}

/*
 * translate mask from os to mask for hw
 *
 * pattern from OS uses 'ethenet frame', like this:
 *	|    6   |    6   |   2  |     20    |  Variable  |  4  |
 *	|--------+--------+------+-----------+------------+-----|
 *	|    802.3 Mac Header    | IP Header | TCP Packet | FCS |
 *	|   DA   |   SA   | Type |
 *
 * BUT, packet catched by our HW is in '802.11 frame', begin from LLC,
 *	|     24 or 30      |    6   |   2  |     20    |  Variable  |  4  |
 *	|-------------------+--------+------+-----------+------------+-----|
 *	| 802.11 MAC Header |       LLC     | IP Header | TCP Packet | FCS |
 *			    | Others | Type |
 *
 * Therefore, we need to translate mask_from_OS to mask_to_hw.
 * We should left-shift mask_from_os by 6 bits to omit 'DA',
 * to make it correspond to 'LLC' of mask_to_hw.
 * Our HW packet begins from LLC, mask_to_hw[5:0] is part of LLC,
 * but mask_from_os[5:0] is 'SA' after left-shift.
 * They just don't match, so we need to set first 5 bits to 0.
*/

void _phl_to_hw_wake_mask(struct rtw_wowcam_upd_info *wowcam_info)
{
	u8 mask_hw[MAX_WOW_PATTERN_SIZE_BYTE] = {0};
	u32 mask_len = _os_div_round_up(wowcam_info->ptrn_len, 8);
	u32 i;
	u8 sa_offset = 6;

	for (i = 0; i < mask_len - 1; i++) {
		mask_hw[i] = wowcam_info->mask[i] >> sa_offset;
		mask_hw[i] |= (wowcam_info->mask[i + 1] & 0x3F) << 2;
	}
	mask_hw[i] = (wowcam_info->mask[i] >> sa_offset) & 0x3F;
	mask_hw[0] &= 0xC0;

	for (i = 0; i < MAX_WOW_PATTERN_SIZE_DWORD; i++) {
		wowcam_info->wake_mask[i] = mask_hw[i * 4];
		wowcam_info->wake_mask[i] |= (mask_hw[i * 4 + 1] << 8);
		wowcam_info->wake_mask[i] |= (mask_hw[i * 4 + 2] << 16);
		wowcam_info->wake_mask[i] |= (mask_hw[i * 4 + 3] << 24);
	}
}

enum rtw_phl_status rtw_phl_remove_wow_ptrn_info(void *phl, u8 wowcam_id)
{
	enum rtw_phl_status phl_status = RTW_PHL_STATUS_FAILURE;
	struct phl_info_t *phl_info = (struct phl_info_t *)phl;
	struct phl_wow_info *wow_info = phl_to_wow_info(phl_info);
	struct rtw_pattern_match_info *pattern_match_info = &wow_info->pattern_match_info;
	struct rtw_wowcam_upd_info *wowcam_info = &(pattern_match_info->wowcam_info[wowcam_id]);

	if (wowcam_id < MAX_WOW_CAM_NUM) {
		wowcam_info->valid = 0;
		phl_status = RTW_PHL_STATUS_SUCCESS;
	} else {
		PHL_TRACE(COMP_PHL_WOW, _PHL_WARNING_, "[wow] %s(): Invalid wowcam id(%u), Fail.\n",
						__func__, wowcam_id);
		phl_status = RTW_PHL_STATUS_FAILURE;
	}

	return phl_status;
}

enum rtw_phl_status rtw_phl_add_wow_ptrn_info(void *phl, struct rtw_wowcam_upd_info *info, u8 *wowcam_id)
{
	enum rtw_phl_status phl_status = RTW_PHL_STATUS_FAILURE;
	struct phl_info_t *phl_info = (struct phl_info_t *)phl;
	struct phl_wow_info *wow_info = phl_to_wow_info(phl_info);
	struct rtw_pattern_match_info *pattern_match_info = &wow_info->pattern_match_info;
	struct rtw_wowcam_upd_info *wowcam_info = NULL;
	void *d = phl_to_drvpriv(phl_info);
	u8 ptrn_after_mask[MAX_WOW_PATTERN_SIZE_BIT] = {0};
	u32 ptrn_len_after_mask = 0;

	*wowcam_id = _phl_query_free_cam_entry_idx(pattern_match_info);

	if (*wowcam_id < MAX_WOW_CAM_NUM) {
		wowcam_info = &(pattern_match_info->wowcam_info[*wowcam_id]);

		_os_mem_set(d, wowcam_info, 0, sizeof(struct rtw_wowcam_upd_info));
		_os_mem_cpy(d, wowcam_info, info, sizeof(struct rtw_wowcam_upd_info));

		ptrn_len_after_mask = _phl_get_ptrn_after_mask(wowcam_info, ptrn_after_mask);
		wowcam_info->match_crc = _phl_cal_wow_ptrn_crc(ptrn_after_mask, ptrn_len_after_mask);

		_phl_to_hw_wake_mask(wowcam_info);

		/* fill in phl */
		wowcam_info->wow_cam_idx = *wowcam_id;
		wowcam_info->rw = 1;
		wowcam_info->is_negative_pattern_match = 0;
		wowcam_info->skip_mac_hdr = 1;
		wowcam_info->valid = 1;

		phl_status = RTW_PHL_STATUS_SUCCESS;
	} else {
		PHL_TRACE(COMP_PHL_WOW, _PHL_WARNING_, "[wow] no free cam entry can be used.\n");
		phl_status = RTW_PHL_STATUS_RESOURCE;
	}

	return phl_status;
}

enum rtw_phl_status rtw_phl_cfg_gtk_ofld_info(void *phl, struct rtw_gtk_ofld_info *info)
{
	enum rtw_phl_status phl_status = RTW_PHL_STATUS_SUCCESS;
	struct phl_info_t *phl_info = (struct phl_info_t *)phl;
	struct phl_wow_info *wow_info = phl_to_wow_info(phl_info);
	struct rtw_gtk_ofld_info *gtk_ofld_info = &wow_info->gtk_ofld_info;
	void *d = phl_to_drvpriv(phl_info);

	FUNCIN();

	if (info == NULL || gtk_ofld_info == NULL) {
		PHL_TRACE(COMP_PHL_WOW, _PHL_WARNING_, "[wow] %s(): some ptr is NULL\n", __func__);
		phl_status = RTW_PHL_STATUS_FAILURE;

	} else {
		_os_mem_set(d, gtk_ofld_info, 0, sizeof(struct rtw_gtk_ofld_info));

		PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] gtk_en(%u), continue to gtk_ofld.\n", info->gtk_en);

		if (info->gtk_en) {
			_os_mem_cpy(d, gtk_ofld_info, info, sizeof(struct rtw_gtk_ofld_info));

			PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] gtk_ofld_info:\n");
			PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] - gtk_en          = %u\n", gtk_ofld_info->gtk_en);
			PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] - tkip_en         = %u\n", gtk_ofld_info->tkip_en);
			PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] - ieee80211w_en   = %u\n", gtk_ofld_info->ieee80211w_en);
			PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] - pairwise_wakeup = %u\n", gtk_ofld_info->pairwise_wakeup);
			PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] - bip_sec_algo    = %u\n", gtk_ofld_info->bip_sec_algo);

			PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] gtk_ofld_content:\n");
			PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] - akmtype_byte3   = %u\n", gtk_ofld_info->akmtype_byte3);
			PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] - kck_len         = %u\n", gtk_ofld_info->gtk_ofld_content.kck_len);
			PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] - kek_len         = %u\n", gtk_ofld_info->gtk_ofld_content.kek_len);
			PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] - replay_cnt      = 0x%x%x\n",
						*((u32 *)(gtk_ofld_info->gtk_ofld_content.replay_cnt)+1),
						*((u32 *)(gtk_ofld_info->gtk_ofld_content.replay_cnt)));

			if(info->ieee80211w_en) {
				gtk_ofld_info->hw_11w_en = true;
				PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] - igtk_keyid      = 0x%x\n",
								*((u32 *)(gtk_ofld_info->gtk_ofld_content.igtk_keyid)));
				PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] - ipn             = 0x%x%x\n",
								*((u32 *)(gtk_ofld_info->gtk_ofld_content.ipn)+1),
								*((u32 *)(gtk_ofld_info->gtk_ofld_content.ipn)));
				PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] - igtk_len        = %u\n", gtk_ofld_info->gtk_ofld_content.igtk_len);
				PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] - psk_len         = %u\n", gtk_ofld_info->gtk_ofld_content.psk_len);
			}
		} else {
			PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] gtk_ofld_info:\n");
			PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] - gtk_en          = %u\n", gtk_ofld_info->gtk_en);
		}
	}

	FUNCOUT();

	return phl_status;
}

enum rtw_phl_status rtw_phl_cfg_realwow_info(void *phl, struct rtw_realwow_info *info)
{
	enum rtw_phl_status phl_status = RTW_PHL_STATUS_SUCCESS;
	struct phl_info_t *phl_info = (struct phl_info_t *)phl;
	struct phl_wow_info *wow_info = phl_to_wow_info(phl_info);
	struct rtw_realwow_info *realwow_info = &wow_info->realwow_info;
	void *d = phl_to_drvpriv(phl_info);

	if (info == NULL || realwow_info == NULL) {
		PHL_TRACE(COMP_PHL_WOW, _PHL_WARNING_, "[wow] %s(): some ptr is NULL\n", __func__);
		phl_status = RTW_PHL_STATUS_FAILURE;

	} else {
		_os_mem_set(d, realwow_info, 0, sizeof(struct rtw_realwow_info));

		PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] realwow_en(%u), continue to realwow_ofld.\n",
					info->realwow_en);

		if (info->realwow_en) {
			_os_mem_cpy(d, realwow_info, info, sizeof(struct rtw_realwow_info));

			PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] realwow_ofld_info:\n");
			PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] - realwow_en 		= %u\n",
						realwow_info->realwow_en);
			PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] - tkip_en			= %u\n",
						realwow_info->auto_wakeup);
			PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] - interval			= %u\n",
						realwow_info->realwow_ofld_content.interval);
			PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] - kapktsize			= %u\n",
						realwow_info->realwow_ofld_content.keep_alive_pkt_size);
			PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] - acklostlimit		= %u\n",
						realwow_info->realwow_ofld_content.ack_lost_limit);
			PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] - ackpatternsize		= %u\n",
						realwow_info->realwow_ofld_content.ack_ptrn_size);
			PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] - wakeuppatternsize	= %u\n",
						realwow_info->realwow_ofld_content.wakeup_ptrn_size);
		} else {
			PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] realwow_ofld_info:\n");
			PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] - realwow_en		= %u\n",
						realwow_info->realwow_en);
		}
	}

	return phl_status;
}

enum rtw_phl_status rtw_phl_cfg_wow_wake(void *phl, struct rtw_wow_wake_info *info)
{
	enum rtw_phl_status phl_status = RTW_PHL_STATUS_SUCCESS;
	struct phl_info_t *phl_info = (struct phl_info_t *)phl;
	struct phl_wow_info *wow_info = phl_to_wow_info(phl_info);
	void *d = phl_to_drvpriv(phl_info);

	struct rtw_wow_wake_info *wow_wake_info = &wow_info->wow_wake_info;

	FUNCIN();

	wow_wake_info->wow_en = info->wow_en;
	wow_wake_info->drop_all_pkt = info->drop_all_pkt;
	wow_wake_info->rx_parse_after_wake = info->rx_parse_after_wake;
	wow_wake_info->pairwise_sec_algo = info->pairwise_sec_algo;
	wow_wake_info->group_sec_algo = info->group_sec_algo;
	wow_wake_info->pattern_match_en = info->pattern_match_en;
	wow_wake_info->magic_pkt_en = info->magic_pkt_en;
	wow_wake_info->hw_unicast_en = info->hw_unicast_en;
	wow_wake_info->fw_unicast_en = info->fw_unicast_en;
	wow_wake_info->deauth_wakeup = info->deauth_wakeup;
	wow_wake_info->rekey_wakeup = info->rekey_wakeup;
	wow_wake_info->eap_wakeup = info->eap_wakeup;
	wow_wake_info->all_data_wakeup = info->all_data_wakeup;
	_os_mem_cpy(d, &wow_wake_info->remote_wake_ctrl_info,
		&info->remote_wake_ctrl_info, sizeof(struct rtw_remote_wake_ctrl_info));

	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] wow_en %d\n", wow_wake_info->wow_en);
	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] drop_all_pkt %d\n", wow_wake_info->drop_all_pkt);
	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] rx_parse_after_wake %d\n", wow_wake_info->rx_parse_after_wake);
	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] pairwise_sec_algo %d\n", wow_wake_info->pairwise_sec_algo);
	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] group_sec_algo %d\n", wow_wake_info->group_sec_algo);
	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] bip_sec_algo %d\n", wow_wake_info->bip_sec_algo);
	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] pattern_match_en %d\n", wow_wake_info->pattern_match_en);
	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] magic_pkt_en %d\n", wow_wake_info->magic_pkt_en);
	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] hw_unicast_en %d\n", wow_wake_info->hw_unicast_en);
	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] fw_unicast_en %d\n", wow_wake_info->fw_unicast_en);
	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] deauth_wakeup %d\n", wow_wake_info->deauth_wakeup);
	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] rekey_wakeup %d\n", wow_wake_info->rekey_wakeup);
	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] eap_wakeup %d\n", wow_wake_info->eap_wakeup);
	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] all_data_wakeup %d\n", wow_wake_info->all_data_wakeup);

	return phl_status;
}

enum rtw_phl_status rtw_phl_cfg_gpio_wake_pulse(void *phl, struct rtw_wow_gpio_info *info)
{
	enum rtw_phl_status phl_status = RTW_PHL_STATUS_SUCCESS;
	struct phl_info_t *phl_info = (struct phl_info_t *)phl;
	struct phl_wow_info *wow_info = phl_to_wow_info(phl_info);
	struct rtw_wow_gpio_info *wow_gpio = &wow_info->wow_gpio;

	FUNCIN();

	wow_gpio->dev2hst_gpio_en = info->dev2hst_gpio_en;
	wow_gpio->disable_inband = info->disable_inband;
	wow_gpio->gpio_output_input = info->gpio_output_input;
	wow_gpio->gpio_active = info->gpio_active;
	wow_gpio->toggle_pulse = info->toggle_pulse;
	wow_gpio->data_pin_wakeup = info->data_pin_wakeup;
	wow_gpio->gpio_pulse_nonstop = info->gpio_pulse_nonstop;
	wow_gpio->gpio_time_unit = info->gpio_time_unit;
	wow_gpio->gpio_num = info->gpio_num;
	wow_gpio->gpio_pulse_dura = info->gpio_pulse_dura;
	wow_gpio->gpio_pulse_period = info->gpio_pulse_period;
	wow_gpio->gpio_pulse_count = info->gpio_pulse_count;
	wow_gpio->customer_id = info->customer_id;
	wow_gpio->gpio_pulse_en_a = info->gpio_pulse_en_a;
	wow_gpio->gpio_duration_unit_a = info->gpio_duration_unit_a;
	wow_gpio->gpio_pulse_nonstop_a = info->gpio_pulse_nonstop_a;
	wow_gpio->special_reason_a = info->special_reason_a;
	wow_gpio->gpio_duration_a = info->gpio_duration_a;
	wow_gpio->gpio_pulse_count_a = info->gpio_pulse_count_a;
	wow_gpio->dev2hst_gpio_mode = info->dev2hst_gpio_mode;
	wow_gpio->dev2hst_gpio = info->dev2hst_gpio;
	wow_gpio->dev2hst_high = info->dev2hst_high;

	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] dev2hst_gpio_en %d\n", wow_gpio->dev2hst_gpio_en);
	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] disable_inband %d\n", wow_gpio->disable_inband);
	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] gpio_output_input %d\n", wow_gpio->gpio_output_input);
	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] gpio_active %d\n", wow_gpio->gpio_active);
	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] toggle_pulse %d\n", wow_gpio->toggle_pulse);
	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] data_pin_wakeup %d\n", wow_gpio->data_pin_wakeup);
	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] gpio_pulse_nonstop %d\n", wow_gpio->gpio_pulse_nonstop);
	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] gpio_time_unit %d\n", wow_gpio->gpio_time_unit);
	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] gpio_num %d\n", wow_gpio->gpio_num);
	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] gpio_pulse_dura %d\n", wow_gpio->gpio_pulse_dura);
	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] gpio_pulse_period %d\n", wow_gpio->gpio_pulse_period);
	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] gpio_pulse_count %d\n", wow_gpio->gpio_pulse_count);
	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] customer_id %d\n", wow_gpio->customer_id);
	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] gpio_pulse_en_a %d\n", wow_gpio->gpio_pulse_en_a);
	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] gpio_duration_unit_a %d\n", wow_gpio->gpio_duration_unit_a);
	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] gpio_pulse_nonstop_a %d\n", wow_gpio->gpio_pulse_nonstop_a);
	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] special_reason_a %d\n", wow_gpio->special_reason_a);
	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] gpio_duration_a %d\n", wow_gpio->gpio_duration_a);
	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] gpio_pulse_count_a %d\n", wow_gpio->gpio_pulse_count_a);
	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] dev2hst_gpio_mode %d\n", wow_gpio->dev2hst_gpio_mode);
	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] dev2hst_gpio %d\n", wow_gpio->dev2hst_gpio);
	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] dev2hst_high %d\n", wow_gpio->dev2hst_high);

	return phl_status;
}

void phl_record_wow_stat(struct phl_wow_info *wow_info)
{
	struct phl_wow_stat *wow_stat = &wow_info->wow_stat;

	/* init */
	wow_stat->func_en = wow_info->func_en;
	wow_stat->op_mode = wow_info->op_mode;
	wow_stat->keep_alive_en = wow_info->keep_alive_info.keep_alive_en;
	wow_stat->disc_det_en = wow_info->disc_det_info.disc_det_en;
	wow_stat->arp_en = wow_info->arp_ofld_info.arp_en;
	wow_stat->ndp_en = wow_info->ndp_ofld_info.ndp_en;
	wow_stat->gtk_en = wow_info->gtk_ofld_info.gtk_en;
	wow_stat->dot11w_en = wow_info->gtk_ofld_info.ieee80211w_en;
	wow_stat->err.init = wow_info->err.init;
	/* deinit */
	wow_stat->mac_pwr = wow_info->mac_pwr;
	wow_stat->wake_rsn = wow_info->wake_rsn;
	wow_stat->err.deinit = wow_info->err.deinit;

	if (wow_info->aoac_info.rpt_fail)
		++wow_stat->aoac_rpt_fail_cnt;
}

#ifdef CONFIG_PCI_HCI
enum rtw_phl_status _init_precfg(struct phl_info_t *phl_info, u8 band)
{
	enum rtw_hal_status hstatus = RTW_HAL_STATUS_FAILURE;
	enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE;
	u8 status = false;

	do {
		/* 1. stop Tx DMA */
		rtw_hal_wow_cfg_txdma(phl_info->hal, false);

		/* 2. stop HW Tx */
		hstatus = rtw_hal_wow_drop_tx(phl_info->hal, band);
		if (RTW_HAL_STATUS_SUCCESS != hstatus) {
			PHL_ERR("[wow] rtw_hal_wow_drop_tx fail!\n");
			break;
		}

		/* 3. poll dma idle */
		status = rtw_hal_poll_txdma_idle(phl_info->hal);
		if (!status) {
			PHL_ERR("[wow] rtw_hal_poll_txdma_idle fail!\n");
			break;
		}

	} while (0);

	if (RTW_HAL_STATUS_SUCCESS != hstatus)
		pstatus = RTW_PHL_STATUS_FAILURE;
	else
		pstatus = RTW_PHL_STATUS_SUCCESS;

	FUNCOUT_WSTS(pstatus);

	return pstatus;
}
enum rtw_phl_status _init_postcfg(struct phl_info_t *phl_info)
{
	/* stop tx/rx hci */
	rtw_hal_cfg_txhci(phl_info->hal, false);
	rtw_hal_cfg_rxhci(phl_info->hal, false);

	/* stop dma io */
	rtw_hal_cfg_dma_io(phl_info->hal, false);

	return RTW_PHL_STATUS_SUCCESS;
}
#elif defined(CONFIG_USB_HCI)
enum rtw_phl_status _init_precfg(struct phl_info_t *phl_info, u8 band)
{
	return RTW_PHL_STATUS_SUCCESS;
}
enum rtw_phl_status _init_postcfg(struct phl_info_t *phl_info)
{
	return RTW_PHL_STATUS_SUCCESS;
}

#elif defined(CONFIG_SDIO_HCI)
enum rtw_phl_status _init_precfg(struct phl_info_t *phl_info, u8 band)
{
	return RTW_PHL_STATUS_SUCCESS;
}
enum rtw_phl_status _init_postcfg(struct phl_info_t *phl_info)
{
	return RTW_PHL_STATUS_SUCCESS;
}

#endif

static enum rtw_phl_status _init_precfg_set_rxfltr(struct phl_info_t *phl_info)
{
	enum rtw_hal_status hstatus = RTW_HAL_STATUS_SUCCESS;

	do {
		hstatus = rtw_hal_set_rxfltr_by_type(phl_info->hal, 0, RTW_PHL_PKT_TYPE_DATA, 0);
	 	if (RTW_HAL_STATUS_SUCCESS != hstatus) {
			PHL_ERR("[wow] set rx filter data drop fail, status(%u)\n", hstatus);
			break;
		}

		hstatus = rtw_hal_set_rxfltr_by_type(phl_info->hal, 0, RTW_PHL_PKT_TYPE_MGNT, 0);
		if (RTW_HAL_STATUS_SUCCESS != hstatus) {
			PHL_ERR("[wow] set rx filter mgnt drop fail, status(%u)\n", hstatus);
			break;
		}

		hstatus = rtw_hal_set_rxfltr_by_type(phl_info->hal, 0, RTW_PHL_PKT_TYPE_CTRL, 0);
		if (RTW_HAL_STATUS_SUCCESS != hstatus) {
			PHL_ERR("[wow] set rx filter ctrl drop fail, status(%u)\n", hstatus);
			break;
		}
	} while (0);

	return (hstatus == RTW_HAL_STATUS_SUCCESS) ?
			RTW_PHL_STATUS_SUCCESS : RTW_PHL_STATUS_FAILURE;
}

static enum rtw_phl_status _init_postcfg_set_rxfltr(struct phl_info_t *phl_info)
{
	enum rtw_hal_status hstatus = RTW_HAL_STATUS_SUCCESS;

	do {
		hstatus = rtw_hal_set_rxfltr_by_type(phl_info->hal, 0, RTW_PHL_PKT_TYPE_DATA, 1);
		if (RTW_HAL_STATUS_SUCCESS != hstatus) {
			PHL_ERR("[wow] set rx filter data to host fail, status(%u)\n", hstatus);
			break;
		}

		hstatus = rtw_hal_set_rxfltr_by_type(phl_info->hal, 0, RTW_PHL_PKT_TYPE_MGNT, 1);
		if (RTW_HAL_STATUS_SUCCESS != hstatus) {
			PHL_ERR("[wow] set rx filter mgnt to host fail, status(%u)\n", hstatus);
			break;
		}

		hstatus = rtw_hal_set_rxfltr_by_type(phl_info->hal, 0, RTW_PHL_PKT_TYPE_CTRL, 1);
		if (RTW_HAL_STATUS_SUCCESS != hstatus) {
			PHL_ERR("[wow] set rx filter ctrl to host fail, status(%u)\n", hstatus);
			break;
		}
	} while (0);

	return (hstatus == RTW_HAL_STATUS_SUCCESS) ?
			RTW_PHL_STATUS_SUCCESS : RTW_PHL_STATUS_FAILURE;
}

#define MAX_POLLING_TRX_STOP 2000 /* us */
enum rtw_phl_status phl_wow_init_precfg(struct phl_wow_info *wow_info)
{
	enum rtw_phl_status pstatus = RTW_PHL_STATUS_SUCCESS;
	struct phl_info_t *phl_info = wow_info->phl_info;
	struct phl_hci_trx_ops *trx_ops = phl_info->hci_trx_ops;
	u32 wait_cnt = 0;

	FUNCIN();

	/* pause sw Tx */
	trx_ops->req_tx_stop(phl_info);

	/* schedule current existing tx handler */
	pstatus = rtw_phl_tx_req_notify(phl_info);
	if (RTW_PHL_STATUS_SUCCESS != pstatus)
		PHL_ERR("[wow] rtw_phl_tx_req_notify fail, status(%u)\n", pstatus);

	/* polling pause sw Tx done */
	while (wait_cnt < MAX_POLLING_TRX_STOP) {
		if (trx_ops->is_tx_pause(phl_info)) {
			PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] sw tx pause succeed.\n");
			break;
		}
		_os_delay_us(phl_info->phl_com->drv_priv, 1);
		wait_cnt++;
	}
	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] stop sw tx wait_cnt %d.\n", wait_cnt);

	/* init pre-configuration for different interfaces */
	pstatus = _init_precfg(phl_info, wow_info->sta->wrole->hw_band);
	if (RTW_PHL_STATUS_SUCCESS != pstatus)
		return pstatus;

	/* set packet drop by setting rx filter */
	pstatus = _init_precfg_set_rxfltr(phl_info);
	if (RTW_PHL_STATUS_SUCCESS != pstatus)
		return pstatus;

	/* disable ppdu sts */
	rtw_hal_ppdu_sts_cfg(phl_info->hal, wow_info->sta->wrole->hw_band, false);

	pstatus = RTW_PHL_STATUS_SUCCESS;

	return pstatus;
}

enum rtw_phl_status phl_wow_init_postcfg(struct phl_wow_info *wow_info)
{
	enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE;
	enum rtw_hal_status hstatus = RTW_HAL_STATUS_FAILURE;
	struct phl_info_t *phl_info = wow_info->phl_info;
	struct phl_hci_trx_ops *trx_ops = phl_info->hci_trx_ops;
	u32 wait_cnt = 0;
#ifdef CONFIG_SYNC_INTERRUPT
	struct rtw_phl_evt_ops *evt_ops = &phl_info->phl_com->evt_ops;
#endif /* CONFIG_SYNC_INTERRUPT */

	/* disable interrupt */
#ifdef CONFIG_SYNC_INTERRUPT
	evt_ops->set_interrupt_caps(phl_to_drvpriv(phl_info), false);
#else
	rtw_hal_disable_interrupt(phl_info->phl_com, phl_info->hal);
#endif /* CONFIG_SYNC_INTERRUPT */

	pstatus = _init_postcfg(phl_info);
	if (RTW_PHL_STATUS_SUCCESS != pstatus)
		PHL_ERR("[wow] _init_postcfg failed.\n");

	/* stop sw rx */
	trx_ops->req_rx_stop(phl_info);
	pstatus = rtw_phl_start_rx_process(phl_info);
	if (RTW_PHL_STATUS_SUCCESS != pstatus)
		PHL_ERR("[wow] rtw_phl_start_rx_process failed.\n");

	while (wait_cnt < MAX_POLLING_TRX_STOP) {
		if (trx_ops->is_rx_pause(phl_info)) {
			PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] sw rx pause succeed.\n");
			break;
		}
		_os_delay_us(phl_info->phl_com->drv_priv, 1);
		wait_cnt++;
	}

	if (wait_cnt == MAX_POLLING_TRX_STOP)
		PHL_WARN("[wow] sw rx pause fail.\n");

	/* configure wow sleep */
	hstatus = rtw_hal_cfg_wow_sleep(phl_info->hal, true);
	if (RTW_HAL_STATUS_SUCCESS != hstatus)
		return RTW_PHL_STATUS_FAILURE;

	/* forward rx packet to host by setting rx filter */
	pstatus = _init_postcfg_set_rxfltr(phl_info);

	/* reset trx */
#ifdef CONFIG_USB_HCI
	trx_ops->trx_stop(phl_info);
#else
	trx_ops->trx_reset(phl_info, PHL_CTRL_TX|PHL_CTRL_RX);
#endif


	return pstatus;
}

static void _phl_indic_wake_sec_upd(struct phl_wow_info *wow_info, u8 aoac_report_get_ok, u8 rx_ready)
{
	struct phl_info_t *phl_info = wow_info->phl_info;
	struct rtw_phl_evt_ops *ops = &phl_info->phl_com->evt_ops;
	void *drv_priv = phl_to_drvpriv(phl_info);

	if (NULL != ops->wow_handle_sec_info_update)
		ops->wow_handle_sec_info_update(drv_priv, &wow_info->aoac_info, aoac_report_get_ok, rx_ready);
	else
		PHL_TRACE(COMP_PHL_WOW, _PHL_ERR_, "[wow] %s : evt_ops->wow_handle_sec_info_update is NULL.\n"
			, __func__);
}

static void _phl_handle_aoac_rpt_action(struct phl_wow_info *wow_info, bool rx_ready)
{
	struct phl_info_t *phl_info = wow_info->phl_info;
	enum rtw_hal_status hstatus = RTW_HAL_STATUS_FAILURE;
	u8 aoac_report_get_ok = false;
	static u8 phase_0_ok = false;

	if (wow_info->wow_wake_info.pairwise_sec_algo) {
		if (rx_ready == false) {
			/* phase 0 */
			hstatus = rtw_hal_get_wow_aoac_rpt(phl_info->hal, &wow_info->aoac_info, rx_ready);
			aoac_report_get_ok = (hstatus == RTW_HAL_STATUS_SUCCESS) ? true : false;
			_phl_indic_wake_sec_upd(wow_info, aoac_report_get_ok, rx_ready);

			phase_0_ok = aoac_report_get_ok;
		}

		if (rx_ready == true) {
			/* phase 1 */
			if (phase_0_ok) {
				hstatus = rtw_hal_get_wow_aoac_rpt(phl_info->hal, &wow_info->aoac_info, rx_ready);
				aoac_report_get_ok = (hstatus == RTW_HAL_STATUS_SUCCESS) ? true : false;
				_phl_indic_wake_sec_upd(wow_info, aoac_report_get_ok, rx_ready);
			}

			phase_0_ok = false;

			wow_info->aoac_info.rpt_fail = (aoac_report_get_ok == false) ? true : false;
		}
	}
}

static enum rtw_phl_status _phl_indic_wake_rsn(struct phl_wow_info *wow_info)
{
	enum rtw_phl_status phl_status = RTW_PHL_STATUS_SUCCESS;
	struct phl_info_t *phl_info = wow_info->phl_info;
	struct rtw_phl_evt_ops *evt_ops = &(phl_info->phl_com->evt_ops);

	FUNCIN_WSTS(phl_status);

	if (NULL != evt_ops->indicate_wake_rsn) {
		evt_ops->indicate_wake_rsn(phl_to_drvpriv(phl_info), wow_info->wake_rsn);
	}

	FUNCOUT_WSTS(phl_status);

	return phl_status;
}

void phl_wow_handle_wake_rsn(struct phl_wow_info *wow_info, u8 *reset)
{
	struct phl_info_t *phl_info = wow_info->phl_info;

	rtw_hal_get_wake_rsn(phl_info->hal, &wow_info->wake_rsn, reset);
	_phl_indic_wake_rsn(wow_info);
}

#ifdef CONFIG_PCI_HCI
enum rtw_phl_status _deinit_precfg(struct phl_info_t *phl_info)
{
#ifdef DBG_RST_BDRAM_TIME
	u32 rst_bdram_start = _os_get_cur_time_ms();
#endif /* DBG_RST_BDRAM_TIME */

	rtw_hal_clear_bdidx(phl_info->hal);

#ifdef DBG_RST_BDRAM_TIME
	rst_bdram_start = _os_get_cur_time_ms();
#endif
	rtw_hal_rst_bdram(phl_info->hal);

#ifdef DBG_RST_BDRAM_TIME
	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] %s : Reset bdram takes %u (ms).\n"
		, __func__, phl_get_passing_time_ms(rst_bdram_start));
#endif

	rtw_hal_cfg_dma_io(phl_info->hal, true);
	rtw_hal_cfg_txhci(phl_info->hal, true);
	rtw_hal_cfg_rxhci(phl_info->hal, true);

	/* start tx dma */
	rtw_hal_wow_cfg_txdma(phl_info->hal, true);

	return RTW_PHL_STATUS_SUCCESS;
}
#elif defined(CONFIG_USB_HCI)
enum rtw_phl_status _deinit_precfg(struct phl_info_t *phl_info)
{
	return RTW_PHL_STATUS_SUCCESS;
}
#elif defined(CONFIG_SDIO_HCI)
enum rtw_phl_status _deinit_precfg(struct phl_info_t *phl_info)
{
	return RTW_PHL_STATUS_SUCCESS;
}
#endif

void _deinit_precfg_set_intr(struct phl_info_t *phl_info)
{
#ifdef CONFIG_SYNC_INTERRUPT
	struct rtw_phl_evt_ops *evt_ops = &phl_info->phl_com->evt_ops;
#endif /* CONFIG_SYNC_INTERRUPT */

	rtw_hal_set_default_var(phl_info->hal, SET_DEF_RSN_WOW_RESUME_HNDL_RX);
#ifdef CONFIG_SYNC_INTERRUPT
	evt_ops->set_interrupt_caps(phl_to_drvpriv(phl_info), true);
#else
	rtw_hal_enable_interrupt(phl_info->phl_com, phl_info->hal);
#endif /* CONFIG_SYNC_INTERRUPT */
}

enum rtw_phl_status phl_wow_deinit_precfg(struct phl_wow_info *wow_info)
{
	enum rtw_phl_status phl_status = RTW_PHL_STATUS_SUCCESS;
	struct phl_info_t *phl_info = wow_info->phl_info;
	struct phl_hci_trx_ops *trx_ops = phl_info->hci_trx_ops;

	FUNCIN();

	_deinit_precfg(phl_info);

	rtw_hal_cfg_wow_sleep(phl_info->hal, false);

	_phl_handle_aoac_rpt_action(wow_info, false);

	/* resume sw rx */
#ifdef CONFIG_USB_HCI
	trx_ops->trx_cfg(phl_info);
#else
	trx_ops->trx_resume(phl_info, PHL_CTRL_RX);
#endif

	_deinit_precfg_set_intr(phl_info);

	_phl_handle_aoac_rpt_action(wow_info, true);

	return phl_status;
}

void phl_reset_wow_info(struct phl_wow_info *wow_info)
{
	struct phl_info_t *phl_info = wow_info->phl_info;
	void *d = phl_to_drvpriv(phl_info);

	wow_info->func_en = 0;
	wow_info->op_mode = RTW_WOW_OP_NONE;
	wow_info->mac_pwr = RTW_MAC_PWR_NONE;
	wow_info->ps_pwr_lvl = PS_PWR_LVL_MAX;

	_os_mem_set(d, &wow_info->err, 0, sizeof(struct phl_wow_error));
	_os_mem_set(d, &wow_info->keep_alive_info, 0, sizeof(struct rtw_keep_alive_info));
	_os_mem_set(d, &wow_info->disc_det_info, 0, sizeof(struct rtw_disc_det_info));
	_os_mem_set(d, &wow_info->nlo_info, 0, sizeof(struct rtw_nlo_info));
	_os_mem_set(d, &wow_info->arp_ofld_info, 0, sizeof(struct rtw_arp_ofld_info));
	_os_mem_set(d, &wow_info->ndp_ofld_info, 0, sizeof(struct rtw_ndp_ofld_info));
	_os_mem_set(d, &wow_info->gtk_ofld_info, 0, sizeof(struct rtw_gtk_ofld_info));
	_os_mem_set(d, &wow_info->realwow_info, 0, sizeof(struct rtw_realwow_info));
	_os_mem_set(d, &wow_info->wow_wake_info, 0, sizeof(struct rtw_wow_wake_info));
	_os_mem_set(d, &wow_info->aoac_info, 0, sizeof(struct rtw_aoac_report));

	wow_info->wake_rsn = RTW_WOW_RSN_UNKNOWN;

	/*
		&wow_info->pattern_match_info need not to be reset here.
		We expect those items will be remove triggered by core layer
	*/
}

void _deinit_postcfg_set_intr(struct phl_info_t *phl_info)
{
#ifdef CONFIG_SYNC_INTERRUPT
	struct rtw_phl_evt_ops *evt_ops = &phl_info->phl_com->evt_ops;
#endif /* CONFIG_SYNC_INTERRUPT */

#ifdef CONFIG_SYNC_INTERRUPT
	evt_ops->set_interrupt_caps(phl_to_drvpriv(phl_info), false);
#else
	rtw_hal_disable_interrupt(phl_info->phl_com, phl_info->hal);
#endif /* CONFIG_SYNC_INTERRUPT */
	rtw_hal_set_default_var(phl_info->hal, SET_DEF_RSN_WOW_RESUME_DONE);
#ifdef CONFIG_SYNC_INTERRUPT
	evt_ops->set_interrupt_caps(phl_to_drvpriv(phl_info), true);
#else
	rtw_hal_enable_interrupt(phl_info->phl_com, phl_info->hal);
#endif /* CONFIG_SYNC_INTERRUPT */
}

enum rtw_phl_status phl_wow_deinit_postcfg(struct phl_wow_info *wow_info)
{
	enum rtw_phl_status phl_status = RTW_PHL_STATUS_SUCCESS;
	struct phl_info_t *phl_info = wow_info->phl_info;
	struct phl_hci_trx_ops *trx_ops = phl_info->hci_trx_ops;

	FUNCIN();

	/* resume sw tx */
	trx_ops->trx_resume(phl_info, PHL_CTRL_TX);
	/* enable ppdu sts */
	rtw_hal_ppdu_sts_cfg(phl_info->hal, wow_info->sta->wrole->hw_band, true);

	_deinit_postcfg_set_intr(phl_info);

	return phl_status;
}

u8 phl_wow_nlo_exist(struct phl_wow_info *wow_info)
{
	return wow_info->nlo_info.nlo_exist;
}

enum rtw_phl_status _phl_wow_cfg_pkt_ofld(struct phl_wow_info *wow_info, u8 pkt_type, u8 *pkt_id, void *buf)
{
	enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE;
	u16 macid = wow_info->sta->macid;
	u32 *token;

	switch(pkt_type) {
	case PKT_TYPE_NULL_DATA:
		token = &wow_info->null_pkt_token;
		break;
	case PKT_TYPE_ARP_RSP:
		token = &wow_info->arp_pkt_token;
		break;
	case PKT_TYPE_NDP:
		token = &wow_info->ndp_pkt_token;
		break;
	case PKT_TYPE_EAPOL_KEY:
		token = &wow_info->eapol_key_pkt_token;
		break;
	case PKT_TYPE_SA_QUERY:
		token = &wow_info->sa_query_pkt_token;
		break;
	case PKT_TYPE_REALWOW_KAPKT:
		token = &wow_info->kapkt_pkt_token;
		break;
	case PKT_TYPE_REALWOW_ACK:
		token = &wow_info->ack_pkt_token;
		break;
	case PKT_TYPE_REALWOW_WP:
		token = &wow_info->wp_token;
		break;
	default:
		PHL_TRACE(COMP_PHL_WOW, _PHL_ERR_, "[wow] %s : unknown pkt_type %d.\n"
			, __func__, pkt_type);
		return pstatus;
	}

	pstatus = RTW_PHL_PKT_OFLD_REQ(wow_info->phl_info, macid, pkt_type, token, buf);

	if (pstatus == RTW_PHL_STATUS_SUCCESS)
		*pkt_id = phl_pkt_ofld_get_id(wow_info->phl_info, macid, pkt_type);

	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] %s : pkt_type %s, pkt_id %d, token %u, status(%u)\n",
		__func__, phl_get_pkt_ofld_str(pkt_type), *pkt_id, *token, pstatus);

	return pstatus;
}

enum rtw_phl_status phl_wow_func_en(struct phl_wow_info *wow_info)
{
	enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE;
	enum rtw_hal_status hstatus = RTW_HAL_STATUS_FAILURE;
	struct phl_info_t *phl_info = wow_info->phl_info;
	struct rtw_phl_stainfo_t *sta = wow_info->sta;
	struct rtw_pkt_ofld_null_info null_info = {0};
	struct rtw_pkt_ofld_arp_rsp_info arp_rsp_info = {0};
	struct rtw_pkt_ofld_na_info na_info = {0};
	struct rtw_pkt_ofld_eapol_key_info eapol_key_info = {0};
	struct rtw_pkt_ofld_sa_query_info sa_query_info = {0};
	struct rtw_pkt_ofld_realwow_kapkt_info kapkt_info = {0};
	struct rtw_pkt_ofld_realwow_ack_info ack_info = {0};
	struct rtw_pkt_ofld_realwow_wp_info wakeup_info = {0};
	struct rtw_hal_wow_cfg cfg;

	FUNCIN();

	if (!wow_info->wow_wake_info.wow_en) {
		PHL_WARN("%s : wow func is not enabled!\n", __func__);
		return pstatus;
	}

	do {

		hstatus = rtw_hal_reset_pkt_ofld_state(phl_info->hal);

		if (RTW_HAL_STATUS_SUCCESS != hstatus) {
			pstatus = RTW_PHL_STATUS_FAILURE;
			break;
		}

		if (wow_info->keep_alive_info.keep_alive_en) {

			_phl_cfg_pkt_ofld_null_info(wow_info, sta, &null_info);

			pstatus = _phl_wow_cfg_pkt_ofld(wow_info,
					PKT_TYPE_NULL_DATA,
					&wow_info->keep_alive_info.null_pkt_id,
					(void *)&null_info);

			if (pstatus != RTW_PHL_STATUS_SUCCESS)
				break;
		}

		if (wow_info->arp_ofld_info.arp_en) {

			_phl_cfg_pkt_ofld_arp_rsp_info(wow_info, sta, &arp_rsp_info);

			pstatus = _phl_wow_cfg_pkt_ofld(wow_info,
					PKT_TYPE_ARP_RSP,
					&wow_info->arp_ofld_info.arp_rsp_id,
					(void *)&arp_rsp_info);

			if (pstatus != RTW_PHL_STATUS_SUCCESS)
				break;
		}

		if (wow_info->ndp_ofld_info.ndp_en) {

			_phl_cfg_pkt_ofld_na_info(wow_info, sta, &na_info);

			pstatus = _phl_wow_cfg_pkt_ofld(wow_info,
					PKT_TYPE_NDP, &wow_info->ndp_ofld_info.ndp_id,
					(void *)&na_info);

			if (pstatus != RTW_PHL_STATUS_SUCCESS)
				break;
		}

		if (wow_info->gtk_ofld_info.gtk_en) {
			_phl_cfg_pkt_ofld_eapol_key_info(wow_info, sta, &eapol_key_info);

			pstatus = _phl_wow_cfg_pkt_ofld(wow_info,
					PKT_TYPE_EAPOL_KEY, &wow_info->gtk_ofld_info.gtk_rsp_id,
					(void *)&eapol_key_info);

			if (pstatus != RTW_PHL_STATUS_SUCCESS)
				break;

			if (wow_info->gtk_ofld_info.ieee80211w_en) {
				_phl_cfg_pkt_ofld_sa_query_info(wow_info, sta, &sa_query_info);

				pstatus = _phl_wow_cfg_pkt_ofld(wow_info,
					PKT_TYPE_SA_QUERY, &wow_info->gtk_ofld_info.sa_query_id,
					(void *)&sa_query_info);

				if (pstatus != RTW_PHL_STATUS_SUCCESS)
					break;
			}
		}

		if (wow_info->realwow_info.realwow_en) {

			/* realwow keep alive */
			_phl_cfg_pkt_ofld_realwow_kapkt_info(wow_info, sta, &kapkt_info);

			pstatus = _phl_wow_cfg_pkt_ofld(wow_info,
					PKT_TYPE_REALWOW_KAPKT,
					&wow_info->realwow_info.keepalive_id,
					(void *)&kapkt_info);

			if (pstatus != RTW_PHL_STATUS_SUCCESS)
				break;

			/* realwow ack */
			_phl_cfg_pkt_ofld_realwow_ack_info(wow_info, &ack_info);

			pstatus = _phl_wow_cfg_pkt_ofld(wow_info,
					PKT_TYPE_REALWOW_ACK,
					&wow_info->realwow_info.ack_pattern_id,
					(void *)&ack_info);

			if (pstatus != RTW_PHL_STATUS_SUCCESS)
				break;

			/* realwow wake up */
			_phl_cfg_pkt_ofld_realwow_wp_info(wow_info, &wakeup_info);

			pstatus = _phl_wow_cfg_pkt_ofld(wow_info,
					PKT_TYPE_REALWOW_WP,
					&wow_info->realwow_info.wakeup_pattern_id,
					(void *)&wakeup_info);

			if (pstatus != RTW_PHL_STATUS_SUCCESS)
				break;
		}

		cfg.keep_alive_cfg = &wow_info->keep_alive_info;
		cfg.disc_det_cfg = &wow_info->disc_det_info;
		cfg.nlo_cfg = &wow_info->nlo_info;
		cfg.arp_ofld_cfg = &wow_info->arp_ofld_info;
		cfg.ndp_ofld_cfg = &wow_info->ndp_ofld_info;
		cfg.gtk_ofld_cfg = &wow_info->gtk_ofld_info;
		cfg.realwow_cfg = &wow_info->realwow_info;
		cfg.wow_wake_cfg = &wow_info->wow_wake_info;
		cfg.pattern_match_info = &wow_info->pattern_match_info;
		cfg.wow_gpio = &wow_info->wow_gpio;

		hstatus = rtw_hal_wow_func_en(phl_info->phl_com, phl_info->hal, sta->macid, &cfg);
		if (hstatus != RTW_HAL_STATUS_SUCCESS) {
			PHL_ERR("rtw_hal_wow_func_en fail, status (%u)\n", hstatus);
			pstatus = RTW_PHL_STATUS_FAILURE;
			break;
		}

		hstatus = rtw_hal_wow_func_start(phl_info->phl_com, phl_info->hal, sta->macid, &cfg);
		if (hstatus != RTW_HAL_STATUS_SUCCESS) {
			PHL_ERR("rtw_hal_wow_func_start fail, status (%u)\n", hstatus);
			pstatus = RTW_PHL_STATUS_FAILURE;
			break;
		}

		wow_info->func_en = true;
		pstatus = RTW_PHL_STATUS_SUCCESS;

	} while (0);

	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] %s status (%u).\n", __func__, pstatus);

	return pstatus;
}

void phl_wow_func_dis(struct phl_wow_info *wow_info)
{
	enum rtw_hal_status hstatus = RTW_HAL_STATUS_FAILURE;
	struct phl_info_t *phl_info = wow_info->phl_info;
	struct rtw_phl_stainfo_t *sta = wow_info->sta; 

	if (!wow_info->wow_wake_info.wow_en) {
		PHL_WARN("%s : wow func is not enabled!\n", __func__);
		return;
	}

	hstatus = rtw_hal_wow_func_dis(phl_info->phl_com, phl_info->hal, sta->macid);
	if (hstatus != RTW_HAL_STATUS_SUCCESS)
		PHL_ERR("rtw_hal_wow_func_dis fail, status (%u)\n", hstatus);

	if (wow_info->keep_alive_info.keep_alive_en) {
		phl_pkt_ofld_cancel(phl_info, sta->macid,
							PKT_TYPE_NULL_DATA, &wow_info->null_pkt_token);
	}

	if (wow_info->arp_ofld_info.arp_en) {
		phl_pkt_ofld_cancel(phl_info, sta->macid,
							PKT_TYPE_ARP_RSP, &wow_info->arp_pkt_token);
	}

	if (wow_info->ndp_ofld_info.ndp_en) {
		phl_pkt_ofld_cancel(phl_info, sta->macid,
							PKT_TYPE_NDP, &wow_info->ndp_pkt_token);
	}

	if (wow_info->gtk_ofld_info.gtk_en) {
		phl_pkt_ofld_cancel(phl_info, sta->macid,
							PKT_TYPE_EAPOL_KEY, &wow_info->eapol_key_pkt_token);
		if (wow_info->gtk_ofld_info.ieee80211w_en) {
			phl_pkt_ofld_cancel(phl_info, sta->macid,
								PKT_TYPE_SA_QUERY, &wow_info->sa_query_pkt_token);
		}
	}

	if (wow_info->realwow_info.realwow_en) {
		phl_pkt_ofld_cancel(phl_info, sta->macid,
					PKT_TYPE_REALWOW_KAPKT, &wow_info->kapkt_pkt_token);
		phl_pkt_ofld_cancel(phl_info, sta->macid,
					PKT_TYPE_REALWOW_ACK, &wow_info->ack_pkt_token);
		phl_pkt_ofld_cancel(phl_info, sta->macid,
					PKT_TYPE_REALWOW_WP, &wow_info->wp_token);
	}

	hstatus = rtw_hal_wow_func_stop(phl_info->phl_com, phl_info->hal, sta->macid);
	if (hstatus != RTW_HAL_STATUS_SUCCESS)
		PHL_ERR("rtw_hal_wow_func_stop fail, status (%u)\n", hstatus);

	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] %s done.\n", __func__);
}

void phl_wow_decide_op_mode(struct phl_wow_info *wow_info, struct rtw_phl_stainfo_t *sta)
{
	u8 nlo_exist = phl_wow_nlo_exist(wow_info);
	enum mlme_state mstat = sta->wrole->mstate;

	wow_info->sta = sta;

	if (mstat == MLME_NO_LINK && !nlo_exist) {
		wow_info->op_mode = RTW_WOW_OP_PWR_DOWN;
	} else if (mstat == MLME_NO_LINK && nlo_exist) {
		wow_info->op_mode = RTW_WOW_OP_DISCONNECT_STBY;
	} else if (mstat == MLME_LINKED) {
		wow_info->op_mode = RTW_WOW_OP_CONNECT_STBY;
	} else {
		wow_info->op_mode = RTW_WOW_OP_PWR_DOWN;
	}

	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] %s op mode set to %d\n.",
			  __func__, wow_info->op_mode);
}
#ifdef CONFIG_POWER_SAVE
void phl_wow_ps_pctl_cfg(struct phl_wow_info *wow_info, u8 enter_wow)
{
	enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE;
	struct phl_info_t *phl_info = wow_info->phl_info;
	struct ps_cfg cfg = {0};
	struct rtw_ps_cap_t *ps_cap = _get_ps_cap(phl_info);

	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] %s : op mode %d.\n.",
			  __func__, wow_info->op_mode);

	if (wow_info->op_mode == RTW_WOW_OP_DISCONNECT_STBY) {
		/* IPS */
	} else if (wow_info->op_mode == RTW_WOW_OP_CONNECT_STBY) {
		/* LPS */
		if (ps_cap->lps_wow_en) {
			cfg.macid = wow_info->sta->macid;
			cfg.awake_interval = ps_cap->lps_wow_awake_interval;
			cfg.listen_bcn_mode = ps_cap->lps_wow_listen_bcn_mode;
			cfg.smart_ps_mode = ps_cap->lps_wow_smart_ps_mode;
			pstatus = phl_ps_lps_cfg(phl_info, &cfg, enter_wow);
		}
	} else {
		PHL_ERR("%s : undefined wowlan op mode.\n", __func__);
	}
}

void phl_wow_ps_pwr_cfg(struct phl_wow_info *wow_info, u8 enter_wow)
{
	enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE;
	struct phl_info_t *phl_info = wow_info->phl_info;
	struct rtw_ps_cap_t *ps_cap = _get_ps_cap(phl_info);

	wow_info->ps_pwr_lvl = PS_PWR_LVL_PWRON;

	if (wow_info->op_mode == RTW_WOW_OP_DISCONNECT_STBY) {
		/* IPS */
	} else if (wow_info->op_mode == RTW_WOW_OP_CONNECT_STBY) {
		if (ps_cap->lps_wow_en) {
			wow_info->ps_pwr_lvl = phl_ps_judge_pwr_lvl(ps_cap->lps_wow_cap, PS_MODE_LPS, enter_wow);
			pstatus = phl_ps_cfg_pwr_lvl(phl_info, PS_MODE_LPS, PS_PWR_LVL_PWRON, wow_info->ps_pwr_lvl);
		}
	} else {
		PHL_ERR("%s : undefined wowlan op mode.\n", __func__);
	}

	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] %s : op mode %d, pwr lvl %s.\n.",
			  __func__, wow_info->op_mode, phl_ps_pwr_lvl_to_str(wow_info->ps_pwr_lvl));
}

enum rtw_phl_status phl_wow_leave_low_power(struct phl_wow_info *wow_info)
{
	enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE;
	struct phl_info_t *phl_info = wow_info->phl_info;

	PHL_TRACE(COMP_PHL_WOW, _PHL_INFO_, "[wow] %s : op mode %d, pwr lvl %s.\n.",
			  __func__, wow_info->op_mode, phl_ps_pwr_lvl_to_str(wow_info->ps_pwr_lvl));

	if (wow_info->ps_pwr_lvl == PS_PWR_LVL_PWRON)
		return RTW_PHL_STATUS_SUCCESS;

	if (wow_info->op_mode == RTW_WOW_OP_DISCONNECT_STBY) {
		/* IPS */
	} else if (wow_info->op_mode == RTW_WOW_OP_CONNECT_STBY) {
		pstatus = phl_ps_cfg_pwr_lvl(phl_info, PS_MODE_LPS, wow_info->ps_pwr_lvl, PS_PWR_LVL_PWRON);
	} else {
		PHL_ERR("%s : undefined wowlan op mode.\n", __func__);
	}

	return pstatus;
}
#endif
#define case_rsn(rsn) \
	case RTW_WOW_RSN_##rsn: return #rsn

const char *rtw_phl_get_wow_rsn_str(void *phl, enum rtw_wow_wake_reason wake_rsn)
{
	switch (wake_rsn) {
	case_rsn(UNKNOWN); /* RTW_WOW_RSN_UNKNOWN */
	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 "UNDEFINED"; /* RTW_WOW_RSN_MAX */
	}
}

enum rtw_phl_status rtw_phl_cfg_wow_set_sw_gpio_mode(void *phl, struct rtw_wow_gpio_info *info)
{
	enum rtw_phl_status phl_status = RTW_PHL_STATUS_SUCCESS;
	struct phl_info_t *phl_info = (struct phl_info_t *)phl;
	struct phl_wow_info *wow_info = phl_to_wow_info(phl_info);
	struct rtw_wow_gpio_info *wow_gpio = &wow_info->wow_gpio;

	FUNCIN();

	wow_gpio->dev2hst_gpio = info->dev2hst_gpio;
	wow_gpio->dev2hst_gpio_mode = info->dev2hst_gpio_mode;
	phl_status = rtw_hal_set_sw_gpio_mode(phl_info->phl_com, phl_info->hal
		, wow_gpio->dev2hst_gpio_mode, wow_gpio->dev2hst_gpio);

	PHL_INFO("%s, gpio=%d, gpio_mode=%d\n", __FUNCTION__
		, wow_gpio->dev2hst_gpio, wow_gpio->dev2hst_gpio_mode);

	return phl_status;
}

enum rtw_phl_status rtw_phl_cfg_wow_sw_gpio_ctrl(void *phl, struct rtw_wow_gpio_info *info)
{
	enum rtw_phl_status phl_status = RTW_PHL_STATUS_SUCCESS;
	struct phl_info_t *phl_info = (struct phl_info_t *)phl;
	struct phl_wow_info *wow_info = phl_to_wow_info(phl_info);
	struct rtw_wow_gpio_info *wow_gpio = &wow_info->wow_gpio;

	FUNCIN();

	wow_gpio->dev2hst_high = info->dev2hst_high;
	phl_status = rtw_hal_sw_gpio_ctrl(phl_info->phl_com, phl_info->hal
		, wow_gpio->dev2hst_high, wow_gpio->dev2hst_gpio);

	PHL_INFO("%s, gpio=%d, output=%d\n", __FUNCTION__
		, wow_gpio->dev2hst_gpio, wow_gpio->dev2hst_high);

	return phl_status;
}

#endif /* CONFIG_WOWLAN */
