/*************************************************************************** 
 * RT2x00 SourceForge Project - http://rt2x00.sourceforge.net              * 
 *                                                                         * 
 *   This program is free software; you can redistribute it and/or modify  * 
 *   it under the terms of the GNU General Public License as published by  * 
 *   the Free Software Foundation; either version 2 of the License, or     * 
 *   (at your option) any later version.                                   * 
 *                                                                         * 
 *   This program is distributed in the hope that it will be useful,       * 
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        * 
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         * 
 *   GNU General Public License for more details.                          * 
 *                                                                         * 
 *   You should have received a copy of the GNU General Public License     * 
 *   along with this program; if not, write to the                         * 
 *   Free Software Foundation, Inc.,                                       * 
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             * 
 *                                                                         * 
 *   Licensed under the GNU GPL                                            * 
 *   Original code supplied under license from RaLink Inc, 2004.           * 
 ***************************************************************************/

/*************************************************************************** 
 *	Module Name:	assoc.c
 *
 *	Abstract:
 *
 *	Revision History:
 *	Who		When		What
 *	--------	----------	-------------------------------
 *	Name		Date		Modification logs
 *	Jan Lee		2005-06-01	Release
 ***************************************************************************/

#include "rt_config.h"

UCHAR	CipherWpaTemplate[] = {
		0xdd, 					// WPA IE
		0x16,					// Length
		0x00, 0x50, 0xf2, 0x01,	// oui
		0x01, 0x00,				// Version
		0x00, 0x50, 0xf2, 0x02,	// Multicast
		0x01, 0x00,				// Number of unicast
		0x00, 0x50, 0xf2, 0x02,	// unicast
		0x01, 0x00,				// number of authentication method
		0x00, 0x50, 0xf2, 0x01	// authentication
		};

UCHAR	CipherWpa2Template[] = {
		0x30,					// RSN IE
		0x14,					// Length
		0x01, 0x00,				// Version
		0x00, 0x0f, 0xac, 0x02,	// group cipher, TKIP
		0x01, 0x00,				// number of pairwise
		0x00, 0x0f, 0xac, 0x02,	// unicast
		0x01, 0x00,				// number of authentication method
		0x00, 0x0f, 0xac, 0x02,	// authentication
		0x00, 0x00,				// RSN capability
		};

/*  
    ==========================================================================
    Description: 
        association state machine init, including state transition and timer init
    Parameters: 
        S - pointer to the association state machine
    Note:
        The state machine looks like the following 
        
                               ASSOC_IDLE               ASSOC_WAIT_RSP             REASSOC_WAIT_RSP             DISASSOC_WAIT_RSP
    MT2_MLME_ASSOC_REQ       mlme_assoc_req_action    invalid_state_when_assoc   invalid_state_when_assoc       invalid_state_when_assoc
    MT2_MLME_REASSOC_REQ     mlme_reassoc_req_action  invalid_state_when_reassoc invalid_state_when_reassoc     invalid_state_when_reassoc
    MT2_MLME_DISASSOC_REQ    mlme_disassoc_req_action mlme_disassoc_req_action   mlme_disassoc_req_action       mlme_disassoc_req_action
    MT2_PEER_DISASSOC_REQ    peer_disassoc_action     peer_disassoc_action       peer_disassoc_action           peer_disassoc_action
    MT2_PEER_ASSOC_REQ       drop                     drop                       drop                           drop
    MT2_PEER_ASSOC_RSP       drop                     peer_assoc_rsp_action      drop                           drop
    MT2_PEER_REASSOC_REQ     drop                     drop                       drop                           drop
    MT2_PEER_REASSOC_RSP     drop                     drop                       peer_reassoc_rsp_action        drop
    MT2_CLS3ERR              cls3err_action           cls3err_action             cls3err_action                 cls3err_action
    MT2_ASSOC_TIMEOUT        timer_nop                assoc_timeout_action       timer_nop                      timer_nop
    MT2_REASSOC_TIMEOUT      timer_nop                timer_nop                  reassoc_timeout_action         timer_nop
    MT2_DISASSOC_TIMEOUT     timer_nop                timer_nop                  timer_nop                      disassoc_timeout_action

	IRQL = PASSIVE_LEVEL
	
    ==========================================================================
 */
VOID AssocStateMachineInit(
    IN	PRT2570ADAPTER	pAd, 
    IN  STATE_MACHINE *S, 
    OUT STATE_MACHINE_FUNC Trans[]) 
{
	StateMachineInit(S, Trans, MAX_ASSOC_STATE, MAX_ASSOC_MSG, (STATE_MACHINE_FUNC)Drop, ASSOC_IDLE, ASSOC_MACHINE_BASE);

	// first column
	StateMachineSetAction(S, ASSOC_IDLE, MT2_MLME_ASSOC_REQ, (STATE_MACHINE_FUNC)MlmeAssocReqAction);
	StateMachineSetAction(S, ASSOC_IDLE, MT2_MLME_REASSOC_REQ, (STATE_MACHINE_FUNC)MlmeReassocReqAction);
	StateMachineSetAction(S, ASSOC_IDLE, MT2_MLME_DISASSOC_REQ, (STATE_MACHINE_FUNC)MlmeDisassocReqAction);
	StateMachineSetAction(S, ASSOC_IDLE, MT2_PEER_DISASSOC_REQ, (STATE_MACHINE_FUNC)PeerDisassocAction);
	//StateMachineSetAction(S, ASSOC_IDLE, MT2_CLS3ERR, Cls3errAction);

	// second column
	StateMachineSetAction(S, ASSOC_WAIT_RSP, MT2_MLME_ASSOC_REQ, (STATE_MACHINE_FUNC)InvalidStateWhenAssoc);
	StateMachineSetAction(S, ASSOC_WAIT_RSP, MT2_MLME_REASSOC_REQ, (STATE_MACHINE_FUNC)InvalidStateWhenReassoc);
	StateMachineSetAction(S, ASSOC_WAIT_RSP, MT2_MLME_DISASSOC_REQ, (STATE_MACHINE_FUNC)InvalidStateWhenDisassociate);
	StateMachineSetAction(S, ASSOC_WAIT_RSP, MT2_PEER_DISASSOC_REQ, (STATE_MACHINE_FUNC)PeerDisassocAction);
	StateMachineSetAction(S, ASSOC_WAIT_RSP, MT2_PEER_ASSOC_RSP, (STATE_MACHINE_FUNC)PeerAssocRspAction);
	//StateMachineSetAction(S, ASSOC_WAIT_RSP, MT2_CLS3ERR, Cls3errAction);
	StateMachineSetAction(S, ASSOC_WAIT_RSP, MT2_ASSOC_TIMEOUT, (STATE_MACHINE_FUNC)AssocTimeoutAction);

	// third column
	StateMachineSetAction(S, REASSOC_WAIT_RSP, MT2_MLME_ASSOC_REQ, (STATE_MACHINE_FUNC)InvalidStateWhenAssoc);
	StateMachineSetAction(S, REASSOC_WAIT_RSP, MT2_MLME_REASSOC_REQ, (STATE_MACHINE_FUNC)InvalidStateWhenReassoc);
	StateMachineSetAction(S, REASSOC_WAIT_RSP, MT2_MLME_DISASSOC_REQ, (STATE_MACHINE_FUNC)InvalidStateWhenDisassociate);
	StateMachineSetAction(S, REASSOC_WAIT_RSP, MT2_PEER_DISASSOC_REQ, (STATE_MACHINE_FUNC)PeerDisassocAction);
	StateMachineSetAction(S, REASSOC_WAIT_RSP, MT2_PEER_REASSOC_RSP, (STATE_MACHINE_FUNC)PeerReassocRspAction);
	// StateMachineSetAction(S, REASSOC_WAIT_RSP, MT2_CLS3ERR, Cls3errAction);
	StateMachineSetAction(S, REASSOC_WAIT_RSP, MT2_REASSOC_TIMEOUT, (STATE_MACHINE_FUNC)ReassocTimeoutAction);

	// fourth column
	StateMachineSetAction(S, DISASSOC_WAIT_RSP, MT2_MLME_ASSOC_REQ, (STATE_MACHINE_FUNC)InvalidStateWhenAssoc);
	StateMachineSetAction(S, DISASSOC_WAIT_RSP, MT2_MLME_REASSOC_REQ, (STATE_MACHINE_FUNC)InvalidStateWhenReassoc);
	StateMachineSetAction(S, DISASSOC_WAIT_RSP, MT2_MLME_DISASSOC_REQ, (STATE_MACHINE_FUNC)InvalidStateWhenDisassociate);
	StateMachineSetAction(S, DISASSOC_WAIT_RSP, MT2_PEER_DISASSOC_REQ, (STATE_MACHINE_FUNC)PeerDisassocAction);
	//StateMachineSetAction(S, DISASSOC_WAIT_RSP, MT2_CLS3ERR, Cls3errAction);
	StateMachineSetAction(S, DISASSOC_WAIT_RSP, MT2_DISASSOC_TIMEOUT, (STATE_MACHINE_FUNC)DisassocTimeoutAction);

	// initialize the timer
	RTMPInitTimer(pAd, &pAd->Mlme.AssocAux.AssocTimer, AssocTimeout);
	RTMPInitTimer(pAd, &pAd->Mlme.AssocAux.ReassocTimer, ReassocTimeout);
	RTMPInitTimer(pAd, &pAd->Mlme.AssocAux.DisassocTimer, DisassocTimeout);
}

/*
    ==========================================================================
    Description:
        Association timeout procedure. After association timeout, this function 
        will be called and it will put a message into the MLME queue
    Parameters:
        Standard timer parameters

	IRQL = DISPATCH_LEVEL

    ==========================================================================
 */
VOID  AssocTimeout(unsigned long data)
{
	PRT2570ADAPTER	pAd = (PRT2570ADAPTER)data;
	DBGPRINT(RT_DEBUG_TRACE, "ASSOC - enqueue MT2_ASSOC_TIMEOUT \n");
	
	// Do nothing if the driver is starting halt state.
	// This might happen when timer already been fired before cancel timer with mlmehalt
	if (RTMP_TEST_FLAG(pAd, fRTMP_ADAPTER_HALT_IN_PROGRESS))
		return;

	MlmeEnqueue(pAd, ASSOC_STATE_MACHINE, MT2_ASSOC_TIMEOUT, 0, NULL);
	//KeSetEvent(&pAd->MLMEEvent, 0, FALSE);	
	RTUSBUp(pAd, (&(pAd->mlme_semaphore)));
}

/*
    ==========================================================================
    Description:
        Reassociation timeout procedure. After reassociation timeout, this 
        function will be called and put a message into the MLME queue
    Parameters:
        Standard timer parameters

	IRQL = DISPATCH_LEVEL

    ==========================================================================
 */
VOID  ReassocTimeout(unsigned long data)
{
	PRT2570ADAPTER	pAd = (PRT2570ADAPTER)data;
	DBGPRINT(RT_DEBUG_TRACE,"ASSOC - enqueue MT2_REASSOC_TIMEOUT \n");
	
	// Do nothing if the driver is starting halt state.
	// This might happen when timer already been fired before cancel timer with mlmehalt
	if (RTMP_TEST_FLAG(pAd, fRTMP_ADAPTER_HALT_IN_PROGRESS))
		return;

	MlmeEnqueue(pAd, ASSOC_STATE_MACHINE, MT2_REASSOC_TIMEOUT, 0, NULL);
	//KeSetEvent(&pAd->MLMEEvent, 0, FALSE);	
	RTUSBUp(pAd, (&(pAd->mlme_semaphore)));
}

/*
    ==========================================================================
    Description:
        Disassociation timeout procedure. After disassociation timeout, this 
        function will be called and put a message into the MLME queue
    Parameters:
        Standard timer parameters
        
	IRQL = DISPATCH_LEVEL

    ==========================================================================
 */
VOID  DisassocTimeout(unsigned long data)
{
	PRT2570ADAPTER	pAd = (PRT2570ADAPTER)data;
	DBGPRINT(RT_DEBUG_TRACE,"ASSOC - enqueue MT2_DISASSOC_TIMEOUT \n");

	// Do nothing if the driver is starting halt state.
	// This might happen when timer already been fired before cancel timer with mlmehalt
	if (RTMP_TEST_FLAG(pAd, fRTMP_ADAPTER_HALT_IN_PROGRESS))
		return;

	MlmeEnqueue(pAd, ASSOC_STATE_MACHINE, MT2_DISASSOC_TIMEOUT, 0, NULL);
	//KeSetEvent(&pAd->MLMEEvent, 0, FALSE);	
	RTUSBUp(pAd, (&(pAd->mlme_semaphore)));
}

/*
    ==========================================================================
    Description:
        mlme assoc req handling procedure
    Parameters:
        Adapter - Adapter pointer
        Elem - MLME Queue Element
    Pre:
        the station has been authenticated and the following information is stored in the config
            -# SSID
            -# supported rates and their length
            -# listen interval (Adapter->PortCfg.default_listen_count)
            -# Transmit power  (Adapter->PortCfg.tx_power)
    Post  :
        -# An association request frame is generated and sent to the air
        -# Association timer starts
        -# Association state -> ASSOC_WAIT_RSP
        
	IRQL = DISPATCH_LEVEL

    ==========================================================================
 */
VOID MlmeAssocReqAction(
    IN PRT2570ADAPTER pAd, 
    IN MLME_QUEUE_ELEM *Elem) 
{
	MACADDR          ApAddr;
	MACHDR           AssocHdr;
	UCHAR            SsidIe = IE_SSID, RateIe = IE_SUPP_RATES, ExtRateIe = IE_EXT_SUPP_RATES;
	UCHAR			 CipherTmp[64];
	UCHAR			 CipherTmpLen;
	USHORT           ListenIntv;
	ULONG            Timeout;
	USHORT           CapabilityInfo;
	UCHAR           *OutBuffer = NULL;
	NDIS_STATUS      NStatus;
	ULONG            FrameLen = 0;
	ULONG			 tmp, idx;
	BOOLEAN			 FoundPMK = FALSE;
	UCHAR			 VarIesOffset;

	// Block all authentication request durning WPA block period
	if (pAd->PortCfg.bBlockAssoc == TRUE)
	{
		DBGPRINT(RT_DEBUG_TRACE, "ASSOC - Block Assoc request durning WPA block period!\n");
		pAd->Mlme.AssocMachine.CurrState = ASSOC_IDLE;
		MlmeCntlConfirm(pAd, MT2_ASSOC_CONF, MLME_STATE_MACHINE_REJECT);
	}	
	// check sanity first
	else if (MlmeAssocReqSanity(pAd, Elem->Msg, Elem->MsgLen, &ApAddr, &CapabilityInfo, &Timeout, &ListenIntv)) 
	{
        RTMPCancelTimer(&pAd->Mlme.AssocAux.AssocTimer);
        COPY_MAC_ADDR(&pAd->Mlme.AssocAux.Addr, &ApAddr);
		// Mask out unnecessary capability information
		CapabilityInfo &= SUPPORTED_CAPABILITY_INFO; // pAd->PortCfg.SupportedCapabilityInfo;
        pAd->Mlme.AssocAux.CapabilityInfo = CapabilityInfo;
        pAd->Mlme.AssocAux.ListenIntv = ListenIntv;

        NStatus = MlmeAllocateMemory(pAd, (PVOID)&OutBuffer);  //Get an unused nonpaged memory
        if (NStatus != NDIS_STATUS_SUCCESS) 
        {
            DBGPRINT(RT_DEBUG_TRACE,"ASSOC - MlmeAssocReqAction() allocate memory failed \n");
            pAd->Mlme.AssocMachine.CurrState = ASSOC_IDLE;
            MlmeCntlConfirm(pAd, MT2_ASSOC_CONF, MLME_FAIL_NO_RESOURCE);
            return;
        }
        
		// Add by James 03/06/27
		pAd->PortCfg.AssocInfo.Length = sizeof(NDIS_802_11_ASSOCIATION_INFORMATION);
		// Association don't need to report MAC address
		pAd->PortCfg.AssocInfo.AvailableRequestFixedIEs =
			NDIS_802_11_AI_REQFI_CAPABILITIES | NDIS_802_11_AI_REQFI_LISTENINTERVAL;
		pAd->PortCfg.AssocInfo.RequestFixedIEs.Capabilities = CapabilityInfo;
		pAd->PortCfg.AssocInfo.RequestFixedIEs.ListenInterval = ListenIntv;		
		// Only reassociate need this
		// memcpy(pAd->PortCfg.AssocInfo.RequestFixedIEs.CurrentAPAddress, &AssocHdr, sizeof(NDIS_802_11_MAC_ADDRESS));
		pAd->PortCfg.AssocInfo.OffsetRequestIEs = sizeof(NDIS_802_11_ASSOCIATION_INFORMATION);	
		
		// First add SSID
		VarIesOffset = 0;
		memcpy(pAd->PortCfg.ReqVarIEs + VarIesOffset, &SsidIe, 1);
		VarIesOffset += 1;
		memcpy(pAd->PortCfg.ReqVarIEs + VarIesOffset, &pAd->PortCfg.SsidLen, 1);
		VarIesOffset += 1;
		memcpy(pAd->PortCfg.ReqVarIEs + VarIesOffset, pAd->PortCfg.Ssid, pAd->PortCfg.SsidLen);
		VarIesOffset += pAd->PortCfg.SsidLen;

		// Second add Supported rates
		memcpy(pAd->PortCfg.ReqVarIEs + VarIesOffset, &RateIe, 1);
		VarIesOffset += 1;
		memcpy(pAd->PortCfg.ReqVarIEs + VarIesOffset, &pAd->PortCfg.SupportedRatesLen, 1);
		VarIesOffset += 1;
		memcpy(pAd->PortCfg.ReqVarIEs + VarIesOffset, pAd->PortCfg.SupportedRates, pAd->PortCfg.SupportedRatesLen);
		VarIesOffset += pAd->PortCfg.SupportedRatesLen;
		// End Add by James

        MgtMacHeaderInit(pAd, &AssocHdr, SUBTYPE_ASSOC_REQ, 0, &ApAddr, &ApAddr);
		
		// Build basic frame first
		MakeOutgoingFrame(
			OutBuffer,					&FrameLen,
						  sizeof(MACHDR),			&AssocHdr,
						  2,						&CapabilityInfo,
						  2,						&ListenIntv,
						  1,						&SsidIe,
						  1,						&pAd->PortCfg.SsidLen, 
						  pAd->PortCfg.SsidLen, 	pAd->PortCfg.Ssid,
						  1,						&RateIe,
						  1,						&pAd->PortCfg.SupRateLen,
						  pAd->PortCfg.SupRateLen,  pAd->PortCfg.SupRate,
						  END_OF_ARGS);
		if (pAd->PortCfg.ExtRateLen != 0)
		{
			MakeOutgoingFrame(
				OutBuffer + FrameLen,		&tmp,
						1,							&ExtRateIe,
						1,							&pAd->PortCfg.ExtRateLen,
						pAd->PortCfg.ExtRateLen,	pAd->PortCfg.ExtRate,							
						END_OF_ARGS);
			FrameLen += tmp;
		}
		
		// For WPA / WPA-PSK
		if ((pAd->PortCfg.AuthMode == Ndis802_11AuthModeWPA) || (pAd->PortCfg.AuthMode == Ndis802_11AuthModeWPAPSK))
		{
			// Copy WPA template to buffer
			CipherTmpLen = sizeof(CipherWpaTemplate);
			memcpy(CipherTmp, CipherWpaTemplate, CipherTmpLen);
			// Modify Group cipher
			CipherTmp[11] = ((pAd->PortCfg.GroupCipher == Ndis802_11Encryption2Enabled) ? 0x2 : 0x4);			
			// Modify Pairwise cipher
			CipherTmp[17] = ((pAd->PortCfg.PairCipher == Ndis802_11Encryption2Enabled) ? 0x2 : 0x4);
			// Modify AKM
			CipherTmp[23] = ((pAd->PortCfg.AuthMode == Ndis802_11AuthModeWPA) ? 0x1 : 0x2);
			// Make outgoing frame
			MakeOutgoingFrame(
				OutBuffer + FrameLen,	&tmp,
				CipherTmpLen,			&CipherTmp[0],
				END_OF_ARGS);
			FrameLen += tmp;
			
			// Append Variable IE
			memcpy(pAd->PortCfg.ReqVarIEs + VarIesOffset, CipherTmp, CipherTmpLen);
			VarIesOffset += CipherTmpLen;
			
			// Set Variable IEs Length
			pAd->PortCfg.ReqVarIELen = VarIesOffset;
			pAd->PortCfg.AssocInfo.RequestIELength = VarIesOffset;
			// OffsetResponseIEs follow ReqVarIE
			pAd->PortCfg.AssocInfo.OffsetResponseIEs = sizeof(NDIS_802_11_ASSOCIATION_INFORMATION) + pAd->PortCfg.ReqVarIELen;			
		}
		// For WPA2 / WPA2-PSK
		else if ((pAd->PortCfg.AuthMode == Ndis802_11AuthModeWPA2) || (pAd->PortCfg.AuthMode == Ndis802_11AuthModeWPA2PSK))
		{
			// Copy WPA2 template to buffer
			CipherTmpLen = sizeof(CipherWpa2Template);
			memcpy(CipherTmp, CipherWpa2Template, CipherTmpLen);
			// Modify Group cipher
			CipherTmp[7] = ((pAd->PortCfg.GroupCipher == Ndis802_11Encryption2Enabled) ? 0x2 : 0x4);			
			// Modify Pairwise cipher
			CipherTmp[13] = ((pAd->PortCfg.PairCipher == Ndis802_11Encryption2Enabled) ? 0x2 : 0x4);
			// Modify AKM
			CipherTmp[19] = ((pAd->PortCfg.AuthMode == Ndis802_11AuthModeWPA2) ? 0x1 : 0x2);
			// Check for WPA PMK cache list
			if (pAd->PortCfg.AuthMode == Ndis802_11AuthModeWPA2)
			{
				// Search chched PMKID, append it if existed
				for (idx = 0; idx < PMKID_NO; idx++)
				{
					if (memcmp(&ApAddr, &pAd->PortCfg.SavedPMK[idx], 6) == 0)
					{
						FoundPMK = TRUE;
						break;
					}
						
				}
				if (FoundPMK)
				{
					// Update length within RSN IE
					CipherTmp[1] += 18;
					// Set PMK number
					*(PUSHORT) &CipherTmp[CipherTmpLen] = 1;
					memcpy(&CipherTmp[CipherTmpLen + 2], &pAd->PortCfg.SavedPMK[idx].PMKID, 16);
					CipherTmpLen += 18;
				}
			}
			
			// Make outgoing frame
			MakeOutgoingFrame(
				OutBuffer + FrameLen,	&tmp,
				CipherTmpLen,			&CipherTmp[0],
				END_OF_ARGS);
			FrameLen += tmp;

			// Append Variable IE
			memcpy(pAd->PortCfg.ReqVarIEs + VarIesOffset, CipherTmp, CipherTmpLen);
			VarIesOffset += CipherTmpLen;
			
			// Set Variable IEs Length
			pAd->PortCfg.ReqVarIELen = VarIesOffset;
			pAd->PortCfg.AssocInfo.RequestIELength = VarIesOffset;
			// OffsetResponseIEs follow ReqVarIE
			pAd->PortCfg.AssocInfo.OffsetResponseIEs = sizeof(NDIS_802_11_ASSOCIATION_INFORMATION) + pAd->PortCfg.ReqVarIELen;			
		}
		else
		{
			// Do nothing
			;
		}

        MiniportMMRequest(pAd, OutBuffer, FrameLen);
            
        RTMPSetTimer(pAd, &pAd->Mlme.AssocAux.AssocTimer, Timeout); /* in mSec */
        pAd->Mlme.AssocMachine.CurrState = ASSOC_WAIT_RSP;
    } 
    else 
    {
        DBGPRINT(RT_DEBUG_TRACE,"ASSOC - MlmeAssocReqAction() sanity check failed. BUG!!!!!! \n");
        pAd->Mlme.AssocMachine.CurrState = ASSOC_IDLE;
        MlmeCntlConfirm(pAd, MT2_ASSOC_CONF, MLME_INVALID_FORMAT);
    }
}

/*
    ==========================================================================
    Description:
        mlme reassoc req handling procedure
    Parameters:
        Elem - 
    Pre:
        -# SSID  (Adapter->PortCfg.ssid[])
        -# BSSID (AP address, Adapter->PortCfg.bssid)
        -# Supported rates (Adapter->PortCfg.supported_rates[])
        -# Supported rates length (Adapter->PortCfg.supported_rates_len)
        -# Tx power (Adapter->PortCfg.tx_power)
        
	IRQL = DISPATCH_LEVEL

    ==========================================================================
 */
VOID MlmeReassocReqAction(
    IN PRT2570ADAPTER pAd, 
    IN MLME_QUEUE_ELEM *Elem) 
{
	MACADDR              ApAddr;
	MACHDR               ReassocHdr;
	UCHAR                SsidIe = IE_SSID, RateIe = IE_SUPP_RATES, ExtRateIe = IE_EXT_SUPP_RATES;
	USHORT               CapabilityInfo, ListenIntv;
	ULONG                Timeout;
	ULONG                FrameLen = 0;
	NDIS_STATUS          NStatus;
	ULONG			 	 tmp;
	UCHAR               *OutBuffer = NULL;

	// Block all authentication request durning WPA block period
	if (pAd->PortCfg.bBlockAssoc == TRUE)
	{
        DBGPRINT(RT_DEBUG_TRACE, "ASSOC - Block ReAssoc request durning WPA block period!\n");
        pAd->Mlme.AssocMachine.CurrState = ASSOC_IDLE;
        MlmeCntlConfirm(pAd, MT2_ASSOC_CONF, MLME_STATE_MACHINE_REJECT);
	}	
    // the parameters are the same as the association
    else if(MlmeAssocReqSanity(pAd, Elem->Msg, Elem->MsgLen, &ApAddr, &CapabilityInfo, &Timeout, &ListenIntv)) 
    {
        RTMPCancelTimer(&pAd->Mlme.AssocAux.ReassocTimer);

        NStatus = MlmeAllocateMemory(pAd, (PVOID)&OutBuffer);  //Get an unused nonpaged memory
        if(NStatus != NDIS_STATUS_SUCCESS) 
        {
            DBGPRINT(RT_DEBUG_TRACE,"ASSOC - MlmeReassocReqAction() allocate memory failed \n");
            pAd->Mlme.AssocMachine.CurrState = ASSOC_IDLE;
            MlmeCntlConfirm(pAd, MT2_REASSOC_CONF, MLME_FAIL_NO_RESOURCE);
            return;
        }

		// Mask out unnecessary capability information
		CapabilityInfo &= SUPPORTED_CAPABILITY_INFO;  // pAd->PortCfg.SupportedCapabilityInfo;
        pAd->Mlme.AssocAux.CapabilityInfo = CapabilityInfo;
        COPY_MAC_ADDR(&pAd->Mlme.AssocAux.Addr, &ApAddr);
        pAd->Mlme.AssocAux.ListenIntv = ListenIntv;

        // make frame, use bssid as the AP address??
        DBGPRINT(RT_DEBUG_TRACE, "ASSOC - Send RE-ASSOC request...\n");
        MgtMacHeaderInit(pAd, &ReassocHdr, SUBTYPE_REASSOC_REQ, 0, &ApAddr, &ApAddr);
        MakeOutgoingFrame(OutBuffer,            &FrameLen,
                          sizeof(MACHDR),       &ReassocHdr,
                          2,                    &CapabilityInfo,
                          2,                    &ListenIntv,
                          MAC_ADDR_LEN,         &ApAddr,
                          1,                    &SsidIe,
                          1,                    &pAd->PortCfg.SsidLen, 
                          pAd->PortCfg.SsidLen, pAd->PortCfg.Ssid, 
                          1,                    &RateIe,
						  1,						&pAd->PortCfg.SupRateLen,
						  pAd->PortCfg.SupRateLen,  pAd->PortCfg.SupRate,
                          END_OF_ARGS);
		if (pAd->PortCfg.ExtRateLen != 0)
		{
			MakeOutgoingFrame(OutBuffer + FrameLen, &tmp,
						1,							&ExtRateIe,
						1,							&pAd->PortCfg.ExtRateLen,
						pAd->PortCfg.ExtRateLen,	pAd->PortCfg.ExtRate,							
						END_OF_ARGS);
			FrameLen += tmp;
		}
        MiniportMMRequest(pAd, OutBuffer, FrameLen);
            
        RTMPSetTimer(pAd, &pAd->Mlme.AssocAux.ReassocTimer, Timeout); /* in mSec */
        pAd->Mlme.AssocMachine.CurrState = REASSOC_WAIT_RSP;
    } 
    else 
    {
        DBGPRINT(RT_DEBUG_TRACE,"ASSOC - MlmeReassocReqAction() sanity check failed. BUG!!!! \n");
        pAd->Mlme.AssocMachine.CurrState = ASSOC_IDLE;
        MlmeCntlConfirm(pAd, MT2_REASSOC_CONF, MLME_INVALID_FORMAT);
    }
}

/*
    ==========================================================================
    Description:
        Upper layer issues disassoc request
    Parameters:
        Elem -
        
	IRQL = PASSIVE_LEVEL

    ==========================================================================
 */
VOID MlmeDisassocReqAction(
    IN PRT2570ADAPTER pAd, 
    IN MLME_QUEUE_ELEM *Elem) 
{
    MLME_DISASSOC_REQ_STRUCT *DisassocReq;
    MACHDR                DisassocHdr;
    CHAR                 *OutBuffer = NULL;
    ULONG                 FrameLen = 0;
    NDIS_STATUS           NStatus;
    ULONG                       Timeout = 0;

    // skip sanity check
    DisassocReq = (MLME_DISASSOC_REQ_STRUCT *)(Elem->Msg);

    NStatus = MlmeAllocateMemory(pAd, (PVOID)&OutBuffer);  //Get an unused nonpaged memory
    if (NStatus != NDIS_STATUS_SUCCESS) 
    {
        DBGPRINT(RT_DEBUG_TRACE, "ASSOC - MlmeDisassocReqAction() allocate memory failed\n");
        pAd->Mlme.AssocMachine.CurrState = ASSOC_IDLE;
        MlmeCntlConfirm(pAd, MT2_DISASSOC_CONF, MLME_FAIL_NO_RESOURCE);
        return;
    }
    
    RTMPCancelTimer(&pAd->Mlme.AssocAux.DisassocTimer);
    
    DBGPRINT(RT_DEBUG_TRACE, "ASSOC - Send DISASSOC request\n");
    MgtMacHeaderInit(pAd, &DisassocHdr, SUBTYPE_DISASSOC, 0, &pAd->PortCfg.Bssid, &pAd->PortCfg.Bssid);
    MakeOutgoingFrame(OutBuffer,            &FrameLen, 
                      sizeof(MACHDR),       &DisassocHdr, 
                      2,                    &DisassocReq->Reason, 
                      END_OF_ARGS);
    MiniportMMRequest(pAd, OutBuffer, FrameLen);
    memset(&(pAd->PortCfg.Bssid), 0, MAC_ADDR_LEN);
    
    pAd->PortCfg.DisassocReason = REASON_DISASSOC_STA_LEAVING;
    COPY_MAC_ADDR(&pAd->PortCfg.DisassocSta, &DisassocReq->Addr);

    RTMPSetTimer(pAd, &pAd->Mlme.AssocAux.DisassocTimer, Timeout); /* in mSec */
    pAd->Mlme.AssocMachine.CurrState = DISASSOC_WAIT_RSP;
}

/*
    ==========================================================================
    Description:
        peer sends assoc rsp back
    Parameters:
        Elme - MLME message containing the received frame
        
	IRQL = DISPATCH_LEVEL

    ==========================================================================
 */
VOID PeerAssocRspAction(
    IN PRT2570ADAPTER pAd, 
    IN MLME_QUEUE_ELEM *Elem) 
{
    USHORT        CapabilityInfo, Status, Aid;
    UCHAR         Rates[MAX_LEN_OF_SUPPORTED_RATES], RatesLen;
    MACADDR       Addr2;
    BOOLEAN       ExtendedRateIeExist;

    if (PeerAssocRspSanity(pAd, Elem->Msg, Elem->MsgLen, &Addr2, &CapabilityInfo, &Status, &Aid, Rates, &RatesLen, &ExtendedRateIeExist)) 
    {
        // The frame is for me ?
        if(MAC_ADDR_EQUAL(&Addr2, &pAd->Mlme.AssocAux.Addr)) 
        {
            DBGPRINT(RT_DEBUG_INFO, "ASSOC - receive ASSOC_RSP to me (status=%d)\n", Status);
            RTMPCancelTimer(&pAd->Mlme.AssocAux.AssocTimer);
            if(Status == MLME_SUCCESS) 
            {
                // go to procedure listed on page 376
				// Mask out unnecessary capability information
				CapabilityInfo &= SUPPORTED_CAPABILITY_INFO;  // pAd->PortCfg.SupportedCapabilityInfo;
                AssocPostProc(pAd, &Addr2, CapabilityInfo, Aid, Rates, RatesLen, ExtendedRateIeExist);
            } 
            pAd->Mlme.AssocMachine.CurrState = ASSOC_IDLE;
            MlmeCntlConfirm(pAd, MT2_ASSOC_CONF, Status);
        } 
    }
    else
    {
        DBGPRINT(RT_DEBUG_TRACE, "ASSOC - PeerAssocRspAction() sanity check fail\n");
    }
}

/*
    ==========================================================================
    Description:
        peer sends reassoc rsp
    Parametrs:
        Elem - MLME message cntaining the received frame
        
	IRQL = DISPATCH_LEVEL

    ==========================================================================
 */
VOID PeerReassocRspAction(
    IN PRT2570ADAPTER pAd, 
    IN MLME_QUEUE_ELEM *Elem) 
{
    USHORT      CapabilityInfo;
    USHORT      Status;
    USHORT      Aid;
    UCHAR       Rates[MAX_LEN_OF_SUPPORTED_RATES];
    UCHAR       RatesLen;
    MACADDR     Addr2;
    BOOLEAN     ExtendedRateIeExist;

    if(PeerAssocRspSanity(pAd, Elem->Msg, Elem->MsgLen, &Addr2, &CapabilityInfo, &Status, &Aid, Rates, &RatesLen, &ExtendedRateIeExist)) 
    {
        if(MAC_ADDR_EQUAL(&Addr2, &pAd->Mlme.AssocAux.Addr)) // The frame is for me ?
        {
            DBGPRINT(RT_DEBUG_TRACE, "ASSOC - receive REASSOC_RSP to me (status=%d)\n", Status);
            RTMPCancelTimer(&pAd->Mlme.AssocAux.ReassocTimer);
            
            if(Status == MLME_SUCCESS) 
            {
				// Mask out unnecessary capability information
				CapabilityInfo &= SUPPORTED_CAPABILITY_INFO;  // pAd->PortCfg.SupportedCapabilityInfo;
                // go to procedure listed on page 376
                AssocPostProc(pAd, &Addr2, CapabilityInfo, Aid, Rates, RatesLen, ExtendedRateIeExist);
            } 

            pAd->Mlme.AssocMachine.CurrState = ASSOC_IDLE;
            MlmeCntlConfirm(pAd, MT2_REASSOC_CONF, Status);
        } 
    }
    else
    {
        DBGPRINT(RT_DEBUG_TRACE, "ASSOC - PeerReassocRspAction() sanity check fail\n");
    }

}

/*
    ==========================================================================
    Description:
        procedures on IEEE 802.11/1999 p.376 
    Parametrs:
        
	IRQL = DISPATCH_LEVEL

    ==========================================================================
 */
VOID AssocPostProc(
    IN PRT2570ADAPTER pAd, 
    IN PMACADDR Addr2, 
    IN USHORT CapabilityInfo, 
    IN USHORT Aid, 
    IN UCHAR Rates[], 
    IN UCHAR RatesLen,
    IN BOOLEAN ExtendedRateIeExist) 
{
	ULONG Idx;
    UCHAR RateIe = IE_SUPP_RATES;
	UCHAR VarIesOffset;

    // 2003/12/11 -  skip the following because experiment show that we can not 
    // trust the "privacy" bit in AssocRsp. We can only trust "Privacy" bit specified in
    // BEACON and ProbeRsp.
    // pAd->PortCfg.PrivacyInvoked = CAP_IS_PRIVACY_ON(CapabilityInfo);
    
    pAd->PortCfg.Aid = Aid;
    memcpy(pAd->PortCfg.SupportedRates, Rates, RatesLen);
    pAd->PortCfg.SupportedRatesLen = RatesLen;
    COPY_MAC_ADDR(&pAd->PortCfg.Bssid, Addr2);
    AsicSetBssid(pAd, &pAd->PortCfg.Bssid);

    // set listen interval
    pAd->PortCfg.DefaultListenCount = pAd->Mlme.AssocAux.ListenIntv;
//  pAd->PortCfg.CurrListenCount = pAd->Mlme.AssocAux.ListenIntv;

	// Set New WPA information
	Idx = BssTableSearch(&pAd->PortCfg.BssTab, Addr2);
	if (Idx == BSS_NOT_FOUND) 
	{
		DBGPRINT(RT_DEBUG_ERROR, "ASSOC - Can't find BSS after receiving Assoc response\n");
	}
	else
	{
		// Mod by James to fix OID_802_11_ASSOCIATION_INFORMATION
		pAd->PortCfg.AssocInfo.Length = sizeof(NDIS_802_11_ASSOCIATION_INFORMATION); //+ sizeof(NDIS_802_11_FIXED_IEs); 	// Filled in assoc request
		pAd->PortCfg.AssocInfo.AvailableResponseFixedIEs =
			NDIS_802_11_AI_RESFI_CAPABILITIES | NDIS_802_11_AI_RESFI_STATUSCODE | NDIS_802_11_AI_RESFI_ASSOCIATIONID;
		pAd->PortCfg.AssocInfo.ResponseFixedIEs.Capabilities  = CapabilityInfo;
		pAd->PortCfg.AssocInfo.ResponseFixedIEs.StatusCode    = MLME_SUCCESS;		// Should be success, add failed later
		pAd->PortCfg.AssocInfo.ResponseFixedIEs.AssociationId = Aid;

		// Copy BSS VarIEs to PortCfg associnfo structure.
		// First add Supported rates
		VarIesOffset = 0;
		memcpy(pAd->PortCfg.ResVarIEs + VarIesOffset, &RateIe, 1);
		VarIesOffset += 1;
		memcpy(pAd->PortCfg.ResVarIEs + VarIesOffset, &RatesLen, 1);
		VarIesOffset += 1;
		memcpy(pAd->PortCfg.ResVarIEs + VarIesOffset, Rates, RatesLen);
		VarIesOffset += RatesLen;

		// Second add RSN
		memcpy(pAd->PortCfg.ResVarIEs + VarIesOffset, pAd->PortCfg.BssTab.BssEntry[Idx].VarIEs, pAd->PortCfg.BssTab.BssEntry[Idx].VarIELen);
		VarIesOffset += pAd->PortCfg.BssTab.BssEntry[Idx].VarIELen;
		
		// Set Variable IEs Length
		pAd->PortCfg.ResVarIELen = VarIesOffset;
		pAd->PortCfg.AssocInfo.ResponseIELength = VarIesOffset;
	}
}

/*
    ==========================================================================
    Description:
        left part of IEEE 802.11/1999 p.374 
    Parameters:
        Elem - MLME message containing the received frame
        
	IRQL = DISPATCH_LEVEL

    ==========================================================================
 */
VOID PeerDisassocAction(
    IN PRT2570ADAPTER pAd, 
    IN MLME_QUEUE_ELEM *Elem) 
{
    MACADDR       Addr2;
    USHORT        Reason;

    if(PeerDisassocSanity(pAd, Elem->Msg, Elem->MsgLen, &Addr2, &Reason)) 
    {
        if (INFRA_ON(pAd) && MAC_ADDR_EQUAL(&pAd->PortCfg.Bssid, &Addr2)) 
        {
			DBGPRINT_RAW(RT_DEBUG_TRACE, "LinkDown(PeerDisassocAction(Reason = %d))\n", Reason);//steven:for debug
            LinkDown(pAd);
            pAd->Mlme.AssocMachine.CurrState = ASSOC_IDLE;

            // 2002/11/21 -
            // patch RT2430/RT2420 hangup issue. We suspect this AP DIS-ASSOCIATE frame
            // is caused by PHY hangup, so we reset PHY, then auto recover the connection.
            // if this attempt fails, then remains in LinkDown and leaves the problem
            // to MlmePeriodicExec()
            // NICPatchRT2430Bug(pAd);
            pAd->RalinkCounters.BeenDisassociatedCount ++;
			// Remove auto recover effort when disassociate by AP, re-enable for patch 2430 only
            DBGPRINT(RT_DEBUG_TRACE, "ASSOC - Disassociated by AP, Auto Recovery attempt #%d\n", pAd->RalinkCounters.BeenDisassociatedCount);
            MlmeAutoReconnectLastSSID(pAd);
        }
    }
    else
    {
        DBGPRINT(RT_DEBUG_TRACE, "ASSOC - PeerDisassocAction() sanity check fail\n");
    }
}

/*
    ==========================================================================
    Description:
        what the state machine will do after assoc timeout
    Parameters:
        Elme -
        
	IRQL = DISPATCH_LEVEL

    ==========================================================================
 */
VOID AssocTimeoutAction(
    IN PRT2570ADAPTER pAd, 
    IN MLME_QUEUE_ELEM *Elem) 
{
    DBGPRINT(RT_DEBUG_TRACE, "ASSOC - AssocTimeoutAction\n");
    pAd->Mlme.AssocMachine.CurrState = ASSOC_IDLE;
    MlmeCntlConfirm(pAd, MT2_ASSOC_CONF, MLME_REJ_TIMEOUT);
}

/*
    ==========================================================================
    Description:
        what the state machine will do after reassoc timeout
        
	IRQL = DISPATCH_LEVEL

    ==========================================================================
 */
VOID ReassocTimeoutAction(
    IN PRT2570ADAPTER pAd, 
    IN MLME_QUEUE_ELEM *Elem) 
{
    DBGPRINT(RT_DEBUG_TRACE, "ASSOC - ReassocTimeoutAction\n");
    pAd->Mlme.AssocMachine.CurrState = ASSOC_IDLE;
    MlmeCntlConfirm(pAd, MT2_REASSOC_CONF, MLME_REJ_TIMEOUT);
}

/*
    ==========================================================================
    Description:
        what the state machine will do after disassoc timeout
        
	IRQL = DISPATCH_LEVEL

    ==========================================================================
 */
VOID DisassocTimeoutAction(
    IN PRT2570ADAPTER pAd, 
    IN MLME_QUEUE_ELEM *Elem) 
{
    DBGPRINT(RT_DEBUG_TRACE, "ASSOC - DisassocTimeoutAction\n");
    pAd->Mlme.AssocMachine.CurrState = ASSOC_IDLE;
    MlmeCntlConfirm(pAd, MT2_DISASSOC_CONF, MLME_SUCCESS);
}

VOID InvalidStateWhenAssoc(
    IN PRT2570ADAPTER pAd, 
    IN MLME_QUEUE_ELEM *Elem) 
{
    DBGPRINT(RT_DEBUG_TRACE, "ASSOC - InvalidStateWhenAssoc(state=%d), reset ASSOC state machine\n", 
        pAd->Mlme.AssocMachine.CurrState);
    pAd->Mlme.AssocMachine.CurrState = ASSOC_IDLE;
    MlmeCntlConfirm(pAd, MT2_ASSOC_CONF, MLME_STATE_MACHINE_REJECT);
}

VOID InvalidStateWhenReassoc(
    IN PRT2570ADAPTER pAd, 
    IN MLME_QUEUE_ELEM *Elem) 
{
    DBGPRINT(RT_DEBUG_TRACE, "ASSOC - InvalidStateWhenReassoc(state=%d), reset ASSOC state machine\n", 
        pAd->Mlme.AssocMachine.CurrState);
    pAd->Mlme.AssocMachine.CurrState = ASSOC_IDLE;
    MlmeCntlConfirm(pAd, MT2_REASSOC_CONF, MLME_STATE_MACHINE_REJECT);
}

VOID InvalidStateWhenDisassociate(
    IN PRT2570ADAPTER pAd, 
    IN MLME_QUEUE_ELEM *Elem) 
{
    DBGPRINT(RT_DEBUG_TRACE, "ASSOC - InvalidStateWhenDisassoc(state=%d), reset ASSOC state machine\n", 
        pAd->Mlme.AssocMachine.CurrState);
    pAd->Mlme.AssocMachine.CurrState = ASSOC_IDLE;
    MlmeCntlConfirm(pAd, MT2_DISASSOC_CONF, MLME_STATE_MACHINE_REJECT);
}

/*
    ==========================================================================
    Description:
        right part of IEEE 802.11/1999 page 374
    Note: 
        This event should never cause ASSOC state machine perform state
        transition, and has no relationship with CNTL machine. So we separate
        this routine as a service outside of ASSOC state transition table.
        
	IRQL = DISPATCH_LEVEL

    ==========================================================================
 */
VOID Cls3errAction(
    IN PRT2570ADAPTER pAd, 
    IN PMACADDR      pAddr) 
{
    MACHDR                DisassocHdr;
    CHAR                 *OutBuffer = NULL;
    ULONG                 FrameLen = 0;
    NDIS_STATUS           NStatus;
    USHORT                Reason = REASON_CLS3ERR;

    NStatus = MlmeAllocateMemory(pAd, (PVOID)&OutBuffer);  //Get an unused nonpaged memory
    if (NStatus != NDIS_STATUS_SUCCESS) 
        return;
    
    DBGPRINT(RT_DEBUG_TRACE, "ASSOC - Class 3 Error, Send DISASSOC frame\n");
    MgtMacHeaderInit(pAd, &DisassocHdr, SUBTYPE_DISASSOC, 0, pAddr, &pAd->PortCfg.Bssid);
    MakeOutgoingFrame(OutBuffer,            &FrameLen, 
                      sizeof(MACHDR),       &DisassocHdr, 
                      2,                    &Reason, 
                      END_OF_ARGS);
    MiniportMMRequest(pAd, OutBuffer, FrameLen);

    pAd->PortCfg.DisassocReason = REASON_CLS3ERR;
    COPY_MAC_ADDR(&pAd->PortCfg.DisassocSta, pAddr);
}
