linux/drivers/staging/ath6kl/reorder/rcv_aggr.c
<<
>>
Prefs
   1/*
   2 *
   3 * Copyright (c) 2010 Atheros Communications Inc.
   4 * All rights reserved.
   5 *
   6 * 
   7//
   8// Permission to use, copy, modify, and/or distribute this software for any
   9// purpose with or without fee is hereby granted, provided that the above
  10// copyright notice and this permission notice appear in all copies.
  11//
  12// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  13// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  14// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  15// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  16// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  17// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  18// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  19//
  20//
  21 *
  22 */
  23
  24#ifdef  ATH_AR6K_11N_SUPPORT
  25
  26#include <a_config.h>
  27#include <athdefs.h>
  28#include <a_types.h>
  29#include <a_osapi.h>
  30#include <a_debug.h>
  31#include "pkt_log.h"
  32#include "aggr_recv_api.h"
  33#include "aggr_rx_internal.h"
  34#include "wmi.h"
  35
  36extern A_STATUS
  37wmi_dot3_2_dix(void *osbuf);
  38
  39static void
  40aggr_slice_amsdu(AGGR_INFO *p_aggr, RXTID *rxtid, void **osbuf);
  41
  42static void
  43aggr_timeout(A_ATH_TIMER arg);
  44
  45static void
  46aggr_deque_frms(AGGR_INFO *p_aggr, A_UINT8 tid, A_UINT16 seq_no, A_UINT8 order);
  47
  48static void
  49aggr_dispatch_frames(AGGR_INFO *p_aggr, A_NETBUF_QUEUE_T *q);
  50
  51static void *
  52aggr_get_osbuf(AGGR_INFO *p_aggr);
  53
  54void *
  55aggr_init(ALLOC_NETBUFS netbuf_allocator)
  56{
  57    AGGR_INFO   *p_aggr = NULL;
  58    RXTID *rxtid;
  59    A_UINT8 i;
  60    A_STATUS status = A_OK;
  61
  62    A_PRINTF("In aggr_init..\n");
  63
  64    do {
  65        p_aggr = A_MALLOC(sizeof(AGGR_INFO));
  66        if(!p_aggr) {
  67            A_PRINTF("Failed to allocate memory for aggr_node\n");
  68            status = A_ERROR;
  69            break;
  70        }
  71
  72        /* Init timer and data structures */
  73        A_MEMZERO(p_aggr, sizeof(AGGR_INFO));
  74        p_aggr->aggr_sz = AGGR_SZ_DEFAULT;
  75        A_INIT_TIMER(&p_aggr->timer, aggr_timeout, p_aggr);
  76        p_aggr->timerScheduled = FALSE;
  77        A_NETBUF_QUEUE_INIT(&p_aggr->freeQ);
  78
  79        p_aggr->netbuf_allocator = netbuf_allocator;
  80        p_aggr->netbuf_allocator(&p_aggr->freeQ, AGGR_NUM_OF_FREE_NETBUFS);
  81
  82        for(i = 0; i < NUM_OF_TIDS; i++) {
  83            rxtid = AGGR_GET_RXTID(p_aggr, i);
  84            rxtid->aggr = FALSE;
  85            rxtid->progress = FALSE;
  86            rxtid->timerMon = FALSE;
  87            A_NETBUF_QUEUE_INIT(&rxtid->q);
  88            A_MUTEX_INIT(&rxtid->lock);
  89        }
  90    }while(FALSE);
  91
  92    A_PRINTF("going out of aggr_init..status %s\n",
  93                    (status == A_OK) ? "OK":"Error");
  94
  95    if(status != A_OK) {
  96        /* Cleanup */
  97        aggr_module_destroy(p_aggr);
  98    }
  99    return ((status == A_OK) ? p_aggr : NULL);
 100}
 101
 102/* utility function to clear rx hold_q for a tid */
 103static void
 104aggr_delete_tid_state(AGGR_INFO *p_aggr, A_UINT8 tid)
 105{
 106    RXTID *rxtid;
 107    RXTID_STATS *stats;
 108
 109    A_ASSERT(tid < NUM_OF_TIDS && p_aggr);
 110
 111    rxtid = AGGR_GET_RXTID(p_aggr, tid);
 112    stats = AGGR_GET_RXTID_STATS(p_aggr, tid);
 113
 114    if(rxtid->aggr) {
 115        aggr_deque_frms(p_aggr, tid, 0, ALL_SEQNO);
 116    }
 117
 118    rxtid->aggr = FALSE;
 119    rxtid->progress = FALSE;
 120    rxtid->timerMon = FALSE;
 121    rxtid->win_sz = 0;
 122    rxtid->seq_next = 0;
 123    rxtid->hold_q_sz = 0;
 124
 125    if(rxtid->hold_q) {
 126        A_FREE(rxtid->hold_q);
 127        rxtid->hold_q = NULL;
 128    }
 129
 130    A_MEMZERO(stats, sizeof(RXTID_STATS));
 131}
 132
 133void
 134aggr_module_destroy(void *cntxt)
 135{
 136    AGGR_INFO *p_aggr = (AGGR_INFO *)cntxt;
 137    RXTID *rxtid;
 138    A_UINT8 i, k;
 139    A_PRINTF("%s(): aggr = %p\n",_A_FUNCNAME_, p_aggr);
 140    A_ASSERT(p_aggr);
 141
 142    if(p_aggr) {
 143        if(p_aggr->timerScheduled) {
 144            A_UNTIMEOUT(&p_aggr->timer);
 145            p_aggr->timerScheduled = FALSE;
 146        }
 147
 148        for(i = 0; i < NUM_OF_TIDS; i++) {
 149            rxtid = AGGR_GET_RXTID(p_aggr, i);
 150            /* Free the hold q contents and hold_q*/
 151            if(rxtid->hold_q) {
 152                for(k = 0; k< rxtid->hold_q_sz; k++) {
 153                    if(rxtid->hold_q[k].osbuf) {
 154                        A_NETBUF_FREE(rxtid->hold_q[k].osbuf);
 155                    }
 156                }
 157                A_FREE(rxtid->hold_q);
 158            }
 159            /* Free the dispatch q contents*/
 160            while(A_NETBUF_QUEUE_SIZE(&rxtid->q)) {
 161                A_NETBUF_FREE(A_NETBUF_DEQUEUE(&rxtid->q));
 162            }
 163            if (A_IS_MUTEX_VALID(&rxtid->lock)) {
 164                A_MUTEX_DELETE(&rxtid->lock);
 165            }
 166        }
 167        /* free the freeQ and its contents*/
 168        while(A_NETBUF_QUEUE_SIZE(&p_aggr->freeQ)) {
 169            A_NETBUF_FREE(A_NETBUF_DEQUEUE(&p_aggr->freeQ));
 170        }
 171        A_FREE(p_aggr);
 172    }
 173    A_PRINTF("out aggr_module_destroy\n");
 174}
 175
 176
 177void
 178aggr_register_rx_dispatcher(void *cntxt, void * dev, RX_CALLBACK fn)
 179{
 180    AGGR_INFO *p_aggr = (AGGR_INFO *)cntxt;
 181
 182    A_ASSERT(p_aggr && fn && dev);
 183
 184    p_aggr->rx_fn = fn;
 185    p_aggr->dev = dev;
 186}
 187
 188
 189void
 190aggr_process_bar(void *cntxt, A_UINT8 tid, A_UINT16 seq_no)
 191{
 192    AGGR_INFO *p_aggr = (AGGR_INFO *)cntxt;
 193    RXTID_STATS *stats;
 194
 195    A_ASSERT(p_aggr);
 196    stats = AGGR_GET_RXTID_STATS(p_aggr, tid);
 197    stats->num_bar++;
 198
 199    aggr_deque_frms(p_aggr, tid, seq_no, ALL_SEQNO);
 200}
 201
 202
 203void
 204aggr_recv_addba_req_evt(void *cntxt, A_UINT8 tid, A_UINT16 seq_no, A_UINT8 win_sz)
 205{
 206    AGGR_INFO *p_aggr = (AGGR_INFO *)cntxt;
 207    RXTID *rxtid;
 208    RXTID_STATS *stats;
 209
 210    A_ASSERT(p_aggr);
 211    rxtid = AGGR_GET_RXTID(p_aggr, tid);
 212    stats = AGGR_GET_RXTID_STATS(p_aggr, tid);
 213
 214    A_PRINTF("%s(): win_sz = %d aggr %d\n", _A_FUNCNAME_, win_sz, rxtid->aggr);
 215    if(win_sz < AGGR_WIN_SZ_MIN || win_sz > AGGR_WIN_SZ_MAX) {
 216        A_PRINTF("win_sz %d, tid %d\n", win_sz, tid);
 217    }
 218
 219    if(rxtid->aggr) {
 220        /* Just go and  deliver all the frames up from this
 221         * queue, as if we got DELBA and re-initialize the queue
 222         */
 223        aggr_delete_tid_state(p_aggr, tid);
 224    }
 225
 226    rxtid->seq_next = seq_no;
 227    /* create these queues, only upon receiving of ADDBA for a
 228     * tid, reducing memory requirement
 229     */
 230    rxtid->hold_q = A_MALLOC(HOLD_Q_SZ(win_sz));
 231    if((rxtid->hold_q == NULL)) {
 232        A_PRINTF("Failed to allocate memory, tid = %d\n", tid);
 233        A_ASSERT(0);
 234    }
 235    A_MEMZERO(rxtid->hold_q, HOLD_Q_SZ(win_sz));
 236
 237    /* Update rxtid for the window sz */
 238    rxtid->win_sz = win_sz;
 239    /* hold_q_sz inicates the depth of holding q - which  is
 240     * a factor of win_sz. Compute once, as it will be used often
 241     */
 242    rxtid->hold_q_sz = TID_WINDOW_SZ(win_sz);
 243    /* There should be no frames on q - even when second ADDBA comes in.
 244     * If aggr was previously ON on this tid, we would have cleaned up
 245     * the q
 246     */
 247    if(A_NETBUF_QUEUE_SIZE(&rxtid->q) != 0) {
 248        A_PRINTF("ERROR: Frames still on queue ?\n");
 249        A_ASSERT(0);
 250    }
 251
 252    rxtid->aggr = TRUE;
 253}
 254
 255void
 256aggr_recv_delba_req_evt(void *cntxt, A_UINT8 tid)
 257{
 258    AGGR_INFO *p_aggr = (AGGR_INFO *)cntxt;
 259    RXTID *rxtid;
 260
 261    A_ASSERT(p_aggr);
 262    A_PRINTF("%s(): tid %d\n", _A_FUNCNAME_, tid);
 263
 264    rxtid = AGGR_GET_RXTID(p_aggr, tid);
 265
 266    if(rxtid->aggr) {
 267        aggr_delete_tid_state(p_aggr, tid);
 268    }
 269}
 270
 271static void
 272aggr_deque_frms(AGGR_INFO *p_aggr, A_UINT8 tid, A_UINT16 seq_no, A_UINT8 order)
 273{
 274    RXTID *rxtid;
 275    OSBUF_HOLD_Q *node;
 276    A_UINT16 idx, idx_end, seq_end;
 277    RXTID_STATS *stats;
 278
 279    A_ASSERT(p_aggr);
 280    rxtid = AGGR_GET_RXTID(p_aggr, tid);
 281    stats = AGGR_GET_RXTID_STATS(p_aggr, tid);
 282
 283    /* idx is absolute location for first frame */
 284    idx = AGGR_WIN_IDX(rxtid->seq_next, rxtid->hold_q_sz);
 285
 286    /* idx_end is typically the last possible frame in the window,
 287     * but changes to 'the' seq_no, when BAR comes. If seq_no
 288     * is non-zero, we will go up to that and stop.
 289     * Note: last seq no in current window will occupy the same
 290     * index position as index that is just previous to start.
 291     * An imp point : if win_sz is 7, for seq_no space of 4095,
 292     * then, there would be holes when sequence wrap around occurs.
 293     * Target should judiciously choose the win_sz, based on
 294     * this condition. For 4095, (TID_WINDOW_SZ = 2 x win_sz
 295     * 2, 4, 8, 16 win_sz works fine).
 296     * We must deque from "idx" to "idx_end", including both.
 297     */
 298    seq_end = (seq_no) ? seq_no : rxtid->seq_next;
 299    idx_end = AGGR_WIN_IDX(seq_end, rxtid->hold_q_sz);
 300
 301    /* Critical section begins */
 302    A_MUTEX_LOCK(&rxtid->lock);
 303    do {
 304
 305        node = &rxtid->hold_q[idx];
 306
 307        if((order == CONTIGUOUS_SEQNO) && (!node->osbuf))
 308            break;
 309
 310        /* chain frames and deliver frames bcos:
 311         *  1. either the frames are in order and window is contiguous, OR
 312         *  2. we need to deque frames, irrespective of holes
 313         */
 314        if(node->osbuf) {
 315            if(node->is_amsdu) {
 316                aggr_slice_amsdu(p_aggr, rxtid, &node->osbuf);
 317            } else {
 318                A_NETBUF_ENQUEUE(&rxtid->q, node->osbuf);
 319            }
 320            node->osbuf = NULL;
 321        } else {
 322            stats->num_hole++;
 323        }
 324
 325        /* window is moving */
 326        rxtid->seq_next = IEEE80211_NEXT_SEQ_NO(rxtid->seq_next);
 327        idx = AGGR_WIN_IDX(rxtid->seq_next, rxtid->hold_q_sz);
 328    } while(idx != idx_end);
 329    /* Critical section ends */
 330    A_MUTEX_UNLOCK(&rxtid->lock);
 331
 332    stats->num_delivered += A_NETBUF_QUEUE_SIZE(&rxtid->q);
 333    aggr_dispatch_frames(p_aggr, &rxtid->q);
 334}
 335
 336static void *
 337aggr_get_osbuf(AGGR_INFO *p_aggr)
 338{
 339    void *buf = NULL;
 340
 341    /* Starving for buffers?  get more from OS
 342     *  check for low netbuffers( < 1/4 AGGR_NUM_OF_FREE_NETBUFS) :
 343     *      re-allocate bufs if so
 344     * allocate a free buf from freeQ
 345     */
 346    if (A_NETBUF_QUEUE_SIZE(&p_aggr->freeQ) < (AGGR_NUM_OF_FREE_NETBUFS >> 2)) {
 347        p_aggr->netbuf_allocator(&p_aggr->freeQ, AGGR_NUM_OF_FREE_NETBUFS);
 348    }
 349
 350    if (A_NETBUF_QUEUE_SIZE(&p_aggr->freeQ)) {
 351        buf = A_NETBUF_DEQUEUE(&p_aggr->freeQ);
 352    }
 353
 354    return buf;
 355}
 356
 357
 358static void
 359aggr_slice_amsdu(AGGR_INFO *p_aggr, RXTID *rxtid, void **osbuf)
 360{
 361    void *new_buf;
 362    A_UINT16 frame_8023_len, payload_8023_len, mac_hdr_len, amsdu_len;
 363    A_UINT8 *framep;
 364
 365    /* Frame format at this point:
 366     *  [DIX hdr | 802.3 | 802.3 | ... | 802.3]
 367     *
 368     * Strip the DIX header.
 369     * Iterate through the osbuf and do:
 370     *  grab a free netbuf from freeQ
 371     *  find the start and end of a frame
 372     *  copy it to netbuf(Vista can do better here)
 373     *  convert all msdu's(802.3) frames to upper layer format - os routine
 374     *      -for now lets convert from 802.3 to dix
 375     *  enque this to dispatch q of tid
 376     * repeat
 377     * free the osbuf - to OS. It's been sliced.
 378     */
 379
 380    mac_hdr_len = sizeof(ATH_MAC_HDR);
 381    framep = A_NETBUF_DATA(*osbuf) + mac_hdr_len;
 382    amsdu_len = A_NETBUF_LEN(*osbuf) - mac_hdr_len;
 383
 384    while(amsdu_len > mac_hdr_len) {
 385        /* Begin of a 802.3 frame */
 386        payload_8023_len = A_BE2CPU16(((ATH_MAC_HDR *)framep)->typeOrLen);
 387#define MAX_MSDU_SUBFRAME_PAYLOAD_LEN 1508
 388#define MIN_MSDU_SUBFRAME_PAYLOAD_LEN 46
 389        if(payload_8023_len < MIN_MSDU_SUBFRAME_PAYLOAD_LEN || payload_8023_len > MAX_MSDU_SUBFRAME_PAYLOAD_LEN) {
 390            A_PRINTF("802.3 AMSDU frame bound check failed. len %d\n", payload_8023_len);
 391            break;
 392        }
 393        frame_8023_len = payload_8023_len + mac_hdr_len;
 394        new_buf = aggr_get_osbuf(p_aggr);
 395        if(new_buf == NULL) {
 396            A_PRINTF("No buffer available \n");
 397            break;
 398        }
 399
 400        A_MEMCPY(A_NETBUF_DATA(new_buf), framep, frame_8023_len);
 401        A_NETBUF_PUT(new_buf, frame_8023_len);
 402        if (wmi_dot3_2_dix(new_buf) != A_OK) {
 403            A_PRINTF("dot3_2_dix err..\n");
 404            A_NETBUF_FREE(new_buf);
 405            break;
 406        }
 407
 408        A_NETBUF_ENQUEUE(&rxtid->q, new_buf);
 409
 410        /* Is this the last subframe within this aggregate ? */
 411        if ((amsdu_len - frame_8023_len) == 0) {
 412            break;
 413        }
 414
 415        /* Add the length of A-MSDU subframe padding bytes -
 416         * Round to nearest word.
 417         */
 418        frame_8023_len = ((frame_8023_len + 3) & ~3);
 419
 420        framep += frame_8023_len;
 421        amsdu_len -= frame_8023_len;
 422    }
 423
 424    A_NETBUF_FREE(*osbuf);
 425    *osbuf = NULL;
 426}
 427
 428void
 429aggr_process_recv_frm(void *cntxt, A_UINT8 tid, A_UINT16 seq_no, A_BOOL is_amsdu, void **osbuf)
 430{
 431    AGGR_INFO *p_aggr = (AGGR_INFO *)cntxt;
 432    RXTID *rxtid;
 433    RXTID_STATS *stats;
 434    A_UINT16 idx, st, cur, end;
 435    A_UINT16 *log_idx;
 436    OSBUF_HOLD_Q *node;
 437    PACKET_LOG *log;
 438
 439    A_ASSERT(p_aggr);
 440    A_ASSERT(tid < NUM_OF_TIDS);
 441
 442    rxtid = AGGR_GET_RXTID(p_aggr, tid);
 443    stats = AGGR_GET_RXTID_STATS(p_aggr, tid);
 444
 445    stats->num_into_aggr++;
 446
 447    if(!rxtid->aggr) {
 448        if(is_amsdu) {
 449            aggr_slice_amsdu(p_aggr, rxtid, osbuf);
 450            stats->num_amsdu++;
 451            aggr_dispatch_frames(p_aggr, &rxtid->q);
 452        }
 453        return;
 454    }
 455
 456    /* Check the incoming sequence no, if it's in the window */
 457    st = rxtid->seq_next;
 458    cur = seq_no;
 459    end = (st + rxtid->hold_q_sz-1) & IEEE80211_MAX_SEQ_NO;
 460    /* Log the pkt info for future analysis */
 461    log = &p_aggr->pkt_log;
 462    log_idx = &log->last_idx;
 463    log->info[*log_idx].cur = cur;
 464    log->info[*log_idx].st = st;
 465    log->info[*log_idx].end = end;
 466    *log_idx = IEEE80211_NEXT_SEQ_NO(*log_idx);
 467
 468    if(((st < end) && (cur < st || cur > end)) ||
 469      ((st > end) && (cur > end) && (cur < st))) {
 470        /* the cur frame is outside the window. Since we know
 471         * our target would not do this without reason it must
 472         * be assumed that the window has moved for some valid reason.
 473         * Therefore, we dequeue all frames and start fresh.
 474         */
 475        A_UINT16 extended_end;
 476
 477        extended_end = (end + rxtid->hold_q_sz-1) & IEEE80211_MAX_SEQ_NO;
 478
 479        if(((end < extended_end) && (cur < end || cur > extended_end)) ||
 480           ((end > extended_end) && (cur > extended_end) && (cur < end))) {
 481            // dequeue all frames in queue and shift window to new frame
 482            aggr_deque_frms(p_aggr, tid, 0, ALL_SEQNO);
 483            //set window start so that new frame is last frame in window
 484            if(cur >= rxtid->hold_q_sz-1) {
 485                rxtid->seq_next = cur - (rxtid->hold_q_sz-1);
 486            }else{
 487                rxtid->seq_next = IEEE80211_MAX_SEQ_NO - (rxtid->hold_q_sz-2 - cur);
 488            }
 489        } else {
 490            // dequeue only those frames that are outside the new shifted window
 491            if(cur >= rxtid->hold_q_sz-1) {
 492                st = cur - (rxtid->hold_q_sz-1);
 493            }else{
 494                st = IEEE80211_MAX_SEQ_NO - (rxtid->hold_q_sz-2 - cur);
 495            }
 496
 497            aggr_deque_frms(p_aggr, tid, st, ALL_SEQNO);
 498        }
 499
 500        stats->num_oow++;
 501    }
 502
 503    idx = AGGR_WIN_IDX(seq_no, rxtid->hold_q_sz);
 504
 505    /*enque the frame, in hold_q */
 506    node = &rxtid->hold_q[idx];
 507
 508    A_MUTEX_LOCK(&rxtid->lock);
 509    if(node->osbuf) {
 510        /* Is the cur frame duplicate or something beyond our
 511         * window(hold_q -> which is 2x, already)?
 512         * 1. Duplicate is easy - drop incoming frame.
 513         * 2. Not falling in current sliding window.
 514         *  2a. is the frame_seq_no preceding current tid_seq_no?
 515         *      -> drop the frame. perhaps sender did not get our ACK.
 516         *         this is taken care of above.
 517         *  2b. is the frame_seq_no beyond window(st, TID_WINDOW_SZ);
 518         *      -> Taken care of it above, by moving window forward.
 519         *
 520         */
 521        A_NETBUF_FREE(node->osbuf);
 522        stats->num_dups++;
 523    }
 524
 525    node->osbuf = *osbuf;
 526    node->is_amsdu = is_amsdu;
 527    node->seq_no = seq_no;
 528    if(node->is_amsdu) {
 529        stats->num_amsdu++;
 530    } else {
 531        stats->num_mpdu++;
 532    }
 533    A_MUTEX_UNLOCK(&rxtid->lock);
 534
 535    *osbuf = NULL;
 536    aggr_deque_frms(p_aggr, tid, 0, CONTIGUOUS_SEQNO);
 537
 538    if(p_aggr->timerScheduled) {
 539        rxtid->progress = TRUE;
 540    }else{
 541        for(idx=0 ; idx<rxtid->hold_q_sz ; idx++) {
 542            if(rxtid->hold_q[idx].osbuf) {
 543                /* there is a frame in the queue and no timer so
 544                 * start a timer to ensure that the frame doesn't remain
 545                 * stuck forever. */
 546                p_aggr->timerScheduled = TRUE;
 547                A_TIMEOUT_MS(&p_aggr->timer, AGGR_RX_TIMEOUT, 0);
 548                rxtid->progress = FALSE;
 549                rxtid->timerMon = TRUE;
 550                break;
 551            }
 552        }
 553    }
 554}
 555
 556/*
 557 * aggr_reset_state -- Called when it is deemed necessary to clear the aggregate
 558 *  hold Q state.  Examples include when a Connect event or disconnect event is
 559 *  received.
 560 */
 561void
 562aggr_reset_state(void *cntxt)
 563{
 564    A_UINT8 tid;
 565    AGGR_INFO *p_aggr = (AGGR_INFO *)cntxt;
 566
 567    A_ASSERT(p_aggr);
 568
 569    for(tid=0 ; tid<NUM_OF_TIDS ; tid++) {
 570        aggr_delete_tid_state(p_aggr, tid);
 571    }
 572}
 573
 574
 575static void
 576aggr_timeout(A_ATH_TIMER arg)
 577{
 578    A_UINT8 i,j;
 579    AGGR_INFO *p_aggr = (AGGR_INFO *)arg;
 580    RXTID   *rxtid;
 581    RXTID_STATS *stats;
 582    /*
 583     * If the q for which the timer was originally started has
 584     * not progressed then it is necessary to dequeue all the
 585     * contained frames so that they are not held forever.
 586     */
 587    for(i = 0; i < NUM_OF_TIDS; i++) {
 588        rxtid = AGGR_GET_RXTID(p_aggr, i);
 589        stats = AGGR_GET_RXTID_STATS(p_aggr, i);
 590
 591        if(rxtid->aggr == FALSE ||
 592           rxtid->timerMon == FALSE ||
 593           rxtid->progress == TRUE) {
 594            continue;
 595        }
 596        // dequeue all frames in for this tid
 597        stats->num_timeouts++;
 598        A_PRINTF("TO: st %d end %d\n", rxtid->seq_next, ((rxtid->seq_next + rxtid->hold_q_sz-1) & IEEE80211_MAX_SEQ_NO));
 599        aggr_deque_frms(p_aggr, i, 0, ALL_SEQNO);
 600    }
 601
 602    p_aggr->timerScheduled = FALSE;
 603    // determine whether a new timer should be started.
 604    for(i = 0; i < NUM_OF_TIDS; i++) {
 605        rxtid = AGGR_GET_RXTID(p_aggr, i);
 606
 607        if(rxtid->aggr == TRUE && rxtid->hold_q) {
 608            for(j = 0 ; j < rxtid->hold_q_sz ; j++)
 609            {
 610                if(rxtid->hold_q[j].osbuf)
 611                {
 612                    p_aggr->timerScheduled = TRUE;
 613                    rxtid->timerMon = TRUE;
 614                    rxtid->progress = FALSE;
 615                    break;
 616                }
 617            }
 618
 619            if(j >= rxtid->hold_q_sz) {
 620                rxtid->timerMon = FALSE;
 621            }
 622        }
 623    }
 624
 625    if(p_aggr->timerScheduled) {
 626        /* Rearm the timer*/
 627        A_TIMEOUT_MS(&p_aggr->timer, AGGR_RX_TIMEOUT, 0);
 628    }
 629
 630}
 631
 632static void
 633aggr_dispatch_frames(AGGR_INFO *p_aggr, A_NETBUF_QUEUE_T *q)
 634{
 635    void *osbuf;
 636
 637    while((osbuf = A_NETBUF_DEQUEUE(q))) {
 638        p_aggr->rx_fn(p_aggr->dev, osbuf);
 639    }
 640}
 641
 642void
 643aggr_dump_stats(void *cntxt, PACKET_LOG **log_buf)
 644{
 645    AGGR_INFO *p_aggr = (AGGR_INFO *)cntxt;
 646    RXTID   *rxtid;
 647    RXTID_STATS *stats;
 648    A_UINT8 i;
 649
 650    *log_buf = &p_aggr->pkt_log;
 651    A_PRINTF("\n\n================================================\n");
 652    A_PRINTF("tid: num_into_aggr, dups, oow, mpdu, amsdu, delivered, timeouts, holes, bar, seq_next\n");
 653    for(i = 0; i < NUM_OF_TIDS; i++) {
 654        stats = AGGR_GET_RXTID_STATS(p_aggr, i);
 655        rxtid = AGGR_GET_RXTID(p_aggr, i);
 656        A_PRINTF("%d: %d %d %d %d %d %d %d %d %d : %d\n", i, stats->num_into_aggr, stats->num_dups,
 657                    stats->num_oow, stats->num_mpdu,
 658                    stats->num_amsdu, stats->num_delivered, stats->num_timeouts,
 659                    stats->num_hole, stats->num_bar,
 660                    rxtid->seq_next);
 661    }
 662    A_PRINTF("================================================\n\n");
 663
 664}
 665
 666#endif  /* ATH_AR6K_11N_SUPPORT */
 667