/*
 * 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
 *      s_bClearBSSID_SCAN- Clear BSSID_SCAN cmd in CMD Queue
 *
 * Revision History:
 *
 */

#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 "usbpipe.h"
#include "rxtx.h"
#include "rf.h"
#include "channel.h"
#include "iowpa.h"

static int msglevel = MSG_LEVEL_INFO;
//static int msglevel = MSG_LEVEL_DEBUG;

static void s_vProbeChannel(struct vnt_private *);

static struct vnt_tx_mgmt *s_MgrMakeProbeRequest(struct vnt_private *,
	struct vnt_manager *pMgmt, u8 *pScanBSSID, PWLAN_IE_SSID pSSID,
	PWLAN_IE_SUPP_RATES pCurrRates, PWLAN_IE_SUPP_RATES pCurrExtSuppRates);

static int s_bCommandComplete(struct vnt_private *);

static int s_bClearBSSID_SCAN(struct vnt_private *);

/*
 * 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)
{
	struct vnt_manager *pMgmt = &pDevice->vnt_mgmt;
	int bStop;

	/*
	 * temporarily stop 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
	 *  (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) {
		//PMESG(("STOP_BEACON: IBSSChannel = %u, ScanChannel = %u\n",
		//        pMgmt->uIBSSChannel, pMgmt->uScanChannel));
		MACvRegBitsOff(pDevice, 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)
{
	struct vnt_manager *pMgmt = &pDevice->vnt_mgmt;

    /*
     * 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)) {
		//PMESG(("RESTART_BEACON\n"));
		MACvRegBitsOn(pDevice, 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)
{
	struct vnt_manager *pMgmt = &pDevice->vnt_mgmt;
	struct vnt_tx_mgmt *pTxPacket;
	u8 abyCurrSuppRatesG[] = {WLAN_EID_SUPP_RATES,
			8, 0x02, 0x04, 0x0B, 0x16, 0x24, 0x30, 0x48, 0x6C};
			/* 1M,   2M,   5M,   11M,  18M,  24M,  36M,  54M*/
	u8 abyCurrExtSuppRatesG[] = {WLAN_EID_EXTSUPP_RATES,
			4, 0x0C, 0x12, 0x18, 0x60};
			/* 6M,   9M,   12M,  48M*/
	u8 abyCurrSuppRatesA[] = {WLAN_EID_SUPP_RATES,
			8, 0x0C, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6C};
	u8 abyCurrSuppRatesB[] = {WLAN_EID_SUPP_RATES,
			4, 0x02, 0x04, 0x0B, 0x16};
	u8 *pbyRate;
	int ii;

	if (pDevice->byBBType == BB_TYPE_11A)
		pbyRate = &abyCurrSuppRatesA[0];
	else if (pDevice->byBBType == BB_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 < 1; ii++) {
			if (csMgmt_xmit(pDevice, pTxPacket) != CMD_STATUS_PENDING) {
				DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO "Probe request sending fail..\n");
			} else {
				DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO "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 struct vnt_tx_mgmt *s_MgrMakeProbeRequest(struct vnt_private *pDevice,
	struct vnt_manager *pMgmt, u8 *pScanBSSID, PWLAN_IE_SSID pSSID,
	PWLAN_IE_SUPP_RATES pCurrRates, PWLAN_IE_SUPP_RATES pCurrExtSuppRates)
{
	struct vnt_tx_mgmt *pTxPacket = NULL;
	WLAN_FR_PROBEREQ sFrame;

	pTxPacket = (struct vnt_tx_mgmt *)pMgmt->pbyMgmtPacketPool;
	memset(pTxPacket, 0, sizeof(struct vnt_tx_mgmt)
		+ WLAN_PROBEREQ_FR_MAXLEN);
	pTxPacket->p80211Header = (PUWLAN_80211HDR)((u8 *)pTxPacket
		+ sizeof(struct vnt_tx_mgmt));
	sFrame.pBuf = (u8 *)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->byBBType == BB_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;
}

static void
vCommandTimerWait(struct vnt_private *pDevice, unsigned long MSecond)
{
	schedule_delayed_work(&pDevice->run_command_work,
						msecs_to_jiffies(MSecond));
}

void vRunCommand(struct work_struct *work)
{
	struct vnt_private *pDevice =
		container_of(work, struct vnt_private, run_command_work.work);
	struct vnt_manager *pMgmt = &pDevice->vnt_mgmt;
	PWLAN_IE_SSID pItemSSID;
	PWLAN_IE_SSID pItemSSIDCurr;
	CMD_STATUS Status;
	struct sk_buff  *skb;
	union iwreq_data wrqu;
	int ii;
	u8 byMask[8] = {1, 2, 4, 8, 0x10, 0x20, 0x40, 0x80};
	u8 byData;
	unsigned long flags;

	if (pDevice->Flags & fMP_DISCONNECTED)
		return;

	if (pDevice->bCmdRunning != true)
		return;

	switch (pDevice->eCommandState) {

	case WLAN_CMD_SCAN_START:

		pDevice->byReAssocCount = 0;
		if (pDevice->bRadioOff == true)
			break;

		if (pMgmt->eCurrMode == WMAC_MODE_ESS_AP)
			break;

		pItemSSID = (PWLAN_IE_SSID)pMgmt->abyScanSSID;

		if (pMgmt->uScanChannel == 0)
			pMgmt->uScanChannel = pDevice->byMinChannel;
		if (pMgmt->uScanChannel > pDevice->byMaxChannel) {
			pDevice->eCommandState = WLAN_CMD_SCAN_END;
			break;
		} else {
			if (!ChannelValid(pDevice->byZoneType, pMgmt->uScanChannel)) {
				DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO "Invalid channel pMgmt->uScanChannel = %d\n", pMgmt->uScanChannel);
				pMgmt->uScanChannel++;
				break;
			}
			if (pMgmt->uScanChannel == pDevice->byMinChannel) {
				// pMgmt->eScanType = WMAC_SCAN_ACTIVE;          //mike mark
				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;
				// clear bssid list
				/* BSSvClearBSSList((void *) pDevice, pDevice->bLinkPass); */
				pMgmt->eScanState = WMAC_IS_SCANNING;
				pDevice->byScanBBType = pDevice->byBBType;  //lucas
				pDevice->bStopDataPkt = true;
				// Turn off RCR_BSSID filter every time
				MACvRegBitsOff(pDevice, MAC_REG_RCR, RCR_BSSID);
				pDevice->byRxMode &= ~RCR_BSSID;
			}
			//lucas
			vAdHocBeaconStop(pDevice);
			if ((pDevice->byBBType != BB_TYPE_11A) &&
			    (pMgmt->uScanChannel > CB_MAX_CHANNEL_24G)) {
				pDevice->byBBType = BB_TYPE_11A;
				CARDvSetBSSMode(pDevice);
			} else if ((pDevice->byBBType == BB_TYPE_11A) &&
				   (pMgmt->uScanChannel <= CB_MAX_CHANNEL_24G)) {
				pDevice->byBBType = BB_TYPE_11G;
				CARDvSetBSSMode(pDevice);
			}
			DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO "Scanning....  channel: [%d]\n", pMgmt->uScanChannel);
			// Set channel
			CARDbSetMediaChannel(pDevice, pMgmt->uScanChannel);
			// Set Baseband to be more sensitive.

			BBvSetShortSlotTime(pDevice);
			BBvSetVGAGainOffset(pDevice, pDevice->abyBBVGA[0]);
			BBvUpdatePreEDThreshold(pDevice, true);

			pMgmt->uScanChannel++;

			while (!ChannelValid(pDevice->byZoneType, pMgmt->uScanChannel) &&
				pMgmt->uScanChannel <= pDevice->byMaxChannel){
				pMgmt->uScanChannel++;
			}

			if (pMgmt->uScanChannel > pDevice->byMaxChannel) {
				// Set Baseband to be not sensitive and rescan
				pDevice->eCommandState = WLAN_CMD_SCAN_END;
			}
			if ((pMgmt->b11hEnable == false) ||
			    (pMgmt->uScanChannel < CB_MAX_CHANNEL_24G)) {
				s_vProbeChannel(pDevice);
				vCommandTimerWait((void *) pDevice, 100);
				return;
			} else {
				vCommandTimerWait((void *) pDevice, WCMD_PASSIVE_SCAN_TIME);
				return;
			}
		}

		break;

	case WLAN_CMD_SCAN_END:

		// Set Baseband's sensitivity back.
		if (pDevice->byBBType != pDevice->byScanBBType) {
			pDevice->byBBType = pDevice->byScanBBType;
			CARDvSetBSSMode(pDevice);
		}

		BBvSetShortSlotTime(pDevice);
		BBvSetVGAGainOffset(pDevice, pDevice->byBBVGACurrent);
		BBvUpdatePreEDThreshold(pDevice, false);

		// Set channel back
		vAdHocBeaconRestart(pDevice);
		// Set channel back
		CARDbSetMediaChannel(pDevice, pMgmt->uCurrChannel);
		// Set Filter
		if (pMgmt->bCurrBSSIDFilterOn) {
			MACvRegBitsOn(pDevice, MAC_REG_RCR, RCR_BSSID);
			pDevice->byRxMode |= RCR_BSSID;
		}
		DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO "Scanning, set back to channel: [%d]\n", pMgmt->uCurrChannel);
		pMgmt->uScanChannel = 0;
		pMgmt->eScanState = WMAC_NO_SCANNING;
		pDevice->bStopDataPkt = false;

		/*send scan event to wpa_Supplicant*/
		PRINT_K("wireless_send_event--->SIOCGIWSCAN(scan done)\n");
		memset(&wrqu, 0, sizeof(wrqu));
		wireless_send_event(pDevice->dev, SIOCGIWSCAN, &wrqu, NULL);

		break;

	case WLAN_CMD_DISASSOCIATE_START:
		pDevice->byReAssocCount = 0;
		if ((pMgmt->eCurrMode == WMAC_MODE_ESS_STA) &&
		    (pMgmt->eCurrState != WMAC_STATE_ASSOC)) {
			break;
		} else {
			pDevice->bwextstep0 = false;
			pDevice->bwextstep1 = false;
			pDevice->bwextstep2 = false;
			pDevice->bwextstep3 = false;
			pDevice->bWPASuppWextEnabled = false;
			pDevice->fWPA_Authened = false;

			DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"Send Disassociation Packet..\n");
			// reason = 8 : disassoc because sta has left
			vMgrDisassocBeginSta((void *) pDevice,
					     pMgmt,
					     pMgmt->abyCurrBSSID,
					     (8),
					     &Status);
			pDevice->bLinkPass = false;

			vnt_mac_set_led(pDevice, LEDSTS_STS, LEDSTS_SLOW);

			// 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;
//			pDevice->bBeaconBufReady = false;
		}
		netif_stop_queue(pDevice->dev);
		if (pDevice->bNeedRadioOFF == true)
			CARDbRadioPowerOff(pDevice);

		break;

	case WLAN_CMD_SSID_START:

		pDevice->byReAssocCount = 0;
		if (pDevice->bRadioOff == true)
			break;

		memcpy(pMgmt->abyAdHocSSID, pMgmt->abyDesireSSID,
		       ((PWLAN_IE_SSID)pMgmt->abyDesireSSID)->len + WLAN_IEHDR_LEN);

		pItemSSID = (PWLAN_IE_SSID)pMgmt->abyDesireSSID;
		pItemSSIDCurr = (PWLAN_IE_SSID)pMgmt->abyCurrSSID;
		DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO" cmd: desire ssid = %s\n", pItemSSID->abySSID);
		DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO" cmd: curr ssid = %s\n", pItemSSIDCurr->abySSID);

		if (pMgmt->eCurrState == WMAC_STATE_ASSOC) {
			DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO" Cmd pMgmt->eCurrState == WMAC_STATE_ASSOC\n");
			DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO" pItemSSID->len =%d\n", pItemSSID->len);
			DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO" pItemSSIDCurr->len = %d\n", pItemSSIDCurr->len);
			DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO" desire ssid = %s\n", pItemSSID->abySSID);
			DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO" 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))
					break;
			}
			netif_stop_queue(pDevice->dev);
			pDevice->bLinkPass = false;

			vnt_mac_set_led(pDevice, LEDSTS_STS, LEDSTS_SLOW);
		}
		// 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);
				DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO" 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;

				vnt_mac_set_led(pDevice, LEDSTS_STS, LEDSTS_INTER);

				pMgmt->sNodeDBTable[0].bActive = true;
				pMgmt->sNodeDBTable[0].uInActiveCount = 0;
			} else {
				// start own IBSS
				DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO "CreateOwn IBSS by CurrMode = IBSS_STA\n");
				vMgrCreateOwnIBSS((void *) pDevice, &Status);
				if (Status != CMD_STATUS_SUCCESS) {
					DBG_PRT(MSG_LEVEL_DEBUG,
						KERN_INFO "WLAN_CMD_IBSS_CREATE fail!\n");
				}
				BSSvAddMulticastNode(pDevice);
			}
			s_bClearBSSID_SCAN(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
				DBG_PRT(MSG_LEVEL_DEBUG,
					KERN_INFO "CreateOwn IBSS by CurrMode = STANDBY\n");
				vMgrCreateOwnIBSS((void *) pDevice, &Status);
				if (Status != CMD_STATUS_SUCCESS) {
					DBG_PRT(MSG_LEVEL_DEBUG,
						KERN_INFO "WLAN_CMD_IBSS_CREATE fail!\n");
				}
				BSSvAddMulticastNode(pDevice);
				s_bClearBSSID_SCAN(pDevice);
/*
				pDevice->bLinkPass = true;
				if (netif_queue_stopped(pDevice->dev)){
					netif_wake_queue(pDevice->dev);
				}
				s_bClearBSSID_SCAN(pDevice);
*/
			} else {
				DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO "Disconnect SSID none\n");
				// if(pDevice->bWPASuppWextEnabled == true)
				{
					union iwreq_data  wrqu;
					memset(&wrqu, 0, sizeof(wrqu));
					wrqu.ap_addr.sa_family = ARPHRD_ETHER;
					PRINT_K("wireless_send_event--->SIOCGIWAP(disassociated:vMgrJoinBSSBegin Fail !!)\n");
					wireless_send_event(pDevice->dev, SIOCGIWAP, &wrqu, NULL);
				}
			}
		}
		break;

	case WLAN_AUTHENTICATE_WAIT:
		DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"eCommandState == WLAN_AUTHENTICATE_WAIT\n");
		if (pMgmt->eCurrState == WMAC_STATE_AUTH) {
			pDevice->byLinkWaitCount = 0;
			// Call mgr to begin the association
			DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"eCurrState == WMAC_STATE_AUTH\n");
			vMgrAssocBeginSta((void *) pDevice, pMgmt, &Status);
			if (Status == CMD_STATUS_SUCCESS) {
				DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"eCommandState = WLAN_ASSOCIATE_WAIT\n");
				pDevice->byLinkWaitCount = 0;
				pDevice->eCommandState = WLAN_ASSOCIATE_WAIT;
				vCommandTimerWait((void *) pDevice, ASSOCIATE_TIMEOUT);
				return;
			}
		} else if (pMgmt->eCurrState < WMAC_STATE_AUTHPENDING) {
			printk("WLAN_AUTHENTICATE_WAIT:Authen Fail???\n");
		} else if (pDevice->byLinkWaitCount <= 4) {
			//mike add:wait another 2 sec if authenticated_frame delay!
			pDevice->byLinkWaitCount++;
			printk("WLAN_AUTHENTICATE_WAIT:wait %d times!!\n", pDevice->byLinkWaitCount);
			vCommandTimerWait((void *) pDevice, AUTHENTICATE_TIMEOUT/2);
			return;
		}
		pDevice->byLinkWaitCount = 0;

		break;

	case WLAN_ASSOCIATE_WAIT:
		if (pMgmt->eCurrState == WMAC_STATE_ASSOC) {
			DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"eCurrState == WMAC_STATE_ASSOC\n");
			if (pDevice->ePSMode != WMAC_POWER_CAM) {
				PSvEnablePowerSaving((void *) pDevice,
						pMgmt->wListenInterval);
			}
/*
			if (pMgmt->eAuthenMode >= WMAC_AUTH_WPA) {
				KeybRemoveAllKey(pDevice, &(pDevice->sKey), pDevice->abyBSSID);
			}
*/
			pDevice->byLinkWaitCount = 0;
			pDevice->byReAssocCount = 0;
			pDevice->bLinkPass = true;

			vnt_mac_set_led(pDevice, LEDSTS_STS, LEDSTS_INTER);

			s_bClearBSSID_SCAN(pDevice);

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

		} 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++;
			printk("WLAN_ASSOCIATE_WAIT:wait %d times!!\n", pDevice->byLinkWaitCount);
			vCommandTimerWait((void *) pDevice, ASSOCIATE_TIMEOUT/2);
			return;
		}

		break;

	case WLAN_CMD_AP_MODE_START:
		DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"eCommandState == WLAN_CMD_AP_MODE_START\n");

		if (pMgmt->eConfigMode == WMAC_CONFIG_AP) {
			cancel_delayed_work_sync(&pDevice->second_callback_work);
			pMgmt->eCurrState = WMAC_STATE_IDLE;
			pMgmt->eCurrMode = WMAC_MODE_STANDBY;
			pDevice->bLinkPass = false;

			vnt_mac_set_led(pDevice, LEDSTS_STS, LEDSTS_SLOW);

			BSSvClearNodeDBTable(pDevice, 0);

			pDevice->uAssocCount = 0;
			pMgmt->eCurrState = WMAC_STATE_IDLE;
			pDevice->bFixRate = false;

			vMgrCreateOwnIBSS((void *) pDevice, &Status);
			if (Status != CMD_STATUS_SUCCESS) {
				DBG_PRT(MSG_LEVEL_DEBUG,
					KERN_INFO "vMgrCreateOwnIBSS fail!\n");
			}
			// always turn off unicast bit
			MACvRegBitsOff(pDevice, MAC_REG_RCR, RCR_UNICAST);
			pDevice->byRxMode &= ~RCR_UNICAST;
			DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO "wcmd: rx_mode = %x\n", pDevice->byRxMode);
			BSSvAddMulticastNode(pDevice);
			if (netif_queue_stopped(pDevice->dev))
				netif_wake_queue(pDevice->dev);
			pDevice->bLinkPass = true;

			vnt_mac_set_led(pDevice, LEDSTS_STS, LEDSTS_INTER);

			schedule_delayed_work(&pDevice->second_callback_work, HZ);
		}
		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;
				}

				spin_lock_irqsave(&pDevice->lock, flags);

				if (nsDMA_tx_packet(pDevice, skb) != 0)
					DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO "Multicast ps tx fail\n");

				spin_unlock_irqrestore(&pDevice->lock, flags);

				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) {
				DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO "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;
					}

					spin_lock_irqsave(&pDevice->lock, flags);

					if (nsDMA_tx_packet(pDevice, skb) != 0)
						DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO "sta ps tx fail\n");

					spin_unlock_irqrestore(&pDevice->lock, flags);

					pMgmt->sNodeDBTable[ii].wEnQueueCnt--;
					// check if sta ps enable, wait next pspoll
					// if sta ps disable, 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];
					DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO "Index=%d PS queue clear\n", ii);
				}
				pMgmt->sNodeDBTable[ii].bRxPSPoll = false;
			}
		}
		break;

	case WLAN_CMD_RADIO_START:

		DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"eCommandState == WLAN_CMD_RADIO_START\n");
//		if (pDevice->bRadioCmd == true)
//			CARDbRadioPowerOn(pDevice);
//		else
//			CARDbRadioPowerOff(pDevice);
		{
			int ntStatus = STATUS_SUCCESS;
			u8            byTmp;

			ntStatus = vnt_control_in(pDevice,
					MESSAGE_TYPE_READ,
					MAC_REG_GPIOCTL1,
					MESSAGE_REQUEST_MACREG,
					1,
					&byTmp);

			if (ntStatus != STATUS_SUCCESS)
				break;
			if ((byTmp & GPIO3_DATA) == 0) {
				DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO" WLAN_CMD_RADIO_START_OFF........................\n");
				// Old commands are useless.
				// empty command Q
				pDevice->cbFreeCmdQueue = CMD_Q_SIZE;
				pDevice->uCmdDequeueIdx = 0;
				pDevice->uCmdEnqueueIdx = 0;
				//0415pDevice->bCmdRunning = false;
				pDevice->bCmdClear = true;
				pDevice->bStopTx0Pkt = false;
				pDevice->bStopDataPkt = true;

				pDevice->byKeyIndex = 0;
				pDevice->bTransmitKey = false;

				KeyvInitTable(pDevice, &pDevice->sKey);

				pMgmt->byCSSPK = KEY_CTL_NONE;
				pMgmt->byCSSGK = KEY_CTL_NONE;

				if (pDevice->bLinkPass == true) {
					// reason = 8 : disassoc because sta has left
					vMgrDisassocBeginSta((void *) pDevice,
							pMgmt,
							pMgmt->abyCurrBSSID,
							(8),
							&Status);
					pDevice->bLinkPass = false;
					// unlock command busy
					pMgmt->eCurrState = WMAC_STATE_IDLE;
					pMgmt->sNodeDBTable[0].bActive = false;
					// if(pDevice->bWPASuppWextEnabled == true)
					{
						union iwreq_data  wrqu;
						memset(&wrqu, 0, sizeof(wrqu));
						wrqu.ap_addr.sa_family = ARPHRD_ETHER;
						PRINT_K("wireless_send_event--->SIOCGIWAP(disassociated)\n");
						wireless_send_event(pDevice->dev, SIOCGIWAP, &wrqu, NULL);
					}
				}
				pDevice->bwextstep0 = false;
				pDevice->bwextstep1 = false;
				pDevice->bwextstep2 = false;
				pDevice->bwextstep3 = false;
				pDevice->bWPASuppWextEnabled = false;
				//clear current SSID
				pItemSSID = (PWLAN_IE_SSID)pMgmt->abyCurrSSID;
				pItemSSID->len = 0;
				memset(pItemSSID->abySSID, 0, WLAN_SSID_MAXLEN);
				//clear desired SSID
				pItemSSID = (PWLAN_IE_SSID)pMgmt->abyDesireSSID;
				pItemSSID->len = 0;
				memset(pItemSSID->abySSID, 0, WLAN_SSID_MAXLEN);

				netif_stop_queue(pDevice->dev);
				CARDbRadioPowerOff(pDevice);
				MACvRegBitsOn(pDevice, MAC_REG_GPIOCTL1, GPIO3_INTMD);

				vnt_mac_set_led(pDevice, LEDSTS_STS, LEDSTS_OFF);

				pDevice->bHWRadioOff = true;
			} else {
				DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO" WLAN_CMD_RADIO_START_ON........................\n");
				pDevice->bHWRadioOff = false;
				CARDbRadioPowerOn(pDevice);
				MACvRegBitsOff(pDevice, MAC_REG_GPIOCTL1, GPIO3_INTMD);

				vnt_mac_set_led(pDevice, LEDSTS_STS, LEDSTS_ON);
			}
		}

		break;

	case WLAN_CMD_CHANGE_BBSENSITIVITY_START:

		pDevice->bStopDataPkt = true;
		pDevice->byBBVGACurrent = pDevice->byBBVGANew;
		BBvSetVGAGainOffset(pDevice, pDevice->byBBVGACurrent);
		DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"Change sensitivity pDevice->byBBVGACurrent = %x\n", pDevice->byBBVGACurrent);
		pDevice->bStopDataPkt = false;
		break;

	case WLAN_CMD_TBTT_WAKEUP_START:
		PSbIsNextTBTTWakeUp(pDevice);
		break;

	case WLAN_CMD_BECON_SEND_START:
		bMgrPrepareBeaconToSend(pDevice, pMgmt);
		break;

	case WLAN_CMD_SETPOWER_START:

		vnt_rf_setpower(pDevice, pDevice->wCurrentRate,
							pMgmt->uCurrChannel);

		break;

	case WLAN_CMD_CHANGE_ANTENNA_START:
		DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"Change from Antenna%d to", (int)pDevice->dwRxAntennaSel);
		if (pDevice->dwRxAntennaSel == 0) {
			pDevice->dwRxAntennaSel = 1;
			if (pDevice->bTxRxAntInv == true)
				BBvSetAntennaMode(pDevice, ANT_RXA);
			else
				BBvSetAntennaMode(pDevice, ANT_RXB);
		} else {
			pDevice->dwRxAntennaSel = 0;
			if (pDevice->bTxRxAntInv == true)
				BBvSetAntennaMode(pDevice, ANT_RXB);
			else
				BBvSetAntennaMode(pDevice, ANT_RXA);
		}
		break;

	case WLAN_CMD_REMOVE_ALLKEY_START:
		KeybRemoveAllKey(pDevice, &(pDevice->sKey), pDevice->abyBSSID);
		break;

	case WLAN_CMD_MAC_DISPOWERSAVING_START:
		vnt_control_in_u8(pDevice, MESSAGE_REQUEST_MACREG, MAC_REG_PSCTL, &byData);
		if ((byData & PSCTL_PS) != 0) {
			// disable power saving hw function
			vnt_control_out(pDevice,
					MESSAGE_TYPE_DISABLE_PS,
					0,
					0,
					0,
					NULL
					);
		}
		break;

	case WLAN_CMD_11H_CHSW_START:
		CARDbSetMediaChannel(pDevice, pDevice->byNewChannel);
		pDevice->bChannelSwitch = false;
		pMgmt->uCurrChannel = pDevice->byNewChannel;
		pDevice->bStopDataPkt = false;
		break;

	case WLAN_CMD_CONFIGURE_FILTER_START:
		vnt_configure_filter(pDevice);
		break;
	default:
		break;
	} //switch

	s_bCommandComplete(pDevice);

	return;
}

static int s_bCommandComplete(struct vnt_private *pDevice)
{
	struct vnt_manager *pMgmt = &pDevice->vnt_mgmt;
	PWLAN_IE_SSID pSSID;
	int bRadioCmd = false;
	int bForceSCAN = true;

	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:
			DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"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);
/*
			if ((bForceSCAN == false) && (pDevice->bLinkPass == true)) {
				if ((pSSID->len == ((PWLAN_IE_SSID)pMgmt->abyCurrSSID)->len) &&
				    ( !memcmp(pSSID->abySSID, ((PWLAN_IE_SSID)pMgmt->abyCurrSSID)->abySSID, pSSID->len))) {
					pDevice->eCommandState = WLAN_CMD_IDLE;
				}
			}
*/
			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(pMgmt->abyDesireSSID, pSSID, WLAN_IEHDR_LEN + WLAN_SSID_MAXLEN + 1);
			DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"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_CHANGE_BBSENSITIVITY_START;
			break;

		case WLAN_CMD_TBTT_WAKEUP:
			pDevice->eCommandState = WLAN_CMD_TBTT_WAKEUP_START;
			break;

		case WLAN_CMD_BECON_SEND:
			pDevice->eCommandState = WLAN_CMD_BECON_SEND_START;
			break;

		case WLAN_CMD_SETPOWER:
			pDevice->eCommandState = WLAN_CMD_SETPOWER_START;
			break;

		case WLAN_CMD_CHANGE_ANTENNA:
			pDevice->eCommandState = WLAN_CMD_CHANGE_ANTENNA_START;
			break;

		case WLAN_CMD_REMOVE_ALLKEY:
			pDevice->eCommandState = WLAN_CMD_REMOVE_ALLKEY_START;
			break;

		case WLAN_CMD_MAC_DISPOWERSAVING:
			pDevice->eCommandState = WLAN_CMD_MAC_DISPOWERSAVING_START;
			break;

		case WLAN_CMD_11H_CHSW:
			pDevice->eCommandState = WLAN_CMD_11H_CHSW_START;
			break;

		case WLAN_CMD_CONFIGURE_FILTER:
			pDevice->eCommandState =
						WLAN_CMD_CONFIGURE_FILTER_START;
			break;

		default:
			break;
		}
		vCommandTimerWait(pDevice, 0);
	}

	return true;
}

int bScheduleCommand(struct vnt_private *pDevice,
		CMD_CODE eCommand, u8 *pbyItem0)
{

	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:
			pDevice->eCmdQueue[pDevice->uCmdEnqueueIdx].bForceSCAN = false;
			memcpy(pDevice->eCmdQueue[pDevice->uCmdEnqueueIdx].abyCmdDesireSSID,
				pbyItem0, WLAN_IEHDR_LEN + WLAN_SSID_MAXLEN + 1);
			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_DEAUTH:
			pDevice->eCmdQueue[pDevice->uCmdEnqueueIdx].wDeAuthenReason = *((u16 *)pbyItem0);
			break;
*/

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

		default:
			break;
		}
	}

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

	if (pDevice->bCmdRunning == false)
		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
 *
 */
static int s_bClearBSSID_SCAN(struct vnt_private *pDevice)
{
	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(struct vnt_private *pDevice)
{
	cancel_delayed_work_sync(&pDevice->run_command_work);

	pDevice->cbFreeCmdQueue = CMD_Q_SIZE;
	pDevice->uCmdDequeueIdx = 0;
	pDevice->uCmdEnqueueIdx = 0;
	pDevice->eCommandState = WLAN_CMD_IDLE;
	pDevice->bCmdRunning = false;
	pDevice->bCmdClear = false;
}