/******************************************************************************
 *
 * Copyright(c) 2007 - 2021 Realtek Corporation.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of version 2 of the GNU General Public License as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 *****************************************************************************/
#define _RTW_CMD_C_

#include <drv_types.h>

#ifndef DBG_CMD_EXECUTE
	#define DBG_CMD_EXECUTE 0
#endif

/*
Caller and the rtw_cmd_thread can protect cmd_q by spin_lock.
No irqsave is necessary.
*/
u32 rtw_init_cmd_priv(struct dvobj_priv *dvobj)
{
	u32 res = _SUCCESS;
	struct cmd_priv *pcmdpriv = &dvobj->cmdpriv;

	pcmdpriv->dvobj = dvobj;
	#if 0 /*#ifdef CONFIG_CORE_CMD_THREAD*/
	_rtw_init_sema(&(pcmdpriv->cmd_queue_sema), 0);
	_rtw_init_sema(&(pcmdpriv->start_cmdthread_sema), 0);
	_rtw_init_queue(&(pcmdpriv->cmd_queue));
	#endif

	/* allocate DMA-able/Non-Page memory for cmd_buf and rsp_buf */

	pcmdpriv->cmd_seq = 1;

	pcmdpriv->cmd_allocated_buf = rtw_zmalloc(MAX_CMDSZ + CMDBUFF_ALIGN_SZ);

	if (pcmdpriv->cmd_allocated_buf == NULL) {
		res = _FAIL;
		goto exit;
	}

	pcmdpriv->cmd_buf = pcmdpriv->cmd_allocated_buf + CMDBUFF_ALIGN_SZ - ((SIZE_PTR)(pcmdpriv->cmd_allocated_buf) & (CMDBUFF_ALIGN_SZ - 1));

	pcmdpriv->rsp_allocated_buf = rtw_zmalloc(MAX_RSPSZ + 4);

	if (pcmdpriv->rsp_allocated_buf == NULL) {
		res = _FAIL;
		goto exit;
	}

	pcmdpriv->rsp_buf = pcmdpriv->rsp_allocated_buf  +  4 - ((SIZE_PTR)(pcmdpriv->rsp_allocated_buf) & 3);

	pcmdpriv->cmd_issued_cnt = 0;

	_rtw_mutex_init(&pcmdpriv->sctx_mutex);

	ATOMIC_SET(&pcmdpriv->event_seq, 0);
	pcmdpriv->evt_done_cnt = 0;
exit:
	return res;

}

void rtw_free_cmd_priv(struct dvobj_priv *dvobj)
{
	struct cmd_priv *pcmdpriv = &dvobj->cmdpriv;

	#if 0 /*#ifdef CONFIG_CORE_CMD_THREAD*/
	_rtw_spinlock_free(&(pcmdpriv->cmd_queue.lock));
	_rtw_free_sema(&(pcmdpriv->cmd_queue_sema));
	_rtw_free_sema(&(pcmdpriv->start_cmdthread_sema));
	#endif
	if (pcmdpriv->cmd_allocated_buf)
		rtw_mfree(pcmdpriv->cmd_allocated_buf, MAX_CMDSZ + CMDBUFF_ALIGN_SZ);

	if (pcmdpriv->rsp_allocated_buf)
		rtw_mfree(pcmdpriv->rsp_allocated_buf, MAX_RSPSZ + 4);

	_rtw_mutex_free(&pcmdpriv->sctx_mutex);
}

static int rtw_cmd_filter(struct cmd_priv *pcmdpriv, struct cmd_obj *cmd_obj)
{
	u8 bAllow = _FALSE; /* set to _TRUE to allow enqueuing cmd when hw_init_completed is _FALSE */
	struct dvobj_priv *dvobj = pcmdpriv->dvobj;


	if (cmd_obj->cmdcode == CMD_SET_CHANPLAN)
		bAllow = _TRUE;

	if (cmd_obj->no_io)
		bAllow = _TRUE;

	return _SUCCESS;
}

/*
Calling Context:

rtw_enqueue_cmd can only be called between kernel thread,
since only spin_lock is used.

ISR/Call-Back functions can't call this sub-function.

*/
#ifdef DBG_CMD_QUEUE
extern u8 dump_cmd_id;
#endif

#if 0 /*#ifdef CONFIG_CORE_CMD_THREAD*/
static sint _rtw_enqueue_cmd(_queue *queue, struct cmd_obj *obj, bool to_head)
{
	unsigned long sp_flags;

	if (obj == NULL)
		goto exit;

	/* _rtw_spinlock_bh(&queue->lock); */
	_rtw_spinlock_irq(&queue->lock, &sp_flags);

	if (to_head)
		rtw_list_insert_head(&obj->list, &queue->queue);
	else
		rtw_list_insert_tail(&obj->list, &queue->queue);

#ifdef DBG_CMD_QUEUE
	if (dump_cmd_id) {
		RTW_INFO("%s===> cmdcode:0x%02x\n", __FUNCTION__, obj->cmdcode);
		if (obj->cmdcode == CMD_SET_MLME_EVT) {
			if (obj->parmbuf) {
				struct rtw_evt_header *evt_hdr = (struct rtw_evt_header *)(obj->parmbuf);
				RTW_INFO("evt_hdr->id:%d\n", evt_hdr->id);
			}
		}
		if (obj->cmdcode == CMD_SET_DRV_EXTRA) {
			if (obj->parmbuf) {
				struct drvextra_cmd_parm *pdrvextra_cmd_parm = (struct drvextra_cmd_parm *)(obj->parmbuf);
				RTW_INFO("pdrvextra_cmd_parm->ec_id:0x%02x\n", pdrvextra_cmd_parm->ec_id);
			}
		}
	}

	if (queue->queue.prev->next != &queue->queue) {
		RTW_INFO("[%d] head %p, tail %p, tail->prev->next %p[tail], tail->next %p[head]\n", __LINE__,
			&queue->queue, queue->queue.prev, queue->queue.prev->prev->next, queue->queue.prev->next);

		RTW_INFO("==========%s============\n", __FUNCTION__);
		RTW_INFO("head:%p,obj_addr:%p\n", &queue->queue, obj);
		RTW_INFO("padapter: %p\n", obj->padapter);
		RTW_INFO("cmdcode: 0x%02x\n", obj->cmdcode);
		RTW_INFO("res: %d\n", obj->res);
		RTW_INFO("parmbuf: %p\n", obj->parmbuf);
		RTW_INFO("cmdsz: %d\n", obj->cmdsz);
		RTW_INFO("rsp: %p\n", obj->rsp);
		RTW_INFO("rspsz: %d\n", obj->rspsz);
		RTW_INFO("sctx: %p\n", obj->sctx);
		RTW_INFO("list->next: %p\n", obj->list.next);
		RTW_INFO("list->prev: %p\n", obj->list.prev);
	}
#endif /* DBG_CMD_QUEUE */

	/* _rtw_spinunlock_bh(&queue->lock);	 */
	_rtw_spinunlock_irq(&queue->lock, &sp_flags);

exit:


	return _SUCCESS;
}
#else
static sint _rtw_enqueue_cmd(struct cmd_obj *obj, bool to_head)
{
	u32 res;

	res = rtw_enqueue_phl_cmd(obj);

#ifdef DBG_CMD_QUEUE
	if (dump_cmd_id) {
		RTW_INFO("%s===> cmdcode:0x%02x\n", __FUNCTION__, obj->cmdcode);
		if (obj->cmdcode == CMD_SET_MLME_EVT) {
			if (obj->parmbuf) {
				struct rtw_evt_header *evt_hdr = (struct rtw_evt_header *)(obj->parmbuf);
				RTW_INFO("evt_hdr->id:%d\n", evt_hdr->id);
			}
		}
		if (obj->cmdcode == CMD_SET_DRV_EXTRA) {
			if (obj->parmbuf) {
				struct drvextra_cmd_parm *pdrvextra_cmd_parm = (struct drvextra_cmd_parm *)(obj->parmbuf);
				RTW_INFO("pdrvextra_cmd_parm->ec_id:0x%02x\n", pdrvextra_cmd_parm->ec_id);
			}
		}
	}
#endif /* DBG_CMD_QUEUE */
	return res;
}
#endif

u32 rtw_enqueue_cmd(struct cmd_priv *pcmdpriv, struct cmd_obj *cmd_obj)
{
	int res = _FAIL;

	if (cmd_obj == NULL)
		goto exit;

	res = rtw_cmd_filter(pcmdpriv, cmd_obj);
	if ((_FAIL == res) || (cmd_obj->cmdsz > MAX_CMDSZ)) {
		if (cmd_obj->cmdsz > MAX_CMDSZ) {
			RTW_INFO("%s failed due to obj->cmdsz(%d) > MAX_CMDSZ(%d)\n", __func__, cmd_obj->cmdsz, MAX_CMDSZ);
			rtw_warn_on(1);
		}

		if (cmd_obj->cmdcode == CMD_SET_DRV_EXTRA) {
			struct drvextra_cmd_parm *extra_parm = (struct drvextra_cmd_parm *)cmd_obj->parmbuf;

			if (extra_parm->pbuf && extra_parm->size > 0)
				rtw_mfree(extra_parm->pbuf, extra_parm->size);
		}
		rtw_free_cmd_obj(cmd_obj);
		goto exit;
	}


	res = _rtw_enqueue_cmd(cmd_obj, 0);
	#if 0 /*#ifdef CONFIG_CORE_CMD_THREAD*/
	if (res == _SUCCESS)
		_rtw_up_sema(&pcmdpriv->cmd_queue_sema);
	#endif

exit:
	return res;
}

#if 0 /*#ifdef CONFIG_CORE_CMD_THREAD*/
struct	cmd_obj	*_rtw_dequeue_cmd(_queue *queue)
{
	struct cmd_obj *obj;
	unsigned long sp_flags;

	/* _rtw_spinlock_bh(&(queue->lock)); */
	_rtw_spinlock_irq(&queue->lock, &sp_flags);

#ifdef DBG_CMD_QUEUE
	if (queue->queue.prev->next != &queue->queue) {
		RTW_INFO("[%d] head %p, tail %p, tail->prev->next %p[tail], tail->next %p[head]\n", __LINE__,
			&queue->queue, queue->queue.prev, queue->queue.prev->prev->next, queue->queue.prev->next);
	}
#endif /* DBG_CMD_QUEUE */


	if (rtw_is_list_empty(&(queue->queue)))
		obj = NULL;
	else {
		obj = LIST_CONTAINOR(get_next(&(queue->queue)), struct cmd_obj, list);

#ifdef DBG_CMD_QUEUE
		if (queue->queue.prev->next != &queue->queue) {
			RTW_INFO("==========%s============\n", __FUNCTION__);
			RTW_INFO("head:%p,obj_addr:%p\n", &queue->queue, obj);
			RTW_INFO("padapter: %p\n", obj->padapter);
			RTW_INFO("cmdcode: 0x%02x\n", obj->cmdcode);
			RTW_INFO("res: %d\n", obj->res);
			RTW_INFO("parmbuf: %p\n", obj->parmbuf);
			RTW_INFO("cmdsz: %d\n", obj->cmdsz);
			RTW_INFO("rsp: %p\n", obj->rsp);
			RTW_INFO("rspsz: %d\n", obj->rspsz);
			RTW_INFO("sctx: %p\n", obj->sctx);
			RTW_INFO("list->next: %p\n", obj->list.next);
			RTW_INFO("list->prev: %p\n", obj->list.prev);
		}

		if (dump_cmd_id) {
			RTW_INFO("%s===> cmdcode:0x%02x\n", __FUNCTION__, obj->cmdcode);
			if (obj->cmdcode == CMD_SET_DRV_EXTRA) {
				if (obj->parmbuf) {
					struct drvextra_cmd_parm *pdrvextra_cmd_parm = (struct drvextra_cmd_parm *)(obj->parmbuf);
					printk("pdrvextra_cmd_parm->ec_id:0x%02x\n", pdrvextra_cmd_parm->ec_id);
				}
			}

		}
#endif /* DBG_CMD_QUEUE */

		rtw_list_delete(&obj->list);
	}

	/* _rtw_spinunlock_bh(&(queue->lock)); */
	_rtw_spinunlock_irq(&queue->lock, &sp_flags);


	return obj;
}

struct	cmd_obj	*rtw_dequeue_cmd(struct cmd_priv *pcmdpriv)
{
	struct cmd_obj *cmd_obj;

	cmd_obj = _rtw_dequeue_cmd(&pcmdpriv->cmd_queue);

	return cmd_obj;
}
#endif
void rtw_free_cmd_obj(struct cmd_obj *pcmd)
{
	if (pcmd->parmbuf != NULL) {
		/* free parmbuf in cmd_obj */
		rtw_mfree((unsigned char *)pcmd->parmbuf, pcmd->cmdsz);
	}
	if (pcmd->rsp != NULL) {
		if (pcmd->rspsz != 0) {
			/* free rsp in cmd_obj */
			rtw_mfree((unsigned char *)pcmd->rsp, pcmd->rspsz);
		}
	}

	/* free cmd_obj */
	rtw_mfree((unsigned char *)pcmd, sizeof(struct cmd_obj));
}
void rtw_run_cmd(_adapter *padapter, struct cmd_obj *pcmd, bool discard)
{
	u8 ret;
	u8 *pcmdbuf;
	systime cmd_start_time;
	u32 cmd_process_time;
	u8(*cmd_hdl)(_adapter *padapter, u8 *pbuf);
	void (*pcmd_callback)(_adapter *dev, struct cmd_obj *pcmd);
	struct cmd_priv *pcmdpriv = &(adapter_to_dvobj(padapter)->cmdpriv);
	struct drvextra_cmd_parm *extra_parm = NULL;

	cmd_start_time = rtw_get_current_time();
	pcmdpriv->cmd_issued_cnt++;

	if (discard)
		goto post_process;

	if (pcmd->cmdsz > MAX_CMDSZ) {
		RTW_ERR("%s cmdsz:%d > MAX_CMDSZ:%d\n", __func__, pcmd->cmdsz, MAX_CMDSZ);
		pcmd->res = H2C_PARAMETERS_ERROR;
		goto post_process;
	}

	if (pcmd->cmdcode >= (sizeof(wlancmds) / sizeof(struct rtw_cmd))) {
		RTW_ERR("%s undefined cmdcode:%d\n", __func__, pcmd->cmdcode);
		pcmd->res = H2C_PARAMETERS_ERROR;
		goto post_process;
	}

	cmd_hdl = wlancmds[pcmd->cmdcode].cmd_hdl;
	if (!cmd_hdl) {
		RTW_ERR("%s no cmd_hdl for cmdcode:%d\n", __func__, pcmd->cmdcode);
		pcmd->res = H2C_PARAMETERS_ERROR;
		goto post_process;
	}

	if (DBG_CMD_EXECUTE)
		RTW_INFO(ADPT_FMT" "CMD_FMT" %sexecute\n", ADPT_ARG(pcmd->padapter), CMD_ARG(pcmd)
			, pcmd->res == H2C_ENQ_HEAD ? "ENQ_HEAD " : (pcmd->res == H2C_ENQ_HEAD_FAIL ? "ENQ_HEAD_FAIL " : ""));

	pcmdbuf = pcmdpriv->cmd_buf;
	_rtw_memcpy(pcmdbuf, pcmd->parmbuf, pcmd->cmdsz);
	ret = cmd_hdl(pcmd->padapter, pcmdbuf);
	pcmd->res = ret;

	pcmdpriv->cmd_seq++;

post_process:

	_rtw_mutex_lock_interruptible(&pcmdpriv->sctx_mutex);
	if (pcmd->sctx) {
		if (0)
			RTW_PRINT(FUNC_ADPT_FMT" pcmd->sctx\n", FUNC_ADPT_ARG(pcmd->padapter));
		if (pcmd->res == H2C_SUCCESS)
			rtw_sctx_done(&pcmd->sctx);
		else
			rtw_sctx_done_err(&pcmd->sctx, RTW_SCTX_DONE_CMD_ERROR);
	}
	_rtw_mutex_unlock(&pcmdpriv->sctx_mutex);

	cmd_process_time = rtw_get_passing_time_ms(cmd_start_time);
	if (cmd_process_time > 1000) {
		RTW_INFO(ADPT_FMT" "CMD_FMT" process_time=%d\n", ADPT_ARG(pcmd->padapter), CMD_ARG(pcmd), cmd_process_time);
		if (0)
			rtw_warn_on(1);
	}

	/* call callback function for post-processed */
	if (pcmd->cmdcode >= (sizeof(wlancmds) / sizeof(struct rtw_cmd)))
		pcmd_callback = wlancmds[pcmd->cmdcode].callback;
	else
		pcmd_callback = NULL;

	if (pcmd_callback == NULL) {
		rtw_free_cmd_obj(pcmd);
	} else {
		/* todo: !!! fill rsp_buf to pcmd->rsp if (pcmd->rsp!=NULL) */
		pcmd_callback(pcmd->padapter, pcmd);/* need conider that free cmd_obj in rtw_cmd_callback */
	}
}
#if 0 /*#ifdef CONFIG_CORE_CMD_THREAD*/
void rtw_stop_cmd_thread(_adapter *adapter)
{
	if (adapter->cmdThread) {
		_rtw_up_sema(&adapter->cmdpriv.cmd_queue_sema);
		rtw_thread_stop(adapter->cmdThread);
		adapter->cmdThread = NULL;
	}
}

thread_return rtw_cmd_thread(thread_context context)
{
	u8 ret;
	struct cmd_obj *pcmd;
	u8 *pcmdbuf, *prspbuf;
	systime cmd_start_time;
	u32 cmd_process_time;
	u8(*cmd_hdl)(_adapter *padapter, u8 *pbuf);
	void (*pcmd_callback)(_adapter *dev, struct cmd_obj *pcmd);
	_adapter *padapter = (_adapter *)context;
	struct cmd_priv *pcmdpriv = &(padapter->cmdpriv);
	struct drvextra_cmd_parm *extra_parm = NULL;
	unsigned long sp_flags;

	rtw_thread_enter("RTW_CMD_THREAD");

	pcmdbuf = pcmdpriv->cmd_buf;
	prspbuf = pcmdpriv->rsp_buf;
	ATOMIC_SET(&(pcmdpriv->cmdthd_running), _TRUE);
	_rtw_up_sema(&pcmdpriv->start_cmdthread_sema);


	while (1) {
		if (_rtw_down_sema(&pcmdpriv->cmd_queue_sema) == _FAIL) {
			RTW_PRINT(FUNC_ADPT_FMT" _rtw_down_sema(&pcmdpriv->cmd_queue_sema) return _FAIL, break\n", FUNC_ADPT_ARG(padapter));
			break;
		}

		if (RTW_CANNOT_RUN(adapter_to_dvobj(padapter))) {
			RTW_DBG(FUNC_ADPT_FMT "- bDriverStopped(%s) bSurpriseRemoved(%s)\n",
				FUNC_ADPT_ARG(padapter),
				dev_is_drv_stopped(adapter_to_dvobj(padapter)) ? "True" : "False",
				dev_is_surprise_removed(adapter_to_dvobj(padapter)) ? "True" : "False");
			break;
		}

		_rtw_spinlock_irq(&pcmdpriv->cmd_queue.lock, &sp_flags);
		if (rtw_is_list_empty(&(pcmdpriv->cmd_queue.queue))) {
			/* RTW_INFO("%s: cmd queue is empty!\n", __func__); */
			_rtw_spinunlock_irq(&pcmdpriv->cmd_queue.lock, &sp_flags);
			continue;
		}
		_rtw_spinunlock_irq(&pcmdpriv->cmd_queue.lock, &sp_flags);

_next:
		if (RTW_CANNOT_RUN(adapter_to_dvobj(padapter))) {
			RTW_PRINT("%s: DriverStopped(%s) SurpriseRemoved(%s) break at line %d\n",
				  __func__
				, dev_is_drv_stopped(adapter_to_dvobj(padapter)) ? "True" : "False"
				, dev_is_surprise_removed(adapter_to_dvobj(padapter)) ? "True" : "False"
				  , __LINE__);
			break;
		}

		pcmd = rtw_dequeue_cmd(pcmdpriv);
		if (!pcmd) {
#ifdef CONFIG_LPS_LCLK
			rtw_unregister_cmd_alive(padapter);
#endif
			continue;
		}

		cmd_start_time = rtw_get_current_time();
		pcmdpriv->cmd_issued_cnt++;

		if (pcmd->cmdsz > MAX_CMDSZ) {
			RTW_ERR("%s cmdsz:%d > MAX_CMDSZ:%d\n", __func__, pcmd->cmdsz, MAX_CMDSZ);
			pcmd->res = H2C_PARAMETERS_ERROR;
			goto post_process;
		}

		if (pcmd->cmdcode >= (sizeof(wlancmds) / sizeof(struct rtw_cmd))) {
			RTW_ERR("%s undefined cmdcode:%d\n", __func__, pcmd->cmdcode);
			pcmd->res = H2C_PARAMETERS_ERROR;
			goto post_process;
		}

		cmd_hdl = wlancmds[pcmd->cmdcode].cmd_hdl;
		if (!cmd_hdl) {
			RTW_ERR("%s no cmd_hdl for cmdcode:%d\n", __func__, pcmd->cmdcode);
			pcmd->res = H2C_PARAMETERS_ERROR;
			goto post_process;
		}

		if (_FAIL == rtw_cmd_filter(pcmdpriv, pcmd)) {
			pcmd->res = H2C_DROPPED;
			if (pcmd->cmdcode == CMD_SET_DRV_EXTRA) {
				extra_parm = (struct drvextra_cmd_parm *)pcmd->parmbuf;
				if (extra_parm && extra_parm->pbuf && extra_parm->size > 0)
					rtw_mfree(extra_parm->pbuf, extra_parm->size);
			}
			#if CONFIG_DFS
			else if (pcmd->cmdcode == CMD_SET_CHANSWITCH)
				adapter_to_rfctl(padapter)->csa_chandef.chan = 0;
			#endif
			goto post_process;
		}

#ifdef CONFIG_LPS_LCLK
		if (pcmd->no_io)
			rtw_unregister_cmd_alive(padapter);
		else {
			if (rtw_register_cmd_alive(padapter) != _SUCCESS) {
				if (DBG_CMD_EXECUTE)
					RTW_PRINT("%s: wait to leave LPS_LCLK\n", __func__);

				pcmd->res = H2C_ENQ_HEAD;
				ret = _rtw_enqueue_cmd(&pcmdpriv->cmd_queue, pcmd, 1);
				if (ret == _SUCCESS) {
					if (DBG_CMD_EXECUTE)
						RTW_INFO(ADPT_FMT" "CMD_FMT" ENQ_HEAD\n", ADPT_ARG(pcmd->padapter), CMD_ARG(pcmd));
					continue;
				}

				RTW_INFO(ADPT_FMT" "CMD_FMT" ENQ_HEAD_FAIL\n", ADPT_ARG(pcmd->padapter), CMD_ARG(pcmd));
				pcmd->res = H2C_ENQ_HEAD_FAIL;
				rtw_warn_on(1);
			}
		}
#endif /* CONFIG_LPS_LCLK */

		if (DBG_CMD_EXECUTE)
			RTW_INFO(ADPT_FMT" "CMD_FMT" %sexecute\n", ADPT_ARG(pcmd->padapter), CMD_ARG(pcmd)
				, pcmd->res == H2C_ENQ_HEAD ? "ENQ_HEAD " : (pcmd->res == H2C_ENQ_HEAD_FAIL ? "ENQ_HEAD_FAIL " : ""));

		_rtw_memcpy(pcmdbuf, pcmd->parmbuf, pcmd->cmdsz);
		ret = cmd_hdl(pcmd->padapter, pcmdbuf);
		pcmd->res = ret;

		pcmdpriv->cmd_seq++;

post_process:

		_rtw_mutex_lock_interruptible(&(pcmd->padapter->cmdpriv.sctx_mutex));
		if (pcmd->sctx) {
			if (0)
				RTW_PRINT(FUNC_ADPT_FMT" pcmd->sctx\n", FUNC_ADPT_ARG(pcmd->padapter));
			if (pcmd->res == H2C_SUCCESS)
				rtw_sctx_done(&pcmd->sctx);
			else
				rtw_sctx_done_err(&pcmd->sctx, RTW_SCTX_DONE_CMD_ERROR);
		}
		_rtw_mutex_unlock(&(pcmd->padapter->cmdpriv.sctx_mutex));

		cmd_process_time = rtw_get_passing_time_ms(cmd_start_time);
		if (cmd_process_time > 1000) {
			RTW_INFO(ADPT_FMT" "CMD_FMT" process_time=%d\n", ADPT_ARG(pcmd->padapter), CMD_ARG(pcmd), cmd_process_time);
			if (0)
				rtw_warn_on(1);
		}

		/* call callback function for post-processed */
		if (pcmd->cmdcode >= (sizeof(wlancmds) / sizeof(struct rtw_cmd)))
			pcmd_callback = wlancmds[pcmd->cmdcode].callback;
		else
			pcmd_callback = NULL;

		if (pcmd_callback == NULL) {
			rtw_free_cmd_obj(pcmd);
		} else {
			/* todo: !!! fill rsp_buf to pcmd->rsp if (pcmd->rsp!=NULL) */
			pcmd_callback(pcmd->padapter, pcmd);/* need conider that free cmd_obj in rtw_cmd_callback */
		}

		flush_signals_thread();

		goto _next;

	}

#ifdef CONFIG_LPS_LCLK
	rtw_unregister_cmd_alive(padapter);
#endif

	/* to avoid enqueue cmd after free all cmd_obj */
	ATOMIC_SET(&(pcmdpriv->cmdthd_running), _FALSE);

	/* free all cmd_obj resources */
	do {
		pcmd = rtw_dequeue_cmd(pcmdpriv);
		if (pcmd == NULL)
			break;

		if (0)
			RTW_INFO("%s: leaving... drop "CMD_FMT"\n", __func__, CMD_ARG(pcmd));

		if (pcmd->cmdcode == CMD_SET_DRV_EXTRA) {
			extra_parm = (struct drvextra_cmd_parm *)pcmd->parmbuf;
			if (extra_parm->pbuf && extra_parm->size > 0)
				rtw_mfree(extra_parm->pbuf, extra_parm->size);
		}
		#if CONFIG_DFS
		else if (pcmd->cmdcode == CMD_SET_CHANSWITCH)
			adapter_to_rfctl(padapter)->csa_chandef.chan = 0;
		#endif

		_rtw_mutex_lock_interruptible(&(pcmd->padapter->cmdpriv.sctx_mutex));
		if (pcmd->sctx) {
			if (0)
				RTW_PRINT(FUNC_ADPT_FMT" pcmd->sctx\n", FUNC_ADPT_ARG(pcmd->padapter));
			rtw_sctx_done_err(&pcmd->sctx, RTW_SCTX_DONE_CMD_DROP);
		}
		_rtw_mutex_unlock(&(pcmd->padapter->cmdpriv.sctx_mutex));

		rtw_free_cmd_obj(pcmd);
	} while (1);

	RTW_INFO(FUNC_ADPT_FMT " Exit\n", FUNC_ADPT_ARG(padapter));

	rtw_thread_wait_stop();

	return 0;
}
#endif

void rtw_readtssi_cmdrsp_callback(_adapter	*padapter,  struct cmd_obj *pcmd)
{

	rtw_mfree((unsigned char *) pcmd->parmbuf, pcmd->cmdsz);
	rtw_mfree((unsigned char *) pcmd, sizeof(struct cmd_obj));

#ifdef CONFIG_MP_INCLUDED
	if (padapter->registrypriv.mp_mode == 1)
		padapter->mppriv.workparam.bcompleted = _TRUE;
#endif

}

static u8 rtw_createbss_cmd(_adapter  *adapter, int flags, bool adhoc
	, u8 ifbmp, u8 excl_ifbmp, s16 req_ch, s8 req_bw, s8 req_offset)
{
	struct cmd_obj *cmdobj;
	struct createbss_parm *parm;
	struct cmd_priv *pcmdpriv = &adapter_to_dvobj(adapter)->cmdpriv;
	struct submit_ctx sctx;
	u8 res = _SUCCESS;

	if (req_ch > 0 && req_bw >= 0 && req_offset >= 0) {
		if (!rtw_chset_is_chbw_valid(adapter_to_chset(adapter), req_ch, req_bw, req_offset, 0, 0)) {
			res = _FAIL;
			goto exit;
		}
	}

	/* prepare cmd parameter */
	parm = (struct createbss_parm *)rtw_zmalloc(sizeof(*parm));
	if (parm == NULL) {
		res = _FAIL;
		goto exit;
	}

	if (adhoc) {
		/* for now, adhoc doesn't support ch,bw,offset request */
		parm->adhoc = 1;
	} else {
		parm->adhoc = 0;
		parm->ifbmp = ifbmp;
		parm->excl_ifbmp = excl_ifbmp;
		parm->req_ch = req_ch;
		parm->req_bw = req_bw;
		parm->req_offset = req_offset;
		parm->ifbmp_ch_changed = 0;
		parm->ch_to_set = 0;
		parm->bw_to_set = 0;
		parm->offset_to_set = 0;
		parm->do_rfk = _FALSE;
	}

	if (flags & RTW_CMDF_DIRECTLY) {
		/* no need to enqueue, do the cmd hdl directly and free cmd parameter */
		if (H2C_SUCCESS != createbss_hdl(adapter, (u8 *)parm))
			res = _FAIL;
		rtw_mfree((u8 *)parm, sizeof(*parm));
	} else {
		/* need enqueue, prepare cmd_obj and enqueue */
		cmdobj = (struct cmd_obj *)rtw_zmalloc(sizeof(*cmdobj));
		if (cmdobj == NULL) {
			res = _FAIL;
			rtw_mfree((u8 *)parm, sizeof(*parm));
			goto exit;
		}
		cmdobj->padapter = adapter;

		init_h2fwcmd_w_parm_no_rsp(cmdobj, parm, CMD_CREATE_BSS);

		if (flags & RTW_CMDF_WAIT_ACK) {
			cmdobj->sctx = &sctx;
			rtw_sctx_init(&sctx, 5000);
		}

		res = rtw_enqueue_cmd(pcmdpriv, cmdobj);

		if (res == _SUCCESS && (flags & RTW_CMDF_WAIT_ACK)) {
			res = rtw_sctx_wait(&sctx, __func__);
			_rtw_mutex_lock_interruptible(&pcmdpriv->sctx_mutex);
			if (sctx.status == RTW_SCTX_SUBMITTED)
				cmdobj->sctx = NULL;
			_rtw_mutex_unlock(&pcmdpriv->sctx_mutex);
		}
	}

exit:
	return res;
}

inline u8 rtw_create_ibss_cmd(_adapter *adapter, int flags)
{
	return rtw_createbss_cmd(adapter, flags
		, 1
		, 0, 0
		, 0, REQ_BW_NONE, REQ_OFFSET_NONE /* for now, adhoc doesn't support ch,bw,offset request */
	);
}

inline u8 rtw_startbss_cmd(_adapter *adapter, int flags)
{
	return rtw_createbss_cmd(adapter, flags
		, 0
		, BIT(adapter->iface_id), 0
		, 0, REQ_BW_NONE, REQ_OFFSET_NONE /* excute entire AP setup cmd */
	);
}

inline u8 rtw_change_bss_chbw_cmd(_adapter *adapter, int flags
	, u8 ifbmp, u8 excl_ifbmp, s16 req_ch, s8 req_bw, s8 req_offset)
{
	return rtw_createbss_cmd(adapter, flags
		, 0
		, ifbmp, excl_ifbmp
		, req_ch, req_bw, req_offset
	);
}

#ifdef CONFIG_80211D
/* Return corresponding country_chplan setting  */
static bool rtw_joinbss_check_country_ie(_adapter *adapter, const WLAN_BSSID_EX *network, struct country_chplan *ent, WLAN_BSSID_EX *out_network)
{
	struct rf_ctl_t *rfctl = adapter_to_rfctl(adapter);
	bool ret = 0;

	if (rfctl->regd_src == REGD_SRC_RTK_PRIV
		&& !rtw_rfctl_is_disable_sw_channel_plan(rfctl_to_dvobj(rfctl))
	) {
		struct mlme_priv *mlme = &adapter->mlmepriv;
		const u8 *country_ie = NULL;
		sint country_ie_len = 0;

		if (rtw_iface_accept_country_ie(adapter)) {
			country_ie = rtw_get_ie(BSS_EX_TLV_IES(network)
				, WLAN_EID_COUNTRY, &country_ie_len, BSS_EX_TLV_IES_LEN(network));
			if (country_ie) {
				if (country_ie_len < 6) {
					country_ie = NULL;
					country_ie_len = 0;
				} else
					country_ie_len += 2;
			}
		}

		if (country_ie) {
			enum country_ie_slave_status status;

			rtw_buf_update(&mlme->recv_country_ie, &mlme->recv_country_ie_len, country_ie, country_ie_len);

			status = rtw_get_chplan_from_recv_country_ie(adapter
				, network->Configuration.DSConfig > 14 ? BAND_ON_5G : BAND_ON_24G
				, network->Configuration.DSConfig, country_ie, ent, NULL, __func__);
			if (status != COUNTRY_IE_SLAVE_NOCOUNTRY)
				ret = 1;

			if (out_network) {
				_rtw_memcpy(BSS_EX_IES(out_network) + BSS_EX_IES_LEN(out_network)
					, country_ie, country_ie_len);
				BSS_EX_IES_LEN(out_network) += country_ie_len;
			}
		} else
			rtw_buf_free(&mlme->recv_country_ie, &mlme->recv_country_ie_len);
	}

	return ret;
}
#endif /* CONFIG_80211D */

u8 rtw_joinbss_cmd(_adapter *padapter, struct wlan_network *pnetwork)
{
	u8	*auth, res = _SUCCESS;
	uint	t_len = 0;
	WLAN_BSSID_EX		*psecnetwork;
	struct cmd_obj		*pcmd;
	struct cmd_priv		*pcmdpriv = &adapter_to_dvobj(padapter)->cmdpriv;
	struct mlme_priv	*pmlmepriv = &padapter->mlmepriv;
	struct qos_priv		*pqospriv = &pmlmepriv->qospriv;
	struct security_priv	*psecuritypriv = &padapter->securitypriv;
	struct registry_priv	*pregistrypriv = &padapter->registrypriv;
#ifdef CONFIG_80211D
	struct country_chplan country_ent;
#endif
	struct country_chplan *req_chplan = NULL;
#ifdef CONFIG_80211N_HT
	struct ht_priv			*phtpriv = &pmlmepriv->htpriv;
#endif /* CONFIG_80211N_HT */
#ifdef CONFIG_80211AC_VHT
	struct vht_priv		*pvhtpriv = &pmlmepriv->vhtpriv;
#endif /* CONFIG_80211AC_VHT */
#ifdef CONFIG_80211AX_HE
	struct he_priv		*phepriv = &pmlmepriv->hepriv;
#endif /* CONFIG_80211AX_HE */
	NDIS_802_11_NETWORK_INFRASTRUCTURE ndis_network_mode = pnetwork->network.InfrastructureMode;
	struct mlme_ext_priv	*pmlmeext = &padapter->mlmeextpriv;
	struct mlme_ext_info	*pmlmeinfo = &(pmlmeext->mlmext_info);
	struct rf_ctl_t *rfctl = adapter_to_rfctl(padapter);
	u32 tmp_len;
	u8 *ptmp = NULL;

	rtw_led_control(padapter, LED_CTL_START_TO_LINK);

	pcmd = (struct cmd_obj *)rtw_zmalloc(sizeof(struct cmd_obj));
	if (pcmd == NULL) {
		res = _FAIL;
		goto exit;
	}
	pcmd->padapter = padapter;

	/* for IEs is fix buf size */
	t_len = sizeof(WLAN_BSSID_EX);


	/* for hidden ap to set fw_state here */
	if (!MLME_IS_STA(padapter) || !MLME_IS_ADHOC(padapter)) {
		switch (ndis_network_mode) {
		case Ndis802_11IBSS:
			set_fwstate(pmlmepriv, WIFI_ADHOC_STATE);
			break;

		case Ndis802_11Infrastructure:
			set_fwstate(pmlmepriv, WIFI_STATION_STATE);
			break;

		default:
			rtw_warn_on(1);
			break;
		}
	}

	pmlmeinfo->assoc_AP_vendor = check_assoc_AP(pnetwork->network.IEs, pnetwork->network.IELength);

#ifdef CONFIG_80211AC_VHT
	/* save AP beamform_cap info for BCM IOT issue */
	if (pmlmeinfo->assoc_AP_vendor == HT_IOT_PEER_BROADCOM)
		get_vht_bf_cap(pnetwork->network.IEs,
			       pnetwork->network.IELength,
			       &pvhtpriv->ap_bf_cap);
#endif
	/*
		Modified by Arvin 2015/05/13
		Solution for allocating a new WLAN_BSSID_EX to avoid race condition issue between disconnect and joinbss
	*/
	psecnetwork = (WLAN_BSSID_EX *)rtw_zmalloc(sizeof(WLAN_BSSID_EX));
	if (psecnetwork == NULL) {
		if (pcmd != NULL)
			rtw_mfree((unsigned char *)pcmd, sizeof(struct	cmd_obj));

		res = _FAIL;


		goto exit;
	}

	_rtw_memset(psecnetwork, 0, t_len);

	_rtw_memcpy(psecnetwork, &pnetwork->network, get_WLAN_BSSID_EX_sz(&pnetwork->network));

	auth = &psecuritypriv->authenticator_ie[0];
	psecuritypriv->authenticator_ie[0] = (unsigned char)psecnetwork->IELength;

	if ((psecnetwork->IELength - 12) < (256 - 1))
		_rtw_memcpy(&psecuritypriv->authenticator_ie[1], &psecnetwork->IEs[12], psecnetwork->IELength - 12);
	else
		_rtw_memcpy(&psecuritypriv->authenticator_ie[1], &psecnetwork->IEs[12], (256 - 1));

	psecnetwork->IELength = 0;
	/* Added by Albert 2009/02/18 */
	/* If the the driver wants to use the bssid to create the connection. */
	/* If not,  we have to copy the connecting AP's MAC address to it so that */
	/* the driver just has the bssid information for PMKIDList searching. */

	if (pmlmepriv->assoc_by_bssid == _FALSE)
		_rtw_memcpy(&pmlmepriv->assoc_bssid[0], &pnetwork->network.MacAddress[0], ETH_ALEN);

	/* copy fixed ie */
	_rtw_memcpy(psecnetwork->IEs, pnetwork->network.IEs, 12);
	psecnetwork->IELength = 12;

	psecnetwork->IELength += rtw_restruct_sec_ie(padapter, psecnetwork->IEs + psecnetwork->IELength);


	pqospriv->qos_option = 0;

	if (pregistrypriv->wmm_enable) {
#ifdef CONFIG_WMMPS_STA	
		rtw_uapsd_use_default_setting(padapter);
#endif /* CONFIG_WMMPS_STA */		
		tmp_len = rtw_restruct_wmm_ie(padapter, &pnetwork->network.IEs[0], &psecnetwork->IEs[0], pnetwork->network.IELength, psecnetwork->IELength);

		if (psecnetwork->IELength != tmp_len) {
			psecnetwork->IELength = tmp_len;
			pqospriv->qos_option = 1; /* There is WMM IE in this corresp. beacon */
		} else {
			pqospriv->qos_option = 0;/* There is no WMM IE in this corresp. beacon */
		}
	}

#ifdef RTW_WKARD_UPDATE_PHL_ROLE_CAP
	rtw_update_phl_cap_by_rgstry(padapter);
#endif

#ifdef CONFIG_80211D
	if (rtw_joinbss_check_country_ie(padapter, &pnetwork->network, &country_ent, psecnetwork))
		req_chplan = &country_ent;
#endif

#ifdef CONFIG_80211N_HT
	phtpriv->ht_option = _FALSE;
	if (pregistrypriv->ht_enable && is_supported_ht(pregistrypriv->wireless_mode)) {
		ptmp = rtw_get_ie(&pnetwork->network.IEs[12], _HT_CAPABILITY_IE_, &tmp_len, pnetwork->network.IELength - 12);
		if (ptmp && tmp_len > 0) {
			/*	Added by Albert 2010/06/23 */
			/*	For the WEP mode, we will use the bg mode to do the connection to avoid some IOT issue. */
			/*	Especially for Realtek 8192u SoftAP. */
			if ((padapter->securitypriv.dot11PrivacyAlgrthm != _WEP40_) &&
			    (padapter->securitypriv.dot11PrivacyAlgrthm != _WEP104_) &&
			    (padapter->securitypriv.dot11PrivacyAlgrthm != _TKIP_)) {
				rtw_ht_use_default_setting(padapter);

				/* rtw_restructure_ht_ie */
				rtw_restructure_ht_ie(padapter, &pnetwork->network.IEs[12], &psecnetwork->IEs[0],
					pnetwork->network.IELength - 12, &psecnetwork->IELength,
					pnetwork->network.Configuration.DSConfig, req_chplan);
			}
		}
	}

#ifdef CONFIG_80211AC_VHT
	pvhtpriv->vht_option = _FALSE;
	if (phtpriv->ht_option
		&& REGSTY_IS_11AC_ENABLE(pregistrypriv)
		&& is_supported_vht(pregistrypriv->wireless_mode)
		&& ((req_chplan && COUNTRY_CHPLAN_EN_11AC(req_chplan))
			|| (!req_chplan && RFCTL_REG_EN_11AC(rfctl)))
	) {
		u8 vht_enable = 0;

		if (pnetwork->network.Configuration.DSConfig > 14)
			vht_enable = 1;
		else if ((REGSTY_IS_11AC_24G_ENABLE(pregistrypriv)) && (padapter->registrypriv.wifi_spec == 0))
			vht_enable = 1;

		if (vht_enable == 1)
			rtw_restructure_vht_ie(padapter, &pnetwork->network.IEs[0], &psecnetwork->IEs[0],
				pnetwork->network.IELength, &psecnetwork->IELength, req_chplan);
	}
#endif /* CONFIG_80211AC_VHT */

#ifdef CONFIG_80211AX_HE
	phepriv->he_option = _FALSE;
	if (((phtpriv->ht_option && pnetwork->network.Configuration.DSConfig <= 14)
#ifdef CONFIG_80211AC_VHT
		|| (pvhtpriv->vht_option && pnetwork->network.Configuration.DSConfig > 14)
#endif
	    )
		&& REGSTY_IS_11AX_ENABLE(pregistrypriv)
		&& is_supported_he(pregistrypriv->wireless_mode)
		&& ((req_chplan && COUNTRY_CHPLAN_EN_11AX(req_chplan))
			|| (!req_chplan && RFCTL_REG_EN_11AX(rfctl)))
	) {
		rtw_restructure_he_ie(padapter, &pnetwork->network.IEs[0], &psecnetwork->IEs[0],
			pnetwork->network.IELength, &psecnetwork->IELength, req_chplan);
	}
#endif /* CONFIG_80211AX_HE */

#endif /* CONFIG_80211N_HT */

	rtw_append_extended_cap(padapter, &psecnetwork->IEs[0], &psecnetwork->IELength);

#ifdef CONFIG_RTW_80211R
	rtw_ft_validate_akm_type(padapter, pnetwork);
#endif

#if 0
	psecuritypriv->supplicant_ie[0] = (u8)psecnetwork->IELength;

	if (psecnetwork->IELength < (256 - 1))
		_rtw_memcpy(&psecuritypriv->supplicant_ie[1], &psecnetwork->IEs[0], psecnetwork->IELength);
	else
		_rtw_memcpy(&psecuritypriv->supplicant_ie[1], &psecnetwork->IEs[0], (256 - 1));
#endif

	pcmd->cmdsz = sizeof(WLAN_BSSID_EX);

	_rtw_init_listhead(&pcmd->list);
	pcmd->cmdcode = CMD_JOINBSS; /*_JoinBss_CMD_;*/
	pcmd->parmbuf = (unsigned char *)psecnetwork;
	pcmd->rsp = NULL;
	pcmd->rspsz = 0;

	res = rtw_enqueue_cmd(pcmdpriv, pcmd);

exit:


	return res;
}

#ifdef CONFIG_STA_CMD_DISPR
/* for sta_mode only */
static u8 sta_disassoc_cmd(struct _ADAPTER *a, u32 deauth_timeout_ms, int flags)
{
	struct cmd_priv *cmdpriv = &adapter_to_dvobj(a)->cmdpriv;
	struct cmd_obj *cmd = NULL;
	struct disconnect_parm *param = NULL;
	struct submit_ctx sctx;
	enum rtw_phl_status status;
	int ret;
	u8 res = _FAIL;


	if (!MLME_IS_ASOC(a))
		return _SUCCESS;

	param = (struct disconnect_parm *)rtw_zmalloc(sizeof(*param));
	if (!param) {
		RTW_ERR(FUNC_ADPT_FMT ": alloc param FAIL!", FUNC_ADPT_ARG(a));
		goto exit;
	}
	cmd = (struct cmd_obj *)rtw_zmalloc(sizeof(*cmd));
	if (!cmd) {
		RTW_ERR(FUNC_ADPT_FMT ": alloc cmd FAIL!", FUNC_ADPT_ARG(a));
		rtw_mfree((u8 *)param, sizeof(*param));
		goto exit;
	}

	param->deauth_timeout_ms = deauth_timeout_ms;
	init_h2fwcmd_w_parm_no_rsp(cmd, param, CMD_DISCONNECT);
	cmd->padapter = a;
	if (flags & RTW_CMDF_WAIT_ACK) {
		cmd->sctx = &sctx;
		rtw_sctx_init(&sctx, 2000);
	}

	status = rtw_disconnect_cmd(a, cmd);
	if (status != RTW_PHL_STATUS_SUCCESS) {
		/* param & cmd would be freed in rtw_enqueue_cmd() */
		RTW_ERR(FUNC_ADPT_FMT ": send disconnect cmd FAIL!(0x%x)\n",
			FUNC_ADPT_ARG(a), status);
		goto exit;
	}
	res = _SUCCESS;

	if (flags & RTW_CMDF_WAIT_ACK) {
		ret = rtw_sctx_wait(&sctx, __func__);
		if (ret == _FAIL)
			res = _FAIL;
		_rtw_spinlock(&a->disconnect_lock);
		if (a->discon_cmd) {
			a->discon_cmd->sctx = NULL;
			/*
			 * a->discon_param would be
			 * freed by disconnect cmd dispatcher.
			 */
		}
		_rtw_spinunlock(&a->disconnect_lock);
	}

exit:
	return res;
}
#endif /* CONFIG_STA_CMD_DISPR */

u8 rtw_disassoc_cmd(_adapter *padapter, u32 deauth_timeout_ms, int flags) /* for sta_mode */
{
	struct cmd_obj *cmdobj = NULL;
	struct disconnect_parm *param = NULL;
	struct cmd_priv *cmdpriv = &adapter_to_dvobj(padapter)->cmdpriv;

	struct submit_ctx sctx;
	u8 res = _SUCCESS;


#ifdef CONFIG_STA_CMD_DISPR
	if (MLME_IS_STA(padapter))
		return sta_disassoc_cmd(padapter, deauth_timeout_ms, flags);
#endif /* CONFIG_STA_CMD_DISPR */

	/* prepare cmd parameter */
	param = (struct disconnect_parm *)rtw_zmalloc(sizeof(*param));
	if (param == NULL) {
		res = _FAIL;
		goto exit;
	}
	param->deauth_timeout_ms = deauth_timeout_ms;

	if (flags & RTW_CMDF_DIRECTLY) {
		/* no need to enqueue, do the cmd hdl directly and free cmd parameter */
		if (disconnect_hdl(padapter, (u8 *)param) != H2C_SUCCESS)
			res = _FAIL;
		rtw_mfree((u8 *)param, sizeof(*param));

	} else {
		cmdobj = (struct cmd_obj *)rtw_zmalloc(sizeof(*cmdobj));
		if (cmdobj == NULL) {
			res = _FAIL;
			rtw_mfree((u8 *)param, sizeof(*param));
			goto exit;
		}
		cmdobj->padapter = padapter;
		init_h2fwcmd_w_parm_no_rsp(cmdobj, param, CMD_DISCONNECT);
		if (flags & RTW_CMDF_WAIT_ACK) {
			cmdobj->sctx = &sctx;
			rtw_sctx_init(&sctx, 2000);
		}
		res = rtw_enqueue_cmd(cmdpriv, cmdobj);
		if (res == _FAIL) {
			RTW_ERR(FUNC_ADPT_FMT ": enqueue disconnect cmd FAIL!\n",
				FUNC_ADPT_ARG(padapter));
			goto exit;
		}
		if (flags & RTW_CMDF_WAIT_ACK) {
			rtw_sctx_wait(&sctx, __func__);
			_rtw_mutex_lock_interruptible(&cmdpriv->sctx_mutex);
			if (sctx.status == RTW_SCTX_SUBMITTED)
				cmdobj->sctx = NULL;
			_rtw_mutex_unlock(&cmdpriv->sctx_mutex);
		}
	}

exit:
	return res;
}


u8 rtw_stop_ap_cmd(_adapter *adapter, u8 flags)
{
#ifdef CONFIG_AP_MODE
	struct cmd_obj *cmdobj;
	struct drvextra_cmd_parm *parm;
	struct cmd_priv *pcmdpriv = &adapter_to_dvobj(adapter)->cmdpriv;
	struct submit_ctx sctx;
	u8 res = _SUCCESS;

	if (flags & RTW_CMDF_DIRECTLY) {
		/* no need to enqueue, do the cmd hdl directly and free cmd parameter */
		if (H2C_SUCCESS != stop_ap_hdl(adapter))
			res = _FAIL;
	} else {
		parm = (struct drvextra_cmd_parm *)rtw_zmalloc(sizeof(struct drvextra_cmd_parm));
		if (parm == NULL) {
			res = _FAIL;
			goto exit;
		}

		parm->ec_id = STOP_AP_WK_CID;
		parm->type = 0;
		parm->size = 0;
		parm->pbuf = NULL;

		/* need enqueue, prepare cmd_obj and enqueue */
		cmdobj = (struct cmd_obj *)rtw_zmalloc(sizeof(*cmdobj));
		if (cmdobj == NULL) {
			res = _FAIL;
			goto exit;
		}
		cmdobj->padapter = adapter;

		init_h2fwcmd_w_parm_no_rsp(cmdobj, parm, CMD_SET_DRV_EXTRA);

		if (flags & RTW_CMDF_WAIT_ACK) {
			cmdobj->sctx = &sctx;
			rtw_sctx_init(&sctx, 2000);
		}

		res = rtw_enqueue_cmd(pcmdpriv, cmdobj);

		if (res == _SUCCESS && (flags & RTW_CMDF_WAIT_ACK)) {
			rtw_sctx_wait(&sctx, __func__);
			_rtw_mutex_lock_interruptible(&pcmdpriv->sctx_mutex);
			if (sctx.status == RTW_SCTX_SUBMITTED)
				cmdobj->sctx = NULL;
			_rtw_mutex_unlock(&pcmdpriv->sctx_mutex);
		}
	}

exit:
	return res;
#endif
}

#ifdef CONFIG_RTW_TOKEN_BASED_XMIT
u8 rtw_tx_control_cmd(_adapter *adapter)
{
	struct cmd_obj *cmd;
	struct drvextra_cmd_parm *pdrvextra_cmd_parm;
	struct cmd_priv *pcmdpriv = &adapter_to_dvobj(adapter)->cmdpriv;

	u8 res = _SUCCESS;

	cmd = (struct cmd_obj *)rtw_zmalloc(sizeof(struct cmd_obj));
	if (cmd == NULL){
		res = _FAIL;
		goto exit;
	}
	cmd->padapter = adapter;

	pdrvextra_cmd_parm = (struct drvextra_cmd_parm *)rtw_zmalloc(sizeof(struct drvextra_cmd_parm));
	if (pdrvextra_cmd_parm == NULL) {
		rtw_mfree((unsigned char *)cmd, sizeof(struct cmd_obj));
		res = _FAIL;
		goto exit;
	}

	pdrvextra_cmd_parm->ec_id = TBTX_CONTROL_TX_WK_CID;
	pdrvextra_cmd_parm->type = 0;
	pdrvextra_cmd_parm->size = 0;
	pdrvextra_cmd_parm->pbuf = NULL;
	init_h2fwcmd_w_parm_no_rsp(cmd, pdrvextra_cmd_parm, CMD_SET_DRV_EXTRA);

	res = rtw_enqueue_cmd(pcmdpriv, cmd);

exit:
	return res;	
}
#endif

u8 rtw_setopmode_cmd(_adapter  *adapter, NDIS_802_11_NETWORK_INFRASTRUCTURE networktype, u8 flags)
{
	struct cmd_obj *cmdobj;
	struct setopmode_parm *parm;
	struct cmd_priv *pcmdpriv = &adapter_to_dvobj(adapter)->cmdpriv;
	struct submit_ctx sctx;
	u8 res = _SUCCESS;

	/* prepare cmd parameter */
	parm = (struct setopmode_parm *)rtw_zmalloc(sizeof(*parm));
	if (parm == NULL) {
		res = _FAIL;
		goto exit;
	}
	parm->mode = (u8)networktype;

	if (flags & RTW_CMDF_DIRECTLY) {
		/* no need to enqueue, do the cmd hdl directly and free cmd parameter */
		if (H2C_SUCCESS != setopmode_hdl(adapter, (u8 *)parm))
			res = _FAIL;
		rtw_mfree((u8 *)parm, sizeof(*parm));
	} else {
		/* need enqueue, prepare cmd_obj and enqueue */
		cmdobj = (struct cmd_obj *)rtw_zmalloc(sizeof(*cmdobj));
		if (cmdobj == NULL) {
			res = _FAIL;
			rtw_mfree((u8 *)parm, sizeof(*parm));
			goto exit;
		}
		cmdobj->padapter = adapter;

		init_h2fwcmd_w_parm_no_rsp(cmdobj, parm, CMD_SET_OPMODE);

		if (flags & RTW_CMDF_WAIT_ACK) {
			cmdobj->sctx = &sctx;
			rtw_sctx_init(&sctx, 2000);
		}

		res = rtw_enqueue_cmd(pcmdpriv, cmdobj);

		if (res == _SUCCESS && (flags & RTW_CMDF_WAIT_ACK)) {
			rtw_sctx_wait(&sctx, __func__);
			_rtw_mutex_lock_interruptible(&pcmdpriv->sctx_mutex);
			if (sctx.status == RTW_SCTX_SUBMITTED)
				cmdobj->sctx = NULL;
			_rtw_mutex_unlock(&pcmdpriv->sctx_mutex);
		}
	}

exit:
	return res;
}

#ifdef CONFIG_CMD_DISP
u8 rtw_setstakey_cmd(_adapter *padapter, struct sta_info *sta, u8 key_type, bool enqueue)
{
	struct set_stakey_parm	setstakey_para;

	struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
	struct security_priv *psecuritypriv = &padapter->securitypriv;
	u8 key_len =16;
	u8 res = _SUCCESS;

	_rtw_memset(&setstakey_para, 0, sizeof(struct set_stakey_parm));
	_rtw_memcpy(setstakey_para.addr, sta->phl_sta->mac_addr, ETH_ALEN);

	if (MLME_IS_STA(padapter))
		setstakey_para.algorithm = (unsigned char) psecuritypriv->dot11PrivacyAlgrthm;
	else
		GET_ENCRY_ALGO(psecuritypriv, sta, setstakey_para.algorithm, _FALSE);

	if ((setstakey_para.algorithm == _GCMP_256_) || (setstakey_para.algorithm == _CCMP_256_))
		key_len = 32;

	if (key_type == GROUP_KEY) {
		_rtw_memcpy(&setstakey_para.key, &psecuritypriv->dot118021XGrpKey[psecuritypriv->dot118021XGrpKeyid].skey, key_len);
		setstakey_para.gk = 1;
	} else if (key_type == UNICAST_KEY)
		_rtw_memcpy(&setstakey_para.key, &sta->dot118021x_UncstKey, key_len);
#ifdef CONFIG_TDLS
	else if (key_type == TDLS_KEY) {
		_rtw_memcpy(&setstakey_para.key, sta->tpk.tk, key_len);
		setstakey_para.algorithm = (u8)sta->dot118021XPrivacy;
	}
#endif /* CONFIG_TDLS */

	/* jeff: set this becasue at least sw key is ready */
	padapter->securitypriv.busetkipkey = _TRUE;

	if (enqueue) {
		set_stakey_hdl(padapter, &setstakey_para, PHL_CMD_NO_WAIT, 0);
	} else {
		set_stakey_hdl(padapter, &setstakey_para, PHL_CMD_DIRECTLY, 0);
	}
exit:
	return res;
}

u8 rtw_clearstakey_cmd(_adapter *padapter, struct sta_info *sta, u8 enqueue)
{
	struct cmd_obj *cmd;
	struct set_stakey_parm	*psetstakey_para;
	struct cmd_priv *pcmdpriv = &adapter_to_dvobj(padapter)->cmdpriv;
	struct set_stakey_rsp *psetstakey_rsp = NULL;
	s16 cam_id = 0;
	u8 res = _SUCCESS;

	if (!sta) {
		RTW_ERR("%s sta == NULL\n", __func__);
		goto exit;
	}

	if (!enqueue)
		rtw_hw_del_all_key(padapter, sta, PHL_CMD_DIRECTLY, 0);
	else
		rtw_hw_del_all_key(padapter, sta, PHL_CMD_NO_WAIT, 0);

exit:


	return res;
}
#else /* CONFIG_FSM */
u8 rtw_setstakey_cmd(_adapter *padapter, struct sta_info *sta, u8 key_type, bool enqueue)
{
	struct cmd_obj *pcmd;
	struct set_stakey_parm	*psetstakey_para;
	struct cmd_priv *pcmdpriv = &adapter_to_dvobj(padapter)->cmdpriv;
	struct set_stakey_rsp *psetstakey_rsp = NULL;

	struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
	struct security_priv *psecuritypriv = &padapter->securitypriv;
	u8 key_len =16;
	u8 res = _SUCCESS;


	psetstakey_para = (struct set_stakey_parm *)rtw_zmalloc(sizeof(struct set_stakey_parm));
	if (psetstakey_para == NULL) {
		res = _FAIL;
		goto exit;
	}

	_rtw_memcpy(psetstakey_para->addr, sta->phl_sta->mac_addr, ETH_ALEN);

	if (MLME_IS_STA(padapter))
		psetstakey_para->algorithm = (unsigned char) psecuritypriv->dot11PrivacyAlgrthm;
	else
		GET_ENCRY_ALGO(psecuritypriv, sta, psetstakey_para->algorithm, _FALSE);

	if ((psetstakey_para->algorithm == _GCMP_256_) || (psetstakey_para->algorithm == _CCMP_256_)) 
		key_len = 32;

	if (key_type == GROUP_KEY) {
		_rtw_memcpy(&psetstakey_para->key, &psecuritypriv->dot118021XGrpKey[psecuritypriv->dot118021XGrpKeyid].skey, key_len);
		psetstakey_para->gk = 1;
	} else if (key_type == UNICAST_KEY)
		_rtw_memcpy(&psetstakey_para->key, &sta->dot118021x_UncstKey, key_len);
#ifdef CONFIG_TDLS
	else if (key_type == TDLS_KEY) {
		_rtw_memcpy(&psetstakey_para->key, sta->tpk.tk, key_len);
		psetstakey_para->algorithm = (u8)sta->dot118021XPrivacy;
	}
#endif /* CONFIG_TDLS */

	/* jeff: set this becasue at least sw key is ready */
	padapter->securitypriv.busetkipkey = _TRUE;

	if (enqueue) {
		pcmd = (struct cmd_obj *)rtw_zmalloc(sizeof(struct cmd_obj));
		if (pcmd == NULL) {
			rtw_mfree((u8 *) psetstakey_para, sizeof(struct set_stakey_parm));
			res = _FAIL;
			goto exit;
		}
		pcmd->padapter = padapter;

		psetstakey_rsp = (struct set_stakey_rsp *)rtw_zmalloc(sizeof(struct set_stakey_rsp));
		if (psetstakey_rsp == NULL) {
			rtw_mfree((u8 *) pcmd, sizeof(struct cmd_obj));
			rtw_mfree((u8 *) psetstakey_para, sizeof(struct set_stakey_parm));
			res = _FAIL;
			goto exit;
		}

		init_h2fwcmd_w_parm_no_rsp(pcmd, psetstakey_para, CMD_SET_STAKEY);
		pcmd->rsp = (u8 *) psetstakey_rsp;
		pcmd->rspsz = sizeof(struct set_stakey_rsp);
		res = rtw_enqueue_cmd(pcmdpriv, pcmd);
	} else {
		set_stakey_hdl(padapter, (u8 *)psetstakey_para);
		rtw_mfree((u8 *) psetstakey_para, sizeof(struct set_stakey_parm));
	}
exit:
	return res;
}

u8 rtw_clearstakey_cmd(_adapter *padapter, struct sta_info *sta, u8 enqueue)
{
	struct cmd_obj *cmd;
	struct set_stakey_parm	*psetstakey_para;
	struct cmd_priv *pcmdpriv = &adapter_to_dvobj(padapter)->cmdpriv;
	struct set_stakey_rsp *psetstakey_rsp = NULL;
	s16 cam_id = 0;
	u8 res = _SUCCESS;

	if (!sta) {
		RTW_ERR("%s sta == NULL\n", __func__);
		goto exit;
	}

	if (!enqueue) {
		rtw_hw_del_all_key(padapter, sta, PHL_CMD_DIRECTLY, 0);
	} else {
		cmd = (struct cmd_obj *)rtw_zmalloc(sizeof(struct cmd_obj));
		if (cmd == NULL) {
			res = _FAIL;
			goto exit;
		}

		psetstakey_para = (struct set_stakey_parm *)rtw_zmalloc(sizeof(struct set_stakey_parm));
		if (psetstakey_para == NULL) {
			rtw_mfree((u8 *)cmd, sizeof(struct cmd_obj));
			res = _FAIL;
			goto exit;
		}
		cmd->padapter = padapter;

		psetstakey_rsp = (struct set_stakey_rsp *)rtw_zmalloc(sizeof(struct set_stakey_rsp));
		if (psetstakey_rsp == NULL) {
			rtw_mfree((u8 *)cmd, sizeof(struct cmd_obj));
			rtw_mfree((u8 *)psetstakey_para, sizeof(struct set_stakey_parm));
			res = _FAIL;
			goto exit;
		}

		init_h2fwcmd_w_parm_no_rsp(cmd, psetstakey_para, CMD_SET_STAKEY);
		cmd->rsp = (u8 *) psetstakey_rsp;
		cmd->rspsz = sizeof(struct set_stakey_rsp);

		_rtw_memcpy(psetstakey_para->addr, sta->phl_sta->mac_addr, ETH_ALEN);

		psetstakey_para->algorithm = _NO_PRIVACY_;

		res = rtw_enqueue_cmd(pcmdpriv, cmd);

	}

exit:


	return res;
}
#endif

u8 rtw_addbareq_cmd(_adapter *padapter, u8 tid, u8 *addr)
{
	struct cmd_priv *pcmdpriv = &adapter_to_dvobj(padapter)->cmdpriv;
	struct cmd_obj *cmd;
	struct addBaReq_parm *paddbareq_parm;

	u8	res = _SUCCESS;


	cmd = (struct cmd_obj *)rtw_zmalloc(sizeof(struct cmd_obj));
	if (cmd == NULL) {
		res = _FAIL;
		goto exit;
	}
	cmd->padapter = padapter;

	paddbareq_parm = (struct addBaReq_parm *)rtw_zmalloc(sizeof(struct addBaReq_parm));
	if (paddbareq_parm == NULL) {
		rtw_mfree((unsigned char *)cmd, sizeof(struct cmd_obj));
		res = _FAIL;
		goto exit;
	}

	paddbareq_parm->tid = tid;
	_rtw_memcpy(paddbareq_parm->addr, addr, ETH_ALEN);

	init_h2fwcmd_w_parm_no_rsp(cmd, paddbareq_parm, CMD_ADD_BAREQ);

	/* RTW_INFO("rtw_addbareq_cmd, tid=%d\n", tid); */

	/* rtw_enqueue_cmd(pcmdpriv, ph2c);	 */
	res = rtw_enqueue_cmd(pcmdpriv, cmd);

exit:
	return res;
}

u8 rtw_addbarsp_cmd(_adapter *padapter, u8 *addr, u16 tid,
		    struct ADDBA_request *paddba_req, u8 status,
		    u8 size, u16 start_seq)
{
	struct cmd_priv *pcmdpriv = &adapter_to_dvobj(padapter)->cmdpriv;
	struct cmd_obj *cmd;
	struct addBaRsp_parm *paddBaRsp_parm;
	u8 res = _SUCCESS;


	cmd = (struct cmd_obj *)rtw_zmalloc(sizeof(struct cmd_obj));
	if (cmd == NULL) {
		res = _FAIL;
		goto exit;
	}
	cmd->padapter = padapter;

	paddBaRsp_parm = (struct addBaRsp_parm *)rtw_zmalloc(sizeof(struct addBaRsp_parm));

	if (paddBaRsp_parm == NULL) {
		rtw_mfree((unsigned char *)cmd, sizeof(struct cmd_obj));
		res = _FAIL;
		goto exit;
	}

	_rtw_memcpy(paddBaRsp_parm->addr, addr, ETH_ALEN);
	_rtw_memcpy(&(paddBaRsp_parm->preq), paddba_req, sizeof(struct ADDBA_request));
	paddBaRsp_parm->tid = tid;
	paddBaRsp_parm->status = status;
	paddBaRsp_parm->size = size;
	paddBaRsp_parm->start_seq = start_seq;

	init_h2fwcmd_w_parm_no_rsp(cmd, paddBaRsp_parm, CMD_ADD_BARSP);

	res = rtw_enqueue_cmd(pcmdpriv, cmd);

exit:


	return res;
}

u8 rtw_delba_cmd(struct _ADAPTER *a, u8 *addr, u16 tid)
{
	struct cmd_priv *cmdpriv = &adapter_to_dvobj(a)->cmdpriv;
	struct cmd_obj *cmd = NULL;
	struct addBaReq_parm *parm = NULL;


	cmd = (struct cmd_obj *)rtw_zmalloc(sizeof(struct cmd_obj));
	if (!cmd)
		return _FAIL;
	cmd->padapter = a;

	parm = (struct addBaReq_parm *)rtw_zmalloc(sizeof(struct addBaReq_parm));
	if (!parm) {
		rtw_mfree(cmd, sizeof(struct cmd_obj));
		return _FAIL;
	}

	parm->tid = tid;
	_rtw_memcpy(parm->addr, addr, ETH_ALEN);
	init_h2fwcmd_w_parm_no_rsp(cmd, parm, CMD_DELBA);

	return rtw_enqueue_cmd(cmdpriv, cmd);
}

/* add for CONFIG_IEEE80211W, none 11w can use it */
u8 rtw_reset_securitypriv_cmd(_adapter *padapter)
{
	struct cmd_obj *cmd;
	struct drvextra_cmd_parm  *pdrvextra_cmd_parm;
	struct cmd_priv	*pcmdpriv = &adapter_to_dvobj(padapter)->cmdpriv;
	u8 res = _SUCCESS;


	cmd = (struct cmd_obj *)rtw_zmalloc(sizeof(struct cmd_obj));
	if (cmd == NULL) {
		res = _FAIL;
		goto exit;
	}
	cmd->padapter = padapter;

	pdrvextra_cmd_parm = (struct drvextra_cmd_parm *)rtw_zmalloc(sizeof(struct drvextra_cmd_parm));
	if (pdrvextra_cmd_parm == NULL) {
		rtw_mfree((unsigned char *)cmd, sizeof(struct cmd_obj));
		res = _FAIL;
		goto exit;
	}

	pdrvextra_cmd_parm->ec_id = RESET_SECURITYPRIV;
	pdrvextra_cmd_parm->type = 0;
	pdrvextra_cmd_parm->size = 0;
	pdrvextra_cmd_parm->pbuf = NULL;

	init_h2fwcmd_w_parm_no_rsp(cmd, pdrvextra_cmd_parm, CMD_SET_DRV_EXTRA);


	/* rtw_enqueue_cmd(pcmdpriv, ph2c);	 */
	res = rtw_enqueue_cmd(pcmdpriv, cmd);

exit:
	return res;

}

void free_assoc_resources_hdl(_adapter *padapter, u8 lock_scanned_queue)
{
	rtw_free_assoc_resources(padapter, lock_scanned_queue);
}

u8 rtw_free_assoc_resources_cmd(_adapter *padapter, u8 lock_scanned_queue, int flags)
{
	struct cmd_obj *cmd;
	struct drvextra_cmd_parm  *pdrvextra_cmd_parm;
	struct cmd_priv	*pcmdpriv = &adapter_to_dvobj(padapter)->cmdpriv;
	struct submit_ctx sctx;
	u8	res = _SUCCESS;

	if (flags & RTW_CMDF_DIRECTLY) {
		free_assoc_resources_hdl(padapter, lock_scanned_queue);
	}
	else {
		cmd = (struct cmd_obj *)rtw_zmalloc(sizeof(struct cmd_obj));
		if (cmd == NULL) {
			res = _FAIL;
			goto exit;
		}
		cmd->padapter = padapter;

		pdrvextra_cmd_parm = (struct drvextra_cmd_parm *)rtw_zmalloc(sizeof(struct drvextra_cmd_parm));
		if (pdrvextra_cmd_parm == NULL) {
			rtw_mfree((unsigned char *)cmd, sizeof(struct cmd_obj));
			res = _FAIL;
			goto exit;
		}

		pdrvextra_cmd_parm->ec_id = FREE_ASSOC_RESOURCES;
		pdrvextra_cmd_parm->type = lock_scanned_queue;
		pdrvextra_cmd_parm->size = 0;
		pdrvextra_cmd_parm->pbuf = NULL;

		init_h2fwcmd_w_parm_no_rsp(cmd, pdrvextra_cmd_parm, CMD_SET_DRV_EXTRA);
		if (flags & RTW_CMDF_WAIT_ACK) {
			cmd->sctx = &sctx;
			rtw_sctx_init(&sctx, 2000);
		}

		res = rtw_enqueue_cmd(pcmdpriv, cmd);

		if (res == _SUCCESS && (flags & RTW_CMDF_WAIT_ACK)) {
			rtw_sctx_wait(&sctx, __func__);
			_rtw_mutex_lock_interruptible(&pcmdpriv->sctx_mutex);
			if (sctx.status == RTW_SCTX_SUBMITTED)
				cmd->sctx = NULL;
			_rtw_mutex_unlock(&pcmdpriv->sctx_mutex);
		}
	}
exit:
	return res;

}
#if 0 /*#ifdef CONFIG_CORE_DM_CHK_TIMER*/
u8 rtw_dynamic_chk_wk_cmd(_adapter *padapter)
{
	struct cmd_obj *cmd;
	struct drvextra_cmd_parm *pdrvextra_cmd_parm;
	struct cmd_priv	*pcmdpriv = &adapter_to_dvobj(padapter)->cmdpriv;
	u8	res = _SUCCESS;


	/* only  primary padapter does this cmd */

	cmd = (struct cmd_obj *)rtw_zmalloc(sizeof(struct cmd_obj));
	if (cmd == NULL) {
		res = _FAIL;
		goto exit;
	}
	cmd->padapter = padapter;

	pdrvextra_cmd_parm = (struct drvextra_cmd_parm *)rtw_zmalloc(sizeof(struct drvextra_cmd_parm));
	if (pdrvextra_cmd_parm == NULL) {
		rtw_mfree((unsigned char *)cmd, sizeof(struct cmd_obj));
		res = _FAIL;
		goto exit;
	}

	pdrvextra_cmd_parm->ec_id = DYNAMIC_CHK_WK_CID;
	pdrvextra_cmd_parm->type = 0;
	pdrvextra_cmd_parm->size = 0;
	pdrvextra_cmd_parm->pbuf = NULL;
	init_h2fwcmd_w_parm_no_rsp(cmd, pdrvextra_cmd_parm, CMD_SET_DRV_EXTRA);


	/* rtw_enqueue_cmd(pcmdpriv, ph2c);	 */
	res = rtw_enqueue_cmd(pcmdpriv, cmd);

exit:
	return res;
}
#endif
u8 rtw_set_chbw_cmd(_adapter *padapter, u8 ch, u8 bw, u8 ch_offset, u8 flags)
{
	struct cmd_obj *pcmdobj;
	struct set_ch_parm *set_ch_parm;
	struct cmd_priv *pcmdpriv = &adapter_to_dvobj(padapter)->cmdpriv;
	struct submit_ctx sctx;
	u8 res = _SUCCESS;


	RTW_INFO(FUNC_NDEV_FMT" ch:%u, bw:%u, ch_offset:%u\n",
		 FUNC_NDEV_ARG(padapter->pnetdev), ch, bw, ch_offset);

	/* check input parameter */

	/* prepare cmd parameter */
	set_ch_parm = (struct set_ch_parm *)rtw_zmalloc(sizeof(*set_ch_parm));
	if (set_ch_parm == NULL) {
		res = _FAIL;
		goto exit;
	}
	set_ch_parm->ch = ch;
	set_ch_parm->bw = bw;
	set_ch_parm->ch_offset = ch_offset;
	set_ch_parm->do_rfk = _FALSE;/*TODO - Need check if do_rfk*/

	if (flags & RTW_CMDF_DIRECTLY) {
		/* no need to enqueue, do the cmd hdl directly and free cmd parameter */
		if (H2C_SUCCESS != rtw_set_chbw_hdl(padapter, (u8 *)set_ch_parm))
			res = _FAIL;

		rtw_mfree((u8 *)set_ch_parm, sizeof(*set_ch_parm));
	} else {
		/* need enqueue, prepare cmd_obj and enqueue */
		pcmdobj = (struct cmd_obj *)rtw_zmalloc(sizeof(struct cmd_obj));
		if (pcmdobj == NULL) {
			rtw_mfree((u8 *)set_ch_parm, sizeof(*set_ch_parm));
			res = _FAIL;
			goto exit;
		}
		pcmdobj->padapter = padapter;

		init_h2fwcmd_w_parm_no_rsp(pcmdobj, set_ch_parm, CMD_SET_CHANNEL);

		if (flags & RTW_CMDF_WAIT_ACK) {
			pcmdobj->sctx = &sctx;
			rtw_sctx_init(&sctx, 10 * 1000);
		}

		res = rtw_enqueue_cmd(pcmdpriv, pcmdobj);

		if (res == _SUCCESS && (flags & RTW_CMDF_WAIT_ACK)) {
			rtw_sctx_wait(&sctx, __func__);
			_rtw_mutex_lock_interruptible(&pcmdpriv->sctx_mutex);
			if (sctx.status == RTW_SCTX_SUBMITTED)
				pcmdobj->sctx = NULL;
			_rtw_mutex_unlock(&pcmdpriv->sctx_mutex);
		}
	}

	/* do something based on res... */
exit:
	RTW_INFO(FUNC_NDEV_FMT" res:%u\n", FUNC_NDEV_ARG(padapter->pnetdev), res);
	return res;
}

static u8 _rtw_set_chplan_cmd(_adapter *adapter, int flags
	, u8 chplan, u8 chplan_6g, const struct country_chplan *country_ent
	, enum regd_src_t regd_src, enum rtw_regd_inr inr
	, const struct country_ie_slave_record *cisr)
{
	struct cmd_obj *cmdobj;
	struct SetChannelPlan_param *parm;
	struct cmd_priv *pcmdpriv = &adapter_to_dvobj(adapter)->cmdpriv;
	struct submit_ctx sctx;
#ifdef PLATFORM_LINUX
	bool rtnl_lock_needed = rtw_rtnl_lock_needed(adapter_to_dvobj(adapter));
#endif
	u8 res = _SUCCESS;

	/* check if allow software config */
	if (rtw_rfctl_is_disable_sw_channel_plan(adapter_to_dvobj(adapter)) == _TRUE) {
		res = _FAIL;
		goto exit;
	}

	if (country_ent) {
		/* if country_entry is provided, replace chplan */
		chplan = country_ent->chplan;
		#if CONFIG_IEEE80211_BAND_6GHZ
		chplan_6g = country_ent->chplan_6g;
		#endif
	}

	/* prepare cmd parameter */
	parm = (struct SetChannelPlan_param *)rtw_zmalloc(sizeof(*parm));
	if (parm == NULL) {
		res = _FAIL;
		goto exit;
	}
	parm->regd_src = regd_src;
	parm->inr = inr;
	if (country_ent) {
		_rtw_memcpy(&parm->country_ent, country_ent, sizeof(parm->country_ent));
		parm->has_country = 1;
	}
	parm->channel_plan = chplan;
#if CONFIG_IEEE80211_BAND_6GHZ
	parm->channel_plan_6g = chplan_6g;
#endif
#ifdef CONFIG_80211D
	if (cisr) {
		_rtw_memcpy(&parm->cisr, cisr, sizeof(*cisr));
		parm->has_cisr = 1;
	}
#endif
#ifdef PLATFORM_LINUX
	if (flags & (RTW_CMDF_DIRECTLY | RTW_CMDF_WAIT_ACK))
		parm->rtnl_lock_needed = rtnl_lock_needed; /* synchronous call, follow caller's */
	else
		parm->rtnl_lock_needed = 1; /* asynchronous call, always needed */
#endif

	if (flags & RTW_CMDF_DIRECTLY) {
		/* no need to enqueue, do the cmd hdl directly and free cmd parameter */
		if (H2C_SUCCESS != rtw_set_chplan_hdl(adapter, (u8 *)parm))
			res = _FAIL;
		rtw_mfree((u8 *)parm, sizeof(*parm));
	} else {
		/* need enqueue, prepare cmd_obj and enqueue */
		cmdobj = (struct cmd_obj *)rtw_zmalloc(sizeof(*cmdobj));
		if (cmdobj == NULL) {
			res = _FAIL;
			rtw_mfree((u8 *)parm, sizeof(*parm));
			goto exit;
		}
		cmdobj->padapter = adapter;

		init_h2fwcmd_w_parm_no_rsp(cmdobj, parm, CMD_SET_CHANPLAN);

		if (flags & RTW_CMDF_WAIT_ACK) {
			cmdobj->sctx = &sctx;
			rtw_sctx_init(&sctx, 2000);
		}

		res = rtw_enqueue_cmd(pcmdpriv, cmdobj);

		if (res == _SUCCESS && (flags & RTW_CMDF_WAIT_ACK)) {
			rtw_sctx_wait(&sctx, __func__);
			_rtw_mutex_lock_interruptible(&pcmdpriv->sctx_mutex);
			if (sctx.status == RTW_SCTX_SUBMITTED)
				cmdobj->sctx = NULL;
			_rtw_mutex_unlock(&pcmdpriv->sctx_mutex);
			if (sctx.status != RTW_SCTX_DONE_SUCCESS)
				res = _FAIL;
		}

		/* allow set channel plan when cmd_thread is not running */
		if (res != _SUCCESS && (flags & RTW_CMDF_WAIT_ACK)) {
			parm = (struct SetChannelPlan_param *)rtw_zmalloc(sizeof(*parm));
			if (parm == NULL) {
				res = _FAIL;
				goto exit;
			}
			parm->regd_src = regd_src;
			parm->inr = inr;
			if (country_ent) {
				_rtw_memcpy(&parm->country_ent, country_ent, sizeof(parm->country_ent));
				parm->has_country = 1;
			}
			parm->channel_plan = chplan;
			#if CONFIG_IEEE80211_BAND_6GHZ
			parm->channel_plan_6g = chplan_6g;
			#endif
			#ifdef CONFIG_80211D
			if (cisr) {
				_rtw_memcpy(&parm->cisr, cisr, sizeof(*cisr));
				parm->has_cisr = 1;
			}
			#endif
			#ifdef PLATFORM_LINUX
			parm->rtnl_lock_needed = rtnl_lock_needed; /* synchronous call, follow caller's */
			#endif

			if (H2C_SUCCESS != rtw_set_chplan_hdl(adapter, (u8 *)parm))
				res = _FAIL;
			else
				res = _SUCCESS;
			rtw_mfree((u8 *)parm, sizeof(*parm));
		}
	}

exit:
	return res;
}

inline u8 rtw_set_chplan_cmd(_adapter *adapter, int flags, u8 chplan, u8 chplan_6g, enum rtw_regd_inr inr)
{
	return _rtw_set_chplan_cmd(adapter, flags, chplan, chplan_6g, NULL, REGD_SRC_RTK_PRIV, inr, NULL);
}

inline u8 rtw_set_country_cmd(_adapter *adapter, int flags, const char *country_code, enum rtw_regd_inr inr)
{
	struct country_chplan ent;

	if (IS_ALPHA2_WORLDWIDE(country_code)) {
		rtw_get_chplan_worldwide(&ent);
		goto cmd;
	}

	if (is_alpha(country_code[0]) == _FALSE
	    || is_alpha(country_code[1]) == _FALSE
	   ) {
		RTW_PRINT("%s input country_code is not alpha2\n", __func__);
		return _FAIL;
	}

	if (!rtw_get_chplan_from_country(country_code, &ent)) {
		RTW_PRINT("%s unsupported country_code:\"%c%c\"\n", __func__, country_code[0], country_code[1]);
		return _FAIL;
	}

cmd:
	RTW_PRINT("%s country_code:\"%c%c\"\n", __func__, country_code[0], country_code[1]);

	return _rtw_set_chplan_cmd(adapter, flags, RTW_CHPLAN_UNSPECIFIED, RTW_CHPLAN_6G_UNSPECIFIED, &ent, REGD_SRC_RTK_PRIV, inr, NULL);
}

#ifdef CONFIG_REGD_SRC_FROM_OS
inline u8 rtw_sync_os_regd_cmd(_adapter *adapter, int flags, const char *country_code, u8 dfs_region, enum rtw_regd_inr inr)
{
	struct country_chplan ent;
	struct country_chplan rtk_ent;
	bool rtk_ent_exist;

	rtk_ent_exist = rtw_get_chplan_from_country(country_code, &rtk_ent);

	_rtw_memcpy(ent.alpha2, country_code, 2);

	/*
	* Regulation follows OS, the internal txpwr limit selection is searched by alpha2
	*     "00" => WW, others use string mapping
	* When  no matching txpwr limit selection is found, use
	*     1. txpwr lmit selection associated with alpha2 inside driver regulation database
	*     2. WW when driver has no support of this alpha2
	*/

	ent.chplan = rtk_ent_exist ? rtk_ent.chplan : RTW_CHPLAN_UNSPECIFIED;
	#if CONFIG_IEEE80211_BAND_6GHZ
	ent.chplan_6g = rtk_ent_exist ? rtk_ent.chplan_6g : RTW_CHPLAN_6G_UNSPECIFIED;
	#endif
	ent.edcca_mode_2g_override = rtk_ent_exist ? rtk_ent.edcca_mode_2g_override : RTW_EDCCA_DEF;
	#if CONFIG_IEEE80211_BAND_5GHZ
	ent.edcca_mode_5g_override = rtk_ent_exist ? rtk_ent.edcca_mode_5g_override : RTW_EDCCA_DEF;
	#endif
	#if CONFIG_IEEE80211_BAND_6GHZ
	ent.edcca_mode_6g_override = rtk_ent_exist ? rtk_ent.edcca_mode_6g_override : RTW_EDCCA_DEF;
	#endif
	ent.txpwr_lmt_override = rtk_ent_exist ? rtk_ent.txpwr_lmt_override : TXPWR_LMT_DEF;
	#if defined(CONFIG_80211AC_VHT) || defined(CONFIG_80211AX_HE)
	ent.proto_en = CHPLAN_PROTO_EN_ALL;
	#endif

	/* TODO: dfs_region */

	return _rtw_set_chplan_cmd(adapter, flags, RTW_CHPLAN_UNSPECIFIED, RTW_CHPLAN_6G_UNSPECIFIED, &ent, REGD_SRC_OS, inr, NULL);
}
#endif /* CONFIG_REGD_SRC_FROM_OS */

u8 rtw_get_chplan_cmd(_adapter *adapter, int flags, struct get_chplan_resp **chplan)
{
	struct cmd_obj *cmdobj;
	struct get_channel_plan_param *parm;
	struct cmd_priv *pcmdpriv = &adapter_to_dvobj(adapter)->cmdpriv;
	struct submit_ctx sctx;
	u8 res = _FAIL;

	if (!(flags & (RTW_CMDF_DIRECTLY | RTW_CMDF_WAIT_ACK)))
		goto exit;

	/* prepare cmd parameter */
	parm = rtw_zmalloc(sizeof(*parm));
	if (parm == NULL)
		goto exit;
	parm->chplan = chplan;

	if (flags & RTW_CMDF_DIRECTLY) {
		/* no need to enqueue, do the cmd hdl directly and free cmd parameter */
		if (H2C_SUCCESS == rtw_get_chplan_hdl(adapter, (u8 *)parm))
			res = _SUCCESS;
		rtw_mfree((u8 *)parm, sizeof(*parm));
	} else {
		/* need enqueue, prepare cmd_obj and enqueue */
		cmdobj = (struct cmd_obj *)rtw_zmalloc(sizeof(*cmdobj));
		if (cmdobj == NULL) {
			rtw_mfree((u8 *)parm, sizeof(*parm));
			goto exit;
		}
		cmdobj->padapter = adapter;

		init_h2fwcmd_w_parm_no_rsp(cmdobj, parm, CMD_GET_CHANPLAN);

		if (flags & RTW_CMDF_WAIT_ACK) {
			cmdobj->sctx = &sctx;
			rtw_sctx_init(&sctx, 2000);
		}

		res = rtw_enqueue_cmd(pcmdpriv, cmdobj);

		if (res == _SUCCESS && (flags & RTW_CMDF_WAIT_ACK)) {
			rtw_sctx_wait(&sctx, __func__);
			_rtw_mutex_lock_interruptible(&pcmdpriv->sctx_mutex);
			if (sctx.status == RTW_SCTX_SUBMITTED)
				cmdobj->sctx = NULL;
			_rtw_mutex_unlock(&pcmdpriv->sctx_mutex);
			if (sctx.status != RTW_SCTX_DONE_SUCCESS)
				res = _FAIL;
		}

		/* allow get channel plan when cmd_thread is not running */
		if (res != _SUCCESS && (flags & RTW_CMDF_WAIT_ACK)) {
			parm = rtw_zmalloc(sizeof(*parm));
			if (parm == NULL)
				goto exit;
			parm->chplan = chplan;

			if (H2C_SUCCESS == rtw_get_chplan_hdl(adapter, (u8 *)parm))
				res = _SUCCESS;

			rtw_mfree((u8 *)parm, sizeof(*parm));
		}
	}

exit:
	return res;
}

#ifdef CONFIG_80211D
inline u8 rtw_apply_recv_country_ie_cmd(_adapter *adapter, int flags, enum band_type band,u8 opch, const u8 *country_ie)
{
	struct country_chplan ent;
	struct country_ie_slave_record cisr;

	rtw_get_chplan_from_recv_country_ie(adapter, band, opch, country_ie, &ent, &cisr, NULL);

	return _rtw_set_chplan_cmd(adapter, flags, RTW_CHPLAN_UNSPECIFIED, RTW_CHPLAN_6G_UNSPECIFIED
		, NULL, REGD_SRC_RTK_PRIV, RTW_REGD_SET_BY_COUNTRY_IE, &cisr);
}
#endif /* CONFIG_80211D */

#ifdef CONFIG_RTW_LED_HANDLED_BY_CMD_THREAD
u8 rtw_led_blink_cmd(_adapter *padapter, void *pLed)
{
	struct cmd_obj	*pcmdobj;
	struct LedBlink_param *ledBlink_param;
	struct cmd_priv *pcmdpriv = &adapter_to_dvobj(padapter)->cmdpriv;
	u8 res = _SUCCESS;

	pcmdobj = (struct cmd_obj *)rtw_zmalloc(sizeof(struct cmd_obj));
	if (pcmdobj == NULL) {
		res = _FAIL;
		goto exit;
	}
	pcmdobj->padapter = padapter;

	ledBlink_param = (struct LedBlink_param *)rtw_zmalloc(sizeof(struct LedBlink_param));
	if (ledBlink_param == NULL) {
		rtw_mfree((u8 *)pcmdobj, sizeof(struct cmd_obj));
		res = _FAIL;
		goto exit;
	}

	ledBlink_param->pLed = pLed;

	init_h2fwcmd_w_parm_no_rsp(pcmdobj, ledBlink_param, CMD_LEDBLINK);
	res = rtw_enqueue_cmd(pcmdpriv, pcmdobj);

exit:
	return res;
}
#endif /*CONFIG_RTW_LED_HANDLED_BY_CMD_THREAD*/

u8 rtw_set_csa_cmd(_adapter *adapter)
{
	struct cmd_obj *cmdobj;
	struct cmd_priv *cmdpriv = &adapter_to_dvobj(adapter)->cmdpriv;
	u8 res = _SUCCESS;

	cmdobj = rtw_zmalloc(sizeof(struct cmd_obj));
	if (cmdobj == NULL) {
		res = _FAIL;
		goto exit;
	}
	cmdobj->padapter = adapter;

	init_h2fwcmd_w_parm_no_parm_rsp(cmdobj, CMD_SET_CHANSWITCH);
	res = rtw_enqueue_cmd(cmdpriv, cmdobj);

exit:
	return res;
}

u8 rtw_tdls_cmd(_adapter *padapter, u8 *addr, u8 option)
{
	u8 res = _SUCCESS;
#ifdef CONFIG_TDLS
	struct	cmd_obj	*pcmdobj;
	struct	TDLSoption_param *TDLSoption;
	struct	mlme_priv *pmlmepriv = &padapter->mlmepriv;
	struct	cmd_priv *pcmdpriv = &adapter_to_dvobj(padapter)->cmdpriv;

	pcmdobj = (struct cmd_obj *)rtw_zmalloc(sizeof(struct cmd_obj));
	if (pcmdobj == NULL) {
		res = _FAIL;
		goto exit;
	}
	pcmdobj->padapter = padapter;

	TDLSoption = (struct TDLSoption_param *)rtw_zmalloc(sizeof(struct TDLSoption_param));
	if (TDLSoption == NULL) {
		rtw_mfree((u8 *)pcmdobj, sizeof(struct cmd_obj));
		res = _FAIL;
		goto exit;
	}

	_rtw_spinlock(&(padapter->tdlsinfo.cmd_lock));
	if (addr != NULL)
		_rtw_memcpy(TDLSoption->addr, addr, 6);
	TDLSoption->option = option;
	_rtw_spinunlock(&(padapter->tdlsinfo.cmd_lock));
	init_h2fwcmd_w_parm_no_rsp(pcmdobj, TDLSoption, CMD_TDLS);
	res = rtw_enqueue_cmd(pcmdpriv, pcmdobj);

exit:
#endif /* CONFIG_TDLS */

	return res;
}

u8 rtw_ssmps_wk_hdl(_adapter *adapter, struct ssmps_cmd_parm *ssmp_param)
{
	u8 res = _SUCCESS;
	struct sta_info *sta = ssmp_param->sta;
	u8 smps = ssmp_param->smps;

	if (sta == NULL)
		return _FALSE;

	if (smps)
		rtw_ssmps_enter(adapter, sta);
	else
		rtw_ssmps_leave(adapter, sta);
	return res;
}

u8 rtw_ssmps_wk_cmd(_adapter *adapter, struct sta_info *sta, u8 smps, u8 enqueue)
{
	struct cmd_obj *cmdobj;
	struct drvextra_cmd_parm *cmd_parm;
	struct ssmps_cmd_parm *ssmp_param;
	struct cmd_priv	*pcmdpriv = &adapter_to_dvobj(adapter)->cmdpriv;
	u8 res = _SUCCESS;

	if (enqueue) {
		cmdobj = (struct cmd_obj *)rtw_zmalloc(sizeof(struct cmd_obj));
		if (cmdobj == NULL) {
			res = _FAIL;
			goto exit;
		}
		cmdobj->padapter = adapter;

		cmd_parm = (struct drvextra_cmd_parm *)rtw_zmalloc(sizeof(struct drvextra_cmd_parm));
		if (cmd_parm == NULL) {
			rtw_mfree((unsigned char *)cmdobj, sizeof(struct cmd_obj));
			res = _FAIL;
			goto exit;
		}

		ssmp_param = (struct ssmps_cmd_parm *)rtw_zmalloc(sizeof(struct ssmps_cmd_parm));
		if (ssmp_param == NULL) {
			rtw_mfree((u8 *)cmdobj, sizeof(struct cmd_obj));
			rtw_mfree((u8 *)cmd_parm, sizeof(struct drvextra_cmd_parm));
			res = _FAIL;
			goto exit;
		}

		ssmp_param->smps = smps;
		ssmp_param->sta = sta;

		cmd_parm->ec_id = SSMPS_WK_CID;
		cmd_parm->type = 0;
		cmd_parm->size = sizeof(struct ssmps_cmd_parm);
		cmd_parm->pbuf = (u8 *)ssmp_param;

		init_h2fwcmd_w_parm_no_rsp(cmdobj, cmd_parm, CMD_SET_DRV_EXTRA);

		res = rtw_enqueue_cmd(pcmdpriv, cmdobj);
	} else {
		struct ssmps_cmd_parm tmp_ssmp_param;

		tmp_ssmp_param.smps = smps;
		tmp_ssmp_param.sta = sta;
		rtw_ssmps_wk_hdl(adapter, &tmp_ssmp_param);
	}

exit:
	return res;
}

#ifdef CONFIG_SUPPORT_STATIC_SMPS
u8 _ssmps_chk_by_tp(_adapter *adapter, u8 from_timer)
{
	u8 enter_smps = _FALSE;
	struct mlme_priv *pmlmepriv = &adapter->mlmepriv;
	struct mlme_ext_priv *pmlmeext = &(adapter->mlmeextpriv);
	struct sta_priv *pstapriv = &adapter->stapriv;
	struct sta_info *psta;
	u32 tx_tp_mbits, rx_tp_mbits;

	if (!MLME_IS_STA(adapter) ||
		!rtw_hw_is_mimo_support(adapter) ||
		!pmlmeext->ssmps_en ||
		(pmlmeext->chandef.chan > 14)
	)
		return enter_smps;

	psta = rtw_get_stainfo(pstapriv, get_bssid(pmlmepriv));
	if (psta == NULL) {
		RTW_ERR(ADPT_FMT" sta == NULL\n", ADPT_ARG(adapter));
		rtw_warn_on(1);
		return enter_smps;
	}
	/*TODO*/
	if (psta->phl_sta->asoc_cap.nss_tx == 1)
		return enter_smps;

	tx_tp_mbits = psta->sta_stats.tx_tp_kbits >> 10;
	rx_tp_mbits = psta->sta_stats.rx_tp_kbits >> 10;

	#ifdef DBG_STATIC_SMPS
	if (pmlmeext->ssmps_test) {
		enter_smps = (pmlmeext->ssmps_test_en == 1) ? _TRUE : _FALSE;
	}
	else
	#endif
	{
		if ((tx_tp_mbits <= pmlmeext->ssmps_tx_tp_th) &&
			(rx_tp_mbits <= pmlmeext->ssmps_rx_tp_th))
			enter_smps = _TRUE;
		else
			enter_smps = _FALSE;
	}

	if (1) {
		RTW_INFO(FUNC_ADPT_FMT" tx_tp:%d [%d], rx_tp:%d [%d] , SSMPS enter :%s\n",
			FUNC_ADPT_ARG(adapter),
			tx_tp_mbits, pmlmeext->ssmps_tx_tp_th,
			rx_tp_mbits, pmlmeext->ssmps_rx_tp_th,
			(enter_smps == _TRUE) ? "True" : "False");
		#ifdef DBG_STATIC_SMPS
		RTW_INFO(FUNC_ADPT_FMT" test:%d test_en:%d\n",
			FUNC_ADPT_ARG(adapter),
			pmlmeext->ssmps_test,
			pmlmeext->ssmps_test_en);
		#endif
	}

	if (enter_smps) {
		if (!from_timer && psta->phl_sta->sm_ps != SM_PS_STATIC)
			rtw_ssmps_enter(adapter, psta);
	} else {
		if (!from_timer && psta->phl_sta->sm_ps != SM_PS_DISABLE)
			rtw_ssmps_leave(adapter, psta);
		else {
			u8 ps_change = _FALSE;

			if (enter_smps && psta->phl_sta->sm_ps != SM_PS_STATIC)
				ps_change = _TRUE;
			else if (!enter_smps && psta->phl_sta->sm_ps != SM_PS_DISABLE)
				ps_change = _TRUE;

			if (ps_change)
				rtw_ssmps_wk_cmd(adapter, psta, enter_smps, 1);
		}
	}

	return enter_smps;
}
#endif /*CONFIG_SUPPORT_STATIC_SMPS*/

#ifdef CONFIG_CTRL_TXSS_BY_TP
void rtw_ctrl_txss_update(_adapter *adapter, struct sta_info *sta)
{
	struct mlme_ext_priv *pmlmeext = &(adapter->mlmeextpriv);

	pmlmeext->txss_bk = sta->phl_sta->asoc_cap.nss_rx;
}

u8 rtw_ctrl_txss(_adapter *adapter, struct sta_info *sta, bool tx_1ss)
{
	struct mlme_ext_priv *pmlmeext = &(adapter->mlmeextpriv);
	struct pwrctrl_priv *pwrpriv = adapter_to_pwrctl(adapter);
	u8 lps_changed = _FALSE;
	u8 rst = _SUCCESS;

	if (pmlmeext->txss_1ss == tx_1ss)
		return _FALSE;
	/*
	if (pwrpriv->bLeisurePs && pwrpriv->pwr_mode != PM_PS_MODE_ACTIVE) {
		lps_changed = _TRUE;
		LPS_Leave(adapter, "LPS_CTRL_TXSS");
	}
	*/

	RTW_INFO(ADPT_FMT" STA [" MAC_FMT "] set tx to %d ss\n",
		ADPT_ARG(adapter), MAC_ARG(sta->phl_sta->mac_addr),
		(tx_1ss) ? 1 : rtw_get_sta_tx_nss(adapter, sta));

	/*update ra*/
	if (tx_1ss)
		sta->phl_sta->asoc_cap.nss_rx = 1;
	else
		sta->phl_sta->asoc_cap.nss_rx = pmlmeext->txss_bk;
	rtw_phl_cmd_change_stainfo(adapter_to_dvobj(adapter)->phl,
					   sta->phl_sta,
					   STA_CHG_RAMASK,
					   NULL,
					   0,
					   PHL_CMD_DIRECTLY,
					   0);

	/*configure trx mode*/
	/*rtw_phydm_trx_cfg(adapter, tx_1ss);*/
	pmlmeext->txss_1ss = tx_1ss;
	/*
	if (lps_changed)
		LPS_Enter(adapter, "LPS_CTRL_TXSS");
	*/

	return rst;
}

u8 rtw_ctrl_txss_wk_hdl(_adapter *adapter, struct txss_cmd_parm *txss_param)
{
	if (!txss_param->sta)
		return _FALSE;

	return rtw_ctrl_txss(adapter, txss_param->sta, txss_param->tx_1ss);
}

u8 rtw_ctrl_txss_wk_cmd(_adapter *adapter, struct sta_info *sta, bool tx_1ss, u8 flag)
{
	struct cmd_obj *cmdobj;
	struct drvextra_cmd_parm *cmd_parm;
	struct txss_cmd_parm *txss_param;
	struct cmd_priv *pcmdpriv = &adapter_to_dvobj(adapter)->cmdpriv;
	struct submit_ctx sctx;
	u8	res = _SUCCESS;

	txss_param = (struct txss_cmd_parm *)rtw_zmalloc(sizeof(struct txss_cmd_parm));
	if (txss_param == NULL) {
		res = _FAIL;
		goto exit;
	}

	txss_param->tx_1ss = tx_1ss;
	txss_param->sta = sta;

	if (flag & RTW_CMDF_DIRECTLY) {
		res = rtw_ctrl_txss_wk_hdl(adapter, txss_param);
		rtw_mfree((u8 *)txss_param, sizeof(*txss_param));
	} else {
		cmdobj = (struct cmd_obj *)rtw_zmalloc(sizeof(struct cmd_obj));
		if (cmdobj == NULL) {
			res = _FAIL;
			goto exit;
		}
		cmdobj->padapter = adapter;

		cmd_parm = (struct drvextra_cmd_parm *)rtw_zmalloc(sizeof(struct drvextra_cmd_parm));
		if (cmd_parm == NULL) {
			rtw_mfree((u8 *)cmdobj, sizeof(struct cmd_obj));
			res = _FAIL;
			goto exit;
		}

		cmd_parm->ec_id = TXSS_WK_CID;
		cmd_parm->type = 0;
		cmd_parm->size = sizeof(struct txss_cmd_parm);
		cmd_parm->pbuf = (u8 *)txss_param;

		init_h2fwcmd_w_parm_no_rsp(cmdobj, cmd_parm, CMD_SET_DRV_EXTRA);

		if (flag & RTW_CMDF_WAIT_ACK) {
			cmdobj->sctx = &sctx;
			rtw_sctx_init(&sctx, 10 * 1000);
		}

		res = rtw_enqueue_cmd(pcmdpriv, cmdobj);
		if (res == _SUCCESS && (flag & RTW_CMDF_WAIT_ACK)) {
			rtw_sctx_wait(&sctx, __func__);
			_rtw_mutex_lock_interruptible(&pcmdpriv->sctx_mutex);
			if (sctx.status == RTW_SCTX_SUBMITTED)
				cmdobj->sctx = NULL;
			_rtw_mutex_unlock(&pcmdpriv->sctx_mutex);
			if (sctx.status != RTW_SCTX_DONE_SUCCESS)
				res = _FAIL;
		}
	}

exit:
	return res;
}

void rtw_ctrl_tx_ss_by_tp(_adapter *adapter, u8 from_timer)
{
	bool tx_1ss  = _FALSE; /*change tx from 2ss to 1ss*/
	struct mlme_priv *pmlmepriv = &adapter->mlmepriv;
	struct mlme_ext_priv *pmlmeext = &(adapter->mlmeextpriv);
	struct sta_priv *pstapriv = &adapter->stapriv;
	struct sta_info *psta;
	u32 tx_tp_mbits;

	if (!MLME_IS_STA(adapter) ||
		!rtw_hw_is_mimo_support(adapter) ||
		!pmlmeext->txss_ctrl_en
	)
		return;

	psta = rtw_get_stainfo(pstapriv, get_bssid(pmlmepriv));
	if (psta == NULL) {
		RTW_ERR(ADPT_FMT" sta == NULL\n", ADPT_ARG(adapter));
		rtw_warn_on(1);
		return;
	}

	tx_tp_mbits = psta->sta_stats.tx_tp_kbits >> 10;
	if (tx_tp_mbits >= pmlmeext->txss_tp_th) {
		tx_1ss = _FALSE;
	} else {
		if (pmlmeext->txss_tp_chk_cnt && --pmlmeext->txss_tp_chk_cnt)
			tx_1ss = _FALSE;
		else
			tx_1ss = _TRUE;
	}

	if (1) {
		RTW_INFO(FUNC_ADPT_FMT" tx_tp:%d [%d] tx_1ss(%d):%s\n",
			FUNC_ADPT_ARG(adapter),
			tx_tp_mbits, pmlmeext->txss_tp_th,
			pmlmeext->txss_tp_chk_cnt,
			(tx_1ss == _TRUE) ? "True" : "False");
	}

	if (pmlmeext->txss_1ss != tx_1ss) {
		if (from_timer)
			rtw_ctrl_txss_wk_cmd(adapter, psta, tx_1ss, 0);
		else
			rtw_ctrl_txss(adapter, psta, tx_1ss);
	}
}
#ifdef DBG_CTRL_TXSS
void dbg_ctrl_txss(_adapter *adapter, bool tx_1ss)
{
	struct mlme_priv *pmlmepriv = &adapter->mlmepriv;
	struct mlme_ext_priv *pmlmeext = &(adapter->mlmeextpriv);
	struct sta_priv *pstapriv = &adapter->stapriv;
	struct sta_info *psta;

	if (!MLME_IS_STA(adapter) ||
		!rtw_hw_is_mimo_support(adapter)
	)
		return;

	psta = rtw_get_stainfo(pstapriv, get_bssid(pmlmepriv));
	if (psta == NULL) {
		RTW_ERR(ADPT_FMT" sta == NULL\n", ADPT_ARG(adapter));
		rtw_warn_on(1);
		return;
	}

	rtw_ctrl_txss(adapter, psta, tx_1ss);
}
#endif
#endif /*CONFIG_CTRL_TXSS_BY_TP*/

#ifdef CONFIG_LPS
#ifdef CONFIG_LPS_CHK_BY_TP
#ifdef LPS_BCN_CNT_MONITOR
static u8 _bcn_cnt_expected(struct sta_info *psta)
{
	_adapter *adapter = psta->padapter;
	struct mlme_ext_priv *pmlmeext = &adapter->mlmeextpriv;
	struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
	u8 dtim = rtw_get_bcn_dtim_period(adapter);
	u8 bcn_cnt = 0;

	if ((pmlmeinfo->bcn_interval !=0) && (dtim != 0))
		bcn_cnt = 2000 / pmlmeinfo->bcn_interval / dtim * 4 / 5; /*2s*/
	if (0)
		RTW_INFO("%s bcn_cnt:%d\n", __func__, bcn_cnt);

	if (bcn_cnt == 0) {
		RTW_ERR(FUNC_ADPT_FMT" bcn_cnt == 0\n", FUNC_ADPT_ARG(adapter));
		rtw_warn_on(1);
	}

	return bcn_cnt;
}
#endif
u8 _lps_chk_by_tp(_adapter *adapter, u8 from_timer)
{
	u8 enter_ps = _FALSE;
	struct mlme_priv *pmlmepriv = &adapter->mlmepriv;
	struct sta_priv *pstapriv = &adapter->stapriv;
	struct sta_info *psta;
	struct pwrctrl_priv *pwrpriv = adapter_to_pwrctl(adapter);
	u32 tx_tp_mbits, rx_tp_mbits, bi_tp_mbits;
	u8 rx_bcn_cnt;

	psta = rtw_get_stainfo(pstapriv, get_bssid(pmlmepriv));
	if (psta == NULL) {
		RTW_ERR(ADPT_FMT" sta == NULL\n", ADPT_ARG(adapter));
		rtw_warn_on(1);
		return enter_ps;
	}

	rx_bcn_cnt = rtw_get_bcn_cnt(psta->padapter);
	psta->sta_stats.acc_tx_bytes = psta->sta_stats.tx_bytes;
	psta->sta_stats.acc_rx_bytes = psta->sta_stats.rx_bytes;

#if 1
	tx_tp_mbits = psta->sta_stats.tx_tp_kbits >> 10;
	rx_tp_mbits = psta->sta_stats.rx_tp_kbits >> 10;
	bi_tp_mbits = tx_tp_mbits + rx_tp_mbits;
#else
	tx_tp_mbits = psta->sta_stats.smooth_tx_tp_kbits >> 10;
	rx_tp_mbits = psta->sta_stats.smooth_rx_tp_kbits >> 10;
	bi_tp_mbits = tx_tp_mbits + rx_tp_mbits;
#endif

	if ((bi_tp_mbits >= pwrpriv->lps_bi_tp_th) ||
		(tx_tp_mbits >= pwrpriv->lps_tx_tp_th) ||
		(rx_tp_mbits >= pwrpriv->lps_rx_tp_th)) {
		enter_ps = _FALSE;
		pwrpriv->lps_chk_cnt = pwrpriv->lps_chk_cnt_th;
	}
	else {
#ifdef LPS_BCN_CNT_MONITOR
		u8 bcn_cnt = _bcn_cnt_expected(psta);

		if (bcn_cnt && (rx_bcn_cnt < bcn_cnt)) {
			pwrpriv->lps_chk_cnt = 2;
			RTW_ERR(FUNC_ADPT_FMT" BCN_CNT:%d(%d) invalid\n",
				FUNC_ADPT_ARG(adapter), rx_bcn_cnt, bcn_cnt);
		}
#endif

		if (pwrpriv->lps_chk_cnt && --pwrpriv->lps_chk_cnt)
			enter_ps = _FALSE;
		else
			enter_ps = _TRUE;
	}

	if (1) {
		RTW_INFO(FUNC_ADPT_FMT" tx_tp:%d [%d], rx_tp:%d [%d], bi_tp:%d [%d], enter_ps(%d):%s\n",
			FUNC_ADPT_ARG(adapter),
			tx_tp_mbits, pwrpriv->lps_tx_tp_th,
			rx_tp_mbits, pwrpriv->lps_rx_tp_th,
			bi_tp_mbits, pwrpriv->lps_bi_tp_th,
			pwrpriv->lps_chk_cnt,
			(enter_ps == _TRUE) ? "True" : "False");
		RTW_INFO(FUNC_ADPT_FMT" tx_pkt_cnt :%d [%d], rx_pkt_cnt :%d [%d]\n",
			FUNC_ADPT_ARG(adapter),
			pmlmepriv->LinkDetectInfo.NumTxOkInPeriod,
			pwrpriv->lps_tx_pkts,
			pmlmepriv->LinkDetectInfo.NumRxUnicastOkInPeriod,
			pwrpriv->lps_rx_pkts);
		if (!adapter->bsta_tp_dump)
			RTW_INFO(FUNC_ADPT_FMT" bcn_cnt:%d (per-%d second)\n",
			FUNC_ADPT_ARG(adapter),
			rx_bcn_cnt,
			2);
	}

	if (enter_ps) {
		if (!from_timer)
			LPS_Enter(adapter, "TRAFFIC_IDLE");
	} else {
		if (!from_timer)
			LPS_Leave(adapter, "TRAFFIC_BUSY");
		else {
			#ifdef CONFIG_CONCURRENT_MODE
			#ifndef CONFIG_FW_MULTI_PORT_SUPPORT
			if (adapter->hw_port == HW_PORT0)
			#endif
			#endif
				rtw_lps_ctrl_wk_cmd(adapter, LPS_CTRL_TRAFFIC_BUSY, 0);
		}
	}

	return enter_ps;
}
#endif

static u8 _lps_chk_by_pkt_cnts(_adapter *padapter, u8 from_timer, u8 bBusyTraffic)
{		
	struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
	u8	bEnterPS = _FALSE;

	/* check traffic for  powersaving. */
	if (((pmlmepriv->LinkDetectInfo.NumRxUnicastOkInPeriod + pmlmepriv->LinkDetectInfo.NumTxOkInPeriod) > 8) ||
		#ifdef CONFIG_LPS_SLOW_TRANSITION
		(pmlmepriv->LinkDetectInfo.NumRxUnicastOkInPeriod > 2)
		#else /* CONFIG_LPS_SLOW_TRANSITION */
		(pmlmepriv->LinkDetectInfo.NumRxUnicastOkInPeriod > 4)
		#endif /* CONFIG_LPS_SLOW_TRANSITION */
	) {
		#ifdef DBG_RX_COUNTER_DUMP
		if (padapter->dump_rx_cnt_mode & DUMP_DRV_TRX_COUNTER_DATA)
			RTW_INFO("(-)Tx = %d, Rx = %d\n", pmlmepriv->LinkDetectInfo.NumTxOkInPeriod, pmlmepriv->LinkDetectInfo.NumRxUnicastOkInPeriod);
		#endif

		bEnterPS = _FALSE;
		#ifdef CONFIG_LPS_SLOW_TRANSITION
		if (bBusyTraffic == _TRUE) {
			if (pmlmepriv->LinkDetectInfo.TrafficTransitionCount <= 4)
				pmlmepriv->LinkDetectInfo.TrafficTransitionCount = 4;

			pmlmepriv->LinkDetectInfo.TrafficTransitionCount++;

			/* RTW_INFO("Set TrafficTransitionCount to %d\n", pmlmepriv->LinkDetectInfo.TrafficTransitionCount); */

			if (pmlmepriv->LinkDetectInfo.TrafficTransitionCount > 30/*TrafficTransitionLevel*/)
				pmlmepriv->LinkDetectInfo.TrafficTransitionCount = 30;
		}
		#endif /* CONFIG_LPS_SLOW_TRANSITION */
	} else {
		#ifdef DBG_RX_COUNTER_DUMP
		if (padapter->dump_rx_cnt_mode & DUMP_DRV_TRX_COUNTER_DATA)
			RTW_INFO("(+)Tx = %d, Rx = %d\n", pmlmepriv->LinkDetectInfo.NumTxOkInPeriod, pmlmepriv->LinkDetectInfo.NumRxUnicastOkInPeriod);
		#endif

		#ifdef CONFIG_LPS_SLOW_TRANSITION
		if (pmlmepriv->LinkDetectInfo.TrafficTransitionCount >= 2)
			pmlmepriv->LinkDetectInfo.TrafficTransitionCount -= 2;
		else
			pmlmepriv->LinkDetectInfo.TrafficTransitionCount = 0;

		if (pmlmepriv->LinkDetectInfo.TrafficTransitionCount == 0)
			bEnterPS = _TRUE;
		#else /* CONFIG_LPS_SLOW_TRANSITION */
			bEnterPS = _TRUE;
		#endif /* CONFIG_LPS_SLOW_TRANSITION */
	}

	#ifdef CONFIG_DYNAMIC_DTIM
	if (pmlmepriv->LinkDetectInfo.LowPowerTransitionCount == 8)
		bEnterPS = _FALSE;

	RTW_INFO("LowPowerTransitionCount=%d\n", pmlmepriv->LinkDetectInfo.LowPowerTransitionCount);
	#endif /* CONFIG_DYNAMIC_DTIM */

	/* LeisurePS only work in infra mode. */
	if (bEnterPS) {
		if (!from_timer) {
			#ifdef CONFIG_DYNAMIC_DTIM
			if (pmlmepriv->LinkDetectInfo.LowPowerTransitionCount < 8)
				adapter_to_pwrctl(padapter)->dtim = 1;
			else
				adapter_to_pwrctl(padapter)->dtim = 3;
			#endif /* CONFIG_DYNAMIC_DTIM */
			LPS_Enter(padapter, "TRAFFIC_IDLE");
		} else {
			/* do this at caller */
			/* rtw_lps_ctrl_wk_cmd(adapter, LPS_CTRL_ENTER, 0); */
			/* rtw_hal_dm_watchdog_in_lps(padapter); */
		}

		#ifdef CONFIG_DYNAMIC_DTIM
		if (adapter_to_pwrctl(padapter)->bFwCurrentInPSMode == _TRUE)
			pmlmepriv->LinkDetectInfo.LowPowerTransitionCount++;
		#endif /* CONFIG_DYNAMIC_DTIM */
	} else {
		#ifdef CONFIG_DYNAMIC_DTIM
		if (pmlmepriv->LinkDetectInfo.LowPowerTransitionCount != 8)
			pmlmepriv->LinkDetectInfo.LowPowerTransitionCount = 0;
		else
			pmlmepriv->LinkDetectInfo.LowPowerTransitionCount++;
		#endif /* CONFIG_DYNAMIC_DTIM */

		if (!from_timer)
			LPS_Leave(padapter, "TRAFFIC_BUSY");
		else {
			#ifdef CONFIG_CONCURRENT_MODE
			#ifndef CONFIG_FW_MULTI_PORT_SUPPORT
			if (padapter->hw_port == HW_PORT0)
			#endif
			#endif
				rtw_lps_ctrl_wk_cmd(padapter, LPS_CTRL_TRAFFIC_BUSY, 0);
		}
	}

	return bEnterPS;
}
#endif /* CONFIG_LPS */

/* from_timer == 1 means driver is in LPS */
u8 traffic_status_watchdog(_adapter *padapter, u8 from_timer)
{
	u8	bEnterPS = _FALSE;
	u16 BusyThresholdHigh;
	u16	BusyThresholdLow;
	u16	BusyThreshold;
	u8	bBusyTraffic = _FALSE, bTxBusyTraffic = _FALSE, bRxBusyTraffic = _FALSE;
	u8	bHigherBusyTraffic = _FALSE, bHigherBusyRxTraffic = _FALSE, bHigherBusyTxTraffic = _FALSE;

	struct mlme_priv		*pmlmepriv = &(padapter->mlmepriv);
#ifdef CONFIG_TDLS
	struct tdls_info *ptdlsinfo = &(padapter->tdlsinfo);
	struct tdls_txmgmt txmgmt;
	u8 baddr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
#endif /* CONFIG_TDLS */
#ifdef CONFIG_TRAFFIC_PROTECT
	RT_LINK_DETECT_T *link_detect = &pmlmepriv->LinkDetectInfo;
#endif

#ifdef CONFIG_BTC
	if (padapter->registrypriv.wifi_spec != 1) {
		BusyThresholdHigh = 25;
		BusyThresholdLow = 10;
	} else
#endif /* CONFIG_BTC */
	{
		BusyThresholdHigh = 100;
		BusyThresholdLow = 75;
	}
	BusyThreshold = BusyThresholdHigh;


	/*  */
	/* Determine if our traffic is busy now */
	/*  */
	if ((check_fwstate(pmlmepriv, WIFI_ASOC_STATE) == _TRUE)
	    /*&& !MgntInitAdapterInProgress(pMgntInfo)*/) {
		/* if we raise bBusyTraffic in last watchdog, using lower threshold. */
		if (pmlmepriv->LinkDetectInfo.bBusyTraffic)
			BusyThreshold = BusyThresholdLow;

		if (pmlmepriv->LinkDetectInfo.NumRxOkInPeriod > BusyThreshold ||
		    pmlmepriv->LinkDetectInfo.NumTxOkInPeriod > BusyThreshold) {
			bBusyTraffic = _TRUE;

			if (pmlmepriv->LinkDetectInfo.NumRxOkInPeriod > pmlmepriv->LinkDetectInfo.NumTxOkInPeriod)
				bRxBusyTraffic = _TRUE;
			else
				bTxBusyTraffic = _TRUE;
		}

		/* Higher Tx/Rx data. */
		if (pmlmepriv->LinkDetectInfo.NumRxOkInPeriod > 4000 ||
		    pmlmepriv->LinkDetectInfo.NumTxOkInPeriod > 4000) {
			bHigherBusyTraffic = _TRUE;

			if (pmlmepriv->LinkDetectInfo.NumRxOkInPeriod > pmlmepriv->LinkDetectInfo.NumTxOkInPeriod)
				bHigherBusyRxTraffic = _TRUE;
			else
				bHigherBusyTxTraffic = _TRUE;
		}

#ifdef CONFIG_TRAFFIC_PROTECT
#define TX_ACTIVE_TH 10
#define RX_ACTIVE_TH 20
#define TRAFFIC_PROTECT_PERIOD_MS 4500

		if (link_detect->NumTxOkInPeriod > TX_ACTIVE_TH
		    || link_detect->NumRxUnicastOkInPeriod > RX_ACTIVE_TH) {

			RTW_INFO(FUNC_ADPT_FMT" acqiure wake_lock for %u ms(tx:%d,rx_unicast:%d)\n",
				 FUNC_ADPT_ARG(padapter),
				 TRAFFIC_PROTECT_PERIOD_MS,
				 link_detect->NumTxOkInPeriod,
				 link_detect->NumRxUnicastOkInPeriod);

			rtw_lock_traffic_suspend_timeout(TRAFFIC_PROTECT_PERIOD_MS);
		}
#endif

#ifdef CONFIG_TDLS
#ifdef CONFIG_TDLS_AUTOSETUP
		/* TDLS_WATCHDOG_PERIOD * 2sec, periodically send */
		if (rtw_hw_chk_wl_func(adapter_to_dvobj(padapter), WL_FUNC_TDLS) == _TRUE) {
			if ((ptdlsinfo->watchdog_count % TDLS_WATCHDOG_PERIOD) == 0) {
				_rtw_memcpy(txmgmt.peer, baddr, ETH_ALEN);
				issue_tdls_dis_req(padapter, &txmgmt);
			}
			ptdlsinfo->watchdog_count++;
		}
#endif /* CONFIG_TDLS_AUTOSETUP */
#endif /* CONFIG_TDLS */

#ifdef CONFIG_SUPPORT_STATIC_SMPS
		_ssmps_chk_by_tp(padapter, from_timer);
#endif
#ifdef CONFIG_CTRL_TXSS_BY_TP
		rtw_ctrl_tx_ss_by_tp(padapter, from_timer);
#endif

#ifdef CONFIG_LPS
		if (adapter_to_pwrctl(padapter)->bLeisurePs && MLME_IS_STA(padapter)) {
			#ifdef CONFIG_LPS_CHK_BY_TP
			if (adapter_to_pwrctl(padapter)->lps_chk_by_tp)
				bEnterPS = _lps_chk_by_tp(padapter, from_timer);
			else
			#endif /*CONFIG_LPS_CHK_BY_TP*/
				bEnterPS = _lps_chk_by_pkt_cnts(padapter, from_timer, bBusyTraffic);
		}
#endif /* CONFIG_LPS */

	} else {
#ifdef CONFIG_LPS
		if (!from_timer && rtw_mi_get_assoc_if_num(padapter) == 0)
			LPS_Leave(padapter, "NON_LINKED");
#endif
	}

	session_tracker_chk_cmd(padapter, NULL);

#ifdef CONFIG_BEAMFORMING
#ifdef RTW_WKARD_TX_DISABLE_BFEE
	/*For each padapter*/
	rtw_core_bf_watchdog(padapter);
#endif
#endif

	pmlmepriv->LinkDetectInfo.NumRxOkInPeriod = 0;
	pmlmepriv->LinkDetectInfo.NumTxOkInPeriod = 0;
	pmlmepriv->LinkDetectInfo.NumRxUnicastOkInPeriod = 0;
	pmlmepriv->LinkDetectInfo.bBusyTraffic = bBusyTraffic;
	pmlmepriv->LinkDetectInfo.bTxBusyTraffic = bTxBusyTraffic;
	pmlmepriv->LinkDetectInfo.bRxBusyTraffic = bRxBusyTraffic;
	pmlmepriv->LinkDetectInfo.bHigherBusyTraffic = bHigherBusyTraffic;
	pmlmepriv->LinkDetectInfo.bHigherBusyRxTraffic = bHigherBusyRxTraffic;
	pmlmepriv->LinkDetectInfo.bHigherBusyTxTraffic = bHigherBusyTxTraffic;

	return bEnterPS;

}


/* for 11n Logo 4.2.31/4.2.32 */
static void dynamic_update_bcn_check(_adapter *padapter)
{
	struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
	struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;

	if (!padapter->registrypriv.wifi_spec)
		return;

	if (!padapter->registrypriv.ht_enable || !is_supported_ht(padapter->registrypriv.wireless_mode))
		return;

	if (!MLME_IS_AP(padapter))
		return;

	if (pmlmeext->bstart_bss) {
		/* In 10 * 2 = 20s, there are no legacy AP, update HT info  */
		static u8 count = 1;

		if (count % 10 == 0) {
			count = 1;
#ifdef CONFIG_80211N_HT
			if (_FALSE == ATOMIC_READ(&pmlmepriv->olbc)
				&& _FALSE == ATOMIC_READ(&pmlmepriv->olbc_ht)) {

				if (rtw_ht_operation_update(padapter) > 0) {
					rtw_update_beacon(padapter, _HT_CAPABILITY_IE_, NULL, _FALSE, 0);
					rtw_update_beacon(padapter, _HT_ADD_INFO_IE_, NULL, _TRUE, 0);
				}
			}
#endif /* CONFIG_80211N_HT */
		}

#ifdef CONFIG_80211N_HT
		/* In 2s, there are any legacy AP, update HT info, and then reset count  */

		if (_FALSE != ATOMIC_READ(&pmlmepriv->olbc)
			&& _FALSE != ATOMIC_READ(&pmlmepriv->olbc_ht)) {
					
			if (rtw_ht_operation_update(padapter) > 0) {
				rtw_update_beacon(padapter, _HT_CAPABILITY_IE_, NULL, _FALSE, 0);
				rtw_update_beacon(padapter, _HT_ADD_INFO_IE_, NULL, _TRUE, 0);

			}
			ATOMIC_SET(&pmlmepriv->olbc, _FALSE);
			ATOMIC_SET(&pmlmepriv->olbc_ht, _FALSE);
			count = 0;
		}
#endif /* CONFIG_80211N_HT */
		count ++;
	}
}

struct turbo_edca_setting{
	u32 edca_ul; /* uplink, tx */
	u32 edca_dl; /* downlink, rx */
};

#define TURBO_EDCA_ENT(UL, DL) {UL, DL}

#define TURBO_EDCA_MODE_NUM 8
static struct turbo_edca_setting ctrl_turbo_edca[TURBO_EDCA_MODE_NUM] = {
	/* { UL, DL } */
	TURBO_EDCA_ENT(0x5e431c, 0x431c), /* mode 0 */

	TURBO_EDCA_ENT(0x431c, 0x431c), /* mode 1 */

	TURBO_EDCA_ENT(0x5e431c, 0x5e431c), /* mode 2 */

	TURBO_EDCA_ENT(0x5ea42b, 0x5ea42b), /* mode 3 */

	TURBO_EDCA_ENT(0x5ea42b, 0x431c), /* mode 4 */

	TURBO_EDCA_ENT(0x6ea42b, 0x6ea42b), /* mode 5 */

	TURBO_EDCA_ENT(0xa42b, 0xa42b), /* mode 6 */

	TURBO_EDCA_ENT(0x5e431c, 0xa42b), /* mode 7 */
};

void rtw_turbo_edca(_adapter *padapter)
{
	struct registry_priv *pregpriv = &padapter->registrypriv;
	struct dvobj_priv *dvobj = adapter_to_dvobj(padapter);
	struct rtw_phl_com_t *phl_com = GET_PHL_COM(dvobj);
	u32	edca_be_ul = padapter->last_edca;
	u32	edca_be_dl = padapter->last_edca;
	u32	ac_parm = padapter->last_edca;
	u8	ac;
	u8	is_linked = _FALSE;

	if (padapter->dis_turboedca == DIS_TURBO)
		return;

	if (rtw_mi_check_status(padapter, MI_ASSOC))
		is_linked = _TRUE;

	if (is_linked != _TRUE)
		return;

	if (pregpriv->wifi_spec == 1)
		return;

	/* keep this condition at last check */
	if (padapter->dis_turboedca == DIS_TURBO_USE_MANUAL) {
		if (padapter->edca_param_mode < TURBO_EDCA_MODE_NUM) {

				struct turbo_edca_setting param;

				param = ctrl_turbo_edca[padapter->edca_param_mode];
				edca_be_ul = param.edca_ul;
				edca_be_dl = param.edca_dl;
			} else {
				edca_be_ul = padapter->edca_param_mode;
				edca_be_dl = padapter->edca_param_mode;
		}
	}

	if (phl_com->phl_stats.tx_traffic.lvl == RTW_TFC_HIGH)
		ac_parm = edca_be_ul;
	else if (phl_com->phl_stats.tx_traffic.lvl != RTW_TFC_HIGH)
		ac_parm = edca_be_dl;
	else
		return;

	if (padapter->last_edca != ac_parm) {
		ac = 0;
		/*RTW_INFO("%s, edca(0x%08x), lvl(%d), sts(%d)\n", __func__, ac_parm,
				phl_com->phl_stats.tx_traffic.lvl, phl_com->phl_stats.tx_traffic.sts);*/
		rtw_hw_set_edca(padapter, ac, ac_parm);
		padapter->last_edca = ac_parm;
	}
}

u32 rtw_get_turbo_edca(_adapter *padapter, u8 aifs, u8 ecwmin, u8 ecwmax, u8 txop)
{
	struct registry_priv *pregpriv = &padapter->registrypriv;
	u32 ret = 0;
	u8 decide_txop = txop;
	u8 default_txop = 0x5e;
	u32 ac_parm = padapter->last_edca;
	u8 ac = 0;/*BE*/

	if (padapter->dis_turboedca == DIS_TURBO)
		return ret;

	if (pregpriv->wifi_spec == 1)
		return ret;

	if (default_txop > txop)
		decide_txop = default_txop;
	else
		decide_txop = txop;

	ac_parm = aifs | (ecwmin << 8) | (ecwmax << 12) | (decide_txop << 16);

	return  ac_parm;

}

void rtw_iface_dynamic_chk_wk_hdl(_adapter *padapter)
{
	#ifdef CONFIG_ACTIVE_KEEP_ALIVE_CHECK
	#ifdef CONFIG_AP_MODE
	if (MLME_IS_AP(padapter) || MLME_IS_MESH(padapter)) {
		expire_timeout_chk(padapter);
		#ifdef CONFIG_RTW_MESH
		if (MLME_IS_MESH(padapter) && MLME_IS_ASOC(padapter))
			rtw_mesh_peer_status_chk(padapter);
		#endif
	}
	#endif
	#endif /* CONFIG_ACTIVE_KEEP_ALIVE_CHECK */
	dynamic_update_bcn_check(padapter);

	rtw_turbo_edca(padapter);

	/* for debug purpose */
	_linked_info_dump(padapter);

#ifdef CONFIG_RTW_CFGVENDOR_RSSIMONITOR
        rtw_cfgvendor_rssi_monitor_evt(padapter);
#endif


}

void rtw_dynamic_chk_wk_hdl(_adapter *padapter)
{
	rtw_mi_dynamic_chk_wk_hdl(padapter);

#ifdef DBG_CONFIG_ERROR_DETECT
	rtw_hal_sreset_xmit_status_check(padapter);
	rtw_hal_sreset_linked_status_check(padapter);
#endif

	/* if(check_fwstate(pmlmepriv, WIFI_UNDER_LINKING|WIFI_UNDER_SURVEY)==_FALSE) */
	{
#ifdef DBG_RX_COUNTER_DUMP
		rtw_dump_rx_counters(padapter);
#endif
	}

#ifdef CONFIG_RTW_MULTI_AP
	rtw_ch_util_rpt(padapter);
#endif

#ifdef CONFIG_DFS_MASTER
	rtw_chset_chk_non_ocp_finish(adapter_to_rfctl(padapter));
#endif

#ifdef CONFIG_IPS_CHECK_IN_WD
	/* always call rtw_ps_processor() at last one. */
	rtw_ps_processor(padapter);
#endif
}

void rtw_dynamic_chk_wk_sw_hdl(_adapter *padapter)
{
#ifdef CONFIG_RTW_MULTI_AP
	rtw_ch_util_rpt(padapter);
#endif

	rtw_update_phl_edcca_mode(padapter);

#ifdef CONFIG_DFS_MASTER
	rtw_chset_chk_non_ocp_finish(adapter_to_rfctl(padapter));
#endif
}

void rtw_dynamic_chk_wk_hw_hdl(_adapter *padapter)
{
	rtw_mi_dynamic_chk_wk_hdl(padapter);

#ifdef DBG_CONFIG_ERROR_DETECT
	rtw_hal_sreset_xmit_status_check(padapter);
	rtw_hal_sreset_linked_status_check(padapter);
#endif

	/* if(check_fwstate(pmlmepriv, WIFI_UNDER_LINKING|WIFI_UNDER_SURVEY)==_FALSE) */
	{
#ifdef DBG_RX_COUNTER_DUMP
		rtw_dump_rx_counters(padapter);
#endif
	}

#ifdef CONFIG_IPS_CHECK_IN_WD
	/* always call rtw_ps_processor() at last one. */
	rtw_ps_processor(padapter);
#endif

#ifdef RTW_DETECT_HANG
	rtw_is_hang_check(padapter);
#endif
}


#ifdef CONFIG_LPS
struct lps_ctrl_wk_parm {
	s8 lps_level;
	#ifdef CONFIG_LPS_1T1R
	s8 lps_1t1r;
	#endif
};

void lps_ctrl_wk_hdl(_adapter *padapter, u8 lps_ctrl_type, u8 *buf)
{
	struct pwrctrl_priv *pwrpriv = adapter_to_pwrctl(padapter);
	struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
	struct lps_ctrl_wk_parm *parm = (struct lps_ctrl_wk_parm *)buf;
	u8	mstatus;

	if ((check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) == _TRUE)
	    || (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) == _TRUE))
		return;

	switch (lps_ctrl_type) {
	case LPS_CTRL_SCAN:
		/* RTW_INFO("LPS_CTRL_SCAN\n"); */
		if (check_fwstate(pmlmepriv, WIFI_ASOC_STATE) == _TRUE) {
			/* connect */
			LPS_Leave(padapter, "LPS_CTRL_SCAN");
		}
		break;
	case LPS_CTRL_JOINBSS:
		/* RTW_INFO("LPS_CTRL_JOINBSS\n"); */
		LPS_Leave(padapter, "LPS_CTRL_JOINBSS");
		break;
	case LPS_CTRL_CONNECT:
		/* RTW_INFO("LPS_CTRL_CONNECT\n"); */
		mstatus = 1;/* connect */
		/* Reset LPS Setting */
		pwrpriv->LpsIdleCount = 0;
		rtw_hal_set_hwreg(padapter, HW_VAR_H2C_FW_JOINBSSRPT, (u8 *)(&mstatus));
		break;
	case LPS_CTRL_DISCONNECT:
		/* RTW_INFO("LPS_CTRL_DISCONNECT\n"); */
		mstatus = 0;/* disconnect */
		LPS_Leave(padapter, "LPS_CTRL_DISCONNECT");
		rtw_hal_set_hwreg(padapter, HW_VAR_H2C_FW_JOINBSSRPT, (u8 *)(&mstatus));
		break;
	case LPS_CTRL_SPECIAL_PACKET:
		/* RTW_INFO("LPS_CTRL_SPECIAL_PACKET\n"); */
		rtw_set_lps_deny(padapter, LPS_DELAY_MS);
		LPS_Leave(padapter, "LPS_CTRL_SPECIAL_PACKET");
		break;
	case LPS_CTRL_LEAVE:
		LPS_Leave(padapter, "LPS_CTRL_LEAVE");
		break;
	case LPS_CTRL_LEAVE_SET_OPTION:
		LPS_Leave(padapter, "LPS_CTRL_LEAVE_SET_OPTION");
		if (parm) {
			if (parm->lps_level >= 0)
				pwrpriv->lps_level = parm->lps_level;
			#ifdef CONFIG_LPS_1T1R
			if (parm->lps_1t1r >= 0)
				pwrpriv->lps_1t1r = parm->lps_1t1r;
			#endif
		}
		break;
	case LPS_CTRL_LEAVE_CFG80211_PWRMGMT:
		LPS_Leave(padapter, "CFG80211_PWRMGMT");
		break;
	case LPS_CTRL_TRAFFIC_BUSY:
		LPS_Leave(padapter, "LPS_CTRL_TRAFFIC_BUSY");
		break;
	case LPS_CTRL_TX_TRAFFIC_LEAVE:
		LPS_Leave(padapter, "LPS_CTRL_TX_TRAFFIC_LEAVE");
		break;
	case LPS_CTRL_RX_TRAFFIC_LEAVE:
		LPS_Leave(padapter, "LPS_CTRL_RX_TRAFFIC_LEAVE");
		break;
	case LPS_CTRL_ENTER:
		LPS_Enter(padapter, "TRAFFIC_IDLE_1");
		break;
	default:
		break;
	}

}

static u8 _rtw_lps_ctrl_wk_cmd(_adapter *adapter, u8 lps_ctrl_type, s8 lps_level, s8 lps_1t1r, u8 flags)
{
	struct cmd_obj *cmdobj;
	struct drvextra_cmd_parm *parm;
	struct lps_ctrl_wk_parm *wk_parm = NULL;
	struct cmd_priv	*pcmdpriv = &adapter_to_dvobj(adapter)->cmdpriv;
	struct submit_ctx sctx;
	u8	res = _SUCCESS;

	if (lps_ctrl_type == LPS_CTRL_LEAVE_SET_OPTION) {
		wk_parm = rtw_zmalloc(sizeof(*wk_parm));
		if (wk_parm == NULL) {
			res = _FAIL;
			goto exit;
		}
		wk_parm->lps_level = lps_level;
		#ifdef CONFIG_LPS_1T1R
		wk_parm->lps_1t1r = lps_1t1r;
		#endif
	}

	if (flags & RTW_CMDF_DIRECTLY) {
		/* no need to enqueue, do the cmd hdl directly */
		lps_ctrl_wk_hdl(adapter, lps_ctrl_type, (u8 *)wk_parm);
		if (wk_parm)
			rtw_mfree(wk_parm, sizeof(*wk_parm));
	} else {
		/* need enqueue, prepare cmd_obj and enqueue */
		parm = rtw_zmalloc(sizeof(*parm));
		if (parm == NULL) {
			if (wk_parm)
				rtw_mfree(wk_parm, sizeof(*wk_parm));
			res = _FAIL;
			goto exit;
		}

		parm->ec_id = LPS_CTRL_WK_CID;
		parm->type = lps_ctrl_type;
		parm->size = wk_parm ? sizeof(*wk_parm) : 0;
		parm->pbuf = (u8 *)wk_parm;

		cmdobj = rtw_zmalloc(sizeof(*cmdobj));
		if (cmdobj == NULL) {
			rtw_mfree(parm, sizeof(*parm));
			if (wk_parm)
				rtw_mfree(wk_parm, sizeof(*wk_parm));
			res = _FAIL;
			goto exit;
		}
		cmdobj->padapter = adapter;

		init_h2fwcmd_w_parm_no_rsp(cmdobj, parm, CMD_SET_DRV_EXTRA);

		if (flags & RTW_CMDF_WAIT_ACK) {
			cmdobj->sctx = &sctx;
			rtw_sctx_init(&sctx, 2000);
		}

		res = rtw_enqueue_cmd(pcmdpriv, cmdobj);

		if (res == _SUCCESS && (flags & RTW_CMDF_WAIT_ACK)) {
			rtw_sctx_wait(&sctx, __func__);
			_rtw_mutex_lock_interruptible(&pcmdpriv->sctx_mutex);
			if (sctx.status == RTW_SCTX_SUBMITTED)
				cmdobj->sctx = NULL;
			_rtw_mutex_unlock(&pcmdpriv->sctx_mutex);
			if (sctx.status != RTW_SCTX_DONE_SUCCESS)
				res = _FAIL;
		}
	}

exit:
	return res;
}

u8 rtw_lps_ctrl_wk_cmd(_adapter *adapter, u8 lps_ctrl_type, u8 flags)
{
	return _rtw_lps_ctrl_wk_cmd(adapter, lps_ctrl_type, -1, -1, flags);
}

u8 rtw_lps_ctrl_leave_set_level_cmd(_adapter *adapter, u8 lps_level, u8 flags)
{
	return _rtw_lps_ctrl_wk_cmd(adapter, LPS_CTRL_LEAVE_SET_OPTION, lps_level, -1, flags);
}

#ifdef CONFIG_LPS_1T1R
u8 rtw_lps_ctrl_leave_set_1t1r_cmd(_adapter *adapter, u8 lps_1t1r, u8 flags)
{
	return _rtw_lps_ctrl_wk_cmd(adapter, LPS_CTRL_LEAVE_SET_OPTION, -1, lps_1t1r, flags);
}
#endif

void rtw_dm_in_lps_hdl(_adapter *padapter)
{
	rtw_hal_set_hwreg(padapter, HW_VAR_DM_IN_LPS_LCLK, NULL);
}

u8 rtw_dm_in_lps_wk_cmd(_adapter *padapter)
{
	struct cmd_obj	*cmd;
	struct drvextra_cmd_parm *pdrvextra_cmd_parm;
	struct cmd_priv	*pcmdpriv = &adapter_to_dvobj(padapter)->cmdpriv;
	u8	res = _SUCCESS;


	cmd = (struct cmd_obj *)rtw_zmalloc(sizeof(struct cmd_obj));
	if (cmd == NULL) {
		res = _FAIL;
		goto exit;
	}
	cmd->padapter = padapter;

	pdrvextra_cmd_parm = (struct drvextra_cmd_parm *)rtw_zmalloc(sizeof(struct drvextra_cmd_parm));
	if (pdrvextra_cmd_parm == NULL) {
		rtw_mfree((unsigned char *)cmd, sizeof(struct cmd_obj));
		res = _FAIL;
		goto exit;
	}

	pdrvextra_cmd_parm->ec_id = DM_IN_LPS_WK_CID;
	pdrvextra_cmd_parm->type = 0;
	pdrvextra_cmd_parm->size = 0;
	pdrvextra_cmd_parm->pbuf = NULL;

	init_h2fwcmd_w_parm_no_rsp(cmd, pdrvextra_cmd_parm, CMD_SET_DRV_EXTRA);

	res = rtw_enqueue_cmd(pcmdpriv, cmd);

exit:
	return res;
}

void rtw_lps_change_dtim_hdl(_adapter *padapter, u8 dtim)
{
	struct pwrctrl_priv *pwrpriv = adapter_to_pwrctl(padapter);

	if (dtim <= 0 || dtim > 16)
		return;

#ifdef CONFIG_LPS_LCLK
	_enter_pwrlock(&pwrpriv->lock);
#endif

	if (pwrpriv->dtim != dtim) {
		RTW_INFO("change DTIM from %d to %d, bFwCurrentInPSMode=%d, ps_mode=%d\n", pwrpriv->dtim, dtim,
			 pwrpriv->bFwCurrentInPSMode, pwrpriv->pwr_mode);

		pwrpriv->dtim = dtim;
	}

	if ((pwrpriv->bFwCurrentInPSMode == _TRUE) && (pwrpriv->pwr_mode > PM_PS_MODE_ACTIVE)) {
		u8 ps_mode = pwrpriv->pwr_mode;

		/* RTW_INFO("change DTIM from %d to %d, ps_mode=%d\n", pwrpriv->dtim, dtim, ps_mode); */

		rtw_hal_set_hwreg(padapter, HW_VAR_H2C_FW_PWRMODE, (u8 *)(&ps_mode));
	}

#ifdef CONFIG_LPS_LCLK
	_exit_pwrlock(&pwrpriv->lock);
#endif

}

#endif

u8 rtw_lps_change_dtim_cmd(_adapter *padapter, u8 dtim)
{
	struct cmd_obj	*cmd;
	struct drvextra_cmd_parm *pdrvextra_cmd_parm;
	struct cmd_priv	*pcmdpriv = &adapter_to_dvobj(padapter)->cmdpriv;
	u8 res = _SUCCESS;
	/*
	#ifdef CONFIG_CONCURRENT_MODE
		if (padapter->hw_port != HW_PORT0)
			return res;
	#endif
	*/
	{
		cmd = (struct cmd_obj *)rtw_zmalloc(sizeof(struct cmd_obj));
		if (cmd == NULL) {
			res = _FAIL;
			goto exit;
		}
		cmd->padapter = padapter;

		pdrvextra_cmd_parm = (struct drvextra_cmd_parm *)rtw_zmalloc(sizeof(struct drvextra_cmd_parm));
		if (pdrvextra_cmd_parm == NULL) {
			rtw_mfree((unsigned char *)cmd, sizeof(struct cmd_obj));
			res = _FAIL;
			goto exit;
		}

		pdrvextra_cmd_parm->ec_id = LPS_CHANGE_DTIM_CID;
		pdrvextra_cmd_parm->type = dtim;
		pdrvextra_cmd_parm->size = 0;
		pdrvextra_cmd_parm->pbuf = NULL;

		init_h2fwcmd_w_parm_no_rsp(cmd, pdrvextra_cmd_parm, CMD_SET_DRV_EXTRA);

		res = rtw_enqueue_cmd(pcmdpriv, cmd);
	}
exit:
	return res;
}


#ifdef CONFIG_POWER_SAVING
void power_saving_wk_hdl(_adapter *padapter)
{
	rtw_ps_processor(padapter);
}
#endif
/* add for CONFIG_IEEE80211W, none 11w can use it */
void reset_securitypriv_hdl(_adapter *padapter)
{
	rtw_reset_securitypriv(padapter);
}

#ifdef CONFIG_IOCTL_CFG80211
#if 0 /*!CONFIG_PHL_ARCH*/
static u8 _p2p_roch_cmd(_adapter *adapter
	, u64 cookie, struct wireless_dev *wdev
	, struct ieee80211_channel *ch, enum nl80211_channel_type ch_type
	, unsigned int duration
	, u8 flags
)
{
	struct cmd_obj *cmdobj;
	struct drvextra_cmd_parm *parm;
	struct p2p_roch_parm *roch_parm;
	struct cmd_priv *pcmdpriv = &adapter_to_dvobj(adapter)->cmdpriv;
	struct submit_ctx sctx;
	u8 cancel = duration ? 0 : 1;
	u8 res = _SUCCESS;

	roch_parm = (struct p2p_roch_parm *)rtw_zmalloc(sizeof(struct p2p_roch_parm));
	if (roch_parm == NULL) {
		res = _FAIL;
		goto exit;
	}

	roch_parm->cookie = cookie;
	roch_parm->wdev = wdev;
	if (!cancel) {
		_rtw_memcpy(&roch_parm->ch, ch, sizeof(struct ieee80211_channel));
		roch_parm->ch_type = ch_type;
		roch_parm->duration = duration;
	}

	if (flags & RTW_CMDF_DIRECTLY) {
		/* no need to enqueue, do the cmd hdl directly and free cmd parameter */
		if (H2C_SUCCESS != p2p_protocol_wk_hdl(adapter, cancel ? P2P_CANCEL_RO_CH_WK : P2P_RO_CH_WK, (u8 *)roch_parm))
			res = _FAIL;
		rtw_mfree((u8 *)roch_parm, sizeof(*roch_parm));
	} else {
		/* need enqueue, prepare cmd_obj and enqueue */
		parm = (struct drvextra_cmd_parm *)rtw_zmalloc(sizeof(struct drvextra_cmd_parm));
		if (parm == NULL) {
			rtw_mfree((u8 *)roch_parm, sizeof(*roch_parm));
			res = _FAIL;
			goto exit;
		}

		parm->ec_id = P2P_PROTO_WK_CID;
		parm->type = cancel ? P2P_CANCEL_RO_CH_WK : P2P_RO_CH_WK;
		parm->size = sizeof(*roch_parm);
		parm->pbuf = (u8 *)roch_parm;

		cmdobj = (struct cmd_obj *)rtw_zmalloc(sizeof(*cmdobj));
		if (cmdobj == NULL) {
			res = _FAIL;
			rtw_mfree((u8 *)roch_parm, sizeof(*roch_parm));
			rtw_mfree((u8 *)parm, sizeof(*parm));
			goto exit;
		}
		cmdobj->padapter = adapter;

		init_h2fwcmd_w_parm_no_rsp(cmdobj, parm, CMD_SET_DRV_EXTRA);

		if (flags & RTW_CMDF_WAIT_ACK) {
			cmdobj->sctx = &sctx;
			rtw_sctx_init(&sctx, 10 * 1000);
		}

		res = rtw_enqueue_cmd(pcmdpriv, cmdobj);

		if (res == _SUCCESS && (flags & RTW_CMDF_WAIT_ACK)) {
			rtw_sctx_wait(&sctx, __func__);
			_rtw_mutex_lock_interruptible(&pcmdpriv->sctx_mutex);
			if (sctx.status == RTW_SCTX_SUBMITTED)
				cmdobj->sctx = NULL;
			_rtw_mutex_unlock(&pcmdpriv->sctx_mutex);
			if (sctx.status != RTW_SCTX_DONE_SUCCESS)
				res = _FAIL;
		}
	}

exit:
	return res;
}
#endif /*!CONFIG_PHL_ARCH*/

inline u8 rtw_mgnt_tx_cmd(_adapter *adapter, u8 tx_ch, u8 no_cck, const u8 *buf, size_t len, int wait_ack, u8 flags)
{
	struct cmd_obj *cmdobj;
	struct drvextra_cmd_parm *parm;
	struct mgnt_tx_parm *mgnt_parm;
	struct cmd_priv *pcmdpriv = &adapter_to_dvobj(adapter)->cmdpriv;
	struct submit_ctx sctx;
	u8	res = _SUCCESS;

	mgnt_parm = (struct mgnt_tx_parm *)rtw_zmalloc(sizeof(struct mgnt_tx_parm));
	if (mgnt_parm == NULL) {
		res = _FAIL;
		goto exit;
	}

	mgnt_parm->tx_ch = tx_ch;
	mgnt_parm->no_cck = no_cck;
	mgnt_parm->buf = buf;
	mgnt_parm->len = len;
	mgnt_parm->wait_ack = wait_ack;

	if (flags & RTW_CMDF_DIRECTLY) {
		/* no need to enqueue, do the cmd hdl directly and free cmd parameter */
		if (H2C_SUCCESS != rtw_mgnt_tx_handler(adapter, (u8 *)mgnt_parm))
			res = _FAIL;
		rtw_mfree((u8 *)mgnt_parm, sizeof(*mgnt_parm));
	} else {
		/* need enqueue, prepare cmd_obj and enqueue */
		parm = (struct drvextra_cmd_parm *)rtw_zmalloc(sizeof(struct drvextra_cmd_parm));
		if (parm == NULL) {
			rtw_mfree((u8 *)mgnt_parm, sizeof(*mgnt_parm));
			res = _FAIL;
			goto exit;
		}

		parm->ec_id = MGNT_TX_WK_CID;
		parm->type = 0;
		parm->size = sizeof(*mgnt_parm);
		parm->pbuf = (u8 *)mgnt_parm;

		cmdobj = (struct cmd_obj *)rtw_zmalloc(sizeof(*cmdobj));
		if (cmdobj == NULL) {
			res = _FAIL;
			rtw_mfree((u8 *)mgnt_parm, sizeof(*mgnt_parm));
			rtw_mfree((u8 *)parm, sizeof(*parm));
			goto exit;
		}
		cmdobj->padapter = adapter;

		init_h2fwcmd_w_parm_no_rsp(cmdobj, parm, CMD_SET_DRV_EXTRA);

		if (flags & RTW_CMDF_WAIT_ACK) {
			cmdobj->sctx = &sctx;
			rtw_sctx_init(&sctx, 10 * 1000);
		}

		res = rtw_enqueue_cmd(pcmdpriv, cmdobj);

		if (res == _SUCCESS && (flags & RTW_CMDF_WAIT_ACK)) {
			rtw_sctx_wait(&sctx, __func__);
			_rtw_mutex_lock_interruptible(&pcmdpriv->sctx_mutex);
			if (sctx.status == RTW_SCTX_SUBMITTED)
				cmdobj->sctx = NULL;
			_rtw_mutex_unlock(&pcmdpriv->sctx_mutex);
			if (sctx.status != RTW_SCTX_DONE_SUCCESS)
				res = _FAIL;
		}
	}

exit:
	return res;
}
#endif

#ifdef CONFIG_POWER_SAVING
u8 rtw_ps_cmd(_adapter *padapter)
{
	struct cmd_obj	*ppscmd;
	struct drvextra_cmd_parm *pdrvextra_cmd_parm;
	struct cmd_priv	*pcmdpriv = &adapter_to_dvobj(padapter)->cmdpriv;

	u8	res = _SUCCESS;

#ifdef CONFIG_CONCURRENT_MODE
	if (!is_primary_adapter(padapter))
		goto exit;
#endif

	ppscmd = (struct cmd_obj *)rtw_zmalloc(sizeof(struct cmd_obj));
	if (ppscmd == NULL) {
		res = _FAIL;
		goto exit;
	}
	ppscmd->padapter = padapter;

	pdrvextra_cmd_parm = (struct drvextra_cmd_parm *)rtw_zmalloc(sizeof(struct drvextra_cmd_parm));
	if (pdrvextra_cmd_parm == NULL) {
		rtw_mfree((unsigned char *)ppscmd, sizeof(struct cmd_obj));
		res = _FAIL;
		goto exit;
	}

	pdrvextra_cmd_parm->ec_id = POWER_SAVING_CTRL_WK_CID;
	pdrvextra_cmd_parm->type = 0;
	pdrvextra_cmd_parm->size = 0;
	pdrvextra_cmd_parm->pbuf = NULL;
	init_h2fwcmd_w_parm_no_rsp(ppscmd, pdrvextra_cmd_parm, CMD_SET_DRV_EXTRA);

	res = rtw_enqueue_cmd(pcmdpriv, ppscmd);
exit:
	return res;
}
#endif /*CONFIG_POWER_SAVING*/

#if CONFIG_DFS
void rtw_dfs_ch_switch_hdl(_adapter *adapter)
{
	struct dvobj_priv *dvobj = adapter_to_dvobj(adapter);
	struct rf_ctl_t *rfctl = dvobj_to_rfctl(dvobj);
	struct mlme_ext_priv *pmlmeext = &adapter->mlmeextpriv;
	u8 ifbmp_m = rtw_mi_get_ap_mesh_ifbmp(adapter);
	u8 ifbmp_s = rtw_mi_get_ld_sta_ifbmp(adapter);
	s16 req_ch;
	u8 req_bw = CHANNEL_WIDTH_20, req_offset = CHAN_OFFSET_NO_EXT, csa_timer = _FALSE;
	u8 need_discon = _FALSE;

	rtw_hal_macid_sleep_all_used(adapter);

	if (rtw_chset_search_ch(rfctl->channel_set, rfctl->csa_chandef.chan) >= 0
		&& !rtw_chset_is_ch_non_ocp(rfctl->channel_set, rfctl->csa_chandef.chan)
	) {
		/* CSA channel available and valid */
		req_ch = rfctl->csa_chandef.chan;
		RTW_INFO("CSA : %s valid CSA ch%u\n", __func__, rfctl->csa_chandef.chan);
		csa_timer = _TRUE;
	} else if (ifbmp_m) {
		/* no available or valid CSA channel, having AP/MESH ifaces */
		req_ch = REQ_CH_NONE;
		need_discon = _TRUE;
		RTW_INFO("CSA : %s ch sel by AP/MESH ifaces\n", __func__);
	} else {
		/* no available or valid CSA channel and no AP/MESH ifaces */
		if (!is_supported_24g(dvobj_to_regsty(dvobj)->band_type)
			#ifdef CONFIG_DFS_MASTER
			|| rfctl->radar_detected
			#endif
		)
			req_ch = 36;
		else
			req_ch = 1;
		need_discon = _TRUE;
		RTW_INFO("CSA : %s switch to ch%d\n", __func__, req_ch);
	}

	if (!need_discon) {
		if (rfctl->csa_ch_width == 1) {
			req_bw = CHANNEL_WIDTH_80;
			req_offset = rfctl->csa_chandef.offset;
		} else if (rfctl->csa_ch_width == 0 && rfctl->csa_chandef.offset != CHAN_OFFSET_NO_EXT) {
			req_bw = CHANNEL_WIDTH_40;
			req_offset = rfctl->csa_chandef.offset;
		} else {
			req_bw = CHANNEL_WIDTH_20;
			req_offset = CHAN_OFFSET_NO_EXT;
		}

		/* get correct offset and check ch/bw/offset is valid or not */
		if (!rtw_get_offset_by_chbw(req_ch, req_bw, &req_offset)) {
			req_bw = CHANNEL_WIDTH_20;
			req_offset = CHAN_OFFSET_NO_EXT;
		}
	}

	RTW_INFO("CSA : req_ch=%d, req_bw=%d, req_offset=%d, ifbmp_m=0x%02x, ifbmp_s=0x%02x\n"
		, req_ch, req_bw, req_offset, ifbmp_m, ifbmp_s);

	/* check all STA ifaces status */
	if (ifbmp_s) {
		_adapter *iface;
		int i;

		for (i = 0; i < dvobj->iface_nums; i++) {
			iface = dvobj->padapters[i];
			if (!iface || !(ifbmp_s & BIT(iface->iface_id)))
				continue;

			if (need_discon) {
				set_fwstate(&iface->mlmepriv,  WIFI_OP_CH_SWITCHING);
				issue_deauth(iface, get_bssid(&iface->mlmepriv), WLAN_REASON_DEAUTH_LEAVING);
			} else {
				/* update STA mode ch/bw/offset */
				iface->mlmeextpriv.chandef.chan= req_ch;
				iface->mlmeextpriv.chandef.bw = req_bw;
				iface->mlmeextpriv.chandef.offset = req_offset;
				/* updaet STA mode DSConfig , ap mode will update in rtw_change_bss_chbw_cmd */
				iface->mlmepriv.cur_network.network.Configuration.DSConfig = req_ch;
				set_fwstate(&iface->mlmepriv, WIFI_CSA_UPDATE_BEACON);

				#ifdef CONFIG_80211D
				if (iface->mlmepriv.recv_country_ie) {
					if (rtw_apply_recv_country_ie_cmd(iface, RTW_CMDF_DIRECTLY
						, req_ch > 14 ? BAND_ON_5G : BAND_ON_24G, req_ch
						, iface->mlmepriv.recv_country_ie) != _SUCCESS
					)
						RTW_WARN(FUNC_ADPT_FMT" rtw_apply_recv_country_ie_cmd() fail\n", FUNC_ADPT_ARG(iface));
				}
				#endif
			}
		}
	}

	if (csa_timer) {
		RTW_INFO("pmlmeext->csa_timer 70 seconds\n");
		/* wait 70 seconds for receiving beacons */
		_set_timer(&pmlmeext->csa_timer, CAC_TIME_MS + 10000);
	}

#ifdef CONFIG_AP_MODE
	if (ifbmp_m) {
		u8 execlude = 0;

		if (need_discon)
			execlude = ifbmp_s;
		/* trigger channel selection with consideraton of asoc STA ifaces */
		rtw_change_bss_chbw_cmd(dvobj_get_primary_adapter(dvobj), RTW_CMDF_DIRECTLY
			, ifbmp_m, execlude, req_ch, REQ_BW_ORI, REQ_OFFSET_NONE);
	} else
#endif
	{
		/* no AP/MESH iface, switch DFS status and channel directly */
		rtw_warn_on(req_ch <= 0);
		#ifdef CONFIG_DFS_MASTER
		if (need_discon)
			rtw_dfs_rd_en_decision(adapter, MLME_OPCH_SWITCH, ifbmp_s);
		else
			rtw_dfs_rd_en_decision(adapter, MLME_OPCH_SWITCH, 0);
		#endif

		set_channel_bwmode(adapter, req_ch, req_offset, req_bw, _TRUE);
		/* update union ch/bw/offset for STA only */
		rtw_mi_update_union_chan_inf(adapter, req_ch, req_offset, req_bw);
		rtw_rfctl_update_op_mode(rfctl, 0, 0);
	}

	/* make asoc STA ifaces disconnect */
	if (ifbmp_s && need_discon) {
		_adapter *iface;
		struct mlme_ext_priv *pmlmeext;
		struct mlme_ext_info *pmlmeinfo;
		int i;

		for (i = 0; i < dvobj->iface_nums; i++) {
			iface = dvobj->padapters[i];
			if (!iface || !(ifbmp_s & BIT(iface->iface_id)))
				continue;
			rtw_disassoc_cmd(iface, 0, RTW_CMDF_DIRECTLY);
			rtw_indicate_disconnect(iface, 0, _FALSE);
			#ifndef CONFIG_STA_CMD_DISPR
			rtw_free_assoc_resources(iface, _TRUE);
			#endif
			rtw_free_network_queue(iface, _TRUE);

			pmlmeext = &iface->mlmeextpriv;
			pmlmeinfo = &pmlmeext->mlmext_info;
			pmlmeinfo->disconnect_occurred_time = rtw_systime_to_ms(rtw_get_current_time());

			if (rtw_chset_is_dfs_chbw(rfctl->channel_set, req_ch, req_bw, req_offset)) {
				RTW_INFO("Switched to DFS band (ch %d, bw %d, offset %d) again!!\n",
					 req_ch, req_bw, req_offset);
				pmlmeinfo->disconnect_code = DISCONNECTION_BY_DRIVER_DUE_TO_RECEIVE_CSA_DFS;
			} else {
				pmlmeinfo->disconnect_code = DISCONNECTION_BY_DRIVER_DUE_TO_RECEIVE_CSA_NON_DFS;
			}

			pmlmeinfo->wifi_reason_code = WLAN_REASON_DEAUTH_LEAVING;
		}
	}

	reset_csa_param(rfctl);

	rtw_hal_macid_wakeup_all_used(adapter);
	rtw_mi_os_xmit_schedule(adapter);
}
#endif /* CONFIG_DFS */

#ifdef CONFIG_AP_MODE

static void rtw_chk_hi_queue_hdl(_adapter *padapter)
{
	struct sta_info *psta_bmc;
	struct sta_priv *pstapriv = &padapter->stapriv;
	systime start = rtw_get_current_time();
	u8 empty = _FALSE;

	psta_bmc = rtw_get_bcmc_stainfo(padapter);
	if (!psta_bmc)
		return;

	rtw_hal_get_hwreg(padapter, HW_VAR_CHK_HI_QUEUE_EMPTY, &empty);

	while (_FALSE == empty && rtw_get_passing_time_ms(start) < rtw_get_wait_hiq_empty_ms()) {
		rtw_msleep_os(100);
		rtw_hal_get_hwreg(padapter, HW_VAR_CHK_HI_QUEUE_EMPTY, &empty);
	}

	if (psta_bmc->sleepq_len == 0) {
		if (empty == _SUCCESS) {
			bool update_tim = _FALSE;

			if (rtw_tim_map_is_set(padapter, pstapriv->tim_bitmap, 0))
				update_tim = _TRUE;

			rtw_tim_map_clear(padapter, pstapriv->tim_bitmap, 0);
			rtw_tim_map_clear(padapter, pstapriv->sta_dz_bitmap, 0);

			if (update_tim == _TRUE)
				_update_beacon(padapter, _TIM_IE_, NULL, _TRUE, 0,"bmc sleepq and HIQ empty");
		} else /* re check again */
			rtw_chk_hi_queue_cmd(padapter);

	}

}

u8 rtw_chk_hi_queue_cmd(_adapter *padapter)
{
	struct cmd_obj	*cmd;
	struct drvextra_cmd_parm *pdrvextra_cmd_parm;
	struct cmd_priv	*pcmdpriv = &adapter_to_dvobj(padapter)->cmdpriv;
	u8	res = _SUCCESS;

	cmd = (struct cmd_obj *)rtw_zmalloc(sizeof(struct cmd_obj));
	if (cmd == NULL) {
		res = _FAIL;
		goto exit;
	}
	cmd->padapter = padapter;

	pdrvextra_cmd_parm = (struct drvextra_cmd_parm *)rtw_zmalloc(sizeof(struct drvextra_cmd_parm));
	if (pdrvextra_cmd_parm == NULL) {
		rtw_mfree((unsigned char *)cmd, sizeof(struct cmd_obj));
		res = _FAIL;
		goto exit;
	}

	pdrvextra_cmd_parm->ec_id = CHECK_HIQ_WK_CID;
	pdrvextra_cmd_parm->type = 0;
	pdrvextra_cmd_parm->size = 0;
	pdrvextra_cmd_parm->pbuf = NULL;

	init_h2fwcmd_w_parm_no_rsp(cmd, pdrvextra_cmd_parm, CMD_SET_DRV_EXTRA);

	res = rtw_enqueue_cmd(pcmdpriv, cmd);

exit:
	return res;

}

#ifdef CONFIG_DFS_MASTER
u8 rtw_dfs_rd_hdl(_adapter *adapter)
{
	struct dvobj_priv *dvobj = adapter_to_dvobj(adapter);
	struct rf_ctl_t *rfctl = adapter_to_rfctl(adapter);
	u8 open_chan;
	u8 cch;

	if (!rfctl->radar_detect_enabled)
		goto exit;

	cch = rtw_get_center_ch(rfctl->radar_detect_ch, rfctl->radar_detect_bw, rfctl->radar_detect_offset);

	open_chan = rtw_get_oper_ch(adapter);
	if (open_chan != rfctl->radar_detect_ch
		|| rtw_get_passing_time_ms(rtw_get_on_oper_ch_time(adapter)) < 300
	) {
		/* offchannel, bypass radar detect */
		goto cac_status_chk;
	}

	if (IS_CH_WAITING(rfctl) && !IS_UNDER_CAC(rfctl)) {
		/* non_ocp, bypass radar detect */
		goto cac_status_chk;
	}

	if (!rfctl->dbg_dfs_fake_radar_detect_cnt
		&& rtw_odm_radar_detect(adapter) != _TRUE)
		goto cac_status_chk;

	if (!rfctl->dbg_dfs_fake_radar_detect_cnt
		&& rfctl->dbg_dfs_radar_detect_trigger_non
	) {
		/* radar detect debug mode, trigger no mlme flow */
		RTW_INFO("%s radar detected on test mode, trigger no mlme flow\n", __func__);
		goto cac_status_chk;
	}

	if (rfctl->dbg_dfs_fake_radar_detect_cnt != 0) {
		RTW_INFO("%s fake radar detected, cnt:%d\n", __func__
			, rfctl->dbg_dfs_fake_radar_detect_cnt);
		rfctl->dbg_dfs_fake_radar_detect_cnt--;
	} else
		RTW_INFO("%s radar detected\n", __func__);

	rfctl->radar_detected = 1;

	rtw_chset_update_non_ocp(rfctl->channel_set
		, rfctl->radar_detect_ch, rfctl->radar_detect_bw, rfctl->radar_detect_offset);

	if (IS_UNDER_CAC(rfctl))
		rtw_nlrtw_cac_abort_event(adapter, cch, rfctl->radar_detect_bw);
	rtw_nlrtw_radar_detect_event(adapter, cch, rfctl->radar_detect_bw);

	rtw_dfs_ch_switch_hdl(adapter);

	if (rfctl->radar_detect_enabled)
		goto set_timer;
	goto exit;

cac_status_chk:

	if (!IS_CAC_STOPPED(rfctl)
		&& ((IS_UNDER_CAC(rfctl) && rfctl->cac_force_stop)
			|| !IS_CH_WAITING(rfctl)
			)
	) {
		u8 pause = 0x00;

		rtw_hal_set_hwreg(adapter, HW_VAR_TXPAUSE, &pause);
		rfctl->cac_start_time = rfctl->cac_end_time = RTW_CAC_STOPPED;
		rtw_nlrtw_cac_finish_event(adapter, cch, rfctl->radar_detect_bw);

		if (rtw_mi_check_fwstate(adapter, WIFI_UNDER_LINKING|WIFI_UNDER_SURVEY) == _FALSE) {
			u8 do_rfk = _TRUE;
			u8 u_ch, u_bw, u_offset;

			if (rtw_mi_get_ch_setting_union(adapter, &u_ch, &u_bw, &u_offset))
				set_channel_bwmode(adapter, u_ch, u_offset, u_bw, do_rfk);
			else
				rtw_warn_on(1);

			rtw_hal_set_hwreg(adapter, HW_VAR_RESUME_BCN, NULL);
			rtw_mi_tx_beacon_hdl(adapter);
		}
	}

set_timer:
	_set_timer(&rfctl->radar_detect_timer
		, rtw_odm_radar_detect_polling_int_ms(dvobj));

exit:
	return H2C_SUCCESS;
}

u8 rtw_dfs_rd_cmd(_adapter *adapter, bool enqueue)
{
	struct cmd_obj *cmdobj;
	struct drvextra_cmd_parm *parm;
	struct cmd_priv *cmdpriv = &adapter_to_dvobj(adapter)->cmdpriv;
	u8 res = _FAIL;

	if (enqueue) {
		cmdobj = rtw_zmalloc(sizeof(struct cmd_obj));
		if (cmdobj == NULL)
			goto exit;
		cmdobj->padapter = adapter;

		parm = rtw_zmalloc(sizeof(struct drvextra_cmd_parm));
		if (parm == NULL) {
			rtw_mfree(cmdobj, sizeof(struct cmd_obj));
			goto exit;
		}

		parm->ec_id = DFS_RADAR_DETECT_WK_CID;
		parm->type = 0;
		parm->size = 0;
		parm->pbuf = NULL;

		init_h2fwcmd_w_parm_no_rsp(cmdobj, parm, CMD_SET_DRV_EXTRA);
		res = rtw_enqueue_cmd(cmdpriv, cmdobj);
	} else {
		rtw_dfs_rd_hdl(adapter);
		res = _SUCCESS;
	}

exit:
	return res;
}

void rtw_dfs_rd_timer_hdl(void *ctx)
{
	struct rf_ctl_t *rfctl = (struct rf_ctl_t *)ctx;
	struct dvobj_priv *dvobj = rfctl_to_dvobj(rfctl);

	rtw_dfs_rd_cmd(dvobj_get_primary_adapter(dvobj), _TRUE);
}

static void rtw_dfs_rd_enable(struct rf_ctl_t *rfctl, u8 ch, u8 bw, u8 offset, bool bypass_cac)
{
	struct dvobj_priv *dvobj = rfctl_to_dvobj(rfctl);
	_adapter *adapter = dvobj_get_primary_adapter(dvobj);

	RTW_INFO("%s on %u,%u,%u\n", __func__, ch, bw, offset);

	if (bypass_cac)
		rfctl->cac_start_time = rfctl->cac_end_time = RTW_CAC_STOPPED;
	else if (rtw_is_cac_reset_needed(rfctl, ch, bw, offset) == _TRUE)
		rtw_reset_cac(rfctl, ch, bw, offset);

	rfctl->radar_detect_by_others = _FALSE;
	rfctl->radar_detect_ch = ch;
	rfctl->radar_detect_bw = bw;
	rfctl->radar_detect_offset = offset;

	rfctl->radar_detected = 0;

	if (IS_CH_WAITING(rfctl))
		rtw_hal_set_hwreg(adapter, HW_VAR_STOP_BCN, NULL);

	if (!rfctl->radar_detect_enabled) {
		RTW_INFO("%s set radar_detect_enabled\n", __func__);
		rfctl->radar_detect_enabled = 1;
		#ifdef CONFIG_LPS
		LPS_Leave(adapter, "RADAR_DETECT_EN");
		#endif
		_set_timer(&rfctl->radar_detect_timer
			, rtw_odm_radar_detect_polling_int_ms(dvobj));

		if (rtw_rfctl_overlap_radar_detect_ch(rfctl)) {
			if (IS_CH_WAITING(rfctl)) {
				u8 pause = 0xFF;

				rtw_hal_set_hwreg(adapter, HW_VAR_TXPAUSE, &pause);
			}
			rtw_odm_radar_detect_enable(adapter);
		}
	}
}

static void rtw_dfs_rd_disable(struct rf_ctl_t *rfctl, u8 ch, u8 bw, u8 offset, bool by_others)
{
	_adapter *adapter = dvobj_get_primary_adapter(rfctl_to_dvobj(rfctl));

	rfctl->radar_detect_by_others = by_others;

	if (rfctl->radar_detect_enabled) {
		bool overlap_radar_detect_ch = rtw_rfctl_overlap_radar_detect_ch(rfctl);

		RTW_INFO("%s clear radar_detect_enabled\n", __func__);

		rfctl->radar_detect_enabled = 0;
		rfctl->radar_detected = 0;
		rfctl->radar_detect_ch = 0;
		rfctl->radar_detect_bw = 0;
		rfctl->radar_detect_offset = 0;
		rfctl->cac_start_time = rfctl->cac_end_time = RTW_CAC_STOPPED;
		_cancel_timer_ex(&rfctl->radar_detect_timer);

		if (rtw_mi_check_fwstate(adapter, WIFI_UNDER_LINKING|WIFI_UNDER_SURVEY) == _FALSE) {
			rtw_hal_set_hwreg(adapter, HW_VAR_RESUME_BCN, NULL);
			rtw_mi_tx_beacon_hdl(adapter);
		}

		if (overlap_radar_detect_ch) {
			u8 pause = 0x00;

			rtw_hal_set_hwreg(adapter, HW_VAR_TXPAUSE, &pause);
			rtw_odm_radar_detect_disable(adapter);
		}
	}

	if (by_others) {
		rfctl->radar_detect_ch = ch;
		rfctl->radar_detect_bw = bw;
		rfctl->radar_detect_offset = offset;
	}
}

void rtw_dfs_rd_en_decision(_adapter *adapter, u8 mlme_act, u8 excl_ifbmp)
{
	struct dvobj_priv *dvobj = adapter_to_dvobj(adapter);
	struct rf_ctl_t *rfctl = adapter_to_rfctl(adapter);
	struct mlme_ext_priv *mlmeext = &adapter->mlmeextpriv;
	struct mi_state mstate;
	u8 ifbmp;
	u8 u_ch, u_bw, u_offset;
	bool ld_sta_in_dfs = _FALSE;
	bool sync_ch = _FALSE; /* _FALSE: asign channel directly */
	bool needed = _FALSE;

	if (mlme_act == MLME_OPCH_SWITCH
		|| mlme_act == MLME_ACTION_NONE
	) {
		ifbmp = ~excl_ifbmp;
		rtw_mi_status_by_ifbmp(dvobj, ifbmp, &mstate);
		rtw_mi_get_ch_setting_union_by_ifbmp(dvobj, ifbmp, &u_ch, &u_bw, &u_offset);
	} else {
		ifbmp = ~excl_ifbmp & ~BIT(adapter->iface_id);
		rtw_mi_status_by_ifbmp(dvobj, ifbmp, &mstate);
		rtw_mi_get_ch_setting_union_by_ifbmp(dvobj, ifbmp, &u_ch, &u_bw, &u_offset);
		if (u_ch != 0)
			sync_ch = _TRUE;

		switch (mlme_act) {
		case MLME_STA_CONNECTING:
			MSTATE_STA_LG_NUM(&mstate)++;
			break;
		case MLME_STA_CONNECTED:
			MSTATE_STA_LD_NUM(&mstate)++;
			break;
		case MLME_STA_DISCONNECTED:
			break;
#ifdef CONFIG_AP_MODE
		case MLME_AP_STARTED:
			MSTATE_AP_NUM(&mstate)++;
			break;
		case MLME_AP_STOPPED:
			break;
#endif
#ifdef CONFIG_RTW_MESH
		case MLME_MESH_STARTED:
			MSTATE_MESH_NUM(&mstate)++;
			break;
		case MLME_MESH_STOPPED:
			break;
#endif
		default:
			rtw_warn_on(1);
			break;
		}

		if (sync_ch == _TRUE) {
			if (!MLME_IS_OPCH_SW(adapter)) {
				if (!rtw_is_chbw_grouped(mlmeext->chandef.chan, mlmeext->chandef.bw, mlmeext->chandef.offset, u_ch, u_bw, u_offset)) {
					RTW_INFO(FUNC_ADPT_FMT" can't sync %u,%u,%u with %u,%u,%u\n", FUNC_ADPT_ARG(adapter)
						, mlmeext->chandef.chan, mlmeext->chandef.bw, mlmeext->chandef.offset, u_ch, u_bw, u_offset);
					goto apply;
				}

				rtw_sync_chbw(&mlmeext->chandef.chan, (u8 *)(&mlmeext->chandef.bw), (u8 *)(&mlmeext->chandef.offset)
					, &u_ch, &u_bw, &u_offset);
			}
		} else {
			u_ch = mlmeext->chandef.chan;
			u_bw = mlmeext->chandef.bw;
			u_offset = mlmeext->chandef.offset;
		}
	}

	if (MSTATE_STA_LG_NUM(&mstate) > 0) {
		/* STA mode is linking */
		goto apply;
	}

	if (MSTATE_STA_LD_NUM(&mstate) > 0) {
		if (rtw_chset_is_dfs_chbw(rfctl->channel_set, u_ch, u_bw, u_offset)) {
			/*
			* if operate as slave w/o radar detect,
			* rely on AP on which STA mode connects
			*/
			if (IS_DFS_SLAVE_WITH_RD(rfctl) && !rtw_rfctl_dfs_domain_unknown(rfctl))
				needed = _TRUE;
			ld_sta_in_dfs = _TRUE;
		}
		goto apply;
	}

	if (!MSTATE_AP_NUM(&mstate) && !MSTATE_MESH_NUM(&mstate)) {
		/* No working AP/Mesh mode */
		goto apply;
	}

	if (rtw_chset_is_dfs_chbw(rfctl->channel_set, u_ch, u_bw, u_offset))
		needed = _TRUE;

apply:

	RTW_INFO(FUNC_ADPT_FMT" needed:%d, mlme_act:%u, excl_ifbmp:0x%02x\n"
		, FUNC_ADPT_ARG(adapter), needed, mlme_act, excl_ifbmp);
	RTW_INFO(FUNC_ADPT_FMT" ld_sta_num:%u, lg_sta_num:%u, ap_num:%u, mesh_num:%u, %u,%u,%u\n"
		, FUNC_ADPT_ARG(adapter), MSTATE_STA_LD_NUM(&mstate), MSTATE_STA_LG_NUM(&mstate)
		, MSTATE_AP_NUM(&mstate), MSTATE_MESH_NUM(&mstate)
		, u_ch, u_bw, u_offset);

	if (needed == _TRUE)
		rtw_dfs_rd_enable(rfctl, u_ch, u_bw, u_offset, ld_sta_in_dfs);
	else
		rtw_dfs_rd_disable(rfctl, u_ch, u_bw, u_offset, ld_sta_in_dfs);
}

u8 rtw_dfs_rd_en_decision_cmd(_adapter *adapter)
{
	struct cmd_obj *cmdobj;
	struct drvextra_cmd_parm *parm;
	struct cmd_priv *cmdpriv = &adapter_to_dvobj(adapter)->cmdpriv;
	u8 res = _FAIL;

	cmdobj = rtw_zmalloc(sizeof(struct cmd_obj));
	if (cmdobj == NULL)
		goto exit;

	cmdobj->padapter = adapter;

	parm = rtw_zmalloc(sizeof(struct drvextra_cmd_parm));
	if (parm == NULL) {
		rtw_mfree(cmdobj, sizeof(struct cmd_obj));
		goto exit;
	}

	parm->ec_id = DFS_RADAR_DETECT_EN_DEC_WK_CID;
	parm->type = 0;
	parm->size = 0;
	parm->pbuf = NULL;

	init_h2fwcmd_w_parm_no_rsp(cmdobj, parm, CMD_SET_DRV_EXTRA);
	res = rtw_enqueue_cmd(cmdpriv, cmdobj);

exit:
	return res;
}
#endif /* CONFIG_DFS_MASTER */

#endif /* CONFIG_AP_MODE */

#if 0 /*def RTW_PHL_DBG_CMD*/

u8 sample_txwd[] = 
{
/*dword 00*/	0x80, 0x00, 0x40, 0x00,
/*dword 01*/	0x00, 0x00, 0x00, 0x00,  
/*dword 02*/	0xF2, 0x05, 0x00, 0x00, 
/*dword 03*/	0x3E, 0x11, 0x00, 0x00,  
/*dword 04*/	0x00, 0x00, 0x00, 0x00, 
/*dword 05*/	0x00, 0x00, 0x00, 0x00,  
/*dword 06*/	0x00, 0x07, 0x9B, 0x63, 
/*dword 07*/	0x3F, 0x00, 0x00, 0x00,  
/*dword 08*/	0x00, 0x00, 0x00, 0x00, 
/*dword 09*/	0x00, 0x00, 0x00, 0x00,  
/*dword 10*/	0x0C, 0x00, 0x00, 0x00, 
/*dword 11*/	0x00, 0x00, 0x00, 0x00,  
};

#define WD_SPEC(_name, _idx_dw, _bit_start, _bit_end) {              \
	.name        	= (_name),                   \
	.idx_dw         = (_idx_dw),                     \
	.bit_start      = (_bit_start),                   \
	.bit_end 		= (_bit_end)  \
}

struct parse_wd {
	char name[32];
	u8 idx_dw;
	u8 bit_start;
	u8 bit_end;
};

#define MAX_PHL_CMD_LEN 200
#define MAX_PHL_CMD_NUM 10
#define MAX_PHL_CMD_STR_LEN 200

char *get_next_para_str(char *para)
{
	return (para+MAX_PHL_CMD_STR_LEN);
}

static struct parse_wd parse_txwd_8852ae_full[] = {
	WD_SPEC("EN_HWSEQ_MODE"			, 0, 0, 1),
	WD_SPEC("HW_SSN_SEL"			, 0, 2, 3),
	WD_SPEC("SMH_EN"				, 0, 4, 4),
	WD_SPEC("HWAMSDU"				, 0, 5, 5),
	WD_SPEC("HW_AES_IV"				, 0, 6, 6),
	WD_SPEC("WD page"				, 0, 7, 7),
	WD_SPEC("CHK_EN"				, 0, 8, 8),
	WD_SPEC("WP_INT"				, 0, 9, 9),
	WD_SPEC("STF mode"				, 0, 10, 10),
	WD_SPEC("HEADERwLLC_LEN"		, 0, 11, 15),
	WD_SPEC("CHANNEL_DMA"			, 0, 16, 19),
	WD_SPEC("FW_download"			, 0, 20, 20),
	WD_SPEC("PKT_OFFSET"			, 0, 21, 21),
	WD_SPEC("WDINFO_EN"				, 0, 22, 22),
	WD_SPEC("MOREDATA"				, 0, 23, 23),
	WD_SPEC("WP_OFFSET" 			, 0, 24, 31),

	WD_SPEC("SHCUT_CAMID"			, 1, 0, 7),
	WD_SPEC("DMA_TXAGG_NUM"			, 1, 8, 15),
	WD_SPEC("PLD(Packet ID)"		, 1, 16, 31),

	WD_SPEC("TXPKTSIZE"			, 2, 0, 13),
	WD_SPEC("RU_TC"				, 2, 14, 16),
	WD_SPEC("QSEL"				, 2, 17, 22),
	WD_SPEC("TID_indicate"		, 2, 23, 23),
	WD_SPEC("MACID"				, 2, 24, 30),
	WD_SPEC("RSVD"				, 2, 31, 31),

	WD_SPEC("Wifi_SEQ"			, 3, 0, 11),
	WD_SPEC("AGG_EN"			, 3, 12, 12),
	WD_SPEC("BK"				, 3, 13, 13),
	WD_SPEC("RTS_TC"			, 3, 14, 19),
	WD_SPEC("DATA_TC"			, 3, 20, 25),
	WD_SPEC("MU_2nd_TC"			, 3, 26, 28),
	WD_SPEC("MU_TC"				, 3, 29, 31),

	WD_SPEC("TIMESTAMP"			, 4, 0, 15),
	WD_SPEC("AES_IV_L"			, 4, 16, 31),

	WD_SPEC("AES_IV_H"			, 5, 0, 31),	

	WD_SPEC("MBSSID"			, 6, 0, 3),
	WD_SPEC("Multiport_ID"		, 6, 4, 6),
	WD_SPEC("RSVD"				, 6, 7, 7),
	WD_SPEC("DATA_BW_ER"		, 6, 8, 8),
	WD_SPEC("DISRTSFB"			, 6, 9, 9),
	WD_SPEC("DISDATAFB"			, 6, 10, 10),
	WD_SPEC("DATA_LDPC"			, 6, 11, 11),
	WD_SPEC("DATA_STBC"			, 6, 12, 13),
	WD_SPEC("DATA_DCM"			, 6, 14, 14),
	WD_SPEC("DATA_ER"			, 6, 15, 15),
	WD_SPEC("DataRate"			, 6, 16, 24),
	WD_SPEC("GI_LTF"			, 6, 25, 27),
	WD_SPEC("DATA_BW"			, 6, 28, 29),
	WD_SPEC("USERATE_SEL" 		, 6, 30, 30),
	WD_SPEC("ACK_CH_INFO"		, 6, 31, 31),

	WD_SPEC("MAX_AGG_NUM"				, 7, 0, 7),
	WD_SPEC("BCN_SRCH_SEQ"				, 7, 8, 9),
	WD_SPEC("NAVUSEHDR"					, 7, 10, 10),
	WD_SPEC("BMC"						, 7, 11, 11),
	WD_SPEC("A_CTRL_BQR"				, 7, 12, 12),
	WD_SPEC("A_CTRL_UPH"				, 7, 13, 13),
	WD_SPEC("A_CTRL_BSR"				, 7, 14, 14),
	WD_SPEC("A_CTRL_CAS"				, 7, 15, 15),
	WD_SPEC("DATA_RTY_LOWEST_RATE"		, 7, 16, 24),
	WD_SPEC("DATA_TXCNT_LMT"			, 7, 25, 30),
	WD_SPEC("DATA_TXCNT_LMT_SEL"		, 7, 31, 31),

	WD_SPEC("SEC_CAM_IDX"				, 8, 0, 7),
	WD_SPEC("SEC_HW_ENC"				, 8, 8, 8),
	WD_SPEC("SECTYPE"					, 8, 9, 12),
	WD_SPEC("lifetime_sel"				, 8, 13, 15),
	WD_SPEC("RSVD"						, 8, 16, 16),
	WD_SPEC("FORCE_TXOP"				, 8, 17, 17),
	WD_SPEC("AMPDU_DENSITY"				, 8, 18, 20),
	WD_SPEC("LSIG_TXOP_EN"				, 8, 21, 21),
	WD_SPEC("TXPWR_OFSET_TYPE"			, 8, 22, 24),
	WD_SPEC("RSVD"						, 8, 25, 25),	
	WD_SPEC("obw_cts2self_dup_type"		, 8, 26, 29),
	WD_SPEC("RSVD"						, 8, 30, 31),

	WD_SPEC("Signaling_TA_PKT_EN"		, 9, 0, 0),
	WD_SPEC("NDPA"						, 9, 1, 2),
	WD_SPEC("SND_PKT_SEL"				, 9, 3, 5),
	WD_SPEC("SIFS_Tx"					, 9, 6, 6),
	WD_SPEC("HT_DATA_SND" 				, 9, 7, 7),
	WD_SPEC("RSVD"						, 9, 8, 8),
	WD_SPEC("RTT_EN" 					, 9, 9, 9),
	WD_SPEC("SPE_RPT" 					, 9, 10, 10),
	WD_SPEC("BT_NULL" 					, 9, 11, 11),
	WD_SPEC("TRI_FRAME"					, 9, 12, 12),
	WD_SPEC("NULL_1" 					, 9, 13, 13),
	WD_SPEC("NULL_0" 					, 9, 14, 14),
	WD_SPEC("RAW" 						, 9, 15, 15),
	WD_SPEC("Group_bit"					, 9, 16, 23),
	WD_SPEC("RSVD" 						, 9, 24, 25),
	WD_SPEC("BCNPKT_TSF_CTRL" 			, 9, 26, 26),
	WD_SPEC("Signaling_TA_PKT_SC" 		, 9, 27, 30),
	WD_SPEC("FORCE_BSS_CLR" 			, 9, 31, 31),

	WD_SPEC("SW_DEFINE"					, 10, 0, 3),
	WD_SPEC("RSVD"						, 10, 4, 26),
	WD_SPEC("RTS_EN"					, 10, 27, 27),
	WD_SPEC("CTS2SELF"					, 10, 28, 28),
	WD_SPEC("CCA_RTS" 					, 10, 29, 30),
	WD_SPEC("HW_RTS_EN" 				, 10, 31, 31),

	WD_SPEC("RSVD"						, 11, 0, 3),
	WD_SPEC("NDPA_duration"				, 11, 4, 26),
};

static struct parse_wd parse_rxwd_8852ae[] = {
	WD_SPEC("PKT_LEN"				, 0, 0, 13),
	WD_SPEC("SHIFT"					, 0, 14, 15),
	WD_SPEC("WL_HD_IV_LEN"			, 0, 16, 21),
	WD_SPEC("BB_SEL"				, 0, 22, 22),
	WD_SPEC("MAC_INFO_VLD"			, 0, 23, 23),
	WD_SPEC("RPKT_TYPE"				, 0, 24, 27),
	WD_SPEC("DRV_INFO_SIZE"			, 0, 28, 30),
	WD_SPEC("LONG_RXD"				, 0, 31, 31),
	
	WD_SPEC("PPDU_TYPE"			, 1, 0, 3),
	WD_SPEC("PPDU_CNT"			, 1, 4, 6),
	WD_SPEC("SR_EN"				, 1, 7, 7),
	WD_SPEC("USER_ID"			, 1, 8, 15),
	WD_SPEC("RX_DATARATE" 		, 1, 16, 24),
	WD_SPEC("RX_GI_LTF"			, 1, 25, 27),
	WD_SPEC("NON_SRG_PPDU"		, 1, 28, 28),
	WD_SPEC("INTER_PPDU" 		, 1, 29, 29),
	WD_SPEC("BW"				, 1, 30, 31),

	WD_SPEC("FREERUN_CNT"		, 2, 0, 31),

	WD_SPEC("A1_MATCH"			, 3, 1, 1),
	WD_SPEC("SW_DEC"			, 3, 2, 2),
	WD_SPEC("HW_DEC"			, 3, 3, 3),
	WD_SPEC("AMPDU"				, 3, 4, 4),
	WD_SPEC("AMPDU_END_PKT"		, 3, 5, 5),
	WD_SPEC("AMSDU"				, 3, 6, 6),
	WD_SPEC("AMSDU_CUT"			, 3, 7, 7),
	WD_SPEC("LAST_MSDU"			, 3, 8, 8),
	WD_SPEC("BYPASS"			, 3, 9, 9),
	WD_SPEC("CRC32" 			, 3, 10, 10),
	WD_SPEC("MAGIC_WAKE" 		, 3, 11, 11),
	WD_SPEC("UNICAST_WAKE"		, 3, 12, 12),
	WD_SPEC("PATTERN_WAKE"		, 3, 13, 13),
	WD_SPEC("GET_CH_INFO" 		, 3, 14, 14),
	WD_SPEC("RX_STATISTICS" 	, 3, 15, 15),
	WD_SPEC("PATTERN_IDX"		, 3, 16, 20),
	WD_SPEC("TARGET_IDC"		, 3, 21, 23),
	WD_SPEC("CHKSUM_OFFLOAD_EN"	, 3, 24, 24),
	WD_SPEC("WITH_LLC"			, 3, 25, 25),
	WD_SPEC("RSVD" 				, 3, 26, 31),

	WD_SPEC("TYPE"			, 4, 0, 1),
	WD_SPEC("MC"			, 4, 2, 2),
	WD_SPEC("BC" 			, 4, 3, 3),
	WD_SPEC("MD"			, 4, 4, 4),
	WD_SPEC("MF" 			, 4, 5, 5),
	WD_SPEC("PWR"			, 4, 6, 6),
	WD_SPEC("QOS" 			, 4, 7, 7),
	WD_SPEC("TID"			, 4, 8, 11),
	WD_SPEC("EOSP" 			, 4, 12, 12),
	WD_SPEC("HTC"			, 4, 13, 13),
	WD_SPEC("QNULL"			, 4, 14, 14),
	WD_SPEC("RSVD" 			, 4, 15, 15),
	WD_SPEC("SEQ"			, 4, 16, 27),
	WD_SPEC("FRAG" 			, 4, 28, 31),
	
	WD_SPEC("SEC_CAM_IDX"		, 5, 0, 7),	
	WD_SPEC("ADDR_CAM"			, 5, 8, 15),	
	WD_SPEC("MAC_ID"			, 5, 16, 23),	
	WD_SPEC("RX_PL_ID"			, 5, 24, 27),	
	WD_SPEC("ADDR_CAM_VLD"		, 5, 28, 28),	
	WD_SPEC("ADDR_FWD_EN"		, 5, 29, 29),	
	WD_SPEC("RX_PL_MATCH"		, 5, 30, 30),	
	WD_SPEC("RSVD"				, 5, 31, 31),	
};


enum WD_TYPE {
	TXWD_INFO = 0,
	TXWD_INFO_BODY,
	RXWD, 

};

u32 get_txdesc_element_val(u32 val_dw, u8 bit_start, u8 bit_end)
{
	u32 mask = 0;
	u32 i = 0;

	if(bit_start>31 
		|| bit_end>31
		|| (bit_start>bit_end)){
		printk("[%s] error %d %d\n", __FUNCTION__, bit_start, bit_end);
		return 0;
	}

	for(i = bit_start; i<=bit_end; i++){
		mask |= (1<<i);
	}

	return ((val_dw & mask)>>bit_start);
}


void parse_wd_8852ae(_adapter *adapter, u32 type, u32 idx_wd, u8 *wd)
{
	u32 i, val = 0;
	u32 cur_dw = 0xFF;
	u32 idx, val_dw = 0;
	u32 array_size = 0;
	struct parse_wd *parser = NULL;

	if(wd==NULL)
		return;

	if(type == TXWD_INFO_BODY){
		parser = parse_txwd_8852ae_full;
		array_size = ARRAY_SIZE(parse_txwd_8852ae_full);
	}
	else if(type == RXWD){
		parser = parse_rxwd_8852ae;
		array_size = ARRAY_SIZE(parse_rxwd_8852ae);
	}

	for(i = 0; i<array_size; i++){
			if(cur_dw != parser[i].idx_dw){
				cur_dw = parser[i].idx_dw;
				idx = (parser[i].idx_dw*4);
				val_dw = wd[idx] + (wd[idx+1]<<8) + (wd[idx+2]<<16) + (wd[idx+3]<<24);
				printk(">>>> WD[%03d].dw%02d = 0x%08x \n", idx_wd, cur_dw, val_dw);
			}

			val = get_txdesc_element_val(val_dw, 
				parser[i].bit_start, parser[i].bit_end);
				
			printk("%s[%d:%d] = (0x%x)\n",  
				parser[i].name, 
				parser[i].bit_end, parser[i].bit_start, val);
		}
		printk("\n");
	
}


void compare_wd_8852ae(_adapter *adapter, u32 type, u8 *wd1, u8 *wd2)
{
	u32 i, val1, val2 = 0;
	u32 cur_dw = 0xFF;
	u32 idx, val_dw1, val_dw2 = 0;
	u32 array_size = 0;
	struct parse_wd *parser = NULL;

	if((wd1==NULL) ||(wd2==NULL))
		return;

	if(type == TXWD_INFO_BODY){
		parser = parse_txwd_8852ae_full;
		array_size = ARRAY_SIZE(parse_txwd_8852ae_full);
	}

	for(i = 0; i<array_size; i++){
			if(cur_dw != parser[i].idx_dw){
				cur_dw = parser[i].idx_dw;
				idx = (parser[i].idx_dw*4);
				val_dw1 = wd1[idx] + (wd1[idx+1]<<8) + (wd1[idx+2]<<16) + (wd1[idx+3]<<24);
				val_dw2 = wd2[idx] + (wd2[idx+1]<<8) + (wd2[idx+2]<<16) + (wd2[idx+3]<<24);
			}

			val1 = get_txdesc_element_val(val_dw1, 
				parser[i].bit_start, parser[i].bit_end);
			val2 = get_txdesc_element_val(val_dw2, 
				parser[i].bit_start, parser[i].bit_end);

			if(val1 != val2){
				printk("Diff dw%02d: %s[%d:%d] = (0x%x) vs (0x%x)\n", cur_dw,
					parser[i].name, 
					parser[i].bit_end, parser[i].bit_start, val1, val2);
			}
		}
		printk("\n");
	
}

void core_dump_map_tx(_adapter *adapter)
{
	struct core_logs *log = &adapter->core_logs;
	u32 idx = 0;

 	printk("drvTx MAP");
	for(idx=0; idx<CORE_LOG_NUM; idx++){
		struct core_record *record = &log->drvTx[idx];
		if(idx >= log->txCnt_all)
			break;
		printk("[drvTx %03d]\n", idx);
		printk("type=%d totalSz=%d virtAddr=%p\n", record->type, record->totalSz, record->virtAddr[0]);
	}

	printk("========= \n\n");
	printk("phlTx MAP");
	for(idx=0; idx<CORE_LOG_NUM; idx++){
		struct core_record *record = &log->phlTx[idx];
		u32 idx1 = 0;
		if(idx >= log->txCnt_phl)
			break;
		printk("[phlTx %03d]\n", idx);
		printk("type=%d totalSz=%d fragNum=%d\n", record->type, record->totalSz, record->fragNum);
		for(idx1=0; idx1<record->fragNum; idx1++){
			printk("frag#%d: len=%d virtaddr=%p \n", idx1, 
				record->fragLen[idx1], record->virtAddr[idx1]);
			printk("frag#%d: phyAddrH=%d phyAddrL=%p \n", idx1, 
				record->phyAddrH[idx1], record->phyAddrL[idx1]);			
		}
	}

	printk("========= \n\n");
	printk("TxRcycle MAP");
	for(idx=0; idx<CORE_LOG_NUM; idx++){
		struct core_record *record = &log->txRcycle[idx];
		u32 idx1 = 0;
		if(idx >= log->txCnt_recycle)
			break;
		printk("[TxRcycle %03d]\n", idx);
		printk("type=%d totalSz=%d fragNum=%d\n", record->type, record->totalSz, record->fragNum);
		for(idx1=0; idx1<record->fragNum; idx1++){
			printk("frag#%d: len=%d virtaddr=%p \n", idx1, 
				record->fragLen[idx1], record->virtAddr[idx1]);
			printk("frag#%d: phyAddrH=%d phyAddrL=%p \n", idx1, 
				record->phyAddrH[idx1], record->phyAddrL[idx1]);			
		}
	}
}

void core_dump_map_rx(_adapter *adapter)
{
	struct core_logs *log = &adapter->core_logs;
	u32 idx = 0;

 	printk("drvRx MAP");
	for(idx=0; idx<CORE_LOG_NUM; idx++){
		struct core_record *record = &log->drvRx[idx];
		if(idx >= log->rxCnt_all)
			break;
		printk("[drvRx %03d]\n", idx);
		printk("type=%d totalSz=%d virtAddr=%p\n", record->type, record->totalSz, record->virtAddr[0]);
		printk("wl_seq=%d wl_type=0x%x wl_subtype=0x%x\n", record->wl_seq, record->wl_type, record->wl_subtype);
	}

	printk("========= \n\n");
	printk("phlRx MAP");
	for(idx=0; idx<CORE_LOG_NUM; idx++){
		struct core_record *record = &log->phlRx[idx];
		if(idx >= log->rxCnt_phl)
			break;
		printk("[phlRx %03d]\n", idx);
		printk("type=%d totalSz=%d virtAddr=%p\n", record->type, record->totalSz, record->virtAddr[0]);
	}

	printk("========= \n\n");
	printk("rxRcycle MAP");
	for(idx=0; idx<CORE_LOG_NUM; idx++){
		struct core_record *record = &log->rxRcycle[idx];
		if(idx >= log->rxCnt_recycle)
			break;
		printk("[rxRcycle %03d]\n", idx);
		printk("type=%d totalSz=%d virtAddr=%p\n", record->type, record->totalSz, record->virtAddr[0]);
	}

}


void core_dump_record(_adapter *adapter, u8 dump_type)
{
	struct core_logs *log = &adapter->core_logs;
	u32 idx = 0;

	printk("txCnt_all: %d (%d) \n", log->txCnt_all, log->txCnt_all%CORE_LOG_NUM);
	printk("txCnt_data: %d \n", log->txCnt_data);
	printk("txCnt_mgmt: %d \n", log->txCnt_mgmt);
	printk("txCnt_phl: %d (%d), Sz=%d \n", log->txCnt_phl, log->txCnt_phl%CORE_LOG_NUM, log->txSize_phl);
	printk("txCnt_recycle: %d (%d), Sz=%d \n", log->txCnt_recycle, log->txCnt_recycle%CORE_LOG_NUM, log->txSize_recycle);

	printk("rxCnt_all: %d (%d) \n", log->rxCnt_all, log->rxCnt_all%CORE_LOG_NUM);
	printk("rxCnt_data: %d (retry=%d)\n", log->rxCnt_data, log->rxCnt_data_retry);
	printk("rxCnt_mgmt: %d \n", log->rxCnt_mgmt);
	printk("rxCnt_phl: %d (%d), Sz=%d \n", log->rxCnt_phl, log->rxCnt_phl%CORE_LOG_NUM, log->rxSize_phl);
	printk("rxCnt_recycle: %d (%d), Sz=%d\n", log->rxCnt_recycle, log->rxCnt_recycle%CORE_LOG_NUM, log->rxSize_recycle);
#ifdef CONFIG_RTW_CORE_RXSC
	printk("enable_rxsc: %d \n", adapter->enable_rxsc);
	printk("rxCnt_data: orig=%d shortcut=%d(ratio=%d)\n",
		log->rxCnt_data_orig, log->rxCnt_data_shortcut,
		log->rxCnt_data_shortcut*100/(log->rxCnt_data_orig+log->rxCnt_data_shortcut));
	printk("rxCnt_coreInd: %d \n", log->rxCnt_coreInd);
#endif

	if(dump_type == REC_DUMP_TX){
		core_dump_map_tx(adapter);
	}
	else if(dump_type == REC_DUMP_RX){
		core_dump_map_rx(adapter);
	}
	else if(dump_type == REC_DUMP_ALL){
		core_dump_map_tx(adapter);
		core_dump_map_rx(adapter);
	}
}

void core_add_record(_adapter *adapter, u8 type, void *p)
{
	struct core_logs *log = &adapter->core_logs;

	if(!adapter->record_enable)
		return;

	if(type == REC_TX_DATA){
		u32 idx = log->txCnt_all%CORE_LOG_NUM;
		struct core_record *record = &(log->drvTx[idx]);
		struct sk_buff *skb = p;
		
		log->txCnt_data++;
		record->type = type;
		record->totalSz = skb->len;
		record->virtAddr[0] = skb->data;
	}

	if(type == REC_TX_MGMT){
		u32 idx = log->txCnt_all%CORE_LOG_NUM;
		struct core_record *record = &(log->drvTx[idx]);
		struct xmit_frame *pxframe = p;
		
		log->txCnt_mgmt++;
		record->type = type;
		record->totalSz = pxframe->attrib.pktlen;
		record->virtAddr[0] = pxframe->buf_addr;
	}

	if(type == REC_TX_PHL || type == REC_TX_PHL_RCC){
		u32 idx = 0;
		struct core_record *record = NULL;
		struct rtw_xmit_req *txreq = p;

		if(type == REC_TX_PHL){
			idx = log->txCnt_phl%CORE_LOG_NUM;
			record = &(log->phlTx[idx]);
			log->txCnt_phl++;
		}
		
		if(type == REC_TX_PHL_RCC){
			idx = log->txCnt_recycle%CORE_LOG_NUM;
			record = &(log->txRcycle[idx]);
			log->txCnt_recycle++;
		}
		
		record->type = type;
		record->totalSz = 0;
		record->fragNum = txreq->pkt_cnt;

		{
			struct rtw_pkt_buf_list *pkt_list =(struct rtw_pkt_buf_list *)txreq->pkt_list;
			u32 idx1 = 0;
			for(idx1=0; idx1<txreq->pkt_cnt; idx1++){
				if(idx1 >= MAX_FRAG){
					printk("!! WARN[%s][%d] type=%d frag>= %d \n", 
						__FUNCTION__, __LINE__, type, MAX_FRAG);
					break;
				}
				record->totalSz += pkt_list->length;
				record->fragLen[idx1] = pkt_list->length;
				record->virtAddr[idx1] = pkt_list->vir_addr;
				record->phyAddrL[idx1] = pkt_list->phy_addr_l;
				record->phyAddrH[idx1] = pkt_list->phy_addr_h;
				pkt_list++;
			}
		}

		if(type == REC_TX_PHL)
			log->txSize_phl += record->totalSz;
		else if(type == REC_TX_PHL_RCC)
			log->txSize_recycle += record->totalSz;
		
	}

	if(type == REC_RX_PHL || type == REC_RX_PHL_RCC){
		u32 idx = 0;
		struct core_record *record = NULL;
		struct rtw_recv_pkt *rx_req = p;
		struct rtw_pkt_buf_list *pkt = rx_req->pkt_list;

		if(type == REC_RX_PHL){
			idx = log->rxCnt_phl%CORE_LOG_NUM;
			record = &(log->phlRx[idx]);
			log->rxCnt_phl++;
		}

		if(type == REC_RX_PHL_RCC){
			idx = log->rxCnt_recycle%CORE_LOG_NUM;
			record = &(log->rxRcycle[idx]);
			log->rxCnt_recycle++;
		}

		record->type = type;
		record->totalSz = pkt->length;
		record->virtAddr[0] = pkt->vir_addr;

		if(type == REC_RX_PHL)
			log->rxSize_phl += record->totalSz;
		else if(type == REC_RX_PHL_RCC)
			log->rxSize_recycle += record->totalSz;
	}

	if(type == REC_RX_DATA || type == REC_RX_MGMT){
		u32 idx = log->rxCnt_all%CORE_LOG_NUM;
		struct core_record *record = &(log->drvRx[idx]);
		union recv_frame *prframe = p;
		struct rx_pkt_attrib *pattrib = &prframe->u.hdr.attrib;
		
		if(type == REC_RX_DATA){
			log->rxCnt_data++;
		}
		
		if(type == REC_RX_MGMT){
			log->rxCnt_mgmt++;
		}

		record->type = type;
		record->totalSz = prframe->u.hdr.len;
		record->virtAddr[0] = prframe->u.hdr.rx_data;

		record->wl_seq = pattrib->seq_num;
		record->wl_type = pattrib->wl_type;
		record->wl_subtype = pattrib->wl_subtype;

	}

	if(type == REC_RX_DATA_RETRY){
		log->rxCnt_data_retry++;
	}

	log->txCnt_all = log->txCnt_mgmt + log->txCnt_data;
	log->rxCnt_all = log->rxCnt_mgmt + log->rxCnt_data;
}


void phl_dump_map_tx(_adapter *adapter)
{
	struct phl_logs *log = &adapter->phl_logs;
	u32 idx = 0;

 	printk("txBd MAP");
	for(idx=0; idx<CORE_LOG_NUM; idx++){
		struct record_txbd *record = &log->txBd[idx];
		if(idx >= log->txCnt_bd)
			break;
		printk("[txBd %03d]\n", idx);
		{
			u8 *tmp=record->bd_buf;
			u32 len = record->bd_len;
			u32 idx1 = 0;
			if(tmp == NULL)
				break;
			for(idx1=0; idx1<len; idx1++){
				if(idx1%8==0) {
					printk("[%03d] %02x %02x %02x %02x %02x %02x %02x %02x \n", 
						idx1, tmp[idx1], tmp[idx1+1], tmp[idx1+2], tmp[idx1+3], 
						tmp[idx1+4], tmp[idx1+5], tmp[idx1+6], tmp[idx1+7]);
				}
			}
			printk("\n");
		}
	}
	
	printk("========= \n\n");
	printk("txWd MAP");
	for(idx=0; idx<CORE_LOG_NUM; idx++){
		struct record_txwd *record = &log->txWd[idx];
		if(idx >= log->txCnt_wd)
			break;
		printk("[txWd %03d]\n", idx);
		{
			u8 *tmp=record->wd_buf;
			u32 len = record->wd_len;
			u32 idx1 = 0;
			if(tmp == NULL)
				break;
			for(idx1=0; idx1<len; idx1++){
				if(idx1%8==0) {
					printk("[%03d] %02x %02x %02x %02x %02x %02x %02x %02x \n", 
						idx1, tmp[idx1], tmp[idx1+1], tmp[idx1+2], tmp[idx1+3], 
						tmp[idx1+4], tmp[idx1+5], tmp[idx1+6], tmp[idx1+7]);
				}
			}
			printk("\n");
		}
		parse_wd_8852ae(adapter, TXWD_INFO_BODY, idx, record->wd_buf);
		//compare_wd_8852ae(adapter, TXWD_INFO_BODY, record->wd_buf, sample_txwd);
	}
	
	printk("========= \n\n");
 	printk("wpRecycle MAP");
	for(idx=0; idx<CORE_LOG_NUM; idx++){
		struct record_wp_rcc *record = &log->wpRecycle[idx];
		if(idx >= log->txCnt_recycle)
			break;
		printk("[txRecycle %03d]\n", idx);
		printk("wp_seq=%d \n", record->wp_seq);
	}
}

void phl_dump_map_rx(_adapter *adapter)
{
	struct phl_logs *log = &adapter->phl_logs;
	u32 idx = 0;

	printk("rxPciMap MAP");
	for(idx=0; idx<CORE_LOG_NUM; idx++){
		struct record_pci *record = &log->rxPciMap[idx];
		if(idx >= log->rxCnt_map)
			break;
		printk("[rxPciMap %03d]\n", idx);
		printk("phyAddrL=%p len=%d\n", record->phyAddrL, record->map_len);
	}


	printk("========= \n\n");
	printk("rxPciUnmap MAP");
	for(idx=0; idx<CORE_LOG_NUM; idx++){
		struct record_pci *record = &log->rxPciUnmap[idx];
		if(idx >= log->rxCnt_unmap)
			break;
		printk("[rxPciUnmap %03d]\n", idx);
		printk("phyAddrL=%p len=%d\n", record->phyAddrL, record->map_len);
	}

	printk("========= \n\n");
	printk("rxWd MAP");
	for(idx=0; idx<CORE_LOG_NUM; idx++){
		struct record_rxwd *record = &log->rxWd[idx];
		if(idx >= log->rxCnt_wd)
			break;
		printk("[rxWd %03d]\n", idx);
		{
			u8 *tmp = record->wd_buf;
			u32 len = record->wd_len;
			u32 idx1 = 0;
			if(tmp == NULL)
				break;
			for(idx1=0; idx1<len; idx1++){
				if(idx1%8==0) {
					printk("[%03d] %02x %02x %02x %02x %02x %02x %02x %02x \n", 
						idx1, tmp[idx1], tmp[idx1+1], tmp[idx1+2], tmp[idx1+3], 
						tmp[idx1+4], tmp[idx1+5], tmp[idx1+6], tmp[idx1+7]);
				}
			}
			printk("\n");
		}
		parse_wd_8852ae(adapter, RXWD, idx, record->wd_buf);
	}

	printk("========= \n\n");
	printk("rxAmpdu MAP");
	for(idx=0; idx<CORE_LOG_NUM; idx++){
		if(idx >= log->rxCnt_ampdu)
			break;
		printk("[rxAmpdu %03d] = %d\n", idx, log->rxAmpdu[idx]);
	}

}

void phl_dump_record(_adapter *adapter, u8 dump_type)
{
	struct phl_logs *log = &adapter->phl_logs;
	u32 idx = 0;

	printk("txBd: %d (%d) \n", log->txCnt_bd, log->txCnt_bd%CORE_LOG_NUM);
	printk("txWd: %d (%d) \n", log->txCnt_wd, log->txCnt_wd%CORE_LOG_NUM);
	printk("wpCnt_recycle: %d (%d) \n", log->txCnt_recycle, log->txCnt_recycle%CORE_LOG_NUM);

	printk("rxMap: %d (%d), Sz=%d \n", log->rxCnt_map, log->rxCnt_map%CORE_LOG_NUM, log->rxSize_map);
	printk("rxUnmap: %d (%d), Sz=%d \n", log->rxCnt_unmap, log->txCnt_wd%CORE_LOG_NUM, log->rxSize_map);
	printk("rxWd: %d (%d) \n", log->rxCnt_wd, log->rxCnt_wd%CORE_LOG_NUM);
	printk("rxCnt_ampdu: %d (%d) \n", log->rxCnt_ampdu, log->rxCnt_ampdu%CORE_LOG_NUM);

	if(dump_type == REC_DUMP_TX)
		phl_dump_map_tx(adapter);
	else if(dump_type == REC_DUMP_RX)
		phl_dump_map_rx(adapter);
	else if(dump_type == REC_DUMP_ALL){
		phl_dump_map_tx(adapter);
		phl_dump_map_rx(adapter);
	}
}

u32 tmp_rx_last_ppdu = 0;

void phl_add_record(void *d, u8 type, void *p, u32 num)
{
	struct dvobj_priv *pobj = (struct dvobj_priv *)d;
	_adapter *adapter = dvobj_get_primary_adapter(pobj);
	struct phl_logs *log = &adapter->phl_logs;

	if(!adapter->record_enable)
		return;

	if(type == REC_TXWD){
		u32 idx = log->txCnt_wd%CORE_LOG_NUM;
		struct record_txwd *record = &(log->txWd[idx]);
		
		log->txCnt_wd++;
		record->wd_len = num;
		memset((u8 *)record->wd_buf, 0, MAX_TXWD_SIZE);
		memcpy((u8 *)record->wd_buf, p, num);
	}

	if(type == REC_TXBD){
		u32 idx = log->txCnt_bd%CORE_LOG_NUM;
		struct record_txbd *record = &(log->txBd[idx]);
	
		log->txCnt_bd++;
		record->bd_len = num;
		memset((u8 *)record->bd_buf, 0, MAX_TXBD_SIZE);
		memcpy((u8 *)record->bd_buf, p, num);
	}

	if(type == REC_WP_RCC){
		u32 idx = log->txCnt_recycle%CORE_LOG_NUM;
		struct record_wp_rcc *record = &(log->wpRecycle[idx]);

		log->txCnt_recycle++;
		record->wp_seq = num;
	}

	if(type == REC_RX_MAP || type == REC_RX_UNMAP){
		struct record_pci *record = NULL;
		if(type == REC_RX_MAP) {
			u32 idx = log->rxCnt_map%CORE_LOG_NUM;
			record = &(log->rxPciMap[idx]);
			log->rxCnt_map++;
			log->rxSize_map+=num;
		}
		else if(type == REC_RX_UNMAP) {
			u32 idx = log->rxCnt_unmap%CORE_LOG_NUM;
			record = &(log->rxPciUnmap[idx]);
			log->rxCnt_unmap++;
			log->rxSize_map+=num;
		}
		record->phyAddrL = p;
		record->map_len = num;
	}

	if(type == REC_RXWD){
		u32 idx = log->rxCnt_wd%CORE_LOG_NUM;
		struct record_rxwd *record = &(log->rxWd[idx]);
	
		log->rxCnt_wd++;
		record->wd_len = num;
		memset((u8 *)record->wd_buf, 0, MAX_RXWD_SIZE);
		memcpy((u8 *)record->wd_buf, p, num);
	}

	if(type == REC_RX_AMPDU){
		u32 idx = 0; 

		if(log->rxCnt_ampdu == 0 && (log->rxAmpdu[0] == 0))
			tmp_rx_last_ppdu = num;

		if(tmp_rx_last_ppdu != num){
			tmp_rx_last_ppdu = num;

			log->rxCnt_ampdu ++;
			idx = log->rxCnt_ampdu%CORE_LOG_NUM;
			log->rxAmpdu[idx] = 1;
		}
		else{
			idx = log->rxCnt_ampdu%CORE_LOG_NUM;
			log->rxAmpdu[idx]++;
	}
}

}

void core_cmd_record_trx(_adapter *adapter, void *cmd_para, u32 para_num)
{
	u32 idx = 0;
	char *para = (char *)cmd_para;
	
	if(para_num<=0)
		return;
	
	if(!strcmp(para, "start")){
		u8 *log = NULL;
		log = (u8*)&adapter->core_logs;
		memset(log, 0, sizeof(struct core_logs));
		log = (u8*)&adapter->phl_logs;
		memset(log, 0, sizeof(struct phl_logs));
		adapter->record_enable = 1;
	}else if(!strcmp(para, "stop")){
		adapter->record_enable = 0;
	}else if(!strcmp(para, "dump")){
		u32 dump_type = 0;
		para=get_next_para_str(para);
		sscanf(para, "%d", &dump_type);
		phl_dump_record(adapter, (u8)dump_type);
	}
}

void reset_txforce_para(_adapter *adapter)
{
	adapter->txForce_rate 	= INV_TXFORCE_VAL;
	adapter->txForce_agg 	= INV_TXFORCE_VAL;
	adapter->txForce_aggnum = INV_TXFORCE_VAL;
	adapter->txForce_gi 	= INV_TXFORCE_VAL;
}

void core_cmd_txforce(_adapter *adapter, void *cmd_para, u32 para_num)
{
	u32 idx = 0;
	char *para = (char *)cmd_para;

	if(para_num<=0)
	return;

	if(!strcmp(para, "start")){
		adapter->txForce_enable = 1;
		reset_txforce_para(adapter);
	}else if(!strcmp(para, "stop")){
		adapter->txForce_enable = 0;
		reset_txforce_para(adapter);
	}else if(!strcmp(para, "rate")){
		u32 rate = 0;
		para=get_next_para_str(para);
		sscanf(para, "%x", &rate);
		adapter->txForce_rate = rate;
	}else if(!strcmp(para, "agg")){
		u32 agg = 0;
		para=get_next_para_str(para);
		sscanf(para, "%x", &agg);
		adapter->txForce_agg = agg;
	}else if(!strcmp(para, "aggnum")){
		u32 aggnum = 0;
		para=get_next_para_str(para);
		sscanf(para, "%d", &aggnum);
		adapter->txForce_aggnum = aggnum;
	}else if(!strcmp(para, "gi")){
		u32 gi = 0;
		para=get_next_para_str(para);
		sscanf(para, "%d", &gi);
		adapter->txForce_gi = gi;
	}else if(!strcmp(para, "macid")){

	}else if(!strcmp(para, "retry")){
	
	}
}

#ifdef CONFIG_RTW_CORE_RXSC
void core_cmd_rxsc(_adapter *adapter, void *cmd_para, u32 para_num)
{
	u32 idx = 0;
	char *para = (char *)cmd_para;

	printk("core_cmd_rxsc \n");

	if(para_num<=0)
		return;

	if(!strcmp(para, "enable")){
		printk("enable");
		adapter->enable_rxsc = 1;
	}else if(!strcmp(para, "disable")){
		printk("disable");
		adapter->enable_rxsc = 0;
	}else if(!strcmp(para, "dump")){
		struct core_logs *log = &adapter->core_logs;
		printk("dump");
		printk("enable_rxsc: %d \n", adapter->enable_rxsc);
		printk("rxCnt_data: orig=%d shortcut=%d(ratio=%d)\n",
			log->rxCnt_data_orig, log->rxCnt_data_shortcut,
			log->rxCnt_data_shortcut*100/(log->rxCnt_data_orig+log->rxCnt_data_shortcut));
	}

}
#endif

void core_sniffer_rx(_adapter *adapter, u8 *pkt, u32 pktlen)
{
	struct sk_buff* pskb = NULL;

	if(!adapter->sniffer_enable)
		return;
	
	if(pkt==NULL)
		return;

	pskb = dev_alloc_skb(pktlen+200);
	
	if(pskb == NULL){
	return;
}

	_rtw_memcpy(pskb->data, pkt, pktlen);
	pskb->len = pktlen;

	skb_reset_mac_header(pskb);
	pskb->dev = adapter->pnetdev;
	pskb->dev->type = ARPHRD_IEEE80211;
	pskb->ip_summed = CHECKSUM_UNNECESSARY;
	pskb->pkt_type = PACKET_OTHERHOST;
	pskb->protocol = htons(ETH_P_802_2);
	netif_receive_skb(pskb);

	return;
}
	
void core_cmd_sniffer(_adapter *adapter, void *cmd_para, u32 para_num)
	{
		u32 idx=0;
	char *para = (char *)cmd_para;
	
	if(para_num<=0)
	return;

	if(!strcmp(para, "start")){
		adapter->sniffer_enable = 1;
	}else if(!strcmp(para, "stop")){
		adapter->sniffer_enable = 0;
	}
}


#define LEN_TEST_BUF 2000
u8 test_buf[LEN_TEST_BUF];

#ifdef CONFIG_PCI_HCI
#include <rtw_trx_pci.h>
#endif

#include "../phl/phl_headers.h"
#include "../phl/phl_api.h"
#include "../phl/hal_g6/hal_api_mac.h"
#include "../phl/hal_g6/mac/mac_reg.h"


#define SHOW_REG32(adapter, reg) \
	do { \
		printk("\t%04X = %08X\n", \
			  (reg), rtw_phl_read32(adapter->dvobj->phl, reg)); \
	} while (0)

#define SHOW_REG32_MSG(adapter, reg, msg) \
	do { \
		printk("\t%04X = %08X - %s\n", \
			  (reg), rtw_phl_read32(adapter->dvobj->phl, reg), msg); \
	} while (0)

#define SHOW_REG16(adapter, reg) \
	do { \
		printk("\t%04X = %04X\n", \
			  (reg), rtw_phl_read16(adapter->dvobj->phl, reg)); \
	} while (0)

#define SHOW_REG16_MSG(adapter, reg, msg) \
	do { \
		printk("\t%04X = %04X - %s\n", \
			  (reg), rtw_phl_read16(adapter->dvobj->phl, reg), msg); \
	} while (0)


static inline void _show_RX_counter(_adapter *adapter)
{
	/* Show RX PPDU counters */
	int i;
	u32 reg32 = rtw_phl_read32(adapter->dvobj->phl, R_AX_RX_DBG_CNT_SEL);
	static const char *cnt_name[] = {"Invalid packet",
	                                 "RE-CCA",
	                                 "RX FIFO overflow",
	                                 "RX packet full drop",
	                                 "RX packet dma OK",
	                                 "UD 0",
	                                 "UD 1",
	                                 "UD 2",
	                                 "UD 3",
	                                 "continuous FCS error",
	                                 "RX packet filter drop",
	                                 "CSI packet DMA OK",
	                                 "CSI packet DMA drop",
	                                 "RX MAC stop"
	};

	printk("CMAC0 RX PPDU Counters @%04X:\n", R_AX_RX_DBG_CNT_SEL);
	reg32 &= ~(B_AX_RX_CNT_IDX_MSK << B_AX_RX_CNT_IDX_SH);
	for (i = 30; i < 44; i++) {
		rtw_phl_write32(adapter->dvobj->phl, R_AX_RX_DBG_CNT_SEL,
		            reg32 | (i << B_AX_RX_CNT_IDX_SH));
		printk("    %02X: %d - %s\n", i,
		         (
		            (   rtw_phl_read32(adapter->dvobj->phl, R_AX_RX_DBG_CNT_SEL)
		             >> B_AX_RX_DBG_CNT_SH)
		          & B_AX_RX_DBG_CNT_MSK),
		          cnt_name[i - 30]);
	}

	reg32 = rtw_phl_read32(adapter->dvobj->phl, R_AX_RX_DBG_CNT_SEL_C1);
	printk("CMAC1 RX PPDU Counters @%04X:\n", R_AX_RX_DBG_CNT_SEL_C1);
	reg32 &= ~(B_AX_RX_CNT_IDX_MSK << B_AX_RX_CNT_IDX_SH);
	for (i = 30; i < 44; i++) {
		rtw_phl_write32(adapter->dvobj->phl, R_AX_RX_DBG_CNT_SEL_C1,
		            reg32 | (i << B_AX_RX_CNT_IDX_SH));
		printk("    %02X: %d - %s\n", i,
		         (
		            (   rtw_phl_read32(adapter->dvobj->phl, R_AX_RX_DBG_CNT_SEL_C1)
		             >> B_AX_RX_DBG_CNT_SH)
		          & B_AX_RX_DBG_CNT_MSK),
		          cnt_name[i - 30]);
	}
} /* _show_RX_counter */

void _show_TX_dbg_status(_adapter *adapter)
{
	u32	reg32 = rtw_phl_read32(adapter->dvobj->phl, 0x9F1C);
	printk("TX Debug: 0x%08X\n", reg32);
}

void _show_BCN_dbg_status(_adapter *adapter)
{
	SHOW_REG32_MSG(adapter, R_AX_PORT_CFG_P0,		"PORT_CFG_P0");
	SHOW_REG32_MSG(adapter, R_AX_TBTT_PROHIB_P0,	"TBTT_PROHIB_P0");
	SHOW_REG32_MSG(adapter, R_AX_EN_HGQ_NOLIMIT,	"EN_HGQ_NOLIMIT");
	SHOW_REG32_MSG(adapter, R_AX_TBTT_AGG_P0,		"TBTT_AGG_P0");

	SHOW_REG32_MSG(adapter, R_AX_PORT_CFG_P0_C1,	"PORT_CFG_P0_C1");
	SHOW_REG32_MSG(adapter, R_AX_TBTT_PROHIB_P0_C1,	"TBTT_PROHIB_P0_C1");
	SHOW_REG32_MSG(adapter, R_AX_EN_HGQ_NOLIMIT_C1,	"EN_HGQ_NOLIMIT_C1");
	SHOW_REG32_MSG(adapter, R_AX_TBTT_AGG_P0_C1,	"TBTT_AGG_P0_C1");

	SHOW_REG32_MSG(adapter, R_AX_WCPU_FW_CTRL,		"R_AX_WCPU_FW_CTRL");
}


void core_cmd_dump_debug(_adapter *adapter, void *cmd_para, u32 para_num)
{
	printk("TX path registers: \n");

	SHOW_REG32_MSG(adapter, R_AX_RXQ_RXBD_IDX, "RX_BD_IDX");
	SHOW_REG32_MSG(adapter, R_AX_RPQ_RXBD_IDX, "RP_BD_IDX");
	SHOW_REG32_MSG(adapter, R_AX_ACH0_TXBD_IDX, "ACH0 IDX");
	SHOW_REG32_MSG(adapter, R_AX_ACH1_TXBD_IDX, "ACH1 IDX");
	SHOW_REG32_MSG(adapter, R_AX_ACH2_TXBD_IDX, "ACH2 IDX");
	SHOW_REG32_MSG(adapter, R_AX_ACH3_TXBD_IDX, "ACH3 IDX");
	SHOW_REG32_MSG(adapter, R_AX_ACH4_TXBD_IDX, "ACH4 IDX");
	SHOW_REG32_MSG(adapter, R_AX_ACH5_TXBD_IDX, "ACH5 IDX");
	SHOW_REG32_MSG(adapter, R_AX_ACH6_TXBD_IDX, "ACH6 IDX");
	SHOW_REG32_MSG(adapter, R_AX_ACH7_TXBD_IDX, "ACH7 IDX");
	SHOW_REG32_MSG(adapter, R_AX_CH8_TXBD_IDX, "CH8 IDX");
	SHOW_REG32_MSG(adapter, R_AX_CH9_TXBD_IDX, "CH9 IDX");
	SHOW_REG32_MSG(adapter, R_AX_CH10_TXBD_IDX, "CH10 IDX");
	SHOW_REG32_MSG(adapter, R_AX_CH11_TXBD_IDX, "CH11 IDX");
	SHOW_REG32_MSG(adapter, R_AX_CH12_TXBD_IDX, "CH12 IDX");
#ifdef R_AX_PCIE_DBG_CTRL
	SHOW_REG32_MSG(adapter, R_AX_PCIE_DBG_CTRL, "DBG_CTRL");
#else
	SHOW_REG32_MSG(adapter, 0x11C0, "DBG_CTRL");
#endif
	SHOW_REG32_MSG(adapter, R_AX_DBG_ERR_FLAG, "DBG_ERR");
	SHOW_REG32_MSG(adapter, R_AX_PCIE_HIMR00, "IMR0");
	SHOW_REG32_MSG(adapter, R_AX_PCIE_HISR00, "ISR0");
	SHOW_REG32_MSG(adapter, R_AX_PCIE_HIMR10, "IMR1");
	SHOW_REG32_MSG(adapter, R_AX_PCIE_HISR10, "IMR1");
	SHOW_REG16_MSG(adapter, R_AX_ACH0_BDRAM_RWPTR, "CH0");
	SHOW_REG16_MSG(adapter, R_AX_ACH1_BDRAM_RWPTR, "CH1");
	SHOW_REG16_MSG(adapter, R_AX_ACH2_BDRAM_RWPTR, "CH2");
	SHOW_REG16_MSG(adapter, R_AX_ACH3_BDRAM_RWPTR, "CH3");
	SHOW_REG16_MSG(adapter, R_AX_ACH4_BDRAM_RWPTR, "CH4");
	SHOW_REG16_MSG(adapter, R_AX_ACH5_BDRAM_RWPTR, "CH5");
	SHOW_REG16_MSG(adapter, R_AX_ACH6_BDRAM_RWPTR, "CH6");
	SHOW_REG16_MSG(adapter, R_AX_ACH7_BDRAM_RWPTR, "CH7");
	SHOW_REG16_MSG(adapter, R_AX_CH8_BDRAM_RWPTR, "CH8");
	SHOW_REG16_MSG(adapter, R_AX_CH9_BDRAM_RWPTR, "CH9");
	SHOW_REG16_MSG(adapter, R_AX_CH10_BDRAM_RWPTR, "CH10");
	SHOW_REG16_MSG(adapter, R_AX_CH11_BDRAM_RWPTR, "CH11");
	SHOW_REG16_MSG(adapter, R_AX_CH12_BDRAM_RWPTR, "CH12");
	SHOW_REG32_MSG(adapter, R_AX_PCIE_DMA_STOP1, "DMA_STOP1");
	SHOW_REG32_MSG(adapter, R_AX_PCIE_DMA_BUSY1, "DMA_BUSY1");
	SHOW_REG32_MSG(adapter, R_AX_PCIE_DMA_STOP2, "DMA_STOP2");
	SHOW_REG32_MSG(adapter, R_AX_PCIE_DMA_BUSY2, "DMA_BUSY2");
	SHOW_REG32(adapter, 0x8840);
	SHOW_REG32(adapter, 0x8844);
	SHOW_REG32(adapter, 0x8854);
	SHOW_REG16(adapter, 0xCA22);
	SHOW_REG32(adapter, 0x8AA8);

	/* Show TX PPDU counters */
	do {
		int i;
		u32 reg32 = rtw_phl_read32(adapter->dvobj->phl, R_AX_TX_PPDU_CNT);

		printk("CMAC0 TX PPDU Counters @%04X:\n", R_AX_TX_PPDU_CNT);

		reg32 &= ~(B_AX_PPDU_CNT_IDX_MSK << B_AX_PPDU_CNT_IDX_SH);
		for (i = 0; i < 11; i++) {
			rtw_phl_write32(adapter->dvobj->phl, R_AX_TX_PPDU_CNT,
			            reg32 | (i << B_AX_PPDU_CNT_IDX_SH));
			printk("    %02X: %d\n", i,
			         (
			            (   rtw_phl_read32(adapter->dvobj->phl, R_AX_TX_PPDU_CNT)
			             >> B_AX_TX_PPDU_CNT_SH)
			          & B_AX_TX_PPDU_CNT_MSK));
		}

		reg32 = rtw_phl_read32(adapter->dvobj->phl, R_AX_TX_PPDU_CNT_C1);

		printk("CMAC1 TX PPDU Counters @%04X:\n", R_AX_TX_PPDU_CNT_C1);

		reg32 &= ~(B_AX_PPDU_CNT_IDX_MSK << B_AX_PPDU_CNT_IDX_SH);
		for (i = 0; i < 11; i++) {
			rtw_phl_write32(adapter->dvobj->phl, R_AX_TX_PPDU_CNT_C1,
			            reg32 | (i << B_AX_PPDU_CNT_IDX_SH));
			printk("    %02X: %d\n", i,
			         (
			            (   rtw_phl_read32(adapter->dvobj->phl, R_AX_TX_PPDU_CNT_C1)
			             >> B_AX_TX_PPDU_CNT_SH)
			          & B_AX_TX_PPDU_CNT_MSK));
		}
	} while (0);

	/* Show RX PPDU counters */
	_show_RX_counter(adapter);

	_show_TX_dbg_status(adapter);

	_show_BCN_dbg_status(adapter);

}

void core_cmd_dump_reg(_adapter *adapter, void *cmd_para, u32 para_num)
{
	u32 *para = (u32 *)cmd_para;
	void *phl = adapter->dvobj->phl;
	u32 reg_start, reg_end;
	u32 idx = 0;

	reg_start = para[0];
	reg_end = reg_start + para[1];

	while(1) {
		if((reg_start>=reg_end) || (reg_start >= 0xffff))
			break;
		
		printk("[%04x] %08x %08x %08x %08x \n", 
			reg_start, 
			rtw_phl_read32(phl, reg_start), rtw_phl_read32(phl, reg_start+4),
			rtw_phl_read32(phl, reg_start+8), rtw_phl_read32(phl, reg_start+12));

		reg_start+=16;
	}
}

enum _CORE_CMD_PARA_TYPE {
	CMD_PARA_DEC = 0,
	CMD_PARA_HEX,
	CMD_PARA_STR,
};

struct test_cmd_list {
	const char *name;
	void (*fun)(_adapter *, void *, u32);
	enum _CORE_CMD_PARA_TYPE para_type;
};

void test_dump_dec(_adapter *adapter, void *cmd_para, u32 para_num)
{
	u32 idx = 0;
	u32 *para = (u32 *)cmd_para;
	DBGP("para_num=%d\n", para_num);

	for(idx=0; idx<para_num; idx++)
		DBGP("para[%d]=%d\n", para_num, para[idx]);
	}

void test_dump_hex(_adapter *adapter, void *cmd_para, u32 para_num)
{
	u32 idx = 0;
	u32 *para = (u32 *)cmd_para;
	DBGP("para_num=%d\n", para_num);

	for(idx=0; idx<para_num; idx++)
		DBGP("para[%d]=0x%x\n", para_num, para[idx]);
}

void test_dump_str(_adapter *adapter, void *cmd_para, u32 para_num)
{
	u32 idx = 0;
	char *para = (char *)cmd_para;
	DBGP("para_num=%d\n", para_num);

	for(idx=0; idx<para_num; idx++, para+=MAX_PHL_CMD_STR_LEN)
		DBGP("para[%d]=%s\n", para_num, para);
}

void get_all_cmd_para_value(_adapter *adapter, char *buf, u32 len, u32 *para, u8 type, u32 *num)
{
	u8 *tmp = NULL;

	if(!buf || !len)
		return;

		DBGP("type=%d buf=%s para=%p num=%d\n", type, buf, para, *num);

	if(len > 0){
		tmp = strsep(&buf, ",");

		if(tmp){
			if(type == CMD_PARA_HEX)
				sscanf(tmp, "%x", para);
			else if(type == CMD_PARA_DEC)
				sscanf(tmp, "%d", para);
			para += 1;
			*num = *num+1;
		}
		else
			return;

		if(buf && (len>strlen(tmp)))
			get_all_cmd_para_value(adapter, buf, strlen(buf), para, type, num);
		else
			return;
	}
	}

void get_all_cmd_para_str(_adapter *adapter, char *buf, u32 len, char *para, u8 type, u32* num)
{
	u8 *tmp = NULL;

	if(!buf || !len)
		return;

	DBGP("type=%d buf=%s para=%p num=%d\n", type, buf, para, *num);

	if(len > 0){
		tmp = strsep(&buf, ",");

		if(tmp){
			strcpy(para, tmp);
			para += MAX_PHL_CMD_STR_LEN;
			*num = *num+1;
	}
		else
			return;

		if(buf && (len>strlen(tmp)))
			get_all_cmd_para_str(adapter, buf, strlen(buf), para, type, num);
		else
			return;
	}
	}

struct test_cmd_list core_test_cmd_list[] = {
	{"dec", test_dump_dec, CMD_PARA_DEC},
	{"hex", test_dump_hex, CMD_PARA_HEX},
	{"str", test_dump_str, CMD_PARA_STR},
	{"dump_reg", 		core_cmd_dump_reg, 		CMD_PARA_HEX},
	{"dump_debug", 		core_cmd_dump_debug, 	CMD_PARA_DEC},
	{"record", 			core_cmd_record_trx, 	CMD_PARA_STR},
	{"txforce",			core_cmd_txforce, 		CMD_PARA_STR},
	{"sniffer",			core_cmd_sniffer, 		CMD_PARA_STR},
#ifdef CONFIG_RTW_CORE_RXSC
	{"rxsc",	core_cmd_rxsc,		CMD_PARA_STR},
#endif
};

void core_cmd_phl_handler(_adapter *adapter, char *extra)
{
	u32 para[MAX_PHL_CMD_NUM]={0};
	char para_str[MAX_PHL_CMD_NUM][MAX_PHL_CMD_STR_LEN]={0};
	char *cmd_name, *cmd_para = NULL;
	struct test_cmd_list *cmd = &core_test_cmd_list;
	u32 array_size = ARRAY_SIZE(core_test_cmd_list);
	u32 i = 0;
	
	cmd_name = strsep(&extra, ",");

	if(!cmd_name){
		for(i = 0; i<array_size; i++, cmd++)
			printk(" - %s\n", cmd->name);
		return;
	}

	for(i = 0; i<array_size; i++, cmd++){
		if(!strcmp(cmd->name, cmd_name)){
			void *cmd_para = NULL;
			u32 cmd_para_num = 0;
			if(cmd->para_type == CMD_PARA_DEC || cmd->para_type == CMD_PARA_HEX){
				cmd_para = para;
				if(extra)
				get_all_cmd_para_value(adapter, extra, strlen(extra), para, cmd->para_type, &cmd_para_num);
		}
		else{
				cmd_para = para_str;
				if(extra)
				get_all_cmd_para_str(adapter, extra, strlen(extra), para_str, cmd->para_type, &cmd_para_num);
	}

			cmd->fun(adapter, cmd_para, cmd_para_num);
			break;
	}
	}
	
}

#endif

u8 rtw_test_h2c_cmd(_adapter *adapter, u8 *buf, u8 len)
{
	struct cmd_obj *pcmdobj;
	struct drvextra_cmd_parm *pdrvextra_cmd_parm;
	u8 *ph2c_content;
	struct cmd_priv *pcmdpriv = &adapter_to_dvobj(adapter)->cmdpriv;
	u8	res = _SUCCESS;

	pcmdobj = (struct cmd_obj *)rtw_zmalloc(sizeof(struct cmd_obj));
	if (pcmdobj == NULL) {
		res = _FAIL;
		goto exit;
	}
	pcmdobj->padapter = adapter;

	pdrvextra_cmd_parm = (struct drvextra_cmd_parm *)rtw_zmalloc(sizeof(struct drvextra_cmd_parm));
	if (pdrvextra_cmd_parm == NULL) {
		rtw_mfree((u8 *)pcmdobj, sizeof(struct cmd_obj));
		res = _FAIL;
		goto exit;
	}

	ph2c_content = rtw_zmalloc(len);
	if (ph2c_content == NULL) {
		rtw_mfree((u8 *)pcmdobj, sizeof(struct cmd_obj));
		rtw_mfree((u8 *)pdrvextra_cmd_parm, sizeof(struct drvextra_cmd_parm));
		res = _FAIL;
		goto exit;
	}

	pdrvextra_cmd_parm->ec_id = TEST_H2C_CID;
	pdrvextra_cmd_parm->type = 0;
	pdrvextra_cmd_parm->size = len;
	pdrvextra_cmd_parm->pbuf = ph2c_content;

	_rtw_memcpy(ph2c_content, buf, len);

	init_h2fwcmd_w_parm_no_rsp(pcmdobj, pdrvextra_cmd_parm, CMD_SET_DRV_EXTRA);

	res = rtw_enqueue_cmd(pcmdpriv, pcmdobj);

exit:
	return res;
}

#ifdef CONFIG_MP_INCLUDED
static s32 rtw_mp_cmd_hdl(_adapter *padapter, u8 mp_cmd_id)
{
	struct dvobj_priv *dvobj = adapter_to_dvobj(padapter);
	int ret = H2C_SUCCESS;
	uint status = _FALSE;
	struct mp_priv *pmppriv = &padapter->mppriv;
	struct rtw_test_module_info test_module_info;
	struct rtw_mp_test_rpt my_mp_test_rpt;

	if (mp_cmd_id == MP_START) {
		if (padapter->registrypriv.mp_mode == 0) {

			test_module_info.tm_mode = RTW_DRV_MODE_MP;
			test_module_info.tm_type = RTW_TEST_SUB_MODULE_MP;
			pmppriv->keep_ips_status = dvobj->phl_com->dev_sw_cap.ps_cap.ips_en;
			pmppriv->keep_lps_status = dvobj->phl_com->dev_sw_cap.ps_cap.lps_en;
			dvobj->phl_com->dev_sw_cap.ps_cap.ips_en = PS_OP_MODE_DISABLED;
			dvobj->phl_com->dev_sw_cap.ps_cap.lps_en = PS_OP_MODE_DISABLED;

			rtw_phl_test_submodule_init(dvobj->phl_com, &test_module_info);
#ifdef CONFIG_BTC
			if (dvobj->phl_com->dev_cap.btc_mode == BTC_MODE_NORMAL ||\
					dvobj->phl_com->dev_cap.btc_mode == BTC_MODE_BT) {

				pmppriv->mp_keep_btc_mode = dvobj->phl_com->dev_cap.btc_mode;
				rtw_mp_phl_config_arg(padapter, RTW_MP_CONFIG_CMD_SWITCH_BT_PATH);
				RTW_INFO("config BTC to WL Only\n");
			}
#endif
			rtw_mp_phl_config_arg(padapter, RTW_MP_CONFIG_CMD_SET_PHY_INDEX);
			status = rtw_mp_phl_config_arg(padapter, RTW_MP_CONFIG_CMD_START_DUT);

			if (status == _TRUE) {
				padapter->registrypriv.mp_mode = 1;
				MPT_InitializeAdapter(padapter, 1);
			}
		}

		if (padapter->registrypriv.mp_mode == 0) {
			ret = H2C_REJECTED;
			goto exit;
		}

		if (padapter->mppriv.mode == MP_OFF) {
			if (mp_start_test(padapter) == _FAIL) {
				ret = H2C_REJECTED;
				goto exit;
			}
			padapter->mppriv.mode = MP_ON;
		}
		padapter->mppriv.bmac_filter = _FALSE;

	} else if (mp_cmd_id == MP_STOP) {
		if (padapter->registrypriv.mp_mode == 1) {

			status = rtw_mp_phl_config_arg(padapter, RTW_MP_CONFIG_CMD_STOP_DUT);
			RTW_INFO("RTW_MP_CONFIG_CMD_STOP_DUT %s\n", status == _TRUE ? "ok" : "fail");
			rtw_phl_test_get_rpt(dvobj->phl_com, (u8*)&my_mp_test_rpt, sizeof(my_mp_test_rpt));

			if (my_mp_test_rpt.status == _TRUE)
				RTW_INFO("TM Sub finished OK!!!\n");
#ifdef CONFIG_BTC
			if (pmppriv->mp_keep_btc_mode != BTC_MODE_MAX) {
				pmppriv->btc_path = pmppriv->mp_keep_btc_mode;
				rtw_mp_phl_config_arg(padapter, RTW_MP_CONFIG_CMD_SWITCH_BT_PATH);
			}
#endif
			test_module_info.tm_mode = RTW_DRV_MODE_NORMAL;
			test_module_info.tm_type = RTW_TEST_SUB_MODULE_MP;
			dvobj->phl_com->dev_sw_cap.ps_cap.ips_en = pmppriv->keep_ips_status;
			dvobj->phl_com->dev_sw_cap.ps_cap.lps_en = pmppriv->keep_lps_status;

			rtw_phl_test_submodule_deinit(dvobj->phl_com, &test_module_info);

			MPT_DeInitAdapter(padapter);
			padapter->registrypriv.mp_mode = 0;
		}

		if (padapter->mppriv.mode != MP_OFF) {
			mp_stop_test(padapter);
			padapter->mppriv.mode = MP_OFF;
		}

	} else {
		RTW_INFO(FUNC_ADPT_FMT"invalid id:%d\n", FUNC_ADPT_ARG(padapter), mp_cmd_id);
		ret = H2C_PARAMETERS_ERROR;
		rtw_warn_on(1);
	}

exit:
	return ret;
}

u8 rtw_mp_cmd(_adapter *adapter, u8 mp_cmd_id, u8 flags)
{
	struct cmd_obj *cmdobj;
	struct drvextra_cmd_parm *parm;
	struct cmd_priv *pcmdpriv = &adapter_to_dvobj(adapter)->cmdpriv;
	struct submit_ctx sctx;
	u8	res = _SUCCESS;

	parm = (struct drvextra_cmd_parm *)rtw_zmalloc(sizeof(struct drvextra_cmd_parm));
	if (parm == NULL) {
		res = _FAIL;
		goto exit;
	}

	parm->ec_id = MP_CMD_WK_CID;
	parm->type = mp_cmd_id;
	parm->size = 0;
	parm->pbuf = NULL;

	if (flags & RTW_CMDF_DIRECTLY) {
		/* no need to enqueue, do the cmd hdl directly and free cmd parameter */
		if (H2C_SUCCESS != rtw_mp_cmd_hdl(adapter, mp_cmd_id))
			res = _FAIL;
		rtw_mfree((u8 *)parm, sizeof(*parm));
	} else {
		/* need enqueue, prepare cmd_obj and enqueue */
		cmdobj = (struct cmd_obj *)rtw_zmalloc(sizeof(*cmdobj));
		if (cmdobj == NULL) {
			res = _FAIL;
			rtw_mfree((u8 *)parm, sizeof(*parm));
			goto exit;
		}
		cmdobj->padapter = adapter;

		init_h2fwcmd_w_parm_no_rsp(cmdobj, parm, CMD_SET_DRV_EXTRA);

		if (flags & RTW_CMDF_WAIT_ACK) {
			cmdobj->sctx = &sctx;
			rtw_sctx_init(&sctx, 10 * 1000);
		}

		res = rtw_enqueue_cmd(pcmdpriv, cmdobj);

		if (res == _SUCCESS && (flags & RTW_CMDF_WAIT_ACK)) {
			rtw_sctx_wait(&sctx, __func__);
			_rtw_mutex_lock_interruptible(&pcmdpriv->sctx_mutex);
			if (sctx.status == RTW_SCTX_SUBMITTED)
				cmdobj->sctx = NULL;
			_rtw_mutex_unlock(&pcmdpriv->sctx_mutex);
			if (sctx.status != RTW_SCTX_DONE_SUCCESS)
				res = _FAIL;
		}
	}

exit:
	return res;
}
#endif	/*CONFIG_MP_INCLUDED*/

#ifdef CONFIG_RTW_CUSTOMER_STR
static s32 rtw_customer_str_cmd_hdl(_adapter *adapter, u8 write, const u8 *cstr)
{
	int ret = H2C_SUCCESS;

	if (write)
		ret = rtw_hal_h2c_customer_str_write(adapter, cstr);
	else
		ret = rtw_hal_h2c_customer_str_req(adapter);

	return ret == _SUCCESS ? H2C_SUCCESS : H2C_REJECTED;
}

static u8 rtw_customer_str_cmd(_adapter *adapter, u8 write, const u8 *cstr)
{
	struct cmd_obj *cmdobj;
	struct drvextra_cmd_parm *parm;
	u8 *str = NULL;
	struct cmd_priv *pcmdpriv = &adapter_to_dvobj(adapter)->cmdpriv;
	struct submit_ctx sctx;
	u8 res = _SUCCESS;

	parm = (struct drvextra_cmd_parm *)rtw_zmalloc(sizeof(struct drvextra_cmd_parm));
	if (parm == NULL) {
		res = _FAIL;
		goto exit;
	}

	if (write) {
		str = rtw_zmalloc(RTW_CUSTOMER_STR_LEN);
		if (str == NULL) {
			rtw_mfree((u8 *)parm, sizeof(struct drvextra_cmd_parm));
			res = _FAIL;
			goto exit;
		}
	}

	parm->ec_id = CUSTOMER_STR_WK_CID;
	parm->type = write;
	parm->size = write ? RTW_CUSTOMER_STR_LEN : 0;
	parm->pbuf = write ? str : NULL;

	if (write)
		_rtw_memcpy(str, cstr, RTW_CUSTOMER_STR_LEN);

	/* need enqueue, prepare cmd_obj and enqueue */
	cmdobj = (struct cmd_obj *)rtw_zmalloc(sizeof(*cmdobj));
	if (cmdobj == NULL) {
		res = _FAIL;
		rtw_mfree((u8 *)parm, sizeof(*parm));
		if (write)
			rtw_mfree(str, RTW_CUSTOMER_STR_LEN);
		goto exit;
	}
	cmdobj->padapter = adapter;

	init_h2fwcmd_w_parm_no_rsp(cmdobj, parm, CMD_SET_DRV_EXTRA);

	cmdobj->sctx = &sctx;
	rtw_sctx_init(&sctx, 2 * 1000);

	res = rtw_enqueue_cmd(pcmdpriv, cmdobj);

	if (res == _SUCCESS) {
		rtw_sctx_wait(&sctx, __func__);
		_rtw_mutex_lock_interruptible(&pcmdpriv->sctx_mutex);
		if (sctx.status == RTW_SCTX_SUBMITTED)
			cmdobj->sctx = NULL;
		_rtw_mutex_unlock(&pcmdpriv->sctx_mutex);
		if (sctx.status != RTW_SCTX_DONE_SUCCESS)
			res = _FAIL;
	}

exit:
	return res;
}

inline u8 rtw_customer_str_req_cmd(_adapter *adapter)
{
	return rtw_customer_str_cmd(adapter, 0, NULL);
}

inline u8 rtw_customer_str_write_cmd(_adapter *adapter, const u8 *cstr)
{
	return rtw_customer_str_cmd(adapter, 1, cstr);
}
#endif /* CONFIG_RTW_CUSTOMER_STR */

u8 rtw_c2h_wk_cmd(_adapter *padapter, u8 *pbuf, u16 length, u8 type)
{
	struct cmd_obj *cmd;
	struct drvextra_cmd_parm *pdrvextra_cmd_parm;
	struct cmd_priv *pcmdpriv = &adapter_to_dvobj(padapter)->cmdpriv;
	u8 *extra_cmd_buf;
	u8 res = _SUCCESS;

	cmd = (struct cmd_obj *)rtw_zmalloc(sizeof(struct cmd_obj));
	if (cmd == NULL) {
		res = _FAIL;
		goto exit;
	}
	cmd->padapter = padapter;

	pdrvextra_cmd_parm = (struct drvextra_cmd_parm *)rtw_zmalloc(sizeof(struct drvextra_cmd_parm));
	if (pdrvextra_cmd_parm == NULL) {
		rtw_mfree((u8 *)cmd, sizeof(struct cmd_obj));
		res = _FAIL;
		goto exit;
	}

	extra_cmd_buf = rtw_zmalloc(length);
	if (extra_cmd_buf == NULL) {
		rtw_mfree((u8 *)cmd, sizeof(struct cmd_obj));
		rtw_mfree((u8 *)pdrvextra_cmd_parm, sizeof(struct drvextra_cmd_parm));
		res = _FAIL;
		goto exit;
	}

	_rtw_memcpy(extra_cmd_buf, pbuf, length);
	pdrvextra_cmd_parm->ec_id = C2H_WK_CID;
	pdrvextra_cmd_parm->type = type;
	pdrvextra_cmd_parm->size = length;
	pdrvextra_cmd_parm->pbuf = extra_cmd_buf;

	init_h2fwcmd_w_parm_no_rsp(cmd, pdrvextra_cmd_parm, CMD_SET_DRV_EXTRA);

	res = rtw_enqueue_cmd(pcmdpriv, cmd);

exit:
	return res;
}

#define C2H_TYPE_PKT 1
inline u8 rtw_c2h_packet_wk_cmd(_adapter *adapter, u8 *c2h_evt, u16 length)
{
	return rtw_c2h_wk_cmd(adapter, c2h_evt, length, C2H_TYPE_PKT);
}

static u8 _rtw_run_in_thread_cmd(_adapter *adapter, void (*func)(void *), void *context, s32 timeout_ms)
{
	struct cmd_priv *cmdpriv = &adapter_to_dvobj(adapter)->cmdpriv;
	struct cmd_obj *cmdobj;
	struct RunInThread_param *parm;
	struct submit_ctx sctx;
	s32 res = _SUCCESS;

	cmdobj = (struct cmd_obj *)rtw_zmalloc(sizeof(struct cmd_obj));
	if (NULL == cmdobj) {
		res = _FAIL;
		goto exit;
	}
	cmdobj->padapter = adapter;

	parm = (struct RunInThread_param *)rtw_zmalloc(sizeof(struct RunInThread_param));
	if (NULL == parm) {
		rtw_mfree((u8 *)cmdobj, sizeof(struct cmd_obj));
		res = _FAIL;
		goto exit;
	}

	parm->func = func;
	parm->context = context;
	init_h2fwcmd_w_parm_no_rsp(cmdobj, parm, CMD_RUN_INTHREAD);

	if (timeout_ms >= 0) {
		cmdobj->sctx = &sctx;
		rtw_sctx_init(&sctx, timeout_ms);
	}

	res = rtw_enqueue_cmd(cmdpriv, cmdobj);

	if (res == _SUCCESS && timeout_ms >= 0) {
		rtw_sctx_wait(&sctx, __func__);
		_rtw_mutex_lock_interruptible(&cmdpriv->sctx_mutex);
		if (sctx.status == RTW_SCTX_SUBMITTED)
			cmdobj->sctx = NULL;
		_rtw_mutex_unlock(&cmdpriv->sctx_mutex);
		if (sctx.status != RTW_SCTX_DONE_SUCCESS)
			res = _FAIL;
	}

exit:
	return res;
}
u8 rtw_run_in_thread_cmd(_adapter *adapter, void (*func)(void *), void *context)
{
	return _rtw_run_in_thread_cmd(adapter, func, context, -1);
}

u8 rtw_run_in_thread_cmd_wait(_adapter *adapter, void (*func)(void *), void *context, s32 timeout_ms)
{
	return _rtw_run_in_thread_cmd(adapter, func, context, timeout_ms);
}


u8 session_tracker_cmd(_adapter *adapter, u8 cmd, struct sta_info *sta, u8 *local_naddr, u8 *local_port, u8 *remote_naddr, u8 *remote_port)
{
	struct cmd_priv	*cmdpriv = &adapter_to_dvobj(adapter)->cmdpriv;
	struct cmd_obj *cmdobj;
	struct drvextra_cmd_parm *cmd_parm;
	struct st_cmd_parm *st_parm;
	u8	res = _SUCCESS;

	cmdobj = (struct cmd_obj *)rtw_zmalloc(sizeof(struct cmd_obj));
	if (cmdobj == NULL) {
		res = _FAIL;
		goto exit;
	}
	cmdobj->padapter = adapter;

	cmd_parm = (struct drvextra_cmd_parm *)rtw_zmalloc(sizeof(struct drvextra_cmd_parm));
	if (cmd_parm == NULL) {
		rtw_mfree((u8 *)cmdobj, sizeof(struct cmd_obj));
		res = _FAIL;
		goto exit;
	}

	st_parm = (struct st_cmd_parm *)rtw_zmalloc(sizeof(struct st_cmd_parm));
	if (st_parm == NULL) {
		rtw_mfree((u8 *)cmdobj, sizeof(struct cmd_obj));
		rtw_mfree((u8 *)cmd_parm, sizeof(struct drvextra_cmd_parm));
		res = _FAIL;
		goto exit;
	}

	st_parm->cmd = cmd;
	st_parm->sta = sta;
	if (cmd != ST_CMD_CHK) {
		_rtw_memcpy(&st_parm->local_naddr, local_naddr, 4);
		_rtw_memcpy(&st_parm->local_port, local_port, 2);
		_rtw_memcpy(&st_parm->remote_naddr, remote_naddr, 4);
		_rtw_memcpy(&st_parm->remote_port, remote_port, 2);
	}

	cmd_parm->ec_id = SESSION_TRACKER_WK_CID;
	cmd_parm->type = 0;
	cmd_parm->size = sizeof(struct st_cmd_parm);
	cmd_parm->pbuf = (u8 *)st_parm;
	init_h2fwcmd_w_parm_no_rsp(cmdobj, cmd_parm, CMD_SET_DRV_EXTRA);
	cmdobj->no_io = 1;

	res = rtw_enqueue_cmd(cmdpriv, cmdobj);

exit:
	return res;
}

inline u8 session_tracker_chk_cmd(_adapter *adapter, struct sta_info *sta)
{
	return session_tracker_cmd(adapter, ST_CMD_CHK, sta, NULL, NULL, NULL, NULL);
}

inline u8 session_tracker_add_cmd(_adapter *adapter, struct sta_info *sta, u8 *local_naddr, u8 *local_port, u8 *remote_naddr, u8 *remote_port)
{
	return session_tracker_cmd(adapter, ST_CMD_ADD, sta, local_naddr, local_port, remote_naddr, remote_port);
}

inline u8 session_tracker_del_cmd(_adapter *adapter, struct sta_info *sta, u8 *local_naddr, u8 *local_port, u8 *remote_naddr, u8 *remote_port)
{
	return session_tracker_cmd(adapter, ST_CMD_DEL, sta, local_naddr, local_port, remote_naddr, remote_port);
}

void session_tracker_chk_for_sta(_adapter *adapter, struct sta_info *sta)
{
	struct st_ctl_t *st_ctl = &sta->st_ctl;
	int i;
	_list *plist, *phead, *pnext;
	_list dlist;
	struct session_tracker *st = NULL;
	u8 op_wfd_mode = MIRACAST_DISABLED;

	if (DBG_SESSION_TRACKER)
		RTW_INFO(FUNC_ADPT_FMT" sta:%p\n", FUNC_ADPT_ARG(adapter), sta);

	if (!(sta->state & WIFI_ASOC_STATE))
		goto exit;

	for (i = 0; i < SESSION_TRACKER_REG_ID_NUM; i++) {
		if (st_ctl->reg[i].s_proto != 0)
			break;
	}
	if (i >= SESSION_TRACKER_REG_ID_NUM)
		goto chk_sta;

	_rtw_init_listhead(&dlist);

	_rtw_spinlock_bh(&st_ctl->tracker_q.lock);

	phead = &st_ctl->tracker_q.queue;
	plist = get_next(phead);
	pnext = get_next(plist);
	while (rtw_end_of_queue_search(phead, plist) == _FALSE) {
		st = LIST_CONTAINOR(plist, struct session_tracker, list);
		plist = pnext;
		pnext = get_next(pnext);

		if (st->status != ST_STATUS_ESTABLISH
			&& rtw_get_passing_time_ms(st->set_time) > ST_EXPIRE_MS
		) {
			rtw_list_delete(&st->list);
			rtw_list_insert_tail(&st->list, &dlist);
		}

		/* TODO: check OS for status update */
		if (st->status == ST_STATUS_CHECK)
			st->status = ST_STATUS_ESTABLISH;

		if (st->status != ST_STATUS_ESTABLISH)
			continue;

		#ifdef CONFIG_WFD
		if (0)
			RTW_INFO(FUNC_ADPT_FMT" local:%u, remote:%u, rtsp:%u, %u, %u\n", FUNC_ADPT_ARG(adapter)
				, ntohs(st->local_port), ntohs(st->remote_port), adapter->wfd_info.rtsp_ctrlport, adapter->wfd_info.tdls_rtsp_ctrlport
				, adapter->wfd_info.peer_rtsp_ctrlport);
		if (ntohs(st->local_port) == adapter->wfd_info.rtsp_ctrlport)
			op_wfd_mode |= MIRACAST_SINK;
		if (ntohs(st->local_port) == adapter->wfd_info.tdls_rtsp_ctrlport)
			op_wfd_mode |= MIRACAST_SINK;
		if (ntohs(st->remote_port) == adapter->wfd_info.peer_rtsp_ctrlport)
			op_wfd_mode |= MIRACAST_SOURCE;
		#endif
	}

	_rtw_spinunlock_bh(&st_ctl->tracker_q.lock);

	plist = get_next(&dlist);
	while (rtw_end_of_queue_search(&dlist, plist) == _FALSE) {
		st = LIST_CONTAINOR(plist, struct session_tracker, list);
		plist = get_next(plist);
		rtw_mfree((u8 *)st, sizeof(struct session_tracker));
	}

chk_sta:
	if (STA_OP_WFD_MODE(sta) != op_wfd_mode) {
		STA_SET_OP_WFD_MODE(sta, op_wfd_mode);
		rtw_sta_media_status_rpt_cmd(adapter, sta, 1);
	}

exit:
	return;
}

void session_tracker_chk_for_adapter(_adapter *adapter)
{
	struct sta_priv *stapriv = &adapter->stapriv;
	struct sta_info *sta;
	int i;
	_list *plist, *phead;
	u8 op_wfd_mode = MIRACAST_DISABLED;

	_rtw_spinlock_bh(&stapriv->sta_hash_lock);

	for (i = 0; i < NUM_STA; i++) {
		phead = &(stapriv->sta_hash[i]);
		plist = get_next(phead);

		while ((rtw_end_of_queue_search(phead, plist)) == _FALSE) {
			sta = LIST_CONTAINOR(plist, struct sta_info, hash_list);
			plist = get_next(plist);

			session_tracker_chk_for_sta(adapter, sta);

			op_wfd_mode |= STA_OP_WFD_MODE(sta);
		}
	}

	_rtw_spinunlock_bh(&stapriv->sta_hash_lock);

#ifdef CONFIG_WFD
	adapter->wfd_info.op_wfd_mode = MIRACAST_MODE_REVERSE(op_wfd_mode);
#endif
}

void session_tracker_cmd_hdl(_adapter *adapter, struct st_cmd_parm *parm)
{
	u8 cmd = parm->cmd;
	struct sta_info *sta = parm->sta;

	if (cmd == ST_CMD_CHK) {
		if (sta)
			session_tracker_chk_for_sta(adapter, sta);
		else
			session_tracker_chk_for_adapter(adapter);

		goto exit;

	} else if (cmd == ST_CMD_ADD || cmd == ST_CMD_DEL) {
		struct st_ctl_t *st_ctl;
		u32 local_naddr = parm->local_naddr;
		u16 local_port = parm->local_port;
		u32 remote_naddr = parm->remote_naddr;
		u16 remote_port = parm->remote_port;
		struct session_tracker *st = NULL;
		_list *plist, *phead;
		u8 free_st = 0;
		u8 alloc_st = 0;

		if (DBG_SESSION_TRACKER)
			RTW_INFO(FUNC_ADPT_FMT" cmd:%u, sta:%p, local:"IP_FMT":"PORT_FMT", remote:"IP_FMT":"PORT_FMT"\n"
				, FUNC_ADPT_ARG(adapter), cmd, sta
				, IP_ARG(&local_naddr), PORT_ARG(&local_port)
				, IP_ARG(&remote_naddr), PORT_ARG(&remote_port)
			);

		if (!(sta->state & WIFI_ASOC_STATE))
			goto exit;

		st_ctl = &sta->st_ctl;

		_rtw_spinlock_bh(&st_ctl->tracker_q.lock);

		phead = &st_ctl->tracker_q.queue;
		plist = get_next(phead);
		while (rtw_end_of_queue_search(phead, plist) == _FALSE) {
			st = LIST_CONTAINOR(plist, struct session_tracker, list);

			if (st->local_naddr == local_naddr
				&& st->local_port == local_port
				&& st->remote_naddr == remote_naddr
				&& st->remote_port == remote_port)
				break;

			plist = get_next(plist);
		}

		if (rtw_end_of_queue_search(phead, plist) == _TRUE)
			st = NULL;

		switch (cmd) {
		case ST_CMD_DEL:
			if (st) {
				rtw_list_delete(plist);
				free_st = 1;
			}
			goto unlock;
		case ST_CMD_ADD:
			if (!st)
				alloc_st = 1;
		}

unlock:
		_rtw_spinunlock_bh(&st_ctl->tracker_q.lock);

		if (free_st) {
			rtw_mfree((u8 *)st, sizeof(struct session_tracker));
			goto exit;
		}

		if (alloc_st) {
			st = (struct session_tracker *)rtw_zmalloc(sizeof(struct session_tracker));
			if (!st)
				goto exit;

			st->local_naddr = local_naddr;
			st->local_port = local_port;
			st->remote_naddr = remote_naddr;
			st->remote_port = remote_port;
			st->set_time = rtw_get_current_time();
			st->status = ST_STATUS_CHECK;

			_rtw_spinlock_bh(&st_ctl->tracker_q.lock);
			rtw_list_insert_tail(&st->list, phead);
			_rtw_spinunlock_bh(&st_ctl->tracker_q.lock);
		}
	}

exit:
	return;
}

#if defined(CONFIG_RTW_MESH) && defined(RTW_PER_CMD_SUPPORT_FW)
static s32 rtw_req_per_cmd_hdl(_adapter *adapter)
{
	struct dvobj_priv *dvobj = adapter_to_dvobj(adapter);
	struct macid_ctl_t *macid_ctl = dvobj_to_macidctl(dvobj);
	struct macid_bmp req_macid_bmp, *macid_bmp;
	u8 i, ret = _FAIL;

	macid_bmp = &macid_ctl->if_g[adapter->iface_id];
	_rtw_memcpy(&req_macid_bmp, macid_bmp, sizeof(struct macid_bmp));

	/* Clear none mesh's macid */
	for (i = 0; i < macid_ctl->num; i++) {
		u8 role;
		role = GET_H2CCMD_MSRRPT_PARM_ROLE(&macid_ctl->h2c_msr[i]);
		if (role != H2C_MSR_ROLE_MESH)
			rtw_macid_map_clr(&req_macid_bmp, i);
	}

	/* group_macid: always be 0 in NIC, so only pass macid_bitmap.m0
	 * rpt_type: 0 includes all info in 1, use 0 for now 
	 * macid_bitmap: pass m0 only for NIC
	 */
	ret = rtw_hal_set_req_per_rpt_cmd(adapter, 0, 0, req_macid_bmp.m0);

	return ret;
}

u8 rtw_req_per_cmd(_adapter *adapter)
{
	struct cmd_obj *cmdobj;
	struct drvextra_cmd_parm *parm;
	struct cmd_priv *pcmdpriv = &adapter_to_dvobj(adapter)->cmdpriv;
	struct submit_ctx sctx;
	u8 res = _SUCCESS;

	parm = (struct drvextra_cmd_parm *)rtw_zmalloc(sizeof(struct drvextra_cmd_parm));
	if (parm == NULL) {
		res = _FAIL;
		goto exit;
	}

	parm->ec_id = REQ_PER_CMD_WK_CID;
	parm->type = 0;
	parm->size = 0;
	parm->pbuf = NULL;

	cmdobj = (struct cmd_obj *)rtw_zmalloc(sizeof(*cmdobj));
	if (cmdobj == NULL) {
		res = _FAIL;
		rtw_mfree((u8 *)parm, sizeof(*parm));
		goto exit;
	}
	cmdobj->padapter = adapter;

	init_h2fwcmd_w_parm_no_rsp(cmdobj, parm, CMD_SET_DRV_EXTRA);

	res = rtw_enqueue_cmd(pcmdpriv, cmdobj);

exit:
	return res;
}
#endif


void rtw_ac_parm_cmd_hdl(_adapter *padapter, u8 *_ac_parm_buf, int ac_type)
{

	u32 ac_parm_buf;

	_rtw_memcpy(&ac_parm_buf, _ac_parm_buf, sizeof(ac_parm_buf));
	switch (ac_type) {
	case XMIT_VO_QUEUE:
		RTW_INFO(FUNC_NDEV_FMT" AC_VO = 0x%08x\n", FUNC_ADPT_ARG(padapter), (unsigned int) ac_parm_buf);
		rtw_ap_set_edca(padapter, 3, ac_parm_buf);
		break;

	case XMIT_VI_QUEUE:
		RTW_INFO(FUNC_NDEV_FMT" AC_VI = 0x%08x\n", FUNC_ADPT_ARG(padapter), (unsigned int) ac_parm_buf);
		rtw_ap_set_edca(padapter, 2, ac_parm_buf);
		break;

	case XMIT_BE_QUEUE:
		RTW_INFO(FUNC_NDEV_FMT" AC_BE = 0x%08x\n", FUNC_ADPT_ARG(padapter), (unsigned int) ac_parm_buf);
		rtw_ap_set_edca(padapter, 0, ac_parm_buf);
		break;

	case XMIT_BK_QUEUE:
		RTW_INFO(FUNC_NDEV_FMT" AC_BK = 0x%08x\n", FUNC_ADPT_ARG(padapter), (unsigned int) ac_parm_buf);
		rtw_ap_set_edca(padapter, 1, ac_parm_buf);
		break;

	default:
		break;
	}

}

u8 rtw_drvextra_cmd_hdl(_adapter *padapter, unsigned char *pbuf)
{
	int ret = H2C_SUCCESS;
	struct drvextra_cmd_parm *pdrvextra_cmd;

	if (!pbuf)
		return H2C_PARAMETERS_ERROR;

	pdrvextra_cmd = (struct drvextra_cmd_parm *)pbuf;

	switch (pdrvextra_cmd->ec_id) {
	case STA_MSTATUS_RPT_WK_CID:
		rtw_sta_media_status_rpt_cmd_hdl(padapter, (struct sta_media_status_rpt_cmd_parm *)pdrvextra_cmd->pbuf);
		break;
	#if 0 /*#ifdef CONFIG_CORE_DM_CHK_TIMER*/
	case DYNAMIC_CHK_WK_CID:/*only  primary padapter go to this cmd, but execute dynamic_chk_wk_hdl() for two interfaces */
		rtw_dynamic_chk_wk_hdl(padapter);
		break;
	#endif
	#ifdef CONFIG_POWER_SAVING
	case POWER_SAVING_CTRL_WK_CID:
		power_saving_wk_hdl(padapter);
		break;
	#endif
#ifdef CONFIG_LPS
	case LPS_CTRL_WK_CID:
		lps_ctrl_wk_hdl(padapter, (u8)pdrvextra_cmd->type, pdrvextra_cmd->pbuf);
		break;
	case DM_IN_LPS_WK_CID:
		rtw_dm_in_lps_hdl(padapter);
		break;
	case LPS_CHANGE_DTIM_CID:
		rtw_lps_change_dtim_hdl(padapter, (u8)pdrvextra_cmd->type);
		break;
#endif
#ifdef CONFIG_ANTENNA_DIVERSITY
	case ANT_SELECT_WK_CID:
		antenna_select_wk_hdl(padapter, pdrvextra_cmd->type);
		break;
#endif
#ifdef CONFIG_P2P_PS
	case P2P_PS_WK_CID:
		p2p_ps_wk_hdl(padapter, pdrvextra_cmd->type);
		break;
#endif
#ifdef CONFIG_AP_MODE
	case CHECK_HIQ_WK_CID:
		rtw_chk_hi_queue_hdl(padapter);
		break;
#endif
	/* add for CONFIG_IEEE80211W, none 11w can use it */
	case RESET_SECURITYPRIV:
		reset_securitypriv_hdl(padapter);
		break;
	case FREE_ASSOC_RESOURCES:
		free_assoc_resources_hdl(padapter, (u8)pdrvextra_cmd->type);
		break;
	case C2H_WK_CID:
		switch (pdrvextra_cmd->type) {
		case C2H_TYPE_PKT:
			rtw_hal_c2h_pkt_hdl(padapter, pdrvextra_cmd->pbuf, pdrvextra_cmd->size);
			break;
		default:
			RTW_ERR("unknown C2H type:%d\n", pdrvextra_cmd->type);
			rtw_warn_on(1);
			break;
		}
		break;

#ifdef CONFIG_DFS_MASTER
	case DFS_RADAR_DETECT_WK_CID:
		rtw_dfs_rd_hdl(padapter);
		break;
	case DFS_RADAR_DETECT_EN_DEC_WK_CID:
		rtw_dfs_rd_en_decision(padapter, MLME_ACTION_NONE, 0);
		break;
#endif
	case SESSION_TRACKER_WK_CID:
		session_tracker_cmd_hdl(padapter, (struct st_cmd_parm *)pdrvextra_cmd->pbuf);
		break;
	case TEST_H2C_CID:
		rtw_hal_fill_h2c_cmd(padapter, pdrvextra_cmd->pbuf[0], pdrvextra_cmd->size - 1, &pdrvextra_cmd->pbuf[1]);
		break;
	case MP_CMD_WK_CID:
#ifdef CONFIG_MP_INCLUDED
		ret = rtw_mp_cmd_hdl(padapter, pdrvextra_cmd->type);
#endif
		break;
#ifdef CONFIG_RTW_CUSTOMER_STR
	case CUSTOMER_STR_WK_CID:
		ret = rtw_customer_str_cmd_hdl(padapter, pdrvextra_cmd->type, pdrvextra_cmd->pbuf);
		break;
#endif

#ifdef CONFIG_IOCTL_CFG80211
	case MGNT_TX_WK_CID:
		ret = rtw_mgnt_tx_handler(padapter, pdrvextra_cmd->pbuf);
		break;
#endif /* CONFIG_IOCTL_CFG80211 */
#if defined(CONFIG_RTW_MESH) && defined(RTW_PER_CMD_SUPPORT_FW)
	case REQ_PER_CMD_WK_CID:
		ret = rtw_req_per_cmd_hdl(padapter);
		break;
#endif
#ifdef CONFIG_SUPPORT_STATIC_SMPS
	case SSMPS_WK_CID :
		rtw_ssmps_wk_hdl(padapter, (struct ssmps_cmd_parm *)pdrvextra_cmd->pbuf);
		break;
#endif
#ifdef CONFIG_CTRL_TXSS_BY_TP
	case TXSS_WK_CID :
		rtw_ctrl_txss_wk_hdl(padapter, (struct txss_cmd_parm *)pdrvextra_cmd->pbuf);
		break;
#endif
	case AC_PARM_CMD_WK_CID:
		rtw_ac_parm_cmd_hdl(padapter, pdrvextra_cmd->pbuf, pdrvextra_cmd->type);
		break;
#ifdef CONFIG_AP_MODE
	case STOP_AP_WK_CID:
		stop_ap_hdl(padapter);
		break;
#endif
#ifdef CONFIG_RTW_TOKEN_BASED_XMIT
	case TBTX_CONTROL_TX_WK_CID:
		tx_control_hdl(padapter);
		break;
#endif
#ifdef ROKU_PRIVATE 
	case FIND_REMOTE_WK_CID:
		ret = issue_action_find_remote(padapter);
		break;
#ifdef CONFIG_P2P
	case HIDE_SSID_WK_CID:
		issue_beacon(padapter, 0);
		break;
#endif
#endif
	default:
		break;
	}

	if (pdrvextra_cmd->pbuf && pdrvextra_cmd->size > 0)
		rtw_mfree(pdrvextra_cmd->pbuf, pdrvextra_cmd->size);

	return ret;
}


void rtw_disassoc_cmd_callback(_adapter *padapter,  struct cmd_obj *pcmd)
{
	struct	mlme_priv *pmlmepriv = &padapter->mlmepriv;


	if (pcmd->res != H2C_SUCCESS) {
		_rtw_spinlock_bh(&pmlmepriv->lock);
		set_fwstate(pmlmepriv, WIFI_ASOC_STATE);
		_rtw_spinunlock_bh(&pmlmepriv->lock);
		goto exit;
	}
#ifdef CONFIG_BR_EXT
	else /* clear bridge database */
		nat25_db_cleanup(padapter);
#endif /* CONFIG_BR_EXT */

	/* free cmd */
	rtw_free_cmd_obj(pcmd);

exit:
	return;
}
void rtw_joinbss_cmd_callback(_adapter *padapter,  struct cmd_obj *pcmd)
{
	struct	mlme_priv *pmlmepriv = &padapter->mlmepriv;


	if (pcmd->res == H2C_DROPPED) {
		/* TODO: cancel timer and do timeout handler directly... */
		/* need to make timeout handlerOS independent */
		set_assoc_timer(pmlmepriv, 1); /*_set_timer(&pmlmepriv->assoc_timer, 1);*/
	} else if (pcmd->res != H2C_SUCCESS)
		set_assoc_timer(pmlmepriv, 1); /*_set_timer(&pmlmepriv->assoc_timer, 1);*/

	rtw_free_cmd_obj(pcmd);
}

void rtw_create_ibss_post_hdl(_adapter *padapter, int status)
{
	struct wlan_network *pwlan = NULL;
	struct	mlme_priv *pmlmepriv = &padapter->mlmepriv;
	WLAN_BSSID_EX *pdev_network = &padapter->registrypriv.dev_network;
	struct wlan_network *mlme_cur_network = &(pmlmepriv->cur_network);

	if (status != H2C_SUCCESS)
		set_assoc_timer(pmlmepriv, 1); /*_set_timer(&pmlmepriv->assoc_timer, 1);*/

	/*_cancel_timer_ex(&pmlmepriv->assoc_timer);*/
	cancel_assoc_timer(pmlmepriv);

	_rtw_spinlock_bh(&pmlmepriv->lock);

	{
		pwlan = _rtw_alloc_network(pmlmepriv);
		_rtw_spinlock_bh(&(pmlmepriv->scanned_queue.lock));
		if (pwlan == NULL) {
			pwlan = rtw_get_oldest_wlan_network(&pmlmepriv->scanned_queue);
			if (pwlan == NULL) {
				_rtw_spinunlock_bh(&(pmlmepriv->scanned_queue.lock));
				goto createbss_cmd_fail;
			}
			pwlan->last_scanned = rtw_get_current_time();
		} else
			rtw_list_insert_tail(&(pwlan->list), &pmlmepriv->scanned_queue.queue);

		pdev_network->Length = get_WLAN_BSSID_EX_sz(pdev_network);
		_rtw_memcpy(&(pwlan->network), pdev_network, pdev_network->Length);
		/* pwlan->fixed = _TRUE; */

		/* copy pdev_network information to pmlmepriv->cur_network */
		_rtw_memcpy(&mlme_cur_network->network, pdev_network, (get_WLAN_BSSID_EX_sz(pdev_network)));

#if 0
		/* reset DSConfig */
		mlme_cur_network->network.Configuration.DSConfig = (u32)rtw_ch2freq(pdev_network->Configuration.DSConfig);
#endif

		_clr_fwstate_(pmlmepriv, WIFI_UNDER_LINKING);
		_rtw_spinunlock_bh(&(pmlmepriv->scanned_queue.lock));
		/* we will set WIFI_ASOC_STATE when there is one more sat to join us (rtw_stassoc_event_callback) */
	}

createbss_cmd_fail:
	_rtw_spinunlock_bh(&pmlmepriv->lock);
	return;
}



void rtw_setstaKey_cmdrsp_callback(_adapter *padapter ,  struct cmd_obj *pcmd)
{

	struct sta_priv *pstapriv = &padapter->stapriv;
	struct set_stakey_rsp *psetstakey_rsp = (struct set_stakey_rsp *)(pcmd->rsp);
	struct sta_info	*psta = rtw_get_stainfo(pstapriv, psetstakey_rsp->addr);


	if (psta == NULL) {
		goto exit;
	}

	/* psta->phl_sta->aid = psta->phl_sta->macid = psetstakey_rsp->keyid; */ /* CAM_ID(CAM_ENTRY) */

exit:

	rtw_free_cmd_obj(pcmd);


}

void rtw_getrttbl_cmd_cmdrsp_callback(_adapter *padapter,  struct cmd_obj *pcmd)
{

	rtw_free_cmd_obj(pcmd);
#ifdef CONFIG_MP_INCLUDED
	if (padapter->registrypriv.mp_mode == 1)
		padapter->mppriv.workparam.bcompleted = _TRUE;
#endif


}

u8 set_txq_params_cmd(_adapter *adapter, u32 ac_parm, u8 ac_type)
{
	struct cmd_obj *cmdobj;
	struct drvextra_cmd_parm *pdrvextra_cmd_parm;
	struct cmd_priv *pcmdpriv = &adapter_to_dvobj(adapter)->cmdpriv;
	u8 *ac_parm_buf = NULL;
	u8 sz;
	u8 res = _SUCCESS;


	cmdobj = (struct cmd_obj *)rtw_zmalloc(sizeof(struct cmd_obj));
	if (cmdobj == NULL) {
		res = _FAIL;
		goto exit;
	}
	cmdobj->padapter = adapter;

	pdrvextra_cmd_parm = (struct drvextra_cmd_parm *)rtw_zmalloc(sizeof(struct drvextra_cmd_parm));
	if (pdrvextra_cmd_parm == NULL) {
		rtw_mfree((u8 *)cmdobj, sizeof(struct cmd_obj));
		res = _FAIL;
		goto exit;
	}

	sz = sizeof(ac_parm);
	ac_parm_buf = rtw_zmalloc(sz);
	if (ac_parm_buf == NULL) {
		rtw_mfree((u8 *)cmdobj, sizeof(struct cmd_obj));
		rtw_mfree((u8 *)pdrvextra_cmd_parm, sizeof(struct drvextra_cmd_parm));
		res = _FAIL;
		goto exit;
	}

	pdrvextra_cmd_parm->ec_id = AC_PARM_CMD_WK_CID;
	pdrvextra_cmd_parm->type = ac_type;
	pdrvextra_cmd_parm->size = sz;
	pdrvextra_cmd_parm->pbuf = ac_parm_buf;

	_rtw_memcpy(ac_parm_buf, &ac_parm, sz);

	init_h2fwcmd_w_parm_no_rsp(cmdobj, pdrvextra_cmd_parm, CMD_SET_DRV_EXTRA);
	res = rtw_enqueue_cmd(pcmdpriv, cmdobj);

exit:
	return res;
}

char UNKNOWN_CID[16] = "UNKNOWN_EXTRA";
char *rtw_extra_name(struct drvextra_cmd_parm *pdrvextra_cmd)
{
	switch(pdrvextra_cmd->ec_id) {
	case NONE_WK_CID:
		return "NONE_WK_CID";
		break;
	case STA_MSTATUS_RPT_WK_CID:
		return "STA_MSTATUS_RPT_WK_CID";
		break;
	#if 0 /*#ifdef CONFIG_CORE_DM_CHK_TIMER*/
	case DYNAMIC_CHK_WK_CID:
		return "DYNAMIC_CHK_WK_CID";
		break;
	#endif
	case DM_CTRL_WK_CID:
		return "DM_CTRL_WK_CID";
		break;
	case PBC_POLLING_WK_CID:
		return "PBC_POLLING_WK_CID";
		break;
	#ifdef CONFIG_POWER_SAVING
	case POWER_SAVING_CTRL_WK_CID:
		return "POWER_SAVING_CTRL_WK_CID";
	#endif
		break;
	case LPS_CTRL_WK_CID:
		return "LPS_CTRL_WK_CID";
		break;
	case ANT_SELECT_WK_CID:
		return "ANT_SELECT_WK_CID";
		break;
	case P2P_PS_WK_CID:
		return "P2P_PS_WK_CID";
		break;
	case CHECK_HIQ_WK_CID:
		return "CHECK_HIQ_WK_CID";
		break;
	case C2H_WK_CID:
		return "C2H_WK_CID";
		break;
	case RESET_SECURITYPRIV:
		return "RESET_SECURITYPRIV";
		break;
	case FREE_ASSOC_RESOURCES:
		return "FREE_ASSOC_RESOURCES";
		break;
	case DM_IN_LPS_WK_CID:
		return "DM_IN_LPS_WK_CID";
		break;
	case LPS_CHANGE_DTIM_CID:
		return "LPS_CHANGE_DTIM_CID";
		break;
	case DFS_RADAR_DETECT_WK_CID:
		return "DFS_RADAR_DETECT_WK_CID";
		break;
	case DFS_RADAR_DETECT_EN_DEC_WK_CID:
		return "DFS_RADAR_DETECT_EN_DEC_WK_CID";
		break;
	case SESSION_TRACKER_WK_CID:
		return "SESSION_TRACKER_WK_CID";
		break;
	case TEST_H2C_CID:
		return "TEST_H2C_CID";
		break;
	case MP_CMD_WK_CID:
		return "MP_CMD_WK_CID";
		break;
	case CUSTOMER_STR_WK_CID:
		return "CUSTOMER_STR_WK_CID";
		break;
	case MGNT_TX_WK_CID:
		return "MGNT_TX_WK_CID";
		break;
	case REQ_PER_CMD_WK_CID:
		return "REQ_PER_CMD_WK_CID";
		break;
	case SSMPS_WK_CID:
		return "SSMPS_WK_CID";
		break;
#ifdef CONFIG_CTRL_TXSS_BY_TP
	case TXSS_WK_CID:
		return "TXSS_WK_CID";
		break;
#endif
	case AC_PARM_CMD_WK_CID:
		return "AC_PARM_CMD_WK_CID";
		break;
#ifdef CONFIG_AP_MODE
	case STOP_AP_WK_CID:
		return "STOP_AP_WK_CID";
		break;
#endif
#ifdef CONFIG_RTW_TOKEN_BASED_XMIT
	case TBTX_CONTROL_TX_WK_CID:
		return "TBTX_CONTROL_TX_WK_CID";
		break;
#endif
#ifdef ROKU_PRIVATE
	case FIND_REMOTE_WK_CID:
		return "FIND_REMOTE_WK_CID";
		break;
#ifdef CONFIG_P2P
	case HIDE_SSID_WK_CID:
		return "HIDE_SSID_WK_CID";
		break;
#endif
#endif
	case MAX_WK_CID:
		return "MAX_WK_CID";
		break;
	default:
		return UNKNOWN_CID;
		break;
	}
	return UNKNOWN_CID;
}

char UNKNOWN_CMD[16] = "UNKNOWN_CMD";
char *rtw_cmd_name(struct cmd_obj *pcmd)
{
	struct rtw_evt_header *pev;

	if (pcmd->cmdcode >= (sizeof(wlancmds) / sizeof(struct rtw_cmd)))
		return UNKNOWN_CMD;

	if (pcmd->cmdcode == CMD_SET_MLME_EVT)
		return rtw_evt_name((struct rtw_evt_header*)pcmd->parmbuf);

	if (pcmd->cmdcode == CMD_SET_DRV_EXTRA)
		return rtw_extra_name((struct drvextra_cmd_parm*)pcmd->parmbuf);

	return wlancmds[pcmd->cmdcode].name;
}

#ifdef ROKU_PRIVATE
u8 rtw_find_remote_wk_cmd(_adapter *adapter)
{
	struct cmd_obj		*cmdobj;
	struct drvextra_cmd_parm  *pdrvextra_cmd_parm;
	struct cmd_priv	*pcmdpriv = &adapter_to_dvobj(adapter)->cmdpriv;
	u8	res = _SUCCESS;

	cmdobj = (struct cmd_obj *)rtw_zmalloc(sizeof(struct cmd_obj));
	if (cmdobj == NULL) {
		res = _FAIL;
		goto exit;
	}

	cmdobj->padapter = adapter;

	pdrvextra_cmd_parm = (struct drvextra_cmd_parm *)rtw_zmalloc(sizeof(struct drvextra_cmd_parm));
	if (pdrvextra_cmd_parm == NULL) {
		rtw_mfree((unsigned char *)cmdobj, sizeof(struct cmd_obj));
		res = _FAIL;
		goto exit;
	}

	pdrvextra_cmd_parm->ec_id = FIND_REMOTE_WK_CID;
	pdrvextra_cmd_parm->type = 0;
	pdrvextra_cmd_parm->size = 0;
	pdrvextra_cmd_parm->pbuf = NULL;
	init_h2fwcmd_w_parm_no_rsp(cmdobj, pdrvextra_cmd_parm, CMD_SET_DRV_EXTRA);

	res = rtw_enqueue_cmd(pcmdpriv, cmdobj);

exit:
	return res;
}

#ifdef CONFIG_P2P
u8 rtw_hide_ssid_wk_cmd(_adapter *adapter)
{
	struct cmd_obj		*cmdobj;
	struct drvextra_cmd_parm  *pdrvextra_cmd_parm;
	struct cmd_priv	*pcmdpriv = &adapter_to_dvobj(adapter)->cmdpriv;
	u8	res = _SUCCESS;

	cmdobj = (struct cmd_obj *)rtw_zmalloc(sizeof(struct cmd_obj));
	if (cmdobj == NULL) {
		res = _FAIL;
		goto exit;
	}

	cmdobj->padapter = adapter;

	pdrvextra_cmd_parm = (struct drvextra_cmd_parm *)rtw_zmalloc(sizeof(struct drvextra_cmd_parm));
	if (pdrvextra_cmd_parm == NULL) {
		rtw_mfree((unsigned char *)cmdobj, sizeof(struct cmd_obj));
		res = _FAIL;
		goto exit;
	}

	pdrvextra_cmd_parm->ec_id = HIDE_SSID_WK_CID;
	pdrvextra_cmd_parm->type = 0;
	pdrvextra_cmd_parm->size = 0;
	pdrvextra_cmd_parm->pbuf = NULL;
	init_h2fwcmd_w_parm_no_rsp(cmdobj, pdrvextra_cmd_parm, CMD_SET_DRV_EXTRA);

	res = rtw_enqueue_cmd(pcmdpriv, cmdobj);

exit:
	return res;

}
#endif
#endif
