/*
 ***************************************************************************
 * Ralink Tech Inc.
 * 4F, No. 2 Technology	5th	Rd.
 * Science-based Industrial	Park
 * Hsin-chu, Taiwan, R.O.C.
 *
 * (c) Copyright 2002-2006, Ralink Technology, Inc.
 *
 * All rights reserved.	Ralink's source	code is	an unpublished work	and	the
 * use of a	copyright notice does not imply	otherwise. This	source code
 * contains	confidential trade secret material of Ralink Tech. Any attemp
 * or participation	in deciphering,	decoding, reverse engineering or in	any
 * way altering	the	source code	is stricitly prohibited, unless	the	prior
 * written consent of Ralink Technology, Inc. is obtained.
 ***************************************************************************

	Module Name:
	wsc.c

	Abstract:

	Revision History:
	Who			When			What
	--------	----------		----------------------------------------------
	Paul Lin	06-08-08		Initial
	Snowpin Lee 06-09-12        Do modifications and Add APIs for AP
	Snowpin Lee 07-04-19        Do modifications and Add APIs for STA
	Snowpin Lee 07-05-17        Do modifications and Add APIs for AP Client
*/

#include    "rt_config.h"

#ifdef WSC_INCLUDED
#include    "wsc_tlv.h"
#ifdef LINUX
#include <net/iw_handler.h>
#endif // LINUX //

#define WSC_UPNP_MSG_TIMEOUT			(150 * OS_HZ)
#define RTMP_WSC_NLMSG_SIGNATURE_LEN	8

#ifndef PF_NOFREEZE
#define PF_NOFREEZE  0
#endif

char WSC_MSG_SIGNATURE[]={"RAWSCMSG"};

extern UCHAR   WPS_OUI[];

UINT8 WPS_DH_G_VALUE[1] = {0x02};
UINT8 WPS_DH_P_VALUE[192] = 
{
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
    0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
    0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
    0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
    0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
    0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
    0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
    0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
    0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
    0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
    0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
    0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
    0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
    0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
    0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
    0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A,
    0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
    0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96,
    0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
    0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
    0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
    0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x23, 0x73, 0x27,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
};

// General used field
#ifdef CONFIG_STA_SUPPORT
UCHAR	STA_Wsc_Pri_Dev_Type[8] = {0x00, 0x01, 0x00, 0x50, 0xf2, 0x04, 0x00, 0x01};
#endif // CONFIG_STA_SUPPORT //


BOOLEAN WscCheckNonce(
	IN	PRTMP_ADAPTER	pAdapter, 
	IN	MLME_QUEUE_ELEM	*Elem,
	IN  BOOLEAN         bFlag,
	IN  PWSC_CTRL       pWscControl);

VOID    WscEapActionDisabled(
    IN  PRTMP_ADAPTER       pAdapter,
    IN  PWSC_CTRL           pWscControl);

VOID    WscGetConfigErrFromNack(
    IN  PRTMP_ADAPTER       pAdapter,
    IN	MLME_QUEUE_ELEM	    *pElem);

INT	    WscSetAuthMode(
	IN	PRTMP_ADAPTER	pAd, 
	IN  UCHAR			apidx,
	IN	PSTRING			arg);

INT	    WscSetEncrypType(
	IN	PRTMP_ADAPTER	pAd, 
	IN  UCHAR			apidx,
	IN	PSTRING			arg);

VOID WscSendNACK(
	IN	PRTMP_ADAPTER	pAdapter,
	IN  MAC_TABLE_ENTRY *pEntry,
	IN  PWSC_CTRL       pWscControl);

static INT write_dat_file_thread (void *data);

#ifdef CONFIG_STA_SUPPORT
VOID WscLinkDown(
	IN	PRTMP_ADAPTER	pAd);
#endif // CONFIG_STA_SUPPORT //


/*
	Standard UUID generation procedure. The UUID format generated by this function is base on UUID std. version 1.
	It's a 16 bytes, one-time global unique number. and can show in string format like this:
			550e8400-e29b-41d4-a716-446655440000 
			
	The format of uuid is:
		uuid                        = <time_low> "-"
		                              <time_mid> "-"
		                              <time_high_and_version> "-"
		                              <clock_seq_high_and_reserved>
	    	                          <clock_seq_low> "-"
		                              <node>
		time_low                    = 4*<hex_octet>
		time_mid                    = 2*<hex_octet>
		time_high_and_version       = 2*<hex_octet>
		clock_seq_high_and_reserved = <hex_octet>
		clock_seq_low               = <hex_octet>
		node                        = 6*<hex_octet>
		hex_octet                   = <hex_digit> <hex_digit>
		hex_digit                   = "0"|"1"|"2"|"3"|"4"|"5"|"6"|"7"|"8"|"9"
		                              |"a"|"b"|"c"|"d"|"e"|"f"
		                              |"A"|"B"|"C"|"D"|"E"|"F"
	Note:
		Actually, to IOT with JumpStart, we fix the first 10 bytes of UUID string!!!!
*/
INT WscGenerateUUID(RTMP_ADAPTER *pAd, UCHAR *uuidHexStr, UCHAR *uuidAscStr, int apIdx)
{
	
	WSC_UUID_T uuid_t;
	unsigned long long uuid_time;
	int i;
	UINT16 clkSeq;
	char uuidTmpStr[UUID_LEN_STR+2];
	
	// Get the current time.
	uuid_time = 2860; //xtime.tv_sec; 	// Well, we fix this to make JumpStart  happy!
	uuid_time *= 10000000;
	uuid_time += 0x01b21dd213814000LL;
	
	uuid_t.timeLow = (UINT32)uuid_time & 0xFFFFFFFF;
	uuid_t.timeMid = (UINT16)((uuid_time >>32) & 0xFFFF);
	uuid_t.timeHi_Version = (UINT16)((uuid_time >> 48) & 0x0FFF);
	uuid_t.timeHi_Version |= (1 << 12);

	// Get the clock sequence.
	clkSeq = (UINT16)(0x0601/*jiffies*/ & 0xFFFF);		// Again, we fix this to make JumpStart happy!
	uuid_t.clockSeqLow = clkSeq & 0xFF;
	uuid_t.clockSeqHi_Var = (clkSeq & 0x3F00) >> 8;
	uuid_t.clockSeqHi_Var |= 0x80;


#ifdef CONFIG_STA_SUPPORT
	IF_DEV_CONFIG_OPMODE_ON_STA(pAd)
	{
		// copy the Mac address as the value of node
		NdisMoveMemory(&uuid_t.node[0], &pAd->CurrentAddress[0], sizeof(uuid_t.node));
	}
#endif // CONFIG_STA_SUPPORT //

	// Create the UUID ASCII string.
#if 0
	snprintf((PSTRING) uuidAscStr, UUID_LEN_STR, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", 
			(unsigned int)uuid_t.timeLow, uuid_t.timeMid, uuid_t.timeHi_Version, uuid_t.clockSeqHi_Var, uuid_t.clockSeqLow, 
			uuid_t.node[0], uuid_t.node[1], uuid_t.node[2], uuid_t.node[3], uuid_t.node[4], uuid_t.node[5]);
#else
	memset(uuidTmpStr, 0, sizeof(uuidTmpStr));
	sprintf(uuidTmpStr, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", 
			(unsigned int)uuid_t.timeLow, uuid_t.timeMid, uuid_t.timeHi_Version, uuid_t.clockSeqHi_Var, uuid_t.clockSeqLow, 
			uuid_t.node[0], uuid_t.node[1], uuid_t.node[2], uuid_t.node[3], uuid_t.node[4], uuid_t.node[5]);
	if (strlen(uuidTmpStr) > UUID_LEN_STR)
		DBGPRINT(RT_DEBUG_ERROR, ("ERROR:UUID String size too large!\n"));
	strncpy((PSTRING)uuidAscStr, uuidTmpStr, UUID_LEN_STR);
#endif

	// Create the UUID Hex format number
	uuid_t.timeLow = cpu2be32(uuid_t.timeLow);
	NdisMoveMemory(&uuidHexStr[0], &uuid_t.timeLow, 4);
	uuid_t.timeMid = cpu2be16(uuid_t.timeMid);
	NdisMoveMemory(&uuidHexStr[4], &uuid_t.timeMid, 2);
	uuid_t.timeHi_Version = cpu2be16(uuid_t.timeHi_Version);
	NdisMoveMemory(&uuidHexStr[6], &uuid_t.timeHi_Version, 2);
	NdisMoveMemory(&uuidHexStr[8], &uuid_t.clockSeqHi_Var, 1);
	NdisMoveMemory(&uuidHexStr[9], &uuid_t.clockSeqLow, 1);
	NdisMoveMemory(&uuidHexStr[10], &uuid_t.node[0], 6);

	DBGPRINT(RT_DEBUG_TRACE, ("The UUID Hex string is:"));
	for (i=0; i< 16; i++)
	{
		DBGPRINT(RT_DEBUG_TRACE, ("%02x", (uuidHexStr[i] & 0xff)));
	}
	DBGPRINT(RT_DEBUG_TRACE, ("\n"));
	DBGPRINT(RT_DEBUG_TRACE, ("The UUID ASCII string is:%s!\n", uuidAscStr));
	return 0;
}


/*  
	==========================================================================
	Description: 
		wps state machine init, including state transition and timer init
	Parameters: 
		S - pointer to the association state machine
	==========================================================================
 */
VOID    WscStateMachineInit(
	IN	PRTMP_ADAPTER		pAdapter, 
	IN	STATE_MACHINE		*S, 
	OUT	STATE_MACHINE_FUNC	Trans[])	
{
	PWSC_CTRL	pWScControl;
	StateMachineInit(S,	(STATE_MACHINE_FUNC*)Trans, MAX_WSC_STATE, MAX_WSC_MSG, (STATE_MACHINE_FUNC)Drop, WSC_IDLE, WSC_MACHINE_BASE);
	StateMachineSetAction(S, WSC_IDLE, WSC_EAPOL_PACKET_MSG, (STATE_MACHINE_FUNC)WscEAPAction);
	StateMachineSetAction(S, WSC_IDLE, WSC_EAPOL_UPNP_MSG, (STATE_MACHINE_FUNC)WscEAPAction);


#ifdef CONFIG_STA_SUPPORT
	IF_DEV_CONFIG_OPMODE_ON_STA(pAdapter)
	{
		pWScControl = &pAdapter->StaCfg.WscControl;
		pWScControl->EntryIfIdx = BSS0;
		WSC_TIMER_INIT(pAdapter, pWScControl, &pWScControl->EapolTimer, pWScControl->EapolTimerRunning, WscEAPOLTimeOutAction);
		WSC_TIMER_INIT(pAdapter, pWScControl, &pWScControl->Wsc2MinsTimer, pWScControl->Wsc2MinsTimerRunning, Wsc2MinsTimeOutAction);    
		WSC_TIMER_INIT(pAdapter, pAdapter, &pWScControl->WscPBCTimer, pWScControl->WscPBCTimerRunning, WscPBCTimeOutAction);
		WSC_TIMER_INIT(pAdapter, pAdapter, &pWScControl->WscScanTimer, pWScControl->WscScanTimerRunning, WscScanTimeOutAction);    
		WSC_TIMER_INIT(pAdapter, pAdapter, &pWScControl->WscProfileRetryTimer, pWScControl->WscProfileRetryTimerRunning, WscProfileRetryTimeout);  // add by johnli, fix WPS test plan 5.1.1

		// Create the timer used to trigger timeout handler when UPnP STATE MACHINE failed.
		WSC_TIMER_INIT(pAdapter, pWScControl, &pWScControl->WscUPnPNodeInfo.UPnPMsgTimer, pWScControl->WscUPnPNodeInfo.bUPnPMsgTimerRunning, WscUPnPMsgTimeOutAction);
		pWScControl->WscUPnPNodeInfo.bUPnPMsgTimerPending = FALSE;
		WSC_TIMER_INIT(pAdapter, pWScControl, &pWScControl->WscUPnPNodeInfo.UPnPM2DTimer, pWScControl->WscUPnPNodeInfo.bUPnPM2DTimerRunning, WscUPnPM2DTimeOutAction);
	    
#ifdef OLD_DH_KEY
		WSC_VMALLOC_KEY_MEM(pWScControl->pPubKeyMem, pWScControl->pSecKeyMem, 15*MAX_NN_DIGITS+1);
#endif //OLD_DH_KEY    
#ifdef WSC_LED_SUPPORT
		WSC_TIMER_INIT(pAdapter, pWScControl, &pWScControl->WscLEDTimer, pWScControl->WscLEDTimerRunning, WscLEDTimeOut);
		WSC_TIMER_INIT(pAdapter, pWScControl, &pWScControl->WscSkipTurnOffLEDTimer, pWScControl->WscSkipTurnOffLEDTimerRunning, WscSkipTurnOffLEDTimeOut);
#endif // WSC_LED_SUPPORT //
	}
#endif // CONFIG_STA_SUPPORT //
}

void WscUPnPM2DTimeOutAction(
    IN PVOID SystemSpecific1, 
    IN PVOID FunctionContext, 
    IN PVOID SystemSpecific2, 
    IN PVOID SystemSpecific3)
{
	/* For each state, we didn't care about the retry issue, we just send control message
		to notify the UPnP deamon that some error happened in STATE MACHINE.
	*/
	PWSC_CTRL pWscControl = (PWSC_CTRL)FunctionContext;
	PRTMP_ADAPTER pAd = (PRTMP_ADAPTER)pWscControl->pAd;
	WSC_UPNP_NODE_INFO	*pWscNodeInfo;
	BOOLEAN             Cancelled;
	
#ifdef CONFIG_STA_SUPPORT
	IF_DEV_CONFIG_OPMODE_ON_STA(pAd)
		pWscControl = &pAd->StaCfg.WscControl;
#endif // CONFIG_STA_SUPPORT //
	pWscNodeInfo = &pWscControl->WscUPnPNodeInfo;
	
	DBGPRINT(RT_DEBUG_TRACE, ("UPnP StateMachine TimeOut(State=%d!)\n", pWscControl->WscState));

	if(
#ifdef CONFIG_STA_SUPPORT
		((pWscNodeInfo->registrarID != 0) &&  (pAd->OpMode == OPMODE_STA)) ||
#endif // CONFIG_STA_SUPPORT //
		(0))
	{
		DBGPRINT(RT_DEBUG_TRACE, ("%s():pEntry maybe gone or already received M2 Packet!\n", __FUNCTION__));
		goto done;
	}
	
	if (pWscNodeInfo->M2DACKBalance != 0)
	{
		DBGPRINT(RT_DEBUG_TRACE, ("%s(): waiting for M2DACK balance, extend the time!\n", __FUNCTION__));
		// Waiting for M2DACK balance.
		RTMPModTimer(&pWscNodeInfo->UPnPM2DTimer, WSC_EAP_ID_TIME_OUT);
		return;
	}
	else
	{	
		RTMPCancelTimer(&pWscControl->EapolTimer, &Cancelled);
		pWscControl->EapolTimerRunning = FALSE;

		pWscControl->EapMsgRunning = FALSE;
		pWscControl->WscState = WSC_STATE_OFF;
    }
	
done:
	pWscNodeInfo->bUPnPM2DTimerRunning = FALSE;
	pWscNodeInfo->M2DACKBalance = 0;
	pWscNodeInfo->registrarID = 0;
		
	
}


VOID WscUPnPMsgTimeOutAction(
    IN PVOID SystemSpecific1, 
    IN PVOID FunctionContext, 
    IN PVOID SystemSpecific2, 
    IN PVOID SystemSpecific3)
{
	PWSC_CTRL pWscControl = (PWSC_CTRL)FunctionContext;
	PRTMP_ADAPTER pAd;
	WSC_UPNP_NODE_INFO	*pWscNodeInfo;

	DBGPRINT(RT_DEBUG_TRACE, ("-----> WscUPnPMsgTimeOutAction\n"));

	//It shouldn't happened!
	if (!pWscControl)
		return;
	
	pAd = (PRTMP_ADAPTER)pWscControl->pAd;
	pWscNodeInfo = &pWscControl->WscUPnPNodeInfo;
	
	DBGPRINT(RT_DEBUG_TRACE, ("UPnP StateMachine TimeOut(State=%d!)\n", pWscControl->WscState));

    if (pWscNodeInfo->bUPnPMsgTimerPending)
    {
#define WSC_UPNP_TIMER_PENDIND_WAIT	2000

        RTMPModTimer(&pWscNodeInfo->UPnPMsgTimer, WSC_UPNP_TIMER_PENDIND_WAIT);
        DBGPRINT(RT_DEBUG_TRACE, ("UPnPMsgTimer Pending......\n"));
	} 
	else
	{
		int dataLen;
		UCHAR *pWscData;

		if( (pWscData = kmalloc(WSC_MAX_DATA_LEN, GFP_ATOMIC)) != NULL)
		{
			memset(pWscData, 0, WSC_MAX_DATA_LEN);
			dataLen = BuildMessageNACK(pAd, pWscControl, pWscData);
			WscSendUPnPMessage(pAd, pWscControl->EntryIfIdx, 
									WSC_OPCODE_UPNP_DATA, WSC_UPNP_DATA_SUB_NORMAL, 
									pWscData, dataLen, 0, 0, &pAd->CurrentAddress[0]);
			kfree(pWscData);
		}
	
		pWscNodeInfo->bUPnPInProgress = FALSE;
		pWscNodeInfo->bUPnPMsgTimerPending = FALSE;
		pWscNodeInfo->bUPnPMsgTimerRunning = FALSE;
		pWscControl->WscState = WSC_STATE_OFF;
		pWscControl->WscStatus = STATUS_WSC_FAIL;
		if (pAd->CommonCfg.bWirelessEvent)
			RTMPSendWirelessEvent(pAd, IW_WSC_STATUS_FAIL, &pWscControl->EntryAddr[0], pWscControl->EntryIfIdx, 0);
    }

	DBGPRINT(RT_DEBUG_TRACE, ("<----- WscUPnPMsgTimeOutAction\n"));
		
}



/*
	==========================================================================
	Description:
		This is state machine function when receiving EAP packets 
		which is WPS Registration Protocol.

		There are two roles at our AP, as an 
		1. Enrollee		
		2. Internal Registrar
		3. Proxy

		There are two roles at our Station, as an 
		1. Enrollee		
		2. External Registrar

		Running Scenarios:
		-----------------------------------------------------------------
		1a. Adding an AP as an Enrollee to a station as an External Registrar (EAP)
			[External Registrar]<----EAP--->[Enrollee_AP]
		-----------------------------------------------------------------
		2a. Adding a station as an Enrollee to an AP with built-in Registrar (EAP)	
			[Registrar_AP]<----EAP--->[Enrollee_STA]
		-----------------------------------------------------------------
		3a. Adding an Enrollee with External Registrar (UPnP/EAP)	
			[External Registrar]<----UPnP--->[Proxy_AP]<---EAP--->[Enrollee_STA]  
		-----------------------------------------------------------------

	Return:
		None
	==========================================================================
*/
VOID WscEAPAction(
	IN	PRTMP_ADAPTER	pAdapter, 
	IN	MLME_QUEUE_ELEM	*Elem) 
{		
	UCHAR		MsgType;
	BOOLEAN		bUPnPMsg, Cancelled;
	MAC_TABLE_ENTRY	*pEntry = NULL;
	PWSC_CTRL				pWscControl = NULL;
	PWSC_UPNP_NODE_INFO	pWscUPnPNodeInfo = NULL;
	
	DBGPRINT(RT_DEBUG_TRACE, ("-----> WscEAPAction\n"));


#ifdef DBG
    hex_dump("(WscEAPAction)Elem->MsgLen", Elem->Msg, Elem->MsgLen);
#endif /* DBG */

	MsgType = WscRxMsgType(pAdapter, Elem);
	bUPnPMsg = Elem->MsgType == WSC_EAPOL_UPNP_MSG ? TRUE : FALSE;

			
	
#ifdef CONFIG_STA_SUPPORT
	IF_DEV_CONFIG_OPMODE_ON_STA(pAdapter)
	{
		pWscControl = &pAdapter->StaCfg.WscControl;
		pWscUPnPNodeInfo = &pWscControl->WscUPnPNodeInfo;
		pWscUPnPNodeInfo->bUPnPMsgTimerPending = TRUE;
	}
#endif // CONFIG_STA_SUPPORT //

	pWscControl->EapolTimerPending = TRUE;
	

	if (MsgType == WSC_MSG_EAP_REQ_ID)
	{
		// Receive EAP_Req/Identity from WPS AP or WCN
		DBGPRINT(RT_DEBUG_TRACE, ("Receive EAP_Req/Identity from WPS AP or WCN\n"));
		if (bUPnPMsg && (pWscControl->WscConfMode == WSC_ENROLLEE))
		{
			pWscControl->WscActionMode = WSC_ENROLLEE;
			pWscControl->WscUseUPnP = 1;
			WscEapEnrolleeAction(pAdapter, Elem, WSC_MSG_EAP_REQ_START, pEntry, pWscControl);
		}
		else
		{
#ifdef CONFIG_STA_SUPPORT
			IF_DEV_CONFIG_OPMODE_ON_STA(pAdapter)
			{
				// Receive EAP_Req/Identity from WPS AP
				WscSendEapRspId(pAdapter, NULL, pWscControl); 
			}
#endif // CONFIG_STA_SUPPORT //            
		}
        
		if (!bUPnPMsg)
		{
			if ((pWscControl->WscState < WSC_STATE_WAIT_M1) ||
				(pWscControl->WscState > WSC_STATE_WAIT_DONE))
			{
				if (pWscControl->WscConfMode == WSC_REGISTRAR)
					pWscControl->WscState = WSC_STATE_WAIT_M1;
				else
					pWscControl->WscState = WSC_STATE_WAIT_WSC_START;
			}
		}
	}
	else if (MsgType == WSC_MSG_EAP_REQ_START)
	{
		DBGPRINT(RT_DEBUG_TRACE, ("Receive EAP_Req(Wsc_Start) from WPS AP\n"));

		// Receive EAP_Req(Wsc_Start) from WPS AP        
		if (pWscControl->WscConfMode == WSC_ENROLLEE)
		{
			pWscControl->WscActionMode = WSC_ENROLLEE;
			pWscControl->WscUseUPnP = bUPnPMsg ? 1 : 0;
			WscEapEnrolleeAction(pAdapter, Elem, WSC_MSG_EAP_REQ_START, pEntry, pWscControl);

			if (!pWscControl->EapolTimerRunning)
			{
				pWscControl->EapolTimerRunning = TRUE;
				RTMPSetTimer(&pWscControl->EapolTimer, WSC_EAP_ID_TIME_OUT);
			}
		}
		else
			DBGPRINT(RT_DEBUG_TRACE, ("Ignore EAP_Req(Wsc_Start) from WPS AP\n"));
	}
	else if (MsgType == WSC_MSG_EAP_FAIL)
	{
		// Receive EAP_Fail from WPS AP
		DBGPRINT(RT_DEBUG_TRACE, ("Receive EAP_Fail from WPS AP\n"));
		if (pWscControl->WscState >= WSC_STATE_WAIT_EAPFAIL)
		{
#ifdef WSC_LED_SUPPORT
			UCHAR WPSLEDStatus;
#endif // WSC_LED_SUPPORT //

			pWscControl->WscState = WSC_STATE_OFF;
#ifdef CONFIG_STA_SUPPORT            
			IF_DEV_CONFIG_OPMODE_ON_STA(pAdapter)
			{
				pWscControl->WscConfMode = WSC_DISABLE;
				WscLinkDown(pAdapter);
			}
#endif // CONFIG_STA_SUPPORT //
#ifdef WSC_LED_SUPPORT
			// The protocol is finished.
			WPSLEDStatus = LED_WPS_SUCCESS;
#ifdef RTMP_MAC_USB
			RTUSBEnqueueInternalCmd(pAdapter, RT_CMD_SET_WPS_LED_STATUS, &WPSLEDStatus, sizeof(WPSLEDStatus));
#endif // RTMP_MAC_USB //
#ifdef RTMP_MAC_PCI
			RTMPSetLED(pAdapter, WPSLEDStatus);
#endif // RTMP_MAC_PCI //
#endif // WSC_LED_SUPPORT //
		}
		else if (pWscControl->WscState == WSC_STATE_RX_M2D)
		{
			// Wait M2;
		}
		else if (pWscControl->WscState <= WSC_STATE_WAIT_REQ_ID)
		{
			// Ignore. D-Link DIR-628 AP sometimes would send EAP_Fail to station after Link UP first then send EAP_Req/Identity.
		}
		else
		{
			pWscControl->WscStatus = STATUS_WSC_FAIL;	
			if (pAdapter->CommonCfg.bWirelessEvent)
				RTMPSendWirelessEvent(pAdapter, IW_WSC_STATUS_FAIL, &pWscControl->EntryAddr[0], pWscControl->EntryIfIdx, 0);

			// Change the state to next one
			pWscControl->WscState = WSC_STATE_OFF;
#ifdef CONFIG_STA_SUPPORT
			IF_DEV_CONFIG_OPMODE_ON_STA(pAdapter)
			{
				WscLinkDown(pAdapter);
			}
#endif // CONFIG_STA_SUPPORT //
		}
	}
	else if (MsgType == WSC_MSG_M1)
	{
		UINT32 rv = 0;

		if ((pWscControl->WscConfMode & WSC_REGISTRAR) != 0 &&
			pWscControl->bWscTrigger)
		{
			pWscControl->WscActionMode = WSC_REGISTRAR;
			// If Message is from EAP, but UPnP Registrar is in progress now, ignore EAP_M1
			if (!bUPnPMsg && pWscControl->WscUPnPNodeInfo.bUPnPInProgress)
			{
				WscEapActionDisabled(pAdapter, pWscControl);
				DBGPRINT(RT_DEBUG_TRACE, ("UPnP Registrar is working now, ignore EAP M1.\n"));
				goto out;
			}
			else
				WscEapRegistrarAction(pAdapter, Elem, MsgType, pEntry, pWscControl);
			rv = 1;
		}
#ifdef CONFIG_STA_SUPPORT
		else
		{
			if ((rv == 0) && (pAdapter->OpMode == OPMODE_STA))
			{
				WscEapActionDisabled(pAdapter, pWscControl);
				DBGPRINT(RT_DEBUG_TRACE, ("(Line:%d)Ignore EAP M1.\n", __LINE__));
				goto out;
			}
		}
#endif // CONFIG_STA_SUPPORT //
	}
	else if (MsgType == WSC_MSG_M3 ||
             MsgType == WSC_MSG_M5 ||
             MsgType == WSC_MSG_M7 ||
             MsgType == WSC_MSG_WSC_DONE)
	{
        BOOLEAN bNonceMatch = WscCheckNonce(pAdapter, Elem, TRUE, pWscControl);
		if (((pWscControl->WscConfMode & WSC_REGISTRAR) != 0) &&
			pWscControl->bWscTrigger &&
              bNonceMatch)
		{
			// If Message is from EAP, but UPnP Registrar is in progress now, ignore EAP Messages
			if (!bUPnPMsg && pWscControl->WscUPnPNodeInfo.bUPnPInProgress)
			{
				WscEapActionDisabled(pAdapter, pWscControl);
				DBGPRINT(RT_DEBUG_TRACE, ("UPnP Registrar is working now, ignore EAP Messages.\n"));
				goto out;
			}
			else
			{
				pWscControl->WscActionMode = WSC_REGISTRAR;
				WscEapRegistrarAction(pAdapter, Elem, MsgType, pEntry, pWscControl);
			}
		}
	}
	else if (MsgType == WSC_MSG_M2 ||
			MsgType == WSC_MSG_M2D ||
			MsgType == WSC_MSG_M4 ||
			MsgType == WSC_MSG_M6 ||
			MsgType == WSC_MSG_M8)
	{
        BOOLEAN bNonceMatch = WscCheckNonce(pAdapter, Elem, FALSE, pWscControl);
		
		if (((pWscControl->WscConfMode & WSC_ENROLLEE) != 0) &&              
			((pAdapter->OpMode == OPMODE_AP) || ((pAdapter->OpMode == OPMODE_STA) && pWscControl->bWscTrigger)) &&               
			bNonceMatch)		
		{
			pWscControl->WscActionMode = WSC_ENROLLEE;
			pWscControl->WscUseUPnP = bUPnPMsg ? 1 : 0;
			if (MsgType == WSC_MSG_M2)
			{

				pWscControl->WscPinCodeLen = pWscControl->WscEnrolleePinCodeLen;
				WscGetRegDataPIN(pAdapter, pWscControl->WscEnrolleePinCode, pWscControl);
				DBGPRINT(RT_DEBUG_TRACE, ("WscEnrolleePinCode: %08u\n", pWscControl->WscEnrolleePinCode));
			}
			// If Message is from EAP, but UPnP Registrar is in progress now, ignore EAP Messages
			if (!bUPnPMsg && pWscControl->WscUPnPNodeInfo.bUPnPInProgress)
			{
				WscEapActionDisabled(pAdapter, pWscControl);
				DBGPRINT(RT_DEBUG_TRACE, ("UPnP Registrar is working now, ignore EAP Messages.\n"));
				goto out;
			}
			else
				WscEapEnrolleeAction(pAdapter, Elem, MsgType, pEntry, pWscControl);
		}
	}
	else if (MsgType == WSC_MSG_WSC_ACK)
	{
		if (((pWscControl->WscConfMode & WSC_REGISTRAR) != 0) && 
			pWscControl->bWscTrigger &&
			pWscControl->WscState <= WSC_STATE_SENT_M2D)
		{
			if (WscCheckNonce(pAdapter, Elem, TRUE, pWscControl))
			{
				pWscControl->WscActionMode = WSC_REGISTRAR;
				pWscControl->WscState = WSC_STATE_WAIT_PIN;
				pWscControl->WscStatus = STATUS_WSC_WAIT_PIN_CODE;
			}
		}
		else
		{
			if (((pWscControl->WscConfMode & WSC_ENROLLEE) != 0) && 
				WscCheckNonce(pAdapter, Elem, FALSE, pWscControl))
			{
				pWscControl->WscActionMode = WSC_ENROLLEE;
				pWscControl->WscUseUPnP = bUPnPMsg ? 1 : 0;
				WscEapEnrolleeAction(pAdapter, Elem, MsgType, pEntry, pWscControl);
			}
		}
	}
	else if (MsgType == WSC_MSG_WSC_NACK)
	{
		if (!bUPnPMsg &&
			(WscCheckNonce(pAdapter, Elem, FALSE, pWscControl) || WscCheckNonce(pAdapter, Elem, TRUE, pWscControl)))
		{
			DBGPRINT(RT_DEBUG_TRACE, ("Receive NACK from WPS client.\n"));
			WscGetConfigErrFromNack(pAdapter, Elem);
			/*
			If a PIN authentication or communication error occurs after sending message M6, 
			the Registrar MUST warn the user and MUST NOT automatically reuse the PIN. 
			Furthermore, if the Registrar detects this situation and prompts the user for a new PIN from the Enrollee device, 
			it MUST NOT accept the same PIN again without warning the user of a potential attack.
			*/
			if (pWscControl->WscState >= WSC_STATE_WAIT_M5)
			{
				pWscControl->WscRejectSamePinFromEnrollee = TRUE;
				pWscControl->WscPinCode = 0;
			}

			if (pWscControl->WscState == WSC_STATE_WAIT_M8)
			{
				// Some external sta will send NACK when AP is configured.
				// bWscTrigger should be set FALSE, otherwise Proxy will send NACK to enrollee.
				pWscControl->bWscTrigger = FALSE;
				pWscControl->WscStatus = STATUS_WSC_CONFIGURED;
				if (pAdapter->CommonCfg.bWirelessEvent)
					RTMPSendWirelessEvent(pAdapter, IW_WSC_STATUS_SUCCESS, &pWscControl->EntryAddr[0], pWscControl->EntryIfIdx, 0);			
			}
			else
			{
				pWscControl->WscStatus = STATUS_WSC_FAIL;
				if (pAdapter->CommonCfg.bWirelessEvent)
					RTMPSendWirelessEvent(pAdapter, IW_WSC_STATUS_FAIL, &pWscControl->EntryAddr[0], pWscControl->EntryIfIdx, 0);			
			}

			RTMPCancelTimer(&pWscControl->EapolTimer, &Cancelled);
			pWscControl->WscState = WSC_STATE_FAIL;
			
			pWscControl->RegData.ReComputePke = 1;
		}
	}
	else
	{
		DBGPRINT(RT_DEBUG_TRACE, ("Unsupported Msg Type (%02X)\n", MsgType));
        pWscControl->WscStatus = STATUS_WSC_FAIL;
        pWscControl->RegData.SelfInfo.ConfigError = WSC_ERROR_NO_ERROR;
        WscSendNACK(pAdapter, pEntry, pWscControl);
		if (pAdapter->CommonCfg.bWirelessEvent)
			RTMPSendWirelessEvent(pAdapter, IW_WSC_STATUS_FAIL, &pWscControl->EntryAddr[0], BSS0, 0);
		goto out;
	}

	if (bUPnPMsg)
	{
		// Messages from UPnP
		if (pWscUPnPNodeInfo->bUPnPMsgTimerRunning)
			RTMPModTimer(&pWscUPnPNodeInfo->UPnPMsgTimer, WSC_UPNP_MSG_TIME_OUT);
	}
	else
	{
		// Messages from EAP
		RTMPModTimer(&pWscControl->EapolTimer, WSC_EAP_MSG_TIME_OUT);
		pWscControl->EapolTimerRunning = TRUE;
	}
	
	if (bUPnPMsg && pWscControl->EapolTimerRunning)
	{   
		{
			RTMPCancelTimer(&pWscControl->EapolTimer, &Cancelled);
			pWscControl->EapolTimerRunning = FALSE;
		}
	}

out:
	if (bUPnPMsg)
		pWscUPnPNodeInfo->bUPnPMsgTimerPending = FALSE;
	
	pWscControl->EapolTimerPending = FALSE;
	
	DBGPRINT(RT_DEBUG_TRACE, ("<----- WscEAPAction\n"));
}

/*
	============================================================================
	Enrollee			Enrollee			Enrollee	
	============================================================================	
*/
VOID WscEapEnrolleeAction(
	IN	PRTMP_ADAPTER	pAdapter, 
	IN	MLME_QUEUE_ELEM	*Elem,
	IN  UCHAR	        MsgType,
	IN  MAC_TABLE_ENTRY *pEntry,
	IN  PWSC_CTRL       pWscControl)
{
    INT     DataLen = 0, rv = 0, DH_Len = 0;
	UCHAR   OpCode, bssIdx;
    PUCHAR  WscData = NULL;
    BOOLEAN bUPnPMsg, bUPnPStatus = FALSE, Cancelled;
	WSC_UPNP_NODE_INFO *pWscUPnPInfo = &pWscControl->WscUPnPNodeInfo;

    DBGPRINT(RT_DEBUG_TRACE, ("WscEapEnrolleeAction Enter!\n"));

	bUPnPMsg = Elem->MsgType == WSC_EAPOL_UPNP_MSG ? TRUE : FALSE;
	OpCode = bUPnPMsg ? WSC_OPCODE_UPNP_MASK : 0;
	bssIdx = 0;

	DBGPRINT(RT_DEBUG_TRACE, ("MsgType=0x%x, WscState=%d, bUPnPMsg=%d!\n", MsgType, pWscControl->WscState, bUPnPMsg));

	if (bUPnPMsg)
	{
#ifdef CONFIG_STA_SUPPORT
		if ((MsgType ==WSC_MSG_EAP_REQ_START) &&  (pAdapter->OpMode == OPMODE_STA))
		{
			//let it pass
		} else 
	#endif // CONFIG_STA_SUPPORT //
		if(MsgType ==WSC_MSG_M2 && pWscUPnPInfo->bUPnPInProgress == FALSE)
		{
			pWscUPnPInfo->bUPnPInProgress = TRUE;
			// Set the WscState as "WSC_STATE_WAIT_RESP_ID" because UPnP start from this state.
			// pWscControl->WscState = WSC_STATE_WAIT_RESP_ID;
			RTMPSetTimer(&pWscUPnPInfo->UPnPMsgTimer, WSC_UPNP_MSG_TIME_OUT);
			pWscUPnPInfo->bUPnPMsgTimerRunning = TRUE;
		}
		else 
		{
			// For other messages, we must make sure pWscUPnPInfo->bUPnPInProgress== TRUE
			if (pWscUPnPInfo->bUPnPInProgress == FALSE)
			{
				goto Done;
			}
		}
	}

	if( (WscData = kmalloc(WSC_MAX_DATA_LEN, GFP_ATOMIC)) == NULL)
	{
		DBGPRINT(RT_DEBUG_TRACE, ("WscData Allocate failed!\n"));
		goto Fail;
	}
	memset(WscData, 0, WSC_MAX_DATA_LEN);

	switch (MsgType)
	{
		case WSC_MSG_EAP_REQ_START:
			if (MsgType == WSC_MSG_EAP_REQ_START)
				DBGPRINT(RT_DEBUG_TRACE, ("WscEapEnrolleeAction : Rx Wsc_Start\n"));
			
			if (pWscControl->RegData.ReComputePke == 1)
			{
#ifdef OLD_DH_ALGORITHM
				GenerateDHPublicKey(pWscControl->pPubKeyMem, pWscControl->RegData.EnrolleeRandom, 192, pWscControl->RegData.Pke, &DH_Len);
#else
                DH_Len = sizeof(pWscControl->RegData.Pke);
            	DH_PublicKey_Generate (
                    WPS_DH_G_VALUE, sizeof(WPS_DH_G_VALUE),
            	    WPS_DH_P_VALUE, sizeof(WPS_DH_P_VALUE),
            	    pWscControl->RegData.EnrolleeRandom, sizeof(pWscControl->RegData.EnrolleeRandom),
            	    pWscControl->RegData.Pke, (UINT *) &DH_Len);
#endif
				pWscControl->RegData.ReComputePke = 0;
			}

			OpCode |= WSC_OPCODE_MSG;
            
			DataLen = BuildMessageM1(pAdapter, pWscControl, WscData);
			if(!bUPnPMsg)
			{
				pWscControl->EapMsgRunning = TRUE;
                pWscControl->WscStatus = STATUS_WSC_EAP_M1_SENT;
			}
            else
                // Sometime out-of-band registrars (ex: Vista) get M1 for collecting information of device.
                pWscControl->WscStatus = STATUS_WSC_IDLE;
            
			// Change the state to next one
			if (pWscControl->WscState < WSC_STATE_SENT_M1)
		        pWscControl->WscState = WSC_STATE_SENT_M1;
			break;
			
		case WSC_MSG_M2:
			DBGPRINT(RT_DEBUG_TRACE, ("WscEapEnrolleeAction : Rx M2\n"));
            
			// Receive M2, if we are at WSC_STATE_WAIT_M2 start, process it immediately
			if (pWscControl->WscState == WSC_STATE_SENT_M1 ||
				pWscControl->WscState == WSC_STATE_RX_M2D)
			{
					// Process M2
					pWscControl->WscStatus = STATUS_WSC_EAP_M2_RECEIVED;
					if ((rv = ProcessMessageM2(pAdapter, Elem->Msg, Elem->MsgLen, pWscControl->EntryIfIdx, &pWscControl->RegData)))
					{
							goto Fail;
					}
					else
					{
						OpCode |= WSC_OPCODE_MSG;
						DataLen = BuildMessageM3(pAdapter, pWscControl, WscData);
						pWscControl->WscStatus = STATUS_WSC_EAP_M3_SENT;

						// Change the state to next one
						pWscControl->WscState = WSC_STATE_WAIT_M4;
					}
			}
			break;
			
		case WSC_MSG_M2D:
			DBGPRINT(RT_DEBUG_TRACE, ("WscEapEnrolleeAction : Rx M2D\n"));
            
			// Receive M2D, if we are at WSC_STATE_WAIT_M2 start, process it immediately
			if (pWscControl->WscState == WSC_STATE_SENT_M1 ||
				pWscControl->WscState == WSC_STATE_RX_M2D)
			{
				if ((rv = ProcessMessageM2D(pAdapter, Elem->Msg, Elem->MsgLen, &pWscControl->RegData)))
					goto Fail;

				pWscControl->WscStatus = STATUS_WSC_EAP_M2D_RECEIVED;
				
				if (pAdapter->OpMode == OPMODE_AP)
				{
					/* For VISTA SP1 internal registrar test */
					OpCode |= WSC_OPCODE_NACK;
					DataLen = BuildMessageNACK(pAdapter, pWscControl, WscData);
				}
				else
				{
					// When external registrar is Marvell station, 
					// wps station sends NACK may confuse or reset Marvell wps state machine.
					OpCode |= WSC_OPCODE_ACK;
					DataLen = BuildMessageACK(pAdapter, pWscControl, WscData);
				}

				// Change the state to next one
				pWscControl->WscState = WSC_STATE_RX_M2D;
			}
			break;

		case WSC_MSG_M4: 
			DBGPRINT(RT_DEBUG_TRACE, ("WscEapEnrolleeAction : Rx M4\n"));

			// Receive M4, if we are at WSC_STATE_WAIT_M4 start, process it immediately
			if (pWscControl->WscState == WSC_STATE_WAIT_M4)
			{       
				// Process M4
				pWscControl->WscStatus = STATUS_WSC_EAP_M4_RECEIVED;
				if ((rv = ProcessMessageM4(pAdapter, pWscControl, Elem->Msg, Elem->MsgLen, &pWscControl->RegData)))
					goto Fail;
				else
				{
					OpCode |= WSC_OPCODE_MSG;
					DataLen = BuildMessageM5(pAdapter, pWscControl, WscData);
					pWscControl->WscStatus = STATUS_WSC_EAP_M5_SENT;

					// Change the state to next one
					pWscControl->WscState = WSC_STATE_WAIT_M6;
				}
			}
			break;

		case WSC_MSG_M6:
			DBGPRINT(RT_DEBUG_TRACE, ("WscEapEnrolleeAction : Rx M6\n"));

			// Receive M6, if we are at WSC_STATE_WAIT_M6 start, process it immediately
			if (pWscControl->WscState == WSC_STATE_WAIT_M6)
			{      
				// Process M6
				pWscControl->WscStatus = STATUS_WSC_EAP_M6_RECEIVED;
				if ((rv=ProcessMessageM6(pAdapter, pWscControl, Elem->Msg, Elem->MsgLen, &pWscControl->RegData)))
					goto Fail;
				else
				{
					OpCode |= WSC_OPCODE_MSG;

					DataLen = BuildMessageM7(pAdapter, pWscControl, WscData);
					pWscControl->WscStatus = STATUS_WSC_EAP_M7_SENT;

					// Change the state to next one
					pWscControl->WscState = WSC_STATE_WAIT_M8;
				}
			}
			break;

		case WSC_MSG_M8:
			DBGPRINT(RT_DEBUG_TRACE, ("WscEapEnrolleeAction : Rx M8\n"));

			// Receive M8, if we are at WSC_STATE_WAIT_M6 start, process it immediately
			if (pWscControl->WscState == WSC_STATE_WAIT_M8)
			{
				// Process M8
				pWscControl->WscStatus = STATUS_WSC_EAP_M8_RECEIVED;
				if ((rv=ProcessMessageM8(pAdapter, Elem->Msg, Elem->MsgLen, pWscControl)))
					goto Fail;
				else
				{
					OpCode |= WSC_OPCODE_DONE;
					DataLen = BuildMessageDONE(pAdapter, pWscControl, WscData);


#ifdef CONFIG_STA_SUPPORT
					IF_DEV_CONFIG_OPMODE_ON_STA(pAdapter)
					{
						pWscControl->WscState = WSC_STATE_WAIT_EAPFAIL;
						pWscControl->WscStatus = STATUS_WSC_EAP_RSP_DONE_SENT;
					}
#endif // CONFIG_STA_SUPPORT //
				}
			}
			break;	


		default:
			DBGPRINT(RT_DEBUG_TRACE, ("WscEapEnrolleeAction : Unsupported Msg Type\n"));
			break;
	}
	
	if (bUPnPMsg)
	{
		if ((MsgType == WSC_MSG_M8) && (pWscControl->WscState == WSC_STATE_WAIT_ACK))
		{
			pWscControl->EapMsgRunning = FALSE;
			//pWscControl->EntryIfIdx = WSC_INIT_ENTRY_APIDX;
			pWscControl->WscState = WSC_STATE_CONFIGURED;
			pWscControl->WscStatus = STATUS_WSC_CONFIGURED;
			if(pWscUPnPInfo->bUPnPMsgTimerRunning == TRUE)
			{
				RTMPCancelTimer(&pWscUPnPInfo->UPnPMsgTimer, &Cancelled);
				pWscUPnPInfo->bUPnPMsgTimerRunning = FALSE;
			}
			pWscUPnPInfo->bUPnPInProgress = FALSE;
			pWscUPnPInfo->registrarID = 0;
		}
	}
	else
	{
		if ((MsgType == WSC_MSG_WSC_ACK) && (pWscControl->WscState == WSC_STATE_CONFIGURED))
		{
			RTMPCancelTimer(&pWscControl->EapolTimer, &Cancelled);
			pWscControl->EapolTimerRunning = FALSE;
			pWscControl->EapMsgRunning = FALSE;
			NdisZeroMemory(pWscControl->EntryAddr, MAC_ADDR_LEN);
		}
	}
	
	if(OpCode > WSC_OPCODE_UPNP_MASK)
		bUPnPStatus = WscSendUPnPMessage(pAdapter, pWscControl->EntryIfIdx, WSC_OPCODE_UPNP_DATA, 
											WSC_UPNP_DATA_SUB_NORMAL, WscData, DataLen, 
											Elem->TimeStamp.u.LowPart, Elem->TimeStamp.u.HighPart, 
											&pAdapter->CurrentAddress[0]);
	else if(OpCode > 0 && OpCode < WSC_OPCODE_UPNP_MASK)
	{   
		if (pWscControl->WscState != WSC_STATE_CONFIGURED)
		{

#ifdef CONFIG_STA_SUPPORT
			IF_DEV_CONFIG_OPMODE_ON_STA(pAdapter)
				WscSendMessage(pAdapter, OpCode, WscData, DataLen, pWscControl, STA_MODE);        
#endif // CONFIG_STA_SUPPORT //
		}
	}
	else
		bUPnPStatus = TRUE;
	
Fail:
    DBGPRINT(RT_DEBUG_TRACE, ("WscEapEnrolleeAction : rv = %d\n", rv));
    if (rv)
    {          
        switch(rv)
        {
            case WSC_ERROR_DEV_PWD_AUTH_FAIL:
                pWscControl->WscStatus = STATUS_WSC_ERROR_DEV_PWD_AUTH_FAIL;
                pWscControl->RegData.SelfInfo.ConfigError = WSC_ERROR_DEV_PWD_AUTH_FAIL;
                break;
            default:
                pWscControl->WscStatus = STATUS_WSC_FAIL;
                pWscControl->RegData.SelfInfo.ConfigError = WSC_ERROR_DECRYPT_CRC_FAIL;
				if (pAdapter->CommonCfg.bWirelessEvent)
					RTMPSendWirelessEvent(pAdapter, IW_WSC_STATUS_FAIL, &pWscControl->EntryAddr[0], pWscControl->EntryIfIdx, 0);
                break;
        }
		if (bUPnPMsg)
		{
			if (pWscUPnPInfo->bUPnPMsgTimerRunning == TRUE)
			{
				RTMPCancelTimer(&pWscUPnPInfo->UPnPMsgTimer, &Cancelled);
				pWscUPnPInfo->bUPnPMsgTimerRunning = FALSE;
			}
			pWscUPnPInfo->bUPnPInProgress = FALSE;
		}
		else
			WscSendNACK(pAdapter, pEntry, pWscControl);


#ifdef CONFIG_STA_SUPPORT
		IF_DEV_CONFIG_OPMODE_ON_STA(pAdapter)
		{
	       	pWscControl->WscState = WSC_STATE_WAIT_DISCONN;
			if (pWscControl->WscSsid.SsidLength)
			{
				pAdapter->MlmeAux.AutoReconnectSsidLen = pWscControl->WscSsid.SsidLength;
				NdisZeroMemory(&pAdapter->MlmeAux.AutoReconnectSsid[0], MAX_LEN_OF_SSID);
				NdisMoveMemory(&pAdapter->MlmeAux.AutoReconnectSsid[0], 
							   &pWscControl->WscSsid.Ssid[0], 
							   pWscControl->WscSsid.SsidLength);
			}
			else
				pAdapter->MlmeAux.AutoReconnectSsidLen = 0;
 		}
#endif // CONFIG_STA_SUPPORT //

		NdisZeroMemory(pWscControl->EntryAddr, MAC_ADDR_LEN);
        pWscControl->WscMode = 1;

        bUPnPStatus = FALSE;
    }

Done:
	if(WscData)
		kfree(WscData);
	if(bUPnPMsg && (bUPnPStatus == FALSE))
		WscUPnPErrHandle(pAdapter, pWscControl, Elem->TimeStamp.u.LowPart);
		
	rv = 0;

#ifdef CONFIG_STA_SUPPORT
	if (pAdapter->OpMode == OPMODE_STA)
	{
		if ((pWscControl->WscState == WSC_STATE_WAIT_EAPFAIL) ||
			(pWscControl->WscState == WSC_STATE_CONFIGURED))
		{
    		rv = 1;
		}
	}
#endif // CONFIG_STA_SUPPORT //

	if (rv == 1)
	{
		pWscControl->bWscTrigger = FALSE;
        pWscControl->RegData.ReComputePke = 1;
		RTMPCancelTimer(&pWscControl->EapolTimer, &Cancelled);
		if (pWscControl->Wsc2MinsTimerRunning)
		{
			pWscControl->Wsc2MinsTimerRunning = FALSE;
			RTMPCancelTimer(&pWscControl->Wsc2MinsTimer, &Cancelled);
		}
		if (pWscControl->WscConfStatus == WSC_SCSTATE_UNCONFIGURED)
		{
			pAdapter->WriteWscCfgToDatFile = TRUE;
			pWscControl->WscStatus = STATUS_WSC_CONFIGURED;
			pWscControl->WscConfStatus = WSC_SCSTATE_CONFIGURED;
			pWscControl->WscMode = 1;
			if (pAdapter->CommonCfg.bWirelessEvent)
	   			 RTMPSendWirelessEvent(pAdapter, IW_WSC_STATUS_SUCCESS, &pWscControl->EntryAddr[0], pWscControl->EntryIfIdx, 0);


#ifdef CONFIG_STA_SUPPORT
			IF_DEV_CONFIG_OPMODE_ON_STA(pAdapter)
			{
				pWscControl->WscConfMode = WSC_DISABLE;
				if (bUPnPMsg)
				{
					pWscControl->WscState = WSC_STATE_OFF;
					WscLinkDown(pAdapter);
				}
				if (pWscControl->WscDriverAutoConnect)
				{
					pAdapter->StaCfg.bAutoConnectByBssid = TRUE;
					pWscControl->WscProfile.ApplyProfileIdx = 0;  // add by johnli, fix WPS test plan 5.1.1
					WscWriteConfToPortCfg(pAdapter, pWscControl, &pWscControl->WscProfile.Profile[0], TRUE);
#ifdef KTHREAD_SUPPORT
					WAKE_UP(&(pAdapter->wscCfgWriteTask));
#else
					RTMP_SEM_EVENT_UP(&(pAdapter->wscCfgWriteTask.taskSema));
#endif
				}
			}
#endif // CONFIG_STA_SUPPORT //

		}
	}
}


/*
	============================================================================
	Registrar			Registrar			Registrar			
	============================================================================	
*/	
VOID WscEapRegistrarAction(
	IN	PRTMP_ADAPTER	pAdapter, 
	IN	MLME_QUEUE_ELEM	*Elem,
	IN  UCHAR	        MsgType,
	IN  MAC_TABLE_ENTRY *pEntry,
	IN  PWSC_CTRL       pWscControl)
{
	INT     DataLen = 0, rv = 0;
	UCHAR   OpCode = 0;
	UCHAR   *WscData = NULL;    
	BOOLEAN bUPnPMsg, bUPnPStatus = FALSE, Cancelled;
	WSC_UPNP_NODE_INFO *pWscUPnPInfo = &pWscControl->WscUPnPNodeInfo;
	
	DBGPRINT(RT_DEBUG_TRACE, ("WscEapRegistrarAction Enter!\n"));

	bUPnPMsg = Elem->MsgType == WSC_EAPOL_UPNP_MSG ? TRUE : FALSE;

	if(bUPnPMsg)
	{
		if(MsgType == WSC_MSG_M1)
		{	/* It's a M1 message, we may need to initialize our state machine. */
			if (pWscControl->WscActionMode == WSC_REGISTRAR && pWscControl->EntryIfIdx == WSC_INIT_ENTRY_APIDX &&
				pWscControl->WscState < WSC_STATE_WAIT_M1 && pWscUPnPInfo->bUPnPInProgress == FALSE)
			{
				pWscUPnPInfo->bUPnPInProgress = TRUE;
				//Set the WscState as "WSC_STATE_WAIT_RESP_ID" because UPnP start from this state.
				pWscControl->WscState = WSC_STATE_WAIT_M1;
				RTMPSetTimer(&pWscUPnPInfo->UPnPMsgTimer, WSC_UPNP_MSG_TIME_OUT);
				pWscUPnPInfo->bUPnPMsgTimerRunning = TRUE;
			}
		}
		OpCode = WSC_OPCODE_UPNP_MASK;
		
	} else {
	    if (pWscControl->EapolTimerRunning)
		pWscControl->EapolTimerRunning = FALSE;

	}

	if( (WscData = kmalloc(WSC_MAX_DATA_LEN, GFP_ATOMIC)) == NULL)
	{
		DBGPRINT(RT_DEBUG_TRACE, ("WscData Allocate failed!\n"));
		goto Fail;
	}
	memset(WscData, 0, WSC_MAX_DATA_LEN);
	
	// Base on state doing the Msg, State change diagram
	switch (MsgType)
	{
		case WSC_MSG_M1:
			DBGPRINT(RT_DEBUG_TRACE, ("WscEapRegistrarAction : Rx M1\n"));
            
			// Receive M1, if we are at WSC_STATE_WAIT_M1 start, process it immediately
			pWscControl->WscStatus = STATUS_WSC_EAP_M1_RECEIVED;
			if (pWscControl->WscState == WSC_STATE_WAIT_M1)
			{
				OpCode |= WSC_OPCODE_MSG;

				// Process M1
				if ((rv=ProcessMessageM1(pAdapter, pWscControl, Elem->Msg, Elem->MsgLen, &pWscControl->RegData)))
					goto Fail;
				else
				{
					if (pWscControl->WscMode == 1 && pWscControl->WscPinCode == 0)
					{
						DataLen = BuildMessageM2D(pAdapter, pWscControl, WscData);
						pWscControl->WscState = WSC_STATE_SENT_M2D;
					}
					else
					{
						pWscControl->WscStatus = STATUS_WSC_EAP_M2_SENT;
						DataLen = BuildMessageM2(pAdapter, pWscControl, WscData);
						
						// Change the state to next one
						pWscControl->WscState = WSC_STATE_WAIT_M3;
#ifdef CONFIG_STA_SUPPORT
						if (pAdapter->OpMode == OPMODE_STA)
						{
							if (!bUPnPMsg)
								pWscControl->WscConfStatus = pWscControl->bConfiguredAP ? WSC_SCSTATE_UNCONFIGURED : WSC_SCSTATE_CONFIGURED;
						}
#endif // CONFIG_STA_SUPPORT //
					}
				}
			}
			break;

		case WSC_MSG_M3:
			// Receive M3
			DBGPRINT(RT_DEBUG_TRACE, ("WscEapRegistrarAction : Rx M3\n"));
			if (pWscControl->WscState == WSC_STATE_WAIT_M3)
			{
				pWscControl->WscStatus = STATUS_WSC_EAP_M3_RECEIVED;

				if((rv = ProcessMessageM3(pAdapter, Elem->Msg, Elem->MsgLen, &pWscControl->RegData)))
					goto Fail;
				else
				{
					OpCode |= WSC_OPCODE_MSG;
					DataLen = BuildMessageM4(pAdapter, pWscControl, WscData);
					pWscControl->WscStatus = STATUS_WSC_EAP_M4_SENT;
					// Change the state to next one
					pWscControl->WscState = WSC_STATE_WAIT_M5;
				}
			}
			break;

		case WSC_MSG_M5:
			DBGPRINT(RT_DEBUG_TRACE, ("WscEapRegistrarAction : Rx M5\n"));
			if (pWscControl->WscState == WSC_STATE_WAIT_M5)
			{
				pWscControl->WscStatus = STATUS_WSC_EAP_M5_RECEIVED;

				if ((rv=ProcessMessageM5(pAdapter, pWscControl, Elem->Msg, Elem->MsgLen, &pWscControl->RegData)))
					goto Fail;
				else
				{
					OpCode |= WSC_OPCODE_MSG;
					DataLen = BuildMessageM6(pAdapter, pWscControl, WscData);
					pWscControl->WscStatus = STATUS_WSC_EAP_M6_SENT;
					// Change the state to next one
					pWscControl->WscState = WSC_STATE_WAIT_M7;
				}
			}
			break;
		case WSC_MSG_M7:
			DBGPRINT(RT_DEBUG_TRACE, ("WscEapRegistrarAction : Rx M7\n"));
			if (pWscControl->WscState == WSC_STATE_WAIT_M7)
			{
				pWscControl->WscStatus = STATUS_WSC_EAP_M7_RECEIVED;
				if ((rv=ProcessMessageM7(pAdapter, pWscControl, Elem->Msg, Elem->MsgLen, &pWscControl->RegData)))
					goto Fail;
				else
				{
					if (
#ifdef CONFIG_STA_SUPPORT
						((pAdapter->OpMode == OPMODE_STA) && ((pWscControl->bConfiguredAP == FALSE)||(pAdapter->MlmeAux.APRalinkIe != 0x0))) ||
#endif // CONFIG_STA_SUPPORT //
						(0))
					{
						OpCode |= WSC_OPCODE_MSG;

						DataLen = BuildMessageM8(pAdapter, pWscControl, WscData);
						pWscControl->WscStatus = STATUS_WSC_EAP_M8_SENT;
						// Change the state to next one
						pWscControl->WscState = WSC_STATE_WAIT_DONE;
					}
#ifdef CONFIG_STA_SUPPORT
					else if ((pAdapter->OpMode == OPMODE_STA) && 
						(pWscControl->bConfiguredAP == TRUE))
					{
						// Some WPS AP expects to receive WSC_NACK when AP is configured
						OpCode |= WSC_OPCODE_NACK;
						DataLen = BuildMessageNACK(pAdapter, pWscControl, WscData);
						pWscControl->WscStatus = STATUS_WSC_CONFIGURED;
						pWscControl->WscState = WSC_STATE_CONFIGURED;
						pWscControl->EapMsgRunning = FALSE;
					}
#endif // CONFIG_STA_SUPPORT //
				}
			}
			break;

		case WSC_MSG_WSC_DONE:
			DBGPRINT(RT_DEBUG_TRACE, ("WscEapRegistrarAction : Rx DONE\n"));
			if (pWscControl->WscState == WSC_STATE_WAIT_DONE)
			{
				// Change the state to OFF, regComplete
				pWscControl->WscState = WSC_STATE_CONFIGURED;

#ifdef CONFIG_STA_SUPPORT
				IF_DEV_CONFIG_OPMODE_ON_STA(pAdapter)
				{
					OpCode |= WSC_OPCODE_ACK;
					DataLen = BuildMessageACK(pAdapter, pWscControl, WscData);
					pWscControl->WscStatus = STATUS_WSC_CONFIGURED;
				if (pAdapter->CommonCfg.bWirelessEvent)
					RTMPSendWirelessEvent(pAdapter, IW_WSC_STATUS_SUCCESS, &pWscControl->EntryAddr[0], pWscControl->EntryIfIdx, 0);				

				}
#endif // CONFIG_STA_SUPPORT //
				pWscControl->EapMsgRunning = FALSE;
			}
			break;
		default:
			DBGPRINT(RT_DEBUG_TRACE, ("WscEapRegistrarAction : Unsupported Msg Type\n"));
			return;
	}

	if(OpCode > WSC_OPCODE_UPNP_MASK)
		bUPnPStatus = WscSendUPnPMessage(pAdapter, pWscControl->EntryIfIdx, 
											WSC_OPCODE_UPNP_DATA, WSC_UPNP_DATA_SUB_NORMAL, 
											WscData, DataLen, 
											Elem->TimeStamp.u.LowPart, Elem->TimeStamp.u.HighPart, &pWscControl->EntryAddr[0]);
	else if(OpCode > 0 && OpCode < WSC_OPCODE_UPNP_MASK)
	{

#ifdef CONFIG_STA_SUPPORT
		IF_DEV_CONFIG_OPMODE_ON_STA(pAdapter)
			WscSendMessage(pAdapter, OpCode, WscData, DataLen, pWscControl, STA_MODE);
#endif // CONFIG_STA_SUPPORT //
		    
	}
	else
		bUPnPStatus = TRUE;

	if(bUPnPMsg)
	{
		if(pWscControl->WscState == WSC_STATE_SENT_M2D)
		{	//After M2D, reset the status of State Machine.
			pWscControl->WscState = WSC_STATE_WAIT_UPNP_START;
			pWscUPnPInfo->bUPnPInProgress = FALSE;
		}
	}
Fail:
    DBGPRINT(RT_DEBUG_TRACE, ("WscEapRegistrarAction : rv = %d\n", rv));
    if (rv)
    {        
        switch(rv)
        {
            case WSC_ERROR_HASH_FAIL:
                pWscControl->WscStatus = STATUS_WSC_ERROR_HASH_FAIL;
                pWscControl->RegData.SelfInfo.ConfigError = WSC_ERROR_HASH_FAIL;
                break;
            case WSC_ERROR_HMAC_FAIL:
                pWscControl->WscStatus = STATUS_WSC_ERROR_HMAC_FAIL;
                pWscControl->RegData.SelfInfo.ConfigError = WSC_ERROR_HMAC_FAIL;
                break;
            default:
                pWscControl->WscStatus = STATUS_WSC_FAIL;
                pWscControl->RegData.SelfInfo.ConfigError = WSC_ERROR_DECRYPT_CRC_FAIL;
				if (pAdapter->CommonCfg.bWirelessEvent)
					RTMPSendWirelessEvent(pAdapter, IW_WSC_STATUS_FAIL, &pWscControl->EntryAddr[0], pWscControl->EntryIfIdx, 0);
                break;
        }        
        if (bUPnPMsg)
        {
			if (pWscUPnPInfo->bUPnPMsgTimerRunning == TRUE)
			{
            	RTMPCancelTimer(&pWscUPnPInfo->UPnPMsgTimer, &Cancelled);
	            pWscUPnPInfo->bUPnPMsgTimerRunning = FALSE;
			}
			pWscUPnPInfo->bUPnPInProgress = FALSE;
        }
		else
        {
            DataLen = BuildMessageNACK(pAdapter, pWscControl, WscData);            

#ifdef CONFIG_STA_SUPPORT
		IF_DEV_CONFIG_OPMODE_ON_STA(pAdapter)
			WscSendMessage(pAdapter, WSC_OPCODE_NACK, WscData, DataLen, pWscControl, STA_MODE);
#endif // CONFIG_STA_SUPPORT //

		RTMPCancelTimer(&pWscControl->EapolTimer, &Cancelled);
		pWscControl->EapolTimerRunning = FALSE;
	}
        /*
           If a PIN authentication or communication error occurs after sending message M6, 
           the Registrar MUST warn the user and MUST NOT automatically reuse the PIN. 
           Furthermore, if the Registrar detects this situation and prompts the user for a new PIN from the Enrollee device, 
           it MUST NOT accept the same PIN again without warning the user of a potential attack.
        */
        if (pWscControl->WscState >= WSC_STATE_WAIT_M7)
        {
            pWscControl->WscRejectSamePinFromEnrollee = TRUE;
            pWscControl->WscPinCode = 0;
        }
        pWscControl->WscState = WSC_STATE_OFF;
        pWscControl->WscStatus = STATUS_WSC_IDLE;
		NdisZeroMemory(pWscControl->EntryAddr, MAC_ADDR_LEN);
        pWscControl->WscMode = 1;
        bUPnPStatus = FALSE;
    }

	if(WscData)
		kfree(WscData);
	
	if(bUPnPMsg && (bUPnPStatus == FALSE))
		WscUPnPErrHandle(pAdapter, pWscControl, Elem->TimeStamp.u.LowPart);

	if (pWscControl->WscState == WSC_STATE_CONFIGURED)
	{
		pWscControl->bWscTrigger = FALSE;
		if (pWscControl->Wsc2MinsTimerRunning)
		{
			pWscControl->Wsc2MinsTimerRunning = FALSE;
			RTMPCancelTimer(&pWscControl->Wsc2MinsTimer, &Cancelled);
		}
		if (bUPnPMsg)
		{
			if (pWscUPnPInfo->bUPnPMsgTimerRunning == TRUE)
			{	RTMPCancelTimer(&pWscUPnPInfo->UPnPMsgTimer, &Cancelled);
				pWscUPnPInfo->bUPnPMsgTimerRunning = FALSE;
			}
			pWscUPnPInfo->bUPnPInProgress = FALSE;
			pWscUPnPInfo->registrarID = 0;
		}

		if (
#ifdef CONFIG_STA_SUPPORT
			(pAdapter->OpMode == OPMODE_STA) ||
#endif // CONFIG_STA_SUPPORT //
			(0))
		{
			pWscControl->WscConfStatus = WSC_SCSTATE_CONFIGURED;
			pAdapter->WriteWscCfgToDatFile = TRUE;
#ifdef CONFIG_STA_SUPPORT
			IF_DEV_CONFIG_OPMODE_ON_STA(pAdapter)
			{
				pAdapter->StaCfg.bAutoConnectByBssid = TRUE;
				if ((pWscControl->WscConfMode == WSC_REGISTRAR )&&
					pWscControl->bConfiguredAP)
					memcpy(&pWscControl->WscProfile, &pWscControl->WscM7Profile, sizeof(pWscControl->WscM7Profile));
				WscWriteConfToPortCfg(pAdapter, pWscControl, &pWscControl->WscProfile.Profile[0], FALSE);
			}
#endif // CONFIG_STA_SUPPORT //
#ifdef KTHREAD_SUPPORT
			WAKE_UP(&(pAdapter->wscCfgWriteTask));
#else
			RTMP_SEM_EVENT_UP(&(pAdapter->wscCfgWriteTask.taskSema));
#endif
		}
#ifdef CONFIG_STA_SUPPORT
		IF_DEV_CONFIG_OPMODE_ON_STA(pAdapter)
		{
			pWscControl->WscState = WSC_STATE_WAIT_EAPFAIL;
			pWscControl->WscConfMode = WSC_DISABLE;
		}
#endif // CONFIG_STA_SUPPORT //
		pWscControl->WscPinCode = 0;
		pWscControl->WscMode = 1;
		RTMPCancelTimer(&pWscControl->EapolTimer, &Cancelled);
		pWscControl->EapolTimerRunning = FALSE;
		return;
	}	
}


VOID WscTimeOutProcess(
    IN  PRTMP_ADAPTER       pAd,
    IN  PMAC_TABLE_ENTRY    pEntry,
    IN  INT                 nWscState,
    IN  PWSC_CTRL           pWscControl)
{
    INT         WscMode;

    if (nWscState == WSC_STATE_WAIT_ACK)
        pWscControl->WscState = WSC_STATE_CONFIGURED;
    else if (nWscState == WSC_STATE_WAIT_RESP_ID)
        pWscControl->WscState = WSC_STATE_OFF;
    else if (nWscState == WSC_STATE_RX_M2D)
    {
        pWscControl->WscState = WSC_STATE_FAIL;
        

		pWscControl->EapolTimerRunning = FALSE;
		pWscControl->WscRetryCount = 0;
        
#ifdef CONFIG_STA_SUPPORT
		IF_DEV_CONFIG_OPMODE_ON_STA(pAd)
		{
			WscLinkDown(pAd);
		}
#endif // CONFIG_STA_SUPPORT //

        return;
    }
    else if (nWscState == WSC_STATE_WAIT_EAPFAIL)
    {
        pWscControl->WscState = WSC_STATE_OFF;
		pWscControl->WscStatus = STATUS_WSC_CONFIGURED;
        pWscControl->WscConfMode = WSC_DISABLE;
    }
    else
    {
    		pWscControl->WscState = WSC_STATE_FAIL;
    }  
    pWscControl->WscRetryCount = 0;
	NdisZeroMemory(pWscControl->EntryAddr, MAC_ADDR_LEN);
    pWscControl->EapolTimerRunning = FALSE;
    if (pWscControl->WscMode == 1)
		WscMode = DEV_PASS_ID_PIN;
	else
		WscMode = DEV_PASS_ID_PBC;
    

#ifdef CONFIG_STA_SUPPORT
	IF_DEV_CONFIG_OPMODE_ON_STA(pAd)
	{
		WscLinkDown(pAd);
	}
#endif // CONFIG_STA_SUPPORT //

    DBGPRINT(RT_DEBUG_TRACE, ("WscTimeOutProcess\n"));
}

VOID WscEAPOLTimeOutAction(
    IN PVOID SystemSpecific1, 
    IN PVOID FunctionContext, 
    IN PVOID SystemSpecific2, 
    IN PVOID SystemSpecific3)
{
    PUCHAR              WscData = NULL;
    PMAC_TABLE_ENTRY    pEntry = NULL;
    PWSC_CTRL           pWscControl = NULL;
	PRTMP_ADAPTER pAd = NULL;
    
    DBGPRINT(RT_DEBUG_TRACE, ("-----> WscEAPOLTimeOutAction\n"));
        
    if (FunctionContext == 0)
    {
        return;
    }
    else
    {
        pWscControl = (PWSC_CTRL)FunctionContext;
		pAd = (PRTMP_ADAPTER)pWscControl->pAd;
		if (pAd == NULL)
		{
			return;
		}
    }


    if (!pWscControl->EapolTimerRunning)
    {
        pWscControl->WscRetryCount = 0;
        goto out;
    }
    
    if (pWscControl->EapolTimerPending)
    {
        RTMPModTimer(&pWscControl->EapolTimer, WSC_EAP_MSG_TIME_OUT);
        DBGPRINT(RT_DEBUG_TRACE, ("EapolTimer Pending......\n"));
        DBGPRINT(RT_DEBUG_TRACE, ("<----- WscEAPOLTimeOutAction\n"));
        return;
    }

    if ((WscData = kmalloc(WSC_MAX_DATA_LEN, GFP_ATOMIC))!= NULL)
        memset(WscData, 0, WSC_MAX_DATA_LEN);


    switch(pWscControl->WscState)
    {
        case WSC_STATE_WAIT_REQ_ID:
			if (pWscControl->WscRetryCount >= 2)
				WscTimeOutProcess(pWscControl->pAd, pEntry, WSC_STATE_WAIT_REQ_ID, pWscControl);
			else
			{
				pWscControl->WscRetryCount++;
#ifdef CONFIG_STA_SUPPORT
				WscSendEapolStart(pAd, pAd->CommonCfg.Bssid);
#endif // CONFIG_STA_SUPPORT //
				RTMPModTimer(&pWscControl->EapolTimer, WSC_EAP_MSG_TIME_OUT);
			}
            break;
        case WSC_STATE_WAIT_WSC_START:
			if (pWscControl->WscRetryCount >= 2)
		    	WscTimeOutProcess(pWscControl->pAd, pEntry, WSC_STATE_WAIT_WSC_START, pWscControl);
			else
			{
				pWscControl->WscRetryCount++;
				RTMPModTimer(&pWscControl->EapolTimer, WSC_EAP_MSG_TIME_OUT);
			}
            break;
        case WSC_STATE_WAIT_M1:
		if (pWscControl->WscRetryCount >= 2)
			WscTimeOutProcess(pWscControl->pAd, pEntry, WSC_STATE_WAIT_M1, pWscControl);
		else
		{
			pWscControl->WscRetryCount++;
			RTMPModTimer(&pWscControl->EapolTimer, WSC_EAP_MSG_TIME_OUT);
		}
            	break;
	case WSC_STATE_SENT_M1:
		if (pWscControl->WscRetryCount >= 2)
			WscTimeOutProcess(pWscControl->pAd, pEntry, WSC_STATE_WAIT_M2, pWscControl);
		else
		{
			if (pWscControl->WscActionMode == WSC_ENROLLEE)
			{
				{

#ifdef CONFIG_STA_SUPPORT
					IF_DEV_CONFIG_OPMODE_ON_STA(pAd)
						WscSendMessage(pWscControl->pAd, WSC_OPCODE_MSG, pWscControl->RegData.LastTx.Data, pWscControl->RegData.LastTx.Length, pWscControl, STA_MODE);
#endif // CONFIG_STA_SUPPORT //
				}
			}
			pWscControl->WscRetryCount++;
			RTMPModTimer(&pWscControl->EapolTimer, WSC_EAP_MSG_TIME_OUT);
		}
		break;
	case WSC_STATE_RX_M2D:
		if (pWscControl->WscRetryCount >= 2)
			WscTimeOutProcess(pWscControl->pAd, pEntry, WSC_STATE_RX_M2D, pWscControl);
		else
		{
			pWscControl->WscRetryCount++;
			RTMPModTimer(&pWscControl->EapolTimer, WSC_EAP_MSG_TIME_OUT);
		}
		break;
        case WSC_STATE_WAIT_PIN:
		if (pWscControl->WscRetryCount >= 2)
			WscTimeOutProcess(pWscControl->pAd, pEntry, WSC_STATE_WAIT_PIN, pWscControl);
		else
		{
			pWscControl->WscRetryCount++;
			if (pWscControl->WscPinCode != 0)
			{

#ifdef CONFIG_STA_SUPPORT
				IF_DEV_CONFIG_OPMODE_ON_STA(pAd)
					WscSendMessage(pWscControl->pAd, WSC_OPCODE_MSG, pWscControl->RegData.LastTx.Data, pWscControl->RegData.LastTx.Length, pWscControl, STA_MODE);
#endif // CONFIG_STA_SUPPORT //
				pWscControl->WscState = WSC_STATE_WAIT_M3;
			}
			else
			{
				DBGPRINT(RT_DEBUG_TRACE, ("No PIN CODE, cannot send M2 out!\n"));
			}
			RTMPModTimer(&pWscControl->EapolTimer, WSC_EAP_MSG_TIME_OUT);
		}
		break;
	case WSC_STATE_WAIT_M3:
		if (pWscControl->WscRetryCount >= 2)
			WscTimeOutProcess(pWscControl->pAd, pEntry, WSC_STATE_WAIT_M3, pWscControl);
		else
		{
			if (pWscControl->WscActionMode == WSC_REGISTRAR)
			{
				{

#ifdef CONFIG_STA_SUPPORT
					IF_DEV_CONFIG_OPMODE_ON_STA(pAd)
						WscSendMessage(pWscControl->pAd, WSC_OPCODE_MSG, pWscControl->RegData.LastTx.Data, pWscControl->RegData.LastTx.Length, pWscControl, STA_MODE);
#endif // CONFIG_STA_SUPPORT //
				}
			}
			pWscControl->WscRetryCount++;
			RTMPModTimer(&pWscControl->EapolTimer, WSC_EAP_MSG_TIME_OUT);
		}
		break;
        case WSC_STATE_WAIT_M4:
		if (pWscControl->WscRetryCount >= 2)
			WscTimeOutProcess(pWscControl->pAd, pEntry, WSC_STATE_WAIT_M4, pWscControl);
		else
		{
			if (pWscControl->WscActionMode == WSC_ENROLLEE)
			{
				{

#ifdef CONFIG_STA_SUPPORT
					IF_DEV_CONFIG_OPMODE_ON_STA(pAd)
						WscSendMessage(pWscControl->pAd, WSC_OPCODE_MSG, pWscControl->RegData.LastTx.Data, pWscControl->RegData.LastTx.Length, pWscControl, STA_MODE);
#endif // CONFIG_STA_SUPPORT //
				}
			}
			pWscControl->WscRetryCount++;
			RTMPModTimer(&pWscControl->EapolTimer, WSC_EAP_MSG_TIME_OUT);
		}
		break;
	case WSC_STATE_WAIT_M5:
		if (pWscControl->WscRetryCount >= 2)
			WscTimeOutProcess(pWscControl->pAd, pEntry, WSC_STATE_WAIT_M5, pWscControl);
		else
		{
			if (pWscControl->WscActionMode == WSC_REGISTRAR)
			{         
				{

#ifdef CONFIG_STA_SUPPORT
					IF_DEV_CONFIG_OPMODE_ON_STA(pAd)
						WscSendMessage(pWscControl->pAd, WSC_OPCODE_MSG, pWscControl->RegData.LastTx.Data, pWscControl->RegData.LastTx.Length, pWscControl, STA_MODE);
#endif // CONFIG_STA_SUPPORT //
				}
			}
			pWscControl->WscRetryCount++;
			RTMPModTimer(&pWscControl->EapolTimer, WSC_EAP_MSG_TIME_OUT);
		}
		break;
        case WSC_STATE_WAIT_M6:
		if (pWscControl->WscRetryCount >= 2)
			WscTimeOutProcess(pWscControl->pAd, pEntry, WSC_STATE_WAIT_M6, pWscControl);
		else
		{
			if (pWscControl->WscActionMode == WSC_ENROLLEE)
			{
				{

#ifdef CONFIG_STA_SUPPORT
					IF_DEV_CONFIG_OPMODE_ON_STA(pAd)
						WscSendMessage(pWscControl->pAd, WSC_OPCODE_MSG, pWscControl->RegData.LastTx.Data, pWscControl->RegData.LastTx.Length, pWscControl, STA_MODE);
#endif // CONFIG_STA_SUPPORT //
				}
			}
			pWscControl->WscRetryCount++;
			RTMPModTimer(&pWscControl->EapolTimer, WSC_EAP_MSG_TIME_OUT);
		}
		break;
        case WSC_STATE_WAIT_M7:
		if (pWscControl->WscRetryCount >= 2)
			WscTimeOutProcess(pWscControl->pAd, pEntry, WSC_STATE_WAIT_M7, pWscControl);
		else
		{
			if (pWscControl->WscActionMode == WSC_REGISTRAR)
			{
				{

#ifdef CONFIG_STA_SUPPORT
					IF_DEV_CONFIG_OPMODE_ON_STA(pAd)
						WscSendMessage(pWscControl->pAd, WSC_OPCODE_MSG, pWscControl->RegData.LastTx.Data, pWscControl->RegData.LastTx.Length, pWscControl, STA_MODE);
#endif // CONFIG_STA_SUPPORT //
				}
			}
			pWscControl->WscRetryCount++;
			RTMPModTimer(&pWscControl->EapolTimer, WSC_EAP_MSG_TIME_OUT);
		}
		break;
        case WSC_STATE_WAIT_M8:
		if (pWscControl->WscRetryCount >= 2)
			WscTimeOutProcess(pWscControl->pAd, pEntry, WSC_STATE_WAIT_M8, pWscControl);
		else
		{
			if (pWscControl->WscActionMode == WSC_ENROLLEE)
			{
				{

#ifdef CONFIG_STA_SUPPORT
					IF_DEV_CONFIG_OPMODE_ON_STA(pAd)
						WscSendMessage(pWscControl->pAd, WSC_OPCODE_MSG, pWscControl->RegData.LastTx.Data, pWscControl->RegData.LastTx.Length, pWscControl, STA_MODE);
#endif // CONFIG_STA_SUPPORT //
				}
			}
			pWscControl->WscRetryCount++;
			RTMPModTimer(&pWscControl->EapolTimer, WSC_EAP_MSG_TIME_OUT);
		}
		break;
	case WSC_STATE_WAIT_DONE:
		if (pWscControl->WscRetryCount >= 2)
			WscTimeOutProcess(pWscControl->pAd, pEntry, WSC_STATE_WAIT_DONE, pWscControl);
		else
		{
			if (pWscControl->WscActionMode == WSC_REGISTRAR)
			{
				{

#ifdef CONFIG_STA_SUPPORT
					IF_DEV_CONFIG_OPMODE_ON_STA(pAd)
						WscSendMessage(pWscControl->pAd, WSC_OPCODE_MSG, pWscControl->RegData.LastTx.Data, pWscControl->RegData.LastTx.Length, pWscControl, STA_MODE);
#endif // CONFIG_STA_SUPPORT //
				}
			}
			pWscControl->WscRetryCount++;
			RTMPModTimer(&pWscControl->EapolTimer, WSC_EAP_MSG_TIME_OUT);
		}
		break;
        case WSC_STATE_WAIT_EAPFAIL:
            // Wait 2 seconds
            if (pWscControl->WscRetryCount >= 1)
                WscTimeOutProcess(pWscControl->pAd, pEntry, WSC_STATE_WAIT_EAPFAIL, pWscControl);
            else
            {
                RTMPModTimer(&pWscControl->EapolTimer, WSC_EAP_EAP_FAIL_TIME_OUT);
                pWscControl->WscRetryCount++;
			}
            break;
        default:
            break;
    }

out:
    if (WscData)
        kfree(WscData);

    DBGPRINT(RT_DEBUG_TRACE, ("<----- WscEAPOLTimeOutAction\n"));
}

VOID Wsc2MinsTimeOutAction(
    IN PVOID SystemSpecific1, 
    IN PVOID FunctionContext, 
    IN PVOID SystemSpecific2, 
    IN PVOID SystemSpecific3)
{
	PWSC_CTRL       pWscControl = (PWSC_CTRL)FunctionContext;
	PRTMP_ADAPTER 	pAd = NULL;
	BOOLEAN         Cancelled;

	DBGPRINT(RT_DEBUG_TRACE, ("-----> Wsc2MinsTimeOutAction\n"));
	if (pWscControl != NULL)
	{
		pAd =  (PRTMP_ADAPTER)pWscControl->pAd;
		DBGPRINT(RT_DEBUG_TRACE, ("Wsc2MinsTimerRunning is %s\n", 
		pWscControl->Wsc2MinsTimerRunning ? "TRUE, reset WscState to WSC_STATE_OFF":"FALSE"));

#ifdef WSC_LED_SUPPORT
		// 120 seconds WPS walk time expiration.
		pWscControl->bWPSWalkTimeExpiration = TRUE;
#endif // WSC_LED_SUPPORT //

		if (pWscControl->Wsc2MinsTimerRunning)
		{
			pWscControl->bWscTrigger = FALSE;
			pWscControl->EapolTimerRunning = FALSE;
			RTMPCancelTimer(&pWscControl->EapolTimer, &Cancelled);


			pWscControl->WscRetryCount = 0;
			pWscControl->Wsc2MinsTimerRunning = FALSE;

			pWscControl->WscSelReg = 0;
			pWscControl->WscStatus = STATUS_WSC_IDLE; 

			if (pAd->CommonCfg.bWirelessEvent)
		   		 RTMPSendWirelessEvent(pAd, IW_WSC_2MINS_TIMEOUT, &pWscControl->EntryAddr[0], pWscControl->EntryIfIdx, 0);

#ifdef CONFIG_STA_SUPPORT
			IF_DEV_CONFIG_OPMODE_ON_STA(pAd)
			{
				if (pWscControl->WscScanTimerRunning)
				{
					pWscControl->WscScanTimerRunning = FALSE;
					RTMPCancelTimer(&pWscControl->WscScanTimer, &Cancelled);
				}
				if (pWscControl->WscPBCTimerRunning)
				{
					pWscControl->WscPBCTimerRunning = FALSE;
					RTMPCancelTimer(&pWscControl->WscPBCTimer, &Cancelled);
				}
				pWscControl->WscConfMode = WSC_DISABLE;
				pWscControl->WscState = WSC_STATE_OFF;
				WscLinkDown(pAd);
			}
#endif // CONFIG_STA_SUPPORT //		
		}
	}

#ifdef WSC_LED_SUPPORT
	// if link is up, there shall be nothing wrong
	// perhaps we will set another flag to do it
	if ((OPSTATUS_TEST_FLAG(pAd, fOP_STATUS_MEDIA_STATE_CONNECTED)) && 
		(pWscControl->WscState == WSC_STATE_OFF) &&
		(pWscControl->WscStatus == STATUS_WSC_CONFIGURED))
	{
		DBGPRINT(RT_DEBUG_TRACE, ("WscConnectTimeout --> Connection OK\n"));
	}
	else
	{
		UCHAR WPSLEDStatus;
		
		pWscControl->WscStatus = STATUS_WSC_FAIL; 
		pWscControl->WscState = WSC_STATE_OFF;

		// WPS LED mode 7 or 8.
		if (((pAd->LedCntl.field.LedMode & LED_MODE) == WPS_LED_MODE_7) || 
			((pAd->LedCntl.field.LedMode & LED_MODE) == WPS_LED_MODE_8))
		{
			pWscControl->bSkipWPSTurnOffLED = FALSE;
			
			// Turn off the WPS LED modoe due to the maximum WPS processing time is expired (120 seconds).
			WPSLEDStatus = LED_WPS_TURN_LED_OFF;
#ifdef RTMP_MAC_USB
			RTUSBEnqueueInternalCmd(pAd, RT_CMD_SET_WPS_LED_STATUS, &WPSLEDStatus, sizeof(WPSLEDStatus));
#endif // RTMP_MAC_USB //
#ifdef RTMP_MAC_PCI
			RTMPSetLED(pAd, WPSLEDStatus);
#endif // RTMP_MAC_PCI //
		}
		else if ((pAd->LedCntl.field.LedMode & LED_MODE) == WPS_LED_MODE_9) // WPS LED mode 9.
		{	
			if (pWscControl->WscMode == WSC_PIN_MODE) // PIN method.
			{
				// The NIC using PIN method fails to finish the WPS handshaking within 120 seconds.
				WPSLEDStatus = LED_WPS_ERROR;
#ifdef RTMP_MAC_USB
				RTUSBEnqueueInternalCmd(pAd, RT_CMD_SET_WPS_LED_STATUS, &WPSLEDStatus, sizeof(WPSLEDStatus));
#endif // RTMP_MAC_USB //
#ifdef RTMP_MAC_PCI
				RTMPSetLED(pAd, WPSLEDStatus);
#endif // RTMP_MAC_PCI //

				// Turn off the WPS LED after 15 seconds.
				RTMPSetTimer(&pWscControl->WscLEDTimer, WSC_WPS_FAIL_LED_PATTERN_TIMEOUT);

				// The Ralink UI would make RT_OID_DISCONNECT_REQUEST request while it receive STATUS_WSC_EAP_FAILED.
				// Allow the NIC to turn off the WPS LED after WSC_WPS_SKIP_TURN_OFF_LED_TIMEOUT seconds.
				pWscControl->bSkipWPSTurnOffLED = TRUE;
				RTMPSetTimer(&pWscControl->WscSkipTurnOffLEDTimer, WSC_WPS_SKIP_TURN_OFF_LED_TIMEOUT);
				
				DBGPRINT(RT_DEBUG_TRACE, ("%s: The NIC using PIN method fails to finish the WPS handshaking within 120 seconds.\n", __FUNCTION__));
			}
			else if (pWscControl->WscMode == WSC_PBC_MODE) // PBC method.
			{
				switch (pWscControl->WscLastWarningLEDMode) // Based on last WPS warning LED mode.
				{
					case LED_WPS_ERROR:
						// Failed to find any partner.
						WPSLEDStatus = LED_WPS_ERROR;
#ifdef RTMP_MAC_USB
						RTUSBEnqueueInternalCmd(pAd, RT_CMD_SET_WPS_LED_STATUS, &WPSLEDStatus, sizeof(WPSLEDStatus));
#endif // RTMP_MAC_USB //
#ifdef RTMP_MAC_PCI
						RTMPSetLED(pAd, WPSLEDStatus);
#endif // RTMP_MAC_PCI //

						// Turn off the WPS LED after 15 seconds.
						RTMPSetTimer(&pWscControl->WscLEDTimer, WSC_WPS_FAIL_LED_PATTERN_TIMEOUT);

						// The Ralink UI would make RT_OID_DISCONNECT_REQUEST request while it receive STATUS_WSC_EAP_FAILED.
						// Allow the NIC to turn off the WPS LED after WSC_WPS_SKIP_TURN_OFF_LED_TIMEOUT seconds.
						pWscControl->bSkipWPSTurnOffLED = TRUE;
						RTMPSetTimer(&pWscControl->WscSkipTurnOffLEDTimer, WSC_WPS_SKIP_TURN_OFF_LED_TIMEOUT);
						
						DBGPRINT(RT_DEBUG_TRACE, ("%s: Last WPS LED status is LED_WPS_ERROR.\n", __FUNCTION__));
						break;

					default:
						// do nothing.
						break;
				}
			}
			else
			{
				// do nothing.
			}
		}
		else
		{
			// do nothing.
		}
		
		DBGPRINT(RT_DEBUG_TRACE, ("WscConnectTimeout --> Fail to connect\n"));
	}
#endif // WSC_LED_SUPPORT //

	DBGPRINT(RT_DEBUG_TRACE, ("<----- Wsc2MinsTimeOutAction\n"));
}

/*
	========================================================================
	
	Routine Description:
		Classify EAP message type for enrolee

	Arguments:
		pAd         - NIC Adapter pointer
		Elem		- The EAP packet
		
	Return Value:
		Received EAP message type

	IRQL = DISPATCH_LEVEL
	
	Note:
		
	========================================================================
*/
UCHAR	WscRxMsgType(
	IN	PRTMP_ADAPTER		pAdapter,
	IN	PMLME_QUEUE_ELEM	pElem) 
{
	USHORT				Length;
	PUCHAR				pData;
	USHORT				WscType, WscLen;
    STRING	            id_data[] = {"hello"};
    STRING	            fail_data[] = {"EAP_FAIL"};
    STRING	            wsc_start[] = {"WSC_START"};
    STRING               regIdentity[] = {"WFA-SimpleConfig-Registrar"};
    STRING               enrIdentity[] = {"WFA-SimpleConfig-Enrollee"};

    if (pElem->Msg[0] == 'W' && pElem->Msg[1] == 'F' && pElem->Msg[2] == 'A')
    {
        // Eap-Rsp(Identity)
		if (memcmp(regIdentity, pElem->Msg, strlen(regIdentity)) == 0)
			return  WSC_MSG_EAP_REG_RSP_ID;
        else if (memcmp(enrIdentity, pElem->Msg, strlen(enrIdentity)) == 0)
			return  WSC_MSG_EAP_ENR_RSP_ID;
    }
    else if (NdisEqualMemory(id_data, pElem->Msg, pElem->MsgLen))
    {
        // Eap-Req/Identity(hello)
		return  WSC_MSG_EAP_REQ_ID;
    }
    else if (NdisEqualMemory(fail_data, pElem->Msg, pElem->MsgLen))
    {
        // Eap-Fail
		return  WSC_MSG_EAP_FAIL;
    }
    else if (NdisEqualMemory(wsc_start, pElem->Msg, pElem->MsgLen))
    {
        // Eap-Req(Wsc_Start)
        return WSC_MSG_EAP_REQ_START;
    }
    else
    {   // Eap-Esp(Messages)
        pData = pElem->Msg;
        Length = (USHORT)pElem->MsgLen;

        // the first TLV item in EAP Messages must be WSC_IE_VERSION
        NdisMoveMemory(&WscType, pData, 2);
        if (ntohs(WscType) != WSC_ID_VERSION)
            goto out;

        // Not Wsc Start, We have to look for WSC_IE_MSG_TYPE to classify M2 ~ M8, the remain size must large than 4
		while (Length > 4)
		{
			// arm-cpu has packet alignment issue, it's better to use memcpy to retrieve data
			NdisMoveMemory(&WscType, pData, 2);
			NdisMoveMemory(&WscLen,  pData + 2, 2);
			WscLen = ntohs(WscLen);
			if (ntohs(WscType) == WSC_ID_MSG_TYPE)
			{
				return(*(pData + 4));	// Found the message type
			}
			else
			{
				pData  += (WscLen + 4);
				Length -= (WscLen + 4);
			}
		}
    }

out:
	return  WSC_MSG_UNKNOWN;
}

/*
	========================================================================
	
	Routine Description:
		Classify WSC message type

	Arguments:
		EAPType		Value of EAP message type
		MsgType		Internal Message definition for MLME state machine
		
	Return Value:
		TRUE		Found appropriate message type
		FALSE		No appropriate message type
	
	Note:
		All these constants are defined in wpa.h
		For supplicant, there is only EAPOL Key message avaliable
		
	========================================================================
*/
BOOLEAN	WscMsgTypeSubst(
	IN	UCHAR	EAPType,
	IN	UCHAR	EAPCode,
	OUT	INT		*MsgType)	
{
	switch (EAPType)
	{
		case EAPPacket:
			*MsgType = WSC_EAPOL_PACKET_MSG;
			break;
        case EAPOLStart:
            *MsgType = WSC_EAPOL_START_MSG;
			break;
		default:
			DBGPRINT(RT_DEBUG_TRACE, ("WscMsgTypeSubst : unsupported EAP Type(%d); \n", EAPType));
			return FALSE;		
	}	

	return TRUE;
}

/*
	========================================================================
	
	Routine Description:
		Init WSC MAC header

	Arguments:
		pAdapter	Pointer	to our adapter
		
	Return Value:
		None
		
	Note:
		
	========================================================================
*/
VOID	WscMacHeaderInit(
	IN		PRTMP_ADAPTER	pAdapter, 
	IN OUT	PHEADER_802_11	Hdr, 
	IN		PUCHAR			pAddr1,
	IN      PUCHAR          pBSSID,
	IN      BOOLEAN         bFromApCli)	
{
	NdisZeroMemory(Hdr,	sizeof(HEADER_802_11));
	Hdr->FC.Type = BTYPE_DATA;
#ifdef CONFIG_STA_SUPPORT 
	IF_DEV_CONFIG_OPMODE_ON_STA(pAdapter)
		Hdr->FC.ToDs = 1;
#endif // CONFIG_STA_SUPPORT //
	
	 //	Addr1: DA, Addr2: BSSID, Addr3:	SA	
	COPY_MAC_ADDR(&Hdr->Addr1, pAddr1);
	    COPY_MAC_ADDR(&Hdr->Addr2, &pAdapter->CurrentAddress);
	COPY_MAC_ADDR(&Hdr->Addr3, pBSSID);
	Hdr->Sequence =	pAdapter->Sequence;		
}

VOID	WscInitRegistrarPair(
	IN	PRTMP_ADAPTER		pAdapter,
	IN  PWSC_CTRL           pWscControl,
	IN  UCHAR				apidx)
{
	INT		idx;
	
	DBGPRINT(RT_DEBUG_TRACE, ("-----> WscInitRegistrarPair\n"));
	pWscControl->WscActionMode = 0;

	// 1. Version
	pWscControl->RegData.SelfInfo.Version = WSC_VERSION;

	// 2. UUID Enrollee, last 6 bytes use MAC
	NdisMoveMemory(&pWscControl->RegData.SelfInfo.Uuid[0], &pWscControl->Wsc_Uuid_E[0], UUID_LEN_HEX);

	// 3. MAC address
#ifdef CONFIG_STA_SUPPORT	
	IF_DEV_CONFIG_OPMODE_ON_STA(pAdapter)
		NdisMoveMemory(pWscControl->RegData.SelfInfo.MacAddr, pAdapter->CurrentAddress, 6);
#endif // CONFIG_STA_SUPPORT //

	// 4. Device Name

#ifdef CONFIG_STA_SUPPORT
	IF_DEV_CONFIG_OPMODE_ON_STA(pAdapter)
		NdisMoveMemory(&pWscControl->RegData.SelfInfo.DeviceName, STA_WSC_DEVICE_NAME, sizeof(STA_WSC_DEVICE_NAME));
#endif // CONFIG_STA_SUPPORT //
	
	// 5. Manufacture
	NdisMoveMemory(&pWscControl->RegData.SelfInfo.Manufacturer, WSC_MANUFACTURE, sizeof(WSC_MANUFACTURE));

	// 6. Model Name

#ifdef CONFIG_STA_SUPPORT
	IF_DEV_CONFIG_OPMODE_ON_STA(pAdapter)
		NdisMoveMemory(&pWscControl->RegData.SelfInfo.ModelName, STA_WSC_MODEL_NAME, sizeof(STA_WSC_MODEL_NAME));
#endif // CONFIG_STA_SUPPORT //
	
	// 7. Model Number
	NdisMoveMemory(&pWscControl->RegData.SelfInfo.ModelNumber, WSC_MODEL_NUMBER, sizeof(WSC_MODEL_NUMBER));
	
	// 8. Serial Number
	NdisMoveMemory(&pWscControl->RegData.SelfInfo.SerialNumber, WSC_MODEL_SERIAL, sizeof(WSC_MODEL_SERIAL));
	
	// 9. Authentication Type Flags
	// Open(=1), WPAPSK(=2),Shared(=4), WPA2PSK(=20),WPA(=8),WPA2(=10)
	// (0x01 | 0x02 | 0x04 | 0x20 | 0x08 | 0x10) = 0x3F
	// WCN vista logo will check this flags.
	pWscControl->RegData.SelfInfo.AuthTypeFlags = cpu2be16(0x003F);
	
	// 10. Encryption Type Flags
	// None(=1), WEP(=2), TKIP(=4), AES(=8)
	// (0x01 | 0x02 | 0x04 | 0x08) = 0x0F
	pWscControl->RegData.SelfInfo.EncrTypeFlags  = cpu2be16(0x000F);
	
	// 11. Connection Type Flag
	pWscControl->RegData.SelfInfo.ConnTypeFlags = 0x01;					// ESS

	// 12. Associate state
	pWscControl->RegData.SelfInfo.AssocState = cpu2be16(0x0000);		// Not associated

	// 13. Configure Error
	pWscControl->RegData.SelfInfo.ConfigError = cpu2be16(0x0000);		// No error

	// 14. OS Version
	pWscControl->RegData.SelfInfo.OsVersion = cpu2be32(0x80000000);		// first bit must be 1
	
	// 15. RF Band
	/* Some WPS AP would check RfBand value in M1, ex. D-Link DIR-628 */
	pWscControl->RegData.SelfInfo.RfBand = 0x00;
	if ((pAdapter->CommonCfg.PhyMode == PHY_11A)
		|| (pAdapter->CommonCfg.PhyMode == PHY_11ABG_MIXED)
#ifdef DOT11_N_SUPPORT
		|| (pAdapter->CommonCfg.PhyMode == PHY_11AN_MIXED)
		|| (pAdapter->CommonCfg.PhyMode == PHY_11ABGN_MIXED)
		|| (pAdapter->CommonCfg.PhyMode == PHY_11AGN_MIXED)
#endif // DOT11_N_SUPPORT //
		)
		pWscControl->RegData.SelfInfo.RfBand |= WSC_RFBAND_50GHZ;			// 5.0G

	if ((pAdapter->CommonCfg.PhyMode != PHY_11A) 
#ifdef DOT11_N_SUPPORT
		&& (pAdapter->CommonCfg.PhyMode != PHY_11AN_MIXED)
#endif // DOT11_N_SUPPORT //
		)
		pWscControl->RegData.SelfInfo.RfBand |= WSC_RFBAND_24GHZ;			// 2.4G

	// 16. Config Method
	pWscControl->RegData.SelfInfo.ConfigMethods = cpu2be16(pWscControl->WscConfigMethods);
	//pWscControl->RegData.EnrolleeInfo.ConfigMethods = cpu2be16(WSC_CONFIG_METHODS);		// Label, Display, PBC
	//pWscControl->RegData.EnrolleeInfo.ConfigMethods = cpu2be16(0x0084);		// Label, Display, PBC

	// 17. Simple Config State
#ifdef CONFIG_STA_SUPPORT
	IF_DEV_CONFIG_OPMODE_ON_STA(pAdapter)
		pWscControl->RegData.SelfInfo.ScState = 0x01;
#endif // CONFIG_STA_SUPPORT //

	// 18. Device Password ID
	if (pWscControl->WscMode == WSC_PIN_MODE)
	{
		pWscControl->RegData.SelfInfo.DevPwdId = cpu2be16(DEV_PASS_ID_PIN);		// PIN mode
	}
	else
	{
		pWscControl->RegData.SelfInfo.DevPwdId = cpu2be16(DEV_PASS_ID_PBC);		// PBC
	}

	// 19. SSID
#ifdef CONFIG_STA_SUPPORT
	IF_DEV_CONFIG_OPMODE_ON_STA(pAdapter)
		NdisMoveMemory(pWscControl->RegData.SelfInfo.Ssid, pAdapter->CommonCfg.Ssid, pAdapter->CommonCfg.SsidLen);
#endif // CONFIG_STA_SUPPORT //

	// 20. Primary Device Type

#ifdef CONFIG_STA_SUPPORT
	IF_DEV_CONFIG_OPMODE_ON_STA(pAdapter)
		NdisMoveMemory(&pWscControl->RegData.SelfInfo.PriDeviceType, &STA_Wsc_Pri_Dev_Type[0], 8);
#endif // CONFIG_STA_SUPPORT //

	// Other enrollee controlled data in RegData structure
	
	// 1. Enrollee Nonce, first generate and save to Wsc Control Block
	for (idx = 0; idx < 16; idx++)
	{
		pWscControl->RegData.SelfNonce[idx] = RandomByte(pAdapter);
        pWscControl->RegData.EnrolleeNonce[idx] = pWscControl->RegData.SelfNonce[idx];
        pWscControl->RegData.RegistrarNonce[idx] = pWscControl->RegData.SelfNonce[idx];
	}

	DBGPRINT(RT_DEBUG_TRACE, ("<----- WscInitRegistrarPair\n"));	
}


BOOLEAN WscCheckWSCHeader(
    IN	PRTMP_ADAPTER		pAdapter,
    IN  PUCHAR              pData)
{
    PWSC_FRAME			pWsc;

	pWsc = (PWSC_FRAME) pData;

    // Verify SMI first
	if (((pWsc->SMI[0] * 256 + pWsc->SMI[1]) * 256 + pWsc->SMI[2]) != WSC_SMI)
	{
		// Wrong WSC SMI Vendor ID, Update WSC status
		return  FALSE;
	}
    
    // Verify Vendor Type
	if (cpu2be32(pWsc->VendorType) != WSC_VENDOR_TYPE)
	{
		// Wrong WSC Vendor Type, Update WSC status
		return  FALSE;
	}
    return TRUE;
}


/*
	========================================================================
	
	Routine Description:
		Send EAPoL-Start packet to AP.

	Arguments:
		pAd         - NIC Adapter pointer
		
	Return Value:
		None
		
	IRQL = DISPATCH_LEVEL
	
	Note:
		Actions after link up
		1. Change the correct parameters
		2. Send EAPOL - START
		
	========================================================================
*/
VOID    WscSendEapolStart(
	IN	PRTMP_ADAPTER	pAdapter,
	IN  PUCHAR          pBssid)
{
	IEEE8021X_FRAME		Packet;
	UCHAR               Header802_3[14];
	
#ifdef CONFIG_STA_SUPPORT
	IF_DEV_CONFIG_OPMODE_ON_STA(pAdapter)
	{
		if (pAdapter->StaCfg.WscControl.WscState >= WSC_STATE_WAIT_WSC_START)
			return;
	}
#endif // CONFIG_STA_SUPPORT //

	DBGPRINT(RT_DEBUG_TRACE, ("-----> WscSendEapolStart\n"));

	NdisZeroMemory(Header802_3,sizeof(UCHAR)*14);

	// 1. Change the authentication to open and encryption to none if necessary.

	// init 802.3 header and Fill Packet
#ifdef CONFIG_STA_SUPPORT
	IF_DEV_CONFIG_OPMODE_ON_STA(pAdapter)
		MAKE_802_3_HEADER(Header802_3, pBssid, &pAdapter->CurrentAddress[0], EAPOL);
#endif // CONFIG_STA_SUPPORT //
	
	// Zero message 2 body
	NdisZeroMemory(&Packet, sizeof(Packet));
	Packet.Version = EAPOL_VER;
	Packet.Type    = EAPOLStart;
	Packet.Length  = cpu2be16(0);
	
#ifdef CONFIG_STA_SUPPORT
	// Copy frame to Tx ring
	IF_DEV_CONFIG_OPMODE_ON_STA(pAdapter)
		RTMPToWirelessSta((PRTMP_ADAPTER)pAdapter, &pAdapter->MacTab.Content[BSSID_WCID],
						 Header802_3, LENGTH_802_3, (PUCHAR)&Packet, 4, TRUE);
#endif // CONFIG_STA_SUPPORT //


#ifdef CONFIG_STA_SUPPORT
	IF_DEV_CONFIG_OPMODE_ON_STA(pAdapter)
	{
		// Update WSC status
		pAdapter->StaCfg.WscControl.WscStatus = STATUS_WSC_EAPOL_START_SENT;
		pAdapter->StaCfg.WscControl.WscState = WSC_STATE_WAIT_REQ_ID;
		if (!pAdapter->StaCfg.WscControl.EapolTimerRunning)
		{
			pAdapter->StaCfg.WscControl.EapolTimerRunning = TRUE;
			RTMPSetTimer(&pAdapter->StaCfg.WscControl.EapolTimer, 5000);
		}
	}
#endif // CONFIG_STA_SUPPORT //    

	DBGPRINT(RT_DEBUG_TRACE, ("<----- WscSendEapolStart\n"));
}

VOID	WscSendEapRspId(
	IN	PRTMP_ADAPTER		pAdapter,
	IN  PMAC_TABLE_ENTRY    pEntry,
	IN  PWSC_CTRL           pWscControl)
{
	UCHAR               Header802_3[14];
	USHORT				Length = 0;
	IEEE8021X_FRAME		Ieee_8021x;
	EAP_FRAME			EapFrame;
	UCHAR				*pOutBuffer = NULL;
	ULONG				FrameLen = 0;
    UCHAR               regIdentity[] = "WFA-SimpleConfig-Registrar-1-0";
    UCHAR               enrIdentity[] = "WFA-SimpleConfig-Enrollee-1-0";
	

	NdisZeroMemory(Header802_3,sizeof(UCHAR)*14);

	// 1. Send EAP-Rsp Id
	DBGPRINT(RT_DEBUG_TRACE, ("-----> WscSendEapRspId\n"));


#ifdef CONFIG_STA_SUPPORT    
	IF_DEV_CONFIG_OPMODE_ON_STA(pAdapter)
	{
		// init 802.3 header and Fill Packet
		MAKE_802_3_HEADER(Header802_3, &pAdapter->CommonCfg.Bssid[0], &pAdapter->CurrentAddress[0], EAPOL);
		
		// Length, -1 NULL pointer of string
		if (pWscControl->WscConfMode == WSC_ENROLLEE)	
			Length = sizeof(EAP_FRAME) + sizeof(enrIdentity) - 1;
		else if (pWscControl->WscConfMode == WSC_REGISTRAR)
			Length = sizeof(EAP_FRAME) + sizeof(regIdentity) - 1;
	}

#endif // CONFIG_STA_SUPPORT //
   	
	// Zero 802.1x body
	NdisZeroMemory(&Ieee_8021x, sizeof(Ieee_8021x));
	Ieee_8021x.Version = EAPOL_VER;
	Ieee_8021x.Type    = EAPPacket;
	Ieee_8021x.Length  = cpu2be16(Length);

	// Zero EAP frame
	NdisZeroMemory(&EapFrame, sizeof(EapFrame));
	EapFrame.Code   = EAP_CODE_RSP;
	EapFrame.Id     = pWscControl->lastId;
	EapFrame.Length = cpu2be16(Length);
	EapFrame.Type   = EAP_TYPE_ID;

    // Out buffer for transmitting EAP-Req(Identity)
    pOutBuffer = kmalloc(MAX_LEN_OF_MLME_BUFFER, MEM_ALLOC_FLAG);
    if(pOutBuffer == NULL)
        return;

	FrameLen = 0;

    if (pWscControl->WscConfMode == WSC_REGISTRAR)
    {
    	// Make	 Transmitting frame
    	MakeOutgoingFrame(pOutBuffer, &FrameLen,
    		sizeof(IEEE8021X_FRAME), &Ieee_8021x,
    		sizeof(EapFrame), &EapFrame, 
    		(sizeof(regIdentity) - 1), regIdentity,
    		END_OF_ARGS);
    }
    else if (pWscControl->WscConfMode == WSC_ENROLLEE)
    {
        // Make	 Transmitting frame
    	MakeOutgoingFrame(pOutBuffer, &FrameLen,
    		sizeof(IEEE8021X_FRAME), &Ieee_8021x,
    		sizeof(EapFrame), &EapFrame, 
    		(sizeof(enrIdentity) - 1), enrIdentity,
    		END_OF_ARGS);
    }
    else
    {
        DBGPRINT(RT_DEBUG_TRACE, ("WscConfMode(%d) is not WSC_REGISTRAR nor WSC_ENROLLEE.\n", pWscControl->WscConfMode));	
        goto out;
    }

	// Copy frame to Tx ring

#ifdef CONFIG_STA_SUPPORT	
	IF_DEV_CONFIG_OPMODE_ON_STA(pAdapter)
		RTMPToWirelessSta((PRTMP_ADAPTER)pWscControl->pAd, &pAdapter->MacTab.Content[BSSID_WCID],
							Header802_3, LENGTH_802_3, (PUCHAR)pOutBuffer, FrameLen, TRUE);
#endif // CONFIG_STA_SUPPORT //

    if (!pWscControl->EapolTimerRunning)
    {
        pWscControl->EapolTimerRunning = TRUE;
        RTMPSetTimer(&pWscControl->EapolTimer, WSC_EAP_ID_TIME_OUT);
    }
out:
	if (pOutBuffer)
 	   kfree(pOutBuffer);
	DBGPRINT(RT_DEBUG_TRACE, ("<----- WscSendEapRspId\n"));	
}

VOID WscUPnPErrHandle(
	IN PRTMP_ADAPTER pAd,
	IN  PWSC_CTRL	pWscControl,
	IN UINT eventID)
{
	int dataLen;
	UCHAR *pWscData;

	DBGPRINT(RT_DEBUG_TRACE, ("Into WscUPnPErrHandle, send WSC_OPCODE_UPNP_CTRL with eventID=0x%x!\n", eventID));

	if( (pWscData = kmalloc(WSC_MAX_DATA_LEN, GFP_ATOMIC)) != NULL)
	{
		memset(pWscData, 0, WSC_MAX_DATA_LEN);
	
		dataLen = BuildMessageNACK(pAd, pWscControl, pWscData);
		WscSendUPnPMessage(pAd, pWscControl->EntryIfIdx, 
								WSC_OPCODE_UPNP_DATA, WSC_UPNP_DATA_SUB_NORMAL, 
								pWscData, dataLen, eventID, 0, NULL);
	
		kfree(pWscData);
	} 
	else 
	{
		WscSendUPnPMessage(pAd, pWscControl->EntryIfIdx, 
								WSC_OPCODE_UPNP_CTRL, 0, NULL, 0, eventID, 0, NULL);
	}
}

/*
	Format of iwcustom msg WSC clientJoin message:
		1. SSID which station want to probe(32 bytes):
			<SSID string>
			*If the length if SSID string is small than 32 bytes, fill 0x0 for remaining bytes.
		2. sender MAC address(6 bytes):
		3. Status:
			Set as 1 means change APStatus as 1. 
			Set as 2 means change STAStatus as 1.
			Set as 3 means trigger msg.
								
			32         6        1 
		+----------+--------+------+
		|SSIDString| SrcMAC |Status|
*/
int WscSendUPnPConfReqMsg(
	IN PRTMP_ADAPTER pAd,
	IN UCHAR apIdx,
	IN PUCHAR ssidStr,
	IN PUCHAR macAddr,
	IN INT	  Status,
	IN UINT   eventID)
{
	UCHAR pData[39] = {0};
	
	strncpy((PSTRING) pData, (PSTRING)ssidStr, strlen((PSTRING) ssidStr));
	NdisMoveMemory(&pData[32], macAddr, MAC_ADDR_LEN);
	pData[38] = Status;
	WscSendUPnPMessage(pAd, apIdx, WSC_OPCODE_UPNP_MGMT, WSC_UPNP_MGMT_SUB_CONFIG_REQ, 
							&pData[0], 39, eventID, 0, NULL);

	return 0;
}

	
/*
	NETLINK tunnel msg format send to WSCUPnP handler in user space:
	1. Signature of following string(Not include the quote, 8 bytes)
			"RAWSCMSG"
	2. eID: eventID (4 bytes)
			the ID of this message(4 bytes)
	3. aID: ackID (4 bytes)
			means that which event ID this mesage was response to.
	4. TL:  Message Total Length (4 bytes) 
			Total length of this message.
	5. F:   Flag (2 bytes)
			used to notify some specific character of this msg segment.
				Bit 1: fragment
					set as 1 if netlink layer have more segment of this Msg need to send.
				Bit 2~15: reserve, should set as 0 now.
	5. SL:  Segment Length(2 bytes)
			msg actual length in this segment, The SL may not equal the "TL" field if "F" ==1
	6. devMac: device mac address(6 bytes)
			Indicate the netdevice which this msg belong. For the wscd in user space will 
			depends this address dispatch the msg to correct UPnP Device instance to handle it.
	7. "WSC_MSG" info:

                 8                 4       4       4      2    2        6      variable length(MAXIMUM=232)
	+------------+----+----+----+--+--+------+------------------------+
	|  Signature       |eID  |aID  | TL   | F | SL|devMac| WSC_MSG                          |

*/
int WscSendUPnPMessage(
	IN PRTMP_ADAPTER	pAd, 
	IN UCHAR			devIfIdx,
	IN USHORT			msgType,
	IN USHORT			msgSubType,
	IN PUCHAR			pData,
	IN INT				dataLen,
	IN UINT				eventID,
	IN UINT				toIPAddr,
	IN PUCHAR			pMACAddr)
{
//	union iwreq_data wrqu;
	RTMP_WSC_NLMSG_HDR *pNLMsgHdr;
	RTMP_WSC_MSG_HDR *pWscMsgHdr;
	
	UCHAR hdrBuf[42]; //RTMP_WSC_NLMSG_HDR_LEN + RTMP_WSC_MSG_HDR_LEN
	int totalLen, leftLen, copyLen;
	PUCHAR pBuf = NULL, pBufPtr = NULL, pPos = NULL;
	PUCHAR	pDevAddr;
	UCHAR	bssIdx = devIfIdx;
	ULONG Now;

	DBGPRINT(RT_DEBUG_TRACE, ("-----> WscSendUPnPMessage\n"));

	if ((msgType & WSC_OPCODE_UPNP_MASK) != WSC_OPCODE_UPNP_MASK)
		return FALSE;
		
#ifdef CONFIG_STA_SUPPORT
	IF_DEV_CONFIG_OPMODE_ON_STA(pAd)
	{
		pDevAddr = &pAd->CurrentAddress[bssIdx];
	}
#endif // CONFIG_STA_SUPPORT //

	//Prepare the NLMsg header
	memset(hdrBuf, 0, sizeof(hdrBuf));
	pNLMsgHdr = (RTMP_WSC_NLMSG_HDR *)hdrBuf;
	memcpy(pNLMsgHdr, WSC_MSG_SIGNATURE, RTMP_WSC_NLMSG_SIGNATURE_LEN);

	NdisGetSystemUpTime(&Now);
	pNLMsgHdr->envID = Now;
	pNLMsgHdr->ackID = eventID;
	pNLMsgHdr->msgLen = dataLen + RTMP_WSC_MSG_HDR_LEN;

	/* 
		In order to support multiple wscd, we need this new field to notify 
		the wscd which interface this msg send from.
	*/
	NdisMoveMemory(&pNLMsgHdr->devAddr[0],  pDevAddr, MAC_ADDR_LEN);

	//Prepare the WscMsg header
	pWscMsgHdr = (RTMP_WSC_MSG_HDR *)(hdrBuf + sizeof(RTMP_WSC_NLMSG_HDR));
	switch(msgType)
	{
		case WSC_OPCODE_UPNP_DATA:
				pWscMsgHdr->msgType = WSC_OPCODE_UPNP_DATA;
				break;
		case WSC_OPCODE_UPNP_MGMT:
				pWscMsgHdr->msgType = WSC_OPCODE_UPNP_MGMT;
				break;
		case WSC_OPCODE_UPNP_CTRL:
				pWscMsgHdr->msgType = WSC_OPCODE_UPNP_CTRL;
				break;
		default:
				return FALSE;
	}
	pWscMsgHdr->msgSubType = msgSubType;
	pWscMsgHdr->ipAddr = toIPAddr;
	pWscMsgHdr->msgLen = dataLen;
	
	if ((pWscMsgHdr->msgType == WSC_OPCODE_UPNP_DATA) && 
		(eventID == 0) &&
		(pMACAddr != NULL) && 
		(NdisEqualMemory(pMACAddr, ZERO_MAC_ADDR, MAC_ADDR_LEN) == FALSE))
	{
		pWscMsgHdr->msgSubType |= WSC_UPNP_DATA_SUB_INCLUDE_MAC;
		pNLMsgHdr->msgLen += MAC_ADDR_LEN;
		pWscMsgHdr->msgLen += MAC_ADDR_LEN;
	}
	
	//Allocate memory and copy the msg.
	totalLen = leftLen = pNLMsgHdr->msgLen;
	pPos = pData;
	if((pBuf = kmalloc(IWEVCUSTOM_MSG_MAX_LEN, GFP_ATOMIC)) != NULL)
	{
		int firstSeg = 1;
	
		while(leftLen)
		{
			//Prepare the payload 
			memset(pBuf, 0, IWEVCUSTOM_MSG_MAX_LEN);

			pNLMsgHdr->segLen = (leftLen > IWEVCUSTOM_PAYLOD_MAX_LEN ? IWEVCUSTOM_PAYLOD_MAX_LEN : leftLen);
			leftLen -= pNLMsgHdr->segLen;
			pNLMsgHdr->flags = (leftLen > 0 ? 1 : 0);

			memcpy(pBuf, pNLMsgHdr, RTMP_WSC_NLMSG_HDR_LEN);
			pBufPtr = &pBuf[RTMP_WSC_NLMSG_HDR_LEN];

			if(firstSeg){
				memcpy(pBufPtr, pWscMsgHdr, RTMP_WSC_MSG_HDR_LEN);
				pBufPtr += RTMP_WSC_MSG_HDR_LEN;
				copyLen = (pNLMsgHdr->segLen - RTMP_WSC_MSG_HDR_LEN);
				if ((pWscMsgHdr->msgSubType & WSC_UPNP_DATA_SUB_INCLUDE_MAC) == WSC_UPNP_DATA_SUB_INCLUDE_MAC)
				{
					NdisMoveMemory(pBufPtr, pMACAddr, MAC_ADDR_LEN);
					pBufPtr += MAC_ADDR_LEN;
					copyLen -= MAC_ADDR_LEN;
				}
				NdisMoveMemory(pBufPtr, pPos, copyLen);
				pPos += copyLen;
				firstSeg = 0;
			} else {
				NdisMoveMemory(pBufPtr, pPos, pNLMsgHdr->segLen);
				pPos += pNLMsgHdr->segLen;
			}
						
			//Send WSC Msg to wscd, msg length = pNLMsgHdr->segLen + sizeof(RTMP_WSC_NLMSG_HDR)
			RtmpOSWrielessEventSend(pAd, IWEVCUSTOM, RT_WSC_UPNP_EVENT_FLAG, NULL, pBuf, pNLMsgHdr->segLen + sizeof(RTMP_WSC_NLMSG_HDR));
		}
		
		kfree(pBuf);
	}

	DBGPRINT(RT_DEBUG_TRACE, ("<----- WscSendUPnPMessage\n"));
	return TRUE;
}


VOID	WscSendMessage(
	IN	PRTMP_ADAPTER		pAdapter, 
	IN  UCHAR               OpCode,
	IN  PUCHAR				pData,
	IN  INT					Len,
	IN  PWSC_CTRL           pWscControl,
    IN  UCHAR               OpMode)
{
        // Inb-EAP Message
    UCHAR               Header802_3[14];
    	USHORT				Length;
    	IEEE8021X_FRAME		Ieee_8021x;
    	EAP_FRAME			EapFrame;
    	WSC_FRAME			WscFrame;
	UCHAR				*pOutBuffer = NULL;
        ULONG				FrameLen = 0;
    
	// Send message
	DBGPRINT(RT_DEBUG_TRACE, ("-----> WscSendMessage\n"));

	NdisZeroMemory(Header802_3,sizeof(UCHAR)*14);

#ifdef CONFIG_STA_SUPPORT
	IF_DEV_CONFIG_OPMODE_ON_STA(pAdapter)
	{
	        if (OpMode == STA_MODE)
	        {
				MAKE_802_3_HEADER(Header802_3, &pAdapter->CommonCfg.Bssid[0], &pAdapter->CurrentAddress[0], EAPOL);
	        }
	}
#endif // CONFIG_STA_SUPPORT //

    	// Length = EAP + WSC_Frame + Payload
    	Length = sizeof(EAP_FRAME) + sizeof(WSC_FRAME) + Len;
    	
    	// Zero 802.1x body
    	NdisZeroMemory(&Ieee_8021x, sizeof(Ieee_8021x));
    	Ieee_8021x.Version = EAPOL_VER;
    	Ieee_8021x.Type    = EAPPacket;
    	Ieee_8021x.Length  = cpu2be16(Length);

    	// Zero EAP frame
    	NdisZeroMemory(&EapFrame, sizeof(EapFrame));

        if (OpMode == AP_MODE)
        {
        	EapFrame.Code   = EAP_CODE_REQ;
        	EapFrame.Id     = ++(pWscControl->lastId);
        }
        else if (OpMode == AP_CLIENT_MODE)
        {
            EapFrame.Code   = EAP_CODE_RSP;
            EapFrame.Id     = pWscControl->lastId; // same as AP eap_req id
        }        
        else if (OpMode == STA_MODE)
        {
            EapFrame.Code   = EAP_CODE_RSP;
            EapFrame.Id     = pWscControl->lastId; // same as AP eap_req id
        }

    	EapFrame.Length = cpu2be16(Length);
    	EapFrame.Type   = EAP_TYPE_WSC;

    	// Zero WSC Frame
    	NdisZeroMemory(&WscFrame, sizeof(WscFrame));
    	WscFrame.SMI[0] = 0x00;
    	WscFrame.SMI[1] = 0x37;
    	WscFrame.SMI[2] = 0x2A;
    	WscFrame.VendorType = cpu2be32(WSC_VENDOR_TYPE);
    	WscFrame.OpCode = OpCode;
    	WscFrame.Flags  = 0x00;

        // Out buffer for transmitting message
    pOutBuffer = kmalloc(MAX_LEN_OF_MLME_BUFFER, MEM_ALLOC_FLAG);
    if(pOutBuffer == NULL)
            return;

    	FrameLen = 0;
    	
    	// Make	 Transmitting frame
    	if (pData && (Len > 0))
    	MakeOutgoingFrame(pOutBuffer, &FrameLen,
    						sizeof(IEEE8021X_FRAME), &Ieee_8021x,
    						sizeof(EapFrame), &EapFrame, 
    						sizeof(WscFrame), &WscFrame, 
    						Len, pData,
        		END_OF_ARGS);
        else
        MakeOutgoingFrame(pOutBuffer, &FrameLen,
							sizeof(IEEE8021X_FRAME), &Ieee_8021x,
							sizeof(EapFrame), &EapFrame, 
							sizeof(WscFrame), &WscFrame, 
							END_OF_ARGS);

	// Copy frame to Tx ring
#ifdef CONFIG_STA_SUPPORT
	IF_DEV_CONFIG_OPMODE_ON_STA(pAdapter)
		RTMPToWirelessSta((PRTMP_ADAPTER)pWscControl->pAd, &pAdapter->MacTab.Content[BSSID_WCID],
							Header802_3, LENGTH_802_3, (PUCHAR)pOutBuffer, FrameLen, TRUE);
#endif // CONFIG_STA_SUPPORT //
    	
	if (pOutBuffer)
		kfree(pOutBuffer);
	DBGPRINT(RT_DEBUG_TRACE, ("<----- WscSendMessage\n"));	
}


#ifdef CONFIG_STA_SUPPORT
/*
	========================================================================
	
	Routine Description:
		Make WSC IE for the ProbeReq frame

	Arguments:
		pAdapter    - NIC Adapter pointer
		pOutBuf		- all of WSC IE field 
		pIeLen		- length
		
	Return Value:
		None

	IRQL = DISPATCH_LEVEL
	
	Note:
		None
		
	========================================================================
*/
VOID WscMakeProbeReqIE(
	IN	PRTMP_ADAPTER	pAd,
	OUT	PUCHAR			pOutBuf,
	OUT	PUCHAR			pIeLen)
{
	UCHAR			WscIEFixed[] = {0xdd, 0x0e, 0x00, 0x50, 0xf2, 0x04};	// length will modify later
	UCHAR			OutMsgBuf[512];		// buffer to create message contents
	UCHAR			Len =0, templen = 0;
	PUCHAR			pData;
	USHORT          tempVal = 0;
	PWSC_REG_DATA	pReg = (PWSC_REG_DATA) &pAd->StaCfg.WscControl.RegData;

	DBGPRINT(RT_DEBUG_TRACE, ("-----> WscMakeProbeReqIE\n"));

	pData = (PUCHAR) &OutMsgBuf[0];
	Len = 0;
	*pIeLen = 0;
	
	// 0. WSC fixed IE
	RTMPMoveMemory(pData, &WscIEFixed[0], 6);
	pData += 6;
	Len += 6;
				
	// 1. Version
	templen = AppendWSCTLV(WSC_ID_VERSION, pData, &pReg->SelfInfo.Version, 0);
	pData += templen;
	Len   += templen;

	// 2. Request Type, Enrollee, 802.1x
	tempVal = 1;
    templen = AppendWSCTLV(WSC_ID_REQ_TYPE, pData, (UINT8 *)&tempVal, 0);
	pData += templen;
	Len   += templen;

	// 3. Config method
	tempVal = htons(0x008c);
	templen = AppendWSCTLV(WSC_ID_CONFIG_METHODS, pData, (UINT8 *)&tempVal, 0);
	pData += templen;
	Len   += templen;

	// 4. UUID
	templen = AppendWSCTLV(WSC_ID_UUID_E, pData, pReg->SelfInfo.Uuid, 0);
	pData += templen;
	Len   += templen;

	// 5. Primary device type
	templen = AppendWSCTLV(WSC_ID_PRIM_DEV_TYPE, pData, pReg->SelfInfo.PriDeviceType, 0);
	pData += templen;
	Len   += templen;

	// 6. RF band, shall change based on current channel
    templen = AppendWSCTLV(WSC_ID_RF_BAND, pData, &pReg->SelfInfo.RfBand, 0);
	pData += templen;
	Len   += templen;

	// 7. Associate state
	tempVal = pReg->SelfInfo.AssocState;
	templen = AppendWSCTLV(WSC_ID_ASSOC_STATE, pData, (UINT8 *)&tempVal, 0);
	pData += templen;
	Len   += templen;
				
	// 8. Config error
	tempVal = pReg->SelfInfo.ConfigError;
	templen = AppendWSCTLV(WSC_ID_CONFIG_ERROR, pData, (UINT8 *)&tempVal, 0);
	pData += templen;
	Len   += templen;

	// 9. Device password ID
	tempVal = pReg->SelfInfo.DevPwdId;
	templen = AppendWSCTLV(WSC_ID_DEVICE_PWD_ID, pData, (UINT8 *)&tempVal, 0);
	pData += templen;
	Len   += templen;

	// update the total length in vendor specific IE
	OutMsgBuf[1] = Len - 2;

	// fill in output buffer
	*pIeLen = Len;
	NdisMoveMemory(pOutBuf, &OutMsgBuf[0], *pIeLen);
	
	DBGPRINT(RT_DEBUG_TRACE, ("<----- WscMakeProbeReqIE\n"));
}

VOID WscPBCTimeOutAction(
    IN PVOID SystemSpecific1, 
    IN PVOID FunctionContext, 
    IN PVOID SystemSpecific2, 
    IN PVOID SystemSpecific3)
{
    RTMP_ADAPTER    *pAd = (PRTMP_ADAPTER)FunctionContext;
    PWSC_CTRL       pWscControl = &pAd->StaCfg.WscControl;
    BOOLEAN         Cancelled;

    DBGPRINT(RT_DEBUG_OFF, ("-----> WscPBCTimeOutAction\n"));

	if (pAd != NULL)
	{
	    if (pWscControl->WscPBCTimerRunning)
	    {
	        pWscControl->WscPBCTimerRunning = FALSE;
	        RTMPCancelTimer(&pWscControl->WscPBCTimer, &Cancelled);
	    }

		WscPBCExec(pAd, FALSE);
		
		// call Mlme handler to execute it
		RTMP_MLME_HANDLER(pAd);
	}
    DBGPRINT(RT_DEBUG_OFF, ("<----- WscPBCTimeOutAction\n"));
}

/*
	========================================================================
	
	Routine Description:
		Exec scan after scan timer expiration 

	Arguments:
		FunctionContext		NIC Adapter pointer
		
	Return Value:
		None
		
	IRQL = DISPATCH_LEVEL
	
	Note:
		
	========================================================================
*/
VOID WscScanTimeOutAction(
    IN PVOID SystemSpecific1,
    IN PVOID FunctionContext,
    IN PVOID SystemSpecific2,
    IN PVOID SystemSpecific3)
{
    RTMP_ADAPTER    *pAdapter = (PRTMP_ADAPTER)FunctionContext;

	if (pAdapter != NULL)
	{
		// call to execute the scan actions
		WscScanExec(pAdapter);

		// register 10 second timer for PBC or PIN connection execution
		if (pAdapter->StaCfg.WscControl.WscMode == WSC_PBC_MODE)
		{
		    // Prevent infinite loop if conncet time out didn't stop the repeat scan
			if (pAdapter->StaCfg.WscControl.WscState != WSC_STATE_OFF)
	        {      
			    RTMPSetTimer(&pAdapter->StaCfg.WscControl.WscPBCTimer, 10000);
	            pAdapter->StaCfg.WscControl.WscPBCTimerRunning = TRUE;
	        }
		}
		else if (pAdapter->StaCfg.WscControl.WscMode == WSC_PIN_MODE)
		{
	        // Prevent infinite loop if conncet time out didn't stop the repeat scan
		}

		DBGPRINT(RT_DEBUG_OFF, ("!!! WscScanTimeOutAction !!!\n"));
	    
		// call Mlme handler to execute it
		RTMP_MLME_HANDLER(pAdapter);
	}
}


VOID WscProfileRetryTimeout(
	IN PVOID SystemSpecific1,
	IN PVOID FunctionContext,
	IN PVOID SystemSpecific2,
	IN PVOID SystemSpecific3)
{
	RTMP_ADAPTER    *pAdapter = (PRTMP_ADAPTER)FunctionContext;
	PWSC_CTRL		pWscControl = NULL;		

	if (pAdapter != NULL)
	{
		pWscControl = &pAdapter->StaCfg.WscControl;
		DBGPRINT(RT_DEBUG_TRACE, ("WscProfileRetryTimeout:: WSC profile retry timeout index: %d\n", pWscControl->WscProfile.ApplyProfileIdx));	
		if (!INFRA_ON(pAdapter))
		{
			if (pWscControl->WscProfile.ApplyProfileIdx < pWscControl->WscProfile.ProfileCnt-1)
				pWscControl->WscProfile.ApplyProfileIdx++;
			else
				pWscControl->WscProfile.ApplyProfileIdx = 0;
			WscWriteConfToPortCfg(pAdapter, pWscControl, &pWscControl->WscProfile.Profile[pWscControl->WscProfile.ApplyProfileIdx], TRUE);
			pAdapter->WriteWscCfgToDatFile = TRUE;
#ifdef KTHREAD_SUPPORT
			WAKE_UP(&(pAdapter->wscCfgWriteTask));
#else
			RTMP_SEM_EVENT_UP(&(pAdapter->wscCfgWriteTask.taskSema));
#endif
			DBGPRINT(RT_DEBUG_TRACE, ("WscProfileRetryTimeout:: WSC profile retry index: %d\n", pWscControl->WscProfile.ApplyProfileIdx));
		}
		pAdapter->StaCfg.bAutoConnectByBssid = FALSE;
	}
}
#endif // CONFIG_STA_SUPPORT //


INT ComputeChecksum(
	IN UINT PIN)
{
	INT digit_s;
    UINT accum = 0;

	PIN *= 10;
	accum += 3 * ((PIN / 10000000) % 10); 
	accum += 1 * ((PIN / 1000000) % 10); 
	accum += 3 * ((PIN / 100000) % 10); 
	accum += 1 * ((PIN / 10000) % 10); 
	accum += 3 * ((PIN / 1000) % 10); 
	accum += 1 * ((PIN / 100) % 10); 
	accum += 3 * ((PIN / 10) % 10); 

	digit_s = (accum % 10);
	return ((10 - digit_s) % 10);
} // ComputeChecksum

BOOLEAN ValidateChecksum(
	IN UINT PIN)
{
	UINT accum = 0;

	accum += 3 * ((PIN / 10000000) % 10); 
	accum += 1 * ((PIN / 1000000) % 10); 
	accum += 3 * ((PIN / 100000) % 10); 
	accum += 1 * ((PIN / 10000) % 10); 
	accum += 3 * ((PIN / 1000) % 10); 
	accum += 1 * ((PIN / 100) % 10); 
	accum += 3 * ((PIN / 10) % 10); 
	accum += 1 * ((PIN / 1) % 10); 
	
    return (0 == (accum % 10));
} // ValidateChecksum

UINT WscGeneratePinCode(
	IN	PRTMP_ADAPTER	pAd,
	IN	UCHAR			apidx)
{
	UCHAR	macAddr[MAC_ADDR_LEN];
	UINT 	iPin;
	UINT	checksum;

#ifdef CONFIG_STA_SUPPORT
	IF_DEV_CONFIG_OPMODE_ON_STA(pAd)
		NdisMoveMemory(&macAddr[0], pAd->CurrentAddress, MAC_ADDR_LEN);
#endif // CONFIG_STA_SUPPORT //

	iPin = macAddr[3] * 256 * 256 + macAddr[4] * 256 + macAddr[5];

	iPin = iPin % 10000000;
	checksum = ComputeChecksum( iPin );
	iPin = iPin*10 + checksum;

	return iPin;
}

UINT WscRandomGeneratePinCode(
	IN	PRTMP_ADAPTER	pAd,
	IN	UCHAR			apidx)
{
	UINT 	iPin;
	UINT	checksum;

	iPin = RandomByte(pAd) * 256 * 256 + RandomByte(pAd) * 256 + RandomByte(pAd);

	iPin = iPin % 10000000;
	checksum = ComputeChecksum( iPin );
	iPin = iPin*10 + checksum;

	return iPin;
}


VOID WscStop(
	IN	PRTMP_ADAPTER	pAd,
	IN  PWSC_CTRL       pWscControl)
{
	PWSC_UPNP_NODE_INFO pWscUPnPInfo;
    BOOLEAN Cancelled;


#ifdef CONFIG_STA_SUPPORT
	IF_DEV_CONFIG_OPMODE_ON_STA(pAd)
		pWscControl->bConfiguredAP = FALSE;
#endif // CONFIG_STA_SUPPORT //

	pWscUPnPInfo = &pWscControl->WscUPnPNodeInfo;
	
	if(pWscUPnPInfo->bUPnPMsgTimerRunning == TRUE)
	{
		pWscUPnPInfo->bUPnPMsgTimerRunning = FALSE;
		RTMPCancelTimer(&pWscUPnPInfo->UPnPMsgTimer, &Cancelled);
		pWscUPnPInfo->bUPnPMsgTimerPending = FALSE;
	}
	if(pWscUPnPInfo->bUPnPM2DTimerRunning)
	{
		pWscUPnPInfo->bUPnPM2DTimerRunning = FALSE;
		RTMPCancelTimer(&pWscUPnPInfo->UPnPM2DTimer, &Cancelled);
	}
	
    pWscUPnPInfo->bUPnPInProgress = FALSE;
    pWscUPnPInfo->M2DACKBalance = 0;
	pWscUPnPInfo->registrarID = 0;

	if (pWscControl->Wsc2MinsTimerRunning)
	{
		pWscControl->Wsc2MinsTimerRunning = FALSE;
		RTMPCancelTimer(&pWscControl->Wsc2MinsTimer, &Cancelled);
	}

	RTMPCancelTimer(&pWscControl->EapolTimer, &Cancelled);
	pWscControl->EapolTimerRunning = FALSE;
    NdisZeroMemory(pWscControl->EntryAddr, MAC_ADDR_LEN);

	pWscControl->WscSelReg = 0;
	if ( (pWscControl->WscStatus == STATUS_WSC_CONFIGURED) ||
         (pWscControl->WscStatus == STATUS_WSC_FAIL) )
         ;
    else
		pWscControl->WscStatus = STATUS_WSC_NOTUSED;
	pWscControl->WscState = WSC_STATE_OFF;
	pWscControl->lastId = 1;
	pWscControl->EapMsgRunning = FALSE;
	pWscControl->EapolTimerPending = FALSE;
	pWscControl->bWscTrigger = FALSE;

#ifdef CONFIG_STA_SUPPORT
	IF_DEV_CONFIG_OPMODE_ON_STA(pAd)
	{
		if (pWscControl->WscScanTimerRunning)
		{
			pWscControl->WscScanTimerRunning = FALSE;
			RTMPCancelTimer(&pWscControl->WscScanTimer, &Cancelled);
		}

		if (pWscControl->WscPBCTimerRunning)
		{
			pWscControl->WscPBCTimerRunning = FALSE;
			RTMPCancelTimer(&pWscControl->WscPBCTimer, &Cancelled);
		}
		if (pWscControl->WscProfileRetryTimerRunning)
		{
			pWscControl->WscProfileRetryTimerRunning = FALSE;
			RTMPCancelTimer(&pWscControl->WscProfileRetryTimer, &Cancelled);
		}
		pAd->MlmeAux.AutoReconnectSsidLen = pAd->MlmeAux.SsidLen;
		NdisZeroMemory(&pAd->MlmeAux.AutoReconnectSsid[0], MAX_LEN_OF_SSID);
		NdisMoveMemory(&pAd->MlmeAux.AutoReconnectSsid[0], &pAd->MlmeAux.Ssid[0], pAd->MlmeAux.SsidLen);
	}
#endif // CONFIG_STA_SUPPORT //

#ifdef WSC_LED_SUPPORT
	if (pWscControl->WscLEDTimerRunning)
	{
		pWscControl->WscLEDTimerRunning = FALSE;
		RTMPCancelTimer(&pWscControl->WscLEDTimer, &Cancelled);
	}
	if (pWscControl->WscSkipTurnOffLEDTimerRunning)
	{
		pWscControl->WscSkipTurnOffLEDTimerRunning = FALSE;
		RTMPCancelTimer(&pWscControl->WscSkipTurnOffLEDTimer, &Cancelled);
	}
	// Reset the WPS walk time.
	pWscControl->bWPSWalkTimeExpiration = FALSE;
#endif // WSC_LED_SUPPORT //

}

VOID WscInit(
	IN	PRTMP_ADAPTER	pAd,
	IN  UCHAR       	BssIndex)
{
	IN  PWSC_CTRL       pWscControl;

#ifdef CONFIG_STA_SUPPORT
	IF_DEV_CONFIG_OPMODE_ON_STA(pAd)
		pWscControl = &pAd->StaCfg.WscControl;
#endif // CONFIG_STA_SUPPORT //

#ifdef OLD_DH_KEY
	if (pWscControl->pPubKeyMem)
		memset(pWscControl->pPubKeyMem, 0, 15 * MAX_NN_DIGITS + 1);
	if (pWscControl->pPubKeyMem)
		memset(pWscControl->pPubKeyMem, 0, 15 * MAX_NN_DIGITS + 1);
#endif //OLD_DH_KEY
	
	if (pWscControl->WscConfMode == WSC_DISABLE)
	{
	}
	else
	{
		WscInitRegistrarPair(pAd, pWscControl, BssIndex);
		pWscControl->WscStatus = STATUS_WSC_IDLE;
	}
}

USHORT WscGetAuthType(
    IN NDIS_802_11_AUTHENTICATION_MODE authType)
{
	switch(authType)
	{
		case Ndis802_11AuthModeOpen:
			return WSC_AUTHTYPE_OPEN;
		case Ndis802_11AuthModeWPAPSK:
			return WSC_AUTHTYPE_WPAPSK;
		case Ndis802_11AuthModeShared:
			return WSC_AUTHTYPE_SHARED;
		case Ndis802_11AuthModeWPA:
			return WSC_AUTHTYPE_WPA;
		case Ndis802_11AuthModeWPA1WPA2:
		case Ndis802_11AuthModeWPA2:
			return WSC_AUTHTYPE_WPA2;
		case Ndis802_11AuthModeWPA1PSKWPA2PSK:
		case Ndis802_11AuthModeWPA2PSK:
			return WSC_AUTHTYPE_WPA2PSK;
		default:
			return WSC_AUTHTYPE_OPEN;
	}
}

USHORT WscGetEncryType(
    IN NDIS_802_11_WEP_STATUS encryType)
{
	switch(encryType)
	{
		case Ndis802_11WEPDisabled:
			return WSC_ENCRTYPE_NONE;
		case Ndis802_11WEPEnabled:
			return WSC_ENCRTYPE_WEP;
		case Ndis802_11Encryption2Enabled:
			return WSC_ENCRTYPE_TKIP;
		 default:
		case Ndis802_11Encryption3Enabled:
			return WSC_ENCRTYPE_AES;
	}
}

PSTRING   WscGetAuthTypeStr(
    IN  USHORT authFlag)
{
	switch(authFlag)
	{
		case WSC_AUTHTYPE_OPEN:
			return "OPEN";
        default:
		case WSC_AUTHTYPE_WPAPSK:
			return "WPAPSK";
		case WSC_AUTHTYPE_SHARED:
			return "SHARED";
		case WSC_AUTHTYPE_WPA:
			return "WPA";
		case WSC_AUTHTYPE_WPA2:
			return "WPA2";
		case (WSC_AUTHTYPE_WPAPSK | WSC_AUTHTYPE_WPA2PSK):
		case WSC_AUTHTYPE_WPA2PSK:
			return "WPA2PSK";
	}
}

PSTRING   WscGetEncryTypeStr(
    IN  USHORT encryFlag)
{
	switch(encryFlag)
	{
		case WSC_ENCRTYPE_NONE:
			return "NONE";
		case WSC_ENCRTYPE_WEP:
			return "WEP";
        default:
		case WSC_ENCRTYPE_TKIP:
			return "TKIP";
		case (WSC_ENCRTYPE_TKIP | WSC_ENCRTYPE_AES):
		case WSC_ENCRTYPE_AES:
			return "AES";
	}
}

void    WscWriteConfToPortCfg(
    IN  PRTMP_ADAPTER   pAd,
    IN  PWSC_CTRL       pWscControl,
#ifdef CONFIG_STA_SUPPORT
    IN  PWSC_CREDENTIAL pCredential,    
#endif // CONFIG_STA_SUPPORT //    
    IN  BOOLEAN         bEnrollee)
{
	UCHAR               CurApIdx = MAIN_MBSSID;


	DBGPRINT(RT_DEBUG_TRACE, ("-----> WscWriteConfToPortCfg\n"));
    


#ifdef CONFIG_STA_SUPPORT
		IF_DEV_CONFIG_OPMODE_ON_STA(pAd)
		{
			pAd->MlmeAux.AutoReconnectSsidLen = pCredential->SSID.SsidLength;
			NdisZeroMemory(pAd->MlmeAux.AutoReconnectSsid, NDIS_802_11_LENGTH_SSID);
			NdisMoveMemory(pAd->MlmeAux.AutoReconnectSsid, pCredential->SSID.Ssid, pAd->MlmeAux.AutoReconnectSsidLen);
			pAd->MlmeAux.SsidLen = pCredential->SSID.SsidLength;
			NdisZeroMemory(pAd->MlmeAux.Ssid, NDIS_802_11_LENGTH_SSID);
			NdisMoveMemory(pAd->MlmeAux.Ssid, pCredential->SSID.Ssid, pAd->MlmeAux.SsidLen);
		}
#endif // CONFIG_STA_SUPPORT //

		DBGPRINT(RT_DEBUG_TRACE, ("ra%d - AuthType: %u, EncrType: %u\n", CurApIdx, pCredential->AuthType, pCredential->EncrType));
		if (pCredential->AuthType & (WSC_AUTHTYPE_WPAPSK | WSC_AUTHTYPE_WPA2PSK))
		{
			if (!(pCredential->EncrType & (WSC_ENCRTYPE_TKIP | WSC_ENCRTYPE_AES)))
			{
				DBGPRINT(RT_DEBUG_TRACE, ("AuthType is WPAPSK or WPA2PAK.\n"
                                         "Get illegal EncrType(%d) from External Registrar, set EncrType to TKIP\n", 
                                          pCredential->EncrType));
				pCredential->EncrType = WSC_ENCRTYPE_TKIP;
			}
#ifdef CONFIG_STA_SUPPORT
			IF_DEV_CONFIG_OPMODE_ON_STA(pAd)
				pAd->StaCfg.WpaState = SS_START;
#endif // CONFIG_STA_SUPPORT //
		}
		WscSetAuthMode(pAd, CurApIdx, WscGetAuthTypeStr(pCredential->AuthType));
		WscSetEncrypType(pAd, CurApIdx, WscGetEncryTypeStr(pCredential->EncrType));
		if (pCredential->EncrType != WSC_ENCRTYPE_NONE)
		{
			if (pCredential->EncrType & (WSC_ENCRTYPE_TKIP | WSC_ENCRTYPE_AES))
			{
#ifdef CONFIG_STA_SUPPORT
				IF_DEV_CONFIG_OPMODE_ON_STA(pAd)
					pAd->StaCfg.DefaultKeyId = 0;
#endif // CONFIG_STA_SUPPORT //

				if (pCredential->KeyLength >= 8 && pCredential->KeyLength <= 64)
				{
					UCHAR  *pPMKBuf, *pSSIDStr;
					INT		ssidLen;
					STRING	PassphraseStr[65] = {0};
					
					pWscControl->WpaPskLen = pCredential->KeyLength;
					RTMPZeroMemory(pWscControl->WpaPsk, 64);
					RTMPMoveMemory(pWscControl->WpaPsk, pCredential->Key, pWscControl->WpaPskLen);
					RTMPMoveMemory(PassphraseStr, pCredential->Key, pWscControl->WpaPskLen);
#if 0
					if (pWscControl->WpaPskLen == 64)
					{
#ifdef CONFIG_STA_SUPPORT
						IF_DEV_CONFIG_OPMODE_ON_STA(pAd)
							AtoH((PSTRING)pWscControl->WpaPsk, pAd->StaCfg.PMK, 32);
#endif // CONFIG_STA_SUPPORT //
					}
					else
					{
						UCHAR       keyMaterial[40] = {0};
#ifdef CONFIG_STA_SUPPORT
						IF_DEV_CONFIG_OPMODE_ON_STA(pAd)
						{
							PasswordHash((PSTRING)pWscControl->WpaPsk, 
								pCredential->SSID.Ssid, pCredential->SSID.SsidLength, keyMaterial);
							NdisMoveMemory(pAd->StaCfg.PMK, keyMaterial, 32);
						}
#endif // CONFIG_STA_SUPPORT //

					}
#else
#ifdef CONFIG_STA_SUPPORT
					IF_DEV_CONFIG_OPMODE_ON_STA(pAd)
					{
						pPMKBuf = pAd->StaCfg.PMK;
						pSSIDStr = (PUCHAR)pCredential->SSID.Ssid;
						ssidLen = pCredential->SSID.SsidLength;
					}
#endif // CONFIG_STA_SUPPORT //
					RT_CfgSetWPAPSKKey(pAd, PassphraseStr, pSSIDStr, ssidLen, pPMKBuf);
#endif
					DBGPRINT(RT_DEBUG_TRACE, ("WpaPskLen = %d\n", pWscControl->WpaPskLen));
				}
				else
				{
					pWscControl->WpaPskLen = 0;
					DBGPRINT(RT_DEBUG_TRACE, ("WPAPSK: Invalid Key Length (%d)\n", pCredential->KeyLength));
				}
			}
			else if (pCredential->EncrType == WSC_ENCRTYPE_WEP)
			{
				UCHAR   WepKeyId = 0;
				USHORT  WepKeyLen = pCredential->KeyLength;

				if ((pCredential->KeyIndex >= 1) && (pCredential->KeyIndex <= 4))
				{
					WepKeyId = (pCredential->KeyIndex - 1); // KeyIndex = 1 ~ 4
#ifdef CONFIG_STA_SUPPORT                
					IF_DEV_CONFIG_OPMODE_ON_STA(pAd)
						pAd->StaCfg.DefaultKeyId = WepKeyId;
#endif // CONFIG_STA_SUPPORT //

					 // 5 or 13 ASCII characters
					// 10 or 26 Hex characters
					if (WepKeyLen == 5 || WepKeyLen == 13 || WepKeyLen == 10 || WepKeyLen == 26)
					{
						if (WepKeyLen == 5 || WepKeyLen == 13)
						{
							pAd->SharedKey[CurApIdx][WepKeyId].KeyLen = (UCHAR)WepKeyLen;
							memcpy(pAd->SharedKey[CurApIdx][WepKeyId].Key, 
								pCredential->Key, 
								WepKeyLen);
							if (WepKeyLen == 5)
								pAd->SharedKey[CurApIdx][WepKeyId].CipherAlg = CIPHER_WEP64;
							else
								pAd->SharedKey[CurApIdx][WepKeyId].CipherAlg = CIPHER_WEP128;
						}
						else
						{
							pAd->SharedKey[CurApIdx][WepKeyId].KeyLen = (UCHAR)(WepKeyLen/2);
							AtoH((PSTRING) pCredential->Key, pAd->SharedKey[CurApIdx][WepKeyId].Key, WepKeyLen/2);
							if (WepKeyLen == 10)
								pAd->SharedKey[CurApIdx][WepKeyId].CipherAlg = CIPHER_WEP64;
							else
								pAd->SharedKey[CurApIdx][WepKeyId].CipherAlg = CIPHER_WEP128;
						}
					}
					else
						DBGPRINT(RT_DEBUG_TRACE, ("WEP: Invalid Key Length (%d)\n", pCredential->KeyLength));
				}
				else
				{
					DBGPRINT(RT_DEBUG_TRACE, ("Unsupport default key index (%d)\n", WepKeyId));
#ifdef CONFIG_STA_SUPPORT
					IF_DEV_CONFIG_OPMODE_ON_STA(pAd)
						pAd->StaCfg.DefaultKeyId = 0;
#endif // CONFIG_STA_SUPPORT //

				}
			}
		}

#ifdef CONFIG_STA_SUPPORT	
	IF_DEV_CONFIG_OPMODE_ON_STA(pAd)
	{
		if (pWscControl->WscProfile.ProfileCnt > 1)
		{
		pWscControl->WscProfileRetryTimerRunning = TRUE;
		RTMPSetTimer(&pWscControl->WscProfileRetryTimer, WSC_PROFILE_RETRY_TIME_OUT);
	}
	}
#endif // CONFIG_STA_SUPPORT //

	DBGPRINT(RT_DEBUG_TRACE, ("<----- ra%d - WscWriteConfToPortCfg\n", CurApIdx));
}


VOID	WscWriteSsidToDatFile(
	IN  PRTMP_ADAPTER	pAd,
	IN  PSTRING		 	pTempStr,
	IN	BOOLEAN			bNewFormat)
{
	INT		offset = 0;

	if (bNewFormat == FALSE)
	{		
		NdisZeroMemory(pTempStr, 512);
		
#ifdef CONFIG_STA_SUPPORT
		IF_DEV_CONFIG_OPMODE_ON_STA(pAd)
		{
			PWSC_CREDENTIAL pCredential = &pAd->StaCfg.WscControl.WscProfile.Profile[0];
			NdisMoveMemory(pTempStr, "SSID=", strlen("SSID="));
			offset = strlen(pTempStr);
			NdisMoveMemory(pTempStr + offset, pCredential->SSID.Ssid, pCredential->SSID.SsidLength);
		}
#endif // CONFIG_STA_SUPPORT //
	}
}


VOID	WscWriteWpaPskToDatFile(
	IN  PRTMP_ADAPTER	pAd,
	IN  PSTRING		 	pTempStr,
	IN	BOOLEAN			bNewFormat)
{
	PWSC_CTRL		pWscControl;
	INT				offset = 0;

	if (bNewFormat == FALSE)
	{		
		NdisZeroMemory(pTempStr, 512);
		
#ifdef CONFIG_STA_SUPPORT
		IF_DEV_CONFIG_OPMODE_ON_STA(pAd)
		{
			pWscControl = &pAd->StaCfg.WscControl;
			NdisMoveMemory(pTempStr, "WPAPSK=", strlen("WPAPSK="));
			if (pWscControl->WpaPskLen)
			{
				offset = strlen(pTempStr);				
				NdisMoveMemory(pTempStr + offset, pWscControl->WpaPsk, pWscControl->WpaPskLen);
			}
		}
#endif // CONFIG_STA_SUPPORT //
	}
}


BOOLEAN WscCheckNonce(
	IN	PRTMP_ADAPTER	pAdapter, 
	IN	MLME_QUEUE_ELEM	*pElem,
	IN  BOOLEAN         bFlag,
	IN  PWSC_CTRL       pWscControl) 
{
    USHORT				Length;
	PUCHAR				pData;
	USHORT				WscType, WscLen, WscId;

    DBGPRINT(RT_DEBUG_TRACE, ("-----> WscCheckNonce\n"));
    
    if (bFlag)
    {
        // check Registrar Nonce
        WscId = WSC_ID_REGISTRAR_NONCE;
        DBGPRINT(RT_DEBUG_TRACE, ("check Registrar Nonce\n"));
    }
    else
    {
        // check Enrollee Nonce
        WscId = WSC_ID_ENROLLEE_NONCE;
        DBGPRINT(RT_DEBUG_TRACE, ("check Enrollee Nonce\n"));
    }
    
    pData = pElem->Msg;
    Length = pElem->MsgLen;

    // We have to look for WSC_IE_MSG_TYPE to classify M2 ~ M8, the remain size must large than 4
	while (Length > 4)
	{
		WSC_TLV_0B	TLV_Recv;
        char ZeroNonce[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
        
		memcpy((UINT8 *)&TLV_Recv, pData, 4);
		WscType = cpu2be16(TLV_Recv.tag);
		WscLen  = cpu2be16(TLV_Recv.len);
		pData  += 4;
		Length -= 4;
        
		if (WscType == WscId)
		{
			if (RTMPCompareMemory(pWscControl->RegData.SelfNonce, pData, 16) == 0)
			{
			    DBGPRINT(RT_DEBUG_TRACE, ("Nonce match!!\n"));
                DBGPRINT(RT_DEBUG_TRACE, ("<----- WscCheckNonce\n"));
				return TRUE;
			}
            else if (NdisEqualMemory(pData, ZeroNonce, 16))
            {
                // Intel external registrar will send WSC_NACK with enrollee nonce
                // "10 1A 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"
                // when AP is configured and user selects not to configure AP.
                DBGPRINT(RT_DEBUG_TRACE, ("Zero Enrollee Nonce!!\n"));
                DBGPRINT(RT_DEBUG_TRACE, ("<----- WscCheckNonce\n"));
                return TRUE;
            }
		}
        
		// Offset to net WSC Ie
		pData  += WscLen;
		Length -= WscLen;
	}

    DBGPRINT(RT_DEBUG_TRACE, ("Nonce mismatch!!\n"));
    DBGPRINT(RT_DEBUG_TRACE, ("<----- WscCheckNonce\n"));
    return FALSE;
}

VOID    WscGetRegDataPIN(
    IN  PRTMP_ADAPTER   pAdapter,
    IN  UINT            PinCode,
    IN  PWSC_CTRL       pWscControl)
{
	UCHAR	tempPIN[9] = {0};

    if (pWscControl->WscMode == 2)
        pWscControl->WscPinCode = 0;
    else
        pWscControl->WscPinCode = PinCode;

    memset(pWscControl->RegData.PIN, 0, 8);

	if (pWscControl->WscPinCode == 0)
	{
		sprintf((PSTRING) tempPIN, "00000000");
		memcpy(pWscControl->RegData.PIN, tempPIN, 8);
		pWscControl->RegData.PinCodeLen = 8;
	}
	else
	{
		if ( pWscControl->WscPinCodeLen == 4)
		{
			UCHAR	temp4PIN[5] = {0};
			sprintf((PSTRING) temp4PIN, "%04u", pWscControl->WscPinCode);
			memcpy(pWscControl->RegData.PIN, temp4PIN, 4);
			pWscControl->RegData.PinCodeLen = 4;
		}
		else
		{
			sprintf((PSTRING) tempPIN, "%08u", pWscControl->WscPinCode);
			memcpy(pWscControl->RegData.PIN, tempPIN, 8);
			pWscControl->RegData.PinCodeLen = 8;
		}
	}
}

VOID    WscEapActionDisabled(
    IN  PRTMP_ADAPTER       pAdapter,
    IN  PWSC_CTRL           pWscControl)
{
	INT     DataLen = 0;
	UCHAR   WscData[60] = {0};
	BOOLEAN Cancelled;
    
	pWscControl->RegData.SelfInfo.ConfigError = WSC_ERROR_DEVICE_BUSY;
	DataLen = BuildMessageNACK(pAdapter, pWscControl, WscData);


#ifdef CONFIG_STA_SUPPORT
	IF_DEV_CONFIG_OPMODE_ON_STA(pAdapter)
		WscSendMessage(pAdapter, WSC_OPCODE_NACK, WscData, DataLen, pWscControl, STA_MODE);
#endif // CONFIG_STA_SUPPORT //

	RTMPCancelTimer(&pWscControl->EapolTimer, &Cancelled);
	pWscControl->EapolTimerRunning = FALSE;
}

VOID    WscGetConfigErrFromNack(
    IN  PRTMP_ADAPTER       pAdapter,
    IN	MLME_QUEUE_ELEM	    *pElem)
{
    USHORT				Length = 0;
	PUCHAR				pData;
	USHORT				WscType, WscLen, ConfigError = 0;

    pData = pElem->Msg;
    Length = pElem->MsgLen;
    
	while (Length > 4)
	{
		WSC_TLV_0B	TLV_Recv;
		memcpy((UINT8 *)&TLV_Recv, pData, 4);
		WscType = cpu2be16(TLV_Recv.tag);
		WscLen  = cpu2be16(TLV_Recv.len);
		pData  += 4;
		Length -= 4;
        
		if (WscType == WSC_ID_CONFIG_ERROR)
		{
			NdisMoveMemory(&ConfigError, pData, sizeof(USHORT));
		    DBGPRINT(RT_DEBUG_TRACE, ("WSC_ID_CONFIG_ERROR: %d\n", ntohs(ConfigError)));
			return;
		}
        
		// Offset to net WSC Ie
		pData  += WscLen;
		Length -= WscLen;
	}
    DBGPRINT(RT_DEBUG_TRACE, ("WSC_ID_CONFIG_ERROR is missing\n"));
}

INT	WscSetAuthMode(
	IN	PRTMP_ADAPTER	pAd, 
	IN  UCHAR			apidx,
	IN	PSTRING			arg)
{

#ifdef CONFIG_STA_SUPPORT
	IF_DEV_CONFIG_OPMODE_ON_STA(pAd)
	{
		if ((strcmp(arg, "WEPAUTO") == 0) || (strcmp(arg, "wepauto") == 0))
			pAd->StaCfg.AuthMode = Ndis802_11AuthModeAutoSwitch;
		else if ((strcmp(arg, "OPEN") == 0) || (strcmp(arg, "open") == 0))
			pAd->StaCfg.AuthMode = Ndis802_11AuthModeOpen;
		else if ((strcmp(arg, "SHARED") == 0) || (strcmp(arg, "shared") == 0))
			pAd->StaCfg.AuthMode = Ndis802_11AuthModeShared;
		else if ((strcmp(arg, "WPAPSK") == 0) || (strcmp(arg, "wpapsk") == 0))
			pAd->StaCfg.AuthMode = Ndis802_11AuthModeWPAPSK;
		else if ((strcmp(arg, "WPANONE") == 0) || (strcmp(arg, "wpanone") == 0))
			pAd->StaCfg.AuthMode = Ndis802_11AuthModeWPANone;
		else if ((strcmp(arg, "WPA2PSK") == 0) || (strcmp(arg, "wpa2psk") == 0))
			pAd->StaCfg.AuthMode = Ndis802_11AuthModeWPA2PSK;    
#ifdef WPA_SUPPLICANT_SUPPORT
		else if ((strcmp(arg, "WPA") == 0) || (strcmp(arg, "wpa") == 0))
			pAd->StaCfg.AuthMode = Ndis802_11AuthModeWPA;    
		else if ((strcmp(arg, "WPA2") == 0) || (strcmp(arg, "wpa2") == 0))
			pAd->StaCfg.AuthMode = Ndis802_11AuthModeWPA2;
#endif // WPA_SUPPLICANT_SUPPORT //
		else
			return FALSE;  

		pAd->StaCfg.PortSecured = WPA_802_1X_PORT_NOT_SECURED;

		DBGPRINT(RT_DEBUG_TRACE, ("WscSetAuthMode::(AuthMode=%d)\n", pAd->StaCfg.AuthMode));
	}
#endif // CONFIG_STA_SUPPORT //

	return TRUE;
}

INT	WscSetEncrypType(
	IN	PRTMP_ADAPTER	pAd, 
	IN  UCHAR			apidx,
	IN	PSTRING			arg)
{

#ifdef CONFIG_STA_SUPPORT
	IF_DEV_CONFIG_OPMODE_ON_STA(pAd)
	{
		if ((strcmp(arg, "NONE") == 0) || (strcmp(arg, "none") == 0))
		{
			if (pAd->StaCfg.AuthMode >= Ndis802_11AuthModeWPA)
				return TRUE;    // do nothing
            
			pAd->StaCfg.WepStatus     = Ndis802_11WEPDisabled;
			pAd->StaCfg.PairCipher    = Ndis802_11WEPDisabled;
			pAd->StaCfg.GroupCipher   = Ndis802_11WEPDisabled;
		}
		else if ((strcmp(arg, "WEP") == 0) || (strcmp(arg, "wep") == 0))
		{
			if (pAd->StaCfg.AuthMode >= Ndis802_11AuthModeWPA)
				return TRUE;    // do nothing

			pAd->StaCfg.WepStatus     = Ndis802_11WEPEnabled;
			pAd->StaCfg.PairCipher    = Ndis802_11WEPEnabled;
			pAd->StaCfg.GroupCipher   = Ndis802_11WEPEnabled;
		}
		else if ((strcmp(arg, "TKIP") == 0) || (strcmp(arg, "tkip") == 0))
		{
			if (pAd->StaCfg.AuthMode < Ndis802_11AuthModeWPA)
				return TRUE;    // do nothing

			pAd->StaCfg.WepStatus     = Ndis802_11Encryption2Enabled;
			pAd->StaCfg.PairCipher    = Ndis802_11Encryption2Enabled;
			pAd->StaCfg.GroupCipher   = Ndis802_11Encryption2Enabled;
		}
		else if ((strcmp(arg, "AES") == 0) || (strcmp(arg, "aes") == 0))
		{
			if (pAd->StaCfg.AuthMode < Ndis802_11AuthModeWPA)
			return TRUE;    // do nothing

			pAd->StaCfg.WepStatus     = Ndis802_11Encryption3Enabled;
			pAd->StaCfg.PairCipher    = Ndis802_11Encryption3Enabled;
			pAd->StaCfg.GroupCipher   = Ndis802_11Encryption3Enabled;
		}
		else
			return FALSE;

		pAd->StaCfg.OrigWepStatus = pAd->StaCfg.WepStatus;

		DBGPRINT(RT_DEBUG_TRACE, ("WscSetEncrypType::(EncrypType=%d)\n", pAd->StaCfg.WepStatus));
	}
#endif // CONFIG_STA_SUPPORT //

	return TRUE;
}

#ifdef CONFIG_STA_SUPPORT
USHORT WscGetAuthTypeFromStr(
    IN  PSTRING          arg)
{
    if ((strcmp(arg, "OPEN") == 0) || (strcmp(arg, "open") == 0))
        return WSC_AUTHTYPE_OPEN;
    else if ((strcmp(arg, "SHARED") == 0) || (strcmp(arg, "shared") == 0))
        return WSC_AUTHTYPE_SHARED;
    else if ((strcmp(arg, "WPAPSK") == 0) || (strcmp(arg, "wpapsk") == 0))
        return WSC_AUTHTYPE_WPAPSK;
    else if ((strcmp(arg, "WPA2PSK") == 0) || (strcmp(arg, "wpa2psk") == 0))
        return WSC_AUTHTYPE_WPA2PSK;
#ifdef WPA_SUPPLICANT_SUPPORT
    else if ((strcmp(arg, "WPA") == 0) || (strcmp(arg, "wpa") == 0))
        return WSC_AUTHTYPE_WPA;
    else if ((strcmp(arg, "WPA2") == 0) || (strcmp(arg, "wpa2") == 0))
        return WSC_AUTHTYPE_WPA2;
#endif // WPA_SUPPLICANT_SUPPORT //
    else
        return 0;
}

USHORT WscGetEncrypTypeFromStr(
    IN  PSTRING          arg)
{
    if ((strcmp(arg, "NONE") == 0) || (strcmp(arg, "none") == 0))
        return WSC_ENCRTYPE_NONE;
    else if ((strcmp(arg, "WEP") == 0) || (strcmp(arg, "wep") == 0))
        return WSC_ENCRTYPE_WEP;
    else if ((strcmp(arg, "TKIP") == 0) || (strcmp(arg, "tkip") == 0))
        return WSC_ENCRTYPE_TKIP;
    else if ((strcmp(arg, "AES") == 0) || (strcmp(arg, "aes") == 0))
        return WSC_ENCRTYPE_AES;
    else
        return 0;
}

/*
	========================================================================
	
	Routine Description:
		Push PBC from HW/SW Buttton

	Arguments:
		pAdapter    - NIC Adapter pointer
		
	Return Value:
		None

	IRQL = DISPATCH_LEVEL
	
	Note:
		
	========================================================================
*/

VOID	WscPushPBCAction(
	IN	PRTMP_ADAPTER	pAdapter)
{
	BOOLEAN		Cancelled;
	PWSC_CTRL   pWscControl = &pAdapter->StaCfg.WscControl;
		
	DBGPRINT(RT_DEBUG_TRACE, ("-----> WscPushPBCAction\n"));

	// 0. PBC mode, disregard the SSID information, we have to get the current AP list 
	//    and check the beacon for Push buttoned AP.

	// 1. Cancel old timer to prevent use push continuously
	if (pWscControl->Wsc2MinsTimerRunning)
	{
		pWscControl->Wsc2MinsTimerRunning = FALSE;
		RTMPCancelTimer(&pWscControl->Wsc2MinsTimer, &Cancelled);
	}
	if (pWscControl->WscScanTimerRunning)
	{
		pWscControl->WscScanTimerRunning = FALSE;
		RTMPCancelTimer(&pWscControl->WscScanTimer, &Cancelled);
	}
	if (pWscControl->WscPBCTimerRunning)
	{
		pWscControl->WscPBCTimerRunning = FALSE;
		RTMPCancelTimer(&pWscControl->WscPBCTimer, &Cancelled);
	}
	
	// Set WSC state to WSC_STATE_INIT
	pAdapter->StaCfg.WscControl.WscState = WSC_STATE_INIT;
	pAdapter->StaCfg.WscControl.WscStatus = STATUS_WSC_SCAN_AP;

	// Init Registrar pair structures
	WscInitRegistrarPair(pAdapter, &pAdapter->StaCfg.WscControl, BSS0);

	// For PBC, the PIN is all '0'
	WscGetRegDataPIN(pAdapter, pAdapter->StaCfg.WscControl.WscPinCode, pWscControl);
 				
	// 2. Set 2 min timout routine
	RTMPSetTimer(&pWscControl->Wsc2MinsTimer, WSC_TWO_MINS_TIME_OUT);
	pWscControl->Wsc2MinsTimerRunning = TRUE;
	pWscControl->bWscTrigger = TRUE;	// start work

	// 3. Call WscScan subroutine
	WscScanExec(pAdapter);

	// 4. Set 10 second timer to invoke PBC connection actions.
	RTMPSetTimer(&pWscControl->WscPBCTimer, 10000);
	pWscControl->WscPBCTimerRunning = TRUE;

	DBGPRINT(RT_DEBUG_TRACE, ("<----- WscPushPBCAction\n"));
}

/*
	========================================================================
	
	Routine Description:
		Doing an active scan with empty SSID, the scanened list will
		be processed in PBCexec or PINexec routines

	Arguments:
		pAd         - NIC Adapter pointer
		
	Return Value:
		None
		
	IRQL = DISPATCH_LEVEL
	
	Note:
		
	========================================================================
*/
VOID	WscScanExec(
	IN	PRTMP_ADAPTER	pAdapter) 
{
	MLME_SCAN_REQ_STRUCT	ScanReq;
//	UCHAR					ZeroSsid[32];

	// Prevent infinite loop if conncet time out didn't stop the repeat scan
	if ((pAdapter->StaCfg.WscControl.WscStatus == STATUS_WSC_FAIL) ||
		(pAdapter->StaCfg.WscControl.WscState == WSC_STATE_OFF))
		return;
	
	// Make sure connect state machine is idle
	if (pAdapter->Mlme.CntlMachine.CurrState != CNTL_IDLE)
	{
		RTMP_MLME_RESET_STATE_MACHINE(pAdapter);
		DBGPRINT(RT_DEBUG_OFF, ("!!! MLME busy, reset MLME state machine !!!\n"));
	}

	DBGPRINT(RT_DEBUG_OFF, ("!!! WscScanExec !!!\n"));

	pAdapter->StaCfg.WscControl.WscStatus = STATUS_WSC_SCAN_AP;

	// Prepare Scan parameters and enqueue the MLME command
//	NdisZeroMemory(ZeroSsid, 32);
	ScanParmFill(pAdapter, &ScanReq, "", 0, BSS_ANY, SCAN_WSC_ACTIVE);
	MlmeEnqueue(pAdapter, SYNC_STATE_MACHINE, MT2_MLME_SCAN_REQ, sizeof(MLME_SCAN_REQ_STRUCT), &ScanReq);
	pAdapter->Mlme.CntlMachine.CurrState = CNTL_WAIT_OID_LIST_SCAN;
}

/*
	========================================================================
	
	Routine Description:
		Doing PBC conenction verification, it will check current BSS list
		and find the correct number of PBC AP. If only 1 exists, it will
		start to make connection. Otherwise, it will set a scan timer
		to perform another scan for next PBC connection execution.

	Arguments:
		pAd         - NIC Adapter pointer
		
	Return Value:
		None
		
	IRQL = DISPATCH_LEVEL
	
	Note:
		
	========================================================================
*/
BOOLEAN	WscPBCExec(
	IN	PRTMP_ADAPTER	pAdapter,
	IN  BOOLEAN			bFromM2) 
{
	DBGPRINT(RT_DEBUG_OFF, ("-----> WscPBCExec !!!\n"));
	
	// 1. Search the qualified SSID from current SSID list
	WscPBCBssTableSort(pAdapter);
				
	// 2. Check the qualified AP for connection, if more than 1 AP avaliable, report error.
	if (pAdapter->StaCfg.WscControl.WscPBCBssCount != 1)
	{
		// Set WSC state to WSC_FAIL
		pAdapter->StaCfg.WscControl.WscState = WSC_STATE_FAIL;
		if (pAdapter->StaCfg.WscControl.WscPBCBssCount== 0)
			pAdapter->StaCfg.WscControl.WscStatus = STATUS_WSC_PBC_NO_AP;
		else
			pAdapter->StaCfg.WscControl.WscStatus = STATUS_WSC_PBC_TOO_MANY_AP;			
		DBGPRINT(RT_DEBUG_OFF, ("WscPBCExec --> AP list is %d, wait for next time\n", pAdapter->StaCfg.WscControl.WscPBCBssCount));
		
		// 2.1. Set 10 second timer to invoke another scan
		RTMPSetTimer(&pAdapter->StaCfg.WscControl.WscScanTimer, 10000);
        pAdapter->StaCfg.WscControl.WscScanTimerRunning = TRUE;
		if (pAdapter->CommonCfg.bWirelessEvent)
			RTMPSendWirelessEvent(pAdapter, IW_WSC_PBC_SESSION_OVERLAP, NULL, BSS0, 0); 

		// 2.2 We have to quit for now
		return FALSE;
	}

	if (bFromM2)
		return TRUE;

	// 3. Now we got the intend AP, Set the WSC state and enqueue the SSID connection command	
	pAdapter->MlmeAux.CurrReqIsFromNdis = FALSE; 

	if (pAdapter->Mlme.CntlMachine.CurrState != CNTL_IDLE)
	{
		RTMP_MLME_RESET_STATE_MACHINE(pAdapter);
		DBGPRINT(RT_DEBUG_OFF, ("!!! WscPBCExec --> MLME busy, reset MLME state machine !!!\n"));
	}
				
					
	// 4. Set WSC state to WSC_STATE_START
	pAdapter->StaCfg.WscControl.WscState = WSC_STATE_START;
	pAdapter->StaCfg.WscControl.WscStatus = STATUS_WSC_START_ASSOC;

	// 5. Enqueue BSSID connection command
	MlmeEnqueue(pAdapter, 
				MLME_CNTL_STATE_MACHINE, 
				OID_802_11_BSSID, 
				sizeof(NDIS_802_11_MAC_ADDRESS),
				(VOID *)&pAdapter->StaCfg.WscControl.WscBssid[0]);

	DBGPRINT(RT_DEBUG_OFF, ("<----- WscPBCExec !!!\n"));
	return TRUE;
}

/*
	========================================================================

	Routine Description:
		Find WSC PBC activated AP list

	Arguments:
		pAd         - NIC Adapter pointer
		OutTab		- Qualified AP BSS table
		
	Return Value:
		None
		
	IRQL = DISPATCH_LEVEL
	
	Note:
		All these constants are defined in wsc.h
		
	========================================================================
*/
VOID WscPBCBssTableSort(
	IN	PRTMP_ADAPTER	pAdapter)
{
	INT					i = 0, j = 0, Len = 0, idx = 0;
	UCHAR				*pData;
	PBSS_ENTRY			pInBss;
	PBEACON_EID_STRUCT	pEid;
	PWSC_IE				pWscIE;
	USHORT				DevicePasswordID;
	BOOLEAN				bFound, bSameAP;
	UUID_BSSID_CH_INFO	ApUuidBssid[8];
	UUID_BSSID_CH_INFO	TmpInfo;
	UCHAR				zeros16[16]= {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

	NdisZeroMemory(&ApUuidBssid[0], sizeof(UUID_BSSID_CH_INFO));
	pAdapter->StaCfg.WscControl.WscPBCBssCount = 0;
	for (i = 0; i < pAdapter->ScanTab.BssNr; i++) 
	{
		// BSS entry for VarIE processing
		pInBss  = (PBSS_ENTRY) &pAdapter->ScanTab.BssEntry[i];	
		pData   = (UCHAR *) &pInBss->VarIEs;
		bFound  = FALSE;
		bSameAP = FALSE;
		NdisZeroMemory(&TmpInfo, sizeof(UUID_BSSID_CH_INFO));

		// 1. Check VarIE length
		if (pInBss->VarIELen == 0)
			continue;

		// 2. Search for WSC IE - 0xdd xx 00 50 f2 04
		Len = (INT)pInBss->VarIELen;
		while ((Len > 0) && (bFound == FALSE))
		{
			pEid = (PBEACON_EID_STRUCT) pData;
			
			// No match, skip the Eid and move forward, IE_WFA_WSC = 0xdd
			if (pEid->Eid != IE_WFA_WSC)
			{
				// Set the offset and look for next IE
				pData += (pEid->Len + 2);
				Len   -= (pEid->Len + 2);
				continue;
			}
			else
			{
				// Found IE with 0xdd
				// check for WSC OUI -- 00 50 f2 04
				if (NdisEqualMemory(pEid->Octet, WPS_OUI, 4) == FALSE)
				{
					// Set the offset and look for next IE
					pData += (pEid->Len + 2);
					Len   -= (pEid->Len + 2);
					continue;
				}				
			}
			
			// 3. Found	AP with WSC IE in beacons, skip 6 bytes = 1 + 1 + 4
			pData += 6;
			Len   -= 6;

			// 4. Start to look the PBC type within WSC VarIE
			while (Len > 0)
			{
				// Check for WSC IEs
				pWscIE = (PWSC_IE) pData;

				// Check for device password ID, PBC = 0x0004
				if (be2cpu16(pWscIE->Type) == WSC_ID_DEVICE_PWD_ID)
				{
					// Found device password ID
#ifdef WINBOND
					//The Winbond's platform will fail to retrive 2-bytes data, if use the original
					//be2cpu16<--
					DevicePasswordID = WINBON_GET16((PUCHAR)&pWscIE->Data[0]);
#else
					DevicePasswordID = be2cpu16(*((USHORT *) &pWscIE->Data[0]));
#endif // WINBOND //
					DBGPRINT(RT_DEBUG_TRACE, ("WscPBCBssTableSort : DevicePasswordID = 0x%04x\n", DevicePasswordID));
					if (DevicePasswordID == DEV_PASS_ID_PBC)	// Check for PBC value
					{
						// Found matching PBC AP in current list, add it into table and add the count
						bFound = TRUE;
						
						DBGPRINT(RT_DEBUG_TRACE, ("PBC Found --> \n"));
						DBGPRINT(RT_DEBUG_TRACE, ("#%d  Bssid %02x:%02x:%02x:%02x:%02x:%02x\n", i,
							pInBss->Bssid[0], pInBss->Bssid[1], pInBss->Bssid[2], pInBss->Bssid[3], pInBss->Bssid[4], pInBss->Bssid[5]));

						if (pInBss->Channel > 14)
							TmpInfo.Band = WSC_RFBAND_50GHZ;
						else
							TmpInfo.Band = WSC_RFBAND_24GHZ;

						RTMPMoveMemory(&TmpInfo.Bssid[0], &pInBss->Bssid[0], MAC_ADDR_LEN);
						TmpInfo.Channel = pInBss->Channel;
						RTMPZeroMemory(&TmpInfo.Ssid[0], MAX_LEN_OF_SSID);
						RTMPMoveMemory(&TmpInfo.Ssid[0], &pInBss->Ssid[0], pInBss->SsidLen);
						TmpInfo.SsidLen = pInBss->SsidLen;
					}
				}

				// UUID_E is optional for beacons, but mandatory for probe-request
				if (be2cpu16(pWscIE->Type) == WSC_ID_UUID_E)
				{
					// Avoid error UUID-E storage from PIN mode
					RTMPMoveMemory(&TmpInfo.Uuid[0], (UCHAR *)(pData+4), 16);
				}
				
				// Set the offset and look for PBC information
				// Since Type and Length are both short type, we need to offset 4, not 2
				pData += (be2cpu16(pWscIE->Length) + 4);
				Len   -= (be2cpu16(pWscIE->Length) + 4);
			}
			
			if (bFound == TRUE)
			{							
				if (pAdapter->StaCfg.WscControl.WscPBCBssCount == 8)
				{
					i = pAdapter->ScanTab.BssNr;
					break;
				}
				
				if (pAdapter->StaCfg.WscControl.WscPBCBssCount > 0)
				{
					for (j = 0; j < pAdapter->StaCfg.WscControl.WscPBCBssCount; j++)
					{
						if (RTMPCompareMemory(&ApUuidBssid[j].Uuid[0], &TmpInfo.Uuid[0], 16) == 0)
						{
							if (RTMPCompareMemory(&TmpInfo.Uuid[0], zeros16, 16) != 0)
							{
								/*
									Same UUID, indicate concurrent AP
								 	We can indicate 1 AP only.
								*/
								bSameAP = TRUE;
								break;
							}
							else if (RTMPCompareMemory(&TmpInfo.Uuid[0], zeros16, 16) == 0)
							{
								if (ApUuidBssid[j].Band != TmpInfo.Band)
								{
									if (RTMPCompareMemory(&ApUuidBssid[j].Bssid[0], &TmpInfo.Bssid[0], 5) == 0)
									{
										/*
											Zero UUID at different band, and first 5bytes of two BSSIDs are the same.
											Indicate concurrent AP, we can indicate 1 AP only.
										*/
										bSameAP = TRUE;
										break;
									}
								}
							}
						}
						else if ((RTMPCompareMemory(&TmpInfo.Uuid[0], zeros16, 16) == 0) ||
								 (RTMPCompareMemory(&ApUuidBssid[j].Uuid[0], zeros16, 16) == 0))
						{
							if ((RTMPCompareMemory(&ApUuidBssid[j].Bssid[0], &TmpInfo.Bssid[0], 5) == 0) &&
								(ApUuidBssid[j].Band != TmpInfo.Band))
							{
								INT tmpDiff = (INT)(ApUuidBssid[j].Bssid[5] - TmpInfo.Bssid[5]);
								/*
									Zero UUID and Non-zero UUID at different band, and two BSSIDs are very close.
									Indicate concurrent AP, we can indicate 1 AP only.
								*/
								if ((tmpDiff <= 4) || 
									(tmpDiff >= -4))
								{
									bSameAP = TRUE;
									break;
								}
							}
						}
					}
				}
				
				if (bSameAP)
				{
					if ((pAdapter->StaCfg.WscControl.WpsApBand == PREFERRED_WPS_AP_PHY_TYPE_2DOT4_G_FIRST) &&
						(TmpInfo.Band == WSC_RFBAND_24GHZ) &&
						(ApUuidBssid[j].Band != TmpInfo.Band))
					{
						RTMPMoveMemory(&(ApUuidBssid[j].Bssid[0]), &TmpInfo.Bssid[0], MAC_ADDR_LEN);
						RTMPZeroMemory(&(ApUuidBssid[j].Ssid[0]), MAX_LEN_OF_SSID);
						RTMPMoveMemory(&(ApUuidBssid[j].Ssid[0]), &TmpInfo.Ssid[0], TmpInfo.SsidLen);
						ApUuidBssid[j].SsidLen = TmpInfo.SsidLen;
						ApUuidBssid[j].Channel = TmpInfo.Channel;
					}
					else if ((pAdapter->StaCfg.WscControl.WpsApBand == PREFERRED_WPS_AP_PHY_TYPE_5_G_FIRST) &&
							 (TmpInfo.Band == WSC_RFBAND_50GHZ) &&
							 (ApUuidBssid[j].Band != TmpInfo.Band))
					{
						RTMPMoveMemory(&(ApUuidBssid[j].Bssid[0]), &TmpInfo.Bssid[0], MAC_ADDR_LEN);
						RTMPZeroMemory(&(ApUuidBssid[j].Ssid[0]), MAX_LEN_OF_SSID);
						RTMPMoveMemory(&(ApUuidBssid[j].Ssid[0]), &TmpInfo.Ssid[0], TmpInfo.SsidLen);
						ApUuidBssid[j].SsidLen = TmpInfo.SsidLen;
						ApUuidBssid[j].Channel = TmpInfo.Channel;
					}
				}
				
				if (bSameAP == FALSE)
				{
					UCHAR index = pAdapter->StaCfg.WscControl.WscPBCBssCount;
					
					// Store UUID
					RTMPMoveMemory(&(ApUuidBssid[index].Uuid[0]), &TmpInfo.Uuid[0], 16);
					RTMPMoveMemory(&(ApUuidBssid[index].Bssid[0]), &pInBss->Bssid[0], MAC_ADDR_LEN);
					RTMPZeroMemory(&(ApUuidBssid[index].Ssid[0]), MAX_LEN_OF_SSID);
					RTMPMoveMemory(&(ApUuidBssid[index].Ssid[0]), &pInBss->Ssid[0], pInBss->SsidLen);
					ApUuidBssid[index].SsidLen = pInBss->SsidLen;
					ApUuidBssid[index].Channel = pInBss->Channel;
					if (ApUuidBssid[index].Channel > 14)
						ApUuidBssid[index].Band = WSC_RFBAND_50GHZ;
					else
						ApUuidBssid[index].Band = WSC_RFBAND_24GHZ;
					DBGPRINT(RT_DEBUG_ERROR, ("UUID-E= "));
					for(idx=0; idx<16; idx++)
						DBGPRINT_RAW(RT_DEBUG_ERROR, ("%02x  ", ApUuidBssid[index].Uuid[idx]));
					DBGPRINT(RT_DEBUG_ERROR, ("\n"));

					pAdapter->StaCfg.WscControl.WscPBCBssCount++;
				}
			}
		}
	}

	if (pAdapter->StaCfg.WscControl.WscPBCBssCount == 1)
	{
		RTMPZeroMemory(&pAdapter->StaCfg.WscControl.WscSsid, sizeof(NDIS_802_11_SSID));
		RTMPMoveMemory(pAdapter->StaCfg.WscControl.WscSsid.Ssid, ApUuidBssid[0].Ssid, ApUuidBssid[0].SsidLen);
		pAdapter->StaCfg.WscControl.WscSsid.SsidLength = ApUuidBssid[0].SsidLen;
		RTMPZeroMemory(pAdapter->StaCfg.WscControl.WscBssid, MAC_ADDR_LEN);
		RTMPMoveMemory(pAdapter->StaCfg.WscControl.WscBssid, ApUuidBssid[0].Bssid, MAC_ADDR_LEN);
		pAdapter->MlmeAux.Channel = ApUuidBssid[0].Channel;
	}
	
	DBGPRINT(RT_DEBUG_OFF, ("WscPBCBssTableSort : Total %d PBC AP Found\n", pAdapter->StaCfg.WscControl.WscPBCBssCount));
}
#endif // CONFIG_STA_SUPPORT //

VOID	WscCreateProfileFromCfg(
	IN	PRTMP_ADAPTER		pAdapter,
	IN  UCHAR               OpMode,
	IN  PWSC_CTRL           pWscControl,
	OUT PWSC_PROFILE        pWscProfile)
{
    UCHAR	        apidx = pWscControl->EntryIfIdx;
    USHORT          authType = 0, encyType = 0;
    UCHAR           WepKeyId = 0;
    PWSC_CREDENTIAL pCredential = NULL;

#ifdef CONFIG_STA_SUPPORT
	IF_DEV_CONFIG_OPMODE_ON_STA(pAdapter)
	{
		authType = WscGetAuthType(pAdapter->StaCfg.AuthMode);
		encyType = WscGetEncryType(pAdapter->StaCfg.WepStatus);
		WepKeyId = pAdapter->StaCfg.DefaultKeyId;
	}
#endif // CONFIG_STA_SUPPORT //    

	DBGPRINT(RT_DEBUG_TRACE, ("-----> WscGetDefaultProfileForM8\n"));

	pCredential = &pWscProfile->Profile[0]; //Only support one credential now. 20070515
	NdisZeroMemory(pCredential, sizeof(WSC_CREDENTIAL));
	pWscProfile->ProfileCnt = 1;

	{
		pCredential->KeyIndex = 1;
		pCredential->KeyLength = 0;
		NdisZeroMemory(pCredential->Key, 64);
		switch (encyType)
		{
			case WSC_ENCRTYPE_NONE:
				break;
			case WSC_ENCRTYPE_WEP:
				pCredential->KeyIndex = (WepKeyId + 1);
				if ((OpMode == AP_MODE || OpMode == STA_MODE) && pAdapter->SharedKey[apidx][WepKeyId].KeyLen)
				{
					int i;
					for (i=0; i<pAdapter->SharedKey[apidx][WepKeyId].KeyLen; i++)
						sprintf((PSTRING) pCredential->Key, "%s%02x", pCredential->Key, pAdapter->SharedKey[apidx][WepKeyId].Key[i]);
					pCredential->KeyLength = pAdapter->SharedKey[apidx][WepKeyId].KeyLen*2;
				}
				break;
			case WSC_ENCRTYPE_TKIP:
			case WSC_ENCRTYPE_AES:
				pCredential->KeyLength = pWscControl->WpaPskLen;
				memcpy(pCredential->Key, 
				pWscControl->WpaPsk, 
				pWscControl->WpaPskLen);
				break;
		}
		pCredential->AuthType = authType;
		pCredential->EncrType = encyType;
	}

#ifdef CONFIG_STA_SUPPORT
	IF_DEV_CONFIG_OPMODE_ON_STA(pAdapter)
	{
		NdisMoveMemory(pCredential->MacAddr, pAdapter->CommonCfg.Bssid, 6);
		NdisMoveMemory(pCredential->SSID.Ssid, pAdapter->CommonCfg.Ssid, pAdapter->CommonCfg.SsidLen);
		pCredential->SSID.SsidLength = pAdapter->CommonCfg.SsidLen;
	}
#endif // CONFIG_STA_SUPPORT //
    
    DBGPRINT(RT_DEBUG_TRACE, ("<----- WscCreateProfileFromCfg\n"));

}



#ifdef CONFIG_STA_SUPPORT
VOID   WpsStaSmProcess(
    IN PRTMP_ADAPTER        pAd,
    IN MLME_QUEUE_ELEM 	   *Elem)
{
    int                 HeaderLen = LENGTH_802_11 + LENGTH_802_1_H + sizeof(IEEE8021X_FRAME) + sizeof(EAP_FRAME);
    PHEADER_802_11      pHeader;
	
    pHeader = (PHEADER_802_11)Elem->Msg;
    						
	if((Elem->MsgType == WSC_EAPOL_UPNP_MSG) && (Elem->MsgLen > HeaderLen))
	{	//The WSC msg from UPnP daemon
		PUCHAR		pData;
        // Skip the (802.11 + 802.1h + 802.1x + EAP) header
    	pData = (PUCHAR) &Elem->Msg[HeaderLen];
        Elem->MsgLen -= HeaderLen;
		if (pData[0] == 'W' && pData[1] == 'F' && pData[2] == 'A')
		{
			STRING	id_data[] = "hello";
            NdisMoveMemory(Elem->Msg, id_data, strlen(id_data));
            Elem->MsgLen = strlen(id_data);
		}
		else
			NdisMoveMemory(Elem->Msg, pData, Elem->MsgLen);
		
		StateMachinePerformAction(pAd, &pAd->Mlme.WscMachine, Elem);
	}
    else if (Elem->MsgType == WSC_EAPOL_PACKET_MSG)
    {   // WSC_STATE_MACHINE can service only one station at one time
        PUCHAR		pData;
        PEAP_FRAME  pEapFrame;
        // Skip the EAP LLC header
    	pData = (PUCHAR) &Elem->Msg[LENGTH_802_11 + LENGTH_802_1_H];
        pEapFrame = (PEAP_FRAME)(pData + sizeof(IEEE8021X_FRAME));
        pAd->StaCfg.WscControl.lastId = pEapFrame->Id;
    	pData += sizeof(IEEE8021X_FRAME) + sizeof(EAP_FRAME);

        if (pEapFrame->Code == EAP_CODE_FAIL)
        { // EAP-Fail
            STRING	fail_data[] = "EAP_FAIL";
            NdisMoveMemory(Elem->Msg, fail_data, strlen(fail_data));
            Elem->MsgLen = strlen(fail_data);
        }
        else if ((pEapFrame->Code == EAP_CODE_REQ) && (pEapFrame->Type == EAP_TYPE_ID))
        { // EAP-Req (Identity)
            STRING	id_data[] = "hello";
            NdisMoveMemory(Elem->Msg, id_data, strlen(id_data));
            Elem->MsgLen = strlen(id_data);
        }
        else if ((pEapFrame->Code == EAP_CODE_REQ) && (pEapFrame->Type == EAP_TYPE_WSC))
        { // EAP-Req (Messages)
            if (Elem->MsgLen <= HeaderLen)
            {
                DBGPRINT(RT_DEBUG_ERROR, ("Elem->MsgLen(%ld) <= HeaderLen(%d) !!\n", Elem->MsgLen, HeaderLen));
                return;
            }
            Elem->MsgLen -= (LENGTH_802_11 + LENGTH_802_1_H + sizeof(IEEE8021X_FRAME) + sizeof(EAP_FRAME));
            if (WscCheckWSCHeader(pAd, pData))
            {
                PWSC_FRAME			pWsc = (PWSC_FRAME) pData;
                if (pWsc->OpCode == WSC_OPCODE_START)
                {
                    STRING	wsc_start[] = "WSC_START";
                    NdisMoveMemory(Elem->Msg, wsc_start, strlen(wsc_start));
                    Elem->MsgLen = strlen(wsc_start);
                }
                else
                {
                    pData += sizeof(WSC_FRAME);
                    Elem->MsgLen -= sizeof(WSC_FRAME);
                    NdisMoveMemory(Elem->Msg, pData, Elem->MsgLen);
                }
            }
            else
            {
                DBGPRINT(RT_DEBUG_TRACE, ("ERROR: WscCheckWSCHeader() return FALSE!\n"));
                return;
            }
        }
        else if (pEapFrame->Code == EAP_CODE_FAIL)
        { // EAP-Fail
            STRING	fail_data[] = "EAP_FAIL";
            NdisMoveMemory(Elem->Msg, fail_data, strlen(fail_data));
            Elem->MsgLen = strlen(fail_data);
        }
        StateMachinePerformAction(pAd, &pAd->Mlme.WscMachine, Elem);
    }
}
#endif // CONFIG_STA_SUPPORT //

VOID WscSendNACK(
    IN	PRTMP_ADAPTER	pAdapter,
    IN  MAC_TABLE_ENTRY *pEntry,
	IN  PWSC_CTRL       pWscControl)
{
    INT     DataLen = 0;
    PUCHAR  pWscData = NULL;
    BOOLEAN Cancelled;

	if( (pWscData = kmalloc(WSC_MAX_DATA_LEN, GFP_ATOMIC)) == NULL)
	{
		DBGPRINT(RT_DEBUG_TRACE, ("WscSendNACK:: WscData Allocate failed!\n"));
		return;
	}
	memset(pWscData, 0, WSC_MAX_DATA_LEN);
    DataLen = BuildMessageNACK(pAdapter, pWscControl, pWscData);            

#ifdef CONFIG_STA_SUPPORT
	IF_DEV_CONFIG_OPMODE_ON_STA(pAdapter)
        WscSendMessage(pAdapter, WSC_OPCODE_NACK, pWscData, DataLen, pWscControl, STA_MODE);
#endif // CONFIG_STA_SUPPORT //

    RTMPCancelTimer(&pWscControl->EapolTimer, &Cancelled);
    pWscControl->EapolTimerRunning = FALSE;
    pWscControl->RegData.ReComputePke = 1;

	if (pWscData)
		kfree(pWscData);
}

#ifdef CONFIG_STA_SUPPORT
VOID WscLinkDown(
	IN	PRTMP_ADAPTER	pAd)
{
	if (OPSTATUS_TEST_FLAG(pAd, fOP_STATUS_MEDIA_STATE_CONNECTED))
	{
		MLME_DISASSOC_REQ_STRUCT   DisassocReq;
		DBGPRINT(RT_DEBUG_TRACE, ("WscLinkDown(): Disassociate with current WPS AP...\n"));
		DisassocParmFill(pAd, &DisassocReq, pAd->CommonCfg.Bssid, REASON_DISASSOC_STA_LEAVING);
		MlmeEnqueue(pAd, ASSOC_STATE_MACHINE, MT2_MLME_DISASSOC_REQ, 
					sizeof(MLME_DISASSOC_REQ_STRUCT), &DisassocReq);
		pAd->Mlme.CntlMachine.CurrState = CNTL_WAIT_DISASSOC;
		pAd->MlmeAux.CurrReqIsFromNdis = TRUE;
		RTMP_MLME_HANDLER(pAd);
		RTMPusecDelay(500000);  // leave enough time for this DISASSOC frame
	}
	
	if (pAd->StaCfg.WscControl.WscConfMode != WSC_DISABLE)
	{		
		pAd->StaCfg.WscControl.WscState = WSC_STATE_START;
	}
	else
	{
		pAd->bConfigChanged = TRUE;
		pAd->StaCfg.WscControl.bWscTrigger = FALSE;
		MlmeEnqueue(pAd, 
					MLME_CNTL_STATE_MACHINE, 
					OID_802_11_BSSID,
					MAC_ADDR_LEN,
					pAd->MlmeAux.Bssid);

		RTMP_MLME_HANDLER(pAd);
	}
	pAd->StaCfg.WscControl.RegData.ReComputePke = 1;
}


VOID WscCheckDPIDFromWpsAP(
    IN  PRTMP_ADAPTER 	pAd, 
    IN  PEID_STRUCT		pEid,
    OUT PUSHORT			pDPIDFromAP)
{
	PUCHAR				pData;
	SHORT				Len = 0;
	PWSC_IE				pWscIE;
	USHORT				DevicePasswordID;
		
	if (NdisEqualMemory(pEid->Octet, WPS_OUI, 4))
	{
		pData = (PUCHAR) pEid->Octet + 4;
		Len = (SHORT)(pEid->Len - 4);

		while (Len > 0)
		{
			WSC_IE	WscIE;
			NdisMoveMemory(&WscIE, pData, sizeof(WSC_IE));
			// Check for WSC IEs
			pWscIE = &WscIE;
			
			// Check for device password ID, PIN = 0x0000, PBC = 0x0004
			if (be2cpu16(pWscIE->Type) == WSC_ID_DEVICE_PWD_ID)
			{
				// Found device password ID
				NdisMoveMemory(&DevicePasswordID, pData + 4, sizeof(DevicePasswordID));
				DevicePasswordID = be2cpu16(DevicePasswordID);
				DBGPRINT(RT_DEBUG_INFO, ("WscCheckDPIDFromWpsAP : DevicePasswordID = 0x%04x\n", DevicePasswordID));
				if (DevicePasswordID == DEV_PASS_ID_PIN)
				{
					// PIN
					*pDPIDFromAP = DEV_PASS_ID_PIN;
				}
				else if (DevicePasswordID == DEV_PASS_ID_PBC)
				{
					// PBC
					*pDPIDFromAP = DEV_PASS_ID_PBC;
				}
			}
			
			// Set the offset and look for PBC information
			// Since Type and Length are both short type, we need to offset 4, not 2
			pData += (be2cpu16(pWscIE->Length) + 4);
			Len   -= (be2cpu16(pWscIE->Length) + 4);
		}
	}

    return;
}

ULONG WscSearchWpsApBySSID(
	IN PRTMP_ADAPTER	pAd,	
	IN PUCHAR	 		pSsid,
	IN UCHAR	 		SsidLen,
	IN INT		 		WscMode)
{
	UCHAR 		i;
	USHORT		DesiredDPID;
	BSS_ENTRY 	*pBss;
	PWSC_CTRL	pWscControl = &pAd->StaCfg.WscControl;

	if (WscMode == WSC_PBC_MODE)
		DesiredDPID = DEV_PASS_ID_PBC;
	else
		DesiredDPID = DEV_PASS_ID_PIN;

	for (i = 0; i < pAd->ScanTab.BssNr; i++) 
	{
		pBss = &pAd->ScanTab.BssEntry[i];
		if (SSID_EQUAL(pSsid, SsidLen, pBss->Ssid, pBss->SsidLen) &&
			pBss->bWpsAP &&
			((pBss->WscDPIDFromWpsAP == DesiredDPID) || (DesiredDPID == DEV_PASS_ID_PIN)))
		{
			if ((pWscControl->WpsApBand == PREFERRED_WPS_AP_PHY_TYPE_5_G_FIRST) &&
				(pBss->Channel <= 14))
				continue;
			else if ((pWscControl->WpsApBand == PREFERRED_WPS_AP_PHY_TYPE_2DOT4_G_FIRST) &&
					 (pBss->Channel > 14))
				continue;
			else
				return (ULONG)i;
		}
	}
	return (ULONG)BSS_NOT_FOUND;
}
#endif // CONFIG_STA_SUPPORT //



void    WscWriteConfToDatFile(
    IN  PRTMP_ADAPTER pAd)
{
	PCHAR			cfgData = 0;
	PSTRING			fileName = NULL;
	RTMP_OS_FD		file_r, file_w;
	RTMP_OS_FS_INFO		osFSInfo;
	ssize_t			rv, fileLen = 0;
	CHAR			*offset = 0;
	PSTRING			pTempStr = 0;
	PWSC_CTRL		pWscControl = NULL;
	PWSC_CREDENTIAL	pCredentail = NULL;
	STRING			WepKeyName[10] = {0};
	STRING			WepKeyFormatName[10] = {0};
	INT				tempStrLen = 0;

	DBGPRINT(RT_DEBUG_TRACE, ("-----> WscWriteConfToDatFile\n"));


#ifdef CONFIG_STA_SUPPORT
	IF_DEV_CONFIG_OPMODE_ON_STA(pAd)
	{
		pWscControl = &pAd->StaCfg.WscControl;
#ifdef RTMP_RBUS_SUPPORT
		if (pAd->infType == RTMP_DEV_INF_RBUS)
			fileName = STA_PROFILE_PATH_RBUS;
		else
#endif // RTMP_RBUS_SUPPORT //
			fileName = STA_PROFILE_PATH;

		sprintf(WepKeyName, "Key%dStr=", pAd->StaCfg.DefaultKeyId+1);
		sprintf(WepKeyFormatName, "Key%dType=", pAd->StaCfg.DefaultKeyId+1);
	}
#endif // CONFIG_STA_SUPPORT //

	RtmpOSFSInfoChange(&osFSInfo, TRUE);

	file_r = RtmpOSFileOpen(fileName, O_RDONLY, 0);
	if (IS_FILE_OPEN_ERR(file_r)) 
	{
		DBGPRINT(RT_DEBUG_TRACE, ("-->1) %s: Error opening file %s\n", __FUNCTION__, fileName));
		return;
	}
	else 
	{
		char tempStr[64] = {0};
		while((rv = RtmpOSFileRead(file_r, tempStr, 64)) > 0)
		{
			fileLen += rv;
		}
		cfgData = kmalloc(fileLen, MEM_ALLOC_FLAG);
		if (cfgData == NULL)
		{
			RtmpOSFileClose(file_r);
			DBGPRINT(RT_DEBUG_TRACE, ("CfgData kmalloc fail. (fileLen = %d)\n", fileLen));
			goto out;
		}
		NdisZeroMemory(cfgData, fileLen);
		RtmpOSFileSeek(file_r, 0);
		rv = RtmpOSFileRead(file_r, (PSTRING)cfgData, fileLen);
		RtmpOSFileClose(file_r);
		if (rv != fileLen)
		{
			DBGPRINT(RT_DEBUG_TRACE, ("CfgData kmalloc fail, fileLen = %d\n", fileLen));
			goto ReadErr;
		}
	}

	file_w = RtmpOSFileOpen(fileName, O_WRONLY|O_TRUNC, 0);
	if (IS_FILE_OPEN_ERR(file_w)) 
	{
		goto WriteFileOpenErr;
	}
	else 
	{
		offset = (PCHAR) rtstrstr((PSTRING) cfgData, "Default\n");
		offset += strlen("Default\n");
		RtmpOSFileWrite(file_w, (PSTRING)cfgData, (int)(offset - cfgData));
		pTempStr = kmalloc(512, MEM_ALLOC_FLAG);
		if (!pTempStr)
		{
			DBGPRINT(RT_DEBUG_TRACE, ("pTempStr kmalloc fail. (512)\n"));
			RtmpOSFileClose(file_w);
			goto WriteErr;
		}
			
		for (;;)
		{
			int i = 0;
			PSTRING ptr;
			BOOLEAN	bNewFormat = TRUE;

			NdisZeroMemory(pTempStr, 512);
			ptr = (PSTRING) offset;
			while(*ptr && *ptr != '\n')
			{
				pTempStr[i++] = *ptr++;
			}
			pTempStr[i] = 0x00;
			if ((size_t)(offset - cfgData) < fileLen)
			{
				offset += strlen(pTempStr) + 1;
				if ((strncmp(pTempStr, "SSID=", strlen("SSID=")) == 0) || 
					strncmp(pTempStr, "SSID1=", strlen("SSID1=")) == 0 ||
					strncmp(pTempStr, "SSID2=", strlen("SSID2=")) == 0 ||
					strncmp(pTempStr, "SSID3=", strlen("SSID3=")) == 0 ||
					strncmp(pTempStr, "SSID4=", strlen("SSID4=")) == 0
				)
				{
					if (rtstrstr(pTempStr, "SSID="))
						bNewFormat = FALSE;

					WscWriteSsidToDatFile(pAd, pTempStr, bNewFormat);
				}
				else if (strncmp(pTempStr, "AuthMode=", strlen("AuthMode=")) == 0)
				{
					NdisZeroMemory(pTempStr, 512);
					sprintf(pTempStr, "AuthMode=");
#ifdef CONFIG_STA_SUPPORT
					IF_DEV_CONFIG_OPMODE_ON_STA(pAd)
					{
						USHORT auth_flag = WscGetAuthType(pAd->StaCfg.AuthMode);
						sprintf(pTempStr, "%s%s", pTempStr, WscGetAuthTypeStr(auth_flag));
					}
#endif // CONFIG_STA_SUPPORT //
				}
			else if (strncmp(pTempStr, "EncrypType=", strlen("EncrypType=")) == 0)
				{
					NdisZeroMemory(pTempStr, 512);
					sprintf(pTempStr, "EncrypType=");
#ifdef CONFIG_STA_SUPPORT
					IF_DEV_CONFIG_OPMODE_ON_STA(pAd)
					{
						USHORT encrypt_flag = WscGetEncryType(pAd->StaCfg.WepStatus);
						sprintf(pTempStr, "%s%s", pTempStr, WscGetEncryTypeStr(encrypt_flag));
					}
#endif // CONFIG_STA_SUPPORT //
				}    
				else if ((strncmp(pTempStr, "WPAPSK=", strlen("WPAPSK=")) == 0) || 
						(strncmp(pTempStr, "WPAPSK1=", strlen("WPAPSK1=")) == 0) ||
						(strncmp(pTempStr, "WPAPSK2=", strlen("WPAPSK2=")) == 0) ||
						(strncmp(pTempStr, "WPAPSK3=", strlen("WPAPSK3=")) == 0) ||
						(strncmp(pTempStr, "WPAPSK4=", strlen("WPAPSK4=")) == 0))
				{
						bNewFormat = TRUE;
						if (strstr(pTempStr, "WPAPSK="))                            
							bNewFormat = FALSE;
						WscWriteWpaPskToDatFile(pAd, pTempStr, bNewFormat);
				}
				else if (strncmp(pTempStr, "WscConfMode=", strlen("WscConfMode=")) == 0)
				{
						sprintf(pTempStr, "WscConfMode=");
#ifdef CONFIG_STA_SUPPORT
						sprintf(pTempStr, "%s%d", pTempStr, pWscControl->WscConfMode);
#endif // CONFIG_STA_SUPPORT //
				}
				else if (strncmp(pTempStr, "WscConfStatus=", strlen("WscConfStatus=")) == 0)
				{
						sprintf(pTempStr, "WscConfStatus=");
#ifdef CONFIG_STA_SUPPORT
						sprintf(pTempStr, "%s%d", pTempStr, pWscControl->WscConfStatus);
#endif // CONFIG_STA_SUPPORT //
				}
				else if (strncmp(pTempStr, "DefaultKeyID=", strlen("DefaultKeyID=")) == 0)
				{
					NdisZeroMemory(pTempStr, 512);
					sprintf(pTempStr, "DefaultKeyID=");
#ifdef CONFIG_STA_SUPPORT
					IF_DEV_CONFIG_OPMODE_ON_STA(pAd)
						sprintf(pTempStr, "%s%d", pTempStr, pAd->StaCfg.DefaultKeyId+1);
#endif // CONFIG_STA_SUPPORT //
				}

#ifdef CONFIG_STA_SUPPORT
				else if (rtstrstr(pTempStr, (PSTRING) WepKeyFormatName) &&  (pAd->OpMode == OPMODE_STA))
				{
						pCredentail = &pWscControl->WscProfile.Profile[pWscControl->WscProfile.ApplyProfileIdx];
					if (pAd->StaCfg.WepStatus == Ndis802_11WEPEnabled)                           
					{
						NdisZeroMemory(pTempStr, 512);
							if ((pCredentail->KeyLength == 5) || 
								(pCredentail->KeyLength == 13))
							sprintf(pTempStr, "%s1", WepKeyFormatName); // ASCII
						else
							sprintf(pTempStr, "%s0", WepKeyFormatName); // Hex
					}
				}
				else if (rtstrstr(pTempStr, (PSTRING) WepKeyName) &&  (pAd->OpMode == OPMODE_STA))
				{
						pCredentail = &pWscControl->WscProfile.Profile[pWscControl->WscProfile.ApplyProfileIdx];
					if (pAd->StaCfg.WepStatus == Ndis802_11WEPEnabled)                           
					{
						NdisZeroMemory(pTempStr, 512);
							NdisMoveMemory(pTempStr, WepKeyName, strlen(WepKeyName));
							tempStrLen = strlen(pTempStr);
							if (pCredentail->KeyLength)
								NdisMoveMemory(pTempStr + tempStrLen, pCredentail->Key, pCredentail->KeyLength);
					}
				}
#endif // CONFIG_STA_SUPPORT //

				RtmpOSFileWrite(file_w, pTempStr, strlen(pTempStr));
				RtmpOSFileWrite(file_w, "\n", 1);
			}
			else
			{
				break;
			}
		}
		RtmpOSFileClose(file_w);
	}

WriteErr:   
	if (pTempStr)
		kfree(pTempStr);
ReadErr:
WriteFileOpenErr:    
	if (cfgData)
		kfree(cfgData);
out:
	RtmpOSFSInfoChange(&osFSInfo, FALSE);


	DBGPRINT(RT_DEBUG_TRACE, ("<----- WscWriteConfToDatFile\n"));
	return;
}


static INT write_dat_file_thread (
    IN void *Context)
{
	RTMP_OS_TASK *pTask;
	RTMP_ADAPTER *pAd;
	int 	Status = 0;

	pTask = (RTMP_OS_TASK *)Context;
	pAd = (PRTMP_ADAPTER)pTask->priv;

	RtmpOSTaskCustomize(pTask);

	while (!pTask->task_killed)
	{
#ifdef KTHREAD_SUPPORT
		RTMP_WAIT_EVENT_INTERRUPTIBLE(pAd, pTask);
#else
		/* waiting for semaphore */
		RTMP_SEM_EVENT_WAIT(&(pTask->taskSema), Status);
#endif

#ifdef KTHREAD_SUPPORT
		if (pTask->task_killed == 1)
		{
			break;
		}
#endif
		
		if (Status != 0)
			break;

#ifdef RTMP_MAC_USB		
		/* device had been closed */
		if (RTMP_TEST_FLAG(pAd, fRTMP_ADAPTER_NIC_NOT_EXIST))
			break;
#endif // RTMP_MAC_USB //

		if (pAd->WriteWscCfgToDatFile)
		{
			WscWriteConfToDatFile(pAd);
			pAd->WriteWscCfgToDatFile = FALSE;
		}
	}

	RtmpOSTaskNotifyToExit(pTask);
	
	return 0;
}


/*
  * This kernel thread init in the probe fucntion, so we should kill it when do remove module.
  */
BOOLEAN WscThreadExit(RTMP_ADAPTER *pAd)
{	
	// This kernel thread init in the probe fucntion, so kill it when do remove module.
	RtmpOSTaskKill(&pAd->wscCfgWriteTask);
	
	if (pAd->pHmacData)
	{
		kfree(pAd->pHmacData);
		pAd->pHmacData = NULL;
	}
	return TRUE;
}


/*
  * This kernel thread init in the probe function.
  */
NDIS_STATUS WscThreadInit(RTMP_ADAPTER *pAd)
{
	NDIS_STATUS status = TRUE;
	RTMP_OS_TASK *pTask;

	
	DBGPRINT(RT_DEBUG_TRACE, ("-->WscThreadInit()\n"));

	pTask = &pAd->wscCfgWriteTask;
	
	InitializeWSCTLV();
	RtmpOSTaskInit(pTask, "RtmpWscTask", pAd);
	status = RtmpOSTaskAttach(pTask, write_dat_file_thread, &pAd->wscCfgWriteTask);
	if (status == NDIS_STATUS_SUCCESS)
	{
		if (pAd->pHmacData)
			kfree(pAd->pHmacData);
		pAd->pHmacData = kmalloc(sizeof(CHAR)*(2048), MEM_ALLOC_FLAG);
		if (pAd->pHmacData == NULL)
		{
			DBGPRINT(RT_DEBUG_ERROR, ("Wsc HmacData memory alloc failed!\n"));
			status = FALSE;
		}
	}

	DBGPRINT(RT_DEBUG_TRACE, ("<--WscThreadInit(), status=%d!\n", status));

	return status;
}


/* WSC hardware push button function 0811 */
/*
========================================================================
Routine Description:
	Initialize the PUSH PUTTION Check Module.

Arguments:
	ad_p			- WLAN control block pointer

Return Value:
	None

Note:
========================================================================
*/
VOID WSC_HDR_BTN_Init(
	IN	PRTMP_ADAPTER	pAd)
{
	pAd->CommonCfg.WscHdrPshBtnCheckCount = 0;
} /* End of WSC_HDR_BTN_Init */


/*
========================================================================
Routine Description:
	Stop the PUSH PUTTION Check Module.

Arguments:
	ad_p			- WLAN control block pointer

Return Value:
	None

Note:
========================================================================
*/
VOID WSC_HDR_BTN_Stop(
	IN	PRTMP_ADAPTER	pAd)
{
	pAd->CommonCfg.WscHdrPshBtnCheckCount = 0;
} /* End of WSC_HDR_BTN_Stop */


/*
========================================================================
Routine Description:
	Start the PUSH PUTTION Check thread.

Arguments:
	*Context		- WLAN control block pointer

Return Value:
	0			- terminate the thread successfully

Note:
========================================================================
*/

#ifdef CONFIG_STA_SUPPORT
extern INT	Set_WscConfMode_Proc(
	IN	PRTMP_ADAPTER	pAd, 
	IN	PUCHAR			arg);
extern INT	Set_WscMode_Proc(
	IN	PRTMP_ADAPTER	pAd, 
	IN	PUCHAR			arg);
extern INT	Set_WscGetConf_Proc(
	IN	PRTMP_ADAPTER	pAd, 
	IN	PUCHAR			arg);
#endif // CONFIG_STA_SUPPORT //

VOID WSC_HDR_BTN_CheckHandler(
	IN	PRTMP_ADAPTER	pAd)
{
 	POS_COOKIE pObj = (POS_COOKIE) pAd->OS_Cookie;
	BOOLEAN flg_pressed;


	WSC_HDR_BTN_MR_PRESS_FLG_GET(pAd, flg_pressed);

	if (flg_pressed)
	{
		/* the button is pressed */
		if (pAd->CommonCfg.WscHdrPshBtnCheckCount == WSC_HDR_BTN_CONT_TIMES)
		{
			/* we only handle once until the button is released */
			pAd->CommonCfg.WscHdrPshBtnCheckCount = 0;

			/* execute WSC PBC function */
			DBGPRINT(RT_DEBUG_TRACE, ("wsc> execute WSC PBC...\n"));


#ifdef CONFIG_STA_SUPPORT
			IF_DEV_CONFIG_OPMODE_ON_STA(pAd)
			{
				pObj->ioctl_if = 0;
				Set_WscConfMode_Proc(pAd, (PUCHAR)"1"); /* 1:  */
				Set_WscMode_Proc(pAd, (PUCHAR)"2"); /* 2: PBC */
				Set_WscGetConf_Proc(pAd, (PUCHAR)"1"); /* 1: Trigger */
			}
#endif // CONFIG_STA_SUPPORT //
			return;
		}

		pAd->CommonCfg.WscHdrPshBtnCheckCount ++;
	}
	else
	{
		/* the button is released */
		pAd->CommonCfg.WscHdrPshBtnCheckCount = 0;		
	}
}

#ifdef WSC_LED_SUPPORT
//
// Support WPS LED mode (mode 7, mode 8 and mode 9).
// Ref: User Feedback (page 80, WPS specification 1.0)
//
BOOLEAN WscSupportWPSLEDMode(
	IN PRTMP_ADAPTER pAdapter)
{
	if (((pAdapter->LedCntl.field.LedMode & LED_MODE) == WPS_LED_MODE_7) || 
	    ((pAdapter->LedCntl.field.LedMode & LED_MODE) == WPS_LED_MODE_8) || 
	    ((pAdapter->LedCntl.field.LedMode & LED_MODE) == WPS_LED_MODE_9))
	{
		DBGPRINT(RT_DEBUG_TRACE, ("%s: Support WPS LED mode (The WPS LED mode = %d).\n", 
			__FUNCTION__, (pAdapter->LedCntl.field.LedMode & LED_MODE)));
		return TRUE; // Support WPS LED mode.
	}
	else
	{
		DBGPRINT(RT_DEBUG_TRACE, ("%s: Not support WPS LED mode (The WPS LED mode = %d).\n", 
			__FUNCTION__, (pAdapter->LedCntl.field.LedMode & LED_MODE)));
		return FALSE; // Not support WPS LED mode.
	}
}

//
// Whether the WPS AP has security setting or not.
// Note that this function is valid only after the WPS handshaking.
//
BOOLEAN WscAPHasSecuritySetting(
	IN PRTMP_ADAPTER pAdapter,
	IN PWSC_CTRL     pWscControl)
{
	BOOLEAN bAPHasSecuritySetting = FALSE;
	UCHAR	currentIdx = MAIN_MBSSID;
	
#ifdef CONFIG_STA_SUPPORT
	currentIdx = pWscControl->WscProfile.ApplyProfileIdx;
#endif // CONFIG_STA_SUPPORT //

	switch (pWscControl->WscProfile.Profile[currentIdx].EncrType)
	{
		case WSC_ENCRTYPE_NONE:
		{
			bAPHasSecuritySetting = FALSE;
			break;
		}

		case WSC_ENCRTYPE_WEP:
		case WSC_ENCRTYPE_TKIP:
		case (WSC_ENCRTYPE_TKIP | WSC_ENCRTYPE_AES):
		case WSC_ENCRTYPE_AES:
		{
			bAPHasSecuritySetting = TRUE;
			break;
		}

		default:
		{
			DBGPRINT(RT_DEBUG_TRACE, ("%s: Incorrect encryption types (%d)\n", 
				__FUNCTION__, pWscControl->WscProfile.Profile[currentIdx].EncrType));
			ASSERT(FALSE);
			break;
		}
	}

	DBGPRINT(RT_DEBUG_TRACE, ("%s: WSC Entryption Type = %d\n", 
		__FUNCTION__, pWscControl->WscProfile.Profile[currentIdx].EncrType));

	return bAPHasSecuritySetting;
}

//
// After the NIC connects with a WPS AP or not, 
// the WscLEDTimer timer controls the LED behavior according to LED mode.
//
VOID WscLEDTimeOut(
	IN PVOID	SystemSpecific1, 
	IN PVOID	FunctionContext, 
	IN PVOID	SystemSpecific2, 
	IN PVOID	SystemSpecific3) 
{
	PWSC_CTRL pWscControl = (PWSC_CTRL)FunctionContext;
	PRTMP_ADAPTER pAd = (PRTMP_ADAPTER)pWscControl->pAd;
	UCHAR WPSLEDStatus;

	// WPS LED mode 7 and 8.
	if (((pAd->LedCntl.field.LedMode & LED_MODE) == WPS_LED_MODE_7) || 
	    ((pAd->LedCntl.field.LedMode & LED_MODE) == WPS_LED_MODE_8))
	{
		WPSLEDStatus = LED_WPS_TURN_LED_OFF;
#ifdef RTMP_MAC_USB
		RTUSBEnqueueInternalCmd(pAd, RT_CMD_SET_WPS_LED_STATUS, &WPSLEDStatus, sizeof(WPSLEDStatus));
#endif // RTMP_MAC_USB //
#ifdef RTMP_MAC_PCI
		RTMPSetLED(pAd, WPSLEDStatus);
#endif // RTMP_MAC_PCI //
		DBGPRINT(RT_DEBUG_TRACE, ("%s: Turn off the WPS successful LED pattern.\n", __FUNCTION__));
	}
	else if (((pAd->LedCntl.field.LedMode & LED_MODE) == WPS_LED_MODE_9)) // WPS LED mode 9.
	{
		switch (pWscControl->WscLEDMode) // Last WPS LED state.
		{
			// Turn off the blue LED after 300 seconds.
			case LED_WPS_SUCCESS:
				// Turn off the WPS LED for one second.
				WPSLEDStatus = LED_WPS_TURN_LED_OFF;
#ifdef RTMP_MAC_USB
				RTUSBEnqueueInternalCmd(pAd, RT_CMD_SET_WPS_LED_STATUS, &WPSLEDStatus, sizeof(WPSLEDStatus));
#endif // RTMP_MAC_USB //
#ifdef RTMP_MAC_PCI
				RTMPSetLED(pAd, WPSLEDStatus);
#endif // RTMP_MAC_PCI //

				// Turn on/off the WPS success LED according to AP's encryption algorithm after one second.
				RTMPSetTimer(&pWscControl->WscLEDTimer, WSC_WPS_TURN_OFF_LED_TIMEOUT);
				pWscControl->WscLEDTimerRunning = TRUE;

				DBGPRINT(RT_DEBUG_TRACE, ("%s: LED_WPS_SUCCESS => LED_WPS_TURN_LED_OFF\n", __FUNCTION__));
				break;

			// After turn off the blue LED for one second.
			// AP uses an encryption algorithm:
			// a) YES: Turn on the blue LED.
			// b) NO: Turn off the blue LED.
			case LED_WPS_TURN_LED_OFF:
				if ((pWscControl->WscState == WSC_STATE_OFF) && 
				     (pWscControl->WscStatus == STATUS_WSC_CONFIGURED))
				{
					if (WscAPHasSecuritySetting(pAd, pWscControl) == TRUE) // The NIC connects with an AP using an encryption algorithm.
					{
						// Turn WPS success LED.
						WPSLEDStatus = LED_WPS_TURN_ON_BLUE_LED;
#ifdef RTMP_MAC_USB
						RTUSBEnqueueInternalCmd(pAd, RT_CMD_SET_WPS_LED_STATUS, &WPSLEDStatus, sizeof(WPSLEDStatus));
#endif // RTMP_MAC_USB //
#ifdef RTMP_MAC_PCI
						RTMPSetLED(pAd, WPSLEDStatus);
#endif // RTMP_MAC_PCI //
						DBGPRINT(RT_DEBUG_TRACE, ("%s: LED_WPS_TURN_LED_OFF => LED_WPS_TURN_ON_BLUE_LED\n", __FUNCTION__));
					}
					else // The NIC connects with an AP using OPEN-NONE.
					{
						// Turn off the WPS LED.
						WPSLEDStatus = LED_WPS_TURN_LED_OFF;
#ifdef RTMP_MAC_USB
						RTUSBEnqueueInternalCmd(pAd, RT_CMD_SET_WPS_LED_STATUS, &WPSLEDStatus, sizeof(WPSLEDStatus));
#endif // RTMP_MAC_USB //
#ifdef RTMP_MAC_PCI
						RTMPSetLED(pAd, WPSLEDStatus);
#endif // RTMP_MAC_PCI //
						DBGPRINT(RT_DEBUG_TRACE, ("%s: LED_WPS_TURN_LED_OFF => LED_WPS_TURN_LED_OFF\n", __FUNCTION__));
					}
				}
				break;
				
			// Turn off the amber LED after 15 seconds.
			case LED_WPS_ERROR:
				// Turn off the WPS LED.
				WPSLEDStatus = LED_WPS_TURN_LED_OFF;
#ifdef RTMP_MAC_USB
				RTUSBEnqueueInternalCmd(pAd, RT_CMD_SET_WPS_LED_STATUS, &WPSLEDStatus, sizeof(WPSLEDStatus));
#endif // RTMP_MAC_USB //
#ifdef RTMP_MAC_PCI
				RTMPSetLED(pAd, WPSLEDStatus);
#endif // RTMP_MAC_PCI //
				DBGPRINT(RT_DEBUG_TRACE, ("%s: LED_WPS_ERROR/LED_WPS_SESSION_OVERLAP_DETECTED => LED_WPS_TURN_LED_OFF\n", __FUNCTION__));
				break;

			// Turn off the amber LED after ~3 seconds.
			case LED_WPS_SESSION_OVERLAP_DETECTED:
				// Turn off the WPS LED.
				WPSLEDStatus = LED_WPS_TURN_LED_OFF;
#ifdef RTMP_MAC_USB
				RTUSBEnqueueInternalCmd(pAd, RT_CMD_SET_WPS_LED_STATUS, &WPSLEDStatus, sizeof(WPSLEDStatus));
#endif // RTMP_MAC_USB //
#ifdef RTMP_MAC_PCI
				RTMPSetLED(pAd, WPSLEDStatus);
#endif // RTMP_MAC_PCI //
				DBGPRINT(RT_DEBUG_TRACE, ("%s: LED_WPS_SESSION_OVERLAP_DETECTED => LED_WPS_TURN_LED_OFF\n", __FUNCTION__));
				break;

			default:
				// do nothing.
				break;
		}
	}
	else
	{
		// do nothing.
	}	
}

VOID WscSkipTurnOffLEDTimeOut(
	IN PVOID	SystemSpecific1, 
	IN PVOID	FunctionContext, 
	IN PVOID	SystemSpecific2, 
	IN PVOID	SystemSpecific3) 
{
	PWSC_CTRL pWscControl = (PWSC_CTRL)FunctionContext;

	// Allow the NIC to turn off the WPS LED again.
	pWscControl->bSkipWPSTurnOffLED = FALSE;
	
	DBGPRINT(RT_DEBUG_TRACE, ("%s: Allow the NIC to turn off the WPS LED again.\n", __FUNCTION__));
}

#endif // WSC_LED_SUPPORT //
#endif // WSC_INCLUDED //
