/*
 * 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.h                                                    */
/*                                                                          */
/*  Abstract                                                                */
/*      This module contains A-MPDU aggregation relatived functions.        */
/*                                                                          */
/*  NOTES                                                                   */
/*      None                                                                */
/*                                                                          */
/****************************************************************************/
/*Revision History:                                                         */
/*    Who         When        What                                          */
/*    --------    --------    ----------------------------------------------*/
/*                                                                          */
/*    Honda       12-4-06     created                                       */
/*                                                                          */
/****************************************************************************/

#ifndef _CAGG_H
#define _CAGG_H


/*
 * the aggregation functions flag, 0 if don't do aggregate
 */

#define ZM_AGG_FPGA_DEBUG                   1
#define ZM_AGG_FPGA_REORDERING              1

#ifndef ZM_AGG_TALLY
//#define ZM_AGG_TALLY
#endif
/*
 * Aggregate control
 */


#define ZM_AGG_POOL_SIZE                    20
#define ZM_BAW_POOL_SIZE                    32
#define ZM_AGGQ_SIZE                        64
#define ZM_AGGQ_SIZE_MASK                   (ZM_AGGQ_SIZE-1)
#define ZM_AGG_LOW_THRESHOLD                1
#define ZM_AGG_HIGH_THRESHOLD               5

/*
 * number of access categories (ac)
 */
#define ZM_AC                               4
/*
 * the timer to clear aggregation queue, unit: 1 tick
 * if the packet is too old (current time - arrival time)
 * the packet and the aggregate queue will be cleared
 */
#define ZM_AGG_CLEAR_TIME                   10
/*
 * delete the queue if idle for ZM_DELETE_TIME
 * unit: 10ms
 */
#define ZM_AGG_DELETE_TIME                  10000

/*
 * block ack window size
 */
#define ZM_AGG_BAW_SIZE                     64
#define ZM_AGG_BAW_MASK                     (ZM_AGG_BAW_SIZE-1)
/*
 * originator     ADDBA Resquest    receiver
 *      |----------------------------->|
 *     1|          ACK                 |1
 *      |<-----------------------------|
 *     2|         ADDBA Response       |2
 *      |<-----------------------------|
 *     3|          ACK                 |3
 *      |----------------------------->|
 *     4                                4
 */
#define ZM_AGG_ADDBA_REQUEST                1
#define ZM_AGG_ADDBA_REQUEST_ACK            2
#define ZM_AGG_ADDBA_RESPONSE               3
#define ZM_AGG_ADDBA_RESPONSE_ACK           4

#define ZM_AGG_SINGLE_MPDU                  00
#define ZM_AGG_FIRST_MPDU                   01
#define ZM_AGG_MIDDLE_MPDU                  11
#define ZM_AGG_LAST_MPDU                    10
/*
 * end of Aggregate control
 */

#define TID_TX  struct aggQueue*
#define TID_BAW struct baw_q*
#define BAW wd->baw_enabler
#define DESTQ wd->destQ

/*
 * Queue access
 */
#define zm_agg_qlen(dev, head, tail) ((head - tail) & ZM_AGGQ_SIZE_MASK)
#define zm_agg_inQ(tid_tx, pt) ((((pt - tid_tx->aggTail) & ZM_AGGQ_SIZE_MASK) < \
        ((tid_tx->aggHead - tid_tx->aggTail) & ZM_AGGQ_SIZE_MASK))? TRUE:FALSE)
#define zm_agg_plus(pt) pt = (pt + 1) & ZM_AGGQ_SIZE_MASK
#define zm_agg_min(A, B) ((A>B)? B:A)
#define zm_agg_GetTime() wd->tick
#define TXQL (zfHpGetMaxTxdCount(dev) - zfHpGetFreeTxdCount(dev))

/* don't change AGG_MIN_TXQL easily, this might cause BAW BSOD */
#define AGG_MIN_TXQL                        2
/*
 * consider tcp,udp,ac(1234)
 */
#define zm_agg_dynamic_threshold(dev, ar)   ((ar > 16)? 11: \
                                             (ar > 12)? 8: \
                                             (ar > 8)? 5: \
                                             (ar > 4)? 2:1)
#define zm_agg_weight(ac)   ((3 == ac)? 4: \
                             (2 == ac)? 3: \
                             (0 == ac)? 2:1)
/*
 * the required free queue ratio per ac
 */

#define zm_agg_ratio(ac)    ((3 == ac)? 3: \
                             (2 == ac)? (zfHpGetMaxTxdCount(dev)*1/4): \
                             (0 == ac)? (zfHpGetMaxTxdCount(dev)*2/4): \
                                        (zfHpGetMaxTxdCount(dev)*3/4))

//#define zm_agg_ratio(ac)    3
/*
 * end of Queue access
 */

#define ZM_AGGMSG_LEV    ZM_LV_3
#define zm_msg0_agg(lv, msg) if (ZM_AGGMSG_LEV >= lv) \
        {zm_debug_msg0(msg);}
#define zm_msg1_agg(lv, msg, val) if (ZM_AGGMSG_LEV >= lv) \
        {zm_debug_msg1(msg, val);}
#define zm_msg2_agg(lv, msg, val) if (ZM_AGGMSG_LEV >= lv) \
        {zm_debug_msg2(msg, val);}

#ifndef ZM_ENABLE_FW_BA_RETRANSMISSION //disable BAW
struct baw_header_r {
    u16_t       *header;
    u16_t       *mic;
    u16_t       *snap;
    u16_t       headerLen;
    u16_t       micLen;
    u16_t       snapLen;
    u16_t       removeLen;
    u8_t        keyIdx;
};

struct baw_header {
    u16_t       header[29];//[(8+30+2+18)/2];  58 bytes  /* ctr+(4+a1+a2+a3+2+a4)+qos+iv */
    u16_t       headerLen;
    u16_t       mic[4]; //[8/2]; 8 bytes
    u16_t       micLen;
    u16_t       snap[4]; //[8/2]; 8 bytes
    u16_t       snapLen;
    u16_t       removeLen;
    u8_t        keyIdx;
};

struct bufInfo {
    zbuf_t*     buf;
    u8_t        baw_retransmit;
    u32_t       timestamp;
    struct baw_header   *baw_header;
};
#endif
struct aggElement
{
    zbuf_t*     buf;
    u32_t       arrivalTime;
    u8_t        baw_retransmit;
    struct zsAdditionInfo addInfo;
    //struct baw_header  baw_header;
};


#ifndef ZM_ENABLE_FW_BA_RETRANSMISSION //disable BAW
struct baw_buf
{
    zbuf_t*     buf;
    u16_t       baw_seq;
    u32_t       timestamp;
    u8_t        baw_retransmit;
    struct baw_header baw_header;
};

struct baw_q {
    struct baw_buf  frame[ZM_VTXQ_SIZE];
    u16_t       enabled;
    u16_t       start_seq;
    u16_t       head;
    u16_t       tail;
    u16_t       size;
    TID_TX      tid_tx;

    //struct baw_header *baw_header;
};

struct baw_enabler
{
    struct baw_q    tid_baw[ZM_BAW_POOL_SIZE];
    u8_t    delPoint;
    void    (*core)(zdev_t* dev, u16_t baw_seq, u32_t bitmap, u16_t aggLen);
    //void    (*core);
    void    (*init)(zdev_t* dev);
    TID_BAW (*getNewQ)(zdev_t* dev, u16_t start_seq, TID_TX tid_tx);
    TID_BAW (*getQ)(zdev_t* dev, u16_t baw_seq);
    u16_t   (*insert)(zdev_t* dev, zbuf_t* buf, u16_t baw_seq, TID_BAW tid_baw, u8_t baw_retransmit, struct baw_header_r *header_r);
    struct bufInfo* (*pop)(zdev_t* dev, u16_t index, TID_BAW tid_baw);
    void    (*enable)(zdev_t* dev, TID_BAW tid_baw, u16_t start_seq);
    void    (*disable)(zdev_t* dev, TID_BAW tid_baw);

};
#endif
struct aggQueue
{
    struct      aggElement  aggvtxq[ZM_AGGQ_SIZE];
    u16_t       aggHead;
    u16_t       aggTail;
    s16_t       size;
    u16_t       aggQSTA;
    u16_t       aggQEnabled;
    u16_t       ac;
    u16_t       tid;
    u16_t       aggReady;
    u16_t       clearFlag;
    u16_t       deleteFlag;
    u32_t       lastArrival;
    u16_t       aggFrameSize;
    u16_t       bar_ssn;    /* starting sequence number in BAR */
    u16_t       dst[3];
    u16_t       complete;     /* complete indication pointer */
};

struct aggSta
{
    u16_t       count[ZM_AC];
    TID_TX      tid_tx[8];
    u16_t       aggFlag[ZM_AC];
};

struct agg_tid_rx
{
    u16_t       aid;
    u16_t       ac;
    u16_t       addBaExchangeStatusCode;
    //struct zsAdditionInfo *addInfo;
    u16_t       seq_start;		/* first seq expected next */
    u16_t       baw_head;		/* head of valid block ack window */
    u16_t       baw_tail;		/* tail of valid block ack window */
    //u16_t       free_count;		/* block ack window size	*/
    u8_t        sq_exceed_count;
    u8_t        sq_behind_count;
    struct aggElement frame[ZM_AGG_BAW_SIZE + 1]; /* out-of-order rx frames */
};

struct aggControl
{
    u16_t       aggEnabled;
    u16_t       ampduIndication;
    u16_t       addbaIndication;
    //TID_BAW     tid_baw;
    u32_t       timestamp;
};

struct aggBaFrameParameter
{
    zbuf_t*     buf;
    u16_t       ba_parameter;
    u8_t        dialog;
    u16_t       ba_policy;
    u16_t       tid;
    u16_t       buffer_size;
    u16_t       ba_timeout;
    u16_t       ba_start_seq;
    u16_t       status_code;
};

struct aggBarControl
{
    u16_t       bar_ack_policy      ;
    u16_t       multi_tid           ;
    u16_t       compressed_bitmap   ;
    u16_t       tid_info            ;
};

struct aggTally
{
    u32_t       got_packets_sum;
    u32_t       got_bytes_sum;
    u32_t       sent_packets_sum;
    u32_t       sent_bytes_sum;
    u32_t       avg_got_packets;
    u32_t       avg_got_bytes;
    u32_t       avg_sent_packets;
    u32_t       avg_sent_bytes;
    u16_t       time;
};


struct destQ {
    struct dest{
        u16_t   Qtype : 1; /* 0 aggr, 1 vtxq */
        TID_TX  tid_tx;
        void*   vtxq;

        struct dest* next;
    } *dest[4];
    struct dest* Head[4];
    //s16_t   size[4];
    u16_t   ppri;
    void    (*insert)(zdev_t* dev, u16_t Qtype, u16_t ac, TID_TX tid_tx, void* vtxq);
    void    (*delete)(zdev_t* dev, u16_t Qtype, TID_TX tid_tx, void* vtxq);
    void    (*init)(zdev_t* dev);
    struct dest* (*getNext)(zdev_t* dev, u16_t ac);
    u16_t   (*exist)(zdev_t* dev, u16_t Qtype, u16_t ac, TID_TX tid_tx, void* vtxq);
    //void    (*scan)(zdev_t* dev);
};
/*
 * aggregation tx
 */
void    zfAggInit(zdev_t* dev);
u16_t   zfApFindSta(zdev_t* dev, u16_t* addr);
u16_t   zfAggGetSta(zdev_t* dev, zbuf_t* buf);
TID_TX  zfAggTxGetQueue(zdev_t* dev, u16_t aid, u16_t tid);
TID_TX  zfAggTxNewQueue(zdev_t* dev, u16_t aid, u16_t tid, zbuf_t* buf);
u16_t   zfAggTxEnqueue(zdev_t* dev, zbuf_t* buf, u16_t aid, TID_TX tid_tx);
u16_t   zfAggTx(zdev_t* dev, zbuf_t* buf, u16_t tid);
u16_t   zfAggTxReadyCount(zdev_t* dev, u16_t ac);
u16_t   zfAggTxPartial(zdev_t* dev, u16_t ac, u16_t readycount);
u16_t   zfAggTxSend(zdev_t* dev, u32_t freeTxd, TID_TX tid_tx);
TID_TX  zfAggTxGetReadyQueue(zdev_t* dev, u16_t ac);
zbuf_t* zfAggTxGetVtxq(zdev_t* dev, TID_TX tid_tx);
u16_t   zfAggTxDeleteQueue(zdev_t* dev, u16_t qnum);
u16_t   zfAggScanAndClear(zdev_t* dev, u32_t time);
u16_t   zfAggClearQueue(zdev_t* dev);
void    zfAggTxScheduler(zdev_t* dev, u8_t ScanAndClear);

/* tid_tx manipulation */
#ifndef ZM_ENABLE_FW_BA_RETRANSMISSION //disable BAW
u16_t   zfAggTidTxInsertHead(zdev_t* dev, struct bufInfo* buf_info, TID_TX tid_tx);
#endif
void    zfAggDestInsert(zdev_t* dev, u16_t Qtype, u16_t ac, TID_TX tid_tx, void* vtxq);
void    zfAggDestDelete(zdev_t* dev, u16_t Qtype, TID_TX tid_tx, void* vtxq);
void    zfAggDestInit(zdev_t* dev);
struct dest* zfAggDestGetNext(zdev_t* dev, u16_t ac);
u16_t   zfAggDestExist(zdev_t* dev, u16_t Qtype, u16_t ac, TID_TX tid_tx, void* vtxq);
/*
 * aggregation rx
 */
struct agg_tid_rx *zfAggRxEnabled(zdev_t* dev, zbuf_t* buf);
u16_t   zfAggRx(zdev_t* dev, zbuf_t* buf, struct zsAdditionInfo *addInfo, struct agg_tid_rx *tid_rx);
struct agg_tid_rx *zfAggRxGetQueue(zdev_t* dev, zbuf_t* buf);
u16_t   zfAggRxEnqueue(zdev_t* dev, zbuf_t* buf, struct agg_tid_rx *tid_rx, struct zsAdditionInfo *addInfo);
u16_t   zfAggRxFlush(zdev_t* dev, u16_t seq_no, struct agg_tid_rx *tid_rx);
u16_t   zfAggRxFreeBuf(zdev_t* dev, u16_t destroy);
u16_t   zfAggRxClear(zdev_t* dev, u32_t time);
void    zfAggRecvBAR(zdev_t* dev, zbuf_t* buf);
/*
 * end of aggregation rx
 */

/*
 * ADDBA
 */
u16_t   zfAggSendAddbaRequest(zdev_t* dev, u16_t *dst, u16_t ac, u16_t up);
u16_t   zfAggSetAddbaFrameBody(zdev_t* dev,zbuf_t* buf, u16_t offset, u16_t ac, u16_t up);
u16_t   zfAggGenAddbaHeader(zdev_t* dev, u16_t* dst,
                u16_t* header, u16_t len, zbuf_t* buf, u16_t vap, u8_t encrypt);
u16_t   zfAggProcessAction(zdev_t* dev, zbuf_t* buf);
u16_t   zfAggBlockAckActionFrame(zdev_t* dev, zbuf_t* buf);
u16_t   zfAggRecvAddbaRequest(zdev_t* dev, zbuf_t* buf);
u16_t   zfAggRecvAddbaResponse(zdev_t* dev, zbuf_t* buf);
u16_t   zfAggRecvDelba(zdev_t* dev, zbuf_t* buf);
u16_t   zfAggSendAddbaResponse(zdev_t* dev, struct aggBaFrameParameter *bf);
u16_t   zfAggSetAddbaResponseFrameBody(zdev_t* dev, zbuf_t* buf,
                struct aggBaFrameParameter *bf, u16_t offset);
u16_t   zfAggAddbaSetTidRx(zdev_t* dev, zbuf_t* buf,
                struct aggBaFrameParameter *bf);
/*
 * zfAggTxSendEth
 */
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);

/*
 * statistics functions
 */
u16_t zfAggTallyReset(zdev_t* dev);

u16_t   zfAggPrintTally(zdev_t* dev);

/*
 * BAR
 */
void    zfAggInvokeBar(zdev_t* dev, TID_TX tid_tx);
u16_t   zfAggSendBar(zdev_t* dev, TID_TX tid_tx, struct aggBarControl *aggBarControl);
u16_t   zfAggSetBarBody(zdev_t* dev, zbuf_t* buf, u16_t offset, TID_TX tid_tx, struct aggBarControl *aggBarControl);
u16_t   zfAggGenBarHeader(zdev_t* dev, u16_t* dst,
                u16_t* header, u16_t len, zbuf_t* buf, u16_t vap, u8_t encrypt);

#ifndef ZM_ENABLE_FW_BA_RETRANSMISSION //disable BAW
/* BAW BA retransmission */
void    zfBawCore(zdev_t* dev, u16_t baw_seq, u32_t bitmap, u16_t aggLen);
void    zfBawInit(zdev_t* dev);
TID_BAW zfBawGetNewQ(zdev_t* dev, u16_t start_seq, TID_TX tid_tx);
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);
struct bufInfo* zfBawPop(zdev_t* dev, u16_t index, TID_BAW tid_baw);
void    zfBawEnable(zdev_t* dev, TID_BAW tid_baw, u16_t start_seq);
void    zfBawDisable(zdev_t* dev, TID_BAW tid_baw);
TID_BAW zfBawGetQ(zdev_t* dev, u16_t baw_seq);
void zfAggTxRetransmit(zdev_t* dev, struct bufInfo *buf_info, struct aggControl *aggControl, TID_TX tid_tx);
#endif
/* extern functions */
extern zbuf_t* zfGetVtxq(zdev_t* dev, u8_t ac);

#endif /* #ifndef _CAGG_H */
