/*
 * Copyright (c) 2007-2008 Atheros Communications Inc.
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */
/*                                                                      */
/*  Module Name : init.c                                                */
/*                                                                      */
/*  Abstract                                                            */
/*      This module contains init functions.                            */
/*                                                                      */
/*  NOTES                                                               */
/*      None                                                            */
/*                                                                      */
/************************************************************************/
#include "cprecomp.h"
#include "../hal/hpreg.h"

extern const u8_t zcUpToAc[8];

u16_t zcIndextoRateBG[16] = {1000, 2000, 5500, 11000, 0, 0, 0, 0, 48000,
                               24000, 12000, 6000, 54000, 36000, 18000, 9000};
u32_t zcIndextoRateN20L[16] = {6500, 13000, 19500, 26000, 39000, 52000, 58500,
                              65000, 13000, 26000, 39000, 52000, 78000, 104000,
                              117000, 130000};
u32_t zcIndextoRateN20S[16] = {7200, 14400, 21700, 28900, 43300, 57800, 65000,
                              72200, 14400, 28900, 43300, 57800, 86700, 115600,
                              130000, 144400};
u32_t zcIndextoRateN40L[16] = {13500, 27000, 40500, 54000, 81000, 108000, 121500,
                              135000, 27000, 54000, 81000, 108000, 162000, 216000,
                              243000, 270000};
u32_t zcIndextoRateN40S[16] = {15000, 30000, 45000, 60000, 90000, 120000, 135000,
                              150000, 30000, 60000, 90000, 120000, 180000, 240000,
                              270000, 300000};

/************************************************************************/
/*                                                                      */
/*    FUNCTION DESCRIPTION                  zfTxGenWlanHeader           */
/*      Generate WLAN MAC header and LLC header.                        */
/*                                                                      */
/*    INPUTS                                                            */
/*      dev : device pointer                                            */
/*      buf : buffer pointer                                            */
/*      id : Index of TxD                                               */
/*      port : WLAN port                                                */
/*                                                                      */
/*    OUTPUTS                                                           */
/*      length of removed Ethernet header                               */
/*                                                                      */
/*    AUTHOR                                                            */
/*      Stephen             ZyDAS Technology Corporation    2005.5      */
/*                                                                      */
/************************************************************************/
u16_t zfTxGenWlanHeader(zdev_t* dev, zbuf_t* buf, u16_t* header, u16_t seq,
                        u8_t flag, u16_t plusLen, u16_t minusLen, u16_t port,
                        u16_t* da, u16_t* sa, u8_t up, u16_t *micLen,
                        u16_t* snap, u16_t snapLen, struct aggControl *aggControl)
{

    u16_t len;
    u16_t macCtrl;
    u32_t phyCtrl;
    u16_t hlen = 16;
    u16_t icvLen = 0;
    u16_t wdsPortId;
    u16_t vap = 0;
    u16_t mcs = 0;
    u16_t mt = 0;
    u8_t  qosType;
    u8_t  b1, b2;
    u16_t wdsPort;
    u8_t  encExemptionActionType;
    u16_t rateProbingFlag = 0;
    u8_t  tkipFrameOffset = 0;

#ifdef ZM_ENABLE_IBSS_WPA2PSK
    u8_t    res, peerIdx;
    u8_t    userIdx=0;
    u16_t   *iv16;
    u32_t   *iv32;
#endif

    zmw_get_wlan_dev(dev);

   /* Generate WLAN header */
    /* Frame control */
    header[4] = 0x0008 | (flag<<8);
    /* Duration */
    header[5] = 0x0000;

    if (wd->wlanMode == ZM_MODE_INFRASTRUCTURE)
    {
        /* ToDS bit */
        header[4] |= 0x0100;

        /*Sometimes we wake up to tx/rx but AP still think we are sleeping, so still need to set this bit*/
        if ( zfPowerSavingMgrIsSleeping(dev) || wd->sta.psMgr.tempWakeUp == 1 )
        {
            header[4] |= 0x1000;
        }

        /* Address 1 = BSSID */
        header[6] = wd->sta.bssid[0];
        header[7] = wd->sta.bssid[1];
        header[8] = wd->sta.bssid[2];
        /* Address 3 = DA */
        header[12] = da[0];
        header[13] = da[1];
        header[14] = da[2];
    }
    else if (wd->wlanMode == ZM_MODE_PSEUDO)
    {
        /* Address 1 = DA */
        header[6] = da[0];
        header[7] = da[1];
        header[8] = da[2];
        /* Address 3 = 00:00:00:00:00:00 */
        header[12] = 0;
        header[13] = 0;
        header[14] = 0;

        /* PSEUDO test : WDS */
        if (wd->enableWDS)
        {
            /* ToDS and FromDS bit */
            header[4] |= 0x0300;

            /* Address 4 = SA */
            header[16] = 0;
            header[17] = 0;
            header[18] = 0;

            hlen = 19;
        }
    }
    else if (wd->wlanMode == ZM_MODE_IBSS)
    {
        /* Address 1 = DA */
        header[6] = da[0];
        header[7] = da[1];
        header[8] = da[2];
        /* Address 3 = BSSID */
        header[12] = wd->sta.bssid[0];
        header[13] = wd->sta.bssid[1];
        header[14] = wd->sta.bssid[2];

#ifdef ZM_ENABLE_IBSS_WPA2PSK
        zmw_enter_critical_section(dev);
        res = zfStaFindOppositeByMACAddr(dev, da, &peerIdx);
        if(res == 0) // Find opposite in our OppositeInfo Structure !
        {
            userIdx = peerIdx;
        }
        zmw_leave_critical_section(dev);
#endif
    }
    else if (wd->wlanMode == ZM_MODE_AP)
    {
        if (port < 0x20)
        /* AP mode */
        {
            /* FromDS bit */
            header[4] |= 0x0200;

            /* Address 1 = DA */
            header[6] = da[0];
            header[7] = da[1];
            header[8] = da[2];
            /* Address 3 = SA */
            header[12] = sa[0];
            header[13] = sa[1];
            header[14] = sa[2];

            if (port < ZM_MAX_AP_SUPPORT)
            {
                vap = port;
                header[14] += (vap<<8);
            }
        }
        else
        /* WDS port */
        {
            /* ToDS and FromDS bit */
            header[4] |= 0x0300;

            wdsPortId = port - 0x20;

            /* Address 1 = RA */
            header[6] = wd->ap.wds.macAddr[wdsPortId][0];
            header[7] = wd->ap.wds.macAddr[wdsPortId][1];
            header[8] = wd->ap.wds.macAddr[wdsPortId][2];
            /* Address 3 = DA */
            header[12] = da[0];
            header[13] = da[1];
            header[14] = da[2];
            /* Address 4 = SA */
            header[16] = sa[0];
            header[17] = sa[1];
            header[18] = sa[2];

            hlen = 19;
        }
    } /* else if (wd->wlanMode == ZM_MODE_AP) */

    /* Address 2 = TA */
    header[9] = wd->macAddr[0];
    header[10] = wd->macAddr[1];
#ifdef ZM_VAPMODE_MULTILE_SSID
    header[11] = wd->macAddr[2]; //Multiple SSID
#else
    header[11] = wd->macAddr[2] + (vap<<8); //VAP
#endif

    if ( (wd->wlanMode == ZM_MODE_IBSS) && (wd->XLinkMode) )
    {
        header[9]  = sa[0];
        header[10] = sa[1];
        header[11] = sa[2];
    }

    /* Sequence Control */
    header[15] = seq;


    if (wd->wlanMode == ZM_MODE_AP)
    {
        zfApGetStaTxRateAndQosType(dev, da, &phyCtrl, &qosType, &rateProbingFlag);
        mt = (u16_t)(phyCtrl & 0x3);
        mcs = (u16_t)((phyCtrl >> 16) & 0x3f);
#if 1
        //zfApGetStaQosType(dev, da, &qosType);

        /* if DA == WME STA */
        if (qosType == 1)
        {
            /* QoS data */
            header[4] |= 0x0080;

            /* QoS Control */
            header[hlen] = up;
            hlen += 1;
        }
#endif
    }

#if 0
    //AGG Test Code
    if (header[6] == 0x8000)
    {
        /* QoS data */
        header[4] |= 0x0080;

        /* QoS Control */
        header[hlen] = 0;
        hlen += 1;
    }
#endif

    if (wd->wlanMode == ZM_MODE_AP) {
        /* Todo: rate control here for qos field */
    }
    else {
        /* Rate control */
        zfStaGetTxRate(dev, da, &phyCtrl, &rateProbingFlag);
        mt = (u16_t)(phyCtrl & 0x3);
        mcs = (u16_t)((phyCtrl >> 16) & 0x3f);
    }

    if (wd->txMCS != 0xff)
    {
        /* fixed rate */
	    phyCtrl = ((u32_t)wd->txMCS<<16) + wd->txMT;
        mcs = wd->txMCS;
        mt = wd->txMT;
    }

    if (wd->enableAggregation)
    {
        /* force enable aggregation */
        if (wd->enableAggregation==2 && !(header[6]&0x1))
        {
            /* QoS data */
            header[4] |= 0x0080;

            /* QoS Control */
            header[hlen] = 0;
            hlen += 1;
        }
        /* if wd->enableAggregation=1 => force disable */
        /* if wd->enableAggregation=0 => auto */
    }

#ifdef ZM_ENABLE_AGGREGATION
    /*
     * aggregation control
     */

    /*
     * QoS data
     */
    if (wd->wlanMode == ZM_MODE_AP) {
        if (aggControl && mt == 2) {
            if (wd->enableAggregation==0 && !(header[6]&0x1))
            {
                header[4] |= 0x0080;

                /*
                 * QoS Control
                 */
                header[hlen] = 0;
                hlen += 1;
            }
        }
    }
#endif

    // MSDU Length
    len = zfwBufGetSize(dev, buf);

    /* Generate control setting */
    /* Backoff, Non-Burst and hardware duration */
    macCtrl = 0x208;

    /* ACK */
    if ((header[6] & 0x1) == 0x1)
    {
        /* multicast frame : Set NO-ACK bit */
        macCtrl |= 0x4;
    }
    else
    {
        /* unicast frame */
    #if 0
        // Enable RTS according to MPDU Lengths ( not MSDU Lengths )
        if (len >= wd->rtsThreshold)
        {
            /* Enable RTS */
            macCtrl |= 1;
        }
    #endif
    }
    /* VAP test code */
    //macCtrl |= 0x4;

    if (wd->wlanMode == ZM_MODE_AP)
    {
        u8_t encryType;
        u16_t iv16;
        u32_t iv32;

        /* Check whether this is a multicast frame */
        if ((header[6] & 0x1) == 0x1)
        {
            /* multicast frame */
            if (wd->ap.encryMode[vap] == ZM_TKIP)
            {
                wd->ap.iv16[vap]++;

                if(wd->ap.iv16[vap] == 0)
                {
                    wd->ap.iv32[vap]++;
                }

                b1 = (u8_t) (wd->ap.iv16[vap] >> 8);
                b2 = (b1 | 0x20) & 0x7f;
                header[hlen] = ((u16_t)b2 << 8) + b1;
                b1 = (u8_t) wd->ap.iv16[vap];
                b2 = 0x20 | (wd->ap.bcKeyIndex[vap] << 6);
                header[hlen+1] = ((u16_t)b2 << 8) + b1;
                header[hlen+2] = (u16_t) wd->ap.iv32[vap];
                header[hlen+3] = (u16_t) (wd->ap.iv32[vap] >> 16);

                //macCtrl |= 0x80;
                macCtrl |= 0x40;
                icvLen = 4;

                /* set hardware MIC */
                if ( (!(seq & 0xf))&&(!(flag & 0x4)) )
                {
                    macCtrl |= 0x100;
                    plusLen += 8;
                    *micLen = 8;
                }

                header[4] |= 0x4000;
                hlen += 4;
            }
            else if (wd->ap.encryMode[vap] == ZM_AES)
            {
                wd->ap.iv16[vap]++;

                if(wd->ap.iv16[vap] == 0)
                {
                    wd->ap.iv32[vap]++;
                }

                b1 = (u8_t) wd->ap.iv16[vap];
                b2 = (u8_t) (wd->ap.iv16[vap] >> 8);
                header[hlen] = ((u16_t)b2 << 8) + b1;
                header[hlen+1] = 0x2000 | (wd->ap.bcKeyIndex[vap] << 14);
                header[hlen+2] = (u16_t) (wd->ap.iv32[vap]);
                header[hlen+3] = (u16_t) (wd->ap.iv32[vap] >> 16);

                macCtrl |= 0xc0;
                icvLen = 8;  /* MIC */

                header[4] |= 0x4000;
                hlen += 4;
            }
            #ifdef ZM_ENABLE_CENC
            else if (wd->ap.encryMode[vap] == ZM_CENC)
            {
                //u32_t txiv[4];

                wd->ap.txiv[vap][0]++;

                if (wd->ap.txiv[vap][0] == 0)
                {
                    wd->ap.txiv[vap][1]++;
                }

                if (wd->ap.txiv[vap][1] == 0)
                {
                    wd->ap.txiv[vap][2]++;
                }

                if (wd->ap.txiv[vap][2] == 0)
                {
                    wd->ap.txiv[vap][3]++;
                }

                if (wd->ap.txiv[vap][3] == 0)
                {
                    wd->ap.txiv[vap][0] = 0;
                    wd->ap.txiv[vap][1] = 0;
                    wd->ap.txiv[vap][2] = 0;
                }

                header[hlen] = (wd->ap.bcKeyIndex[vap] & 0x0001);    /* For Key Id and reserved field */
                header[hlen+1] = (u16_t)wd->ap.txiv[vap][0];
                header[hlen+2] = (u16_t)(wd->ap.txiv[vap][0] >> 16);
                header[hlen+3] = (u16_t)wd->ap.txiv[vap][1];
                header[hlen+4] = (u16_t)(wd->ap.txiv[vap][1] >> 16);
                header[hlen+5] = (u16_t)wd->ap.txiv[vap][2];
                header[hlen+6] = (u16_t)(wd->ap.txiv[vap][2] >> 16);
                header[hlen+7] = (u16_t)wd->ap.txiv[vap][3];
                header[hlen+8] = (u16_t)(wd->ap.txiv[vap][3] >> 16);

                macCtrl |= 0x80;
                icvLen = 16;  /* MIC */

                header[4] |= 0x4000;
                hlen += 9;
            }
            #endif //ZM_ENABLE_CENC
        }
        else
        {
            /* Get STA's encryption type */
            zfApGetStaEncryType(dev, da, &encryType);

            if (encryType == ZM_TKIP)
            {
                /* Get iv16 and iv32 */
                zfApGetStaWpaIv(dev, da, &iv16, &iv32);

                iv16++;
                if (iv16 == 0)
                {
                    iv32++;
                }

                b1 = (u8_t) (iv16 >> 8);
                b2 = (b1 | 0x20) & 0x7f;
                header[hlen] = ((u16_t)b2 << 8) + b1;
                b1 = (u8_t) iv16;
                b2 = 0x20;
                header[hlen+1] = ((u16_t)b2 << 8) + b1;
                header[hlen+2] = (u16_t) iv32;
                header[hlen+3] = (u16_t) (iv32 >> 16);

                //macCtrl |= 0x80;
                macCtrl |= 0x40;
                icvLen = 4;

                /* set hardware MIC */
                if ( (!(seq & 0xf))&&(!(flag & 0x4)) )
                {
                    macCtrl |= 0x100;
                    plusLen += 8;
                    *micLen = 8;
                }

                header[4] |= 0x4000;
                hlen += 4;

                /* Set iv16 and iv32 */
                zfApSetStaWpaIv(dev, da, iv16, iv32);
            }
            else if (encryType == ZM_AES)
            {
                /* Get iv16 and iv32 */
                zfApGetStaWpaIv(dev, da, &iv16, &iv32);

                iv16++;
                if (iv16 == 0)
                {
                    iv32++;
                }

                b1 = (u8_t) iv16;
                b2 = (u8_t) (iv16 >> 8);
                header[hlen] = ((u16_t)b2 << 8) + b1;
                header[hlen+1] = 0x2000;
                header[hlen+2] = (u16_t) (iv32);
                header[hlen+3] = (u16_t) (iv32 >> 16);

                macCtrl |= 0xc0;
                icvLen = 8;  /* MIC */

                header[4] |= 0x4000;
                hlen += 4;

                /* Set iv16 and iv32 */
                zfApSetStaWpaIv(dev, da, iv16, iv32);
            }
            #ifdef ZM_ENABLE_CENC
            else if (encryType == ZM_CENC)
            {
                u32_t txiv[4];
                u8_t keyIdx;

                /* Get CENC TxIV */
                zfApGetStaCencIvAndKeyIdx(dev, da, txiv, &keyIdx);

                txiv[0] += 2;

                if (txiv[0] == 0 || txiv[0] == 1)
                {
                    txiv[1]++;
                }

                if (txiv[1] == 0)
                {
                    txiv[2]++;
                }

                if (txiv[2] == 0)
                {
                    txiv[3]++;
                }

                if (txiv[3] == 0)
                {
                    txiv[0] = 0;
                    txiv[1] = 0;
                    txiv[2] = 0;
                }

                header[hlen] = (keyIdx & 0x0001);    /* For Key Id and reserved field */
                header[hlen+1] = (u16_t)txiv[0];
                header[hlen+2] = (u16_t)(txiv[0] >> 16);
                header[hlen+3] = (u16_t)txiv[1];
                header[hlen+4] = (u16_t)(txiv[1] >> 16);
                header[hlen+5] = (u16_t)txiv[2];
                header[hlen+6] = (u16_t)(txiv[2] >> 16);
                header[hlen+7] = (u16_t)txiv[3];
                header[hlen+8] = (u16_t)(txiv[3] >> 16);

                macCtrl |= 0x80;
                icvLen = 16;  /* MIC */

                header[4] |= 0x4000;
                hlen += 9;

                /* Set CENC IV */
                zfApSetStaCencIv(dev, da, txiv);
            }
            #endif //ZM_ENABLE_CENC
        }

        /* protection mode */
        if (wd->ap.protectionMode == 1)
        {
            /* Enable Self-CTS */
            macCtrl &= 0xFFFC;
            macCtrl |= 2;
        }

        /* Rate Control */
        if (port < 0x20)
        {
            /* AP */
            /* IV */
            if ((wd->ap.encryMode[vap] == ZM_WEP64) ||
                    (wd->ap.encryMode[vap] == ZM_WEP128) ||
                    (wd->ap.encryMode[vap] == ZM_WEP256))
            {
                header[4] |= 0x4000;
                header[hlen] = 0x0;   //IV
                header[hlen+1] = wd->ap.bcKeyIndex[vap] << 14; //IV with Keyid--CWYang(m)
                hlen += 2;
                icvLen = 4;
                macCtrl |= 0x40;
            }
        }
        else
        {
            /* WDS */

            /* TODO : Fixed rate to 54M */
            phyCtrl = 0xc0001;   //PHY control L

            /* WDS port checking */
            wdsPort = port - 0x20;
            if (wdsPort >= ZM_MAX_WDS_SUPPORT)
            {
                wdsPort = 0;
            }

            #if 1
            /* IV */
            switch (wd->ap.wds.encryMode[wdsPort])
            {
            case ZM_WEP64:
            case ZM_WEP128:
            case ZM_WEP256:
                    header[4] |= 0x4000;
                    header[hlen] = 0x0;   //IV
                    header[hlen+1] = wd->ap.bcKeyIndex[vap] << 14; //IV with Keyid
                    hlen += 2;
                    icvLen = 4;
                    macCtrl |= 0x40;
                    break;

            case ZM_TKIP:
                    wd->sta.iv16++;

                    if ( wd->sta.iv16 == 0 )
                    {
                        wd->sta.iv32++;
                    }

                    b1 = (u8_t) (wd->sta.iv16 >> 8);
                    b2 = (b1 | 0x20) & 0x7f;
                    header[hlen] = ((u16_t)b2 << 8) + b1;
                    b1 = (u8_t) wd->sta.iv16;
                    b2 = 0x20;
                    header[hlen+1] = ((u16_t)b2 << 8) + b1;
                    header[hlen+2] = (u16_t) wd->sta.iv32;
                    header[hlen+3] = (u16_t) (wd->sta.iv32 >> 16);

                    //macCtrl |= 0x80;
                    macCtrl |= 0x40;
                    icvLen = 4;

                    /* set hardware MIC */
                    if ( (!(seq & 0xf))&&(!(flag & 0x4)) )
                    {
                        macCtrl |= 0x100;
                        plusLen += 8;
                        *micLen = 8;
                    }

                    header[4] |= 0x4000;
                    hlen += 4;
                    break;

            case ZM_AES:
                    wd->sta.iv16++;
                    if ( wd->sta.iv16 == 0 )
                    {
                        wd->sta.iv32++;
                    }

                    b1 = (u8_t) wd->sta.iv16;
                    b2 = (u8_t) (wd->sta.iv16 >> 8);
                    header[hlen] = ((u16_t)b2 << 8) + b1;
                    header[hlen+1] = 0x2000;
                    header[hlen+2] = (u16_t) (wd->sta.iv32);
                    header[hlen+3] = (u16_t) (wd->sta.iv32 >> 16);

                    macCtrl |= 0xc0; /* Set to AES in control setting */
                    icvLen = 8;  /* MIC */

                    header[4] |= 0x4000; /* Set WEP bit in wlan header */
                    hlen += 4; /* plus IV length */
                    break;
            }/* end of switch */
            #endif
        }
    }
    else   /* wd->wlanMode != ZM_MODE_AP */
    {
        encExemptionActionType = zfwGetPktEncExemptionActionType(dev, buf);

        if ( wd->wlanMode == ZM_MODE_INFRASTRUCTURE )
        {
            #if 1
            /* if WME AP */
            if (wd->sta.wmeConnected != 0)
            {
                /* QoS data */
                header[4] |= 0x0080;

                /* QoS Control */
                header[hlen] = up;
                hlen += 1;
            }
            #endif

            if ( encExemptionActionType == ZM_ENCRYPTION_EXEMPT_NO_EXEMPTION )
            {
                if ( wd->sta.authMode < ZM_AUTH_MODE_WPA )
                {   /* non-WPA */
                    if ( wd->sta.wepStatus == ZM_ENCRYPTION_WEP_ENABLED )
                    {
                        if ( (wd->sta.encryMode == ZM_WEP64)||
                             (wd->sta.encryMode == ZM_WEP128)||
                             (wd->sta.encryMode == ZM_WEP256) )
                        {
                            header[4] |= 0x4000;
                            header[hlen] = 0x0;   //IV
                            header[hlen+1] = 0x0; //IV
                            header[hlen+1] |= (((u16_t) wd->sta.keyId) << 14);
                            hlen += 2;
                            icvLen = 4;

                            /* For Software WEP */
                            if ((wd->sta.SWEncryptEnable & ZM_SW_WEP_ENCRY_EN) != 0)
                            {
                                u8_t keyLen = 5;
                                u8_t iv[3];

                                iv[0] = 0x0;
                                iv[1] = 0x0;
                                iv[2] = 0x0;

                                if (wd->sta.SWEncryMode[wd->sta.keyId] == ZM_WEP64)
                                {
                                    keyLen = 5;
                                }
                                else if (wd->sta.SWEncryMode[wd->sta.keyId] == ZM_WEP128)
                                {
                                    keyLen = 13;
                                }
                                else if (wd->sta.SWEncryMode[wd->sta.keyId] == ZM_WEP256)
                                {
                                    keyLen = 29;
                                }

                                zfWEPEncrypt(dev, buf, (u8_t*) snap, snapLen, minusLen, keyLen,
                                        wd->sta.wepKey[wd->sta.keyId], iv);
                            }
                            else
                            {
                                macCtrl |= 0x40;
                            }
                        }
                    }
                }
                else
                {   /* WPA */
                    if ( wd->sta.wpaState >= ZM_STA_WPA_STATE_PK_OK )
                    {
                        wd->sta.iv16++;
                        if ( wd->sta.iv16 == 0 )
                        {
                            wd->sta.iv32++;
                        }

                        /* set encryption mode */
                        if ( wd->sta.encryMode == ZM_TKIP )
                        {
                            b1 = (u8_t) (wd->sta.iv16 >> 8);
                            b2 = (b1 | 0x20) & 0x7f;
                            header[hlen] = ((u16_t)b2 << 8) + b1;
                            b1 = (u8_t) wd->sta.iv16;
                            b2 = 0x20;

                            // header[hlen+1] = (((u16_t) wd->sta.keyId) << 14) | (((u16_t)b2 << 8) + b1);
                            // STA in infrastructure mode should use keyId = 0 to transmit unicast !
                            header[hlen+1] = (((u16_t)b2 << 8) + b1);
                            header[hlen+2] = (u16_t) wd->sta.iv32;
                            header[hlen+3] = (u16_t) (wd->sta.iv32 >> 16);

                            /* If software encryption enable */
                            if ((wd->sta.SWEncryptEnable & ZM_SW_TKIP_ENCRY_EN) == 0)
                            {
                                //macCtrl |= 0x80;
                                /* TKIP same to WEP */
                                macCtrl |= 0x40;
                                icvLen = 4;

                                /* set hardware MIC */
                                if ( (!(seq & 0xf))&&(!(flag & 0x4)) )
                                {
                                    macCtrl |= 0x100;
                                    plusLen += 8;
                                    *micLen = 8;
                                }
                            }
                            else
                            {
                                u8_t mic[8];
                                u16_t offset;
                                u32_t icv;
                                u8_t RC4Key[16];

                                /* TODO: Remove the criticial section here. */
                                zmw_declare_for_critical_section();

                                zmw_enter_critical_section(dev);
                                /* Calculate MIC */
                                zfCalTxMic(dev, buf, (u8_t *)snap, snapLen, minusLen, da, sa, up, mic);

                                offset = zfwBufGetSize(dev, buf);

                                /* Append MIC to the buffer */
                                zfCopyToIntTxBuffer(dev, buf, mic, offset, 8);
                                zfwBufSetSize(dev, buf, offset+8);
                                zmw_leave_critical_section(dev);

                                /* TKIP Key Mixing */
                                zfTkipPhase1KeyMix(wd->sta.iv32, &wd->sta.txSeed);
                                zfTkipPhase2KeyMix(wd->sta.iv16, &wd->sta.txSeed);
                                zfTkipGetseeds(wd->sta.iv16, RC4Key, &wd->sta.txSeed);

                                /* Encrypt Data */
                                zfTKIPEncrypt(dev, buf, (u8_t *)snap, snapLen, minusLen, 16, RC4Key, &icv);

                                icvLen = 4;
                                len += 8;
                            }

                            header[4] |= 0x4000;
                            hlen += 4;
                        }
                        else if ( wd->sta.encryMode == ZM_AES )
                        {
                            b1 = (u8_t) wd->sta.iv16;
                            b2 = (u8_t) (wd->sta.iv16 >> 8);
                            header[hlen] = ((u16_t)b2 << 8) + b1;
                            // header[hlen+1] = (((u16_t) wd->sta.keyId) << 14) | (0x2000);
                            // STA in infrastructure mode should use keyId = 0 to transmit unicast !
                            header[hlen+1] = 0x2000;
                            header[hlen+2] = (u16_t) (wd->sta.iv32);
                            header[hlen+3] = (u16_t) (wd->sta.iv32 >> 16);

                            macCtrl |= 0xc0;
                            icvLen = 8;  /* MIC */

                            header[4] |= 0x4000;
                            hlen += 4;
                        }
                        #ifdef ZM_ENABLE_CENC
                        else if ( wd->sta.encryMode == ZM_CENC )
                        {
                            /* Accumlate the PN sequence */
                            wd->sta.txiv[0] += 2;

                            if (wd->sta.txiv[0] == 0 || wd->sta.txiv[0] == 1)
                            {
                                wd->sta.txiv[1]++;
                            }

                            if (wd->sta.txiv[1] == 0)
                            {
                                wd->sta.txiv[2]++;
                            }

                            if (wd->sta.txiv[2] == 0)
                            {
                                wd->sta.txiv[3]++;
                            }

                            if (wd->sta.txiv[3] == 0)
                            {
                                wd->sta.txiv[0] = 0;
                                wd->sta.txiv[1] = 0;
                                wd->sta.txiv[2] = 0;
                            }

                            header[hlen] = (wd->sta.cencKeyId & 0x0001);    /* For Key Id and reserved field */
                            header[hlen+1] = (u16_t) wd->sta.txiv[0];
                            header[hlen+2] = (u16_t) (wd->sta.txiv[0] >> 16);
                            header[hlen+3] = (u16_t) wd->sta.txiv[1];
                            header[hlen+4] = (u16_t) (wd->sta.txiv[1] >> 16);
                            header[hlen+5] = (u16_t) wd->sta.txiv[2];
                            header[hlen+6] = (u16_t) (wd->sta.txiv[2] >> 16);
                            header[hlen+7] = (u16_t) wd->sta.txiv[3];
                            header[hlen+8] = (u16_t) (wd->sta.txiv[3] >> 16);

                            macCtrl |= 0x80;
                            icvLen = 16;  /* MIC */

                            header[4] |= 0x4000;
                            hlen += 9;
                        }
                        #endif //ZM_ENABLE_CENC
                    }
                }
            } // if ( encExemptionActionType == ZM_ENCRYPTION_EXEMPT_NO_EXEMPTION )
        } /* if ( wd->wlanMode != ZM_MODE_INFRASTRUCTURE ) */

        if ( wd->wlanMode == ZM_MODE_IBSS )
        {
            if ( encExemptionActionType == ZM_ENCRYPTION_EXEMPT_NO_EXEMPTION )
            {
#ifdef ZM_ENABLE_IBSS_WPA2PSK
                if( wd->sta.oppositeInfo[userIdx].wpaState >= ZM_STA_WPA_STATE_PK_OK || wd->sta.wpaState >= ZM_STA_WPA_STATE_PK_OK)
                {
                    int isUnicast = 1 ;

                    if((da[0]& 0x1))
                    {
                        isUnicast = 0 ; // Not unicast , is broadcast
                    }

                    if( wd->sta.ibssWpa2Psk == 1 )
                    { /* The IV order is not the same between unicast and broadcast ! */
                        if ( isUnicast )
                        {
                            iv16 = &wd->sta.oppositeInfo[userIdx].iv16;
                            iv32 = &wd->sta.oppositeInfo[userIdx].iv32;
                        }
                        else
                        {
                            iv16 = &wd->sta.iv16;
                            iv32 = &wd->sta.iv32;
                        }
                    }
                    else
                    {
                        iv16 = &wd->sta.iv16;
                        iv32 = &wd->sta.iv32;
                    }

                    (*iv16)++;
                    if ( *iv16 == 0 )
                    {
                        *iv32++;
                    }

                    if ( wd->sta.oppositeInfo[userIdx].encryMode == ZM_AES || wd->sta.encryMode == ZM_AES)
                    {
                        //printk("Station encryption mode is AES-CCMP\n") ;
                        b1 = (u8_t) (*iv16);
                        b2 = (u8_t) ((*iv16) >> 8);
                        header[hlen] = ((u16_t)b2 << 8) + b1;

                        if ( isUnicast )
                        {
                            header[hlen+1] = 0x2000;
                        }
                        else
                        {
                            header[hlen+1] = 0x2000 | (((u16_t) wd->sta.keyId) << 14);
                        }

                        header[hlen+2] = (u16_t) (*iv32);
                        header[hlen+3] = (u16_t) ((*iv32) >> 16);
                        macCtrl |= 0xc0;
                        icvLen = 8;  /* MIC */
                    }

                    header[4] |= 0x4000;
                    hlen += 4;
                }
                else if ( wd->sta.wepStatus == ZM_ENCRYPTION_WEP_ENABLED)
                {
                    if ( (wd->sta.encryMode == ZM_WEP64)||
                        (wd->sta.encryMode == ZM_WEP128)||
                        (wd->sta.encryMode == ZM_WEP256) )
                    {
                        header[4] |= 0x4000;
                        header[hlen] = 0x0;   //IV
                        header[hlen+1] = 0x0; //IV
                        header[hlen+1] |= (((u16_t) wd->sta.keyId) << 14);
                        hlen += 2;
                        icvLen = 4;
                        macCtrl |= 0x40;
                    }
                }
#else
                /* ----- 20070405 add by Mxzeng ----- */
                if( wd->sta.wpaState >= ZM_STA_WPA_STATE_PK_OK )
                {
                    int isUnicast = 1 ;

                    if((da[0]& 0x1))
                    {
                        isUnicast = 0 ; // Not unicast , is broadcast
                    }

                    wd->sta.iv16++;
                    if ( wd->sta.iv16 == 0 )
                    {
                        wd->sta.iv32++;
                    }

                    if ( wd->sta.encryMode == ZM_AES )
                    {
                        //printk("Station encryption mode is AES-CCMP\n") ;
                        b1 = (u8_t) wd->sta.iv16;
                        b2 = (u8_t) (wd->sta.iv16 >> 8);
                        header[hlen] = ((u16_t)b2 << 8) + b1;

                        if ( isUnicast )
                        {
                            header[hlen+1] = 0x2000;
                        }
                        else
                        {
                            header[hlen+1] = 0x2000 | (((u16_t) wd->sta.keyId) << 14);
                        }

                            header[hlen+2] = (u16_t) (wd->sta.iv32);
                            header[hlen+3] = (u16_t) (wd->sta.iv32 >> 16);
                            macCtrl |= 0xc0;
                            icvLen = 8;  /* MIC */
                    }

                    header[4] |= 0x4000;
                    hlen += 4;
                }
                else if ( wd->sta.wepStatus == ZM_ENCRYPTION_WEP_ENABLED)
                {
                    if ( (wd->sta.encryMode == ZM_WEP64)||
                         (wd->sta.encryMode == ZM_WEP128)||
                         (wd->sta.encryMode == ZM_WEP256) )
                    {
                        header[4] |= 0x4000;
                        header[hlen] = 0x0;   //IV
                        header[hlen+1] = 0x0; //IV
                        header[hlen+1] |= (((u16_t) wd->sta.keyId) << 14);
                        hlen += 2;
                        icvLen = 4;
                        macCtrl |= 0x40;
                    }
                }
#endif
            }   // End if ( encExemptionActionType == ZM_ENCRYPTION_EXEMPT_NO_EXEMPTION )
        }   // End if ( wd->wlanMode == ZM_MODE_IBSS )
        else if ( wd->wlanMode == ZM_MODE_PSEUDO )
        {
            switch (wd->sta.encryMode)
	        {
            case ZM_WEP64:
            case ZM_WEP128:
            case ZM_WEP256:
                header[4] |= 0x4000;
                header[hlen] = 0x0;   //IV
                header[hlen+1] = 0x0; //IV
                hlen += 2;
                icvLen = 4;
                macCtrl |= 0x40;
                break;

            case ZM_TKIP:
            {
                wd->sta.iv16++;
                if ( wd->sta.iv16 == 0 )
                {
                    wd->sta.iv32++;
                }

                b1 = (u8_t) (wd->sta.iv16 >> 8);
                b2 = (b1 | 0x20) & 0x7f;
                header[hlen] = ((u16_t)b2 << 8) + b1;
                b1 = (u8_t) wd->sta.iv16;
                b2 = 0x20;
                header[hlen+1] = ((u16_t)b2 << 8) + b1;
                header[hlen+2] = (u16_t) wd->sta.iv32;
                header[hlen+3] = (u16_t) (wd->sta.iv32 >> 16);

                //macCtrl |= 0x80;
                macCtrl |= 0x40;
                icvLen = 4;

                /* set hardware MIC */
                if ( (!(seq & 0xf))&&(!(flag & 0x4)) )
                {
                    macCtrl |= 0x100;
                    plusLen += 8;
                    *micLen = 8;
                }

                header[4] |= 0x4000;
                hlen += 4;
            }/* end of PSEUDO TKIP */
                break;

            case ZM_AES:
            {
                wd->sta.iv16++;
                if ( wd->sta.iv16 == 0 )
                {
                    wd->sta.iv32++;
                }

                b1 = (u8_t) wd->sta.iv16;
                b2 = (u8_t) (wd->sta.iv16 >> 8);
                header[hlen] = ((u16_t)b2 << 8) + b1;
                header[hlen+1] = 0x2000;
                header[hlen+2] = (u16_t) (wd->sta.iv32);
                header[hlen+3] = (u16_t) (wd->sta.iv32 >> 16);
                macCtrl |= 0xc0;
                icvLen = 8;  /* MIC */
                header[4] |= 0x4000;
                hlen += 4;
            }/* end of PSEUDO AES */
                    break;

              #ifdef ZM_ENABLE_CENC
              case ZM_CENC:
                    /* Accumlate the PN sequence */
                    wd->sta.txiv[0] += 2;

                    if (wd->sta.txiv[0] == 0 || wd->sta.txiv[0] == 1)
                    {
                        wd->sta.txiv[1]++;
                    }

                    if (wd->sta.txiv[1] == 0)
                    {
                        wd->sta.txiv[2]++;
                    }

                    if (wd->sta.txiv[2] == 0)
                    {
                        wd->sta.txiv[3]++;
                    }

                    if (wd->sta.txiv[3] == 0)
                    {
                        wd->sta.txiv[0] = 0;
                        wd->sta.txiv[1] = 0;
                        wd->sta.txiv[2] = 0;
                    }

                    header[hlen] = 0;
                    header[hlen+1] = (u16_t) wd->sta.txiv[0];
                    header[hlen+2] = (u16_t) (wd->sta.txiv[0] >> 16);
                    header[hlen+3] = (u16_t) wd->sta.txiv[1];
                    header[hlen+4] = (u16_t) (wd->sta.txiv[1] >> 16);
                    header[hlen+5] = (u16_t) wd->sta.txiv[2];
                    header[hlen+6] = (u16_t) (wd->sta.txiv[2] >> 16);
                    header[hlen+7] = (u16_t) wd->sta.txiv[3];
                    header[hlen+8] = (u16_t) (wd->sta.txiv[3] >> 16);

                    macCtrl |= 0x80;
                    icvLen = 16;  /* MIC */

                    header[4] |= 0x4000;
                    hlen += 9;
				break;
		    #endif //ZM_ENABLE_CENC
			}/* end of switch */
		}

        /* Generate control setting */

        /* protection mode */
        if (wd->enableProtectionMode)
        {
            if (wd->enableProtectionMode==2)
            {
                /* Force enable protection: self cts  */
                macCtrl &= 0xFFFC;
                macCtrl |= 2;
            }
            /* if wd->enableProtectionMode=1 => force disable */
            /* if wd->enableProtectionMode=0 => auto */
        }
        else
        {

            /* protection mode */
            if (wd->sta.bProtectionMode == TRUE)
            {
                /* Enable Self-CTS */
                macCtrl &= 0xFFFC;
                macCtrl |= 2;
            }
        }

    }

    if (wd->txMCS != 0xff)
    {
        /* fixed rate */
	    phyCtrl = ((u32_t)wd->txMCS<<16) + wd->txMT;
        mcs = wd->txMCS;
        mt = wd->txMT;
    }

    if (mt == 2)
    {
#if 0
        /* HT PT: 0 Mixed mode    1 Green field */
	    if (wd->sta.preambleTypeHT == ZM_PREAMBLE_TYPE_GREEN_FIELD)
	    {
            phyCtrl |= 0x4;     /* Bit 2 */
        }
#endif
        /* Bandwidth */
        if (wd->sta.htCtrlBandwidth == ZM_BANDWIDTH_40MHZ)
        {
            phyCtrl |= (0x80<<16);  /* BIT 23 */
        }
#if 0
        /* STBC */
        if (wd->sta.htCtrlSTBC<=0x3)
        {
            phyCtrl |= (wd->sta.htCtrlSTBC<<28);   /* BIT 23 */
        }
#endif
        /* Short GI */
        if(wd->sta.htCtrlSG)
        {
            phyCtrl |= (0x8000<<16);         /* BIT 31 */
        }

        /* TA */
        if ( ((mcs >=0x8) && (mcs<=0xf))  || (wd->sta.htCtrlSTBC) )
        {
       	    phyCtrl |= 0x1800;               /* BIT 11 12 */
    	}
    }
    else if(mt == 1)
    {
        #if 0
        //bug that cause OFDM rate become duplicate legacy rate
        /* Bandwidth */
        if (wd->sta.htCtrlBandwidth == ZM_BANDWIDTH_40MHZ)
        {
            phyCtrl |= (0x80<<16);  /* BIT 23 */
            mt = 3;                 /* duplicate legacy */
            phyCtrl |= mt;
        }
        #endif
    }
    else if(mt == 0)
    {
	/* CCK PT: Legcy Preamble: 1 long preamble    2 short preamble */
        if (wd->preambleTypeInUsed == ZM_PREAMBLE_TYPE_SHORT)
        {
    	       //phyCtrl |= 0x4;    /* BIT 2 */
    	}
    }

    /* TA */
    if (wd->sta.defaultTA)
    {
        phyCtrl |= 0x1000;
    }
    else
    {
        phyCtrl |= 0x0800;
    }

    //Get CurrentTxRate -- CWYang(+)
    if ((mt == 0) || (mt == 1)) //B,G Rate
    {
        if (mcs < 16)
        {
            wd->CurrentTxRateKbps = zcIndextoRateBG[mcs];
        }
    }
    else if (mt == 2)
    {
        if (mcs < 16)
        {
            if (wd->sta.htCtrlBandwidth == ZM_BANDWIDTH_40MHZ)
            {
                if((phyCtrl & 0x80000000) != 0)
                {
                    /* Short GI 40 MHz MIMO Rate */
                    wd->CurrentTxRateKbps = zcIndextoRateN40S[mcs];
                }
                else
                {
                    /* Long GI 40 MHz MIMO Rate */
                    wd->CurrentTxRateKbps = zcIndextoRateN40L[mcs];
                }
            }
            else
            {
                if((phyCtrl & 0x80000000) != 0)
                {
                    /* Short GI 20 MHz MIMO Rate */
                    wd->CurrentTxRateKbps = zcIndextoRateN20S[mcs];
                }
                else
                {
                    /* Long GI 20 MHz MIMO Rate */
                    wd->CurrentTxRateKbps = zcIndextoRateN20L[mcs];
                }
            }
        }
    }

    //802.11 header(include IV) = (hlen<<1)-8
    //ethernet frame = len
    //snap + mic = plusLen
    //ethernet header = minusLen
    //icv = icvLen
    //crc32 = 4
    //length=802.11 header+snap+(ethernet frame-ethernet header)+mic+icv+crc32
    header[0] = ((hlen<<1)-8)+plusLen+(len-minusLen)+icvLen+4;  //Length

    // header[0] : MPDU Lengths
    if ((header[6] & 0x1) != 0x1) // Unicast Frame
    {
        if (header[0] >= wd->rtsThreshold)
        {
            /* Enable RTS */
            macCtrl |= 1;
        }
    }

    if ( wd->sta.encryMode == ZM_TKIP )
        tkipFrameOffset = 8;

    if( wd->sta.EnableHT != 1 )
    { // Aggregation should not be fragmented !
        if ( header[0] > ( wd->fragThreshold + tkipFrameOffset ) )
        {
            return 0; // Need to be fragmented ! !
        }
    }

    //if ( wd->sta.encryMode == ZM_TKIP )
    //{
    //    zm_debug_msg1("ctrl length = ", header[0]);
    //}

    //MAC control
    if (rateProbingFlag != 0)
    {
        macCtrl |= 0x8000;
    }
    header[1] = macCtrl;
    //PHY control L
    header[2] = (u16_t) ((phyCtrl&0xffff) | 0x700 | (zcUpToAc[up&0x7]<<13));
    //PHY control H
    header[3] = (u16_t) ((phyCtrl>>16) | 0x700);

    if (wd->enableAggregation)
    {
        /* force enable aggregation */
        if (wd->enableAggregation==2 && !(header[6]&0x1))
        {
            if (((header[2] & 0x3) == 2))
            {
                /* Enable aggregation */
                header[1] |= 0x20;
            }
        }
        /* if wd->enableAggregation=1 => force disable */
        /* if wd->enableAggregation=0 => auto */
    }

#ifdef ZM_ENABLE_AGGREGATION
    if (wd->addbaComplete) {
        #ifdef ZM_BYPASS_AGGR_SCHEDULING
        if (!(header[6]&0x1) && !rateProbingFlag && (wd->enableAggregation != 1))
        {
            if (((header[2] & 0x3) == 2))
            {
                /* Unicast frame with HT rate => Enable aggregation */
                /* We only support software encryption in single packet mode */
                if ((wd->sta.SWEncryptEnable & ZM_SW_TKIP_ENCRY_EN) == 0 &&
                    (wd->sta.SWEncryptEnable & ZM_SW_WEP_ENCRY_EN) == 0)
                {
                    /* Set aggregation group bits per AC */
                    header[1] |= (0x20 | (zcUpToAc[up&0x7]<<10));

                    //if (wd->sta.currentFrequency < 3000)
                    {
                        /* issue: -PB42 Enable RTS/CTS to prevent OWL Tx hang up */
                        /* If this is Owl Ap, enable RTS/CTS protect */
                        if ( (wd->sta.athOwlAp == 1) || (wd->sta.RTSInAGGMode == TRUE) )
                        {
                            header[1] &= 0xfffc;
                            header[1] |= 0x1;
                        }

                        /* Enable RIFS : workaround 854T RTS/CTS */
                        /* Bit13 : TI enable RIFS */
                        //header[1] |= 0x2000;
                    }
                }
            }
        }
        #else
        /*
         * aggregation ampduIndication control
         */
        if (aggControl && aggControl->aggEnabled) {
            if (wd->enableAggregation==0 && !(header[6]&0x1))
            {
                if (((header[2] & 0x3) == 2))
                {
                    /* Enable aggregation */
                    header[1] |= 0x20;
                    if (ZM_AGG_LAST_MPDU == aggControl->ampduIndication)
                        header[1] |= 0x4000;
                }
                else {
                    zm_debug_msg1("no aggr, header[2]&0x3 = ",header[2] & 0x3)
                    aggControl->aggEnabled = 0;
                }
            }
            else {
                zm_debug_msg1("no aggr, wd->enableAggregation = ", wd->enableAggregation);
                zm_debug_msg1("no aggr, !header[6]&0x1 = ",!(header[6]&0x1));
                aggControl->aggEnabled = 0;
            }
        }
        #endif

        #ifdef ZM_AGGR_BIT_ON
        if (!(header[6]&0x1) && !rateProbingFlag)
        {
            if (((header[2] & 0x3) == 2))
            {
                /* Unicast frame with HT rate => Enable aggregation */
                /* Set aggregation group bits per AC */
                header[1] |= (0x20 | (zcUpToAc[up&0x7]<<10));

                //if (wd->sta.currentFrequency < 3000)
                {
                    /* Enable RTS/CTS to prevent OWL Tx hang up */
                    header[1] &= 0xfffc;
                    header[1] |= 0x1;
                }
            }
        }
        #endif
    }
#endif

    return (hlen<<1);
}


u16_t zfTxGenMmHeader(zdev_t* dev, u8_t frameType, u16_t* dst,
        u16_t* header, u16_t len, zbuf_t* buf, u16_t vap, u8_t encrypt)
{
    //u16_t bodyLen;
    u8_t  hlen = 32;        // MAC ctrl + PHY ctrl + 802.11 MM header

    zmw_get_wlan_dev(dev);

    zmw_declare_for_critical_section();

    /* Generate control setting */
    //bodyLen = zfwBufGetSize(dev, buf);
    header[0] = 24+len+4;   //Length
    if ((dst[0] & 0x1) != 0) //Broadcast, multicast frames
    {
        header[1] = 0xc;            //MAC control, backoff + noack
    }
    else
    {
        header[1] = 0x8;            //MAC control, backoff + (ack)
    }
    /* Dualband Management frame tx Rate */
    if (wd->wlanMode == ZM_MODE_AP)
    {
        if (wd->frequency < 3000)
        {
            /* CCK 1M */
            header[2] = 0x0f00;          //PHY control L
            header[3] = 0x0000;          //PHY control H
        }
        else
        {
            /* CCK 6M */
            header[2] = 0x0f01;          //PHY control L
            header[3] = 0x000B;          //PHY control H
        }
    }
    else
    {
        if (wd->sta.currentFrequency < 3000)
        {
            /* CCK 2M */
            header[2] = 0x0f00;          //PHY control L
            header[3] = 0x0001;          //PHY control H
        }
        else
        {
            /* CCK 6M */
            header[2] = 0x0f01;          //PHY control L
            header[3] = 0x000B;          //PHY control H
        }
    }
    /* Generate WLAN header */
    /* Frame control */
    header[4+0] = frameType;
    /* Duration */
    header[4+1] = 0;

    if (wd->wlanMode == ZM_MODE_INFRASTRUCTURE)
    {
        if ( frameType == ZM_WLAN_FRAME_TYPE_PROBEREQ )
        {
            header[4+8] = 0xFFFF;
            header[4+9] = 0xFFFF;
            header[4+10] = 0xFFFF;
        }
        else if ( frameType == ZM_WLAN_FRAME_TYPE_BA ) {
            /* do nothing */
        }
        else
        {
            header[4+8] = wd->sta.bssid[0];
            header[4+9] = wd->sta.bssid[1];
            header[4+10] = wd->sta.bssid[2];
        }
    }
    else if (wd->wlanMode == ZM_MODE_PSEUDO)
    {
        /* Address 3 = 00:00:00:00:00:00 */
        header[4+8] = 0;
        header[4+9] = 0;
        header[4+10] = 0;
    }
    else if (wd->wlanMode == ZM_MODE_IBSS)
    {
        header[4+8] = wd->sta.bssid[0];
        header[4+9] = wd->sta.bssid[1];
        header[4+10] = wd->sta.bssid[2];

        if ( frameType == ZM_WLAN_FRAME_TYPE_ATIM )
        {
            /* put ATIM to queue 5th */
            //header[2] |= (ZM_BIT_13|ZM_BIT_14);
            header[2] |= ZM_BIT_15;
        }
    }
    else if (wd->wlanMode == ZM_MODE_AP)
    {
        /* Address 3 = BSSID */
        header[4+8] = wd->macAddr[0];
        header[4+9] = wd->macAddr[1];
#ifdef ZM_VAPMODE_MULTILE_SSID
        header[4+10] = wd->macAddr[2]; //Multiple SSID
#else
        header[4+10] = wd->macAddr[2] + (vap<<8); //VAP
#endif
        //if in scan, must set address 3 to broadcast because of some ap would care this
        //if ((wd->heartBeatNotification & ZM_BSSID_LIST_SCAN)
        //        == ZM_BSSID_LIST_SCAN)
        //if FrameType is Probe Request, Address3 should be boradcast
        if (frameType == ZM_WLAN_FRAME_TYPE_PROBEREQ)
        {
            header[4+8] = 0xFFFF;
            header[4+9] = 0xFFFF;
            header[4+10] = 0xFFFF;
        }
    }

    /* Address 1 = DA */
    header[4+2] = dst[0];
    header[4+3] = dst[1];
    header[4+4] = dst[2];

    /* Address 2 = SA */
    header[4+5] = wd->macAddr[0];
    header[4+6] = wd->macAddr[1];
    if (wd->wlanMode == ZM_MODE_AP)
    {
#ifdef ZM_VAPMODE_MULTILE_SSID
        header[4+7] = wd->macAddr[2]; //Multiple SSID
#else
        header[4+7] = wd->macAddr[2] + (vap<<8); //VAP
#endif
    }
    else
    {
        header[4+7] = wd->macAddr[2];
    }

    /* Sequence Control */
    zmw_enter_critical_section(dev);
    header[4+11] = ((wd->mmseq++)<<4);
    zmw_leave_critical_section(dev);

    if( frameType == ZM_WLAN_FRAME_TYPE_QOS_NULL )
    {
        /*Qos Control*/
        header[4+12] = 0x0;
        hlen+=2;
        header[0]+=2;
    }

    if ( encrypt )
    {
        if ( wd->sta.wepStatus == ZM_ENCRYPTION_WEP_ENABLED )
        {
            if ( (wd->sta.encryMode == ZM_WEP64)||
                 (wd->sta.encryMode == ZM_WEP128)||
                 (wd->sta.encryMode == ZM_WEP256) )
            {
                header[4] |= 0x4000;
                header[16] = 0x0;   //IV
                header[17] = 0x0; //IV
                header[17] |= (((u16_t) wd->sta.keyId) << 14);
                hlen += 4;

                header[0] += 8;         // icvLen = 4;
                header[1] |= 0x40;      // enable encryption on macCtrl
             }
        }
    }

    // Enable HW duration
    if ( frameType != ZM_WLAN_FRAME_TYPE_PSPOLL )
    {
        header[1] |= 0x200;
    }

    return hlen;
}

void zfInitMacApMode(zdev_t* dev)
{
    u16_t i;

    zmw_get_wlan_dev(dev);

    zfHpEnableBeacon(dev, ZM_MODE_AP, (wd->beaconInterval/wd->ap.vapNumber), 1, 0);

    /* AP mode */
    zfHpSetApStaMode(dev, ZM_HAL_80211_MODE_AP);

    /* VAP test code */
    /* AP + VAP mode */
    if (wd->ap.vapNumber >= 2)
    {
        for (i=1; i<ZM_MAX_AP_SUPPORT; i++)
        {
            if (((wd->ap.apBitmap >> i) & 0x1) != 0)
            {
                u16_t mac[3];
                mac[0] = wd->macAddr[0];
                mac[1] = wd->macAddr[1];
#ifdef ZM_VAPMODE_MULTILE_SSID
                mac[2] = wd->macAddr[2]; //Multiple SSID
#else
                mac[2] = wd->macAddr[2] + (i<<8); //VAP
#endif
                zfHpSetMacAddress(dev, mac, i);

            }
        }
    }

    /* basic rate setting */
    zfHpSetBasicRateSet(dev, wd->bRateBasic, wd->gRateBasic);

    /* Set TxQs CWMIN, CWMAX, AIFS and TXO to WME AP default. */
    zfUpdateDefaultQosParameter(dev, 1);

    return;
}

u16_t zfChGetNextChannel(zdev_t* dev, u16_t frequency, u8_t* pbPassive)
{
    u8_t   i;
    u8_t   bPassive;

    zmw_get_wlan_dev(dev);

    /* Avoid NULL value */
    if ( pbPassive == NULL )
    {
        pbPassive = &bPassive;
    }

    for( i=0; i<wd->regulationTable.allowChannelCnt; i++ )
    {
        if ( wd->regulationTable.allowChannel[i].channel == frequency )
        {
            if ( i == (wd->regulationTable.allowChannelCnt-1) )
            {
                i = 0;
            }
            else
            {
                i++;
            }

            if ( wd->regulationTable.allowChannel[i].channelFlags
                    & ZM_REG_FLAG_CHANNEL_PASSIVE )
            {
                *pbPassive = TRUE;
            }
            else
            {
                *pbPassive = FALSE;
            }

            return wd->regulationTable.allowChannel[i].channel;
        }
    }

    return 0xffff;
}

u16_t zfChGetFirstChannel(zdev_t* dev, u8_t* pbPassive)
{
    u8_t   bPassive;

    zmw_get_wlan_dev(dev);

    /* Avoid NULL value */
    if ( pbPassive == NULL )
    {
        pbPassive = &bPassive;
    }

   if ( wd->regulationTable.allowChannel[0].channelFlags & ZM_REG_FLAG_CHANNEL_PASSIVE )
    {
        *pbPassive = TRUE;
    }
    else
    {
        *pbPassive = FALSE;
    }

    return wd->regulationTable.allowChannel[0].channel;
}

u16_t zfChGetFirst2GhzChannel(zdev_t* dev)
{
    u8_t    i;

    zmw_get_wlan_dev(dev);

    for( i=0; i<wd->regulationTable.allowChannelCnt; i++ )
    {
        if ( wd->regulationTable.allowChannel[i].channel < 3000 )
        {
            /* find the first 2Ghz channel */
            return wd->regulationTable.allowChannel[i].channel;
        }
    }

    /* Can not find any 2Ghz channel */
    return 0;
}

u16_t zfChGetFirst5GhzChannel(zdev_t* dev)
{
    u8_t    i;

    zmw_get_wlan_dev(dev);

    for( i=0; i<wd->regulationTable.allowChannelCnt; i++ )
    {
        if ( wd->regulationTable.allowChannel[i].channel > 3000 )
        {
            /* find the first 5Ghz channel */
            return wd->regulationTable.allowChannel[i].channel;
        }
    }

    /* Can not find any 5Ghz channel */
    return 0;
}

u16_t zfChGetLastChannel(zdev_t* dev, u8_t* pbPassive)
{
    u8_t   bPassive;
    u8_t   ChannelIndex;

    zmw_get_wlan_dev(dev);

    ChannelIndex = wd->regulationTable.allowChannelCnt-1;

    /* Avoid NULL value */
    if ( pbPassive == NULL )
    {
        pbPassive = &bPassive;
    }

    if ( wd->regulationTable.allowChannel[ChannelIndex].channelFlags
            & ZM_REG_FLAG_CHANNEL_PASSIVE )
    {
        *pbPassive = TRUE;
    }
    else
    {
        *pbPassive = FALSE;
    }

    return wd->regulationTable.allowChannel[ChannelIndex].channel;
}

u16_t zfChGetLast5GhzChannel(zdev_t* dev)
{
    u8_t    i;
    u16_t   last5Ghzfrequency;

    zmw_get_wlan_dev(dev);

    last5Ghzfrequency = 0;
    for( i=0; i<wd->regulationTable.allowChannelCnt; i++ )
    {
        if ( wd->regulationTable.allowChannel[i].channel > 3000 )
        {
            last5Ghzfrequency = wd->regulationTable.allowChannel[i].channel;
        }
    }

    return last5Ghzfrequency;
}

/* freqBand = 0 => auto check   */
/*          = 1 => 2.4 GHz band */
/*          = 2 => 5 GHz band   */
u16_t zfChNumToFreq(zdev_t* dev, u8_t ch, u8_t freqBand)
{
    u16_t freq = 0xffff;

    if ( freqBand == 0 )
    {
        if (ch > 14)
        {   /* adapter is at 5 GHz band */
            freqBand = 2;
        }
        else
        {
            freqBand = 1;
        }
    }

    if ( freqBand == 2 )
    {   /* the channel belongs to 5 GHz band */
        if ( (ch >= 184)&&(ch <= 196) )
        {
            freq = 4000 + ch*5;
        }
        else
        {
            freq = 5000 + ch*5;
        }
    }
    else
    {   /* the channel belongs to 2.4 GHz band */
        if ( ch == 14 )
        {
            freq = ZM_CH_G_14;
        }
        else
        {
            freq = ZM_CH_G_1 + (ch-1)*5;
        }
    }

    return freq;
}

u8_t zfChFreqToNum(u16_t freq, u8_t* pbIs5GBand)
{
    u8_t   ch;
    u8_t   Is5GBand;

    /* to avoid NULL value */
    if ( pbIs5GBand == NULL )
    {
        pbIs5GBand = &Is5GBand;
    }

    *pbIs5GBand = FALSE;

    if ( freq == ZM_CH_G_14 )
    {
        ch = 14;
    }
    else if ( freq < 4000 )
    {
        ch = (freq - ZM_CH_G_1) / 5 + 1;
    }
    else if ( freq < 5000 )
    {
        ch = (freq - 4000) / 5;
        *pbIs5GBand = TRUE;
    }
    else
    {
        ch = (freq - 5000) / 5;
        *pbIs5GBand = TRUE;
    }

    return ch;
}
