diff options
author | merge <null@invalid> | 2009-01-22 13:55:32 +0000 |
---|---|---|
committer | Andy Green <agreen@octopus.localdomain> | 2009-01-22 13:55:32 +0000 |
commit | aa6f5ffbdba45aa8e19e5048648fc6c7b25376d3 (patch) | |
tree | fbb786d0ac6f8a774fd834e9ce951197e60fbffa /drivers/staging/otus/80211core/cagg.c | |
parent | f2d78193eae5dccd3d588d2c8ea0866efc368332 (diff) |
MERGE-via-pending-tracking-hist-MERGE-via-stable-tracking-MERGE-via-mokopatches-tracking-fix-stray-endmenu-patch-1232632040-1232632141
pending-tracking-hist top was MERGE-via-stable-tracking-MERGE-via-mokopatches-tracking-fix-stray-endmenu-patch-1232632040-1232632141 / fdf777a63bcb59e0dfd78bfe2c6242e01f6d4eb9 ... parent commitmessage:
From: merge <null@invalid>
MERGE-via-stable-tracking-hist-MERGE-via-mokopatches-tracking-fix-stray-endmenu-patch-1232632040
stable-tracking-hist top was MERGE-via-mokopatches-tracking-fix-stray-endmenu-patch-1232632040 / 90463bfd2d5a3c8b52f6e6d71024a00e052b0ced ... parent commitmessage:
From: merge <null@invalid>
MERGE-via-mokopatches-tracking-hist-fix-stray-endmenu-patch
mokopatches-tracking-hist top was fix-stray-endmenu-patch / 3630e0be570de8057e7f8d2fe501ed353cdf34e6 ... parent commitmessage:
From: Andy Green <andy@openmoko.com>
fix-stray-endmenu.patch
Signed-off-by: Andy Green <andy@openmoko.com>
Diffstat (limited to 'drivers/staging/otus/80211core/cagg.c')
-rw-r--r-- | drivers/staging/otus/80211core/cagg.c | 3611 |
1 files changed, 3611 insertions, 0 deletions
diff --git a/drivers/staging/otus/80211core/cagg.c b/drivers/staging/otus/80211core/cagg.c new file mode 100644 index 00000000000..4942190747a --- /dev/null +++ b/drivers/staging/otus/80211core/cagg.c @@ -0,0 +1,3611 @@ +/* + * 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 : cagg.c */ +/* */ +/* Abstract */ +/* This module contains A-MPDU aggregation related functions. */ +/* */ +/* NOTES */ +/* None */ +/* */ +/************************************************************************/ + +#include "cprecomp.h" + +extern u8_t zcUpToAc[8]; +const u8_t pri[] = {3,3,2,3,2,1,3,2,1,0}; + + +u16_t aggr_count; +u32_t success_mpdu; +u32_t total_mpdu; + +void zfAggInit(zdev_t* dev) +{ + u16_t i,j; + + zmw_get_wlan_dev(dev); + + zmw_declare_for_critical_section(); + /* + * reset sta information + */ + + zmw_enter_critical_section(dev); + wd->aggInitiated = 0; + wd->addbaComplete = 0; + wd->addbaCount = 0; + wd->reorder = 1; + for (i=0; i<ZM_MAX_STA_SUPPORT; i++) + { + for (j=0; j<ZM_AC; j++) + { + //wd->aggSta[i].aggQNumber[j] = ZM_AGG_POOL_SIZE; + wd->aggSta[i].aggFlag[j] = wd->aggSta[i].count[j] = 0; + wd->aggSta[i].tid_tx[j] = NULL; + wd->aggSta[i].tid_tx[j+1] = NULL; + + } + } + + /* + * reset Tx/Rx aggregation queue information + */ + wd->aggState = 0; + for (i=0; i<ZM_AGG_POOL_SIZE; i++) + { + /* + * reset tx aggregation queue + */ + wd->aggQPool[i] = zfwMemAllocate(dev, sizeof(struct aggQueue)); + if(!wd->aggQPool[i]) + { + zmw_leave_critical_section(dev); + return; + } + wd->aggQPool[i]->aggHead = wd->aggQPool[i]->aggTail = + wd->aggQPool[i]->aggQEnabled = wd->aggQPool[i]->aggReady = + wd->aggQPool[i]->clearFlag = wd->aggQPool[i]->deleteFlag = 0; + //wd->aggQPool[i]->aggSize = 16; + + /* + * reset rx aggregation queue + */ + wd->tid_rx[i] = zfwMemAllocate(dev, sizeof(struct agg_tid_rx)); + if (!wd->tid_rx[i]) + { + zmw_leave_critical_section(dev); + return; + } + wd->tid_rx[i]->aid = ZM_MAX_STA_SUPPORT; + wd->tid_rx[i]->seq_start = wd->tid_rx[i]->baw_head = \ + wd->tid_rx[i]->baw_tail = 0; + wd->tid_rx[i]->sq_exceed_count = wd->tid_rx[i]->sq_behind_count = 0; + for (j=0; j<=ZM_AGG_BAW_SIZE; j++) + wd->tid_rx[i]->frame[j].buf = 0; + /* + * reset ADDBA exchange status code + * 0: NULL + * 1: ADDBA Request sent/received + * 2: ACK for ADDBA Request sent/received + * 3: ADDBA Response sent/received + * 4: ACK for ADDBA Response sent/received + */ + wd->tid_rx[i]->addBaExchangeStatusCode = 0; + + } + zmw_leave_critical_section(dev); + zfAggTallyReset(dev); + DESTQ.init = zfAggDestInit; + DESTQ.init(dev); + wd->aggInitiated = 1; + aggr_count = 0; + success_mpdu = 0; + total_mpdu = 0; +#ifdef ZM_ENABLE_AGGREGATION +#ifndef ZM_ENABLE_FW_BA_RETRANSMISSION //disable BAW + BAW = zfwMemAllocate(dev, sizeof(struct baw_enabler)); + if(!BAW) + { + return; + } + BAW->init = zfBawInit; + BAW->init(dev); +#endif //disable BAW +#endif +} + +/************************************************************************/ +/* */ +/* FUNCTION DESCRIPTION zfAggGetSta */ +/* return STA AID. */ +/* take buf as input, use the dest address of buf as index to */ +/* search STA AID. */ +/* */ +/* INPUTS */ +/* dev : device pointer */ +/* buf : buffer for one particular packet */ +/* */ +/* OUTPUTS */ +/* AID */ +/* */ +/* AUTHOR */ +/* Honda ZyDAS Technology Corporation 2006.11 */ +/* */ +/************************************************************************/ + + + +u16_t zfAggGetSta(zdev_t* dev, zbuf_t* buf) +{ + u16_t id; + u16_t dst[3]; + + zmw_get_wlan_dev(dev); + + zmw_declare_for_critical_section(); + + 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); + + zmw_enter_critical_section(dev); + + if(wd->wlanMode == ZM_MODE_AP) { + id = zfApFindSta(dev, dst); + } + else { + id = 0; + } + zmw_leave_critical_section(dev); + +#if ZM_AGG_FPGA_DEBUG + id = 0; +#endif + + return id; +} + + +/************************************************************************/ +/* */ +/* FUNCTION DESCRIPTION zfAggTxGetQueue */ +/* return Queue Pool index. */ +/* take aid as input, look for the queue index associated */ +/* with this aid. */ +/* */ +/* INPUTS */ +/* dev : device pointer */ +/* aid : associated id */ +/* */ +/* OUTPUTS */ +/* Queue number */ +/* */ +/* AUTHOR */ +/* Honda ZyDAS Technology Corporation 2006.11 */ +/* */ +/************************************************************************/ +TID_TX zfAggTxGetQueue(zdev_t* dev, u16_t aid, u16_t tid) +{ + //u16_t i; + TID_TX tid_tx; + zmw_get_wlan_dev(dev); + + //zmw_declare_for_critical_section(); + + /* + * not a STA aid + */ + if (0xffff == aid) + return NULL; + + //zmw_enter_critical_section(dev); + + tid_tx = wd->aggSta[aid].tid_tx[tid]; + if (!tid_tx) return NULL; + if (0 == tid_tx->aggQEnabled) + return NULL; + + //zmw_leave_critical_section(dev); + + return tid_tx; +} + +/************************************************************************/ +/* */ +/* FUNCTION DESCRIPTION zfAggTxNewQueue */ +/* return Queue Pool index. */ +/* take aid as input, find a new queue for this aid. */ +/* */ +/* INPUTS */ +/* dev : device pointer */ +/* aid : associated id */ +/* */ +/* OUTPUTS */ +/* Queue number */ +/* */ +/* AUTHOR */ +/* Honda ZyDAS Technology Corporation 2006.12 */ +/* */ +/************************************************************************/ +TID_TX zfAggTxNewQueue(zdev_t* dev, u16_t aid, u16_t tid, zbuf_t* buf) +{ + u16_t i; + TID_TX tid_tx=NULL; + u16_t ac = zcUpToAc[tid&0x7] & 0x3; + zmw_get_wlan_dev(dev); + + zmw_declare_for_critical_section(); + + /* + * not a STA aid + */ + if (0xffff == aid) + return NULL; + + zmw_enter_critical_section(dev); + + /* + * find one new queue for sta + */ + for (i=0; i<ZM_AGG_POOL_SIZE; i++) + { + if (wd->aggQPool[i]->aggQEnabled) + { + /* + * this q is enabled + */ + } + else + { + tid_tx = wd->aggQPool[i]; + tid_tx->aggQEnabled = 1; + tid_tx->aggQSTA = aid; + tid_tx->ac = ac; + tid_tx->tid = tid; + tid_tx->aggHead = tid_tx->aggTail = tid_tx->size = 0; + tid_tx->aggReady = 0; + wd->aggSta[aid].tid_tx[tid] = tid_tx; + tid_tx->dst[0] = zmw_rx_buf_readh(dev, buf, 0); + tid_tx->dst[1] = zmw_rx_buf_readh(dev, buf, 2); + tid_tx->dst[2] = zmw_rx_buf_readh(dev, buf, 4); + break; + } + } + + zmw_leave_critical_section(dev); + + return tid_tx; +} + + + +/************************************************************************/ +/* */ +/* FUNCTION DESCRIPTION zfAggTxEnqueue */ +/* return Status code ZM_SUCCESS or error code */ +/* take (aid,ac,qnum,buf) as input */ +/* */ +/* INPUTS */ +/* dev : device pointer */ +/* aid : associated id */ +/* ac : access category */ +/* qnum: the queue number to which will be enqueued */ +/* buf : the packet to be queued */ +/* */ +/* OUTPUTS */ +/* status code */ +/* */ +/* AUTHOR */ +/* Honda Atheros Communications, INC. 2006.12 */ +/* */ +/************************************************************************/ +u16_t zfAggTxEnqueue(zdev_t* dev, zbuf_t* buf, u16_t aid, TID_TX tid_tx) +{ + //u16_t qlen, frameLen; + u32_t time; + + zmw_get_wlan_dev(dev); + + zmw_declare_for_critical_section(); + + zmw_enter_critical_section(dev); + + tid_tx->size = zm_agg_qlen(dev, tid_tx->aggHead, tid_tx->aggTail); + + if (tid_tx->size < (ZM_AGGQ_SIZE - 2)) + { + /* Queue not full */ + + + /* + * buffer copy + * in zfwBufFree will return a ndismsendcomplete + * to resolve the synchronize problem in aggregate + */ + + u8_t sendComplete = 0; + + tid_tx->aggvtxq[tid_tx->aggHead].buf = buf; + time = zm_agg_GetTime(); + tid_tx->aggvtxq[tid_tx->aggHead].arrivalTime = time; + tid_tx->aggvtxq[tid_tx->aggHead].baw_retransmit = 0; + + tid_tx->aggHead = ((tid_tx->aggHead + 1) & ZM_AGGQ_SIZE_MASK); + tid_tx->lastArrival = time; + tid_tx->size++; + tid_tx->size = zm_agg_qlen(dev, tid_tx->aggHead, tid_tx->aggTail); + if (buf && (tid_tx->size < (ZM_AGGQ_SIZE - 10))) { + tid_tx->complete = tid_tx->aggHead; + sendComplete = 1; + } + zmw_leave_critical_section(dev); + + if (!DESTQ.exist(dev, 0, tid_tx->ac, tid_tx, NULL)) { + DESTQ.insert(dev, 0, tid_tx->ac, tid_tx, NULL); + } + + zm_msg1_agg(ZM_LV_0, "tid_tx->size=", tid_tx->size); + //zm_debug_msg1("tid_tx->size=", tid_tx->size); + + if (buf && sendComplete && wd->zfcbSendCompleteIndication) { + //zmw_leave_critical_section(dev); + wd->zfcbSendCompleteIndication(dev, buf); + } + + /*if (tid_tx->size >= 16 && zfHpGetFreeTxdCount(dev) > 20) + zfAggTxSend(dev, zfHpGetFreeTxdCount(dev), tid_tx); + */ + return ZM_SUCCESS; + } + else + { + zm_msg1_agg(ZM_LV_0, "can't enqueue, tid_tx->size=", tid_tx->size); + /* + * Queue Full + */ + + /* + * zm_msg1_agg(ZM_LV_0, "Queue full, qnum = ", qnum); + * wd->commTally.txQosDropCount[ac]++; + * zfwBufFree(dev, buf, ZM_SUCCESS); + * zm_msg1_agg(ZM_LV_1, "Packet discarded, VTXQ full, ac=", ac); + * + * return ZM_ERR_EXCEED_PRIORITY_THRESHOLD; + */ + } + + zmw_leave_critical_section(dev); + + if (!DESTQ.exist(dev, 0, tid_tx->ac, tid_tx, NULL)) { + DESTQ.insert(dev, 0, tid_tx->ac, tid_tx, NULL); + } + + return ZM_ERR_EXCEED_PRIORITY_THRESHOLD; +} + +u16_t zfAggDestExist(zdev_t* dev, u16_t Qtype, u16_t ac, TID_TX tid_tx, void* vtxq) { + struct dest* dest; + u16_t exist = 0; + zmw_get_wlan_dev(dev); + + zmw_declare_for_critical_section(); + + zmw_enter_critical_section(dev); + if (!DESTQ.Head[ac]) { + exist = 0; + } + else { + dest = DESTQ.Head[ac]; + if (dest->tid_tx == tid_tx) { + exist = 1; + } + else { + while (dest->next != DESTQ.Head[ac]) { + dest = dest->next; + if (dest->tid_tx == tid_tx){ + exist = 1; + break; + } + } + } + } + + zmw_leave_critical_section(dev); + + return exist; +} + +void zfAggDestInsert(zdev_t* dev, u16_t Qtype, u16_t ac, TID_TX tid_tx, void* vtxq) +{ + struct dest* new_dest; + zmw_get_wlan_dev(dev); + + zmw_declare_for_critical_section(); + + new_dest = zfwMemAllocate(dev, sizeof(struct dest)); + if(!new_dest) + { + return; + } + new_dest->Qtype = Qtype; + new_dest->tid_tx = tid_tx; + if (0 == Qtype) + new_dest->tid_tx = tid_tx; + else + new_dest->vtxq = vtxq; + if (!DESTQ.Head[ac]) { + + zmw_enter_critical_section(dev); + new_dest->next = new_dest; + DESTQ.Head[ac] = DESTQ.dest[ac] = new_dest; + zmw_leave_critical_section(dev); + } + else { + + zmw_enter_critical_section(dev); + new_dest->next = DESTQ.dest[ac]->next; + DESTQ.dest[ac]->next = new_dest; + zmw_leave_critical_section(dev); + } + + + //DESTQ.size[ac]++; + return; +} + +void zfAggDestDelete(zdev_t* dev, u16_t Qtype, TID_TX tid_tx, void* vtxq) +{ + struct dest* dest, *temp; + u16_t i; + + zmw_get_wlan_dev(dev); + + zmw_declare_for_critical_section(); + + zmw_enter_critical_section(dev); + if (wd->destLock) { + zmw_leave_critical_section(dev); + return; + } + + + //zmw_declare_for_critical_section(); + for (i=0; i<4; i++) { + if (!DESTQ.Head[i]) continue; + dest = DESTQ.Head[i]; + if (!dest) continue; + + + while (dest && (dest->next != DESTQ.Head[i])) { + if (Qtype == 0 && dest->next->tid_tx == tid_tx){ + break; + } + if (Qtype == 1 && dest->next->vtxq == vtxq) { + break; + } + dest = dest->next; + } + + if ((Qtype == 0 && dest->next->tid_tx == tid_tx) || (Qtype == 1 && dest->next->vtxq == vtxq)) { + + tid_tx->size = zm_agg_qlen(dev, tid_tx->aggHead, tid_tx->aggTail); + if (tid_tx->size) { + zmw_leave_critical_section(dev); + return; + } + if (!DESTQ.Head[i]) { + temp = NULL; + } + else { + temp = dest->next; + if (temp == dest) { + DESTQ.Head[i] = DESTQ.dest[i] = NULL; + //DESTQ.size[i] = 0; + } + else { + dest->next = dest->next->next; + } + } + + if (temp == NULL) + {/* do nothing */} //zfwMemFree(dev, temp, sizeof(struct dest)); + else + zfwMemFree(dev, temp, sizeof(struct dest)); + + /*zmw_enter_critical_section(dev); + if (DESTQ.size[i] > 0) + DESTQ.size[i]--; + zmw_leave_critical_section(dev); + */ + } + + } + zmw_leave_critical_section(dev); + return; +} + +void zfAggDestInit(zdev_t* dev) +{ + u16_t i; + zmw_get_wlan_dev(dev); + + //zmw_declare_for_critical_section(); + + for (i=0; i<4; i++) { + //wd->destQ.Head[i].next = wd->destQ.Head[i]; + //wd->destQ.dest[i] = wd->destQ.Head[i]; + //DESTQ.size[i] = 0; + DESTQ.Head[i] = NULL; + } + DESTQ.insert = zfAggDestInsert; + DESTQ.delete = zfAggDestDelete; + DESTQ.init = zfAggDestInit; + DESTQ.getNext = zfAggDestGetNext; + DESTQ.exist = zfAggDestExist; + DESTQ.ppri = 0; + return; +} + +struct dest* zfAggDestGetNext(zdev_t* dev, u16_t ac) +{ + struct dest *dest = NULL; + zmw_get_wlan_dev(dev); + + zmw_declare_for_critical_section(); + + zmw_enter_critical_section(dev); + if (DESTQ.dest[ac]) { + dest = DESTQ.dest[ac]; + DESTQ.dest[ac] = DESTQ.dest[ac]->next; + } + else { + dest = NULL; + } + zmw_leave_critical_section(dev); + + return dest; +} + +#ifdef ZM_ENABLE_AGGREGATION +#ifndef ZM_ENABLE_FW_BA_RETRANSMISSION //disable BAW +u16_t zfAggTidTxInsertHead(zdev_t* dev, struct bufInfo *buf_info,TID_TX tid_tx) +{ + zbuf_t* buf; + u32_t time; + struct baw_header *baw_header; + + zmw_get_wlan_dev(dev); + + zmw_declare_for_critical_section(); + + + buf = buf_info->buf; + + zmw_enter_critical_section(dev); + tid_tx->size = zm_agg_qlen(dev, tid_tx->aggHead, tid_tx->aggTail); + zmw_leave_critical_section(dev); + + if (tid_tx->size >= (ZM_AGGQ_SIZE - 2)) { + zfwBufFree(dev, buf, ZM_SUCCESS); + return 0; + } + + zmw_enter_critical_section(dev); + tid_tx->aggTail = (tid_tx->aggTail == 0)? ZM_AGGQ_SIZE_MASK: tid_tx->aggTail - 1; + tid_tx->aggvtxq[tid_tx->aggTail].buf = buf; + //time = zm_agg_GetTime(); + tid_tx->aggvtxq[tid_tx->aggTail].arrivalTime = buf_info->timestamp; + tid_tx->aggvtxq[tid_tx->aggTail].baw_retransmit = buf_info->baw_retransmit; + + baw_header = &tid_tx->aggvtxq[tid_tx->aggTail].baw_header; + baw_header->headerLen = buf_info->baw_header->headerLen; + baw_header->micLen = buf_info->baw_header->micLen; + baw_header->snapLen = buf_info->baw_header->snapLen; + baw_header->removeLen = buf_info->baw_header->removeLen; + baw_header->keyIdx = buf_info->baw_header->keyIdx; + zfwMemoryCopy((u8_t *)baw_header->header, (u8_t *)buf_info->baw_header->header, 58); + zfwMemoryCopy((u8_t *)baw_header->mic , (u8_t *)buf_info->baw_header->mic , 8); + zfwMemoryCopy((u8_t *)baw_header->snap , (u8_t *)buf_info->baw_header->snap , 8); + + tid_tx->size++; + tid_tx->size = zm_agg_qlen(dev, tid_tx->aggHead, tid_tx->aggTail); + zmw_leave_critical_section(dev); + + //tid_tx->lastArrival = time; + if (1 == tid_tx->size) { + DESTQ.insert(dev, 0, tid_tx->ac, tid_tx, NULL); + } + + + zm_msg1_agg(ZM_LV_0, "0xC2:insertHead, tid_tx->size=", tid_tx->size); + + return TRUE; +} +#endif //disable BAW +#endif + +void zfiTxComplete(zdev_t* dev) +{ + + zmw_get_wlan_dev(dev); + + //zmw_declare_for_critical_section(); + + if( (wd->wlanMode == ZM_MODE_AP) || + (wd->wlanMode == ZM_MODE_INFRASTRUCTURE && wd->sta.EnableHT) || + (wd->wlanMode == ZM_MODE_PSEUDO) ) { + zfAggTxScheduler(dev, 0); + } + + return; +} + +TID_TX zfAggTxReady(zdev_t* dev) { + //struct dest* dest; + u16_t i; + TID_TX tid_tx = NULL; + zmw_get_wlan_dev(dev); + + zmw_declare_for_critical_section(); + + zmw_enter_critical_section(dev); + for (i=0; i<ZM_AGG_POOL_SIZE; i++) + { + if (wd->aggQPool[i]->aggQEnabled) + { + if (wd->aggQPool[i]->size >= 16) { + tid_tx = wd->aggQPool[i]; + break; + } + } + else { + } + } + zmw_leave_critical_section(dev); + return tid_tx; +} + +u16_t zfAggValidTidTx(zdev_t* dev, TID_TX tid_tx) { + u16_t i, valid = 0; + zmw_get_wlan_dev(dev); + + zmw_declare_for_critical_section(); + + zmw_enter_critical_section(dev); + for (i=0; i<ZM_AGG_POOL_SIZE; i++) + { + if (wd->aggQPool[i] == tid_tx) + { + valid = 1; + break; + } + else { + } + } + zmw_leave_critical_section(dev); + + return valid; +} + +void zfAggTxScheduler(zdev_t* dev, u8_t ScanAndClear) +{ + TID_TX tid_tx = NULL; + void* vtxq; + struct dest* dest; + zbuf_t* buf; + u32_t txql, min_txql; + //u16_t aggr_size = 1; + u16_t txq_threshold; + zmw_get_wlan_dev(dev); + + zmw_declare_for_critical_section(); + + if (!wd->aggInitiated) + { + return; + } + + /* debug */ + txql = TXQL; + min_txql = AGG_MIN_TXQL; + + if(wd->txq_threshold) + txq_threshold = wd->txq_threshold; + else + txq_threshold = AGG_MIN_TXQL; + + tid_tx = zfAggTxReady(dev); + if (tid_tx) ScanAndClear = 0; + while (zfHpGetFreeTxdCount(dev) > 20 && (TXQL < txq_threshold || tid_tx)) { + //while (zfHpGetFreeTxdCount(dev) > 20 && (ScanAndClear || tid_tx)) { + //while (TXQL < txq_threshold) { + u16_t i; + u8_t ac; + s8_t destQ_count = 0; + //while ((zfHpGetFreeTxdCount(dev)) > 32) { + + //DbgPrint("zfAggTxScheduler: in while loop"); + for (i=0; i<4; i++) { + if (DESTQ.Head[i]) destQ_count++; + } + if (0 >= destQ_count) break; + + zmw_enter_critical_section(dev); + ac = pri[DESTQ.ppri]; DESTQ.ppri = (DESTQ.ppri + 1) % 10; + zmw_leave_critical_section(dev); + + for (i=0; i<10; i++){ + if(DESTQ.Head[ac]) break; + + zmw_enter_critical_section(dev); + ac = pri[DESTQ.ppri]; DESTQ.ppri = (DESTQ.ppri + 1) % 10; + zmw_leave_critical_section(dev); + } + if (i == 10) break; + //DbgPrint("zfAggTxScheduler: have dest Q"); + zmw_enter_critical_section(dev); + wd->destLock = 1; + zmw_leave_critical_section(dev); + + dest = DESTQ.getNext(dev, ac); + if (!dest) { + zmw_enter_critical_section(dev); + wd->destLock = 0; + zmw_leave_critical_section(dev); + + DbgPrint("bug report! DESTQ.getNext got nothing!"); + break; + } + if (dest->Qtype == 0) { + tid_tx = dest->tid_tx; + + //DbgPrint("zfAggTxScheduler: have tid_tx Q"); + + if(tid_tx && zfAggValidTidTx(dev, tid_tx)) + tid_tx->size = zm_agg_qlen(dev, tid_tx->aggHead, tid_tx->aggTail); + else { + zmw_enter_critical_section(dev); + wd->destLock = 0; + zmw_leave_critical_section(dev); + + tid_tx = zfAggTxReady(dev); + continue; + } + + zmw_enter_critical_section(dev); + wd->destLock = 0; + zmw_leave_critical_section(dev); + //zmw_enter_critical_section(dev); + if (tid_tx && !tid_tx->size) { + + //zmw_leave_critical_section(dev); + //DESTQ.delete(dev, 0, tid_tx, NULL); + } + else if(wd->aggState == 0){ + //wd->aggState = 1; + //zmw_leave_critical_section(dev); + zfAggTxSend(dev, zfHpGetFreeTxdCount(dev), tid_tx); + //wd->aggState = 0; + } + else { + //zmw_leave_critical_section(dev); + break; + } + } + else { + vtxq = dest->vtxq; + buf = zfGetVtxq(dev, ac); + zm_assert( buf != 0 ); + + zfTxSendEth(dev, buf, 0, ZM_EXTERNAL_ALLOC_BUF, 0); + + } + /*flush all but < 16 frames in tid_tx to TXQ*/ + tid_tx = zfAggTxReady(dev); + } + + /*while ((zfHpGetFreeTxdCount(dev)) > 32) { + //while ((zfHpGetFreeTxdCount(dev)) > 32) { + + destQ_count = 0; + for (i=0; i<4; i++) destQ_count += wd->destQ.size[i]; + if (0 >= destQ_count) break; + + ac = pri[wd->destQ.ppri]; wd->destQ.ppri = (wd->destQ.ppri + 1) % 10; + for (i=0; i<10; i++){ + if(wd->destQ.size[ac]!=0) break; + ac = pri[wd->destQ.ppri]; wd->destQ.ppri = (wd->destQ.ppri + 1) % 10; + } + if (i == 10) break; + dest = wd->destQ.getNext(dev, ac); + if (dest->Qtype == 0) { + tid_tx = dest->tid_tx; + tid_tx->size = zm_agg_qlen(dev, tid_tx->aggHead, tid_tx->aggTail); + if (!tid_tx->size) { + wd->destQ.delete(dev, 0, tid_tx, NULL); + break; + } + else if((wd->aggState == 0) && (tid_tx->size >= 16)){ + zfAggTxSend(dev, zfHpGetFreeTxdCount(dev), tid_tx); + } + else { + break; + } + } + + } + */ + return; +} + +/************************************************************************/ +/* */ +/* FUNCTION DESCRIPTION zfAggTx */ +/* return Status code ZM_SUCCESS or error code */ +/* management A-MPDU aggregation function, */ +/* management aggregation queue, calculate arrivalrate, */ +/* add/delete an aggregation queue of a stream, */ +/* enqueue packets into responsible aggregate queue. */ +/* take (dev, buf, ac) as input */ +/* */ +/* INPUTS */ +/* dev : device pointer */ +/* buf : packet buff */ +/* ac : access category */ +/* */ +/* OUTPUTS */ +/* status code */ +/* */ +/* AUTHOR */ +/* Honda Atheros Communications, INC. 2006.12 */ +/* */ +/************************************************************************/ +u16_t zfAggTx(zdev_t* dev, zbuf_t* buf, u16_t tid) +{ + u16_t aid; + //u16_t qnum; + //u16_t aggflag = 0; + //u16_t arrivalrate = 0; + TID_TX tid_tx; + + zmw_get_wlan_dev(dev); + + zmw_declare_for_critical_section(); + + if(!wd->aggInitiated) + { + return ZM_ERR_TX_BUFFER_UNAVAILABLE; + } + + aid = zfAggGetSta(dev, buf); + + //arrivalrate = zfAggTxArrivalRate(dev, aid, tid); + + if (0xffff == aid) + { + /* + * STA not associated, this is a BC/MC or STA->AP packet + */ + + return ZM_ERR_TX_BUFFER_UNAVAILABLE; + } + + /* + * STA associated, a unicast packet + */ + + tid_tx = zfAggTxGetQueue(dev, aid, tid); + + /*tid_q.tid_tx = tid_tx; + wd->destQ.insert = zfAggDestInsert; + wd->destQ.insert(dev, 0, tid_q); + */ + if (tid_tx != NULL) + { + /* + * this (aid, ac) is aggregated + */ + + //if (arrivalrate < ZM_AGG_LOW_THRESHOLD) + if (0) + { + /* + * arrival rate too low + * delete this aggregate queue + */ + + zmw_enter_critical_section(dev); + + //wd->aggQPool[qnum]->clearFlag = wd->aggQPool[qnum]->deleteFlag =1; + + zmw_leave_critical_section(dev); + + } + + return zfAggTxEnqueue(dev, buf, aid, tid_tx); + + } + else + { + /* + * this (aid, ac) not yet aggregated + * queue not found + */ + + //if (arrivalrate > ZM_AGG_HIGH_THRESHOLD) + if (1) + { + /* + * arrivalrate high enough to get a new agg queue + */ + + tid_tx = zfAggTxNewQueue(dev, aid, tid, buf); + + //zm_msg1_agg(ZM_LV_0, "get new AggQueue qnum = ", tid_tx->); + + if (tid_tx) + { + /* + * got a new aggregate queue + */ + + //zmw_enter_critical_section(dev); + + //wd->aggSta[aid].aggFlag[ac] = 1; + + //zmw_leave_critical_section(dev); + + /* + * add ADDBA functions here + * return ZM_ERR_TX_BUFFER_UNAVAILABLE; + */ + + + //zfAggSendAddbaRequest(dev, tid_tx->dst, tid_tx->ac, tid_tx->tid); + //zmw_enter_critical_section(dev); + + //wd->aggSta[aid].aggFlag[ac] = 0; + + //zmw_leave_critical_section(dev); + + return zfAggTxEnqueue(dev, buf, aid, tid_tx); + + } + else + { + /* + * just can't get a new aggregate queue + */ + + return ZM_ERR_TX_BUFFER_UNAVAILABLE; + } + } + else + { + /* + * arrival rate is not high enough to get a new agg queue + */ + + return ZM_ERR_TX_BUFFER_UNAVAILABLE; + } + } + + + +} + + +/************************************************************************/ +/* */ +/* FUNCTION DESCRIPTION zfAggTxReadyCount */ +/* return counter of ready to aggregate queues. */ +/* take (dev, ac) as input, only calculate the ready to aggregate */ +/* queues of one particular ac. */ +/* */ +/* INPUTS */ +/* dev : device pointer */ +/* ac : access category */ +/* */ +/* OUTPUTS */ +/* counter of ready to aggregate queues */ +/* */ +/* AUTHOR */ +/* Honda Atheros Communications, INC. 2006.12 */ +/* */ +/************************************************************************/ +u16_t zfAggTxReadyCount(zdev_t* dev, u16_t ac) +{ + u16_t i; + u16_t readycount = 0; + + zmw_get_wlan_dev(dev); + + zmw_declare_for_critical_section(); + + zmw_enter_critical_section(dev); + + for (i=0 ; i<ZM_AGG_POOL_SIZE; i++) + { + if (wd->aggQPool[i]->aggQEnabled && (wd->aggQPool[i]->aggReady || \ + wd->aggQPool[i]->clearFlag) && ac == wd->aggQPool[i]->ac) + readycount++; + } + + zmw_leave_critical_section(dev); + + return readycount; +} + +/************************************************************************/ +/* */ +/* FUNCTION DESCRIPTION zfAggTxPartial */ +/* return the number that Vtxq has to send. */ +/* take (dev, ac, readycount) as input, calculate the ratio of */ +/* Vtxq length to (Vtxq length + readycount) of a particular ac, */ +/* and returns the Vtxq length * the ratio */ +/* */ +/* INPUTS */ +/* dev : device pointer */ +/* ac : access category */ +/* readycount: the number of ready to aggregate queues of this ac */ +/* */ +/* OUTPUTS */ +/* Vtxq length * ratio */ +/* */ +/* AUTHOR */ +/* Honda Atheros Communications, INC. 2006.12 */ +/* */ +/************************************************************************/ +u16_t zfAggTxPartial(zdev_t* dev, u16_t ac, u16_t readycount) +{ + u16_t qlen; + u16_t partial; + + zmw_get_wlan_dev(dev); + + zmw_declare_for_critical_section(); + + zmw_enter_critical_section(dev); + + qlen = zm_agg_qlen(dev, wd->vtxqHead[ac], wd->vtxqTail[ac]); + + if ((qlen + readycount) > 0) + { + partial = (u16_t)( zm_agg_weight(ac) * ((u16_t)qlen/(qlen + \ + readycount)) ); + } + else + { + partial = 0; + } + + zmw_leave_critical_section(dev); + + if (partial > qlen) + partial = qlen; + + return partial; +} + + +/************************************************************************/ +/* */ +/* FUNCTION DESCRIPTION zfAggTxSend */ +/* return sentcount */ +/* take (dev, ac, n) as input, n is the number of scheduled agg */ +/* queues to be sent of the particular ac. */ +/* */ +/* INPUTS */ +/* dev : device pointer */ +/* ac : access category */ +/* n : the number of scheduled aggregation queues to be sent */ +/* */ +/* OUTPUTS */ +/* sentcount */ +/* */ +/* AUTHOR */ +/* Honda Atheros Communications, INC. 2006.12 */ +/* */ +/************************************************************************/ +u16_t zfAggTxSend(zdev_t* dev, u32_t freeTxd, TID_TX tid_tx) +{ + //u16_t qnum; + //u16_t qlen; + u16_t j; + //u16_t sentcount = 0; + zbuf_t* buf; + struct aggControl aggControl; + u16_t aggLen; + //zbuf_t* newBuf; + //u16_t bufLen; + //TID_BAW tid_baw = NULL; + //struct bufInfo *buf_info; + + zmw_get_wlan_dev(dev); + + zmw_declare_for_critical_section(); + + //while (tid_tx->size > 0) + + zmw_enter_critical_section(dev); + tid_tx->size = zm_agg_qlen(dev, tid_tx->aggHead, tid_tx->aggTail); + aggLen = zm_agg_min(16, zm_agg_min(tid_tx->size, (u16_t)(freeTxd - 2))); + zmw_leave_critical_section(dev); + + /* + * why there have to be 2 free Txd? + */ + if (aggLen <=0 ) + return 0; + + + if (aggLen == 1) { + buf = zfAggTxGetVtxq(dev, tid_tx); + if (buf) + zfTxSendEth(dev, buf, 0, ZM_EXTERNAL_ALLOC_BUF, 0); + if (tid_tx->size == 0) { + //DESTQ.delete(dev, 0, tid_tx, NULL); + } + + return 1; + } + /* + * Free Txd queue is big enough to put aggregation + */ + zmw_enter_critical_section(dev); + if (wd->aggState == 1) { + zmw_leave_critical_section(dev); + return 0; + } + wd->aggState = 1; + zmw_leave_critical_section(dev); + + + zm_msg1_agg(ZM_LV_0, "aggLen=", aggLen); + tid_tx->aggFrameSize = 0; + for (j=0; j < aggLen; j++) { + buf = zfAggTxGetVtxq(dev, tid_tx); + + zmw_enter_critical_section(dev); + tid_tx->size = zm_agg_qlen(dev, tid_tx->aggHead, tid_tx->aggTail); + zmw_leave_critical_section(dev); + + if ( buf ) { + //struct aggTally *agg_tal; + u16_t completeIndex; + + if (0 == j) { + aggControl.ampduIndication = ZM_AGG_FIRST_MPDU; + + } + else if ((j == (aggLen - 1)) || tid_tx->size == 0) + { + aggControl.ampduIndication = ZM_AGG_LAST_MPDU; + //wd->aggState = 0; + + } + else + { + aggControl.ampduIndication = ZM_AGG_MIDDLE_MPDU; + /* the packet is delayed more than 500 ms, drop it */ + + } + tid_tx->aggFrameSize += zfwBufGetSize(dev, buf); + aggControl.addbaIndication = 0; + aggControl.aggEnabled = 1; + +#ifdef ZM_AGG_TALLY + agg_tal = &wd->agg_tal; + agg_tal->sent_packets_sum++; + +#endif + + zfAggTxSendEth(dev, buf, 0, ZM_EXTERNAL_ALLOC_BUF, 0, &aggControl, tid_tx); + + zmw_enter_critical_section(dev); + completeIndex = tid_tx->complete; + if(zm_agg_inQ(tid_tx, tid_tx->complete)) + zm_agg_plus(tid_tx->complete); + zmw_leave_critical_section(dev); + + if(zm_agg_inQ(tid_tx, completeIndex) && wd->zfcbSendCompleteIndication + && tid_tx->aggvtxq[completeIndex].buf) { + wd->zfcbSendCompleteIndication(dev, tid_tx->aggvtxq[completeIndex].buf); + zm_debug_msg0("in queue complete worked!"); + } + + } + else { + /* + * this aggregation queue is empty + */ + zm_msg1_agg(ZM_LV_0, "aggLen not reached, but no more frame, j=", j); + + break; + } + } + zmw_enter_critical_section(dev); + wd->aggState = 0; + zmw_leave_critical_section(dev); + + //zm_acquire_agg_spin_lock(Adapter); + tid_tx->size = zm_agg_qlen(dev, tid_tx->aggHead, tid_tx->aggTail); + //zm_release_agg_spin_lock(Adapter); + + if (tid_tx->size == 0) { + //DESTQ.delete(dev, 0, tid_tx, NULL); + } + + + + //zfAggInvokeBar(dev, tid_tx); + if(j>0) { + aggr_count++; + zm_msg1_agg(ZM_LV_0, "0xC2:sent 1 aggr, aggr_count=", aggr_count); + zm_msg1_agg(ZM_LV_0, "0xC2:sent 1 aggr, aggr_size=", j); + } + return j; +} + + +/************************************************************************/ +/* */ +/* FUNCTION DESCRIPTION zfAggTxGetReadyQueue */ +/* return the number of the aggregation queue */ +/* take (dev, ac) as input, find the agg queue with smallest */ +/* arrival time (waited longest) among those ready or clearFlag */ +/* set queues. */ +/* */ +/* INPUTS */ +/* dev : device pointer */ +/* ac : access category */ +/* */ +/* OUTPUTS */ +/* aggregation queue number */ +/* */ +/* AUTHOR */ +/* Honda Atheros Communications, INC. 2006.12 */ +/* */ +/************************************************************************/ +TID_TX zfAggTxGetReadyQueue(zdev_t* dev, u16_t ac) +{ + //u16_t qnum = ZM_AGG_POOL_SIZE; + u16_t i; + u32_t time = 0; + TID_TX tid_tx = NULL; + + zmw_get_wlan_dev(dev); + + zmw_declare_for_critical_section(); + + zmw_enter_critical_section(dev); + + for (i=0 ;i<ZM_AGG_POOL_SIZE; i++) + { + if (1 == wd->aggQPool[i]->aggQEnabled && ac == wd->aggQPool[i]->ac && + (wd->aggQPool[i]->size > 0)) + { + if (0 == time || time > wd->aggQPool[i]->aggvtxq[ \ + wd->aggQPool[i]->aggHead ].arrivalTime) + { + tid_tx = wd->aggQPool[i]; + time = tid_tx->aggvtxq[ tid_tx->aggHead ].arrivalTime; + } + } + } + + zmw_leave_critical_section(dev); + + return tid_tx; +} + + + +/************************************************************************/ +/* */ +/* FUNCTION DESCRIPTION zfAggTxGetVtxq */ +/* return an MSDU */ +/* take (dev, qnum) as input, return an MSDU out of the agg queue. */ +/* */ +/* INPUTS */ +/* dev : device pointer */ +/* qnum: queue number */ +/* */ +/* OUTPUTS */ +/* a MSDU */ +/* */ +/* AUTHOR */ +/* Honda Atheros Communications, INC. 2006.12 */ +/* */ +/************************************************************************/ +zbuf_t* zfAggTxGetVtxq(zdev_t* dev, TID_TX tid_tx) +{ + zbuf_t* buf = NULL; + + zmw_declare_for_critical_section(); + + if (tid_tx->aggHead != tid_tx->aggTail) + { + buf = tid_tx->aggvtxq[ tid_tx->aggTail ].buf; + + tid_tx->aggvtxq[tid_tx->aggTail].buf = NULL; + + zmw_enter_critical_section(dev); + tid_tx->aggTail = ((tid_tx->aggTail + 1) & ZM_AGGQ_SIZE_MASK); + if(tid_tx->size > 0) tid_tx->size--; + tid_tx->size = zm_agg_qlen(dev, tid_tx->aggHead, tid_tx->aggTail); + if (NULL == buf) { + //tid_tx->aggTail = tid_tx->aggHead = tid_tx->size = 0; + //zm_msg1_agg(ZM_LV_0, "GetVtxq buf == NULL, tid_tx->size=", tid_tx->size); + } + zmw_leave_critical_section(dev); + } + else + { + /* + * queue is empty + */ + zm_msg1_agg(ZM_LV_0, "tid_tx->aggHead == tid_tx->aggTail, tid_tx->size=", tid_tx->size); + + } + + if (zm_agg_qlen(dev, tid_tx->aggHead, tid_tx->aggTail) != tid_tx->size) + zm_msg1_agg(ZM_LV_0, "qlen!=tid_tx->size! tid_tx->size=", tid_tx->size); + return buf; +} + + +/************************************************************************/ +/* */ +/* FUNCTION DESCRIPTION zfAggTxDeleteQueue */ +/* return ZM_SUCCESS (can't fail) */ +/* take (dev, qnum) as input, reset (delete) this aggregate queue, */ +/* this queue is virtually returned to the aggregate queue pool. */ +/* */ +/* INPUTS */ +/* dev : device pointer */ +/* qnum: queue number */ +/* */ +/* OUTPUTS */ +/* ZM_SUCCESS */ +/* */ +/* AUTHOR */ +/* Honda Atheros Communications, INC. 2006.12 */ +/* */ +/************************************************************************/ +u16_t zfAggTxDeleteQueue(zdev_t* dev, u16_t qnum) +{ + u16_t ac, tid; + struct aggQueue *tx_tid; + struct aggSta *agg_sta; + + zmw_get_wlan_dev(dev); + + zmw_declare_for_critical_section(); + + tx_tid = wd->aggQPool[qnum]; + agg_sta = &wd->aggSta[tx_tid->aggQSTA]; + ac = tx_tid->ac; + tid = tx_tid->tid; + + zmw_enter_critical_section(dev); + + tx_tid->aggQEnabled = 0; + tx_tid->aggHead = tx_tid->aggTail = 0; + tx_tid->aggReady = 0; + tx_tid->clearFlag = tx_tid->deleteFlag = 0; + tx_tid->size = 0; + agg_sta->count[ac] = 0; + + agg_sta->tid_tx[tid] = NULL; + agg_sta->aggFlag[ac] = 0; + + zmw_leave_critical_section(dev); + + zm_msg1_agg(ZM_LV_0, "queue deleted! qnum=", qnum); + + return ZM_SUCCESS; +} + +#ifdef ZM_ENABLE_AGGREGATION +#ifndef ZM_ENABLE_FW_BA_RETRANSMISSION //disable BAW +void zfBawCore(zdev_t* dev, u16_t baw_seq, u32_t bitmap, u16_t aggLen) { + TID_BAW tid_baw; + s16_t i; + zbuf_t* buf; + struct bufInfo *buf_info; + + zmw_get_wlan_dev(dev); + //zmw_declare_for_critical_section(); + tid_baw = BAW->getQ(dev, baw_seq); + //tid_baw = NULL; + if (NULL == tid_baw) + return; + + total_mpdu += aggLen; + for (i = aggLen - 1; i>=0; i--) { + if (((bitmap >> i) & 0x1) == 0) { + buf_info = BAW->pop(dev, i, tid_baw); + buf = buf_info->buf; + if (buf) { + //wd->zfcbSetBawQ(dev, buf, 0); + zfAggTidTxInsertHead(dev, buf_info, tid_baw->tid_tx); + } + } + else { + success_mpdu++; + } + } + BAW->disable(dev, tid_baw); + zfAggTxScheduler(dev); + zm_debug_msg1("success_mpdu = ", success_mpdu); + zm_debug_msg1(" total_mpdu = ", total_mpdu); +} + +void zfBawInit(zdev_t* dev) { + TID_BAW tid_baw; + u16_t i,j; + zmw_get_wlan_dev(dev); + //zmw_declare_for_critical_section(); + + for (i=0; i<ZM_BAW_POOL_SIZE; i++){ + tid_baw = &BAW->tid_baw[i]; + for (j=0; j<ZM_VTXQ_SIZE; j++) { + tid_baw->frame[j].buf = NULL; + } + tid_baw->enabled = tid_baw->head = tid_baw->tail = tid_baw->size = 0; + tid_baw->start_seq = 0; + } + BAW->delPoint = 0; + BAW->core = zfBawCore; + BAW->getNewQ = zfBawGetNewQ; + BAW->insert = zfBawInsert; + BAW->pop = zfBawPop; + BAW->enable = zfBawEnable; + BAW->disable = zfBawDisable; + BAW->getQ = zfBawGetQ; +} + + + +TID_BAW zfBawGetNewQ(zdev_t* dev, u16_t start_seq, TID_TX tid_tx) { + TID_BAW tid_baw=NULL; + TID_BAW next_baw=NULL; + u16_t i; + zmw_get_wlan_dev(dev); + //zmw_declare_for_critical_section(); + + /* + for (i=0; i<ZM_BAW_POOL_SIZE; i++){ + tid_baw = &BAW->tid_baw[i]; + if (FALSE == tid_baw->enabled) + break; + } + */ + + tid_baw = &BAW->tid_baw[BAW->delPoint]; + i = BAW->delPoint; + //if (ZM_BAW_POOL_SIZE == i) { + //return NULL; + // u8_t temp = BAW->delPoint; + // tid_baw = &BAW->tid_baw[BAW->delPoint]; + // BAW->disable(dev, tid_baw); + // BAW->delPoint = (BAW->delPoint < (ZM_BAW_POOL_SIZE - 1))? (BAW->delPoint + 1): 0; + // temp = BAW->delPoint; + //} + + zm_msg1_agg(ZM_LV_0, "get new tid_baw, index=", i); + BAW->delPoint = (i < (ZM_BAW_POOL_SIZE -1))? (i + 1): 0; + next_baw = &BAW->tid_baw[BAW->delPoint]; + if (1 == next_baw->enabled) BAW->disable(dev, next_baw); + + BAW->enable(dev, tid_baw, start_seq); + tid_baw->tid_tx = tid_tx; + + return tid_baw; +} + +u16_t zfBawInsert(zdev_t* dev, zbuf_t* buf, u16_t baw_seq, TID_BAW tid_baw, u8_t baw_retransmit, struct baw_header_r *header_r) { + //TID_BAW tid_baw; + //u16_t bufLen; + + //zmw_get_wlan_dev(dev); + //zmw_declare_for_critical_section(); + + if(tid_baw->size < (ZM_VTXQ_SIZE - 1)) { + struct baw_header *baw_header = &tid_baw->frame[tid_baw->head].baw_header; + + baw_header->headerLen = header_r->headerLen; + baw_header->micLen = header_r->micLen; + baw_header->snapLen = header_r->snapLen; + baw_header->removeLen = header_r->removeLen; + baw_header->keyIdx = header_r->keyIdx; + zfwMemoryCopy((u8_t *)baw_header->header, (u8_t *)header_r->header, 58); + zfwMemoryCopy((u8_t *)baw_header->mic , (u8_t *)header_r->mic , 8); + zfwMemoryCopy((u8_t *)baw_header->snap , (u8_t *)header_r->snap , 8); + //wd->zfcbSetBawQ(dev, buf, 1); + tid_baw->frame[tid_baw->head].buf = buf; + tid_baw->frame[tid_baw->head].baw_seq = baw_seq; + tid_baw->frame[tid_baw->head].baw_retransmit = baw_retransmit + 1; + + //tid_baw->frame[tid_baw->head].data = pBuf->data; + tid_baw->head++; + tid_baw->size++; + } + else { + //wd->zfcbSetBawQ(dev, buf, 0); + zfwBufFree(dev, buf, ZM_SUCCESS); + return FALSE; + } + return TRUE; +} + +struct bufInfo* zfBawPop(zdev_t* dev, u16_t index, TID_BAW tid_baw) { + //TID_BAW tid_baw; + //zbuf_t* buf; + struct bufInfo *buf_info; + zmw_get_wlan_dev(dev); + + buf_info = &wd->buf_info; + buf_info->baw_header = NULL; + + if (NULL == (buf_info->buf = tid_baw->frame[index].buf)) + return buf_info; + + buf_info->baw_retransmit = tid_baw->frame[index].baw_retransmit; + buf_info->baw_header = &tid_baw->frame[index].baw_header; + buf_info->timestamp = tid_baw->frame[index].timestamp; + //pBuf->data = pBuf->buffer; + //wd->zfcbRestoreBufData(dev, buf); + tid_baw->frame[index].buf = NULL; + + return buf_info; +} + +void zfBawEnable(zdev_t* dev, TID_BAW tid_baw, u16_t start_seq) { + //TID_BAW tid_baw; + + //zmw_get_wlan_dev(dev); + //zmw_declare_for_critical_section(); + + tid_baw->enabled = TRUE; + tid_baw->head = tid_baw->tail = tid_baw->size = 0; + tid_baw->start_seq = start_seq; +} + +void zfBawDisable(zdev_t* dev, TID_BAW tid_baw) { + //TID_BAW tid_baw; + u16_t i; + + //zmw_get_wlan_dev(dev); + //zmw_declare_for_critical_section(); + for (i=0; i<ZM_VTXQ_SIZE; i++) { + if (tid_baw->frame[i].buf) { + + //wd->zfcbSetBawQ(dev, tid_baw->frame[i].buf, 0); + zfwBufFree(dev, tid_baw->frame[i].buf, ZM_SUCCESS); + tid_baw->frame[i].buf = NULL; + } + } + + tid_baw->enabled = FALSE; +} + +TID_BAW zfBawGetQ(zdev_t* dev, u16_t baw_seq) { + TID_BAW tid_baw=NULL; + u16_t i; + + zmw_get_wlan_dev(dev); + //zmw_declare_for_critical_section(); + for (i=0; i<ZM_BAW_POOL_SIZE; i++){ + tid_baw = &BAW->tid_baw[i]; + if (TRUE == tid_baw->enabled) + { + zm_msg1_agg(ZM_LV_0, "get an old tid_baw, baw_seq=", baw_seq); + zm_msg1_agg(ZM_LV_0, "check a tid_baw->start_seq=", tid_baw->start_seq); + if(baw_seq == tid_baw->start_seq) + break; + } + + } + if (ZM_BAW_POOL_SIZE == i) + return NULL; + return tid_baw; +} +#endif //disable BAW +#endif + +u16_t zfAggTallyReset(zdev_t* dev) +{ + struct aggTally* agg_tal; + + zmw_get_wlan_dev(dev); + + //zmw_declare_for_critical_section(); + + agg_tal = &wd->agg_tal; + agg_tal->got_packets_sum = 0; + agg_tal->got_bytes_sum = 0; + agg_tal->sent_bytes_sum = 0; + agg_tal->sent_packets_sum = 0; + agg_tal->avg_got_packets = 0; + agg_tal->avg_got_bytes = 0; + agg_tal->avg_sent_packets = 0; + agg_tal->avg_sent_bytes = 0; + agg_tal->time = 0; + return 0; +} + + +/************************************************************************/ +/* */ +/* FUNCTION DESCRIPTION zfAggScanAndClear */ +/* If the packets in a queue have waited for too long, clear and */ +/* delete this aggregation queue. */ +/* */ +/* INPUTS */ +/* dev : device pointer */ +/* time : current time */ +/* */ +/* OUTPUTS */ +/* ZM_SUCCESS */ +/* */ +/* AUTHOR */ +/* Honda Atheros Communications, INC. 2006.12 */ +/* */ +/************************************************************************/ +u16_t zfAggScanAndClear(zdev_t* dev, u32_t time) +{ + u16_t i; + u16_t head; + u16_t tail; + u32_t tick; + u32_t arrivalTime; + //u16_t aid, ac; + TID_TX tid_tx; + + zmw_get_wlan_dev(dev); + + zmw_declare_for_critical_section(); + + if(!(wd->state == ZM_WLAN_STATE_ENABLED)) return 0; + zfAggTxScheduler(dev, 1); + tick = zm_agg_GetTime(); + for (i=0; i<ZM_AGG_POOL_SIZE; i++) + { + if (!wd->aggQPool[i]) return 0; + if (1 == wd->aggQPool[i]->aggQEnabled) + { + tid_tx = wd->aggQPool[i]; + zmw_enter_critical_section(dev); + + head = tid_tx->aggHead; + tail = tid_tx->aggTail; + + arrivalTime = (u32_t)tid_tx->aggvtxq[tid_tx->aggTail].arrivalTime; + + + if((tick - arrivalTime) <= ZM_AGG_CLEAR_TIME) + { + + } + else if((tid_tx->size = zm_agg_qlen(dev, tid_tx->aggHead, tid_tx->aggTail)) > 0) + { + + tid_tx->clearFlag = 1; + + //zm_msg1_agg(ZM_LV_0, "clear queue tick =", tick); + //zm_msg1_agg(ZM_LV_0, "clear queue arrival =", arrivalTime); + + + //zmw_leave_critical_section(dev); + //zfAggTxScheduler(dev); + //zmw_enter_critical_section(dev); + + } + + if (tid_tx->size == 0) + { + /* + * queue empty + */ + if (tick - tid_tx->lastArrival > ZM_AGG_DELETE_TIME) + { + zm_msg1_agg(ZM_LV_0, "delete queue, idle for n sec. n = ", \ + ZM_AGG_DELETE_TIME/10); + + zmw_leave_critical_section(dev); + zfAggTxDeleteQueue(dev, i); + zmw_enter_critical_section(dev); + } + } + + zmw_leave_critical_section(dev); + } + } + + zfAggRxClear(dev, time); + +#ifdef ZM_AGG_TALLY + if((wd->tick % 100) == 0) { + zfAggPrintTally(dev); + } +#endif + + return ZM_SUCCESS; +} + +u16_t zfAggPrintTally(zdev_t* dev) +{ + struct aggTally* agg_tal; + + zmw_get_wlan_dev(dev); + + //zmw_declare_for_critical_section(); + + agg_tal = &wd->agg_tal; + + if(agg_tal->got_packets_sum < 10) + { + zfAggTallyReset(dev); + return 0; + } + + agg_tal->time++; + agg_tal->avg_got_packets = (agg_tal->avg_got_packets * (agg_tal->time - 1) + + agg_tal->got_packets_sum) / agg_tal->time; + agg_tal->avg_got_bytes = (agg_tal->avg_got_bytes * (agg_tal->time - 1) + + agg_tal->got_bytes_sum) / agg_tal->time; + agg_tal->avg_sent_packets = (agg_tal->avg_sent_packets * (agg_tal->time - 1) + + agg_tal->sent_packets_sum) / agg_tal->time; + agg_tal->avg_sent_bytes = (agg_tal->avg_sent_bytes * (agg_tal->time - 1) + + agg_tal->sent_bytes_sum) / agg_tal->time; + zm_msg1_agg(ZM_LV_0, "got_packets_sum =", agg_tal->got_packets_sum); + zm_msg1_agg(ZM_LV_0, " got_bytes_sum =", agg_tal->got_bytes_sum); + zm_msg1_agg(ZM_LV_0, "sent_packets_sum=", agg_tal->sent_packets_sum); + zm_msg1_agg(ZM_LV_0, " sent_bytes_sum =", agg_tal->sent_bytes_sum); + agg_tal->got_packets_sum = agg_tal->got_bytes_sum =agg_tal->sent_packets_sum + = agg_tal->sent_bytes_sum = 0; + zm_msg1_agg(ZM_LV_0, "avg_got_packets =", agg_tal->avg_got_packets); + zm_msg1_agg(ZM_LV_0, " avg_got_bytes =", agg_tal->avg_got_bytes); + zm_msg1_agg(ZM_LV_0, "avg_sent_packets=", agg_tal->avg_sent_packets); + zm_msg1_agg(ZM_LV_0, " avg_sent_bytes =", agg_tal->avg_sent_bytes); + if ((wd->commTally.BA_Fail == 0) || (wd->commTally.Hw_Tx_MPDU == 0)) + { + zm_msg1_agg(ZM_LV_0, "Hardware Tx MPDU=", wd->commTally.Hw_Tx_MPDU); + zm_msg1_agg(ZM_LV_0, " BA Fail number=", wd->commTally.BA_Fail); + } + else + zm_msg1_agg(ZM_LV_0, "1/(BA fail rate)=", wd->commTally.Hw_Tx_MPDU/wd->commTally.BA_Fail); + + return 0; +} + +u16_t zfAggRxClear(zdev_t* dev, u32_t time) +{ + u16_t i; + struct agg_tid_rx *tid_rx; + + zmw_get_wlan_dev(dev); + + zmw_declare_for_critical_section(); + + for (i=0; i<ZM_AGG_POOL_SIZE; i++) + { + zmw_enter_critical_section(dev); + tid_rx = wd->tid_rx[i]; + if (tid_rx->baw_head != tid_rx->baw_tail) + { + u16_t j = tid_rx->baw_tail; + while ((j != tid_rx->baw_head) && !tid_rx->frame[j].buf) { + j = (j + 1) & ZM_AGG_BAW_MASK; + } + if ((j != tid_rx->baw_head) && (time - tid_rx->frame[j].arrivalTime) > + (ZM_AGG_CLEAR_TIME - 5)) + { + zmw_leave_critical_section(dev); + zm_msg0_agg(ZM_LV_1, "queue RxFlush by RxClear"); + zfAggRxFlush(dev, 0, tid_rx); + zmw_enter_critical_section(dev); + } + } + zmw_leave_critical_section(dev); + } + + return ZM_SUCCESS; +} + +struct agg_tid_rx* zfAggRxEnabled(zdev_t* dev, zbuf_t* buf) +{ + u16_t dst0, src[3], ac, aid, fragOff; + u8_t up; + u16_t offset = 0; + u16_t seq_no; + u16_t frameType; + u16_t frameCtrl; + u16_t frameSubtype; + u32_t tcp_seq; + //struct aggSta *agg_sta; +#if ZM_AGG_FPGA_REORDERING + struct agg_tid_rx *tid_rx; +#endif + zmw_get_wlan_dev(dev); + + //zmw_declare_for_critical_section(); + seq_no = zmw_rx_buf_readh(dev, buf, 22) >> 4; + //DbgPrint("Rx seq=%d\n", seq_no); + if (wd->sta.EnableHT == 0) + { + return NULL; + } + + frameCtrl = zmw_rx_buf_readb(dev, buf, 0); + frameType = frameCtrl & 0xf; + frameSubtype = frameCtrl & 0xf0; + + + if (frameType != ZM_WLAN_DATA_FRAME) //non-Qos Data? (frameSubtype&0x80) + { + return NULL; + } +#ifdef ZM_ENABLE_PERFORMANCE_EVALUATION + tcp_seq = zmw_rx_buf_readb(dev, buf, 22+36) << 24; + tcp_seq += zmw_rx_buf_readb(dev, buf, 22+37) << 16; + tcp_seq += zmw_rx_buf_readb(dev, buf, 22+38) << 8; + tcp_seq += zmw_rx_buf_readb(dev, buf, 22+39); +#endif + + ZM_SEQ_DEBUG("In %5d, %12u\n", seq_no, tcp_seq); + dst0 = zmw_rx_buf_readh(dev, buf, offset+4); + + src[0] = zmw_rx_buf_readh(dev, buf, offset+10); + src[1] = zmw_rx_buf_readh(dev, buf, offset+12); + src[2] = zmw_rx_buf_readh(dev, buf, offset+14); + +#if ZM_AGG_FPGA_DEBUG + aid = 0; +#else + aid = zfApFindSta(dev, src); +#endif + + //agg_sta = &wd->aggSta[aid]; + //zfTxGetIpTosAndFrag(dev, buf, &up, &fragOff); + //ac = zcUpToAc[up&0x7] & 0x3; + + /* + * Filter unicast frame only, aid == 0 is for debug only + */ + if ((dst0 & 0x1) == 0 && aid == 0) + { +#if ZM_AGG_FPGA_REORDERING + tid_rx = zfAggRxGetQueue(dev, buf) ; + if(!tid_rx) + return NULL; + else + { + //if (tid_rx->addBaExchangeStatusCode == ZM_AGG_ADDBA_RESPONSE) + return tid_rx; + } +#else + return NULL; +#endif + } + + return NULL; +} + +u16_t zfAggRx(zdev_t* dev, zbuf_t* buf, struct zsAdditionInfo *addInfo, struct agg_tid_rx *tid_rx) +{ + u16_t seq_no; + s16_t index; + u16_t offset = 0; + zbuf_t* pbuf; + u8_t frameSubType; + + zmw_get_wlan_dev(dev); + + zmw_declare_for_critical_section(); + + ZM_BUFFER_TRACE(dev, buf) + + ZM_PERFORMANCE_RX_REORDER(dev); + + seq_no = zmw_rx_buf_readh(dev, buf, offset+22) >> 4; + + index = seq_no - tid_rx->seq_start; + /* + * for debug + */ + + /* zm_msg2_agg(ZM_LV_0, "queue seq = ", seq_no); + * DbgPrint("%s:%s%lxh %s%lxh\n", __func__, "queue seq=", seq_no, + * "; seq_start=", tid_rx->seq_start); + */ + + //DbgPrint("seq_no=%d, seq_start=%d\n", seq_no, tid_rx->seq_start); + + /* In some APs, we found that it might transmit NULL data whose sequence number + is out or order. In order to avoid this problem, we ignore these NULL data. + */ + + frameSubType = (zmw_rx_buf_readh(dev, buf, 0) & 0xF0) >> 4; + + /* If this is a NULL data instead of Qos NULL data */ + if ((frameSubType & 0x0C) == 0x04) + { + s16_t seq_diff; + + seq_diff = (seq_no > tid_rx->seq_start) ? + seq_no - tid_rx->seq_start : tid_rx->seq_start - seq_no; + + if (seq_diff > ZM_AGG_BAW_SIZE) + { + zm_debug_msg0("Free Rx NULL data in zfAggRx"); + + /* Free Rx buffer */ + zfwBufFree(dev, buf, 0); + return ZM_ERR_OUT_OF_ORDER_NULL_DATA; + } + } + + /* + * sequence number wrap at 4k + */ + if (tid_rx->seq_start > seq_no) + { + //index += 4096; + + zmw_enter_critical_section(dev); + if (tid_rx->seq_start >= 4096) { + tid_rx->seq_start = 0; + } + zmw_leave_critical_section(dev); + + } + + if (tid_rx->seq_start == seq_no) { + zmw_enter_critical_section(dev); + if (((tid_rx->baw_head - tid_rx->baw_tail) & ZM_AGG_BAW_MASK) > 0) { + //DbgPrint("head=%d, tail=%d", tid_rx->baw_head, tid_rx->baw_tail); + tid_rx->baw_tail = (tid_rx->baw_tail + 1) & ZM_AGG_BAW_MASK; + } + tid_rx->seq_start = (tid_rx->seq_start + 1) & (4096 - 1); + zmw_leave_critical_section(dev); + + ZM_PERFORMANCE_RX_SEQ(dev, buf); + + if (wd->zfcbRecv80211 != NULL) { + //seq_no = zmw_rx_buf_readh(dev, buf, offset+22) >> 4; + //DbgPrint("Recv indicate seq=%d\n", seq_no); + //DbgPrint("1. seq=%d\n", seq_no); + + wd->zfcbRecv80211(dev, buf, addInfo); + } + else { + zfiRecv80211(dev, buf, addInfo); + } + } + else if (!zfAggRxEnqueue(dev, buf, tid_rx, addInfo)) + { + /* + * duplicated packet + */ + return 1; + } + + while (tid_rx->baw_head != tid_rx->baw_tail) {// && tid_rx->frame[tid_rx->baw_tail].buf) + u16_t tailIndex; + + zmw_enter_critical_section(dev); + + tailIndex = tid_rx->baw_tail; + pbuf = tid_rx->frame[tailIndex].buf; + tid_rx->frame[tailIndex].buf = 0; + if (!pbuf) + { + zmw_leave_critical_section(dev); + break; + } + + tid_rx->baw_tail = (tid_rx->baw_tail + 1) & ZM_AGG_BAW_MASK; + tid_rx->seq_start = (tid_rx->seq_start + 1) & (4096 - 1); + + + //if(pbuf && tid_rx->baw_size > 0) + // tid_rx->baw_size--; + + zmw_leave_critical_section(dev); + + ZM_PERFORMANCE_RX_SEQ(dev, pbuf); + + if (wd->zfcbRecv80211 != NULL) + { + //seq_no = zmw_rx_buf_readh(dev, pbuf, offset+22) >> 4; + //DbgPrint("Recv indicate seq=%d\n", seq_no); + //DbgPrint("1. seq=%d\n", seq_no); + wd->zfcbRecv80211(dev, pbuf, addInfo); + } + else + { + //seq_no = zmw_rx_buf_readh(dev, pbuf, offset+22) >> 4; + //DbgPrint("Recv indicate seq=%d\n", seq_no); + zfiRecv80211(dev, pbuf, addInfo); + } + } + + return 1; +} + +struct agg_tid_rx *zfAggRxGetQueue(zdev_t* dev, zbuf_t* buf) +{ + u16_t src[3]; + u16_t aid, ac, i; + u16_t offset = 0; + struct agg_tid_rx *tid_rx = NULL; + + zmw_get_wlan_dev(dev); + + //zmw_declare_for_critical_section(); + + src[0] = zmw_rx_buf_readh(dev, buf, offset+10); + src[1] = zmw_rx_buf_readh(dev, buf, offset+12); + src[2] = zmw_rx_buf_readh(dev, buf, offset+14); + aid = zfApFindSta(dev, src); + + ac = (zmw_rx_buf_readh(dev, buf, 24) & 0xF); + + // mark by spin lock debug + //zmw_enter_critical_section(dev); + + for (i=0; i<ZM_AGG_POOL_SIZE ; i++) + { + if((wd->tid_rx[i]->aid == aid) && (wd->tid_rx[i]->ac == ac)) + { + tid_rx = wd->tid_rx[i]; + break; + } + } + + // mark by spin lock debug + //zmw_leave_critical_section(dev); + return tid_rx; +} + + +u16_t zfAggRxEnqueue(zdev_t* dev, zbuf_t* buf, struct agg_tid_rx *tid_rx, struct zsAdditionInfo *addInfo) +{ + u16_t seq_no, offset = 0; + u16_t q_index; + s16_t index; + u8_t bdropframe = 0; + + zmw_get_wlan_dev(dev); + + zmw_declare_for_critical_section(); + + ZM_BUFFER_TRACE(dev, buf) + + seq_no = zmw_rx_buf_readh(dev, buf, offset+22) >> 4; + index = seq_no - tid_rx->seq_start; + + /* + * sequence number wrap at 4k + * -1000: check for duplicate past packet + */ + bdropframe = 0; + if (tid_rx->seq_start > seq_no) { + if ((tid_rx->seq_start > 3967) && (seq_no < 128)) { + index += 4096; + } else if (tid_rx->seq_start - seq_no > 70) { + zmw_enter_critical_section(dev); + tid_rx->sq_behind_count++; + if (tid_rx->sq_behind_count > 3) { + tid_rx->sq_behind_count = 0; + } else { + bdropframe = 1; + } + zmw_leave_critical_section(dev); + } else { + bdropframe = 1; + } + } else { + if (seq_no - tid_rx->seq_start > 70) { + zmw_enter_critical_section(dev); + tid_rx->sq_exceed_count++; + if (tid_rx->sq_exceed_count > 3) { + tid_rx->sq_exceed_count = 0; + } else { + bdropframe = 1; + } + zmw_leave_critical_section(dev); + } + } + + if (bdropframe == 1) { + /*if (wd->zfcbRecv80211 != NULL) { + wd->zfcbRecv80211(dev, buf, addInfo); + } + else { + zfiRecv80211(dev, buf, addInfo); + }*/ + + ZM_PERFORMANCE_FREE(dev, buf); + + zfwBufFree(dev, buf, 0); + /*zfAggRxFlush(dev, seq_no, tid_rx); + tid_rx->seq_start = seq_no; + index = seq_no - tid_rx->seq_start; + */ + + //DbgPrint("Free an old packet, seq_start=%d, seq_no=%d\n", tid_rx->seq_start, seq_no); + + /* + * duplicate past packet + * happens only in simulated aggregation environment + */ + return 0; + } else { + zmw_enter_critical_section(dev); + if (tid_rx->sq_exceed_count > 0){ + tid_rx->sq_exceed_count--; + } + + if (tid_rx->sq_behind_count > 0) { + tid_rx->sq_behind_count--; + } + zmw_leave_critical_section(dev); + } + + if (index < 0) { + zfAggRxFlush(dev, seq_no, tid_rx); + tid_rx->seq_start = seq_no; + index = 0; + } + + //if (index >= (ZM_AGG_BAW_SIZE - 1)) + if (index >= (ZM_AGG_BAW_MASK)) + { + /* + * queue full + */ + //DbgPrint("index >= 64, seq_start=%d, seq_no=%d\n", tid_rx->seq_start, seq_no); + zfAggRxFlush(dev, seq_no, tid_rx); + //tid_rx->seq_start = seq_no; + index = seq_no - tid_rx->seq_start; + if ((tid_rx->seq_start > seq_no) && (tid_rx->seq_start > 1000) && (tid_rx->seq_start - 1000) > seq_no) + { + //index = seq_no - tid_rx->seq_start; + index += 4096; + } + //index = seq_no - tid_rx->seq_start; + while (index >= (ZM_AGG_BAW_MASK)) { + //DbgPrint("index >= 64, seq_start=%d, seq_no=%d\n", tid_rx->seq_start, seq_no); + tid_rx->seq_start = (tid_rx->seq_start + ZM_AGG_BAW_MASK) & (4096 - 1); + index = seq_no - tid_rx->seq_start; + if ((tid_rx->seq_start > seq_no) && (tid_rx->seq_start > 1000) && (tid_rx->seq_start - 1000) > seq_no) + { + index += 4096; + } + } + } + + + q_index = (tid_rx->baw_tail + index) & ZM_AGG_BAW_MASK; + if (tid_rx->frame[q_index].buf && (((tid_rx->baw_head - tid_rx->baw_tail) & ZM_AGG_BAW_MASK) > + (((q_index) - tid_rx->baw_tail) & ZM_AGG_BAW_MASK))) + { + + ZM_PERFORMANCE_DUP(dev, tid_rx->frame[q_index].buf, buf); + zfwBufFree(dev, buf, 0); + //DbgPrint("Free a duplicate packet, seq_start=%d, seq_no=%d\n", tid_rx->seq_start, seq_no); + //DbgPrint("head=%d, tail=%d", tid_rx->baw_head, tid_rx->baw_tail); + /* + * duplicate packet + */ + return 0; + } + + zmw_enter_critical_section(dev); + if(tid_rx->frame[q_index].buf) { + zfwBufFree(dev, tid_rx->frame[q_index].buf, 0); + tid_rx->frame[q_index].buf = 0; + } + + tid_rx->frame[q_index].buf = buf; + tid_rx->frame[q_index].arrivalTime = zm_agg_GetTime(); + zfwMemoryCopy((void*)&tid_rx->frame[q_index].addInfo, (void*)addInfo, sizeof(struct zsAdditionInfo)); + + /* + * for debug simulated aggregation only, + * should be done in rx of ADDBA Request + */ + //tid_rx->addInfo = addInfo; + + + if (((tid_rx->baw_head - tid_rx->baw_tail) & ZM_AGG_BAW_MASK) <= index) + { + //tid_rx->baw_size = index + 1; + if (((tid_rx->baw_head - tid_rx->baw_tail) & ZM_AGG_BAW_MASK) <= + //((q_index + 1) & ZM_AGG_BAW_MASK)) + (((q_index) - tid_rx->baw_tail) & ZM_AGG_BAW_MASK))//tid_rx->baw_size ) + tid_rx->baw_head = (q_index + 1) & ZM_AGG_BAW_MASK; + } + zmw_leave_critical_section(dev); + + /* + * success + */ + //DbgPrint("head=%d, tail=%d, start=%d", tid_rx->baw_head, tid_rx->baw_tail, tid_rx->seq_start); + return 1; +} + +u16_t zfAggRxFlush(zdev_t* dev, u16_t seq_no, struct agg_tid_rx *tid_rx) +{ + zbuf_t* pbuf; + u16_t seq; + struct zsAdditionInfo addInfo; + zmw_get_wlan_dev(dev); + zmw_declare_for_critical_section(); + + ZM_PERFORMANCE_RX_FLUSH(dev); + + while (1) + { + zmw_enter_critical_section(dev); + if (tid_rx->baw_tail == tid_rx->baw_head) { + zmw_leave_critical_section(dev); + break; + } + + pbuf = tid_rx->frame[tid_rx->baw_tail].buf; + zfwMemoryCopy((void*)&addInfo, (void*)&tid_rx->frame[tid_rx->baw_tail].addInfo, sizeof(struct zsAdditionInfo)); + tid_rx->frame[tid_rx->baw_tail].buf = 0; + //if(pbuf && tid_rx->baw_size > 0) tid_rx->baw_size--; + tid_rx->baw_tail = (tid_rx->baw_tail + 1) & ZM_AGG_BAW_MASK; + tid_rx->seq_start = (tid_rx->seq_start + 1) & (4096 - 1); + zmw_leave_critical_section(dev); + + if (pbuf) + { + + ZM_PERFORMANCE_RX_SEQ(dev, pbuf); + + if (wd->zfcbRecv80211 != NULL) + { + seq = zmw_rx_buf_readh(dev, pbuf, 22) >> 4; + //DbgPrint("Recv indicate seq=%d\n", seq); + //DbgPrint("2. seq=%d\n", seq); + wd->zfcbRecv80211(dev, pbuf, &addInfo); + } + else + { + seq = zmw_rx_buf_readh(dev, pbuf, 22) >> 4; + //DbgPrint("Recv indicate seq=%d\n", seq); + zfiRecv80211(dev, pbuf, &addInfo); + } + } + } + + zmw_enter_critical_section(dev); + tid_rx->baw_head = tid_rx->baw_tail = 0; + zmw_leave_critical_section(dev); + return 1; +} + + + +/************************************************************************/ +/* */ +/* FUNCTION DESCRIPTION zfAggRxFreeBuf */ +/* Frees all queued packets in buffer when the driver is down. */ +/* The zfFreeResource() will check if the buffer is all freed. */ +/* */ +/* INPUTS */ +/* dev : device pointer */ +/* */ +/* OUTPUTS */ +/* ZM_SUCCESS */ +/* */ +/* AUTHOR */ +/* Honda Atheros Communications, INC. 2006.12 */ +/* */ +/************************************************************************/ +u16_t zfAggRxFreeBuf(zdev_t* dev, u16_t destroy) +{ + u16_t i; + zbuf_t* buf; + struct agg_tid_rx *tid_rx; + + TID_TX tid_tx; + //struct bufInfo *buf_info; + + zmw_get_wlan_dev(dev); + zmw_declare_for_critical_section(); + + for (i=0; i<ZM_AGG_POOL_SIZE; i++) + { + u16_t j; + + tid_rx = wd->tid_rx[i]; + + for(j=0; j <= ZM_AGG_BAW_SIZE; j++) + { + zmw_enter_critical_section(dev); + buf = tid_rx->frame[j].buf; + tid_rx->frame[j].buf = 0; + zmw_leave_critical_section(dev); + + if (buf) + { + zfwBufFree(dev, buf, 0); + } + } + + #if 0 + if ( tid_rx->baw_head != tid_rx->baw_tail ) + { + while (tid_rx->baw_head != tid_rx->baw_tail) + { + buf = tid_rx->frame[tid_rx->baw_tail].buf; + tid_rx->frame[tid_rx->baw_tail].buf = 0; + if (buf) + { + zfwBufFree(dev, buf, 0); + + zmw_enter_critical_section(dev); + tid_rx->frame[tid_rx->baw_tail].buf = 0; + zmw_leave_critical_section(dev); + } + zmw_enter_critical_section(dev); + //if (tid_rx->baw_size > 0)tid_rx->baw_size--; + tid_rx->baw_tail = (tid_rx->baw_tail + 1) & ZM_AGG_BAW_MASK; + tid_rx->seq_start++; + zmw_leave_critical_section(dev); + } + } + #endif + + zmw_enter_critical_section(dev); + tid_rx->seq_start = 0; + tid_rx->baw_head = tid_rx->baw_tail = 0; + tid_rx->aid = ZM_MAX_STA_SUPPORT; + zmw_leave_critical_section(dev); + + #ifdef ZM_ENABLE_AGGREGATION + #ifndef ZM_ENABLE_FW_BA_RETRANSMISSION //disable BAW + if (tid_baw->enabled) { + zm_msg1_agg(ZM_LV_0, "Device down, clear BAW queue:", i); + BAW->disable(dev, tid_baw); + } + #endif + #endif + if (1 == wd->aggQPool[i]->aggQEnabled) { + tid_tx = wd->aggQPool[i]; + buf = zfAggTxGetVtxq(dev, tid_tx); + while (buf) { + zfwBufFree(dev, buf, 0); + buf = zfAggTxGetVtxq(dev, tid_tx); + } + } + + if(destroy) { + zfwMemFree(dev, wd->aggQPool[i], sizeof(struct aggQueue)); + zfwMemFree(dev, wd->tid_rx[i], sizeof(struct agg_tid_rx)); + } + } + #ifdef ZM_ENABLE_AGGREGATION + #ifndef ZM_ENABLE_FW_BA_RETRANSMISSION //disable BAW + if(destroy) zfwMemFree(dev, BAW, sizeof(struct baw_enabler)); + #endif + #endif + return ZM_SUCCESS; +} + + +void zfAggRecvBAR(zdev_t* dev, zbuf_t *buf) { + u16_t start_seq, len; + u8_t i, bitmap[8]; + len = zfwBufGetSize(dev, buf); + start_seq = zmw_rx_buf_readh(dev, buf, len-2); + DbgPrint("Received a BAR Control frame, start_seq=%d", start_seq>>4); + /* todo: set the bitmap by reordering buffer! */ + for (i=0; i<8; i++) bitmap[i]=0; + zfSendBA(dev, start_seq, bitmap); +} + +#ifdef ZM_ENABLE_AGGREGATION +#ifndef ZM_ENABLE_FW_BA_RETRANSMISSION //disable BAW +void zfAggTxRetransmit(zdev_t* dev, struct bufInfo *buf_info, struct aggControl *aggControl, TID_TX tid_tx) { + u16_t removeLen; + u16_t err; + + zmw_get_wlan_dev(dev); + if (aggControl && (ZM_AGG_FIRST_MPDU == aggControl->ampduIndication) ) { + tid_tx->bar_ssn = buf_info->baw_header->header[15]; + aggControl->tid_baw->start_seq = tid_tx->bar_ssn >> 4; + zm_msg1_agg(ZM_LV_0, "start seq=", tid_tx->bar_ssn >> 4); + } + buf_info->baw_header->header[4] |= (1 << 11); + if (aggControl && aggControl->aggEnabled) { + //if (wd->enableAggregation==0 && !(buf_info->baw_header->header[6]&0x1)) + //{ + //if (((buf_info->baw_header->header[2] & 0x3) == 2)) + //{ + /* Enable aggregation */ + buf_info->baw_header->header[1] |= 0x20; + if (ZM_AGG_LAST_MPDU == aggControl->ampduIndication) { + buf_info->baw_header->header[1] |= 0x4000; + } + else { + buf_info->baw_header->header[1] &= ~0x4000; + //zm_debug_msg0("ZM_AGG_LAST_MPDU"); + } + //} + //else { + // zm_debug_msg1("no aggr, header[2]&0x3 = ",buf_info->baw_header->header[2] & 0x3) + // aggControl->aggEnabled = 0; + //} + //} + //else { + // zm_debug_msg1("no aggr, wd->enableAggregation = ", wd->enableAggregation); + // zm_debug_msg1("no aggr, !header[6]&0x1 = ",!(buf_info->baw_header->header[6]&0x1)); + // aggControl->aggEnabled = 0; + //} + } + + /*if (aggControl->tid_baw) { + struct baw_header_r header_r; + + header_r.header = buf_info->baw_header->header; + header_r.mic = buf_info->baw_header->mic; + header_r.snap = buf_info->baw_header->snap; + header_r.headerLen = buf_info->baw_header->headerLen; + header_r.micLen = buf_info->baw_header->micLen; + header_r.snapLen = buf_info->baw_header->snapLen; + header_r.removeLen = buf_info->baw_header->removeLen; + header_r.keyIdx = buf_info->baw_header->keyIdx; + + BAW->insert(dev, buf_info->buf, tid_tx->bar_ssn >> 4, aggControl->tid_baw, buf_info->baw_retransmit, &header_r); + }*/ + + if ((err = zfHpSend(dev, + buf_info->baw_header->header, + buf_info->baw_header->headerLen, + buf_info->baw_header->snap, + buf_info->baw_header->snapLen, + buf_info->baw_header->mic, + buf_info->baw_header->micLen, + buf_info->buf, + buf_info->baw_header->removeLen, + ZM_EXTERNAL_ALLOC_BUF, + (u8_t)tid_tx->ac, + buf_info->baw_header->keyIdx)) != ZM_SUCCESS) + { + goto zlError; + } + + return; + +zlError: + zfwBufFree(dev, buf_info->buf, 0); + return; + +} +#endif //disable BAW +#endif +/************************************************************************/ +/* */ +/* FUNCTION DESCRIPTION zfAggTxSendEth */ +/* Called to transmit Ethernet frame from upper elayer. */ +/* */ +/* INPUTS */ +/* dev : device pointer */ +/* buf : buffer pointer */ +/* port : WLAN port, 0=>standard, 0x10-0x17=>VAP, 0x20-0x25=>WDS */ +/* */ +/* OUTPUTS */ +/* error code */ +/* */ +/* AUTHOR */ +/* Stephen, Honda Atheros Communications, Inc. 2006.12 */ +/* */ +/************************************************************************/ +u16_t zfAggTxSendEth(zdev_t* dev, zbuf_t* buf, u16_t port, u16_t bufType, u8_t flag, struct aggControl *aggControl, TID_TX tid_tx) +{ + u16_t err; + //u16_t addrTblSize; + //struct zsAddrTbl addrTbl; + u16_t removeLen; + u16_t header[(8+30+2+18)/2]; /* ctr+(4+a1+a2+a3+2+a4)+qos+iv */ + u16_t headerLen; + u16_t mic[8/2]; + u16_t micLen; + u16_t snap[8/2]; + u16_t snapLen; + u16_t fragLen; + u16_t frameLen; + u16_t fragNum; + struct zsFrag frag; + u16_t i, id; + u16_t da[3]; + u16_t sa[3]; + u8_t up; + u8_t qosType, keyIdx = 0; + u16_t fragOff; + + zmw_get_wlan_dev(dev); + + zmw_declare_for_critical_section(); + + zm_msg1_tx(ZM_LV_2, "zfTxSendEth(), port=", port); + + /* Get IP TOS for QoS AC and IP frag offset */ + zfTxGetIpTosAndFrag(dev, buf, &up, &fragOff); + +#ifdef ZM_ENABLE_NATIVE_WIFI + if ( wd->wlanMode == ZM_MODE_INFRASTRUCTURE ) + { + /* DA */ + da[0] = zmw_tx_buf_readh(dev, buf, 16); + da[1] = zmw_tx_buf_readh(dev, buf, 18); + da[2] = zmw_tx_buf_readh(dev, buf, 20); + /* SA */ + sa[0] = zmw_tx_buf_readh(dev, buf, 10); + sa[1] = zmw_tx_buf_readh(dev, buf, 12); + sa[2] = zmw_tx_buf_readh(dev, buf, 14); + } + else if ( wd->wlanMode == ZM_MODE_IBSS ) + { + /* DA */ + da[0] = zmw_tx_buf_readh(dev, buf, 4); + da[1] = zmw_tx_buf_readh(dev, buf, 6); + da[2] = zmw_tx_buf_readh(dev, buf, 8); + /* SA */ + sa[0] = zmw_tx_buf_readh(dev, buf, 10); + sa[1] = zmw_tx_buf_readh(dev, buf, 12); + sa[2] = zmw_tx_buf_readh(dev, buf, 14); + } + else if ( wd->wlanMode == ZM_MODE_AP ) + { + /* DA */ + da[0] = zmw_tx_buf_readh(dev, buf, 4); + da[1] = zmw_tx_buf_readh(dev, buf, 6); + da[2] = zmw_tx_buf_readh(dev, buf, 8); + /* SA */ + sa[0] = zmw_tx_buf_readh(dev, buf, 16); + sa[1] = zmw_tx_buf_readh(dev, buf, 18); + sa[2] = zmw_tx_buf_readh(dev, buf, 20); + } + else + { + // + } +#else + /* DA */ + da[0] = zmw_tx_buf_readh(dev, buf, 0); + da[1] = zmw_tx_buf_readh(dev, buf, 2); + da[2] = zmw_tx_buf_readh(dev, buf, 4); + /* SA */ + sa[0] = zmw_tx_buf_readh(dev, buf, 6); + sa[1] = zmw_tx_buf_readh(dev, buf, 8); + sa[2] = zmw_tx_buf_readh(dev, buf, 10); +#endif + //Decide Key Index in ATOM, No meaning in OTUS--CWYang(m) + if (wd->wlanMode == ZM_MODE_AP) + { + keyIdx = wd->ap.bcHalKeyIdx[port]; + id = zfApFindSta(dev, da); + if (id != 0xffff) + { + switch (wd->ap.staTable[id].encryMode) + { + case ZM_AES: + case ZM_TKIP: +#ifdef ZM_ENABLE_CENC + case ZM_CENC: +#endif //ZM_ENABLE_CENC + keyIdx = wd->ap.staTable[id].keyIdx; + break; + } + } + } + else + { + switch (wd->sta.encryMode) + { + case ZM_WEP64: + case ZM_WEP128: + case ZM_WEP256: + keyIdx = wd->sta.keyId; + break; + case ZM_AES: + case ZM_TKIP: + if ((da[0]& 0x1)) + keyIdx = 5; + else + keyIdx = 4; + break; +#ifdef ZM_ENABLE_CENC + case ZM_CENC: + keyIdx = wd->sta.cencKeyId; + break; +#endif //ZM_ENABLE_CENC + } + } + + /* Create SNAP */ + removeLen = zfTxGenWlanSnap(dev, buf, snap, &snapLen); + //zm_msg1_tx(ZM_LV_0, "fragOff=", fragOff); + + fragLen = wd->fragThreshold; + frameLen = zfwBufGetSize(dev, buf); + frameLen -= removeLen; + +#if 0 + /* Create MIC */ + if ( (wd->wlanMode == ZM_MODE_INFRASTRUCTURE)&& + (wd->sta.encryMode == ZM_TKIP) ) + { + if ( frameLen > fragLen ) + { + micLen = zfTxGenWlanTail(dev, buf, snap, snapLen, mic); + } + else + { + /* append MIC by HMAC */ + micLen = 8; + } + } + else + { + micLen = 0; + } +#else + if ( frameLen > fragLen ) + { + micLen = zfTxGenWlanTail(dev, buf, snap, snapLen, mic); + } + else + { + /* append MIC by HMAC */ + micLen = 0; + } +#endif + + /* Access Category */ + if (wd->wlanMode == ZM_MODE_AP) + { + zfApGetStaQosType(dev, da, &qosType); + if (qosType == 0) + { + up = 0; + } + } + else if (wd->wlanMode == ZM_MODE_INFRASTRUCTURE) + { + if (wd->sta.wmeConnected == 0) + { + up = 0; + } + } + else + { + /* TODO : STA QoS control field */ + up = 0; + } + + /* Assign sequence number */ + zmw_enter_critical_section(dev); + frag.seq[0] = ((wd->seq[zcUpToAc[up&0x7]]++) << 4); + if (aggControl && (ZM_AGG_FIRST_MPDU == aggControl->ampduIndication) ) { + tid_tx->bar_ssn = frag.seq[0]; + + zm_msg1_agg(ZM_LV_0, "start seq=", tid_tx->bar_ssn >> 4); + } + //tid_tx->baw_buf[tid_tx->baw_head-1].baw_seq=frag.seq[0]; + zmw_leave_critical_section(dev); + + + frag.buf[0] = buf; + frag.bufType[0] = bufType; + frag.flag[0] = flag; + fragNum = 1; + + for (i=0; i<fragNum; i++) + { + /* Create WLAN header(Control Setting + 802.11 header + IV) */ + if (up !=0 ) zm_debug_msg1("up not 0, up=",up); + headerLen = zfTxGenWlanHeader(dev, frag.buf[i], header, frag.seq[i], + frag.flag[i], snapLen+micLen, removeLen, + port, da, sa, up, &micLen, snap, snapLen, + aggControl); + + /* Get buffer DMA address */ + //if ((addrTblSize = zfwBufMapDma(dev, frag.buf[i], &addrTbl)) == 0) + //if ((addrTblSize = zfwMapTxDma(dev, frag.buf[i], &addrTbl)) == 0) + //{ + // err = ZM_ERR_BUFFER_DMA_ADDR; + // goto zlError; + //} + + /* Flush buffer on cache */ + //zfwBufFlush(dev, frag.buf[i]); + +#if 0 + zm_msg1_tx(ZM_LV_0, "headerLen=", headerLen); + zm_msg1_tx(ZM_LV_0, "snapLen=", snapLen); + zm_msg1_tx(ZM_LV_0, "micLen=", micLen); + zm_msg1_tx(ZM_LV_0, "removeLen=", removeLen); + zm_msg1_tx(ZM_LV_0, "addrTblSize=", addrTblSize); + zm_msg1_tx(ZM_LV_0, "frag.bufType[0]=", frag.bufType[0]); +#endif + + fragLen = zfwBufGetSize(dev, frag.buf[i]); + if ((da[0]&0x1) == 0) + { + wd->commTally.txUnicastFrm++; + wd->commTally.txUnicastOctets += (fragLen+snapLen); + } + else if ((da[0]& 0x1)) + { + wd->commTally.txBroadcastFrm++; + wd->commTally.txBroadcastOctets += (fragLen+snapLen); + } + else + { + wd->commTally.txMulticastFrm++; + wd->commTally.txMulticastOctets += (fragLen+snapLen); + } + wd->ledStruct.txTraffic++; + +#if 0 //Who care this? + if ( (i)&&(i == (fragNum-1)) ) + { + wd->trafTally.txDataByteCount -= micLen; + } +#endif + + /*if (aggControl->tid_baw && aggControl->aggEnabled) { + struct baw_header_r header_r; + + header_r.header = header; + header_r.mic = mic; + header_r.snap = snap; + header_r.headerLen = headerLen; + header_r.micLen = micLen; + header_r.snapLen = snapLen; + header_r.removeLen = removeLen; + header_r.keyIdx = keyIdx; + + BAW->insert(dev, buf, tid_tx->bar_ssn >> 4, aggControl->tid_baw, 0, &header_r); + }*/ + + if ((err = zfHpSend(dev, header, headerLen, snap, snapLen, + mic, micLen, frag.buf[i], removeLen, + frag.bufType[i], zcUpToAc[up&0x7], keyIdx)) != ZM_SUCCESS) + { + goto zlError; + } + + + continue; + +zlError: + if (frag.bufType[i] == ZM_EXTERNAL_ALLOC_BUF) + { + zfwBufFree(dev, frag.buf[i], err); + } + else if (frag.bufType[i] == ZM_INTERNAL_ALLOC_BUF) + { + zfwBufFree(dev, frag.buf[i], 0); + } + else + { + zm_assert(0); + } + } /* for (i=0; i<fragNum; i++) */ + + return ZM_SUCCESS; +} + +/* + * zfAggSendADDBA() refers zfSendMmFrame() in cmm.c + */ +u16_t zfAggSendAddbaRequest(zdev_t* dev, u16_t *dst, u16_t ac, u16_t up) +{ + zbuf_t* buf; + //u16_t addrTblSize; + //struct zsAddrTbl addrTbl; + //u16_t err; + u16_t offset = 0; + u16_t hlen = 32; + u16_t header[(24+25+1)/2]; + u16_t vap = 0; + u16_t i; + u8_t encrypt = 0; + + //zmw_get_wlan_dev(dev); + + //zmw_declare_for_critical_section(); + + + /* + * TBD : Maximum size of managment frame + */ + if ((buf = zfwBufAllocate(dev, 1024)) == NULL) + { + zm_msg0_mm(ZM_LV_0, "Alloc mm buf Fail!"); + return ZM_SUCCESS; + } + + /* + * Reserve room for wlan header + */ + offset = hlen; + + /* + * add addba frame body + */ + offset = zfAggSetAddbaFrameBody(dev, buf, offset, ac, up); + + + zfwBufSetSize(dev, buf, offset); + + /* + * Copy wlan header + */ + zfAggGenAddbaHeader(dev, dst, header, offset-hlen, buf, vap, encrypt); + for (i=0; i<(hlen>>1); i++) + { + zmw_tx_buf_writeh(dev, buf, i*2, header[i]); + } + + /* Get buffer DMA address */ + //if ((addrTblSize = zfwBufMapDma(dev, buf, &addrTbl)) == 0) + //if ((addrTblSize = zfwMapTxDma(dev, buf, &addrTbl)) == 0) + //{ + // goto zlError; + //} + + //zm_msg2_mm(ZM_LV_2, "offset=", offset); + //zm_msg2_mm(ZM_LV_2, "hlen=", hlen); + //zm_msg2_mm(ZM_LV_2, "addrTblSize=", addrTblSize); + //zm_msg2_mm(ZM_LV_2, "addrTbl.len[0]=", addrTbl.len[0]); + //zm_msg2_mm(ZM_LV_2, "addrTbl.physAddrl[0]=", addrTbl.physAddrl[0]); + //zm_msg2_mm(ZM_LV_2, "buf->data=", buf->data); + + #if 0 + if ((err = zfHpSend(dev, NULL, 0, NULL, 0, NULL, 0, buf, 0, + ZM_INTERNAL_ALLOC_BUF, 0, 0xff)) != ZM_SUCCESS) + { + goto zlError; + } + #else + zfPutVmmq(dev, buf); + zfPushVtxq(dev); + #endif + + return ZM_SUCCESS; + +} + +u16_t zfAggSetAddbaFrameBody(zdev_t* dev, zbuf_t* buf, u16_t offset, u16_t ac, u16_t up) +{ + u16_t ba_parameter, start_seq; + + zmw_get_wlan_dev(dev); + + //zmw_declare_for_critical_section(); + /* + * ADDBA Request frame body + */ + + /* + * Category + */ + zmw_tx_buf_writeb(dev, buf, offset++, 3); + /* + * Action details = 0 + */ + zmw_tx_buf_writeb(dev, buf, offset++, ZM_WLAN_ADDBA_REQUEST_FRAME); + /* + * Dialog Token = nonzero + * TBD: define how to get dialog token? + */ + zmw_tx_buf_writeb(dev, buf, offset++, 2); + /* + * Block Ack parameter set + * BA policy = 1 for immediate BA, 0 for delayed BA + * TID(4bits) & buffer size(4bits) (TID=up & buffer size=0x80) + * TBD: how to get buffer size? + * ¢z¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢s¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢s¢w¢w¢w¢w¢w¢w¢w¢w¢s¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢{ + * ¢x B0 ¢x B1 ¢x B2 B5 ¢x B6 B15 ¢x + * ¢u¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢q¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢q¢w¢w¢w¢w¢w¢w¢w¢w¢q¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢t + * ¢x Reserved ¢x BA policy ¢x TID ¢x Buffer size ¢x + * ¢|¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢r¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢r¢w¢w¢w¢w¢w¢w¢w¢w¢r¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢} + */ + ba_parameter = 1 << 12; // buffer size = 0x40(64) + ba_parameter |= up << 2; // tid = up + ba_parameter |= 2; // ba policy = 1 + zmw_tx_buf_writeh(dev, buf, offset, ba_parameter); + offset+=2; + /* + * BA timeout value + */ + zmw_tx_buf_writeh(dev, buf, offset, 0); + offset+=2; + /* + * BA starting sequence number + * ¢z¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢s¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢{ + * ¢x B0 B3 ¢x B4 B15 ¢x + * ¢u¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢q¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢t + * ¢x Frag num(0) ¢x BA starting seq num ¢x + * ¢|¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢r¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢} + */ + start_seq = ((wd->seq[ac]) << 4) & 0xFFF0; + zmw_tx_buf_writeh(dev, buf, offset, start_seq); + offset+=2; + + return offset; +} + +u16_t zfAggGenAddbaHeader(zdev_t* dev, u16_t* dst, + u16_t* header, u16_t len, zbuf_t* buf, u16_t vap, u8_t encrypt) +{ + u8_t hlen = 32; // MAC ctrl + PHY ctrl + 802.11 MM header + //u8_t frameType = ZM_WLAN_FRAME_TYPE_ACTION; + + zmw_get_wlan_dev(dev); + + zmw_declare_for_critical_section(); + + /* + * Generate control setting + */ + //bodyLen = zfwBufGetSize(dev, buf); + header[0] = 24+len+4; //Length + header[1] = 0x8; //MAC control, backoff + (ack) + +#if 0 + /* CCK 1M */ + header[2] = 0x0f00; //PHY control L + header[3] = 0x0000; //PHY control H +#else + /* OFDM 6M */ + header[2] = 0x0f01; //PHY control L + header[3] = 0x000B; //PHY control H +#endif + + /* + * Generate WLAN header + * Frame control frame type and subtype + */ + header[4+0] = ZM_WLAN_FRAME_TYPE_ACTION; + /* + * Duration + */ + header[4+1] = 0; + + if (wd->wlanMode == ZM_MODE_INFRASTRUCTURE) + { + 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]; + } + else if (wd->wlanMode == ZM_MODE_AP) + { + /* Address 3 = BSSID */ + header[4+8] = wd->macAddr[0]; + header[4+9] = wd->macAddr[1]; + header[4+10] = wd->macAddr[2] + (vap<<8); + } + + /* 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) + { + header[4+7] = wd->macAddr[2] + (vap<<8); + } + 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); + + + return hlen; +} + + +u16_t zfAggProcessAction(zdev_t* dev, zbuf_t* buf) +{ + u16_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; + + } + + return ZM_SUCCESS; +} + + +u16_t zfAggBlockAckActionFrame(zdev_t* dev, zbuf_t* buf) +{ + u8_t action; + + //zmw_get_wlan_dev(dev); + + //zmw_declare_for_critical_section(); + + action = zmw_rx_buf_readb(dev, buf, 25); +#ifdef ZM_ENABLE_AGGREGATION + switch (action) + { + case ZM_WLAN_ADDBA_REQUEST_FRAME: + zm_msg0_agg(ZM_LV_0, "Received BA Action frame is ADDBA request"); + zfAggRecvAddbaRequest(dev, buf); + break; + case ZM_WLAN_ADDBA_RESPONSE_FRAME: + zm_msg0_agg(ZM_LV_0, "Received BA Action frame is ADDBA response"); + zfAggRecvAddbaResponse(dev, buf); + break; + case ZM_WLAN_DELBA_FRAME: + zfAggRecvDelba(dev, buf); + break; + } +#endif + return ZM_SUCCESS; +} + +u16_t zfAggRecvAddbaRequest(zdev_t* dev, zbuf_t* buf) +{ + //u16_t dialog; + struct aggBaFrameParameter bf; + u16_t i; + //zmw_get_wlan_dev(dev); + + //zmw_declare_for_critical_section(); + + bf.buf = buf; + bf.dialog = zmw_rx_buf_readb(dev, buf, 26); + /* + * ba parameter set + */ + bf.ba_parameter = zmw_rx_buf_readh(dev, buf, 27); + bf.ba_policy = (bf.ba_parameter >> 1) & 1; + bf.tid = (bf.ba_parameter >> 2) & 0xF; + bf.buffer_size = (bf.ba_parameter >> 6); + /* + * BA timeout value + */ + bf.ba_timeout = zmw_rx_buf_readh(dev, buf, 29); + /* + * BA starting sequence number + */ + bf.ba_start_seq = zmw_rx_buf_readh(dev, buf, 31) >> 4; + + i=26; + while(i < 32) { + zm_debug_msg2("Recv ADDBA Req:", zmw_rx_buf_readb(dev,buf,i)); + i++; + } + + zfAggSendAddbaResponse(dev, &bf); + + zfAggAddbaSetTidRx(dev, buf, &bf); + + return ZM_SUCCESS; +} + +u16_t zfAggAddbaSetTidRx(zdev_t* dev, zbuf_t* buf, struct aggBaFrameParameter *bf) +{ + u16_t i, ac, aid, fragOff; + u16_t src[3]; + u16_t offset = 0; + u8_t up; + struct agg_tid_rx *tid_rx = NULL; + + zmw_get_wlan_dev(dev); + + zmw_declare_for_critical_section(); + + src[0] = zmw_rx_buf_readh(dev, buf, offset+10); + src[1] = zmw_rx_buf_readh(dev, buf, offset+12); + src[2] = zmw_rx_buf_readh(dev, buf, offset+14); + aid = zfApFindSta(dev, src); + + zfTxGetIpTosAndFrag(dev, buf, &up, &fragOff); + ac = zcUpToAc[up&0x7] & 0x3; + + ac = bf->tid; + + for (i=0; i<ZM_AGG_POOL_SIZE ; i++) + { + if((wd->tid_rx[i]->aid == aid) && (wd->tid_rx[i]->ac == ac)) + { + tid_rx = wd->tid_rx[i]; + break; + } + } + + if (!tid_rx) + { + for (i=0; i<ZM_AGG_POOL_SIZE; i++) + { + if (wd->tid_rx[i]->aid == ZM_MAX_STA_SUPPORT) + { + tid_rx = wd->tid_rx[i]; + break; + } + } + if (!tid_rx) + return 0; + } + + zmw_enter_critical_section(dev); + + tid_rx->aid = aid; + tid_rx->ac = ac; + tid_rx->addBaExchangeStatusCode = ZM_AGG_ADDBA_RESPONSE; + tid_rx->seq_start = bf->ba_start_seq; + tid_rx->baw_head = tid_rx->baw_tail = 0; + tid_rx->sq_exceed_count = tid_rx->sq_behind_count = 0; + zmw_leave_critical_section(dev); + + return 0; +} + +u16_t zfAggRecvAddbaResponse(zdev_t* dev, zbuf_t* buf) +{ + u16_t i,ac, aid=0; + u16_t src[3]; + struct aggBaFrameParameter bf; + + zmw_get_wlan_dev(dev); + + //zmw_declare_for_critical_section(); + + 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 (wd->wlanMode == ZM_MODE_AP) + aid = zfApFindSta(dev, src); + + + bf.buf = buf; + bf.dialog = zmw_rx_buf_readb(dev, buf, 26); + bf.status_code = zmw_rx_buf_readh(dev, buf, 27); + if (!bf.status_code) + { + wd->addbaComplete=1; + } + + /* + * ba parameter set + */ + bf.ba_parameter = zmw_rx_buf_readh(dev, buf, 29); + bf.ba_policy = (bf.ba_parameter >> 1) & 1; + bf.tid = (bf.ba_parameter >> 2) & 0xF; + bf.buffer_size = (bf.ba_parameter >> 6); + /* + * BA timeout value + */ + bf.ba_timeout = zmw_rx_buf_readh(dev, buf, 31); + + i=26; + while(i < 32) { + zm_debug_msg2("Recv ADDBA Rsp:", zmw_rx_buf_readb(dev,buf,i)); + i++; + } + + ac = zcUpToAc[bf.tid&0x7] & 0x3; + + //zmw_enter_critical_section(dev); + + //wd->aggSta[aid].aggFlag[ac] = 0; + + //zmw_leave_critical_section(dev); + + return ZM_SUCCESS; +} + +u16_t zfAggRecvDelba(zdev_t* dev, zbuf_t* buf) +{ + //zmw_get_wlan_dev(dev); + + //zmw_declare_for_critical_section(); + return ZM_SUCCESS; +} + +u16_t zfAggSendAddbaResponse(zdev_t* dev, struct aggBaFrameParameter *bf) +{ + zbuf_t* buf; + //u16_t addrTblSize; + //struct zsAddrTbl addrTbl; + //u16_t err; + u16_t offset = 0; + u16_t hlen = 32; + u16_t header[(24+25+1)/2]; + u16_t vap = 0; + u16_t i; + u8_t encrypt = 0; + u16_t dst[3]; + + //zmw_get_wlan_dev(dev); + + //zmw_declare_for_critical_section(); + + + /* + * TBD : Maximum size of managment frame + */ + if ((buf = zfwBufAllocate(dev, 1024)) == NULL) + { + zm_msg0_mm(ZM_LV_0, "Alloc mm buf Fail!"); + return ZM_SUCCESS; + } + + /* + * Reserve room for wlan header + */ + offset = hlen; + + /* + * add addba frame body + */ + offset = zfAggSetAddbaResponseFrameBody(dev, buf, bf, offset); + + + zfwBufSetSize(dev, buf, offset); + + /* + * Copy wlan header + */ + + dst[0] = zmw_rx_buf_readh(dev, bf->buf, 10); + dst[1] = zmw_rx_buf_readh(dev, bf->buf, 12); + dst[2] = zmw_rx_buf_readh(dev, bf->buf, 14); + zfAggGenAddbaHeader(dev, dst, header, offset-hlen, buf, vap, encrypt); + for (i=0; i<(hlen>>1); i++) + { + zmw_tx_buf_writeh(dev, buf, i*2, header[i]); + } + + /* Get buffer DMA address */ + //if ((addrTblSize = zfwBufMapDma(dev, buf, &addrTbl)) == 0) + //if ((addrTblSize = zfwMapTxDma(dev, buf, &addrTbl)) == 0) + //{ + // goto zlError; + //} + + //zm_msg2_mm(ZM_LV_2, "offset=", offset); + //zm_msg2_mm(ZM_LV_2, "hlen=", hlen); + //zm_msg2_mm(ZM_LV_2, "addrTblSize=", addrTblSize); + //zm_msg2_mm(ZM_LV_2, "addrTbl.len[0]=", addrTbl.len[0]); + //zm_msg2_mm(ZM_LV_2, "addrTbl.physAddrl[0]=", addrTbl.physAddrl[0]); + //zm_msg2_mm(ZM_LV_2, "buf->data=", buf->data); + + #if 0 + if ((err = zfHpSend(dev, NULL, 0, NULL, 0, NULL, 0, buf, 0, + ZM_INTERNAL_ALLOC_BUF, 0, 0xff)) != ZM_SUCCESS) + { + goto zlError; + } + #else + zfPutVmmq(dev, buf); + zfPushVtxq(dev); + #endif + + //zfAggSendAddbaRequest(dev, dst, zcUpToAc[bf->tid&0x7] & 0x3, bf->tid); + return ZM_SUCCESS; + +} + +u16_t zfAggSetAddbaResponseFrameBody(zdev_t* dev, zbuf_t* buf, + struct aggBaFrameParameter *bf, u16_t offset) +{ + + //zmw_get_wlan_dev(dev); + + //zmw_declare_for_critical_section(); + /* + * ADDBA Request frame body + */ + + /* + * Category + */ + zmw_tx_buf_writeb(dev, buf, offset++, 3); + /* + * Action details = 0 + */ + zmw_tx_buf_writeb(dev, buf, offset++, ZM_WLAN_ADDBA_RESPONSE_FRAME); + /* + * Dialog Token = nonzero + */ + zmw_tx_buf_writeb(dev, buf, offset++, bf->dialog); + /* + * Status code + */ + zmw_tx_buf_writeh(dev, buf, offset, 0); + offset+=2; + /* + * Block Ack parameter set + * BA policy = 1 for immediate BA, 0 for delayed BA + * TID(4bits) & buffer size(4bits) (TID=0x1 & buffer size=0x80) + * TBD: how to get TID number and buffer size? + * ¢z¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢s¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢s¢w¢w¢w¢w¢w¢w¢w¢w¢s¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢{ + * ¢x B0 ¢x B1 ¢x B2 B5 ¢x B6 B15 ¢x + * ¢u¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢q¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢q¢w¢w¢w¢w¢w¢w¢w¢w¢q¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢t + * ¢x Reserved ¢x BA policy ¢x TID ¢x Buffer size ¢x + * ¢|¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢r¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢r¢w¢w¢w¢w¢w¢w¢w¢w¢r¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢} + */ + zmw_tx_buf_writeh(dev, buf, offset, bf->ba_parameter); + offset+=2; + /* + * BA timeout value + */ + zmw_tx_buf_writeh(dev, buf, offset, bf->ba_timeout); + offset+=2; + + return offset; +} + +void zfAggInvokeBar(zdev_t* dev, TID_TX tid_tx) +{ + struct aggBarControl aggBarControl; + //zmw_get_wlan_dev(dev); + + //zmw_declare_for_critical_section(); + //bar_control = aggBarControl->tid_info << 12 | aggBarControl->compressed_bitmap << 2 + // | aggBarControl->multi_tid << 1 | aggBarControl->bar_ack_policy; + aggBarControl.bar_ack_policy = 0; + aggBarControl.multi_tid = 0; + aggBarControl.compressed_bitmap = 0; + aggBarControl.tid_info = tid_tx->tid; + zfAggSendBar(dev, tid_tx, &aggBarControl); + + return; + +} +/* + * zfAggSendBar() refers zfAggSendAddbaRequest() + */ +u16_t zfAggSendBar(zdev_t* dev, TID_TX tid_tx, struct aggBarControl *aggBarControl) +{ + zbuf_t* buf; + //u16_t addrTblSize; + //struct zsAddrTbl addrTbl; + //u16_t err; + u16_t offset = 0; + u16_t hlen = 16+8; /* mac header + control headers*/ + u16_t header[(8+24+1)/2]; + u16_t vap = 0; + u16_t i; + u8_t encrypt = 0; + + //zmw_get_wlan_dev(dev); + + //zmw_declare_for_critical_section(); + + + /* + * TBD : Maximum size of managment frame + */ + if ((buf = zfwBufAllocate(dev, 1024)) == NULL) + { + zm_msg0_mm(ZM_LV_0, "Alloc mm buf Fail!"); + return ZM_SUCCESS; + } + + /* + * Reserve room for wlan header + */ + offset = hlen; + + /* + * add addba frame body + */ + offset = zfAggSetBarBody(dev, buf, offset, tid_tx, aggBarControl); + + + zfwBufSetSize(dev, buf, offset); + + /* + * Copy wlan header + */ + zfAggGenBarHeader(dev, tid_tx->dst, header, offset-hlen, buf, vap, encrypt); + for (i=0; i<(hlen>>1); i++) + { + zmw_tx_buf_writeh(dev, buf, i*2, header[i]); + } + + /* Get buffer DMA address */ + //if ((addrTblSize = zfwBufMapDma(dev, buf, &addrTbl)) == 0) + //if ((addrTblSize = zfwMapTxDma(dev, buf, &addrTbl)) == 0) + //{ + // goto zlError; + //} + + //zm_msg2_mm(ZM_LV_2, "offset=", offset); + //zm_msg2_mm(ZM_LV_2, "hlen=", hlen); + //zm_msg2_mm(ZM_LV_2, "addrTblSize=", addrTblSize); + //zm_msg2_mm(ZM_LV_2, "addrTbl.len[0]=", addrTbl.len[0]); + //zm_msg2_mm(ZM_LV_2, "addrTbl.physAddrl[0]=", addrTbl.physAddrl[0]); + //zm_msg2_mm(ZM_LV_2, "buf->data=", buf->data); + + #if 0 + if ((err = zfHpSend(dev, NULL, 0, NULL, 0, NULL, 0, buf, 0, + ZM_INTERNAL_ALLOC_BUF, 0, 0xff)) != ZM_SUCCESS) + { + goto zlError; + } + #else + zfPutVmmq(dev, buf); + zfPushVtxq(dev); + #endif + + return ZM_SUCCESS; + +} + +u16_t zfAggSetBarBody(zdev_t* dev, zbuf_t* buf, u16_t offset, TID_TX tid_tx, struct aggBarControl *aggBarControl) +{ + u16_t bar_control, start_seq; + + //zmw_get_wlan_dev(dev); + + //zmw_declare_for_critical_section(); + /* + * BAR Control frame body + */ + + /* + * BAR Control Field + * ¢z¢w¢w¢w¢w¢w¢w¢w¢w¢w¢s¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢s¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢s¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢s¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢{ + * ¢x B0 ¢x B1 ¢x B2 ¢x B3 B11 ¢x B12 B15 ¢x + * ¢u¢w¢w¢w¢w¢w¢w¢w¢w¢w¢q¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢q¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢q¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢q¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢t + * ¢x BAR Ack ¢x Multi-TID ¢x Compressed ¢x Reserved ¢x TID_INFO ¢x + * ¢x Policy ¢x ¢x Bitmap ¢x ¢x ¢x + * ¢|¢w¢w¢w¢w¢w¢w¢w¢w¢w¢r¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢r¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢r¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢r¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢} + */ + bar_control = aggBarControl->tid_info << 12 | aggBarControl->compressed_bitmap << 2 + | aggBarControl->multi_tid << 1 | aggBarControl->bar_ack_policy; + + zmw_tx_buf_writeh(dev, buf, offset, bar_control); + offset+=2; + if (0 == aggBarControl->multi_tid) { + /* + * BA starting sequence number + * ¢z¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢s¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢{ + * ¢x B0 B3 ¢x B4 B15 ¢x + * ¢u¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢q¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢t + * ¢x Frag num(0) ¢x BA starting seq num ¢x + * ¢|¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢r¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢} + */ + start_seq = (tid_tx->bar_ssn << 4) & 0xFFF0; + zmw_tx_buf_writeh(dev, buf, offset, start_seq); + offset+=2; + } + if (1 == aggBarControl->multi_tid && 1 == aggBarControl->compressed_bitmap) { + /* multi-tid BlockAckReq variant, not implemented*/ + } + + return offset; +} + +u16_t zfAggGenBarHeader(zdev_t* dev, u16_t* dst, + u16_t* header, u16_t len, zbuf_t* buf, u16_t vap, u8_t encrypt) +{ + u8_t hlen = 16+8; // MAC ctrl + PHY ctrl + 802.11 MM header + //u8_t frameType = ZM_WLAN_FRAME_TYPE_ACTION; + + zmw_get_wlan_dev(dev); + + zmw_declare_for_critical_section(); + + /* + * Generate control setting + */ + //bodyLen = zfwBufGetSize(dev, buf); + header[0] = 16+len+4; //Length + header[1] = 0x8; //MAC control, backoff + (ack) + +#if 1 + /* 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 + +#endif + /* + * Generate WLAN header + * Frame control frame type and subtype + */ + header[4+0] = ZM_WLAN_FRAME_TYPE_BAR; + /* + * Duration + */ + header[4+1] = 0; + + /* 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); + + + return hlen; +} |