/*
 * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * 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.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * File: wcmd.c
 *
 * Purpose: Handles the management command interface functions
 *
 * Author: Lyndon Chen
 *
 * Date: May 8, 2003
 *
 * Functions:
 *      s_vProbeChannel - Active scan channel
 *      s_MgrMakeProbeRequest - Make ProbeRequest packet
 *      CommandTimer - Timer function to handle command
 *      s_bCommandComplete - Command Complete function
 *      bScheduleCommand - Push Command and wait Command Scheduler to do
 *      vCommandTimer- Command call back functions
 *      vCommandTimerWait- Call back timer
 *      bClearBSSID_SCAN- Clear BSSID_SCAN cmd in CMD Queue
 *
 * Revision History:
 *
 */

#include "ttype.h"
#include "tmacro.h"
#include "device.h"
#include "mac.h"
#include "card.h"
#include "80211hdr.h"
#include "wcmd.h"
#include "wmgr.h"
#include "power.h"
#include "wctl.h"
#include "baseband.h"
#include "rxtx.h"
#include "rf.h"
#include "iowpa.h"
#include "channel.h"

/*---------------------  Static Definitions -------------------------*/

/*---------------------  Static Classes  ----------------------------*/

/*---------------------  Static Functions  --------------------------*/

static
void
s_vProbeChannel(
	struct vnt_private *pDevice
);

static
PSTxMgmtPacket
s_MgrMakeProbeRequest(
	struct vnt_private *pDevice,
	PSMgmtObject pMgmt,
	unsigned char *pScanBSSID,
	PWLAN_IE_SSID pSSID,
	PWLAN_IE_SUPP_RATES pCurrRates,
	PWLAN_IE_SUPP_RATES pCurrExtSuppRates
);

static
bool
s_bCommandComplete(
	struct vnt_private *pDevice
);

/*---------------------  Export Variables  --------------------------*/

/*---------------------  Export Functions  --------------------------*/

/*
 * Description:
 *      Stop AdHoc beacon during scan process
 *
 * Parameters:
 *  In:
 *      pDevice     - Pointer to the adapter
 *  Out:
 *      none
 *
 * Return Value: none
 *
 */
static
void
vAdHocBeaconStop(struct vnt_private *pDevice)
{
	PSMgmtObject    pMgmt = &(pDevice->sMgmtObj);
	bool bStop;

	/*
	 * temporarily stop Beacon packet for AdHoc Server
	 * if all of the following conditions are met:
	 *  (1) STA is in AdHoc mode
	 *  (2) VT3253 is programmed as automatic Beacon Transmitting
	 *  (3) One of the following conditions is met
	 *      (3.1) AdHoc channel is in B/G band and the
	 *      current scan channel is in A band
	 *      or
	 *      (3.2) AdHoc channel is in A mode
	 */
	bStop = false;
	if ((pMgmt->eCurrMode == WMAC_MODE_IBSS_STA) &&
	    (pMgmt->eCurrState >= WMAC_STATE_STARTED)) {
		if ((pMgmt->uIBSSChannel <=  CB_MAX_CHANNEL_24G) &&
		    (pMgmt->uScanChannel > CB_MAX_CHANNEL_24G)) {
			bStop = true;
		}
		if (pMgmt->uIBSSChannel >  CB_MAX_CHANNEL_24G)
			bStop = true;

	}

	if (bStop)
		MACvRegBitsOff(pDevice->PortOffset, MAC_REG_TCR, TCR_AUTOBCNTX);
} /* vAdHocBeaconStop */

/*
 * Description:
 *      Restart AdHoc beacon after scan process complete
 *
 * Parameters:
 *  In:
 *      pDevice     - Pointer to the adapter
 *  Out:
 *      none
 *
 * Return Value: none
 *
 */
static
void
vAdHocBeaconRestart(struct vnt_private *pDevice)
{
	PSMgmtObject    pMgmt = &(pDevice->sMgmtObj);

	/*
	 * Restart Beacon packet for AdHoc Server
	 * if all of the following coditions are met:
	 *  (1) STA is in AdHoc mode
	 *  (2) VT3253 is programmed as automatic Beacon Transmitting
	 */
	if ((pMgmt->eCurrMode == WMAC_MODE_IBSS_STA) &&
	    (pMgmt->eCurrState >= WMAC_STATE_STARTED)) {
		MACvRegBitsOn(pDevice->PortOffset, MAC_REG_TCR, TCR_AUTOBCNTX);
	}
}

/*+
 *
 * Routine Description:
 *   Prepare and send probe request management frames.
 *
 *
 * Return Value:
 *    none.
 *
 -*/

static
void
s_vProbeChannel(
	struct vnt_private *pDevice
)
{
	//1M,   2M,   5M,   11M,  18M,  24M,  36M,  54M
	unsigned char abyCurrSuppRatesG[] = {WLAN_EID_SUPP_RATES, 8, 0x02, 0x04, 0x0B, 0x16, 0x24, 0x30, 0x48, 0x6C};
	unsigned char abyCurrExtSuppRatesG[] = {WLAN_EID_EXTSUPP_RATES, 4, 0x0C, 0x12, 0x18, 0x60};
	//6M,   9M,   12M,  48M
	unsigned char abyCurrSuppRatesA[] = {WLAN_EID_SUPP_RATES, 8, 0x0C, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6C};
	unsigned char abyCurrSuppRatesB[] = {WLAN_EID_SUPP_RATES, 4, 0x02, 0x04, 0x0B, 0x16};
	unsigned char *pbyRate;
	PSTxMgmtPacket  pTxPacket;
	PSMgmtObject    pMgmt = pDevice->pMgmt;
	unsigned int ii;

	if (pDevice->eCurrentPHYType == PHY_TYPE_11A)
		pbyRate = &abyCurrSuppRatesA[0];
	else if (pDevice->eCurrentPHYType == PHY_TYPE_11B)
		pbyRate = &abyCurrSuppRatesB[0];
	else
		pbyRate = &abyCurrSuppRatesG[0];

	// build an assocreq frame and send it
	pTxPacket = s_MgrMakeProbeRequest
		(
			pDevice,
			pMgmt,
			pMgmt->abyScanBSSID,
			(PWLAN_IE_SSID)pMgmt->abyScanSSID,
			(PWLAN_IE_SUPP_RATES)pbyRate,
			(PWLAN_IE_SUPP_RATES)abyCurrExtSuppRatesG
			);

	if (pTxPacket != NULL) {
		for (ii = 0; ii < 2; ii++) {
			if (csMgmt_xmit(pDevice, pTxPacket) != CMD_STATUS_PENDING)
				pr_debug("Probe request sending fail..\n");
			else
				pr_debug("Probe request is sending..\n");
		}
	}
}

/*+
 *
 * Routine Description:
 *  Constructs an probe request frame
 *
 *
 * Return Value:
 *    A ptr to Tx frame or NULL on allocation failure
 *
 -*/

static PSTxMgmtPacket
s_MgrMakeProbeRequest(
	struct vnt_private *pDevice,
	PSMgmtObject pMgmt,
	unsigned char *pScanBSSID,
	PWLAN_IE_SSID pSSID,
	PWLAN_IE_SUPP_RATES pCurrRates,
	PWLAN_IE_SUPP_RATES pCurrExtSuppRates

)
{
	PSTxMgmtPacket      pTxPacket = NULL;
	WLAN_FR_PROBEREQ    sFrame;

	pTxPacket = (PSTxMgmtPacket)pMgmt->pbyMgmtPacketPool;
	memset(pTxPacket, 0, sizeof(STxMgmtPacket) + WLAN_PROBEREQ_FR_MAXLEN);
	pTxPacket->p80211Header = (PUWLAN_80211HDR)((unsigned char *)pTxPacket + sizeof(STxMgmtPacket));
	sFrame.pBuf = (unsigned char *)pTxPacket->p80211Header;
	sFrame.len = WLAN_PROBEREQ_FR_MAXLEN;
	vMgrEncodeProbeRequest(&sFrame);
	sFrame.pHdr->sA3.wFrameCtl = cpu_to_le16(
		(
			WLAN_SET_FC_FTYPE(WLAN_TYPE_MGR) |
			WLAN_SET_FC_FSTYPE(WLAN_FSTYPE_PROBEREQ)
));
	memcpy(sFrame.pHdr->sA3.abyAddr1, pScanBSSID, WLAN_ADDR_LEN);
	memcpy(sFrame.pHdr->sA3.abyAddr2, pMgmt->abyMACAddr, WLAN_ADDR_LEN);
	memcpy(sFrame.pHdr->sA3.abyAddr3, pScanBSSID, WLAN_BSSID_LEN);
	// Copy the SSID, pSSID->len=0 indicate broadcast SSID
	sFrame.pSSID = (PWLAN_IE_SSID)(sFrame.pBuf + sFrame.len);
	sFrame.len += pSSID->len + WLAN_IEHDR_LEN;
	memcpy(sFrame.pSSID, pSSID, pSSID->len + WLAN_IEHDR_LEN);
	sFrame.pSuppRates = (PWLAN_IE_SUPP_RATES)(sFrame.pBuf + sFrame.len);
	sFrame.len += pCurrRates->len + WLAN_IEHDR_LEN;
	memcpy(sFrame.pSuppRates, pCurrRates, pCurrRates->len + WLAN_IEHDR_LEN);
	// Copy the extension rate set
	if (pDevice->eCurrentPHYType == PHY_TYPE_11G) {
		sFrame.pExtSuppRates = (PWLAN_IE_SUPP_RATES)(sFrame.pBuf + sFrame.len);
		sFrame.len += pCurrExtSuppRates->len + WLAN_IEHDR_LEN;
		memcpy(sFrame.pExtSuppRates, pCurrExtSuppRates, pCurrExtSuppRates->len + WLAN_IEHDR_LEN);
	}
	pTxPacket->cbMPDULen = sFrame.len;
	pTxPacket->cbPayloadLen = sFrame.len - WLAN_HDR_ADDR3_LEN;

	return pTxPacket;
}

void
vCommandTimerWait(
	void *hDeviceContext,
	unsigned int MSecond
)
{
	struct vnt_private *pDevice = hDeviceContext;

	init_timer(&pDevice->sTimerCommand);
	pDevice->sTimerCommand.data = (unsigned long) pDevice;
	pDevice->sTimerCommand.function = (TimerFunction)vCommandTimer;
	// RUN_AT :1 msec ~= (HZ/1024)
	pDevice->sTimerCommand.expires = (unsigned int)RUN_AT((MSecond * HZ) >> 10);
	add_timer(&pDevice->sTimerCommand);
}

void
vCommandTimer(
	void *hDeviceContext
)
{
	struct vnt_private *pDevice = hDeviceContext;
	PSMgmtObject    pMgmt = pDevice->pMgmt;
	PWLAN_IE_SSID   pItemSSID;
	PWLAN_IE_SSID   pItemSSIDCurr;
	CMD_STATUS      Status;
	unsigned int ii;
	unsigned char byMask[8] = {1, 2, 4, 8, 0x10, 0x20, 0x40, 0x80};
	struct sk_buff  *skb;

	if (pDevice->dwDiagRefCount != 0)
		return;
	if (!pDevice->bCmdRunning)
		return;

	spin_lock_irq(&pDevice->lock);

	switch (pDevice->eCommandState) {
	case WLAN_CMD_SCAN_START:

		pDevice->byReAssocCount = 0;
		if (pDevice->bRadioOff) {
			s_bCommandComplete(pDevice);
			spin_unlock_irq(&pDevice->lock);
			return;
		}

		if (pMgmt->eCurrMode == WMAC_MODE_ESS_AP) {
			s_bCommandComplete(pDevice);
			CARDbSetBSSID(pMgmt->pAdapter, pMgmt->abyCurrBSSID, NL80211_IFTYPE_AP);
			spin_unlock_irq(&pDevice->lock);
			return;
		}

		pr_debug("eCommandState= WLAN_CMD_SCAN_START\n");
		pItemSSID = (PWLAN_IE_SSID)pMgmt->abyScanSSID;
		// wait all Data TD complete
		if (pDevice->iTDUsed[TYPE_AC0DMA] != 0) {
			spin_unlock_irq(&pDevice->lock);
			vCommandTimerWait((void *)pDevice, 10);
			return;
		}

		if (pMgmt->uScanChannel == 0) {
			pMgmt->uScanChannel = pDevice->byMinChannel;
			// Set Baseband to be more sensitive.

		}
		if (pMgmt->uScanChannel > pDevice->byMaxChannel) {
			pMgmt->eScanState = WMAC_NO_SCANNING;

			// Set Baseband's sensitivity back.
			// Set channel back
			set_channel(pMgmt->pAdapter, pMgmt->uCurrChannel);
			pr_debug("Scanning, set back to channel: [%d]\n",
				 pMgmt->uCurrChannel);
			if (pMgmt->eCurrMode == WMAC_MODE_IBSS_STA)
				CARDbSetBSSID(pMgmt->pAdapter, pMgmt->abyCurrBSSID, NL80211_IFTYPE_ADHOC);
			else
				CARDbSetBSSID(pMgmt->pAdapter, pMgmt->abyCurrBSSID, NL80211_IFTYPE_STATION);

			vAdHocBeaconRestart(pDevice);
			s_bCommandComplete(pDevice);

		} else {
//2008-8-4 <add> by chester
			if (!is_channel_valid(pMgmt->uScanChannel)) {
				pr_debug("Invalid channel pMgmt->uScanChannel = %d\n",
					 pMgmt->uScanChannel);
				s_bCommandComplete(pDevice);
				spin_unlock_irq(&pDevice->lock);
				return;
			}
			if (pMgmt->uScanChannel == pDevice->byMinChannel) {
				pMgmt->abyScanBSSID[0] = 0xFF;
				pMgmt->abyScanBSSID[1] = 0xFF;
				pMgmt->abyScanBSSID[2] = 0xFF;
				pMgmt->abyScanBSSID[3] = 0xFF;
				pMgmt->abyScanBSSID[4] = 0xFF;
				pMgmt->abyScanBSSID[5] = 0xFF;
				pItemSSID->byElementID = WLAN_EID_SSID;
				pMgmt->eScanState = WMAC_IS_SCANNING;

			}

			vAdHocBeaconStop(pDevice);

			if (set_channel(pMgmt->pAdapter, pMgmt->uScanChannel))
				pr_debug("SCAN Channel: %d\n",
					 pMgmt->uScanChannel);
			else
				pr_debug("SET SCAN Channel Fail: %d\n",
					 pMgmt->uScanChannel);

			CARDbSetBSSID(pMgmt->pAdapter, pMgmt->abyCurrBSSID, NL80211_IFTYPE_UNSPECIFIED);
			pMgmt->uScanChannel++;
//2008-8-4 <modify> by chester
			if (!is_channel_valid(pMgmt->uScanChannel) &&
			    pMgmt->uScanChannel <= pDevice->byMaxChannel) {
				pMgmt->uScanChannel = pDevice->byMaxChannel + 1;
				pMgmt->eCommandState = WLAN_CMD_SCAN_END;

			}

			if (!pMgmt->b11hEnable ||
			    (pMgmt->uScanChannel < CB_MAX_CHANNEL_24G)) {
				s_vProbeChannel(pDevice);
				spin_unlock_irq(&pDevice->lock);
				vCommandTimerWait((void *)pDevice, WCMD_ACTIVE_SCAN_TIME);
				return;
			} else {
				spin_unlock_irq(&pDevice->lock);
				vCommandTimerWait((void *)pDevice, WCMD_PASSIVE_SCAN_TIME);
				return;
			}

		}

		break;

	case WLAN_CMD_SCAN_END:

		// Set Baseband's sensitivity back.
		// Set channel back
		set_channel(pMgmt->pAdapter, pMgmt->uCurrChannel);
		pr_debug("Scanning, set back to channel: [%d]\n",
			 pMgmt->uCurrChannel);
		if (pMgmt->eCurrMode == WMAC_MODE_IBSS_STA)
			CARDbSetBSSID(pMgmt->pAdapter, pMgmt->abyCurrBSSID, NL80211_IFTYPE_ADHOC);
		else
			CARDbSetBSSID(pMgmt->pAdapter, pMgmt->abyCurrBSSID, NL80211_IFTYPE_STATION);

		pMgmt->eScanState = WMAC_NO_SCANNING;
		vAdHocBeaconRestart(pDevice);
//2008-0409-07, <Add> by Einsn Liu
#ifdef WPA_SUPPLICANT_DRIVER_WEXT_SUPPORT
		if (pMgmt->eScanType == WMAC_SCAN_PASSIVE) {
			//send scan event to wpa_Supplicant
			union iwreq_data wrqu;

			memset(&wrqu, 0, sizeof(wrqu));
			wireless_send_event(pDevice->dev, SIOCGIWSCAN, &wrqu, NULL);
		}
#endif
		s_bCommandComplete(pDevice);
		break;

	case WLAN_CMD_DISASSOCIATE_START:
		pDevice->byReAssocCount = 0;
		if ((pMgmt->eCurrMode == WMAC_MODE_ESS_STA) &&
		    (pMgmt->eCurrState != WMAC_STATE_ASSOC)) {
			s_bCommandComplete(pDevice);
			spin_unlock_irq(&pDevice->lock);
			return;
		} else {
			pr_debug("Send Disassociation Packet..\n");
			// reason = 8 : disassoc because sta has left
			vMgrDisassocBeginSta((void *)pDevice, pMgmt, pMgmt->abyCurrBSSID, (8), &Status);
			pDevice->bLinkPass = false;
			// unlock command busy
			pItemSSID = (PWLAN_IE_SSID)pMgmt->abyCurrSSID;
			pItemSSID->len = 0;
			memset(pItemSSID->abySSID, 0, WLAN_SSID_MAXLEN);
			pMgmt->eCurrState = WMAC_STATE_IDLE;
			pMgmt->sNodeDBTable[0].bActive = false;
		}
		netif_stop_queue(pDevice->dev);
		pDevice->eCommandState = WLAN_DISASSOCIATE_WAIT;
		// wait all Control TD complete
		if (pDevice->iTDUsed[TYPE_TXDMA0] != 0) {
			vCommandTimerWait((void *)pDevice, 10);
			spin_unlock_irq(&pDevice->lock);
			return;
		}
		pr_debug(" CARDbRadioPowerOff\n");
		//2008-09-02  <mark>	by chester
		s_bCommandComplete(pDevice);
		break;

	case WLAN_DISASSOCIATE_WAIT:
		// wait all Control TD complete
		if (pDevice->iTDUsed[TYPE_TXDMA0] != 0) {
			vCommandTimerWait((void *)pDevice, 10);
			spin_unlock_irq(&pDevice->lock);
			return;
		}
//2008-09-02  <mark> by chester
		s_bCommandComplete(pDevice);
		break;

	case WLAN_CMD_SSID_START:
		pDevice->byReAssocCount = 0;
		if (pDevice->bRadioOff) {
			s_bCommandComplete(pDevice);
			spin_unlock_irq(&pDevice->lock);
			return;
		}
		pr_debug("chester-abyDesireSSID=%s\n", ((PWLAN_IE_SSID)pMgmt->abyDesireSSID)->abySSID);
		pItemSSID = (PWLAN_IE_SSID)pMgmt->abyDesireSSID;
		pItemSSIDCurr = (PWLAN_IE_SSID)pMgmt->abyCurrSSID;
		pr_debug(" cmd: desire ssid = %s\n", pItemSSID->abySSID);
		pr_debug(" cmd: curr ssid = %s\n", pItemSSIDCurr->abySSID);

		if (pMgmt->eCurrState == WMAC_STATE_ASSOC) {
			pr_debug(" Cmd pMgmt->eCurrState == WMAC_STATE_ASSOC\n");
			pr_debug(" pItemSSID->len =%d\n", pItemSSID->len);
			pr_debug(" pItemSSIDCurr->len = %d\n",
				 pItemSSIDCurr->len);
			pr_debug(" desire ssid = %s\n", pItemSSID->abySSID);
			pr_debug(" curr ssid = %s\n", pItemSSIDCurr->abySSID);
		}

		if ((pMgmt->eCurrState == WMAC_STATE_ASSOC) ||
		    ((pMgmt->eCurrMode == WMAC_MODE_IBSS_STA) && (pMgmt->eCurrState == WMAC_STATE_JOINTED))) {
			if (pItemSSID->len == pItemSSIDCurr->len) {
				if (memcmp(pItemSSID->abySSID, pItemSSIDCurr->abySSID, pItemSSID->len) == 0) {
					s_bCommandComplete(pDevice);
					spin_unlock_irq(&pDevice->lock);
					return;
				}
			}

			netif_stop_queue(pDevice->dev);
			pDevice->bLinkPass = false;
		}
		// set initial state
		pMgmt->eCurrState = WMAC_STATE_IDLE;
		pMgmt->eCurrMode = WMAC_MODE_STANDBY;
		PSvDisablePowerSaving((void *)pDevice);
		BSSvClearNodeDBTable(pDevice, 0);

		vMgrJoinBSSBegin((void *)pDevice, &Status);
		// if Infra mode
		if ((pMgmt->eCurrMode == WMAC_MODE_ESS_STA) && (pMgmt->eCurrState == WMAC_STATE_JOINTED)) {
			// Call mgr to begin the deauthentication
			// reason = (3) because sta has left ESS
			if (pMgmt->eCurrState >= WMAC_STATE_AUTH)
				vMgrDeAuthenBeginSta((void *)pDevice, pMgmt, pMgmt->abyCurrBSSID, (3), &Status);

			// Call mgr to begin the authentication
			vMgrAuthenBeginSta((void *)pDevice, pMgmt, &Status);
			if (Status == CMD_STATUS_SUCCESS) {
				pDevice->byLinkWaitCount = 0;
				pDevice->eCommandState = WLAN_AUTHENTICATE_WAIT;
				vCommandTimerWait((void *)pDevice, AUTHENTICATE_TIMEOUT);
				spin_unlock_irq(&pDevice->lock);
				pr_debug(" Set eCommandState = WLAN_AUTHENTICATE_WAIT\n");
				return;
			}
		}
		// if Adhoc mode
		else if (pMgmt->eCurrMode == WMAC_MODE_IBSS_STA) {
			if (pMgmt->eCurrState == WMAC_STATE_JOINTED) {
				if (netif_queue_stopped(pDevice->dev))
					netif_wake_queue(pDevice->dev);

				pDevice->bLinkPass = true;

				pMgmt->sNodeDBTable[0].bActive = true;
				pMgmt->sNodeDBTable[0].uInActiveCount = 0;
				bClearBSSID_SCAN(pDevice);
			} else {
				// start own IBSS
				vMgrCreateOwnIBSS((void *)pDevice, &Status);
				if (Status != CMD_STATUS_SUCCESS)
					pr_debug(" WLAN_CMD_IBSS_CREATE fail !\n");

				BSSvAddMulticastNode(pDevice);
			}
		}
		// if SSID not found
		else if (pMgmt->eCurrMode == WMAC_MODE_STANDBY) {
			if (pMgmt->eConfigMode == WMAC_CONFIG_IBSS_STA ||
			    pMgmt->eConfigMode == WMAC_CONFIG_AUTO) {
				// start own IBSS
				vMgrCreateOwnIBSS((void *)pDevice, &Status);
				if (Status != CMD_STATUS_SUCCESS)
					pr_debug(" WLAN_CMD_IBSS_CREATE fail !\n");

				BSSvAddMulticastNode(pDevice);
				if (netif_queue_stopped(pDevice->dev))
					netif_wake_queue(pDevice->dev);

				pDevice->bLinkPass = true;
			} else {
				pr_debug("Disconnect SSID none\n");
#ifdef WPA_SUPPLICANT_DRIVER_WEXT_SUPPORT
				{
					union iwreq_data  wrqu;

					memset(&wrqu, 0, sizeof(wrqu));
					wrqu.ap_addr.sa_family = ARPHRD_ETHER;
					pr_debug("wireless_send_event--->SIOCGIWAP(disassociated:vMgrJoinBSSBegin Fail !!)\n");
					wireless_send_event(pDevice->dev, SIOCGIWAP, &wrqu, NULL);
				}
#endif

			}
		}
		s_bCommandComplete(pDevice);
		break;

	case WLAN_AUTHENTICATE_WAIT:
		pr_debug("eCommandState == WLAN_AUTHENTICATE_WAIT\n");
		if (pMgmt->eCurrState == WMAC_STATE_AUTH) {
			// Call mgr to begin the association
			pDevice->byLinkWaitCount = 0;
			pr_debug("eCurrState == WMAC_STATE_AUTH\n");
			vMgrAssocBeginSta((void *)pDevice, pMgmt, &Status);
			if (Status == CMD_STATUS_SUCCESS) {
				pDevice->byLinkWaitCount = 0;
				pr_debug("eCommandState = WLAN_ASSOCIATE_WAIT\n");
				pDevice->eCommandState = WLAN_ASSOCIATE_WAIT;
				vCommandTimerWait((void *)pDevice, ASSOCIATE_TIMEOUT);
				spin_unlock_irq(&pDevice->lock);
				return;
			}
		}

		else if (pMgmt->eCurrState < WMAC_STATE_AUTHPENDING) {
			pr_debug("WLAN_AUTHENTICATE_WAIT:Authen Fail???\n");
		} else if (pDevice->byLinkWaitCount <= 4) {    //mike add:wait another 2 sec if authenticated_frame delay!
			pDevice->byLinkWaitCount++;
			pr_debug("WLAN_AUTHENTICATE_WAIT:wait %d times!!\n", pDevice->byLinkWaitCount);
			spin_unlock_irq(&pDevice->lock);
			vCommandTimerWait((void *)pDevice, AUTHENTICATE_TIMEOUT/2);
			return;
		}
		pDevice->byLinkWaitCount = 0;
		s_bCommandComplete(pDevice);
		break;

	case WLAN_ASSOCIATE_WAIT:
		if (pMgmt->eCurrState == WMAC_STATE_ASSOC) {
			pr_debug("eCurrState == WMAC_STATE_ASSOC\n");
			if (pDevice->ePSMode != WMAC_POWER_CAM)
				PSvEnablePowerSaving((void *)pDevice, pMgmt->wListenInterval);

			if (pMgmt->eAuthenMode >= WMAC_AUTH_WPA)
				KeybRemoveAllKey(&(pDevice->sKey), pDevice->abyBSSID, pDevice->PortOffset);

			pDevice->bLinkPass = true;
			pDevice->byLinkWaitCount = 0;
			pDevice->byReAssocCount = 0;
			bClearBSSID_SCAN(pDevice);
			if (pDevice->byFOETuning) {
				BBvSetFOE(pDevice->PortOffset);
				PSbSendNullPacket(pDevice);
			}
			if (netif_queue_stopped(pDevice->dev))
				netif_wake_queue(pDevice->dev);

			if (pDevice->IsTxDataTrigger) {    //TxDataTimer is not triggered at the first time
				del_timer(&pDevice->sTimerTxData);
				init_timer(&pDevice->sTimerTxData);
				pDevice->sTimerTxData.data = (unsigned long) pDevice;
				pDevice->sTimerTxData.function = (TimerFunction)BSSvSecondTxData;
				pDevice->sTimerTxData.expires = RUN_AT(10*HZ);      //10s callback
				pDevice->fTxDataInSleep = false;
				pDevice->nTxDataTimeCout = 0;
			}

			pDevice->IsTxDataTrigger = true;
			add_timer(&pDevice->sTimerTxData);

		} else if (pMgmt->eCurrState < WMAC_STATE_ASSOCPENDING) {
			printk("WLAN_ASSOCIATE_WAIT:Association Fail???\n");
		} else if (pDevice->byLinkWaitCount <= 4) {    //mike add:wait another 2 sec if associated_frame delay!
			pDevice->byLinkWaitCount++;
			pr_debug("WLAN_ASSOCIATE_WAIT:wait %d times!!\n", pDevice->byLinkWaitCount);
			spin_unlock_irq(&pDevice->lock);
			vCommandTimerWait((void *)pDevice, ASSOCIATE_TIMEOUT/2);
			return;
		}
		pDevice->byLinkWaitCount = 0;

		s_bCommandComplete(pDevice);
		break;

	case WLAN_CMD_AP_MODE_START:
		pr_debug("eCommandState == WLAN_CMD_AP_MODE_START\n");

		if (pMgmt->eConfigMode == WMAC_CONFIG_AP) {
			del_timer(&pMgmt->sTimerSecondCallback);
			pMgmt->eCurrState = WMAC_STATE_IDLE;
			pMgmt->eCurrMode = WMAC_MODE_STANDBY;
			pDevice->bLinkPass = false;
			if (pDevice->bEnableHostWEP)
				BSSvClearNodeDBTable(pDevice, 1);
			else
				BSSvClearNodeDBTable(pDevice, 0);
			pDevice->uAssocCount = 0;
			pMgmt->eCurrState = WMAC_STATE_IDLE;
			pDevice->bFixRate = false;

			vMgrCreateOwnIBSS((void *)pDevice, &Status);
			if (Status != CMD_STATUS_SUCCESS)
				pr_debug(" vMgrCreateOwnIBSS fail !\n");

			// alway turn off unicast bit
			MACvRegBitsOff(pDevice->PortOffset, MAC_REG_RCR, RCR_UNICAST);
			pDevice->byRxMode &= ~RCR_UNICAST;
			pr_debug("wcmd: rx_mode = %x\n", pDevice->byRxMode);
			BSSvAddMulticastNode(pDevice);
			if (netif_queue_stopped(pDevice->dev))
				netif_wake_queue(pDevice->dev);

			pDevice->bLinkPass = true;
			add_timer(&pMgmt->sTimerSecondCallback);
		}
		s_bCommandComplete(pDevice);
		break;

	case WLAN_CMD_TX_PSPACKET_START:
		// DTIM Multicast tx
		if (pMgmt->sNodeDBTable[0].bRxPSPoll) {
			while ((skb = skb_dequeue(&pMgmt->sNodeDBTable[0].sTxPSQueue)) != NULL) {
				if (skb_queue_empty(&pMgmt->sNodeDBTable[0].sTxPSQueue)) {
					pMgmt->abyPSTxMap[0] &= ~byMask[0];
					pDevice->bMoreData = false;
				} else {
					pDevice->bMoreData = true;
				}
				if (!device_dma0_xmit(pDevice, skb, 0))
					pr_debug("Multicast ps tx fail\n");

				pMgmt->sNodeDBTable[0].wEnQueueCnt--;
			}
		}

		// PS nodes tx
		for (ii = 1; ii < (MAX_NODE_NUM + 1); ii++) {
			if (pMgmt->sNodeDBTable[ii].bActive &&
			    pMgmt->sNodeDBTable[ii].bRxPSPoll) {
				pr_debug("Index=%d Enqueu Cnt= %d\n",
					 ii,
					 pMgmt->sNodeDBTable[ii].wEnQueueCnt);
				while ((skb = skb_dequeue(&pMgmt->sNodeDBTable[ii].sTxPSQueue)) != NULL) {
					if (skb_queue_empty(&pMgmt->sNodeDBTable[ii].sTxPSQueue)) {
						// clear tx map
						pMgmt->abyPSTxMap[pMgmt->sNodeDBTable[ii].wAID >> 3] &=
							~byMask[pMgmt->sNodeDBTable[ii].wAID & 7];
						pDevice->bMoreData = false;
					} else {
						pDevice->bMoreData = true;
					}
					if (!device_dma0_xmit(pDevice, skb, ii))
						pr_debug("sta ps tx fail\n");

					pMgmt->sNodeDBTable[ii].wEnQueueCnt--;
					// check if sta ps enabled, and wait next pspoll.
					// if sta ps disable, then send all pending buffers.
					if (pMgmt->sNodeDBTable[ii].bPSEnable)
						break;
				}
				if (skb_queue_empty(&pMgmt->sNodeDBTable[ii].sTxPSQueue)) {
					// clear tx map
					pMgmt->abyPSTxMap[pMgmt->sNodeDBTable[ii].wAID >> 3] &=
						~byMask[pMgmt->sNodeDBTable[ii].wAID & 7];
					pr_debug("Index=%d PS queue clear\n",
						 ii);
				}
				pMgmt->sNodeDBTable[ii].bRxPSPoll = false;
			}
		}

		s_bCommandComplete(pDevice);
		break;

	case WLAN_CMD_RADIO_START:
		pr_debug("eCommandState == WLAN_CMD_RADIO_START\n");
		if (pDevice->bRadioCmd)
			CARDbRadioPowerOn(pDevice);
		else
			CARDbRadioPowerOff(pDevice);

		s_bCommandComplete(pDevice);
		break;

	case WLAN_CMD_CHECK_BBSENSITIVITY_CHANGE:
		// wait all TD complete
		if (pDevice->iTDUsed[TYPE_AC0DMA] != 0) {
			vCommandTimerWait((void *)pDevice, 10);
			spin_unlock_irq(&pDevice->lock);
			return;
		}
		if (pDevice->iTDUsed[TYPE_TXDMA0] != 0) {
			vCommandTimerWait((void *)pDevice, 10);
			spin_unlock_irq(&pDevice->lock);
			return;
		}
		pDevice->byBBVGACurrent = pDevice->byBBVGANew;
		BBvSetVGAGainOffset(pDevice, pDevice->byBBVGACurrent);
		pr_debug("SetVGAGainOffset %02X\n", pDevice->byBBVGACurrent);
		s_bCommandComplete(pDevice);
		break;

	default:
		s_bCommandComplete(pDevice);
		break;

	} //switch
	spin_unlock_irq(&pDevice->lock);
}

static
bool
s_bCommandComplete(
	struct vnt_private *pDevice
)
{
	PWLAN_IE_SSID pSSID;
	bool bRadioCmd = false;
	bool bForceSCAN = true;
	PSMgmtObject  pMgmt = pDevice->pMgmt;

	pDevice->eCommandState = WLAN_CMD_IDLE;
	if (pDevice->cbFreeCmdQueue == CMD_Q_SIZE) {
		//Command Queue Empty
		pDevice->bCmdRunning = false;
		return true;
	} else {
		pDevice->eCommand = pDevice->eCmdQueue[pDevice->uCmdDequeueIdx].eCmd;
		pSSID = (PWLAN_IE_SSID)pDevice->eCmdQueue[pDevice->uCmdDequeueIdx].abyCmdDesireSSID;
		bRadioCmd = pDevice->eCmdQueue[pDevice->uCmdDequeueIdx].bRadioCmd;
		bForceSCAN = pDevice->eCmdQueue[pDevice->uCmdDequeueIdx].bForceSCAN;
		ADD_ONE_WITH_WRAP_AROUND(pDevice->uCmdDequeueIdx, CMD_Q_SIZE);
		pDevice->cbFreeCmdQueue++;
		pDevice->bCmdRunning = true;
		switch (pDevice->eCommand) {
		case WLAN_CMD_BSSID_SCAN:
			pr_debug("eCommandState= WLAN_CMD_BSSID_SCAN\n");
			pDevice->eCommandState = WLAN_CMD_SCAN_START;
			pMgmt->uScanChannel = 0;
			if (pSSID->len != 0)
				memcpy(pMgmt->abyScanSSID, pSSID, WLAN_IEHDR_LEN + WLAN_SSID_MAXLEN + 1);
			else
				memset(pMgmt->abyScanSSID, 0, WLAN_IEHDR_LEN + WLAN_SSID_MAXLEN + 1);

			break;
		case WLAN_CMD_SSID:
			pDevice->eCommandState = WLAN_CMD_SSID_START;
			if (pSSID->len > WLAN_SSID_MAXLEN)
				pSSID->len = WLAN_SSID_MAXLEN;
			if (pSSID->len != 0)
				memcpy(pDevice->pMgmt->abyDesireSSID, pSSID, WLAN_IEHDR_LEN + WLAN_SSID_MAXLEN + 1);
			pr_debug("eCommandState= WLAN_CMD_SSID_START\n");
			break;
		case WLAN_CMD_DISASSOCIATE:
			pDevice->eCommandState = WLAN_CMD_DISASSOCIATE_START;
			break;
		case WLAN_CMD_RX_PSPOLL:
			pDevice->eCommandState = WLAN_CMD_TX_PSPACKET_START;
			break;
		case WLAN_CMD_RUN_AP:
			pDevice->eCommandState = WLAN_CMD_AP_MODE_START;
			break;
		case WLAN_CMD_RADIO:
			pDevice->eCommandState = WLAN_CMD_RADIO_START;
			pDevice->bRadioCmd = bRadioCmd;
			break;
		case WLAN_CMD_CHANGE_BBSENSITIVITY:
			pDevice->eCommandState = WLAN_CMD_CHECK_BBSENSITIVITY_CHANGE;
			break;

		default:
			break;

		}

		vCommandTimerWait((void *)pDevice, 0);
	}

	return true;
}

bool bScheduleCommand(
	void *hDeviceContext,
	CMD_CODE    eCommand,
	unsigned char *pbyItem0
)
{
	struct vnt_private *pDevice = hDeviceContext;

	if (pDevice->cbFreeCmdQueue == 0)
		return false;

	pDevice->eCmdQueue[pDevice->uCmdEnqueueIdx].eCmd = eCommand;
	pDevice->eCmdQueue[pDevice->uCmdEnqueueIdx].bForceSCAN = true;
	memset(pDevice->eCmdQueue[pDevice->uCmdEnqueueIdx].abyCmdDesireSSID, 0 , WLAN_IEHDR_LEN + WLAN_SSID_MAXLEN + 1);

	if (pbyItem0 != NULL) {
		switch (eCommand) {
		case WLAN_CMD_BSSID_SCAN:
			memcpy(pDevice->eCmdQueue[pDevice->uCmdEnqueueIdx].abyCmdDesireSSID,
			       pbyItem0, WLAN_IEHDR_LEN + WLAN_SSID_MAXLEN + 1);
			pDevice->eCmdQueue[pDevice->uCmdEnqueueIdx].bForceSCAN = false;
			break;

		case WLAN_CMD_SSID:
			memcpy(pDevice->eCmdQueue[pDevice->uCmdEnqueueIdx].abyCmdDesireSSID,
			       pbyItem0, WLAN_IEHDR_LEN + WLAN_SSID_MAXLEN + 1);
			break;

		case WLAN_CMD_DISASSOCIATE:
			pDevice->eCmdQueue[pDevice->uCmdEnqueueIdx].bNeedRadioOFF = *((int *)pbyItem0);
			break;

		case WLAN_CMD_RX_PSPOLL:
			break;

		case WLAN_CMD_RADIO:
			pDevice->eCmdQueue[pDevice->uCmdEnqueueIdx].bRadioCmd = *((int *)pbyItem0);
			break;

		case WLAN_CMD_CHANGE_BBSENSITIVITY:
			pDevice->eCommandState = WLAN_CMD_CHECK_BBSENSITIVITY_CHANGE;
			break;

		default:
			break;
		}
	}

	ADD_ONE_WITH_WRAP_AROUND(pDevice->uCmdEnqueueIdx, CMD_Q_SIZE);
	pDevice->cbFreeCmdQueue--;

	if (!pDevice->bCmdRunning)
		s_bCommandComplete(pDevice);

	return true;
}

/*
 * Description:
 *      Clear BSSID_SCAN cmd in CMD Queue
 *
 * Parameters:
 *  In:
 *      hDeviceContext  - Pointer to the adapter
 *      eCommand        - Command
 *  Out:
 *      none
 *
 * Return Value: true if success; otherwise false
 *
 */
bool bClearBSSID_SCAN(
	void *hDeviceContext
)
{
	struct vnt_private *pDevice = hDeviceContext;
	unsigned int uCmdDequeueIdx = pDevice->uCmdDequeueIdx;
	unsigned int ii;

	if ((pDevice->cbFreeCmdQueue < CMD_Q_SIZE) && (uCmdDequeueIdx != pDevice->uCmdEnqueueIdx)) {
		for (ii = 0; ii < (CMD_Q_SIZE - pDevice->cbFreeCmdQueue); ii++) {
			if (pDevice->eCmdQueue[uCmdDequeueIdx].eCmd == WLAN_CMD_BSSID_SCAN)
				pDevice->eCmdQueue[uCmdDequeueIdx].eCmd = WLAN_CMD_IDLE;
			ADD_ONE_WITH_WRAP_AROUND(uCmdDequeueIdx, CMD_Q_SIZE);
			if (uCmdDequeueIdx == pDevice->uCmdEnqueueIdx)
				break;
		}
	}
	return true;
}

//mike add:reset command timer
void
vResetCommandTimer(
	void *hDeviceContext
)
{
	struct vnt_private *pDevice = hDeviceContext;

	//delete timer
	del_timer(&pDevice->sTimerCommand);
	//init timer
	init_timer(&pDevice->sTimerCommand);
	pDevice->sTimerCommand.data = (unsigned long) pDevice;
	pDevice->sTimerCommand.function = (TimerFunction)vCommandTimer;
	pDevice->sTimerCommand.expires = RUN_AT(HZ);
	pDevice->cbFreeCmdQueue = CMD_Q_SIZE;
	pDevice->uCmdDequeueIdx = 0;
	pDevice->uCmdEnqueueIdx = 0;
	pDevice->eCommandState = WLAN_CMD_IDLE;
	pDevice->bCmdRunning = false;
	pDevice->bCmdClear = false;
}

void
BSSvSecondTxData(
	void *hDeviceContext
)
{
	struct vnt_private *pDevice = hDeviceContext;
	PSMgmtObject  pMgmt = &(pDevice->sMgmtObj);

	pDevice->nTxDataTimeCout++;

	if (pDevice->nTxDataTimeCout < 4)     //don't tx data if timer less than 40s
	{
		pDevice->sTimerTxData.expires = RUN_AT(10*HZ);      //10s callback
		add_timer(&pDevice->sTimerTxData);
		return;
	}

	spin_lock_irq(&pDevice->lock);

	/* open && sharekey linking */
	if ((pDevice->bLinkPass && (pMgmt->eAuthenMode < WMAC_AUTH_WPA)) ||
	    pDevice->fWPA_Authened) {   /* wpa linking */
		pDevice->fTxDataInSleep = true;
		PSbSendNullPacket(pDevice);	/* send null packet */
		pDevice->fTxDataInSleep = false;
	}

	spin_unlock_irq(&pDevice->lock);

	pDevice->sTimerTxData.expires = RUN_AT(10*HZ); /* 10s callback */
	add_timer(&pDevice->sTimerTxData);
}