/******************************************************************************
 *
 * Copyright(c) 2007 - 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 _RTW_SEC_CAM_C_

#include <drv_types.h>
#include <rtw_sec_cam.h>

void invalidate_cam_all(_adapter *padapter)
{
	struct dvobj_priv *dvobj = adapter_to_dvobj(padapter);
	struct cam_ctl_t *cam_ctl = &dvobj->cam_ctl;
	u8 val8 = 0;

	rtw_hal_set_hwreg(padapter, HW_VAR_CAM_INVALID_ALL, &val8);

	_rtw_spinlock_bh(&cam_ctl->lock);
	rtw_sec_cam_map_clr_all(&cam_ctl->used);
	_rtw_memset(dvobj->cam_cache, 0, sizeof(struct sec_cam_ent) * SEC_CAM_ENT_NUM_SW_LIMIT);
	_rtw_spinunlock_bh(&cam_ctl->lock);
}

void _clear_cam_entry(_adapter *padapter, u8 entry)
{
	unsigned char null_sta[6] = {0};
	unsigned char null_key[32] = {0};

	rtw_hal_sec_write_cam_ent(padapter, entry, 0, null_sta, null_key);
}

inline void _write_cam(_adapter *adapter, u8 id, u16 ctrl, u8 *mac, u8 *key)
{
#ifdef CONFIG_WRITE_CACHE_ONLY
	write_cam_cache(adapter, id , ctrl, mac, key);
#else
	rtw_hal_sec_write_cam_ent(adapter, id, ctrl, mac, key);
	write_cam_cache(adapter, id , ctrl, mac, key);
#endif
}

inline void write_cam(_adapter *adapter, u8 id, u16 ctrl, u8 *mac, u8 *key)
{
	if (ctrl & BIT(9)) {
		_write_cam(adapter, id, ctrl, mac, key);
		_write_cam(adapter, (id + 1), ctrl | BIT(5), mac, (key + 16));
		RTW_INFO_DUMP("key-0: ", key, 16);
		RTW_INFO_DUMP("key-1: ", (key + 16), 16);
	} else
		_write_cam(adapter, id, ctrl, mac, key);
}

inline void clear_cam_entry(_adapter *adapter, u8 id)
{
	_clear_cam_entry(adapter, id);
	clear_cam_cache(adapter, id);
}

inline void write_cam_from_cache(_adapter *adapter, u8 id)
{
	struct dvobj_priv *dvobj = adapter_to_dvobj(adapter);
	struct cam_ctl_t *cam_ctl = &dvobj->cam_ctl;
	struct sec_cam_ent cache;

	_rtw_spinlock_bh(&cam_ctl->lock);
	_rtw_memcpy(&cache, &dvobj->cam_cache[id], sizeof(struct sec_cam_ent));
	_rtw_spinunlock_bh(&cam_ctl->lock);

	rtw_hal_sec_write_cam_ent(adapter, id, cache.ctrl, cache.mac, cache.key);
}
void write_cam_cache(_adapter *adapter, u8 id, u16 ctrl, u8 *mac, u8 *key)
{
	struct dvobj_priv *dvobj = adapter_to_dvobj(adapter);
	struct cam_ctl_t *cam_ctl = &dvobj->cam_ctl;

	_rtw_spinlock_bh(&cam_ctl->lock);

	dvobj->cam_cache[id].ctrl = ctrl;
	_rtw_memcpy(dvobj->cam_cache[id].mac, mac, ETH_ALEN);
	_rtw_memcpy(dvobj->cam_cache[id].key, key, 16);

	_rtw_spinunlock_bh(&cam_ctl->lock);
}

void clear_cam_cache(_adapter *adapter, u8 id)
{
	struct dvobj_priv *dvobj = adapter_to_dvobj(adapter);
	struct cam_ctl_t *cam_ctl = &dvobj->cam_ctl;

	_rtw_spinlock_bh(&cam_ctl->lock);

	_rtw_memset(&(dvobj->cam_cache[id]), 0, sizeof(struct sec_cam_ent));

	_rtw_spinunlock_bh(&cam_ctl->lock);
}

inline bool _rtw_camctl_chk_cap(_adapter *adapter, u8 cap)
{
	struct dvobj_priv *dvobj = adapter_to_dvobj(adapter);
	struct cam_ctl_t *cam_ctl = &dvobj->cam_ctl;

	if (cam_ctl->sec_cap & cap)
		return _TRUE;
	return _FALSE;
}

inline void _rtw_camctl_set_flags(_adapter *adapter, u32 flags)
{
	struct dvobj_priv *dvobj = adapter_to_dvobj(adapter);
	struct cam_ctl_t *cam_ctl = &dvobj->cam_ctl;

	cam_ctl->flags |= flags;
}

inline void rtw_camctl_set_flags(_adapter *adapter, u32 flags)
{
	struct dvobj_priv *dvobj = adapter_to_dvobj(adapter);
	struct cam_ctl_t *cam_ctl = &dvobj->cam_ctl;

	_rtw_spinlock_bh(&cam_ctl->lock);
	_rtw_camctl_set_flags(adapter, flags);
	_rtw_spinunlock_bh(&cam_ctl->lock);
}

inline void _rtw_camctl_clr_flags(_adapter *adapter, u32 flags)
{
	struct dvobj_priv *dvobj = adapter_to_dvobj(adapter);
	struct cam_ctl_t *cam_ctl = &dvobj->cam_ctl;

	cam_ctl->flags &= ~flags;
}

inline void rtw_camctl_clr_flags(_adapter *adapter, u32 flags)
{
	struct dvobj_priv *dvobj = adapter_to_dvobj(adapter);
	struct cam_ctl_t *cam_ctl = &dvobj->cam_ctl;

	_rtw_spinlock_bh(&cam_ctl->lock);
	_rtw_camctl_clr_flags(adapter, flags);
	_rtw_spinunlock_bh(&cam_ctl->lock);
}

inline bool _rtw_camctl_chk_flags(_adapter *adapter, u32 flags)
{
	struct dvobj_priv *dvobj = adapter_to_dvobj(adapter);
	struct cam_ctl_t *cam_ctl = &dvobj->cam_ctl;

	if (cam_ctl->flags & flags)
		return _TRUE;
	return _FALSE;
}

void dump_sec_cam_map(void *sel, struct sec_cam_bmp *map, u8 max_num)
{
	RTW_PRINT_SEL(sel, "0x%08x\n", map->m0);
#if (SEC_CAM_ENT_NUM_SW_LIMIT > 32)
	if (max_num && max_num > 32)
		RTW_PRINT_SEL(sel, "0x%08x\n", map->m1);
#endif
#if (SEC_CAM_ENT_NUM_SW_LIMIT > 64)
	if (max_num && max_num > 64)
		RTW_PRINT_SEL(sel, "0x%08x\n", map->m2);
#endif
#if (SEC_CAM_ENT_NUM_SW_LIMIT > 96)
	if (max_num && max_num > 96)
		RTW_PRINT_SEL(sel, "0x%08x\n", map->m3);
#endif
}

inline bool rtw_sec_camid_is_set(struct sec_cam_bmp *map, u8 id)
{
	if (id < 32)
		return map->m0 & BIT(id);
#if (SEC_CAM_ENT_NUM_SW_LIMIT > 32)
	else if (id < 64)
		return map->m1 & BIT(id - 32);
#endif
#if (SEC_CAM_ENT_NUM_SW_LIMIT > 64)
	else if (id < 96)
		return map->m2 & BIT(id - 64);
#endif
#if (SEC_CAM_ENT_NUM_SW_LIMIT > 96)
	else if (id < 128)
		return map->m3 & BIT(id - 96);
#endif
	else
		rtw_warn_on(1);

	return 0;
}

inline void rtw_sec_cam_map_set(struct sec_cam_bmp *map, u8 id)
{
	if (id < 32)
		map->m0 |= BIT(id);
#if (SEC_CAM_ENT_NUM_SW_LIMIT > 32)
	else if (id < 64)
		map->m1 |= BIT(id - 32);
#endif
#if (SEC_CAM_ENT_NUM_SW_LIMIT > 64)
	else if (id < 96)
		map->m2 |= BIT(id - 64);
#endif
#if (SEC_CAM_ENT_NUM_SW_LIMIT > 96)
	else if (id < 128)
		map->m3 |= BIT(id - 96);
#endif
	else
		rtw_warn_on(1);
}

inline void rtw_sec_cam_map_clr(struct sec_cam_bmp *map, u8 id)
{
	if (id < 32)
		map->m0 &= ~BIT(id);
#if (SEC_CAM_ENT_NUM_SW_LIMIT > 32)
	else if (id < 64)
		map->m1 &= ~BIT(id - 32);
#endif
#if (SEC_CAM_ENT_NUM_SW_LIMIT > 64)
	else if (id < 96)
		map->m2 &= ~BIT(id - 64);
#endif
#if (SEC_CAM_ENT_NUM_SW_LIMIT > 96)
	else if (id < 128)
		map->m3 &= ~BIT(id - 96);
#endif
	else
		rtw_warn_on(1);
}

inline void rtw_sec_cam_map_clr_all(struct sec_cam_bmp *map)
{
	map->m0 = 0;
#if (SEC_CAM_ENT_NUM_SW_LIMIT > 32)
	map->m1 = 0;
#endif
#if (SEC_CAM_ENT_NUM_SW_LIMIT > 64)
	map->m2 = 0;
#endif
#if (SEC_CAM_ENT_NUM_SW_LIMIT > 96)
	map->m3 = 0;
#endif
}

inline bool rtw_sec_camid_is_drv_forbid(struct cam_ctl_t *cam_ctl, u8 id)
{
	struct sec_cam_bmp forbid_map;

	forbid_map.m0 = 0x00000ff0;
#if (SEC_CAM_ENT_NUM_SW_LIMIT > 32)
	forbid_map.m1 = 0x00000000;
#endif
#if (SEC_CAM_ENT_NUM_SW_LIMIT > 64)
	forbid_map.m2 = 0x00000000;
#endif
#if (SEC_CAM_ENT_NUM_SW_LIMIT > 96)
	forbid_map.m3 = 0x00000000;
#endif

	if (id < 32)
		return forbid_map.m0 & BIT(id);
#if (SEC_CAM_ENT_NUM_SW_LIMIT > 32)
	else if (id < 64)
		return forbid_map.m1 & BIT(id - 32);
#endif
#if (SEC_CAM_ENT_NUM_SW_LIMIT > 64)
	else if (id < 96)
		return forbid_map.m2 & BIT(id - 64);
#endif
#if (SEC_CAM_ENT_NUM_SW_LIMIT > 96)
	else if (id < 128)
		return forbid_map.m3 & BIT(id - 96);
#endif
	else
		rtw_warn_on(1);

	return 1;
}

bool _rtw_sec_camid_is_used(struct cam_ctl_t *cam_ctl, u8 id)
{
	bool ret = _FALSE;

	if (id >= cam_ctl->num) {
		rtw_warn_on(1);
		goto exit;
	}

#if 0 /* for testing */
	if (rtw_sec_camid_is_drv_forbid(cam_ctl, id)) {
		ret = _TRUE;
		goto exit;
	}
#endif

	ret = rtw_sec_camid_is_set(&cam_ctl->used, id);

exit:
	return ret;
}

inline bool rtw_sec_camid_is_used(struct cam_ctl_t *cam_ctl, u8 id)
{
	bool ret;

	_rtw_spinlock_bh(&cam_ctl->lock);
	ret = _rtw_sec_camid_is_used(cam_ctl, id);
	_rtw_spinunlock_bh(&cam_ctl->lock);

	return ret;
}
u8 rtw_get_sec_camid(_adapter *adapter, u8 max_bk_key_num, u8 *sec_key_id)
{
	struct dvobj_priv *dvobj = adapter_to_dvobj(adapter);
	struct cam_ctl_t *cam_ctl = &dvobj->cam_ctl;
	int i;

	u8 sec_cam_num = 0;

	_rtw_spinlock_bh(&cam_ctl->lock);
	for (i = 0; i < cam_ctl->num; i++) {
		if (_rtw_sec_camid_is_used(cam_ctl, i)) {
			sec_key_id[sec_cam_num++] = i;
			if (sec_cam_num == max_bk_key_num)
				break;
		}
	}
	_rtw_spinunlock_bh(&cam_ctl->lock);

	return sec_cam_num;
}

inline bool _rtw_camid_is_gk(_adapter *adapter, u8 cam_id)
{
	struct dvobj_priv *dvobj = adapter_to_dvobj(adapter);
	struct cam_ctl_t *cam_ctl = &dvobj->cam_ctl;
	bool ret = _FALSE;

	if (cam_id >= cam_ctl->num) {
		rtw_warn_on(1);
		goto exit;
	}

	if (_rtw_sec_camid_is_used(cam_ctl, cam_id) == _FALSE)
		goto exit;

	ret = (dvobj->cam_cache[cam_id].ctrl & BIT6) ? _TRUE : _FALSE;

exit:
	return ret;
}

inline bool rtw_camid_is_gk(_adapter *adapter, u8 cam_id)
{
	struct dvobj_priv *dvobj = adapter_to_dvobj(adapter);
	struct cam_ctl_t *cam_ctl = &dvobj->cam_ctl;
	bool ret;

	_rtw_spinlock_bh(&cam_ctl->lock);
	ret = _rtw_camid_is_gk(adapter, cam_id);
	_rtw_spinunlock_bh(&cam_ctl->lock);

	return ret;
}

bool cam_cache_chk(_adapter *adapter, u8 id, u8 *addr, s16 kid, s8 gk)
{
	struct dvobj_priv *dvobj = adapter_to_dvobj(adapter);
	bool ret = _FALSE;

	if (addr && _rtw_memcmp(dvobj->cam_cache[id].mac, addr, ETH_ALEN) == _FALSE)
		goto exit;
	if (kid >= 0 && kid != (dvobj->cam_cache[id].ctrl & 0x03))
		goto exit;
	if (gk != -1 && (gk ? _TRUE : _FALSE) != _rtw_camid_is_gk(adapter, id))
		goto exit;

	ret = _TRUE;

exit:
	return ret;
}

s16 _rtw_camid_search(_adapter *adapter, u8 *addr, s16 kid, s8 gk)
{
	struct dvobj_priv *dvobj = adapter_to_dvobj(adapter);
	struct cam_ctl_t *cam_ctl = &dvobj->cam_ctl;
	int i;
	s16 cam_id = -1;

	for (i = 0; i < cam_ctl->num; i++) {
		if (cam_cache_chk(adapter, i, addr, kid, gk)) {
			cam_id = i;
			break;
		}
	}

	if (0) {
		if (addr)
			RTW_INFO(FUNC_ADPT_FMT" addr:"MAC_FMT" kid:%d, gk:%d, return cam_id:%d\n"
				, FUNC_ADPT_ARG(adapter), MAC_ARG(addr), kid, gk, cam_id);
		else
			RTW_INFO(FUNC_ADPT_FMT" addr:%p kid:%d, gk:%d, return cam_id:%d\n"
				, FUNC_ADPT_ARG(adapter), addr, kid, gk, cam_id);
	}

	return cam_id;
}

s16 rtw_camid_search(_adapter *adapter, u8 *addr, s16 kid, s8 gk)
{
	struct dvobj_priv *dvobj = adapter_to_dvobj(adapter);
	struct cam_ctl_t *cam_ctl = &dvobj->cam_ctl;
	s16 cam_id = -1;

	_rtw_spinlock_bh(&cam_ctl->lock);
	cam_id = _rtw_camid_search(adapter, addr, kid, gk);
	_rtw_spinunlock_bh(&cam_ctl->lock);

	return cam_id;
}

s16 rtw_get_camid(_adapter *adapter, u8 *addr, s16 kid, u8 gk, bool ext_sec)
{
	struct dvobj_priv *dvobj = adapter_to_dvobj(adapter);
	struct cam_ctl_t *cam_ctl = &dvobj->cam_ctl;
	int i;
#if 0 /* for testing */
	static u8 start_id = 0;
#else
	u8 start_id = 0;
#endif
	s16 cam_id = -1;

	if (addr == NULL) {
		RTW_PRINT(FUNC_ADPT_FMT" mac_address is NULL\n"
			  , FUNC_ADPT_ARG(adapter));
		rtw_warn_on(1);
		goto _exit;
	}

	/* find cam entry which has the same addr, kid (, gk bit) */
	if (_rtw_camctl_chk_cap(adapter, SEC_CAP_CHK_BMC) == _TRUE)
		i = _rtw_camid_search(adapter, addr, kid, gk);
	else
		i = _rtw_camid_search(adapter, addr, kid, -1);

	if (i >= 0) {
		cam_id = i;
		goto _exit;
	}

	for (i = 0; i < cam_ctl->num; i++) {
		/* bypass default key which is allocated statically */
#if 0//ndef CONFIG_CONCURRENT_MODE
		if (((i + start_id) % cam_ctl->num) < 4)
			continue;
#endif
		if (_rtw_sec_camid_is_used(cam_ctl, ((i + start_id) % cam_ctl->num)) == _FALSE) {
			if (ext_sec) {
				/* look out continue slot */
				if (((i + 1) < cam_ctl->num) &&
					(_rtw_sec_camid_is_used(cam_ctl, (((i + 1) + start_id) % cam_ctl->num)) == _FALSE))
			break;
				else
					continue;
			} else
				break;
		}
	}

	if (i == cam_ctl->num) {
		RTW_PRINT(FUNC_ADPT_FMT" %s key with "MAC_FMT" id:%u no room\n"
			, FUNC_ADPT_ARG(adapter), gk ? "group" : "pairwise", MAC_ARG(addr), kid);
		rtw_warn_on(1);
		goto _exit;
	}

	cam_id = ((i + start_id) % cam_ctl->num);
	start_id = ((i + start_id + 1) % cam_ctl->num);

_exit:
	return cam_id;
}

s16 rtw_camid_alloc(_adapter *adapter, struct sta_info *sta, u8 kid, u8 gk, bool ext_sec, bool *used)
{
	struct mlme_ext_info *mlmeinfo = &adapter->mlmeextpriv.mlmext_info;
	struct dvobj_priv *dvobj = adapter_to_dvobj(adapter);
	struct cam_ctl_t *cam_ctl = &dvobj->cam_ctl;
	s16 cam_id = -1;

	*used = _FALSE;

	_rtw_spinlock_bh(&cam_ctl->lock);

	if ((((mlmeinfo->state & 0x03) == WIFI_FW_AP_STATE) || ((mlmeinfo->state & 0x03) == WIFI_FW_ADHOC_STATE))
	    && !sta) {
		/*
		* 1. non-STA mode WEP key
		* 2. group TX key
		*/
#if 0//ndef CONFIG_CONCURRENT_MODE
		/* static alloction to default key by key ID when concurrent is not defined */
		if (kid > 3) {
			RTW_PRINT(FUNC_ADPT_FMT" group key with invalid key id:%u\n"
				  , FUNC_ADPT_ARG(adapter), kid);
			rtw_warn_on(1);
			goto bitmap_handle;
		}
		cam_id = kid;
#else
		u8 *addr = adapter_mac_addr(adapter);

		cam_id = rtw_get_camid(adapter, addr, kid, gk, ext_sec);
		if (1)
			RTW_PRINT(FUNC_ADPT_FMT" group key with "MAC_FMT" assigned cam_id:%u\n"
				, FUNC_ADPT_ARG(adapter), MAC_ARG(addr), cam_id);
#endif
	} else {
		/*
		* 1. STA mode WEP key
		* 2. STA mode group RX key
		* 3. sta key (pairwise, group RX)
		*/
		u8 *addr = sta ? sta->phl_sta->mac_addr : NULL;

		if (!sta) {
			if (!(mlmeinfo->state & WIFI_FW_ASSOC_SUCCESS)) {
				/* bypass STA mode group key setting before connected(ex:WEP) because bssid is not ready */
				goto bitmap_handle;
			}
			addr = get_bssid(&adapter->mlmepriv);/*A2*/
		}
		cam_id = rtw_get_camid(adapter, addr, kid, gk, ext_sec);
	}


bitmap_handle:
	if (cam_id >= 0) {
		*used = _rtw_sec_camid_is_used(cam_ctl, cam_id);
		rtw_sec_cam_map_set(&cam_ctl->used, cam_id);
		if (ext_sec)
			rtw_sec_cam_map_set(&cam_ctl->used, cam_id + 1);
	}

	_rtw_spinunlock_bh(&cam_ctl->lock);

	return cam_id;
}

void rtw_camid_set(_adapter *adapter, u8 cam_id)
{
	struct dvobj_priv *dvobj = adapter_to_dvobj(adapter);
	struct cam_ctl_t *cam_ctl = &dvobj->cam_ctl;

	_rtw_spinlock_bh(&cam_ctl->lock);

	if (cam_id < cam_ctl->num)
		rtw_sec_cam_map_set(&cam_ctl->used, cam_id);

	_rtw_spinunlock_bh(&cam_ctl->lock);
}

void rtw_camid_free(_adapter *adapter, u8 cam_id)
{
	struct dvobj_priv *dvobj = adapter_to_dvobj(adapter);
	struct cam_ctl_t *cam_ctl = &dvobj->cam_ctl;

	_rtw_spinlock_bh(&cam_ctl->lock);

	if (cam_id < cam_ctl->num)
		rtw_sec_cam_map_clr(&cam_ctl->used, cam_id);

	_rtw_spinunlock_bh(&cam_ctl->lock);
}

/*Must pause TX/RX before use this API*/
inline void rtw_sec_cam_swap(_adapter *adapter, u8 cam_id_a, u8 cam_id_b)
{
	struct dvobj_priv *dvobj = adapter_to_dvobj(adapter);
	struct cam_ctl_t *cam_ctl = &dvobj->cam_ctl;
	struct sec_cam_ent cache_a, cache_b;
	bool cam_a_used, cam_b_used;

	if (1)
		RTW_INFO(ADPT_FMT" - sec_cam %d,%d swap\n", ADPT_ARG(adapter), cam_id_a, cam_id_b);

	if (cam_id_a == cam_id_b)
		return;

	/*setp-1. backup org cam_info*/
	_rtw_spinlock_bh(&cam_ctl->lock);

	cam_a_used = _rtw_sec_camid_is_used(cam_ctl, cam_id_a);
	cam_b_used = _rtw_sec_camid_is_used(cam_ctl, cam_id_b);

	if (cam_a_used)
		_rtw_memcpy(&cache_a, &dvobj->cam_cache[cam_id_a], sizeof(struct sec_cam_ent));

	if (cam_b_used)
		_rtw_memcpy(&cache_b, &dvobj->cam_cache[cam_id_b], sizeof(struct sec_cam_ent));

	_rtw_spinunlock_bh(&cam_ctl->lock);

	/*setp-2. clean cam_info*/
	if (cam_a_used) {
		rtw_camid_free(adapter, cam_id_a);
		clear_cam_entry(adapter, cam_id_a);
	}
	if (cam_b_used) {
		rtw_camid_free(adapter, cam_id_b);
		clear_cam_entry(adapter, cam_id_b);
	}

	/*setp-3. set cam_info*/
	if (cam_a_used) {
		write_cam(adapter, cam_id_b, cache_a.ctrl, cache_a.mac, cache_a.key);
		rtw_camid_set(adapter, cam_id_b);
	}

	if (cam_b_used) {
		write_cam(adapter, cam_id_a, cache_b.ctrl, cache_b.mac, cache_b.key);
		rtw_camid_set(adapter, cam_id_a);
	}
}

s16 rtw_get_empty_cam_entry(_adapter *adapter, u8 start_camid)
{
	struct dvobj_priv *dvobj = adapter_to_dvobj(adapter);
	struct cam_ctl_t *cam_ctl = &dvobj->cam_ctl;
	int i;
	s16 cam_id = -1;

	_rtw_spinlock_bh(&cam_ctl->lock);
	for (i = start_camid; i < cam_ctl->num; i++) {
		if (_FALSE == _rtw_sec_camid_is_used(cam_ctl, i)) {
			cam_id = i;
			break;
		}
	}
	_rtw_spinunlock_bh(&cam_ctl->lock);

	return cam_id;
}
void rtw_clean_dk_section(_adapter *adapter)
{
	struct dvobj_priv *dvobj = adapter_to_dvobj(adapter);
	struct cam_ctl_t *cam_ctl = dvobj_to_sec_camctl(dvobj);
	s16 ept_cam_id;
	int i;

	for (i = 0; i < 4; i++) {
		if (rtw_sec_camid_is_used(cam_ctl, i)) {
			ept_cam_id = rtw_get_empty_cam_entry(adapter, 4);
			if (ept_cam_id > 0)
				rtw_sec_cam_swap(adapter, i, ept_cam_id);
		}
	}
}
void rtw_clean_hw_dk_cam(_adapter *adapter)
{
	int i;

	for (i = 0; i < 4; i++)
		rtw_hal_sec_clr_cam_ent(adapter, i);
		/*_clear_cam_entry(adapter, i);*/
}

void flush_all_cam_entry(struct _ADAPTER *a, enum phl_cmd_type cmd_type, u32 cmd_timeout)
{
	struct mlme_ext_info *pmlmeinfo = &a->mlmeextpriv.mlmext_info;
	struct sta_priv	*stapriv = &a->stapriv;
	u8 *mac;
	struct sta_info	*sta;


	if (MLME_IS_STA(a)) {
		mac = pmlmeinfo->network.MacAddress;
		sta = rtw_get_stainfo(stapriv, mac);
		if (sta) {
			if (sta->state & WIFI_AP_STATE)
				/*clear cam when ap free per sta_info*/
				RTW_INFO("%s: sta->state(0x%x) is AP, "
					 "do nothing\n",
					 __func__, sta->state);
			else
				rtw_hw_del_all_key(a, sta, cmd_type, cmd_timeout);
		} else {
			RTW_WARN("%s: cann't find sta for %pM\n", __func__, mac);
			rtw_warn_on(1);
		}
	} else if (MLME_IS_AP(a) || MLME_IS_MESH(a)) {
		mac = adapter_mac_addr(a);
		sta = rtw_get_stainfo(stapriv, mac);
		if (sta)
			rtw_hw_del_all_key(a, sta, cmd_type, cmd_timeout);
	}
}

#if defined(DBG_CONFIG_ERROR_RESET) && defined(CONFIG_CONCURRENT_MODE)
void rtw_iface_bcmc_sec_cam_map_restore(_adapter *adapter)
{
	struct dvobj_priv *dvobj = adapter_to_dvobj(adapter);
	struct cam_ctl_t *cam_ctl = dvobj_to_sec_camctl(dvobj);
	int cam_id = -1;

	cam_id = rtw_phl_wrole_bcmc_id_get(GET_PHL_INFO(dvobj), adapter->phl_role);
	if (cam_id != INVALID_SEC_MAC_CAM_ID)
		rtw_sec_cam_map_set(&cam_ctl->used, cam_id);
}
#endif

#ifdef  CONFIG_DBG_AX_CAM
/*
1. Mode 0 : no key
2. Mode 1 : unicast	0 - 6
3. Mode 2 : unicast	0 - 1	Group: 2 - 4	BIP: 5 - 6
4. Mode 3 : unicast	0 - 1	Group: 2 - 5	BIP: 6
*/
static enum CAM_KEY_TYPE search_keytype_by_mode(u8 mode, u8 entry_num)
{
	enum CAM_KEY_TYPE key_type = KEY_TYPE_NONE;

	switch (mode) {
	case 0:
		key_type = KEY_TYPE_NONE;
		break;
	case 1:
		key_type = KEY_TYPE_UNI;
		break;
	case 2:
		if (entry_num < 2)
			key_type = KEY_TYPE_UNI;
		else if (entry_num < 5)
			key_type = KEY_TYPE_GROUP;
		else if (entry_num < 7)
			key_type = KEY_TYPE_BIP;
		else
			key_type = KEY_TYPE_NONE;
		break;
	case 3:
		if (entry_num < 2)
			key_type = KEY_TYPE_UNI;
		else if (entry_num < 6)
			key_type = KEY_TYPE_GROUP;
		else if (entry_num < 7)
			key_type = KEY_TYPE_BIP;
		else
			key_type = KEY_TYPE_NONE;
		break;
	default:
		key_type = KEY_TYPE_NONE;
		break;
	}
	return key_type;
}

static const char* enc_algo_to_string[] = {
	"RTW_ENC_NONE",
	"RTW_ENC_WEP40",
	"RTW_ENC_WEP104",
	"RTW_ENC_TKIP",
	"RTW_ENC_WAPI",
	"RTW_ENC_GCMSMS4",
	"RTW_ENC_CCMP",
	"RTW_ENC_CCMP256",
	"RTW_ENC_GCMP",
	"RTW_ENC_GCMP256",
	"RTW_ENC_BIP_CCMP128",
	"RTW_ENC_MAX"
};

static const char* type_to_string[] = {
	"unicast",
	"multicast",
	"BIP",
	"NONE"
};

static const char* nettype_to_string[] = {
	"NoLink",
	"Ad-hoc",
	"Infra",
	"AP"
};

static u8 get_keyid(u8 num, u8* addr_cam)
{
	u8 keyid = 0;

	switch (num) {
	case 0:
		keyid = GET_AX_ADDR_CAM_SEC_ENT0_KEYID(addr_cam);
		break;
	case 1:
		keyid = GET_AX_ADDR_CAM_SEC_ENT1_KEYID(addr_cam);
		break;
	case 2:
		keyid = GET_AX_ADDR_CAM_SEC_ENT2_KEYID(addr_cam);
		break;
	case 3:
		keyid = GET_AX_ADDR_CAM_SEC_ENT3_KEYID(addr_cam);
		break;
	case 4:
		keyid = GET_AX_ADDR_CAM_SEC_ENT4_KEYID(addr_cam);
		break;
	case 5:
		keyid = GET_AX_ADDR_CAM_SEC_ENT5_KEYID(addr_cam);
		break;
	case 6:
		keyid = GET_AX_ADDR_CAM_SEC_ENT6_KEYID(addr_cam);
		break;
	default:
		keyid = 0;
		break;
	}
	return keyid;
}

static u8 get_sec_entry(u8 num, u8* addr_cam)
{
	u8 entry = 0;

	switch (num) {
	case 0:
		entry = GET_AX_ADDR_CAM_SEC_ENT0(addr_cam);
		break;
	case 1:
		entry = GET_AX_ADDR_CAM_SEC_ENT1(addr_cam);
		break;
	case 2:
		entry = GET_AX_ADDR_CAM_SEC_ENT2(addr_cam);
		break;
	case 3:
		entry = GET_AX_ADDR_CAM_SEC_ENT3(addr_cam);
		break;
	case 4:
		entry = GET_AX_ADDR_CAM_SEC_ENT4(addr_cam);
		break;
	case 5:
		entry = GET_AX_ADDR_CAM_SEC_ENT5(addr_cam);
		break;
	case 6:
		entry = GET_AX_ADDR_CAM_SEC_ENT6(addr_cam);
		break;
	default:
		entry = 0;
		break;
	}
	return entry;
}

static void dump_cam_info(void* sel, u8 *buf, u32 start, u32 end)
{
	int i, j = 1;

	for (i = start; i < end; i += 1) {
		if (j % 4 == 1)
			RTW_PRINT_SEL(sel,"0x%04x", i);
		RTW_PRINT_SEL(sel," 0x%08x ", *(u32 *)(buf+ (i*4)));
		if ((j++) % 4 == 0)
			RTW_PRINT_SEL(sel,"\n");
	}
}

static void search_sec_cam_by_entry(struct _ADAPTER *a, u8 num, u8* target_entry)
{
	struct dvobj_priv *dvobj = adapter_to_dvobj(a);
	void *phl;
	u32 sec_tmp_buf = 0;
	int i;

	phl = GET_PHL_INFO(dvobj);
	rtw_phl_write32(phl, INDIRECT_ACCESS_ADDR, (SEC_CAM_BASE_ADDR + num*32));

	for (i = 0; i < 5; i++) {
		sec_tmp_buf = rtw_phl_read32(phl,INDIRECT_ACCESS_VALUE + i*4);
		_rtw_memcpy((target_entry + i*4), &sec_tmp_buf, 4);
	}
}

static void search_bssid_cam_by_entry(struct _ADAPTER *a, u8 num, u8* target_entry)
{
	struct dvobj_priv *dvobj = adapter_to_dvobj(a);
	void *phl;
	u32 BSSID_tmp_buf = 0;
	int i;

	phl = GET_PHL_INFO(dvobj);
	rtw_phl_write32(phl, INDIRECT_ACCESS_ADDR, (BSSID_CAM_BASE_ADDR+ num*8));

	for (i = 0 ; i < 2; i++) {
		BSSID_tmp_buf = rtw_phl_read32(phl,INDIRECT_ACCESS_VALUE + i*4);
		_rtw_memcpy((target_entry + i*4), &BSSID_tmp_buf, 4);
	}
}

static u8 varify_256_len(u8 enc)
{
	u8 hit = _FALSE;

	if (enc == 4|| enc == 7|| enc == 9)
		hit = _TRUE;
	return  hit;
}

int get_ax_address_cam(void* sel, struct _ADAPTER *a)
{
	struct dvobj_priv *dvobj = adapter_to_dvobj(a);
	void *phl;
	/*addr cam: 128 entries*/
	int loop_num = 128;
	/*addr cam: 11 DW but the last one all rsvd*/
	u32 addr_tmp_buf = 0;
	u8  addr_map[40] = {0};
	int i, j;

	phl = GET_PHL_INFO(dvobj);

	for (i = 0; i < loop_num; i++) {
		_rtw_memset(addr_map,0,sizeof(addr_map));
		rtw_phl_write32(phl, INDIRECT_ACCESS_ADDR, (ADDR_CAM_BASE_ADDR + i*64));

		for (j = 0; j < 10; j++) {
			addr_tmp_buf = rtw_phl_read32(phl,INDIRECT_ACCESS_VALUE + j*4);
			_rtw_memcpy(&addr_map[j*4], &addr_tmp_buf, 4);
		}
		RTW_PRINT_SEL(sel, "======= ADDR CAM (%d)DUMP  =======\n", i);
		dump_cam_info(sel,addr_map,0,10);
		RTW_PRINT_SEL(sel, "\n");
	}
	return 0;
}

int get_ax_sec_cam(void* sel, struct _ADAPTER *a)
{
	struct dvobj_priv *dvobj = adapter_to_dvobj(a);
	void *phl;
	/*security cam: 128 entries*/
	int loop_num = 128;
	/*security cam: 5 DW*/
	u32 sec_tmp_buf = 0;
	u8  sec_map[20] = {0};
	int i, j;

	phl = GET_PHL_INFO(dvobj);

	for (i = 0; i < loop_num; i++) {
		_rtw_memset(sec_map,0,sizeof(sec_map));
		rtw_phl_write32(phl, INDIRECT_ACCESS_ADDR, (SEC_CAM_BASE_ADDR + i*32));

		for (j = 0; j < 5; j++) {
			sec_tmp_buf = rtw_phl_read32(phl,INDIRECT_ACCESS_VALUE + j*4);
			_rtw_memcpy(&sec_map[j*4], &sec_tmp_buf, 4);
		}

		RTW_PRINT_SEL(sel, "======= sec CAM (%d)DUMP  =======\n", i);
		dump_cam_info(sel, sec_map, 0, 5);
		RTW_PRINT_SEL(sel, "\n");
	}
	return 0;
}

static void dump_valid_key(void* sel, u8* addr_map, u8* sec_map, u8* bssid_map\
		, u8 sec_entry, u8 key_type, u8 keyid)
{
	u8 SMA[ETH_ALEN] = {0}, TMA[ETH_ALEN] = {0};
	u8 macid = 0, nettype = 0;

	macid = GET_AX_ADDR_CAM_MACID(addr_map);
	nettype = GET_AX_ADDR_CAM_NET_TYPE(addr_map);

	_rtw_memcpy(SMA, &addr_map[8], ETH_ALEN);
	_rtw_memcpy(TMA,&addr_map[14], ETH_ALEN);
	RTW_PRINT_SEL(sel, "%-5u %s "MAC_FMT"  " MAC_FMT" " MAC_FMT\
		" %-5u %-5u %s %-7u %-3u %s "KEY_FMT"\n", macid\
		, nettype_to_string[nettype], MAC_ARG(SMA), MAC_ARG(TMA)\
		, MAC_ARG(&bssid_map[2]), keyid, sec_entry\
		, enc_algo_to_string[GET_AX_SEC_CAM_TYPE(sec_map)] \
		, GET_AX_SEC_CAM_EXT_KEY(sec_map), GET_AX_SEC_SPP_MODE_(sec_map)\
		, type_to_string[key_type], KEY_ARG(&sec_map[4]));
}

static void prepare_to_dump_valid_key(void* sel, struct _ADAPTER *a, u8* addr_map, u8 ent)
{
	u8 sec_map[20] = {0};
	u8 keyid = 0, sec_entry = 0, key_type = KEY_TYPE_NONE;
	u8 bssid_entry;
	u8 bssid_map[8] = {0};
	u8 enc_algor = 0;

	key_type = search_keytype_by_mode(GET_AX_ADDR_CAM_SEC_ENT_MODE(addr_map) ,ent);
	sec_entry = get_sec_entry(ent, addr_map);
	bssid_entry = GET_AX_ADDR_CAM_BSSID_CAM_IDX(addr_map);
	keyid = get_keyid(ent,addr_map);
	search_sec_cam_by_entry(a, sec_entry, sec_map);
	search_bssid_cam_by_entry(a, bssid_entry, bssid_map);
	enc_algor = GET_AX_SEC_CAM_TYPE(sec_map);

	if (varify_256_len(enc_algor) == _TRUE) {
		dump_valid_key(sel, addr_map, sec_map, bssid_map, sec_entry, key_type, keyid);
		search_sec_cam_by_entry(a, sec_entry + 1, sec_map);
		dump_valid_key(sel, addr_map, sec_map, bssid_map, sec_entry + 1, key_type, keyid);
	} else {
		dump_valid_key(sel, addr_map, sec_map, bssid_map, sec_entry, key_type, keyid);
	}
}

int get_ax_valid_key(void* sel, struct _ADAPTER *a)
{
	struct dvobj_priv *dvobj = adapter_to_dvobj(a);
	void *phl;
	/*addr cam: 128 entries*/
	int loop_num = 128;
	/*addr cam: 11 DW but the last one all rsvd*/
	u32 addr_tmp_buf = 0;
	u8 addr_map[40]= {0};
	u8 sec_ent_valid = 0;
	u8 macid = 0;
	int i, j;

	phl = GET_PHL_INFO(dvobj);

	RTW_PRINT_SEL(sel, "Dump valid KEY\n");
	RTW_PRINT_SEL(sel, "%-5s %-7s %-17s %-17s %-17s %-5s %-5s %-8s %-3s"\
		"  %-3s  %s  %s\n", "macid", "NETTYPE", "SMA", "TMA", "BSSID", "keyid"\
		, "sec_entry", "enc_algor", "ext_key", "ssp", "key_type", "key");

	for (i = 0; i < loop_num; i++) {
		_rtw_memset(addr_map,0,sizeof(addr_map));
		rtw_phl_write32(phl, INDIRECT_ACCESS_ADDR, (ADDR_CAM_BASE_ADDR + i*64));

		for (j = 0; j < 10; j++) {
			addr_tmp_buf = rtw_phl_read32(phl,INDIRECT_ACCESS_VALUE + j*4);
			_rtw_memcpy(&addr_map[j*4], &addr_tmp_buf, 4);
		}
		sec_ent_valid = GET_AX_ADDR_CAM_SEC_ENT_VALID(addr_map);

		for (j = 0 ;j< 7; j++) {
			if (sec_ent_valid & BIT(j))
				prepare_to_dump_valid_key(sel, a, addr_map, j);
		}
	}
	return 0;
}
#endif /* CONFIG_DBG_AX_CAM */

