blob: 7f09fded459ea98f2ee9425733bad6594b2f4e05 [file] [log] [blame]
/*
* 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 : mm.c */
/* */
/* Abstract */
/* This module contains common functions for handle AP */
/* management frame. */
/* */
/* NOTES */
/* None */
/* */
/************************************************************************/
#include "cprecomp.h"
#include "ratectrl.h"
extern const u8_t zcUpToAc[];
void zfMmApTimeTick(zdev_t* dev)
{
u32_t now;
zmw_get_wlan_dev(dev);
//zm_debug_msg1("wd->wlanMode : ", wd->wlanMode);
if (wd->wlanMode == ZM_MODE_AP)
{
/* => every 1.28 seconds */
/* AP : aging STA that does not active for wd->ap.staAgingTime */
now = wd->tick & 0x7f;
if (now == 0x0)
{
zfApAgingSta(dev);
}
else if (now == 0x1f)
{
zfQueueAge(dev, wd->ap.uapsdQ, wd->tick, 10000);
}
/* AP : check (wd->ap.protectedObss) and (wd->ap.bStaAssociated) */
/* to enable NonErp and Protection mode */
else if (now == 0x3f)
{
//zfApProtctionMonitor(dev);
}
}
}
/************************************************************************/
/* */
/* FUNCTION DESCRIPTION zfApInitStaTbl */
/* Init AP's station table. */
/* */
/* INPUTS */
/* dev : device pointer */
/* */
/* OUTPUTS */
/* None */
/* */
/* AUTHOR */
/* Stephen Chen ZyDAS Technology Corporation 2005.10 */
/* */
/************************************************************************/
void zfApInitStaTbl(zdev_t* dev)
{
u16_t i;
zmw_get_wlan_dev(dev);
for (i=0; i<ZM_MAX_STA_SUPPORT; i++)
{
wd->ap.staTable[i].valid = 0;
wd->ap.staTable[i].state = 0;
wd->ap.staTable[i].addr[0] = 0;
wd->ap.staTable[i].addr[1] = 0;
wd->ap.staTable[i].addr[2] = 0;
wd->ap.staTable[i].time = 0;
wd->ap.staTable[i].vap = 0;
wd->ap.staTable[i].encryMode = ZM_NO_WEP;
}
return;
}
/************************************************************************/
/* */
/* FUNCTION DESCRIPTION zfApFindSta */
/* Find a STA in station table. */
/* */
/* INPUTS */
/* dev : device pointer */
/* addr : Target STA address */
/* */
/* OUTPUTS */
/* 0xffff : fail */
/* other : STA table index */
/* */
/* AUTHOR */
/* Stephen Chen ZyDAS Technology Corporation 2005.10 */
/* */
/************************************************************************/
u16_t zfApFindSta(zdev_t* dev, u16_t* addr)
{
u16_t i;
zmw_get_wlan_dev(dev);
for (i=0; i<ZM_MAX_STA_SUPPORT; i++)
{
if (wd->ap.staTable[i].valid == 1)
{
if ((wd->ap.staTable[i].addr[0] == addr[0])
&& (wd->ap.staTable[i].addr[1] == addr[1])
&& (wd->ap.staTable[i].addr[2] == addr[2]))
{
return i;
}
}
}
return 0xffff;
}
u16_t zfApGetSTAInfo(zdev_t* dev, u16_t* addr, u16_t* state, u8_t* vap)
{
u16_t id;
zmw_get_wlan_dev(dev);
zmw_declare_for_critical_section();
zmw_enter_critical_section(dev);
if ((id = zfApFindSta(dev, addr)) != 0xffff)
{
*vap = wd->ap.staTable[id].vap;
*state = wd->ap.staTable[id++].state;
}
zmw_leave_critical_section(dev);
return id;
}
void zfApGetStaQosType(zdev_t* dev, u16_t* addr, u8_t* qosType)
{
u16_t id;
zmw_get_wlan_dev(dev);
zmw_declare_for_critical_section();
zmw_enter_critical_section(dev);
if ((id = zfApFindSta(dev, addr)) != 0xffff)
{
*qosType = wd->ap.staTable[id].qosType;
}
else
{
*qosType = 0;
}
zmw_leave_critical_section(dev);
return;
}
void zfApGetStaTxRateAndQosType(zdev_t* dev, u16_t* addr, u32_t* phyCtrl,
u8_t* qosType, u16_t* rcProbingFlag)
{
u16_t id;
u8_t rate;
zmw_get_wlan_dev(dev);
zmw_declare_for_critical_section();
zmw_enter_critical_section(dev);
if ((id = zfApFindSta(dev, addr)) != 0xffff)
{
rate = (u8_t)zfRateCtrlGetTxRate(dev, &wd->ap.staTable[id].rcCell, rcProbingFlag);
#ifdef ZM_AP_DEBUG
//rate = 15;
#endif
*phyCtrl = zcRateToPhyCtrl[rate];
*qosType = wd->ap.staTable[id].qosType;
}
else
{
if (wd->frequency < 3000)
{
/* CCK 1M */
//header[2] = 0x0f00; //PHY control L
//header[3] = 0x0000; //PHY control H
*phyCtrl = 0x00000F00;
}
else
{
/* CCK 6M */
//header[2] = 0x0f01; //PHY control L
//header[3] = 0x000B; //PHY control H
*phyCtrl = 0x000B0F01;
}
*qosType = 0;
}
zmw_leave_critical_section(dev);
zm_msg2_mm(ZM_LV_3, "PhyCtrl=", *phyCtrl);
return;
}
void zfApGetStaEncryType(zdev_t* dev, u16_t* addr, u8_t* encryType)
{
//struct zsWlanDev* wd = (struct zsWlanDev*) zmw_wlan_dev(dev);
u16_t id;
zmw_get_wlan_dev(dev);
zmw_declare_for_critical_section();
zmw_enter_critical_section(dev);
if ((id = zfApFindSta(dev, addr)) != 0xffff)
{
*encryType = wd->ap.staTable[id].encryMode;
}
else
{
*encryType = ZM_NO_WEP;
}
zmw_leave_critical_section(dev);
zm_msg2_mm(ZM_LV_3, "encyrType=", *encryType);
return;
}
void zfApGetStaWpaIv(zdev_t* dev, u16_t* addr, u16_t* iv16, u32_t* iv32)
{
//struct zsWlanDev* wd = (struct zsWlanDev*) zmw_wlan_dev(dev);
u16_t id;
zmw_get_wlan_dev(dev);
zmw_declare_for_critical_section();
zmw_enter_critical_section(dev);
if ((id = zfApFindSta(dev, addr)) != 0xffff)
{
*iv16 = wd->ap.staTable[id].iv16;
*iv32 = wd->ap.staTable[id].iv32;
}
else
{
*iv16 = 0;
*iv32 = 0;
}
zmw_leave_critical_section(dev);
zm_msg2_mm(ZM_LV_3, "iv16=", *iv16);
zm_msg2_mm(ZM_LV_3, "iv32=", *iv32);
return;
}
void zfApSetStaWpaIv(zdev_t* dev, u16_t* addr, u16_t iv16, u32_t iv32)
{
//struct zsWlanDev* wd = (struct zsWlanDev*) zmw_wlan_dev(dev);
u16_t id;
zmw_get_wlan_dev(dev);
zmw_declare_for_critical_section();
zmw_enter_critical_section(dev);
if ((id = zfApFindSta(dev, addr)) != 0xffff)
{
wd->ap.staTable[id].iv16 = iv16;
wd->ap.staTable[id].iv32 = iv32;
}
zmw_leave_critical_section(dev);
zm_msg2_mm(ZM_LV_3, "iv16=", iv16);
zm_msg2_mm(ZM_LV_3, "iv32=", iv32);
return;
}
void zfApClearStaKey(zdev_t* dev, u16_t* addr)
{
//struct zsWlanDev* wd = (struct zsWlanDev*) zmw_wlan_dev(dev);
u16_t bcAddr[3] = { 0xffff, 0xffff, 0xffff };
u16_t id;
zmw_get_wlan_dev(dev);
if (zfMemoryIsEqual((u8_t*)bcAddr, (u8_t*)addr, sizeof(bcAddr)) == TRUE)
{
/* Turn off group key information */
// zfClearKey(dev, 0);
}
else
{
zmw_declare_for_critical_section();
zmw_enter_critical_section(dev);
if ((id = zfApFindSta(dev, addr)) != 0xffff)
{
/* Turn off STA's key information */
zfHpRemoveKey(dev, id+1);
/* Update STA's Encryption Type */
wd->ap.staTable[id].encryMode = ZM_NO_WEP;
}
else
{
zm_msg0_mm(ZM_LV_3, "Can't find STA address\n");
}
zmw_leave_critical_section(dev);
}
}
#ifdef ZM_ENABLE_CENC
void zfApGetStaCencIvAndKeyIdx(zdev_t* dev, u16_t* addr, u32_t *iv, u8_t *keyIdx)
{
//struct zsWlanDev* wd = (struct zsWlanDev*) zmw_wlan_dev(dev);
u16_t id;
zmw_get_wlan_dev(dev);
zmw_declare_for_critical_section();
zmw_enter_critical_section(dev);
if ((id = zfApFindSta(dev, addr)) != 0xffff)
{
*iv++ = wd->ap.staTable[id].txiv[0];
*iv++ = wd->ap.staTable[id].txiv[1];
*iv++ = wd->ap.staTable[id].txiv[2];
*iv = wd->ap.staTable[id].txiv[3];
*keyIdx = wd->ap.staTable[id].cencKeyIdx;
}
else
{
*iv++ = 0x5c365c37;
*iv++ = 0x5c365c36;
*iv++ = 0x5c365c36;
*iv = 0x5c365c36;
*keyIdx = 0;
}
zmw_leave_critical_section(dev);
return;
}
void zfApSetStaCencIv(zdev_t* dev, u16_t* addr, u32_t *iv)
{
//struct zsWlanDev* wd = (struct zsWlanDev*) zmw_wlan_dev(dev);
u16_t id;
zmw_get_wlan_dev(dev);
zmw_declare_for_critical_section();
zmw_enter_critical_section(dev);
if ((id = zfApFindSta(dev, addr)) != 0xffff)
{
wd->ap.staTable[id].txiv[0] = *iv++;
wd->ap.staTable[id].txiv[1] = *iv++;
wd->ap.staTable[id].txiv[2] = *iv++;
wd->ap.staTable[id].txiv[3] = *iv;
}
zmw_leave_critical_section(dev);
return;
}
#endif //ZM_ENABLE_CENC
/************************************************************************/
/* */
/* FUNCTION DESCRIPTION zfApFlushBufferedPsFrame */
/* Free buffered PS frames. */
/* */
/* INPUTS */
/* dev : device pointer */
/* */
/* OUTPUTS */
/* None */
/* */
/* AUTHOR */
/* Stephen Chen Atheros Communications, INC. 2007.1 */
/* */
/************************************************************************/
void zfApFlushBufferedPsFrame(zdev_t* dev)
{
u16_t emptyFlag;
u16_t freeCount;
u16_t vap;
zbuf_t* psBuf = NULL;
zmw_get_wlan_dev(dev);
zmw_declare_for_critical_section();
freeCount = 0;
emptyFlag = 0;
while (1)
{
psBuf = NULL;
zmw_enter_critical_section(dev);
if (wd->ap.uniHead != wd->ap.uniTail)
{
psBuf = wd->ap.uniArray[wd->ap.uniHead];
wd->ap.uniHead = (wd->ap.uniHead + 1) & (ZM_UNI_ARRAY_SIZE - 1);
}
else
{
emptyFlag = 1;
}
zmw_leave_critical_section(dev);
if (psBuf != NULL)
{
zfwBufFree(dev, psBuf, ZM_ERR_FLUSH_PS_QUEUE);
}
zm_assert(freeCount++ < (ZM_UNI_ARRAY_SIZE*2));
if (emptyFlag != 0)
{
break;
}
}
for (vap=0; vap<ZM_MAX_AP_SUPPORT; vap++)
{
freeCount = 0;
emptyFlag = 0;
while (1)
{
psBuf = NULL;
zmw_enter_critical_section(dev);
if (wd->ap.bcmcHead[vap] != wd->ap.bcmcTail[vap])
{
psBuf = wd->ap.bcmcArray[vap][wd->ap.bcmcHead[vap]];
wd->ap.bcmcHead[vap] = (wd->ap.bcmcHead[vap] + 1)
& (ZM_BCMC_ARRAY_SIZE - 1);
}
else
{
emptyFlag = 1;
}
zmw_leave_critical_section(dev);
if (psBuf != NULL)
{
zfwBufFree(dev, psBuf, ZM_ERR_FLUSH_PS_QUEUE);
}
zm_assert(freeCount++ < (ZM_BCMC_ARRAY_SIZE*2));
if (emptyFlag != 0)
{
break;
}
}
}
return;
}
u16_t zfApBufferPsFrame(zdev_t* dev, zbuf_t* buf, u16_t port)
{
u16_t id;
u16_t addr[3];
u16_t vap = 0;
u8_t up;
u16_t fragOff;
u8_t ac;
u16_t ret;
zmw_get_wlan_dev(dev);
zmw_declare_for_critical_section();
if (port < ZM_MAX_AP_SUPPORT)
{
vap = port;
}
addr[0] = zmw_rx_buf_readh(dev, buf, 0);
addr[1] = zmw_rx_buf_readh(dev, buf, 2);
addr[2] = zmw_rx_buf_readh(dev, buf, 4);
if ((addr[0] & 0x1) == 0x1)
{
if (wd->ap.staPowerSaving > 0)
{
zmw_enter_critical_section(dev);
/* Buffer this BC or MC frame */
if (((wd->ap.bcmcTail[vap]+1)&(ZM_BCMC_ARRAY_SIZE-1))
!= wd->ap.bcmcHead[vap])
{
wd->ap.bcmcArray[vap][wd->ap.bcmcTail[vap]++] = buf;
wd->ap.bcmcTail[vap] &= (ZM_BCMC_ARRAY_SIZE-1);
zmw_leave_critical_section(dev);
zm_msg0_tx(ZM_LV_0, "Buffer BCMC");
}
else
{
/* bcmcArray full */
zmw_leave_critical_section(dev);
zm_msg0_tx(ZM_LV_0, "BCMC buffer full");
/* free buffer according to buffer type */
zfwBufFree(dev, buf, ZM_ERR_BCMC_PS_BUFFER_UNAVAILABLE);
}
return 1;
}
}
else
{
zmw_enter_critical_section(dev);
if ((id = zfApFindSta(dev, addr)) != 0xffff)
{
if (wd->ap.staTable[id].psMode == 1)
{
zfTxGetIpTosAndFrag(dev, buf, &up, &fragOff);
ac = zcUpToAc[up&0x7] & 0x3;
if ((wd->ap.staTable[id].qosType == 1) &&
((wd->ap.staTable[id].qosInfo & (0x8>>ac)) != 0))
{
ret = zfQueuePutNcs(dev, wd->ap.uapsdQ, buf, wd->tick);
zmw_leave_critical_section(dev);
if (ret != ZM_SUCCESS)
{
zfwBufFree(dev, buf, ZM_ERR_AP_UAPSD_QUEUE_FULL);
}
}
else
{
/* Buffer this unicast frame */
if (((wd->ap.uniTail+1)&(ZM_UNI_ARRAY_SIZE-1))
!= wd->ap.uniHead)
{
wd->ap.uniArray[wd->ap.uniTail++] = buf;
wd->ap.uniTail &= (ZM_UNI_ARRAY_SIZE-1);
zmw_leave_critical_section(dev);
zm_msg0_tx(ZM_LV_0, "Buffer UNI");
}
else
{
/* uniArray full */
zmw_leave_critical_section(dev);
zm_msg0_tx(ZM_LV_0, "UNI buffer full");
/* free buffer according to buffer type */
zfwBufFree(dev, buf, ZM_ERR_UNI_PS_BUFFER_UNAVAILABLE);
}
}
return 1;
} /* if (wd->ap.staTable[id++].psMode == 1) */
} /* if ((id = zfApFindSta(dev, addr)) != 0xffff) */
zmw_leave_critical_section(dev);
}
return 0;
}
u16_t zfApGetSTAInfoAndUpdatePs(zdev_t* dev, u16_t* addr, u16_t* state,
u8_t* vap, u16_t psMode, u8_t* uapsdTrig)
{
u16_t id;
u8_t uapsdStaAwake = 0;
zmw_get_wlan_dev(dev);
zmw_declare_for_critical_section();
zmw_enter_critical_section(dev);
#ifdef ZM_AP_DEBUG
//psMode=0;
#endif
if ((id = zfApFindSta(dev, addr)) != 0xffff)
{
if (psMode != 0)
{
zm_msg0_mm(ZM_LV_0, "psMode = 1");
if (wd->ap.staTable[id].psMode == 0)
{
wd->ap.staPowerSaving++;
}
else
{
if (wd->ap.staTable[id].qosType == 1)
{
zm_msg0_mm(ZM_LV_0, "UAPSD trigger");
*uapsdTrig = wd->ap.staTable[id].qosInfo;
}
}
}
else
{
if (wd->ap.staTable[id].psMode != 0)
{
wd->ap.staPowerSaving--;
if ((wd->ap.staTable[id].qosType == 1) && ((wd->ap.staTable[id].qosInfo&0xf)!=0))
{
uapsdStaAwake = 1;
}
}
}
wd->ap.staTable[id].psMode = (u8_t) psMode;
wd->ap.staTable[id].time = wd->tick;
*vap = wd->ap.staTable[id].vap;
*state = wd->ap.staTable[id++].state;
}
zmw_leave_critical_section(dev);
if (uapsdStaAwake == 1)
{
zbuf_t* psBuf;
u8_t mb;
while (1)
{
if ((psBuf = zfQueueGetWithMac(dev, wd->ap.uapsdQ, (u8_t*)addr, &mb)) != NULL)
{
zfTxSendEth(dev, psBuf, 0, ZM_EXTERNAL_ALLOC_BUF, 0);
}
else
{
break;
}
}
}
return id;
}
/************************************************************************/
/* */
/* FUNCTION DESCRIPTION zfApGetNewSta */
/* Get a new STA from station table. */
/* */
/* INPUTS */
/* dev : device pointer */
/* */
/* OUTPUTS */
/* 0xffff : fail */
/* other : STA table index */
/* */
/* AUTHOR */
/* Stephen Chen ZyDAS Technology Corporation 2005.10 */
/* */
/************************************************************************/
u16_t zfApGetNewSta(zdev_t* dev)
{
u16_t i;
zmw_get_wlan_dev(dev);
for (i=0; i<ZM_MAX_STA_SUPPORT; i++)
{
if (wd->ap.staTable[i].valid == 0)
{
zm_msg2_mm(ZM_LV_0, "zfApGetNewSta=", i);
return i;
}
}
return 0xffff;
}
/************************************************************************/
/* */
/* FUNCTION DESCRIPTION zfApAddSta */
/* Add a STA to station table. */
/* */
/* INPUTS */
/* dev : device pointer */
/* addr : STA MAC address */
/* state : STA state */
/* apId : Virtual AP ID */
/* type : 0=>11b, 1=>11g */
/* */
/* OUTPUTS */
/* 0xffff : fail */
/* Other : index */
/* */
/* AUTHOR */
/* Stephen Chen ZyDAS Technology Corporation 2005.10 */
/* */
/************************************************************************/
u16_t zfApAddSta(zdev_t* dev, u16_t* addr, u16_t state, u16_t apId, u8_t type,
u8_t qosType, u8_t qosInfo)
{
u16_t index;
u16_t i;
zmw_get_wlan_dev(dev);
zmw_declare_for_critical_section();
zm_msg1_mm(ZM_LV_0, "STA type=", type);
zmw_enter_critical_section(dev);
if ((index = zfApFindSta(dev, addr)) != 0xffff)
{
zm_msg0_mm(ZM_LV_2, "found");
/* Update STA state */
if ((state == ZM_STATE_AUTH) || (state == ZM_STATE_PREAUTH))
{
wd->ap.staTable[index].state = state;
wd->ap.staTable[index].time = wd->tick;
wd->ap.staTable[index].vap = (u8_t)apId;
}
else if (state == ZM_STATE_ASOC)
{
if ((wd->ap.staTable[index].state == ZM_STATE_AUTH))
//&& (wd->ap.staTable[index].vap == apId))
{
wd->ap.staTable[index].state = state;
wd->ap.staTable[index].time = wd->tick;
wd->ap.staTable[index].qosType = qosType;
wd->ap.staTable[index].vap = (u8_t)apId;
wd->ap.staTable[index].staType = type;
wd->ap.staTable[index].qosInfo = qosInfo;
if (wd->frequency < 3000)
{
/* Init 11b/g */
zfRateCtrlInitCell(dev, &wd->ap.staTable[index].rcCell, type, 1, 1);
}
else
{
/* Init 11a */
zfRateCtrlInitCell(dev, &wd->ap.staTable[index].rcCell, type, 0, 1);
}
if (wd->zfcbApConnectNotify != NULL)
{
wd->zfcbApConnectNotify(dev, (u8_t*)addr, apId);
}
}
else
{
index = 0xffff;
}
}
}
else
{
zm_msg0_mm(ZM_LV_2, "Not found");
if ((state == ZM_STATE_AUTH) || (state == ZM_STATE_PREAUTH))
{
/* Get a new STA and update state */
index = zfApGetNewSta(dev);
zm_msg2_mm(ZM_LV_1, "new STA index=", index);
if (index != 0xffff)
{
for (i=0; i<3; i++)
{
wd->ap.staTable[index].addr[i] = addr[i];
}
wd->ap.staTable[index].state = state;
wd->ap.staTable[index].valid = 1;
wd->ap.staTable[index].time = wd->tick;
wd->ap.staTable[index].vap = (u8_t)apId;
wd->ap.staTable[index].encryMode = ZM_NO_WEP;
}
}
}
zmw_leave_critical_section(dev);
return index;
}
/************************************************************************/
/* */
/* FUNCTION DESCRIPTION zfApAgingSta */
/* Aging STA in station table. */
/* */
/* INPUTS */
/* dev : device pointer */
/* */
/* OUTPUTS */
/* number of 11b STA in STA table */
/* */
/* AUTHOR */
/* Stephen Chen ZyDAS Technology Corporation 2005.10 */
/* */
/************************************************************************/
void zfApAgingSta(zdev_t* dev)
{
u16_t i;
u32_t deltaMs;
u16_t addr[3];
u16_t txFlag;
u16_t psStaCount = 0;
zmw_get_wlan_dev(dev);
zmw_declare_for_critical_section();
wd->ap.gStaAssociated = wd->ap.bStaAssociated = 0;
for (i=0; i<ZM_MAX_STA_SUPPORT; i++)
{
txFlag = 0;
zmw_enter_critical_section(dev);
if (wd->ap.staTable[i].valid == 1)
{
addr[0] = wd->ap.staTable[i].addr[0];
addr[1] = wd->ap.staTable[i].addr[1];
addr[2] = wd->ap.staTable[i].addr[2];
/* millisecond */
deltaMs = (u32_t)((u32_t)wd->tick-(u32_t)wd->ap.staTable[i].time)
* ZM_MS_PER_TICK;
/* preauth */
if ((wd->ap.staTable[i].state == ZM_STATE_PREAUTH)
&& (deltaMs > ZM_PREAUTH_TIMEOUT_MS))
{
/* Aging STA */
wd->ap.staTable[i].valid = 0;
wd->ap.authSharing = 0;
txFlag = 1;
}
/* auth */
if ((wd->ap.staTable[i].state == ZM_STATE_AUTH)
&& (deltaMs > ZM_AUTH_TIMEOUT_MS))
{
/* Aging STA */
wd->ap.staTable[i].valid = 0;
txFlag = 1;
}
/* asoc */
if (wd->ap.staTable[i].state == ZM_STATE_ASOC)
{
if (wd->ap.staTable[i].psMode != 0)
{
psStaCount++;
}
if (deltaMs > ((u32_t)wd->ap.staAgingTimeSec<<10))
{
/* Aging STA */
zm_msg1_mm(ZM_LV_0, "Age STA index=", i);
wd->ap.staTable[i].valid = 0;
txFlag = 1;
}
else if (deltaMs > ((u32_t)wd->ap.staProbingTimeSec<<10))
{
if (wd->ap.staTable[i].psMode == 0)
{
/* Probing non-PS STA */
zm_msg1_mm(ZM_LV_0, "Probing STA index=", i);
wd->ap.staTable[i].time +=
(wd->ap.staProbingTimeSec * ZM_TICK_PER_SECOND);
txFlag = 2;
}
}
}
}
zmw_leave_critical_section(dev);
if (txFlag == 1)
{
/* Send deauthentication management frame */
zfSendMmFrame(dev, ZM_WLAN_FRAME_TYPE_DEAUTH, addr, 4, 0, 0);
}
else if (txFlag == 2)
{
zfSendMmFrame(dev, ZM_WLAN_DATA_FRAME, addr, 0, 0, 0);
}
}
wd->ap.staPowerSaving = psStaCount;
return;
}
void zfApProtctionMonitor(zdev_t* dev)
{
zmw_get_wlan_dev(dev);
/* 11b STA associated => nonErp, Protect */
if (wd->ap.bStaAssociated > 0)
{
/* Enable NonErp bit in information element */
wd->erpElement = ZM_WLAN_NON_ERP_PRESENT_BIT
| ZM_WLAN_USE_PROTECTION_BIT;
/* Enable protection mode */
zfApSetProtectionMode(dev, 1);
}
/* 11b STA not associated, protection OBSS present => Protect */
else if (wd->ap.protectedObss > 2) //Threshold
{
if (wd->disableSelfCts == 0)
{
/* Disable NonErp bit in information element */
wd->erpElement = ZM_WLAN_USE_PROTECTION_BIT;
/* Enable protection mode */
zfApSetProtectionMode(dev, 1);
}
}
else
{
/* Disable NonErp bit in information element */
wd->erpElement = 0;
/* Disable protection mode */
zfApSetProtectionMode(dev, 0);
}
wd->ap.protectedObss = 0;
}
void zfApProcessBeacon(zdev_t* dev, zbuf_t* buf)
{
u16_t offset;
u8_t ch;
zmw_get_wlan_dev(dev);
zm_msg0_mm(ZM_LV_3, "Rx beacon");
/* update Non-ERP flag(wd->ap.nonErpObss) */
if ((offset = zfFindElement(dev, buf, ZM_WLAN_EID_ERP)) == 0xffff)
{
/* 11b OBSS */
wd->ap.protectedObss++;
return;
}
ch = zmw_rx_buf_readb(dev, buf, offset+2);
if ((ch & ZM_WLAN_USE_PROTECTION_BIT) == ZM_WLAN_USE_PROTECTION_BIT)
{
/* Protected OBSS */
wd->ap.protectedObss = 1;
}
return;
}
/************************************************************************/
/* */
/* FUNCTION DESCRIPTION zfProcessAuth */
/* Process authenticate management frame. */
/* */
/* INPUTS */
/* dev : device pointer */
/* buf : auth frame buffer */
/* */
/* OUTPUTS */
/* none */
/* */
/* AUTHOR */
/* Stephen Chen ZyDAS Technology Corporation 2005.10 */
/* */
/************************************************************************/
/* Note : AP allows one authenticating STA at a time, does not */
/* support multiple authentication process. Make sure */
/* authentication state machine will not be blocked due */
/* to incompleted authentication handshake. */
void zfApProcessAuth(zdev_t* dev, zbuf_t* buf, u16_t* src, u16_t apId)
{
u16_t algo, seq, status;
u8_t authSharing;
u16_t ret;
u16_t i;
u8_t challengePassed = 0;
u8_t frameCtrl;
u32_t retAlgoSeq;
u32_t retStatus;
zmw_get_wlan_dev(dev);
zmw_declare_for_critical_section();
frameCtrl = zmw_rx_buf_readb(dev, buf, 1);
/* AP : Auth share 3 */
/* shift for WEP IV */
if ((frameCtrl & 0x40) != 0)
{
algo = zmw_rx_buf_readh(dev, buf, 28);
seq = zmw_rx_buf_readh(dev, buf, 30);
status = zmw_rx_buf_readh(dev, buf, 32);
}
else
{
algo = zmw_rx_buf_readh(dev, buf, 24);
seq = zmw_rx_buf_readh(dev, buf, 26);
status = zmw_rx_buf_readh(dev, buf, 28);
}
zm_msg2_mm(ZM_LV_0, "Rx Auth, seq=", seq);
/* Set default to authentication algorithm not support */
retAlgoSeq = 0x20000 | algo;
retStatus = 13; /* authentication algorithm not support */
/* AP : Auth open 1 */
if (algo == 0)
{
if (wd->ap.authAlgo[apId] == 0)
{
retAlgoSeq = 0x20000;
if (seq == 1)
{
/* AP : update STA to auth */
if ((ret = zfApAddSta(dev, src, ZM_STATE_AUTH, apId, 0, 0, 0)) != 0xffff)
{
/* AP : call zfwAuthNotify() for host to judge */
//zfwAuthNotify(dev, src);
/* AP : response Auth seq=2, success */
retStatus = 0;
}
else
{
/* AP : response Auth seq=2, unspecific error */
retStatus = 1;
}
}
else
{
/* AP : response Auth seq=2, sequence number out of expected */
retStatus = 14;
}
}
}
/* AP : Auth share 1 */
else if (algo == 1)
{
if (wd->ap.authAlgo[apId] == 1)
{
if (seq == 1)
{
retAlgoSeq = 0x20001;
/* critical section */
zmw_enter_critical_section(dev);
if (wd->ap.authSharing == 1)
{
authSharing = 1;
}
else
{
authSharing = 0;
wd->ap.authSharing = 1;
}
/* end of critical section */
zmw_leave_critical_section(dev);
if (authSharing == 1)
{
/* AP : response Auth seq=2, status = fail */
retStatus = 1;
}
else
{
/* AP : update STA to preauth */
zfApAddSta(dev, src, ZM_STATE_PREAUTH, apId, 0, 0, 0);
/* AP : call zfwAuthNotify() for host to judge */
//zfwAuthNotify(dev, src);
/* AP : response Auth seq=2 */
retStatus = 0;
}
}
else if (seq == 3)
{
retAlgoSeq = 0x40001;
if (wd->ap.authSharing == 1)
{
/* check challenge text */
if (zmw_buf_readh(dev, buf, 30+4) == 0x8010)
{
for (i=0; i<128; i++)
{
if (wd->ap.challengeText[i]
!= zmw_buf_readb(dev, buf, 32+i+4))
{
break;
}
}
if (i == 128)
{
challengePassed = 1;
}
}
if (challengePassed == 1)
{
/* AP : update STA to auth */
zfApAddSta(dev, src, ZM_STATE_AUTH, apId, 0, 0, 0);
/* AP : response Auth seq=2 */
retStatus = 0;
}
else
{
/* AP : response Auth seq=2, challenge failure */
retStatus = 15;
/* TODO : delete STA */
}
wd->ap.authSharing = 0;
}
}
else
{
retAlgoSeq = 0x40001;
retStatus = 14;
}
}
}
zfSendMmFrame(dev, ZM_WLAN_FRAME_TYPE_AUTH, src, retAlgoSeq,
retStatus, apId);
return;
}
void zfApProcessAsocReq(zdev_t* dev, zbuf_t* buf, u16_t* src, u16_t apId)
{
u16_t aid = 0xffff;
u8_t frameType;
u16_t offset;
u8_t staType = 0;
u8_t qosType = 0;
u8_t qosInfo = 0;
u8_t tmp;
u16_t i, j, k;
u16_t encMode = 0;
zmw_get_wlan_dev(dev);
/* AP : check SSID */
if ((offset = zfFindElement(dev, buf, ZM_WLAN_EID_SSID)) != 0xffff)
{
k = 0;
for (j = 0; j < wd->ap.vapNumber; j++)
{
if ((tmp = zmw_buf_readb(dev, buf, offset+1))
!= wd->ap.ssidLen[j])
{
k++;
}
}
if (k == wd->ap.vapNumber)
{
goto zlDeauth;
}
k = 0;
for (j = 0; j < wd->ap.vapNumber; j++)
{
for (i=0; i<wd->ap.ssidLen[j]; i++)
{
if ((tmp = zmw_buf_readb(dev, buf, offset+2+i))
!= wd->ap.ssid[j][i])
{
break;
}
}
if (i == wd->ap.ssidLen[j])
{
apId = j;
}
else
{
k++;
}
}
if (k == wd->ap.vapNumber)
{
goto zlDeauth;
}
}
/* TODO : check capability */
/* AP : check support rate */
if ((offset = zfFindElement(dev, buf, ZM_WLAN_EID_EXTENDED_RATE)) != 0xffff)
{
/* 11g STA */
staType = 1;
}
//CWYang(+)
if ((offset = zfFindElement(dev, buf, ZM_WLAN_EID_HT_CAPABILITY)) != 0xffff)
{
/* 11n STA */
staType = 2;
}
/* TODO : do not allow 11b STA to associated in Pure G mode */
if (wd->ap.wlanType[apId] == ZM_WLAN_TYPE_PURE_G && staType == 0)
{
zfSendMmFrame(dev, ZM_WLAN_FRAME_TYPE_DEAUTH, src, 3, 0, 0);
return;
}
/* In pure B mode, we set G STA into B mode */
if (wd->ap.wlanType[apId] == ZM_WLAN_TYPE_PURE_B && staType == 1)
{
staType = 0;
}
/* AP : check 11i and WPA */
/* AP : check 11h */
/* AP : check WME */
if ((offset = zfFindWifiElement(dev, buf, 2, 0)) != 0xffff)
{
/* WME STA */
qosType = 1;
zm_msg0_mm(ZM_LV_0, "WME STA");
if (wd->ap.uapsdEnabled != 0)
{
qosInfo = zmw_rx_buf_readb(dev, buf, offset+8);
}
}
if (wd->ap.wpaSupport[apId] == 1)
{
if ( (offset = zfFindElement(dev, buf, ZM_WLAN_EID_WPA_IE)) != 0xffff )
{
/* get WPA IE */
u8_t length = zmw_rx_buf_readb(dev, buf, offset+1);
if (length+2 < ZM_MAX_WPAIE_SIZE)
{
zfCopyFromRxBuffer(dev, buf, wd->ap.stawpaIe[apId], offset, length+2);
wd->ap.stawpaLen[apId] = length+2;
encMode = 1;
zm_msg1_mm(ZM_LV_0, "WPA Mode zfwAsocNotify, apId=", apId);
/* AP : Call zfwAsocNotify() */
if (wd->zfcbAsocNotify != NULL)
{
wd->zfcbAsocNotify(dev, src, wd->ap.stawpaIe[apId], wd->ap.stawpaLen[apId], apId);
}
}
else
{
goto zlDeauth;
}
}
else if ( (offset = zfFindElement(dev, buf, ZM_WLAN_EID_RSN_IE)) != 0xffff )
{
/* get RSN IE */
u8_t length = zmw_rx_buf_readb(dev, buf, offset+1);
if (length+2 < ZM_MAX_WPAIE_SIZE)
{
zfCopyFromRxBuffer(dev, buf, wd->ap.stawpaIe[apId], offset, length+2);
wd->ap.stawpaLen[apId] = length+2;
encMode = 1;
zm_msg1_mm(ZM_LV_0, "RSN Mode zfwAsocNotify, apId=", apId);
/* AP : Call zfwAsocNotify() */
if (wd->zfcbAsocNotify != NULL)
{
wd->zfcbAsocNotify(dev, src, wd->ap.stawpaIe[apId], wd->ap.stawpaLen[apId], apId);
}
}
else
{
goto zlDeauth;
}
}
#ifdef ZM_ENABLE_CENC
else if ( (offset = zfFindElement(dev, buf, ZM_WLAN_EID_CENC_IE)) != 0xffff )
{
/* get CENC IE */
u8_t length = zmw_rx_buf_readb(dev, buf, offset+1);
if (length+2 < ZM_MAX_WPAIE_SIZE)
{
zfCopyFromRxBuffer(dev, buf, wd->ap.stawpaIe[apId], offset, length+2);
wd->ap.stawpaLen[apId] = length+2;
encMode = 1;
zm_msg1_mm(ZM_LV_0, "CENC Mode zfwAsocNotify, apId=", apId);
/* AP : Call zfwAsocNotify() */
if (wd->zfcbCencAsocNotify != NULL)
{
wd->zfcbCencAsocNotify(dev, src, wd->ap.stawpaIe[apId],
wd->ap.stawpaLen[apId], apId);
}
}
else
{
goto zlDeauth;
}
}
#endif //ZM_ENABLE_CENC
else
{ /* ap is encryption but sta has no wpa/rsn ie */
zfSendMmFrame(dev, ZM_WLAN_FRAME_TYPE_DEAUTH, src, 6, 0, 0);
return;
}
}
/* sta has wpa/rsn ie but ap is no encryption */
if ((wd->ap.wpaSupport[apId] == 0) && (encMode == 1))
{
zfSendMmFrame(dev, ZM_WLAN_FRAME_TYPE_DEAUTH, src, 6, 0, 0);
return;
}
/* AP : update STA to asoc */
aid = zfApAddSta(dev, src, ZM_STATE_ASOC, apId, staType, qosType, qosInfo);
zfApStoreAsocReqIe(dev, buf, aid);
zlDeauth:
/* AP : send asoc rsp2 */
if (aid != 0xffff)
{
frameType = zmw_rx_buf_readb(dev, buf, 0);
if (frameType == ZM_WLAN_FRAME_TYPE_ASOCREQ)
{
zfSendMmFrame(dev, ZM_WLAN_FRAME_TYPE_ASOCRSP, src, 0, aid+1, apId);
}
else
{
zfSendMmFrame(dev, ZM_WLAN_FRAME_TYPE_REASOCRSP, src, 0, aid+1, apId);
}
}
else
{
/* TODO : send deauthentication */
zfSendMmFrame(dev, ZM_WLAN_FRAME_TYPE_DEAUTH, src, 6, 0, 0);
}
return;
}
void zfApStoreAsocReqIe(zdev_t* dev, zbuf_t* buf, u16_t aid)
{
//struct zsWlanAssoFrameHeader* pAssoFrame;
//u8_t pBuf[sizeof(struct zsWlanAssoFrameHeader)];
u16_t offset;
u32_t i;
u16_t length;
u8_t *htcap;
zmw_get_wlan_dev(dev);
for (i=0; i<wd->sta.asocRspFrameBodySize; i++)
{
wd->sta.asocRspFrameBody[i] = zmw_rx_buf_readb(dev, buf, i+24);
}
/* capability: 2 octets */
offset = 24;
/* Listen interval: 2 octets */
offset = 26;
/* SSID */
offset = 28;
/* supported rates */
if ((offset = zfFindElement(dev, buf, ZM_WLAN_EID_SUPPORT_RATE)) == 0xffff)
return;
length = zmw_rx_buf_readb(dev, buf, offset + 1);
/* extended supported rates */
if ((offset = zfFindElement(dev, buf, ZM_WLAN_EID_EXTENDED_RATE)) == 0xffff)
return;
length = zmw_rx_buf_readb(dev, buf, offset + 1);
/* power capability:4 octets */
offset = offset + 2 + length;
/* supported channels: 4 octets */
offset = offset + 2 + 4;
/* RSN */
/* QoS */
/* HT capabilities: 28 octets */
if ((offset = zfFindElement(dev, buf, ZM_WLAN_EID_HT_CAPABILITY)) != 0xffff) {
/* atheros pre n */
htcap = (u8_t *)&wd->ap.ie[aid].HtCap;
htcap[0] = zmw_rx_buf_readb(dev, buf, offset);
htcap[1] = 26;
for (i=1; i<=26; i++)
{
htcap[i+1] = zmw_rx_buf_readb(dev, buf, offset + i);
zm_debug_msg2("ASOC: HT Capabilities, htcap=", htcap[i+1]);
}
return;
}
else if ((offset = zfFindElement(dev, buf, ZM_WLAN_PREN2_EID_HTCAPABILITY)) != 0xffff) {
/* pre n 2.0 standard */
htcap = (u8_t *)&wd->ap.ie[aid].HtCap;
for (i=0; i<28; i++)
{
htcap[i] = zmw_rx_buf_readb(dev, buf, offset + i);
zm_debug_msg2("ASOC: HT Capabilities, htcap=", htcap[i]);
}
}
else {
/* not 11n AP */
return;
}
/* supported regulatory classes */
offset = offset + length;
//length = zmw_rx_buf_readb(dev, buf, offset + 1);
{
u8_t *htcap;
htcap = (u8_t *)&wd->sta.ie.HtInfo;
//zm_debug_msg2("ASOC: HT Capabilities info=", ((u16_t *)htcap)[1]);
//zm_debug_msg2("ASOC: A-MPDU parameters=", htcap[4]);
//zm_debug_msg2("ASOC: Supported MCS set=", ((u32_t *)htcap)[1]>>8);
}
}
void zfApProcessAsocRsp(zdev_t* dev, zbuf_t* buf)
{
}
void zfApProcessDeauth(zdev_t* dev, zbuf_t* buf, u16_t* src, u16_t apId)
{
u16_t aid;
zmw_get_wlan_dev(dev);
zmw_declare_for_critical_section();
zmw_enter_critical_section(dev);
/* AP : if SA=associated STA then deauthenticate STA */
if ((aid = zfApFindSta(dev, src)) != 0xffff)
{
/* Clear STA table */
wd->ap.staTable[aid].valid = 0;
if (wd->zfcbDisAsocNotify != NULL)
{
wd->zfcbDisAsocNotify(dev, (u8_t*)src, apId);
}
}
zmw_leave_critical_section(dev);
}
void zfApProcessDisasoc(zdev_t* dev, zbuf_t* buf, u16_t* src, u16_t apId)
{
u16_t aid;
zmw_get_wlan_dev(dev);
zmw_declare_for_critical_section();
zmw_enter_critical_section(dev);
/* AP : if SA=associated STA then deauthenticate STA */
if ((aid = zfApFindSta(dev, src)) != 0xffff)
{
/* Clear STA table */
wd->ap.staTable[aid].valid = 0;
zmw_leave_critical_section(dev);
if (wd->zfcbDisAsocNotify != NULL)
{
wd->zfcbDisAsocNotify(dev, (u8_t*)src, apId);
}
}
zmw_leave_critical_section(dev);
}
void zfApProcessProbeRsp(zdev_t* dev, zbuf_t* buf, struct zsAdditionInfo* AddInfo)
{
#if 0
zmw_get_wlan_dev(dev);
zm_msg0_mm(ZM_LV_0, "Rx probersp");
/* Gather scan result */
//zm_debug_msg1("bssList Count = ", wd->sta.bssList.bssCount);
/* return if not in scanning */
if ((wd->heartBeatNotification & ZM_BSSID_LIST_SCAN)
!= ZM_BSSID_LIST_SCAN)
{
return;
}
//if ( wd->sta.pUpdateBssList->bssCount == ZM_MAX_BSS )
if ( wd->sta.bssList.bssCount == ZM_MAX_BSS )
{
return;
}
zfProcessProbeRsp(dev, buf, AddInfo);
#endif
}
/************************************************************************/
/* */
/* FUNCTION DESCRIPTION zfApAddIeSsid */
/* Add AP information element SSID to buffer. */
/* */
/* INPUTS */
/* dev : device pointer */
/* buf : buffer to add information element */
/* offset : add information element from this offset */
/* vap : virtual AP ID */
/* */
/* OUTPUTS */
/* buffer offset after adding information element */
/* */
/* AUTHOR */
/* Stephen Chen ZyDAS Technology Corporation 2005.11 */
/* */
/************************************************************************/
u16_t zfApAddIeSsid(zdev_t* dev, zbuf_t* buf, u16_t offset, u16_t vap)
{
u16_t i;
zmw_get_wlan_dev(dev);
/* Element ID */
zmw_tx_buf_writeb(dev, buf, offset++, ZM_WLAN_EID_SSID);
/* Element Length */
zmw_tx_buf_writeb(dev, buf, offset++, wd->ap.ssidLen[vap]);
/* Information : SSID */
for (i=0; i<wd->ap.ssidLen[vap]; i++)
{
zmw_tx_buf_writeb(dev, buf, offset++, wd->ap.ssid[vap][i]);
}
return offset;
}
/************************************************************************/
/* */
/* FUNCTION DESCRIPTION zfApAddIeTim */
/* Add AP information element TIM to buffer. */
/* */
/* INPUTS */
/* dev : device pointer */
/* buf : buffer to add information element */
/* offset : add information element from this offset */
/* vap : virtual AP ID */
/* */
/* OUTPUTS */
/* buffer offset after adding information element */
/* */
/* AUTHOR */
/* Stephen Chen ZyDAS Technology Corporation 2005.11 */
/* */
/************************************************************************/
u16_t zfApAddIeTim(zdev_t* dev, zbuf_t* buf, u16_t offset, u16_t vap)
{
u8_t uniBitMap[9];
u16_t highestByte;
u16_t i;
u16_t lenOffset;
u16_t id;
u16_t dst[3];
u16_t aid;
u16_t bitPosition;
u16_t bytePosition;
zbuf_t* psBuf;
zbuf_t* tmpBufArray[ZM_UNI_ARRAY_SIZE];
u16_t tmpBufArraySize = 0;
zmw_get_wlan_dev(dev);
zmw_declare_for_critical_section();
/* Element ID */
zmw_tx_buf_writeb(dev, buf, offset++, ZM_WLAN_EID_TIM);
/* offset of Element Length */
lenOffset = offset++;
/* Information : TIM */
/* DTIM count */
/* TODO : Doesn't work for Virtual AP's case */
wd->CurrentDtimCount++;
if (wd->CurrentDtimCount >= wd->dtim)
{
wd->CurrentDtimCount = 0;
}
zmw_tx_buf_writeb(dev, buf, offset++, wd->CurrentDtimCount);
/* DTIM period */
zmw_tx_buf_writeb(dev, buf, offset++, wd->dtim);
/* bitmap offset */
zmw_tx_buf_writeb(dev, buf, offset++, 0);
/* Update BCMC bit */
if (wd->CurrentDtimCount == 0)
{
zmw_enter_critical_section(dev);
wd->ap.timBcmcBit[vap] = (wd->ap.bcmcTail[vap]!=wd->ap.bcmcHead[vap])?1:0;
zmw_leave_critical_section(dev);
}
else
{
wd->ap.timBcmcBit[vap] = 0;
}
/* Update Unicast bitmap */
/* reset bit map */
for (i=0; i<9; i++)
{
uniBitMap[i] = 0;
}
highestByte = 0;
#if 1
zmw_enter_critical_section(dev);
id = wd->ap.uniHead;
while (id != wd->ap.uniTail)
{
psBuf = wd->ap.uniArray[id];
/* TODO : Aging PS frame after queuing for more than 10 seconds */
/* get destination STA's aid */
dst[0] = zmw_tx_buf_readh(dev, psBuf, 0);
dst[1] = zmw_tx_buf_readh(dev, psBuf, 2);
dst[2] = zmw_tx_buf_readh(dev, psBuf, 4);
if ((aid = zfApFindSta(dev, dst)) != 0xffff)
{
if (wd->ap.staTable[aid].psMode != 0)
{
zm_msg1_mm(ZM_LV_0, "aid=",aid);
aid++;
zm_assert(aid<=64);
bitPosition = (1 << (aid & 0x7));
bytePosition = (aid >> 3);
uniBitMap[bytePosition] |= bitPosition;
if (bytePosition>highestByte)
{
highestByte = bytePosition;
}
id = (id+1) & (ZM_UNI_ARRAY_SIZE-1);
}
else
{
zm_msg0_mm(ZM_LV_0, "Send PS frame which STA no longer in PS mode");
/* Send PS frame which STA no longer in PS mode */
zfApRemoveFromPsQueue(dev, id, dst);
tmpBufArray[tmpBufArraySize++] = psBuf;
}
}
else
{
zm_msg0_mm(ZM_LV_0, "Free garbage PS frame");
/* Free garbage PS frame */
zfApRemoveFromPsQueue(dev, id, dst);
zfwBufFree(dev, psBuf, 0);
}
}
zmw_leave_critical_section(dev);
#endif
zfQueueGenerateUapsdTim(dev, wd->ap.uapsdQ, uniBitMap, &highestByte);
zm_msg1_mm(ZM_LV_3, "bm=",uniBitMap[0]);
zm_msg1_mm(ZM_LV_3, "highestByte=",highestByte);
zm_msg1_mm(ZM_LV_3, "timBcmcBit[]=",wd->ap.timBcmcBit[vap]);
/* bitmap */
zmw_tx_buf_writeb(dev, buf, offset++,
uniBitMap[0] | wd->ap.timBcmcBit[vap]);
for (i=0; i<highestByte; i++)
{
zmw_tx_buf_writeb(dev, buf, offset++, uniBitMap[i+1]);
}
/* Element Length */
zmw_tx_buf_writeb(dev, buf, lenOffset, highestByte+4);
for (i=0; i<tmpBufArraySize; i++)
{
/* Put to VTXQ[ac] */
zfPutVtxq(dev, tmpBufArray[i]);
}
/* Push VTXQ[ac] */
zfPushVtxq(dev);
return offset;
}
/************************************************************************/
/* */
/* FUNCTION DESCRIPTION zfApRemoveFromPsQueue */
/* Remove zbuf from PS queue. */
/* */
/* INPUTS */
/* dev : device pointer */
/* id : index in ps queue */
/* */
/* OUTPUTS */
/* more data bit */
/* */
/* AUTHOR */
/* Stephen Chen Atheros Communications, INC. 2007.1 */
/* */
/************************************************************************/
u8_t zfApRemoveFromPsQueue(zdev_t* dev, u16_t id, u16_t* addr)
{
u16_t dst[3];
u16_t nid;
u8_t moreData = 0;
zmw_get_wlan_dev(dev);
wd->ap.uniTail = (wd->ap.uniTail-1) & (ZM_UNI_ARRAY_SIZE-1);
while (id != wd->ap.uniTail)
{
nid = (id + 1) & (ZM_UNI_ARRAY_SIZE - 1);
wd->ap.uniArray[id] = wd->ap.uniArray[nid];
/* Search until tail to config more data bit */
dst[0] = zmw_buf_readh(dev, wd->ap.uniArray[id], 0);
dst[1] = zmw_buf_readh(dev, wd->ap.uniArray[id], 2);
dst[2] = zmw_buf_readh(dev, wd->ap.uniArray[id], 4);
if ((addr[0] == dst[0]) && (addr[1] == dst[1])
&& (addr[2] == dst[2]))
{
moreData = 0x20;
}
id = nid;
}
return moreData;
}
/************************************************************************/
/* */
/* FUNCTION DESCRIPTION zfApAddIeWmePara */
/* Add WME Parameter Element to buffer. */
/* */
/* INPUTS */
/* dev : device pointer */
/* buf : buffer to add information element */
/* offset : add information element from this offset */
/* vap : virtual AP ID */
/* */
/* OUTPUTS */
/* buffer offset after adding information element */
/* */
/* AUTHOR */
/* Stephen Chen ZyDAS Technology Corporation 2006.1 */
/* */
/************************************************************************/
u16_t zfApAddIeWmePara(zdev_t* dev, zbuf_t* buf, u16_t offset, u16_t vap)
{
zmw_get_wlan_dev(dev);
/* Element ID */
zmw_tx_buf_writeb(dev, buf, offset++, ZM_WLAN_EID_WIFI_IE);
/* Element Length */
zmw_tx_buf_writeb(dev, buf, offset++, 24);
/* OUI */
zmw_tx_buf_writeb(dev, buf, offset++, 0x00);
zmw_tx_buf_writeb(dev, buf, offset++, 0x50);
zmw_tx_buf_writeb(dev, buf, offset++, 0xF2);
zmw_tx_buf_writeb(dev, buf, offset++, 0x02);
zmw_tx_buf_writeb(dev, buf, offset++, 0x01);
zmw_tx_buf_writeb(dev, buf, offset++, 0x01);
/* QoS Info */
if (wd->ap.uapsdEnabled)
{
zmw_tx_buf_writeb(dev, buf, offset++, 0x81);
}
else
{
zmw_tx_buf_writeb(dev, buf, offset++, 0x01);
}
/* Reserved */
zmw_tx_buf_writeb(dev, buf, offset++, 0x00);
/* Best Effort AC parameters */
zmw_tx_buf_writeb(dev, buf, offset++, 0x03);
zmw_tx_buf_writeb(dev, buf, offset++, 0xA4);
zmw_tx_buf_writeb(dev, buf, offset++, 0x00);
zmw_tx_buf_writeb(dev, buf, offset++, 0x00);
/* Backfround AC parameters */
zmw_tx_buf_writeb(dev, buf, offset++, 0x27);
zmw_tx_buf_writeb(dev, buf, offset++, 0xA4);
zmw_tx_buf_writeb(dev, buf, offset++, 0x00);
zmw_tx_buf_writeb(dev, buf, offset++, 0x00);
/* Video AC parameters */
zmw_tx_buf_writeb(dev, buf, offset++, 0x42);
zmw_tx_buf_writeb(dev, buf, offset++, 0x43);
zmw_tx_buf_writeb(dev, buf, offset++, 0x5E);
zmw_tx_buf_writeb(dev, buf, offset++, 0x00);
/* Voice AC parameters */
zmw_tx_buf_writeb(dev, buf, offset++, 0x62);
zmw_tx_buf_writeb(dev, buf, offset++, 0x32);
zmw_tx_buf_writeb(dev, buf, offset++, 0x2F);
zmw_tx_buf_writeb(dev, buf, offset++, 0x00);
return offset;
}
/************************************************************************/
/* */
/* FUNCTION DESCRIPTION zfApSendBeacon */
/* Sned AP mode beacon. */
/* */
/* INPUTS */
/* dev : device pointer */
/* */
/* OUTPUTS */
/* none */
/* */
/* AUTHOR */
/* Stephen Chen ZyDAS Technology Corporation 2005.11 */
/* */
/************************************************************************/
void zfApSendBeacon(zdev_t* dev)
{
zbuf_t* buf;
u16_t offset;
u16_t vap;
u16_t seq;
zmw_get_wlan_dev(dev);
zmw_declare_for_critical_section();
wd->ap.beaconCounter++;
if (wd->ap.beaconCounter >= wd->ap.vapNumber)
{
wd->ap.beaconCounter = 0;
}
vap = wd->ap.beaconCounter;
zm_msg1_mm(ZM_LV_2, "Send beacon, vap=", vap);
/* TBD : Maximum size of beacon */
if ((buf = zfwBufAllocate(dev, 1024)) == NULL)
{
zm_msg0_mm(ZM_LV_0, "Alloc beacon buf Fail!");
return;
}
offset = 0;
/* wlan header */
/* Frame control */
zmw_tx_buf_writeh(dev, buf, offset, 0x0080);
offset+=2;
/* Duration */
zmw_tx_buf_writeh(dev, buf, offset, 0x0000);
offset+=2;
/* Address 1 */
zmw_tx_buf_writeh(dev, buf, offset, 0xffff);
offset+=2;
zmw_tx_buf_writeh(dev, buf, offset, 0xffff);
offset+=2;
zmw_tx_buf_writeh(dev, buf, offset, 0xffff);
offset+=2;
/* Address 2 */
zmw_tx_buf_writeh(dev, buf, offset, wd->macAddr[0]);
offset+=2;
zmw_tx_buf_writeh(dev, buf, offset, wd->macAddr[1]);
offset+=2;
#ifdef ZM_VAPMODE_MULTILE_SSID
zmw_tx_buf_writeh(dev, buf, offset, wd->macAddr[2]); //Multiple SSID
#else
zmw_tx_buf_writeh(dev, buf, offset, (wd->macAddr[2]+(vap<<8))); //VAP
#endif
offset+=2;
/* Address 3 */
zmw_tx_buf_writeh(dev, buf, offset, wd->macAddr[0]);
offset+=2;
zmw_tx_buf_writeh(dev, buf, offset, wd->macAddr[1]);
offset+=2;
#ifdef ZM_VAPMODE_MULTILE_SSID
zmw_tx_buf_writeh(dev, buf, offset, wd->macAddr[2]); //Multiple SSID
#else
zmw_tx_buf_writeh(dev, buf, offset, (wd->macAddr[2]+(vap<<8))); //VAP
#endif
offset+=2;
/* Sequence number */
zmw_enter_critical_section(dev);
seq = ((wd->mmseq++)<<4);
zmw_leave_critical_section(dev);
zmw_tx_buf_writeh(dev, buf, offset, seq);
offset+=2;
/* 24-31 Time Stamp : hardware will fill this field */
zmw_tx_buf_writeh(dev, buf, offset, 0);
zmw_tx_buf_writeh(dev, buf, offset+2, 0);
zmw_tx_buf_writeh(dev, buf, offset+4, 0);
zmw_tx_buf_writeh(dev, buf, offset+6, 0);
offset+=8;
/* Beacon Interval */
zmw_tx_buf_writeh(dev, buf, offset, wd->beaconInterval);
offset+=2;
/* Capability */
zmw_tx_buf_writeh(dev, buf, offset, wd->ap.capab[vap]);
offset+=2;
/* SSID */
if (wd->ap.hideSsid[vap] == 0)
{
offset = zfApAddIeSsid(dev, buf, offset, vap);
}
else
{
zmw_tx_buf_writeb(dev, buf, offset++, ZM_WLAN_EID_SSID);
zmw_tx_buf_writeb(dev, buf, offset++, 0);
}
/* Support Rate */
if ( wd->frequency < 3000 )
{
offset = zfMmAddIeSupportRate(dev, buf, offset,
ZM_WLAN_EID_SUPPORT_RATE, ZM_RATE_SET_CCK);
}
else
{
offset = zfMmAddIeSupportRate(dev, buf, offset,
ZM_WLAN_EID_SUPPORT_RATE, ZM_RATE_SET_OFDM);
}
/* DS parameter set */
offset = zfMmAddIeDs(dev, buf, offset);
/* TIM */
offset = zfApAddIeTim(dev, buf, offset, vap);
/* If WLAN Type is not PURE B */
if (wd->ap.wlanType[vap] != ZM_WLAN_TYPE_PURE_B)
{
if ( wd->frequency < 3000 )
{
/* ERP Information */
offset = zfMmAddIeErp(dev, buf, offset);
/* Extended Supported Rates */
offset = zfMmAddIeSupportRate(dev, buf, offset,
ZM_WLAN_EID_EXTENDED_RATE, ZM_RATE_SET_OFDM);
}
}
/* TODO : country information */
/* TODO : RSN */
if (wd->ap.wpaSupport[vap] == 1)
{
offset = zfMmAddIeWpa(dev, buf, offset, vap);
}
/* WME Parameters */
if (wd->ap.qosMode == 1)
{
offset = zfApAddIeWmePara(dev, buf, offset, vap);
}
/* HT Capabilities Info */
offset = zfMmAddHTCapability(dev, buf, offset);
/* Extended HT Capabilities Info */
offset = zfMmAddExtendedHTCapability(dev, buf, offset);
/* 1212 : write to beacon fifo */
/* 1221 : write to share memory */
zfHpSendBeacon(dev, buf, offset);
/* Free beacon buffer */
/* TODO: In order to fit the madwifi beacon architecture, we need to
free beacon buffer in the HAL layer.
*/
//zfwBufFree(dev, buf, 0);
}
/************************************************************************/
/* */
/* FUNCTION DESCRIPTION zfIntrabssForward */
/* Called to transmit intra-BSS frame from upper layer. */
/* */
/* INPUTS */
/* dev : device pointer */
/* buf : buffer pointer */
/* vap : virtual AP */
/* */
/* OUTPUTS */
/* 1 : unicast intras-BSS frame */
/* 0 : other frames */
/* */
/* AUTHOR */
/* Stephen ZyDAS Technology Corporation 2005.11 */
/* */
/************************************************************************/
u16_t zfIntrabssForward(zdev_t* dev, zbuf_t* buf, u8_t srcVap)
{
u16_t err;
u16_t asocFlag = 0;
u16_t dst[3];
u16_t aid;
u16_t staState;
zbuf_t* txBuf;
u16_t len;
u16_t i;
u16_t temp;
u16_t ret;
u8_t vap = 0;
#ifdef ZM_ENABLE_NATIVE_WIFI
dst[0] = zmw_rx_buf_readh(dev, buf, ZM_WLAN_HEADER_A3_OFFSET);
dst[1] = zmw_rx_buf_readh(dev, buf, ZM_WLAN_HEADER_A3_OFFSET+2);
dst[2] = zmw_rx_buf_readh(dev, buf, ZM_WLAN_HEADER_A3_OFFSET+4);
#else
dst[0] = zmw_rx_buf_readh(dev, buf, 0);
dst[1] = zmw_rx_buf_readh(dev, buf, 2);
dst[2] = zmw_rx_buf_readh(dev, buf, 4);
#endif // ZM_ENABLE_NATIVE_WIFI
/* Do Intra-BSS forward(data copy) if necessary*/
if ((dst[0]&0x1) != 0x1)
{
aid = zfApGetSTAInfo(dev, dst, &staState, &vap);
if ((aid != 0xffff) && (staState == ZM_STATE_ASOC) && (srcVap == vap))
{
asocFlag = 1;
zm_msg0_rx(ZM_LV_2, "Intra-BSS forward : asoc STA");
}
}
else
{
vap = srcVap;
zm_msg0_rx(ZM_LV_2, "Intra-BSS forward : BCorMC");
}
/* destination address = associated STA or BC/MC */
if ((asocFlag == 1) || ((dst[0]&0x1) == 0x1))
{
/* Allocate frame */
if ((txBuf = zfwBufAllocate(dev, ZM_RX_FRAME_SIZE))
== NULL)
{
zm_msg0_rx(ZM_LV_1, "Alloc intra-bss buf Fail!");
goto zlAllocError;
}
/* Copy frame */
len = zfwBufGetSize(dev, buf);
for (i=0; i<len; i+=2)
{
temp = zmw_rx_buf_readh(dev, buf, i);
zmw_tx_buf_writeh(dev, txBuf, i, temp);
}
zfwBufSetSize(dev, txBuf, len);
#ifdef ZM_ENABLE_NATIVE_WIFI
/* Tx-A2 = Rx-A1, Tx-A3 = Rx-A2, Tx-A1 = Rx-A3 */
for (i=0; i<6; i+=2)
{
temp = zmw_rx_buf_readh(dev, buf, ZM_WLAN_HEADER_A1_OFFSET+i);
zmw_tx_buf_writeh(dev, txBuf, ZM_WLAN_HEADER_A2_OFFSET+i, temp);
temp = zmw_rx_buf_readh(dev, buf, ZM_WLAN_HEADER_A2_OFFSET+i);
zmw_tx_buf_writeh(dev, txBuf, ZM_WLAN_HEADER_A3_OFFSET+i, temp);
temp = zmw_rx_buf_readh(dev, buf, ZM_WLAN_HEADER_A3_OFFSET+i);
zmw_tx_buf_writeh(dev, txBuf, ZM_WLAN_HEADER_A1_OFFSET+i, temp);
}
#endif
/* Transmit frame */
/* Return error if port is disabled */
if ((err = zfTxPortControl(dev, txBuf, vap)) == ZM_PORT_DISABLED)
{
err = ZM_ERR_TX_PORT_DISABLED;
goto zlTxError;
}
#if 1
/* AP : Buffer frame for power saving STA */
if ((ret = zfApBufferPsFrame(dev, txBuf, vap)) == 0)
{
/* forward frame if not been buffered */
#if 1
/* Put to VTXQ[ac] */
ret = zfPutVtxq(dev, txBuf);
/* Push VTXQ[ac] */
zfPushVtxq(dev);
#else
zfTxSendEth(dev, txBuf, vap, ZM_INTERNAL_ALLOC_BUF, 0);
#endif
}
#endif
}
return asocFlag;
zlTxError:
zfwBufFree(dev, txBuf, 0);
zlAllocError:
return asocFlag;
}
struct zsMicVar* zfApGetRxMicKey(zdev_t* dev, zbuf_t* buf)
{
u8_t sa[6];
u16_t id = 0, macAddr[3];
zmw_get_wlan_dev(dev);
zfCopyFromRxBuffer(dev, buf, sa, ZM_WLAN_HEADER_A2_OFFSET, 6);
macAddr[0] = sa[0] + (sa[1] << 8);
macAddr[1] = sa[2] + (sa[3] << 8);
macAddr[2] = sa[4] + (sa[5] << 8);
if ((id = zfApFindSta(dev, macAddr)) != 0xffff)
return (&wd->ap.staTable[id].rxMicKey);
return NULL;
}
struct zsMicVar* zfApGetTxMicKey(zdev_t* dev, zbuf_t* buf, u8_t* qosType)
{
u8_t da[6];
u16_t id = 0, macAddr[3];
zmw_get_wlan_dev(dev);
zfCopyFromIntTxBuffer(dev, buf, da, 0, 6);
macAddr[0] = da[0] + (da[1] << 8);
macAddr[1] = da[2] + (da[3] << 8);
macAddr[2] = da[4] + (da[5] << 8);
if ((macAddr[0] & 0x1))
{
return (&wd->ap.bcMicKey[0]);
}
else if ((id = zfApFindSta(dev, macAddr)) != 0xffff)
{
*qosType = wd->ap.staTable[id].qosType;
return (&wd->ap.staTable[id].txMicKey);
}
return NULL;
}
u16_t zfApUpdatePsBit(zdev_t* dev, zbuf_t* buf, u8_t* vap, u8_t* uapsdTrig)
{
u16_t staState;
u16_t aid;
u16_t psBit;
u16_t src[3];
u16_t dst[1];
u16_t i;
zmw_get_wlan_dev(dev);
src[0] = zmw_rx_buf_readh(dev, buf, 10);
src[1] = zmw_rx_buf_readh(dev, buf, 12);
src[2] = zmw_rx_buf_readh(dev, buf, 14);
if ((zmw_rx_buf_readb(dev, buf, 1) & 0x3) != 3)
{
/* AP */
dst[0] = zmw_rx_buf_readh(dev, buf, 4);
psBit = (zmw_rx_buf_readb(dev, buf, 1) & 0x10) >> 4;
/* Get AID and update STA PS mode */
aid = zfApGetSTAInfoAndUpdatePs(dev, src, &staState, vap, psBit, uapsdTrig);
/* if STA not associated, send deauth */
if ((aid == 0xffff) || (staState != ZM_STATE_ASOC))
{
if ((dst[0]&0x1)==0)
{
zfSendMmFrame(dev, ZM_WLAN_FRAME_TYPE_DEAUTH, src, 0x7,
0, 0);
}
return ZM_ERR_STA_NOT_ASSOCIATED;
}
} /* if ((zmw_rx_buf_readb(dev, buf, 1) & 0x3) != 3) */
else
{
/* WDS */
for (i=0; i<ZM_MAX_WDS_SUPPORT; i++)
{
if ((wd->ap.wds.wdsBitmap & (1<<i)) != 0)
{
if ((src[0] == wd->ap.wds.macAddr[i][0])
&& (src[1] == wd->ap.wds.macAddr[i][1])
&& (src[2] == wd->ap.wds.macAddr[i][2]))
{
*vap = 0x20 + i;
break;
}
}
}
}
return ZM_SUCCESS;
}
void zfApProcessPsPoll(zdev_t* dev, zbuf_t* buf)
{
u16_t src[3];
u16_t dst[3];
zbuf_t* psBuf = NULL;
u16_t id;
u8_t moreData = 0;
zmw_get_wlan_dev(dev);
zmw_declare_for_critical_section();
src[0] = zmw_tx_buf_readh(dev, buf, 10);
src[1] = zmw_tx_buf_readh(dev, buf, 12);
src[2] = zmw_tx_buf_readh(dev, buf, 14);
/* Find ps buffer for PsPoll */
zmw_enter_critical_section(dev);
id = wd->ap.uniHead;
while (id != wd->ap.uniTail)
{
psBuf = wd->ap.uniArray[id];
dst[0] = zmw_tx_buf_readh(dev, psBuf, 0);
dst[1] = zmw_tx_buf_readh(dev, psBuf, 2);
dst[2] = zmw_tx_buf_readh(dev, psBuf, 4);
if ((src[0] == dst[0]) && (src[1] == dst[1]) && (src[2] == dst[2]))
{
moreData = zfApRemoveFromPsQueue(dev, id, src);
break;
}
else
{
psBuf = NULL;
}
id = (id + 1) & (ZM_UNI_ARRAY_SIZE - 1);
}
zmw_leave_critical_section(dev);
/* Send ps buffer */
if (psBuf != NULL)
{
/* Send with more data bit */
zfTxSendEth(dev, psBuf, 0, ZM_EXTERNAL_ALLOC_BUF, moreData);
}
return;
}
void zfApSetProtectionMode(zdev_t* dev, u16_t mode)
{
zmw_get_wlan_dev(dev);
if (mode == 0)
{
if (wd->ap.protectionMode != mode)
{
/* Write MAC&PHY registers to disable protection */
wd->ap.protectionMode = mode;
}
}
else
{
if (wd->ap.protectionMode != mode)
{
/* Write MAC&PHY registers to enable protection */
wd->ap.protectionMode = mode;
}
}
return;
}
/************************************************************************/
/* */
/* FUNCTION DESCRIPTION zfApSendFailure */
/* Send failure. */
/* */
/* INPUTS */
/* dev : device pointer */
/* addr : receiver address */
/* */
/* OUTPUTS */
/* None */
/* */
/* AUTHOR */
/* Stephen Chen Atheros Communications, INC. 2007.1 */
/* */
/************************************************************************/
void zfApSendFailure(zdev_t* dev, u8_t* addr)
{
u16_t id;
u16_t staAddr[3];
zmw_get_wlan_dev(dev);
zmw_declare_for_critical_section();
staAddr[0] = addr[0] + (((u16_t)addr[1])<<8);
staAddr[1] = addr[2] + (((u16_t)addr[3])<<8);
staAddr[2] = addr[4] + (((u16_t)addr[5])<<8);
zmw_enter_critical_section(dev);
if ((id = zfApFindSta(dev, staAddr)) != 0xffff)
{
/* Send failture : Add 3 minutes to inactive time that will */
/* will make STA been kicked out soon */
wd->ap.staTable[id].time -= (3*ZM_TICK_PER_MINUTE);
}
zmw_leave_critical_section(dev);
}
void zfApProcessAction(zdev_t* dev, zbuf_t* buf)
{
u8_t category;
//zmw_get_wlan_dev(dev);
//zmw_declare_for_critical_section();
category = zmw_rx_buf_readb(dev, buf, 24);
switch (category)
{
case ZM_WLAN_BLOCK_ACK_ACTION_FRAME:
zfAggBlockAckActionFrame(dev, buf);
break;
default:
break;
}
return;
}