/* 2008-03-12: File added by Sony Corporation */
#ifndef __ZDASOCSVC_C__
#define __ZDASOCSVC_C__

#include "zd80211.h"
#include "zd1205.h"
#include "zdturbo_burst.h"

U8 AsocState = STE_ASOC_IDLE;
static MacAddr_t AsSta;
extern struct net_device *g_dev;


BOOLEAN Disasoc(Signal_t *signal)
{
	FrmDesc_t *pfrmDesc;
	Frame_t *rdu;
	MacAddr_t Sta;
	ReasonCode Rsn;
	U8 vapId = 0;

	//ZDEBUG("Disasoc");
	//FPRINT("Disasoc");
	pfrmDesc = signal->frmInfo.frmDesc;
	rdu = pfrmDesc->mpdu;
	memcpy((U8 *)&Sta, (U8 *)(addr2(rdu)), 6); // Get the address of the transmitter.
	Rsn = (ReasonCode)(reason(rdu));

	if (mBssType == INFRASTRUCTURE_BSS)
	{// This frame should be come from the associated AP.
	    if ((!mAssoc) || (memcmp(&Sta, (U8*)&mBssId, 6) != 0))
	    { //Not for this BSSID
		//discard this packet
		freeFdesc(pfrmDesc);
		return TRUE;
	    }
	    else
	    {
		freeFdesc(pfrmDesc);
		UpdateStaStatus(&Sta, STATION_STATE_DIS_ASOC, vapId);
		pdot11Obj->StatusNotify(STA_DISASSOCIATED, (U8 *)&Sta);
		mAssoc = FALSE;
		memset((U8 *)&mBssId, 0, 6);
                printk(KERN_ERR "Rx Disasoc from AP(Rsn:%d),set DIS_CONNECT_SET\n",Rsn);
		mRequestFlag |= DIS_CONNECT_SET;
		return TRUE;
            }
	}
	else if (mBssType == AP_BSS)
	{
	    if (memcmp(addr1(rdu), (U8*)&mBssId, 6))
	    { //Not for this BSSID
		freeFdesc(pfrmDesc);
		return TRUE;
            }
	    UpdateStaStatus(&Sta, STATION_STATE_DIS_ASOC, vapId);
	    freeFdesc(pfrmDesc);
	    //here to handle disassoc ind.
	    pdot11Obj->StatusNotify(STA_DISASSOCIATED, (U8 *)&Sta);
	    return TRUE;
	}
	else
	{
	    freeFdesc(pfrmDesc);
	    return TRUE;
	}
}


BOOLEAN DisasocReq(Signal_t *signal)
{
	FrmDesc_t *pfrmDesc;
	MacAddr_t Sta;
	ReasonCode Rsn = RC_UNSPEC_REASON;
	U8 vapId = 0;

	//ZDEBUG("DisasocReq");
	//FPRINT("DisasocReq");

	memcpy((U8 *)&Sta, (U8 *)&signal->frmInfo.Sta, 6);
	Rsn = signal->frmInfo.rCode;
	pdot11Obj->StatusNotify(STA_DISASSOCIATED, (U8 *)&Sta);

	vapId = signal->vapId;
	UpdateStaStatus(&Sta, STATION_STATE_DIS_ASOC, vapId);

	pfrmDesc = allocFdesc();
	if(!pfrmDesc){
		sigEnque(pMgtQ, (signal));
		return FALSE;
	}

	mkDisAssoc_DeAuthFrm(pfrmDesc, ST_DISASOC, &Sta, Rsn, vapId);
	sendMgtFrame(signal, pfrmDesc);

	if (mBssType == INFRASTRUCTURE_BSS){
		if (memcmp((U8 *)&mBssId, (U8 *)&Sta, 6) == 0){ // my AP
			mAssoc = FALSE;
			memset((U8 *)&mBssId, 0, 6);
		}
	}

	return FALSE;
}


BOOLEAN Re_Asociate(Signal_t *signal)
{
	struct zd1205_private *macp=g_dev->priv;
	Hash_t	*pHash;
	FrmDesc_t *pfrmDesc;
	Frame_t *rdu;
	MacAddr_t Sta;
	U16 aid = 0;
	StatusCode asStatus;
	U8 lsInterval;
	Element WPA;
	Element asSsid;
	Element asRates;
	Element extRates;
	U16 cap;
	U8 ZydasMode = 0;
	int i;
	U8 tmpMaxRate = 0x02;
	U8 MaxRate;
	U16 notifyStatus = STA_ASOC_REQ;
	U16 notifyStatus1 = STA_ASSOCIATED;
	TypeSubtype type = ST_ASOC_RSP;
	U8	Preamble = 0;
	U8	HigestBasicRate = 0;
	U8	vapId = 0;
	U8	Len;
	Element *pExtRate = NULL;

	BOOLEAN bErpSta = TRUE;
	pExtRate = &mExtRates;

	ZDEBUG("Re_Asociate");
	pfrmDesc = signal->frmInfo.frmDesc;

	if (mBssType == INFRASTRUCTURE_BSS){
		freeFdesc(pfrmDesc);
		return TRUE;
	} else if (mBssType == AP_BSS){
		rdu = pfrmDesc->mpdu;
		lsInterval = listenInt(pfrmDesc->mpdu);
		//FPRINT_V("lsInterval", lsInterval);
		cap = cap(rdu);
		memcpy((U8 *)&Sta, (U8 *)addr2(rdu), 6);

		if ((isGroup(addr2(rdu))) ||  (!getElem(rdu, EID_SSID, &asSsid,1))
			|| (!getElem(rdu, EID_SUPRATES, &asRates,1))){
			freeFdesc(pfrmDesc);
			return TRUE;
		}

		if ((eLen(&asSsid) != eLen(&dot11DesiredSsid) ||
			memcmp(&asSsid, &dot11DesiredSsid, eLen(&dot11DesiredSsid)+2) != 0)){
			freeFdesc(pfrmDesc);
			return TRUE;
		}

		//check capability
		if (cap & CAP_SHORT_PREAMBLE){
			if (mPreambleType == LONG_PREAMBLE){ //we are long preamble, and STA is short preamble capability
				freeFdesc(pfrmDesc);
				return TRUE;
			}
			else
				Preamble = 1;
		}
		else {
			Preamble = 0;
		}


		// Privacy not match
		if (cap & CAP_PRIVACY){
			if (!mPrivacyInvoked && macp->cardSetting.WPAIeLen==0){
			//if (!mPrivacyInvoked){
				freeFdesc(pfrmDesc);
				return TRUE;
			}
		}
		else {
			if (mPrivacyInvoked){
				freeFdesc(pfrmDesc);
				return TRUE;
			}
		}

		//check short slot time
#if 0//defined(OFDM)
		if (mMacMode != PURE_B_MODE){
			if (cap & CAP_SHORT_SLOT_TIME){
				if (!(mCap & CAP_SHORT_SLOT_TIME)){
					FPRINT("CAP_SHORT_SLOT_TIME not match!!");
					freeFdesc(pfrmDesc);
					return TRUE;
				}
			}
			else {
				if (mCap & CAP_SHORT_SLOT_TIME){
					asStatus = (StatusCode)SC_UNSUP_SHORT_SLOT_TIME;
					goto check_failed;
				}
			}
		}

		FPRINT_V("cap", cap);
#endif


		//check supported rates
		Len = eLen(&asRates);
		for (i=0; i<Len; i++){
			if ( (asRates.buf[2+i] & 0x7f) > tmpMaxRate ){
				tmpMaxRate = (asRates.buf[2+i] & 0x7f);
				if (asRates.buf[2+i] & 0x80)
					HigestBasicRate = asRates.buf[2+i];
			}

			if (((asRates.buf[2+i] & 0x7f) == 0x21) && (!(cap & CAP_PBCC_ENABLE))){ //Zydas 16.5M
				ZydasMode = 1;
				mZyDasModeClient = TRUE;
			//FPRINT("ZydasMode");
			}
		}

		if (!getElem(rdu, EID_EXT_RATES, &extRates,1)){
			void *reg = pdot11Obj->reg;
			// 11b STA
			//FPRINT("11b STA");
			if (mMacMode == PURE_G_MODE){ // don't support b only sta
				MaxRate = RateConvert((tmpMaxRate & 0x7f));
				//MaxBasicRate = RateConvert((HigestBasicRate & 0x7f));
				if (MaxRate < pdot11Obj->BasicRate){
					//FPRINT_V("MaxRate", MaxRate);
					//FPRINT_V("pdot11Obj->BasicRate", pdot11Obj->BasicRate);
					asStatus = SC_UNSUP_RATES;
					goto check_failed;
				}
			}
			bErpSta = FALSE;
            pdot11Obj->ConfigFlag |= NON_ERP_PRESENT_SET;
            pdot11Obj->ConfigFlag &= ~SHORT_SLOT_TIME_SET;
			mCap &= ~CAP_SHORT_SLOT_TIME;
			pdot11Obj->SetReg(reg, ZD_CWmin_CWmax, CW_NORMAL_SLOT);
			if(PURE_A_MODE == mMacMode) {
				pdot11Obj->ConfigFlag &= ~NON_ERP_PRESENT_SET;
				pdot11Obj->ConfigFlag |= SHORT_SLOT_TIME_SET;
				mCap |= CAP_SHORT_SLOT_TIME;
				pdot11Obj->SetReg(reg, ZD_CWmin_CWmax, CW_SHORT_SLOT);
			}

		}
		else { //11g STA
			if (mMacMode != PURE_B_MODE && mMacMode != PURE_A_MODE){

				Len = eLen(&extRates);
				for (i=0; i<Len; i++){
					if ( (extRates.buf[2+i] & 0x7f) > tmpMaxRate ){
						tmpMaxRate = (extRates.buf[2+i] & 0x7f);
					}
				}
				bErpSta = TRUE;
				//FPRINT("11g STA");
			}
			else {
				FPRINT("Pure B mode don't support G sta");
				asStatus = SC_UNSUP_RATES;
				goto check_failed;
			}
		}

		MaxRate = RateConvert((tmpMaxRate & 0x7f));

        if (MaxRate > mMaxTxRate)
            MaxRate = mMaxTxRate;

		if (signal->id == SIG_REASSOC)
			notifyStatus = STA_REASOC_REQ;

		if (!pdot11Obj->StatusNotify(notifyStatus, (U8 *)&Sta))
		{ //Accept it
			//if ((mDynKeyMode == DYN_KEY_TKIP) || (mDynKeyMode == DYN_KEY_AES)){
			if (macp->cardSetting.WPAIeLen)
            {
				if (getElem(rdu, EID_WPA, &WPA,1)||getElem(rdu,EID_RSN,&WPA,1))
				{
					//zd1205_OctetDump("AssocRequest = ", asRdu->body, asRdu->bodyLen);
					//zd1205_OctetDump("AssocRequest WPA_IE = ", &WPA.buf[2], WPA.buf[1]);
                    			if (pdot11Obj->AssocRequest)
                    			{
			    			if (pdot11Obj->AssocRequest((U8 *)&Sta, rdu->body, rdu->bodyLen))
						{ //reject
				    			asStatus = SC_UNSPEC_FAILURE;
				    			goto check_failed;
				    			//we need reason code here
						}
					}
				}
				else
				{
				    asStatus = SC_UNSPEC_FAILURE;
				    goto wpa_check_failed;
				}
			}

//wpa_check_ok:
			if (!UpdateStaStatus(&Sta, STATION_STATE_ASOC, vapId)){
				asStatus = SC_AP_FULL;
			}
			else{
				AssocInfoUpdate(&Sta, MaxRate, lsInterval, ZydasMode, Preamble, bErpSta, 0,0,1,vapId);
				aid = AIdLookup(&Sta);
				printk(KERN_DEBUG "Re_Asoc: aid=%d\n", aid);
				pHash = HashSearch(&Sta);
				if (pHash != NULL)
				{
					if (!pHash->AlreadyIn)
					{
						pHash->AlreadyIn=1;
						mCurrConnUser++;
						if (bErpSta == FALSE)
						{
							printk(KERN_DEBUG "Increment mNumBOnlySta\n");
							mNumBOnlySta++;
						}
					}
				}

				asStatus = SC_SUCCESSFUL;
				if (signal->id == SIG_REASSOC)
					notifyStatus1 = STA_REASSOCIATED;
				pdot11Obj->StatusNotify(notifyStatus1, (U8 *)&Sta);

				if (mMacMode != PURE_B_MODE && mMacMode != PURE_A_MODE){
					if (pdot11Obj->ConfigFlag & NON_ERP_PRESENT_SET){
						U32 tmpValue;
						void *reg = pdot11Obj->reg;

				 		// force enabled protection mode for debug
						mErp.buf[2] |= (NON_ERP_PRESENT | USE_PROTECTION);
						tmpValue = pdot11Obj->GetReg(reg, ZD_RTS_CTS_Rate);
						tmpValue &= ~CTS_MOD_TYPE_OFDM;
						tmpValue |= CTS_RATE_11M;
						pdot11Obj->SetReg(reg, ZD_RTS_CTS_Rate, tmpValue);

						if (Preamble == 0){ //long preamble
							mErp.buf[2] |= BARKER_PREAMBLE;
							tmpValue = pdot11Obj->GetReg(reg, ZD_RTS_CTS_Rate);
							tmpValue &= ~NON_BARKER_PMB_SET;
							tmpValue |= CTS_RATE_11M;
							pdot11Obj->SetReg(reg, ZD_RTS_CTS_Rate, tmpValue);
							//FPRINT("Enable Barker Preamble");
						}
						pdot11Obj->ConfigFlag |= ENABLE_PROTECTION_SET;
						//FPRINT("Enable Protection Mode");
					}
				}

			}
		}
		else{
//wpa_check_failed:
			asStatus = SC_UNSPEC_FAILURE;
		}
wpa_check_failed:

		aid |= 0xC000;
		if (aid != 0xC000){
			FPRINT_V("Aid", aid);
			FPRINT_V("MaxRate", MaxRate);
		}

check_failed:
		if (signal->id == SIG_REASSOC)
			type = ST_REASOC_RSP;
		mkRe_AsocRspFrm(pfrmDesc, type, &Sta, mCap, asStatus, aid, &mBrates, pExtRate, vapId);
		sendMgtFrame(signal, pfrmDesc);

		return FALSE;
	}
	else {
		freeFdesc(pfrmDesc);
		return TRUE;
	}
}

BOOLEAN Re_AsocReq(Signal_t *signal)
{
	FrmDesc_t *pfrmDesc;
	U8 vapId = 0;
	TypeSubtype subType = ST_ASOC_REQ;
	Element *pExtRate = NULL;

	FPRINT("Re_AsocReq");

	pfrmDesc = allocFdesc();
	if(!pfrmDesc){
		sigEnque(pMgtQ, (signal));
		return FALSE;
	}


	if (signal->id == SIG_REASSOC_REQ){
		subType = ST_REASOC_REQ;
	}

	pExtRate = &mExtRates;

	memcpy((U8 *)&AsSta, (U8 *)&mBssId, 6);

	mkRe_AsocReqFrm(pfrmDesc, subType, &mBssId, mCap, mListenInterval,
		&mOldAP, &mSsid, &mBrates, pExtRate, &mWPAIe, vapId);

	pdot11Obj->StartTimer(ASOC_TIMEOUT, DO_ASOC);
	AsocState = STE_WAIT_ASOC_RSP;
	return sendMgtFrame(signal, pfrmDesc);
}

BOOLEAN Re_AsocRsp(Signal_t *signal)
{
    int i;
    BssInfo_t *asocBss = NULL;
    U8 Turbo_AMSDU = FALSE, Turbo_BURST = FALSE, Turbo_AMSDU_LEN = 1;
	Hash_t	*pHash;
	FrmDesc_t *pfrmDesc;
	Frame_t *rdu;
	U8 vapId = 0;
	MacAddr_t Sta;
	U16 status;

	//ZDEBUG("Re_AsocRsp");
	FPRINT("Re_AsocRsp");

	pfrmDesc = signal->frmInfo.frmDesc;
	rdu = pfrmDesc->mpdu;

	memcpy((U8 *)&Sta, (U8 *)addr2(rdu), 6);
	if (memcmp(&AsSta, &Sta, 6) != 0){
		//FPRINT("Not for my Assoc AP");
		goto asoc_release;
	}

	pdot11Obj->StopTimer(DO_ASOC);
	AsocState = STE_ASOC_IDLE;


	status = status(rdu);
	if (status == SC_SUCCESSFUL){
		U8	Len;
		U8	tmpMaxRate = 0x02;
		U8	ZydasMode = 0;
		U8	Preamble = 0;
		U8	MaxRate;
		BOOLEAN bErpSta = FALSE;
		int i;
		U8	HigestBasicRate = 0;

		UpdateStaStatus(&Sta, STATION_STATE_ASOC, vapId);
		pHash = HashSearch(&Sta);
		if (pHash != NULL)
		{
			if (!pHash->AlreadyIn)
			{
				pHash->AlreadyIn=1;
				mCurrConnUser++;
			}
		}

		mAPCap = cap(rdu);

		//check capability
		if (mAPCap & CAP_SHORT_PREAMBLE){
			Preamble = 1;
		}
		else {
			Preamble = 0;
		}

		mAid = (aid(rdu) & 0x3FFF);
		getElem(rdu, EID_SUPRATES, &mAPBrates,1);
		//zd1205_dump_data("mAPBrates", &mAPBrates.buf[2], mAPBrates.buf[1]);

		if (getElem(rdu, EID_EXT_RATES, &mAPErates,1)){
			//zd1205_dump_data("mAPErates", &mAPErates.buf[2], mAPErates.buf[1]);
		}

		memcpy((U8 *)&mBssId, (U8 *)addr2(rdu), 6);
		mAssoc = TRUE;

		//update supported rates
		HW_SetSupportedRate(pdot11Obj, (U8 *)&mAPBrates);
		HW_SetSupportedRate(pdot11Obj, (U8 *)&mAPErates);

		Len = eLen(&mAPBrates);
		for (i=0; i<Len; i++){
			if ((mAPBrates.buf[2+i] & 0x7f) > tmpMaxRate ){
				tmpMaxRate = (mAPBrates.buf[2+i] & 0x7f);
				if (mAPBrates.buf[2+i] & 0x80)
					HigestBasicRate = mAPBrates.buf[2+i];
			}

			if (((mAPBrates.buf[2+i] & 0x7f) == 0x21) && (!(mAPCap & CAP_PBCC_ENABLE))){ //Zydas 16.5M
				ZydasMode = 1;
			}
		}


		Len = eLen(&mAPErates);
		for (i=0; i<Len; i++){
			if ((mAPErates.buf[2+i] & 0x7f) > tmpMaxRate ){
				tmpMaxRate = (mAPErates.buf[2+i] & 0x7f);
				if (mAPErates.buf[2+i] & 0x80)
					HigestBasicRate = mAPErates.buf[2+i];
			}
		}

		//FPRINT_V("tmpMaxRate", tmpMaxRate);
		//FPRINT_V("mMaxTxRate", mMaxTxRate);
		MaxRate = RateConvert((tmpMaxRate & 0x7f));
		//FPRINT_V("MaxRate", MaxRate);
		if (MaxRate > mMaxTxRate)
			MaxRate = mMaxTxRate;
		memset(mAPBrates.buf, 0, sizeof(Element));
		memset(mAPErates.buf, 0, sizeof(Element));
#if ZDCONF_LP_SUPPORT == 1
        asocBss = zd1212_bssid_to_BssInfo(mBssId.mac);
        if(asocBss != NULL)
        {
            if(asocBss->zdIE_BURST.buf[0] == EID_ZYDAS)
            {
                if(asocBss->zdIE_BURST.buf[8] & BIT_7)
                    Turbo_BURST = TRUE;
                Turbo_BURST = Turbo_BURST && pdot11Obj->BURST_MODE;
            }

            if(asocBss->zdIE_AMSDU.buf[0] == EID_ZYDAS)
            {
                if(asocBss->zdIE_AMSDU.buf[8] & BIT_0)
                {
                    Turbo_AMSDU = TRUE;
                    Turbo_AMSDU_LEN = asocBss->zdIE_AMSDU.buf[8] & BIT_1;
                }
                Turbo_AMSDU = Turbo_AMSDU && pdot11Obj->LP_MODE;
            }
        }
        else
        {
            printk("zd1212_bssid_to_BssInfo is NULL in %s, Something wrong\n", __FUNCTION__);
        }

        AssocInfoUpdate(&mBssId, MaxRate, mAid, ZydasMode, Preamble, bErpSta, Turbo_BURST, Turbo_AMSDU, Turbo_AMSDU_LEN,vapId);
#elif ZDCONF_LP_SUPPORT == 0
        AssocInfoUpdate(&mBssId, MaxRate, mAid, ZydasMode, Preamble, bErpSta,0,0,0,vapId);
#endif

		pdot11Obj->StatusNotify(STA_ASSOCIATED, (U8 *)&mBssId);
		pdot11Obj->Aid = mAid;
		mConnRetryCnt = 0;

		FPRINT_V("Aid", mAid);
		FPRINT_V("MaxRate", MaxRate);
	} else {
		FPRINT("Asoc Failed!!!");
		FPRINT_V("Status Code", status);
	}

asoc_release:
	freeFdesc(pfrmDesc);
	return TRUE;
}


BOOLEAN AsocTimeOut(Signal_t *signal)
{
	U8 vapId = 0;
	FPRINT("AsocTimeOut");

	if (AsocState == STE_WAIT_ASOC_RSP){
		AsocState = STE_ASOC_IDLE;
		UpdateStaStatus(&mBssId, STATION_STATE_DIS_ASOC, vapId);
	}

	mRequestFlag |= CONNECT_TOUT_SET;
	return FALSE;
}


BOOLEAN AsocEntry(Signal_t *signal)
{
	FrmDesc_t *pfrmDesc;

	if (AsocState == STE_ASOC_IDLE){
		switch(signal->id){
			case SIG_DIASSOC_REQ:
				return DisasocReq(signal);

			case SIG_DISASSOC:
				return Disasoc(signal);

			case SIG_ASSOC:
			case SIG_REASSOC:
				return Re_Asociate(signal);

			case SIG_ASSOC_REQ:
			case SIG_REASSOC_REQ:
                if(memcmp(mBssId.mac, zeroMacAddress, ETH_ALEN) == 0)
                   goto asoc_discard;
				return Re_AsocReq(signal);

			default:
				goto asoc_discard;
		}
	} else if (AsocState == STE_WAIT_ASOC_RSP){
		switch(signal->id){
			case SIG_DIASSOC_REQ:
				return DisasocReq(signal);

			case SIG_DISASSOC:
				return Disasoc(signal);

			case SIG_ASSOC:
			case SIG_REASSOC:
				return Re_Asociate(signal);

			case SIG_ASSOC_REQ:
			case SIG_REASSOC_REQ:
                if(memcmp(mBssId.mac, zeroMacAddress, ETH_ALEN) == 0)
                   goto asoc_discard;
				return Re_AsocReq(signal);

			case SIG_ASSOC_RSP:
			case SIG_REASSOC_RSP:
				return Re_AsocRsp(signal);

			case SIG_TO_ASOC:

				return AsocTimeOut(signal);

			default:
				goto asoc_discard;
		}
	} else
		goto asoc_discard;

asoc_discard:
	pfrmDesc = signal->frmInfo.frmDesc;
	freeFdesc(pfrmDesc);
	return TRUE;
}

#endif

