/*
  * Copyright (c) 2016 MediaTek Inc.  All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
  * General Public License (GPL) Version 2, available from the file
  * COPYING in the main directory of this source tree, or the
  * BSD license below:
  *
  *     Redistribution and use in source and binary forms, with or
  *     without modification, are permitted provided that the following
  *     conditions are met:
  *
  *      - Redistributions of source code must retain the above
  *        copyright notice, this list of conditions and the following
  *        disclaimer.
  *
  *      - Redistributions in binary form must reproduce the above
  *        copyright notice, this list of conditions and the following
  *        disclaimer in the documentation and/or other materials
  *        provided with the distribution.
  *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */


#ifdef APCLI_SUPPORT


#include "rt_config.h"

static VOID ApCliAssocTimeout(IN PVOID SystemSpecific1,
			      IN PVOID FunctionContext,
			      IN PVOID SystemSpecific2, IN PVOID SystemSpecific3);

#ifdef MAC_REPEATER_SUPPORT
static VOID ApCliAssocTimeoutExt(IN PVOID SystemSpecific1,
				 IN PVOID FunctionContext,
				 IN PVOID SystemSpecific2, IN PVOID SystemSpecific3);
#endif /* MAC_REPEATER_SUPPORT */

static VOID ApCliMlmeAssocReqAction(IN PRTMP_ADAPTER pAd, IN MLME_QUEUE_ELEM * Elem);

static VOID ApCliMlmeDisassocReqAction(IN PRTMP_ADAPTER pAd, IN MLME_QUEUE_ELEM * Elem);

static VOID ApCliPeerAssocRspAction(IN PRTMP_ADAPTER pAd, IN MLME_QUEUE_ELEM * Elem);

static VOID ApCliPeerDisassocAction(IN PRTMP_ADAPTER pAd, IN MLME_QUEUE_ELEM * Elem);

static VOID ApCliAssocTimeoutAction(IN PRTMP_ADAPTER pAd, IN MLME_QUEUE_ELEM * Elem);

static VOID ApCliInvalidStateWhenAssoc(IN PRTMP_ADAPTER pAd, IN MLME_QUEUE_ELEM * Elem);

static VOID ApCliInvalidStateWhenDisassociate(IN PRTMP_ADAPTER pAd, IN MLME_QUEUE_ELEM * Elem);

static VOID ApCliAssocPostProc(IN PRTMP_ADAPTER pAd,
			       IN PUCHAR pAddr2,
			       IN USHORT CapabilityInfo,
			       IN USHORT IfIndex,
			       IN UCHAR SupRate[],
			       IN UCHAR SupRateLen,
			       IN UCHAR ExtRate[],
			       IN UCHAR ExtRateLen,
			       IN PEDCA_PARM pEdcaParm,
			       IN HT_CAPABILITY_IE * pHtCapability,
			       IN UCHAR HtCapabilityLen, IN ADD_HT_INFO_IE * pAddHtInfo);

DECLARE_TIMER_FUNCTION(ApCliAssocTimeout);
BUILD_TIMER_FUNCTION(ApCliAssocTimeout);
#ifdef MAC_REPEATER_SUPPORT
DECLARE_TIMER_FUNCTION(ApCliAssocTimeoutExt);
BUILD_TIMER_FUNCTION(ApCliAssocTimeoutExt);
#endif /* MAC_REPEATER_SUPPORT */

/*
    ==========================================================================
    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
    ==========================================================================
 */
VOID ApCliAssocStateMachineInit(IN PRTMP_ADAPTER pAd,
				IN STATE_MACHINE * S, OUT STATE_MACHINE_FUNC Trans[])
{
	UCHAR i;
#ifdef MAC_REPEATER_SUPPORT
	UCHAR j;
#endif /* MAC_REPEATER_SUPPORT */

	StateMachineInit(S, (STATE_MACHINE_FUNC *) Trans,
			 APCLI_MAX_ASSOC_STATE, APCLI_MAX_ASSOC_MSG,
			 (STATE_MACHINE_FUNC) Drop, APCLI_ASSOC_IDLE, APCLI_ASSOC_MACHINE_BASE);

	/* first column */
	StateMachineSetAction(S, APCLI_ASSOC_IDLE, APCLI_MT2_MLME_ASSOC_REQ,
			      (STATE_MACHINE_FUNC) ApCliMlmeAssocReqAction);
	StateMachineSetAction(S, APCLI_ASSOC_IDLE, APCLI_MT2_MLME_DISASSOC_REQ,
			      (STATE_MACHINE_FUNC) ApCliMlmeDisassocReqAction);
	StateMachineSetAction(S, APCLI_ASSOC_IDLE, APCLI_MT2_PEER_DISASSOC_REQ,
			      (STATE_MACHINE_FUNC) ApCliPeerDisassocAction);

	/* second column */
	StateMachineSetAction(S, APCLI_ASSOC_WAIT_RSP, APCLI_MT2_MLME_ASSOC_REQ,
			      (STATE_MACHINE_FUNC) ApCliInvalidStateWhenAssoc);
	StateMachineSetAction(S, APCLI_ASSOC_WAIT_RSP, APCLI_MT2_MLME_DISASSOC_REQ,
			      (STATE_MACHINE_FUNC) ApCliInvalidStateWhenDisassociate);
	StateMachineSetAction(S, APCLI_ASSOC_WAIT_RSP, APCLI_MT2_PEER_DISASSOC_REQ,
			      (STATE_MACHINE_FUNC) ApCliPeerDisassocAction);
	StateMachineSetAction(S, APCLI_ASSOC_WAIT_RSP, APCLI_MT2_PEER_ASSOC_RSP,
			      (STATE_MACHINE_FUNC) ApCliPeerAssocRspAction);
	StateMachineSetAction(S, APCLI_ASSOC_WAIT_RSP, APCLI_MT2_ASSOC_TIMEOUT,
			      (STATE_MACHINE_FUNC) ApCliAssocTimeoutAction);

	for (i = 0; i < MAX_APCLI_NUM; i++) {
		pAd->ApCfg.ApCliTab[i].AssocCurrState = APCLI_ASSOC_IDLE;

		/* timer init */
		RTMPInitTimer(pAd, &pAd->ApCfg.ApCliTab[i].MlmeAux.ApCliAssocTimer,
			      GET_TIMER_FUNCTION(ApCliAssocTimeout), pAd, FALSE);

		RTMPInitTimer(pAd, &pAd->ApCfg.ApCliTab[i].MlmeAux.WpaDisassocAndBlockAssocTimer,
			      GET_TIMER_FUNCTION(ApCliWpaDisassocApAndBlockAssoc), pAd, FALSE);

#ifdef MAC_REPEATER_SUPPORT
		for (j = 0; j < MAX_EXT_MAC_ADDR_SIZE; j++) {
			pAd->ApCfg.ApCliTab[i].RepeaterCli[j].pAd = pAd;
			pAd->ApCfg.ApCliTab[i].RepeaterCli[j].MatchApCliIdx = i;
			pAd->ApCfg.ApCliTab[i].RepeaterCli[j].MatchLinkIdx = j;
			pAd->ApCfg.ApCliTab[i].RepeaterCli[j].AssocCurrState = APCLI_ASSOC_IDLE;

			/* timer init */
			RTMPInitTimer(pAd, &pAd->ApCfg.ApCliTab[i].RepeaterCli[j].ApCliAssocTimer,
				      GET_TIMER_FUNCTION(ApCliAssocTimeoutExt),
				      &pAd->ApCfg.ApCliTab[i].RepeaterCli[j], FALSE);
		}
#endif /* MAC_REPEATER_SUPPORT */
	}
}

/*
    ==========================================================================
    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
    ==========================================================================
 */
static VOID ApCliAssocTimeout(IN PVOID SystemSpecific1,
			      IN PVOID FunctionContext,
			      IN PVOID SystemSpecific2, IN PVOID SystemSpecific3)
{
	RTMP_ADAPTER *pAd = (RTMP_ADAPTER *) FunctionContext;

	DBGPRINT(RT_DEBUG_TRACE, ("APCLI_ASSOC - enqueue APCLI_MT2_ASSOC_TIMEOUT\n"));

	MlmeEnqueue(pAd, APCLI_ASSOC_STATE_MACHINE, APCLI_MT2_ASSOC_TIMEOUT, 0, NULL, 0);
	RTMP_MLME_HANDLER(pAd);
}

#ifdef MAC_REPEATER_SUPPORT
/*
    ==========================================================================
    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
    ==========================================================================
 */
static VOID ApCliAssocTimeoutExt(IN PVOID SystemSpecific1,
				 IN PVOID FunctionContext,
				 IN PVOID SystemSpecific2, IN PVOID SystemSpecific3)
{
	PREPEATER_CLIENT_ENTRY pRepeaterCliEntry = (PREPEATER_CLIENT_ENTRY) FunctionContext;
	PRTMP_ADAPTER pAd;
	USHORT ifIndex = 0;

	DBGPRINT(RT_DEBUG_TRACE, ("Repeater Cli ASSOC - enqueue APCLI_MT2_ASSOC_TIMEOUT\n"));

	pAd = pRepeaterCliEntry->pAd;
	ifIndex = (64 + (16 * pRepeaterCliEntry->MatchApCliIdx) + pRepeaterCliEntry->MatchLinkIdx);

	DBGPRINT(RT_DEBUG_ERROR, (" (%s) ifIndex = %d, CliIdx = %d !!!\n",
				  __func__, pRepeaterCliEntry->MatchApCliIdx,
				  pRepeaterCliEntry->MatchLinkIdx));

	MlmeEnqueue(pAd, APCLI_ASSOC_STATE_MACHINE, APCLI_MT2_ASSOC_TIMEOUT, 0, NULL, ifIndex);
	RTMP_MLME_HANDLER(pAd);
}
#endif /* MAC_REPEATER_SUPPORT */

/*
    ==========================================================================
    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
    Post  :
	-# An association request frame is generated and sent to the air
	-# Association timer starts
	-# Association state -> ASSOC_WAIT_RSP

    ==========================================================================
 */
static VOID ApCliMlmeAssocReqAction(IN PRTMP_ADAPTER pAd, IN MLME_QUEUE_ELEM * Elem)
{
	NDIS_STATUS NStatus;
	BOOLEAN Cancelled;
	UCHAR ApAddr[6];
	HEADER_802_11 AssocHdr;
	UCHAR WmeIe[9] = { IE_VENDOR_SPECIFIC, 0x07, 0x00, 0x50, 0xf2, 0x02, 0x00, 0x01, 0x00 };
	USHORT ListenIntv;
	ULONG Timeout;
	USHORT CapabilityInfo;
	PUCHAR pOutBuffer = NULL;
	ULONG FrameLen = 0;
	ULONG tmp = 0;
	UCHAR SsidIe = IE_SSID;
	UCHAR SupRateIe = IE_SUPP_RATES;
	UCHAR ExtRateIe = IE_EXT_SUPP_RATES;
	APCLI_CTRL_MSG_STRUCT ApCliCtrlMsg;
	USHORT ifIndex = (USHORT) (Elem->Priv);
	PULONG pCurrState = NULL;
	UCHAR RSNIe = IE_WPA;
	APCLI_STRUCT *apcli_entry;
	struct wifi_dev *wdev;
#ifdef MAC_REPEATER_SUPPORT
	UCHAR CliIdx = 0xFF;
#endif /* MAC_REPEATER_SUPPORT */

	if ((ifIndex >= MAX_APCLI_NUM)
#ifdef MAC_REPEATER_SUPPORT
	    && (ifIndex < 64)
#endif /* MAC_REPEATER_SUPPORT */
	    )
		return;

#ifdef MAC_REPEATER_SUPPORT
	if (ifIndex >= 64) {
		CliIdx = ((ifIndex - 64) % 16);
		ifIndex = ((ifIndex - 64) / 16);
		pCurrState = &pAd->ApCfg.ApCliTab[ifIndex].RepeaterCli[CliIdx].AssocCurrState;
	} else
#endif /* MAC_REPEATER_SUPPORT */
		pCurrState = &pAd->ApCfg.ApCliTab[ifIndex].AssocCurrState;

	apcli_entry = &pAd->ApCfg.ApCliTab[ifIndex];
	wdev = &apcli_entry->wdev;

	/* Block all authentication request durning WPA block period */
	if (apcli_entry->bBlockAssoc == TRUE) {
		DBGPRINT(RT_DEBUG_TRACE,
			 ("APCLI_ASSOC - Block Auth request durning WPA block period!\n"));
		*pCurrState = APCLI_ASSOC_IDLE;
		ApCliCtrlMsg.Status = MLME_STATE_MACHINE_REJECT;
		MlmeEnqueue(pAd, APCLI_CTRL_STATE_MACHINE, APCLI_CTRL_ASSOC_RSP,
			    sizeof(APCLI_CTRL_MSG_STRUCT), &ApCliCtrlMsg, ifIndex);
	} else
	    if (MlmeAssocReqSanity
		(pAd, Elem->Msg, Elem->MsgLen, ApAddr, &CapabilityInfo, &Timeout, &ListenIntv)) {
		/* RTMPCancelTimer(&apcli_entry->MlmeAux.ApCliAssocTimer, &Cancelled); */
#ifdef MAC_REPEATER_SUPPORT
		if (CliIdx != 0xFF)
			RTMPCancelTimer(&apcli_entry->RepeaterCli[CliIdx].ApCliAssocTimer,
					&Cancelled);
		else
#endif /* MAC_REPEATER_SUPPORT */
			RTMPCancelTimer(&apcli_entry->MlmeAux.ApCliAssocTimer, &Cancelled);

		/* allocate and send out AssocRsp frame */
		NStatus = MlmeAllocateMemory(pAd, &pOutBuffer);	/*Get an unused nonpaged memory */
		if (NStatus != NDIS_STATUS_SUCCESS) {
			DBGPRINT(RT_DEBUG_TRACE,
				 ("APCLI_ASSOC - ApCliMlmeAssocReqAction() allocate memory failed\n"));
			*pCurrState = APCLI_ASSOC_IDLE;

			ApCliCtrlMsg.Status = MLME_FAIL_NO_RESOURCE;
			MlmeEnqueue(pAd, APCLI_CTRL_STATE_MACHINE, APCLI_CTRL_ASSOC_RSP,
				    sizeof(APCLI_CTRL_MSG_STRUCT), &ApCliCtrlMsg, ifIndex);

			return;
		}

		DBGPRINT(RT_DEBUG_OFF, ("APCLI_ASSOC - Send ASSOC request to [%s with Len %d]...\n",
			apcli_entry->MlmeAux.Ssid, apcli_entry->MlmeAux.SsidLen));
		ApCliMgtMacHeaderInit(pAd, &AssocHdr, SUBTYPE_ASSOC_REQ, 0, ApAddr, ApAddr,
				      ifIndex);

#ifdef MAC_REPEATER_SUPPORT
		if (CliIdx != 0xFF)
			COPY_MAC_ADDR(AssocHdr.Addr2,
				      apcli_entry->RepeaterCli[CliIdx].CurrentAddress);
#endif /* MAC_REPEATER_SUPPORT */

		/* Build basic frame first */
		MakeOutgoingFrame(pOutBuffer, &FrameLen,
				  sizeof(HEADER_802_11), &AssocHdr,
				  2, &CapabilityInfo,
				  2, &ListenIntv,
				  1, &SsidIe,
				  1, &apcli_entry->MlmeAux.SsidLen,
				  apcli_entry->MlmeAux.SsidLen, apcli_entry->MlmeAux.Ssid,
				  1, &SupRateIe,
				  1, &apcli_entry->MlmeAux.SupRateLen,
				  apcli_entry->MlmeAux.SupRateLen, apcli_entry->MlmeAux.SupRate,
				  END_OF_ARGS);

		if (apcli_entry->MlmeAux.ExtRateLen != 0) {
			MakeOutgoingFrame(pOutBuffer + FrameLen, &tmp,
					  1, &ExtRateIe,
					  1, &apcli_entry->MlmeAux.ExtRateLen,
					  apcli_entry->MlmeAux.ExtRateLen,
					  apcli_entry->MlmeAux.ExtRate, END_OF_ARGS);
			FrameLen += tmp;
		}

		/* HT */
		if ((apcli_entry->MlmeAux.HtCapabilityLen > 0) &&
		    WMODE_CAP_N(pAd->CommonCfg.PhyMode)) {
			ULONG TmpLen;
			HT_CAPABILITY_IE HtCapabilityTmp;

			NdisZeroMemory(&HtCapabilityTmp, sizeof(HT_CAPABILITY_IE));
			NdisMoveMemory(&HtCapabilityTmp, &apcli_entry->MlmeAux.HtCapability,
				       apcli_entry->MlmeAux.HtCapabilityLen);

#ifdef CUSTOMIZED_BW_SETTING
			if (RTMP_GetCustomizedChannelBw(pAd,
				apcli_entry->wdev.channel) == HT_BW_20) {
				HtCapabilityTmp.HtCapInfo.ChannelWidth = BW_20;
			}
#endif /* CUSTOMIZED_BW_SETTING */

#ifdef DOT11N_SS3_SUPPORT
			HtCapabilityTmp.MCSSet[2] =
			    (apcli_entry->MlmeAux.HtCapability.MCSSet[2] & apcli_entry->
			     RxMcsSet[2]);
#endif /* DOT11N_SS3_SUPPORT */

#ifdef RT_BIG_ENDIAN
			*(USHORT *) (&HtCapabilityTmp.HtCapInfo) =
			    SWAP16(*(USHORT *) (&HtCapabilityTmp.HtCapInfo));
			*(USHORT *) (&HtCapabilityTmp.ExtHtCapInfo) =
			    SWAP16(*(USHORT *) (&HtCapabilityTmp.ExtHtCapInfo));
#endif /* RT_BIG_ENDINA */
			MakeOutgoingFrame(pOutBuffer + FrameLen, &TmpLen,
					  1, &HtCapIe,
					  1, &apcli_entry->MlmeAux.HtCapabilityLen,
					  apcli_entry->MlmeAux.HtCapabilityLen, &HtCapabilityTmp,
					  END_OF_ARGS);
			FrameLen += TmpLen;

#ifdef DOT11_VHT_AC
			if (WMODE_CAP_AC(pAd->CommonCfg.PhyMode) &&
			    (pAd->CommonCfg.Channel > 14) && (apcli_entry->MlmeAux.vht_cap_len)) {
				FrameLen +=
				    build_vht_ies(pAd, (UCHAR *) (pOutBuffer + FrameLen),
						  SUBTYPE_ASSOC_REQ);
			}
#endif /* DOT11_VHT_AC */
		}

		{
			ULONG TmpLen;
			EXT_CAP_INFO_ELEMENT extCapInfo;
			UCHAR extInfoLen;

			extInfoLen = sizeof(EXT_CAP_INFO_ELEMENT);
			NdisZeroMemory(&extCapInfo, extInfoLen);

#ifdef APCLI_CERT_SUPPORT
			if ((pAd->CommonCfg.bBssCoexEnable == TRUE) &&
			    (pAd->CommonCfg.PhyMode >= PHY_11ABGN_MIXED)
			    && (pAd->CommonCfg.Channel <= 14)
			    && (pAd->bApCliCertTest == TRUE)
			    ) {
				extCapInfo.BssCoexistMgmtSupport = 1;
				DBGPRINT(RT_DEBUG_TRACE,
					 ("%s: BssCoexistMgmtSupport = 1\n", __func__));
			}
#endif /* APCLI_CERT_SUPPORT */
#ifdef DOT11_VHT_AC
			if (WMODE_CAP_AC(pAd->CommonCfg.PhyMode) && (pAd->CommonCfg.Channel > 14))
				extCapInfo.operating_mode_notification = 1;
#endif /* DOT11_VHT_AC */

			MakeOutgoingFrame(pOutBuffer + FrameLen, &TmpLen,
					  1, &ExtCapIe,
					  1, &extInfoLen, extInfoLen, &extCapInfo, END_OF_ARGS);
			FrameLen += TmpLen;
		}

#ifdef AGGREGATION_SUPPORT
		/*
		   add Ralink proprietary IE to inform AP this STA is going to use AGGREGATION or PIGGY-BACK+AGGREGATION
		   Case I: (Aggregation + Piggy-Back)
		   1. user enable aggregation, AND
		   2. Mac support piggy-back
		   3. AP annouces it's PIGGY-BACK+AGGREGATION-capable in BEACON
		   Case II: (Aggregation)
		   1. user enable aggregation, AND
		   2. AP annouces it's AGGREGATION-capable in BEACON
		 */
		if (pAd->CommonCfg.bAggregationCapable) {
#ifdef PIGGYBACK_SUPPORT
			if ((pAd->CommonCfg.bPiggyBackCapable)
			    && ((apcli_entry->MlmeAux.APRalinkIe & 0x00000003) == 3)) {
				ULONG TmpLen;
				UCHAR RalinkIe[9] = {
				    IE_VENDOR_SPECIFIC, 7, 0x00, 0x0c, 0x43, 0x03, 0x00, 0x00,
					0x00
				};
				MakeOutgoingFrame(pOutBuffer + FrameLen, &TmpLen, 9, RalinkIe,
						  END_OF_ARGS);
				FrameLen += TmpLen;
			} else
#endif /* PIGGYBACK_SUPPORT */
			if (apcli_entry->MlmeAux.APRalinkIe & 0x00000001) {
				ULONG TmpLen;
				UCHAR RalinkIe[9] = {
				    IE_VENDOR_SPECIFIC, 7, 0x00, 0x0c, 0x43, 0x01, 0x00, 0x00,
					0x00
				};
				MakeOutgoingFrame(pOutBuffer + FrameLen, &TmpLen, 9, RalinkIe,
						  END_OF_ARGS);
				FrameLen += TmpLen;
			}
		} else {
			ULONG TmpLen;
			UCHAR RalinkIe[9] = {
			    IE_VENDOR_SPECIFIC, 7, 0x00, 0x0c, 0x43, 0x06, 0x00, 0x00, 0x00 };
			MakeOutgoingFrame(pOutBuffer + FrameLen, &TmpLen, 9, RalinkIe, END_OF_ARGS);
			FrameLen += TmpLen;
		}
#endif /* AGGREGATION_SUPPORT */

		if (apcli_entry->MlmeAux.APEdcaParm.bValid) {
			if (apcli_entry->UapsdInfo.bAPSDCapable &&
			    apcli_entry->MlmeAux.APEdcaParm.bAPSDCapable) {
				QBSS_STA_INFO_PARM QosInfo;

				NdisZeroMemory(&QosInfo, sizeof(QBSS_STA_INFO_PARM));
				QosInfo.UAPSD_AC_BE = pAd->CommonCfg.bAPSDAC_BE;
				QosInfo.UAPSD_AC_BK = pAd->CommonCfg.bAPSDAC_BK;
				QosInfo.UAPSD_AC_VI = pAd->CommonCfg.bAPSDAC_VI;
				QosInfo.UAPSD_AC_VO = pAd->CommonCfg.bAPSDAC_VO;
				QosInfo.MaxSPLength = pAd->CommonCfg.MaxSPLength;
				WmeIe[8] |= *(PUCHAR) &QosInfo;
			} else {
				/* The Parameter Set Count is set to ¡§0¡¨ in the association request frames */
				/* WmeIe[8] |= (pAd->MlmeAux.APEdcaParm.EdcaUpdateCount & 0x0f); */
			}

			MakeOutgoingFrame(pOutBuffer + FrameLen, &tmp, 9, &WmeIe[0], END_OF_ARGS);
			FrameLen += tmp;
		}
#ifdef RT_CFG80211_P2P_CONCURRENT_DEVICE
		apcli_entry->ReqVarIELen = 0;
		NdisZeroMemory(apcli_entry->ReqVarIEs, MAX_VIE_LEN);

		if ((apcli_entry->wpa_supplicant_info.WpaSupplicantUP & 0x7F) ==
		    WPA_SUPPLICANT_ENABLE) {
			ULONG TmpWpaAssocIeLen = 0;
			USHORT VarIesOffset = 0;

			DBGPRINT(RT_DEBUG_TRACE,
				 ("%s:: APCLI WPA_ASSOC_IE FROM SUPPLICANT\n", __func__));
			MakeOutgoingFrame(pOutBuffer + FrameLen, &TmpWpaAssocIeLen,
					  apcli_entry->wpa_supplicant_info.WpaAssocIeLen,
					  apcli_entry->wpa_supplicant_info.pWpaAssocIe,
					  END_OF_ARGS);

			FrameLen += TmpWpaAssocIeLen;

			NdisMoveMemory(apcli_entry->ReqVarIEs + VarIesOffset,
				       apcli_entry->wpa_supplicant_info.pWpaAssocIe,
				       apcli_entry->wpa_supplicant_info.WpaAssocIeLen);
			VarIesOffset += apcli_entry->wpa_supplicant_info.WpaAssocIeLen;

			/* Set Variable IEs Length */
			apcli_entry->ReqVarIELen = VarIesOffset;
		} else
#endif /* RT_CFG80211_P2P_CONCURRENT_DEVICE */
			/* Append RSN_IE when WPAPSK OR WPA2PSK, */
		if (((wdev->AuthMode == Ndis802_11AuthModeWPAPSK) ||
		     (wdev->AuthMode == Ndis802_11AuthModeWPA2PSK))
#ifdef WPA_SUPPLICANT_SUPPORT
			    || (wdev->AuthMode >= Ndis802_11AuthModeWPA)
#endif /* WPA_SUPPLICANT_SUPPORT */
#ifdef WSC_AP_SUPPORT
			    && ((apcli_entry->WscControl.WscConfMode == WSC_DISABLE)
				|| ((apcli_entry->WscControl.WscConfMode != WSC_DISABLE)
				    && !(apcli_entry->WscControl.bWscTrigger)))
#endif /* WSC_AP_SUPPORT */
		    ) {
			RSNIe = IE_WPA;

			if ((wdev->AuthMode == Ndis802_11AuthModeWPA2PSK)
#ifdef WPA_SUPPLICANT_SUPPORT
			    || (wdev->AuthMode == Ndis802_11AuthModeWPA2)
#endif /* WPA_SUPPLICANT_SUPPORT */
			    )
				RSNIe = IE_WPA2;

#ifdef WPA_SUPPLICANT_SUPPORT
			if (wdev->AuthMode == Ndis802_11AuthModeWPA2) {
				INT idx;
				BOOLEAN FoundPMK = FALSE;
				/* Search chched PMKID, append it if existed */
				for (idx = 0; idx < PMKID_NO; idx++) {
					if (NdisEqualMemory
					    (ApAddr, &apcli_entry->SavedPMK[idx].BSSID, 6)) {
						FoundPMK = TRUE;
						break;
					}
				}

				/*
				   When AuthMode is WPA2-Enterprise and AP reboot or STA lost AP,
				   AP would not do PMK cache with STA after STA re-connect to AP again.
				   In this case, driver doesn't need to send PMKID to AP and WpaSupplicant.
				 */
				if ((wdev->AuthMode == Ndis802_11AuthModeWPA2) &&
				    (NdisEqualMemory
				     (pAd->MlmeAux.Bssid, pAd->CommonCfg.LastBssid,
				      MAC_ADDR_LEN))) {
					FoundPMK = FALSE;
				}

				if (FoundPMK) {
					/* Set PMK number */
					*(PUSHORT) & apcli_entry->RSN_IE[apcli_entry->RSNIE_Len] =
					    1;
					NdisMoveMemory(&apcli_entry->RSN_IE
						       [apcli_entry->RSNIE_Len + 2],
						       &apcli_entry->SavedPMK[idx].PMKID, 16);
					apcli_entry->RSNIE_Len += 18;
				}
			}
#ifdef SIOCSIWGENIE
			if ((apcli_entry->wpa_supplicant_info.
			     WpaSupplicantUP & WPA_SUPPLICANT_ENABLE)
			    && (apcli_entry->wpa_supplicant_info.bRSN_IE_FromWpaSupplicant == TRUE)) {
				;
			} else
#endif /* endif */
#endif /* WPA_SUPPLICANT_SUPPORT */

				MakeOutgoingFrame(pOutBuffer + FrameLen, &tmp,
						  1, &RSNIe,
						  1, &apcli_entry->RSNIE_Len,
						  apcli_entry->RSNIE_Len, apcli_entry->RSN_IE,
						  END_OF_ARGS);

			FrameLen += tmp;
		}
#ifdef WSC_AP_SUPPORT
		/* Add WSC IE if we are connecting to WSC AP */
		if ((pAd->ApCfg.ApCliTab[ifIndex].WscControl.WscConfMode != WSC_DISABLE) &&
		    (pAd->ApCfg.ApCliTab[ifIndex].WscControl.bWscTrigger)) {
			UCHAR *pWscBuf = NULL, WscIeLen = 0;
			ULONG WscTmpLen = 0;

			os_alloc_mem(pAd, (UCHAR **) &pWscBuf, 512);
/*			if( (pWscBuf = kmalloc(512, GFP_ATOMIC)) != NULL) */
			if (pWscBuf != NULL) {
				NdisZeroMemory(pWscBuf, 512);
				WscBuildAssocReqIE(&pAd->ApCfg.ApCliTab[ifIndex].WscControl,
						   pWscBuf, &WscIeLen);

				MakeOutgoingFrame(pOutBuffer + FrameLen,
						  &WscTmpLen, WscIeLen, pWscBuf, END_OF_ARGS);

				FrameLen += WscTmpLen;
/*				kfree(pWscBuf); */
				os_free_mem(NULL, pWscBuf);
			} else
				DBGPRINT(RT_DEBUG_WARN,
					 ("%s:: WscBuf Allocate failed!\n", __func__));
		}
#endif /* WSC_AP_SUPPORT */
		MiniportMMRequest(pAd, QID_AC_BE, pOutBuffer, FrameLen);
		MlmeFreeMemory(pAd, pOutBuffer);

#ifdef MAC_REPEATER_SUPPORT
		if (CliIdx != 0xFF)
			RTMPSetTimer(&apcli_entry->RepeaterCli[CliIdx].ApCliAssocTimer, Timeout);
		else
#endif /* MAC_REPEATER_SUPPORT */
			RTMPSetTimer(&apcli_entry->MlmeAux.ApCliAssocTimer, Timeout);
		*pCurrState = APCLI_ASSOC_WAIT_RSP;
	} else {
		DBGPRINT(RT_DEBUG_TRACE,
			 ("APCLI_ASSOC - ApCliMlmeAssocReqAction() sanity check failed. BUG!!!!!!\n"));
		*pCurrState = APCLI_ASSOC_IDLE;

		ApCliCtrlMsg.Status = MLME_INVALID_FORMAT;
		MlmeEnqueue(pAd, APCLI_CTRL_STATE_MACHINE, APCLI_CTRL_ASSOC_RSP,
			    sizeof(APCLI_CTRL_MSG_STRUCT), &ApCliCtrlMsg, ifIndex);
	}
}

/*
    ==========================================================================
    Description:
	Upper layer issues disassoc request
    Parameters:
	Elem -
    ==========================================================================
 */
static VOID ApCliMlmeDisassocReqAction(IN PRTMP_ADAPTER pAd, IN MLME_QUEUE_ELEM * Elem)
{
	PMLME_DISASSOC_REQ_STRUCT pDisassocReq;
	HEADER_802_11 DisassocHdr;
	PUCHAR pOutBuffer = NULL;
	ULONG FrameLen = 0;
	NDIS_STATUS NStatus;
	APCLI_CTRL_MSG_STRUCT ApCliCtrlMsg;
	USHORT ifIndex = (USHORT) (Elem->Priv);
	PULONG pCurrState = NULL;
#ifdef MAC_REPEATER_SUPPORT
	UCHAR CliIdx = 0xFF;
#endif /* MAC_REPEATER_SUPPORT */

	if ((ifIndex >= MAX_APCLI_NUM)
#ifdef MAC_REPEATER_SUPPORT
	    && (ifIndex < 64)
#endif /* MAC_REPEATER_SUPPORT */
	    )
		return;

#ifdef MAC_REPEATER_SUPPORT
	if (ifIndex >= 64) {
		CliIdx = ((ifIndex - 64) % 16);
		ifIndex = ((ifIndex - 64) / 16);
		pCurrState = &pAd->ApCfg.ApCliTab[ifIndex].RepeaterCli[CliIdx].AssocCurrState;
	} else
#endif /* MAC_REPEATER_SUPPORT */
		pCurrState = &pAd->ApCfg.ApCliTab[ifIndex].AssocCurrState;

	/* skip sanity check */
	pDisassocReq = (PMLME_DISASSOC_REQ_STRUCT) (Elem->Msg);

	/* allocate and send out DeassocReq frame */
	NStatus = MlmeAllocateMemory(pAd, &pOutBuffer);	/*Get an unused nonpaged memory */
	if (NStatus != NDIS_STATUS_SUCCESS) {
		DBGPRINT(RT_DEBUG_TRACE,
			 ("APCLI_ASSOC - ApCliMlmeDisassocReqAction() allocate memory failed\n"));
		*pCurrState = APCLI_ASSOC_IDLE;

		ApCliCtrlMsg.Status = MLME_FAIL_NO_RESOURCE;
#ifdef MAC_REPEATER_SUPPORT
		ApCliCtrlMsg.BssIdx = ifIndex;
		ApCliCtrlMsg.CliIdx = CliIdx;
		ifIndex = (USHORT) (Elem->Priv);
#endif /* MAC_REPEATER_SUPPORT */
		MlmeEnqueue(pAd, APCLI_CTRL_STATE_MACHINE, APCLI_CTRL_DEASSOC_RSP,
			    sizeof(APCLI_CTRL_MSG_STRUCT), &ApCliCtrlMsg, ifIndex);
		return;
	}

	DBGPRINT(RT_DEBUG_TRACE,
		 ("APCLI_ASSOC - Send DISASSOC request [BSSID::%02x:%02x:%02x:%02x:%02x:%02x]\n",
		  pDisassocReq->Addr[0], pDisassocReq->Addr[1], pDisassocReq->Addr[2],
		  pDisassocReq->Addr[3], pDisassocReq->Addr[4], pDisassocReq->Addr[5]));
	ApCliMgtMacHeaderInit(pAd, &DisassocHdr, SUBTYPE_DISASSOC, 0, pDisassocReq->Addr,
			      pDisassocReq->Addr, ifIndex);

#ifdef MAC_REPEATER_SUPPORT
	if (CliIdx != 0xFF)
		COPY_MAC_ADDR(DisassocHdr.Addr2,
			      pAd->ApCfg.ApCliTab[ifIndex].RepeaterCli[CliIdx].CurrentAddress);
#endif /* MAC_REPEATER_SUPPORT */

	MakeOutgoingFrame(pOutBuffer, &FrameLen,
			  sizeof(HEADER_802_11), &DisassocHdr,
			  2, &pDisassocReq->Reason, END_OF_ARGS);
	MiniportMMRequest(pAd, QID_AC_BE, pOutBuffer, FrameLen);
	MlmeFreeMemory(pAd, pOutBuffer);

	*pCurrState = APCLI_ASSOC_IDLE;

	ApCliCtrlMsg.Status = MLME_SUCCESS;

#ifdef MAC_REPEATER_SUPPORT
	ApCliCtrlMsg.BssIdx = ifIndex;
	ApCliCtrlMsg.CliIdx = CliIdx;
	ifIndex = (USHORT) (Elem->Priv);
#endif /* MAC_REPEATER_SUPPORT */

	MlmeEnqueue(pAd, APCLI_CTRL_STATE_MACHINE, APCLI_CTRL_DEASSOC_RSP,
		    sizeof(APCLI_CTRL_MSG_STRUCT), &ApCliCtrlMsg, ifIndex);

#ifdef WPA_SUPPLICANT_SUPPORT
	if (pAd->ApCfg.ApCliTab[ifIndex].wpa_supplicant_info.WpaSupplicantUP !=
	    WPA_SUPPLICANT_DISABLE) {
		/*send disassociate event to wpa_supplicant */
		RtmpOSWrielessEventSend(pAd->net_dev, RT_WLAN_EVENT_CUSTOM, RT_DISASSOC_EVENT_FLAG,
					NULL, NULL, 0);
	}
	RtmpOSWrielessEventSend(pAd->net_dev, SIOCGIWAP, -1, NULL, NULL, 0);
	RTMPSendWirelessEvent(pAd, IW_DISASSOC_EVENT_FLAG, NULL, BSS0, 0);
#endif /* WPA_SUPPLICANT_SUPPORT */

#ifdef RT_CFG80211_P2P_CONCURRENT_DEVICE
	RT_CFG80211_LOST_GO_INFORM(pAd, pDisassocReq->Reason);
#endif /* RT_CFG80211_P2P_CONCURRENT_DEVICE */
}

/*
    ==========================================================================
    Description:
	peer sends assoc rsp back
    Parameters:
	Elme - MLME message containing the received frame
    ==========================================================================
 */
static VOID ApCliPeerAssocRspAction(IN PRTMP_ADAPTER pAd, IN MLME_QUEUE_ELEM * Elem)
{
	BOOLEAN Cancelled;
	USHORT CapabilityInfo, Status, Aid;
	UCHAR SupRate[MAX_LEN_OF_SUPPORTED_RATES], SupRateLen;
	UCHAR ExtRate[MAX_LEN_OF_SUPPORTED_RATES], ExtRateLen;
	UCHAR Addr2[MAC_ADDR_LEN];
	EDCA_PARM EdcaParm;
	UCHAR CkipFlag;
	APCLI_CTRL_MSG_STRUCT ApCliCtrlMsg;
	HT_CAPABILITY_IE HtCapability;
	ADD_HT_INFO_IE AddHtInfo;	/* AP might use this additional ht info IE */
	UCHAR HtCapabilityLen;
	UCHAR AddHtInfoLen;
	UCHAR NewExtChannelOffset = 0xff;
	USHORT ifIndex = (USHORT) (Elem->Priv);
	ULONG *pCurrState = NULL;
	PAPCLI_STRUCT pApCliEntry = NULL;
#ifdef MAC_REPEATER_SUPPORT
	UCHAR CliIdx = 0xFF;
#endif /* MAC_REPEATER_SUPPORT */
	IE_LISTS *ie_list = NULL;

	if ((ifIndex >= MAX_APCLI_NUM)
#ifdef MAC_REPEATER_SUPPORT
	    && (ifIndex < 64)
#endif /* MAC_REPEATER_SUPPORT */
	    )
		return;

#ifdef MAC_REPEATER_SUPPORT
	if (ifIndex >= 64) {
		CliIdx = ((ifIndex - 64) % 16);
		ifIndex = ((ifIndex - 64) / 16);
		pCurrState = &pAd->ApCfg.ApCliTab[ifIndex].RepeaterCli[CliIdx].AssocCurrState;
	} else
#endif /* MAC_REPEATER_SUPPORT */
		pCurrState = &pAd->ApCfg.ApCliTab[ifIndex].AssocCurrState;

	pApCliEntry = &pAd->ApCfg.ApCliTab[ifIndex];

	os_alloc_mem(pAd, (UCHAR **) &ie_list, sizeof(IE_LISTS));
	if (ie_list == NULL) {
		DBGPRINT(RT_DEBUG_OFF, ("%s():mem alloc failed!\n", __func__));
		return;
	}

	NdisZeroMemory((UCHAR *) ie_list, sizeof(IE_LISTS));

	if (ApCliPeerAssocRspSanity
	    (pAd, Elem->Msg, Elem->MsgLen, Addr2, &CapabilityInfo, &Status, &Aid, SupRate,
	     &SupRateLen, ExtRate, &ExtRateLen, &HtCapability, &AddHtInfo, &HtCapabilityLen,
	     &AddHtInfoLen, &NewExtChannelOffset, &EdcaParm, &CkipFlag, ie_list)) {
		/* The frame is for me ? */
		if (MAC_ADDR_EQUAL(Addr2, pAd->ApCfg.ApCliTab[ifIndex].MlmeAux.Bssid)) {
#ifdef RT_CFG80211_P2P_CONCURRENT_DEVICE
			PFRAME_802_11 pFrame = (PFRAME_802_11) (Elem->Msg);
#endif /* #ifdef RT_CFG80211_P2P_CONCURRENT_DEVICE */
			DBGPRINT(RT_DEBUG_OFF,
				 ("APCLI_ASSOC - receive ASSOC_RSP to me (status=%d)\n", Status));
#ifdef RT_CFG80211_P2P_CONCURRENT_DEVICE
			/* Store the AssocRsp Frame to wpa_supplicant via CFG80211 */
			NdisZeroMemory(pAd->ApCfg.ApCliTab[ifIndex].ResVarIEs, MAX_VIE_LEN);
			pAd->ApCfg.ApCliTab[ifIndex].ResVarIELen = 0;
			pAd->ApCfg.ApCliTab[ifIndex].ResVarIELen =
			    Elem->MsgLen - 6 - sizeof(HEADER_802_11);
			NdisCopyMemory(pAd->ApCfg.ApCliTab[ifIndex].ResVarIEs, &pFrame->Octet[6],
				       pAd->ApCfg.ApCliTab[ifIndex].ResVarIELen);
#endif /* RT_CFG80211_P2P_CONCURRENT_DEVICE */

#ifdef MAC_REPEATER_SUPPORT
			if (CliIdx != 0xFF)
				RTMPCancelTimer(&pAd->ApCfg.ApCliTab[ifIndex].RepeaterCli[CliIdx].
						ApCliAssocTimer, &Cancelled);
			else
#endif /* MAC_REPEATER_SUPPORT */
				RTMPCancelTimer(&pAd->ApCfg.ApCliTab[ifIndex].MlmeAux.
						ApCliAssocTimer, &Cancelled);
			if (Status == MLME_SUCCESS) {
				/* go to procedure listed on page 376 */
#ifdef MAC_REPEATER_SUPPORT
			if (CliIdx == 0xFF)
#endif /* MAC_REPEATER_SUPPORT */
				{
					ApCliAssocPostProc(pAd, Addr2, CapabilityInfo, ifIndex,
							   SupRate, SupRateLen, ExtRate, ExtRateLen,
							   &EdcaParm, &HtCapability,
							   HtCapabilityLen, &AddHtInfo);
					pAd->ApCfg.ApCliTab[0].MlmeAux.Aid = Aid;

#ifdef DOT11_VHT_AC
					RTMPZeroMemory(&pApCliEntry->MlmeAux.vht_cap,
						       sizeof(VHT_CAP_IE));
					RTMPZeroMemory(&pApCliEntry->MlmeAux.vht_op,
						       sizeof(VHT_OP_IE));
					pApCliEntry->MlmeAux.vht_cap_len = 0;
					pApCliEntry->MlmeAux.vht_op_len = 0;
					if (WMODE_CAP_AC(pAd->CommonCfg.PhyMode)
					    && ie_list->vht_cap_len && ie_list->vht_op_len) {
						DBGPRINT(RT_DEBUG_TRACE,
							 ("There is vht le at Assoc Rsp ifIndex=%d vht_cap_len=%d\n",
							  ifIndex, ie_list->vht_cap_len));
						NdisMoveMemory(&pApCliEntry->MlmeAux.vht_cap,
							       &(ie_list->vht_cap),
							       ie_list->vht_cap_len);
						pApCliEntry->MlmeAux.vht_cap_len =
						    ie_list->vht_cap_len;
						NdisMoveMemory(&pApCliEntry->MlmeAux.vht_op,
							       &(ie_list->vht_op),
							       ie_list->vht_op_len);
						pApCliEntry->MlmeAux.vht_op_len =
						    ie_list->vht_op_len;
					}
#endif /* DOT11_VHT_AC */
				}

				ApCliCtrlMsg.Status = MLME_SUCCESS;
#ifdef MAC_REPEATER_SUPPORT
				ApCliCtrlMsg.BssIdx = ifIndex;
				ApCliCtrlMsg.CliIdx = CliIdx;
				ifIndex = (USHORT) (Elem->Priv);
#endif /* MAC_REPEATER_SUPPORT */

				MlmeEnqueue(pAd, APCLI_CTRL_STATE_MACHINE, APCLI_CTRL_ASSOC_RSP,
					    sizeof(APCLI_CTRL_MSG_STRUCT), &ApCliCtrlMsg, ifIndex);
			} else {
				ApCliCtrlMsg.Status = Status;
				MlmeEnqueue(pAd, APCLI_CTRL_STATE_MACHINE, APCLI_CTRL_ASSOC_RSP,
					    sizeof(APCLI_CTRL_MSG_STRUCT), &ApCliCtrlMsg, ifIndex);
			}

			*pCurrState = APCLI_ASSOC_IDLE;
		}
	} else {
		DBGPRINT(RT_DEBUG_TRACE,
			 ("APCLI_ASSOC - ApCliPeerAssocRspAction() sanity check fail\n"));
	}

	if (ie_list)
		os_free_mem(pAd, ie_list);
}

/*
    ==========================================================================
    Description:
	left part of IEEE 802.11/1999 p.374
    Parameters:
	Elem - MLME message containing the received frame
    ==========================================================================
 */
static VOID ApCliPeerDisassocAction(IN PRTMP_ADAPTER pAd, IN MLME_QUEUE_ELEM * Elem)
{
	UCHAR Addr2[MAC_ADDR_LEN];
	USHORT Reason;
	USHORT ifIndex = (USHORT) (Elem->Priv);
	ULONG *pCurrState = NULL;
#ifdef MAC_REPEATER_SUPPORT
	UCHAR CliIdx = 0xFF;
#endif /* MAC_REPEATER_SUPPORT */

	if ((ifIndex >= MAX_APCLI_NUM)
#ifdef MAC_REPEATER_SUPPORT
	    && (ifIndex < 64)
#endif /* MAC_REPEATER_SUPPORT */
	    )
		return;

#ifdef MAC_REPEATER_SUPPORT
	if (ifIndex >= 64) {
		CliIdx = ((ifIndex - 64) % 16);
		ifIndex = ((ifIndex - 64) / 16);
		pCurrState = &pAd->ApCfg.ApCliTab[ifIndex].RepeaterCli[CliIdx].AssocCurrState;
	} else
#endif /* MAC_REPEATER_SUPPORT */
		pCurrState = &pAd->ApCfg.ApCliTab[ifIndex].AssocCurrState;

#ifdef MAC_REPEATER_SUPPORT
	DBGPRINT(RT_DEBUG_WARN,
		 (" (%s) ifIndex = %d, CliIdx = %d !!!\n", __func__, ifIndex, CliIdx));
#endif /* MAC_REPEATER_SUPPORT */

	if (PeerDisassocSanity(pAd, Elem->Msg, Elem->MsgLen, Addr2, &Reason)) {
		if (MAC_ADDR_EQUAL(pAd->ApCfg.ApCliTab[ifIndex].MlmeAux.Bssid, Addr2)) {
			*pCurrState = APCLI_ASSOC_IDLE;

#ifdef MAC_REPEATER_SUPPORT
			if ((pAd->ApCfg.bMACRepeaterEn == TRUE) && (CliIdx != 0xFF)) {
				/* free resources of BA */
				BASessionTearDownALL(pAd,
						     pAd->ApCfg.ApCliTab[ifIndex].
						     RepeaterCli[CliIdx].MacTabWCID);
				RTMPRemoveRepeaterDisconnectEntry(pAd, ifIndex, CliIdx);
				RTMPRemoveRepeaterEntry(pAd, ifIndex, CliIdx);
			} else
#endif /* MAC_REPEATER_SUPPORT */
			{
				MlmeEnqueue(pAd, APCLI_CTRL_STATE_MACHINE,
					    APCLI_CTRL_PEER_DISCONNECT_REQ, 0, NULL, ifIndex);
				RTMP_MLME_HANDLER(pAd);
			}
		}
	} else {
		DBGPRINT(RT_DEBUG_TRACE,
			 ("APCLI_ASSOC - ApCliPeerDisassocAction() sanity check fail\n"));
	}
}

/*
    ==========================================================================
    Description:
	what the state machine will do after assoc timeout
    ==========================================================================
 */
static VOID ApCliAssocTimeoutAction(IN PRTMP_ADAPTER pAd, IN MLME_QUEUE_ELEM * Elem)
{
	USHORT ifIndex = (USHORT) (Elem->Priv);
	PULONG pCurrState = NULL;
#ifdef MAC_REPEATER_SUPPORT
	UCHAR CliIdx = 0xFF;
#endif /* MAC_REPEATER_SUPPORT */

	DBGPRINT(RT_DEBUG_TRACE, ("APCLI_ASSOC - ApCliAssocTimeoutAction\n"));

	if ((ifIndex >= MAX_APCLI_NUM)
#ifdef MAC_REPEATER_SUPPORT
	    && (ifIndex < 64)
#endif /* MAC_REPEATER_SUPPORT */
	    )
		return;

#ifdef MAC_REPEATER_SUPPORT
	if (ifIndex >= 64) {
		CliIdx = ((ifIndex - 64) % 16);
		ifIndex = ((ifIndex - 64) / 16);
		pCurrState = &pAd->ApCfg.ApCliTab[ifIndex].RepeaterCli[CliIdx].AssocCurrState;
	} else
#endif /* MAC_REPEATER_SUPPORT */
		pCurrState = &pAd->ApCfg.ApCliTab[ifIndex].AssocCurrState;

	*pCurrState = APCLI_ASSOC_IDLE;

#ifdef MAC_REPEATER_SUPPORT
	ifIndex = (USHORT) (Elem->Priv);
#endif /* MAC_REPEATER_SUPPORT */

	MlmeEnqueue(pAd, APCLI_CTRL_STATE_MACHINE, APCLI_CTRL_ASSOC_REQ_TIMEOUT, 0, NULL, ifIndex);
}

static VOID ApCliInvalidStateWhenAssoc(IN PRTMP_ADAPTER pAd, IN MLME_QUEUE_ELEM * Elem)
{
	APCLI_CTRL_MSG_STRUCT ApCliCtrlMsg;
	USHORT ifIndex = (USHORT) (Elem->Priv);
	PULONG pCurrState = NULL;
#ifdef MAC_REPEATER_SUPPORT
	UCHAR CliIdx = 0xFF;
#endif /* MAC_REPEATER_SUPPORT */

	if ((ifIndex >= MAX_APCLI_NUM)
#ifdef MAC_REPEATER_SUPPORT
	    && (ifIndex < 64)
#endif /* MAC_REPEATER_SUPPORT */
	    )
		return;

#ifdef MAC_REPEATER_SUPPORT
	if (ifIndex >= 64) {
		CliIdx = ((ifIndex - 64) % 16);
		ifIndex = ((ifIndex - 64) / 16);
		pCurrState = &pAd->ApCfg.ApCliTab[ifIndex].RepeaterCli[CliIdx].AssocCurrState;
	} else
#endif /* MAC_REPEATER_SUPPORT */
		pCurrState = &pAd->ApCfg.ApCliTab[ifIndex].AssocCurrState;

	DBGPRINT(RT_DEBUG_TRACE,
		 ("APCLI_ASSOC - ApCliInvalidStateWhenAssoc(state=%ld), reset APCLI_ASSOC state machine\n",
		  *pCurrState));
	*pCurrState = APCLI_ASSOC_IDLE;

	ApCliCtrlMsg.Status = MLME_STATE_MACHINE_REJECT;

#ifdef MAC_REPEATER_SUPPORT
	ifIndex = (USHORT) (Elem->Priv);
#endif /* MAC_REPEATER_SUPPORT */

	MlmeEnqueue(pAd, APCLI_CTRL_STATE_MACHINE, APCLI_CTRL_ASSOC_RSP,
		    sizeof(APCLI_CTRL_MSG_STRUCT), &ApCliCtrlMsg, ifIndex);
}

static VOID ApCliInvalidStateWhenDisassociate(IN PRTMP_ADAPTER pAd, IN MLME_QUEUE_ELEM * Elem)
{
	APCLI_CTRL_MSG_STRUCT ApCliCtrlMsg;
	USHORT ifIndex = (USHORT) (Elem->Priv);
	ULONG *pCurrState = NULL;
#ifdef MAC_REPEATER_SUPPORT
	UCHAR CliIdx = 0xFF;
#endif /* MAC_REPEATER_SUPPORT */

	if ((ifIndex >= MAX_APCLI_NUM)
#ifdef MAC_REPEATER_SUPPORT
	    && (ifIndex < 64)
#endif /* MAC_REPEATER_SUPPORT */
	    )
		return;

#ifdef MAC_REPEATER_SUPPORT
	if (ifIndex >= 64) {
		CliIdx = ((ifIndex - 64) % 16);
		ifIndex = ((ifIndex - 64) / 16);
		pCurrState = &pAd->ApCfg.ApCliTab[ifIndex].RepeaterCli[CliIdx].AssocCurrState;
	} else
#endif /* MAC_REPEATER_SUPPORT */
		pCurrState = &pAd->ApCfg.ApCliTab[ifIndex].AssocCurrState;

	DBGPRINT(RT_DEBUG_TRACE,
		 ("APCLI_ASSOC - InvalidStateWhenApCliDisassoc(state=%ld), reset APCLI_ASSOC state machine\n",
		  *pCurrState));
	*pCurrState = APCLI_ASSOC_IDLE;

	ApCliCtrlMsg.Status = MLME_STATE_MACHINE_REJECT;

#ifdef MAC_REPEATER_SUPPORT
	ApCliCtrlMsg.BssIdx = ifIndex;
	ApCliCtrlMsg.CliIdx = CliIdx;
	ifIndex = (USHORT) (Elem->Priv);
#endif /* MAC_REPEATER_SUPPORT */

	MlmeEnqueue(pAd, APCLI_CTRL_STATE_MACHINE, APCLI_CTRL_DEASSOC_RSP,
		    sizeof(APCLI_CTRL_MSG_STRUCT), &ApCliCtrlMsg, ifIndex);
}

/*
    ==========================================================================
    Description:
	procedures on IEEE 802.11/1999 p.376
    Parametrs:
    ==========================================================================
 */
static VOID ApCliAssocPostProc(IN PRTMP_ADAPTER pAd,
			       IN PUCHAR pAddr2,
			       IN USHORT CapabilityInfo,
			       IN USHORT IfIndex,
			       IN UCHAR SupRate[],
			       IN UCHAR SupRateLen,
			       IN UCHAR ExtRate[],
			       IN UCHAR ExtRateLen,
			       IN PEDCA_PARM pEdcaParm,
			       IN HT_CAPABILITY_IE *pHtCapability,
			       IN UCHAR HtCapabilityLen, IN ADD_HT_INFO_IE *pAddHtInfo)
{
	APCLI_STRUCT *pApCliEntry = NULL;

	if (IfIndex >= MAX_APCLI_NUM)
		return;

	pApCliEntry = &pAd->ApCfg.ApCliTab[IfIndex];

	pApCliEntry->MlmeAux.BssType = BSS_INFRA;
	pApCliEntry->MlmeAux.CapabilityInfo = CapabilityInfo & SUPPORTED_CAPABILITY_INFO;
	NdisMoveMemory(&pApCliEntry->MlmeAux.APEdcaParm, pEdcaParm, sizeof(EDCA_PARM));

	/* filter out un-supported rates */
	pApCliEntry->MlmeAux.SupRateLen = SupRateLen;
	NdisMoveMemory(pApCliEntry->MlmeAux.SupRate, SupRate, SupRateLen);
	RTMPCheckRates(pAd, pApCliEntry->MlmeAux.SupRate, &(pApCliEntry->MlmeAux.SupRateLen));

	/* filter out un-supported rates */
	pApCliEntry->MlmeAux.ExtRateLen = ExtRateLen;
	NdisMoveMemory(pApCliEntry->MlmeAux.ExtRate, ExtRate, ExtRateLen);
	RTMPCheckRates(pAd, pApCliEntry->MlmeAux.ExtRate, &(pApCliEntry->MlmeAux.ExtRateLen));

	DBGPRINT(RT_DEBUG_TRACE,
		 (HtCapabilityLen ? "%s===> 11n HT STA\n" : "%s===> legacy STA\n", __func__));

	if (HtCapabilityLen > 0 && WMODE_CAP_N(pAd->CommonCfg.PhyMode))
		ApCliCheckHt(pAd, IfIndex, pHtCapability, pAddHtInfo);

}

#ifdef WPA_SUPPLICANT_SUPPORT
VOID ApcliSendAssocIEsToWpaSupplicant(IN RTMP_ADAPTER *pAd, IN UINT ifIndex)
{
	STRING custom[IW_CUSTOM_MAX] = { 0 };

	if ((pAd->ApCfg.ApCliTab[ifIndex].ReqVarIELen + 17) <= IW_CUSTOM_MAX) {
		snprintf(custom, sizeof(custom), "ASSOCINFO_ReqIEs=");
		NdisMoveMemory(custom + 17, pAd->ApCfg.ApCliTab[ifIndex].ReqVarIEs,
			       pAd->ApCfg.ApCliTab[ifIndex].ReqVarIELen);
		RtmpOSWrielessEventSend(pAd->net_dev, RT_WLAN_EVENT_CUSTOM, RT_REQIE_EVENT_FLAG,
					NULL, (PUCHAR) custom,
					pAd->ApCfg.ApCliTab[ifIndex].ReqVarIELen + 17);

		RtmpOSWrielessEventSend(pAd->net_dev, RT_WLAN_EVENT_CUSTOM, RT_ASSOCINFO_EVENT_FLAG,
					NULL, NULL, 0);
	} else
		DBGPRINT(RT_DEBUG_TRACE,
			 ("pAd->ApCfg.ApCliTab[%d].ReqVarIELen + 17 > MAX_CUSTOM_LEN\n", ifIndex));
}
#endif /*WPA_SUPPLICANT_SUPPORT */
#endif /* APCLI_SUPPORT */
