3 * Copyright (c) 2010 Atheros Communications Inc.
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.
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.
24 #ifdef ATH_AR6K_11N_SUPPORT
32 #include "aggr_recv_api.h"
33 #include "aggr_rx_internal.h"
37 wmi_dot3_2_dix(void *osbuf
);
40 aggr_slice_amsdu(struct aggr_info
*p_aggr
, struct rxtid
*rxtid
, void **osbuf
);
43 aggr_timeout(A_ATH_TIMER arg
);
46 aggr_deque_frms(struct aggr_info
*p_aggr
, u8 tid
, u16 seq_no
, u8 order
);
49 aggr_dispatch_frames(struct aggr_info
*p_aggr
, A_NETBUF_QUEUE_T
*q
);
52 aggr_get_osbuf(struct aggr_info
*p_aggr
);
55 aggr_init(ALLOC_NETBUFS netbuf_allocator
)
57 struct aggr_info
*p_aggr
= NULL
;
62 A_PRINTF("In aggr_init..\n");
65 p_aggr
= A_MALLOC(sizeof(struct aggr_info
));
67 A_PRINTF("Failed to allocate memory for aggr_node\n");
72 /* Init timer and data structures */
73 A_MEMZERO(p_aggr
, sizeof(struct 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
);
79 p_aggr
->netbuf_allocator
= netbuf_allocator
;
80 p_aggr
->netbuf_allocator(&p_aggr
->freeQ
, AGGR_NUM_OF_FREE_NETBUFS
);
82 for(i
= 0; i
< NUM_OF_TIDS
; i
++) {
83 rxtid
= AGGR_GET_RXTID(p_aggr
, i
);
85 rxtid
->progress
= false;
86 rxtid
->timerMon
= false;
87 A_NETBUF_QUEUE_INIT(&rxtid
->q
);
88 A_MUTEX_INIT(&rxtid
->lock
);
92 A_PRINTF("going out of aggr_init..status %s\n",
93 (status
== 0) ? "OK":"Error");
97 aggr_module_destroy(p_aggr
);
99 return ((status
== 0) ? p_aggr
: NULL
);
102 /* utility function to clear rx hold_q for a tid */
104 aggr_delete_tid_state(struct aggr_info
*p_aggr
, u8 tid
)
107 struct rxtid_stats
*stats
;
109 A_ASSERT(tid
< NUM_OF_TIDS
&& p_aggr
);
111 rxtid
= AGGR_GET_RXTID(p_aggr
, tid
);
112 stats
= AGGR_GET_RXTID_STATS(p_aggr
, tid
);
115 aggr_deque_frms(p_aggr
, tid
, 0, ALL_SEQNO
);
119 rxtid
->progress
= false;
120 rxtid
->timerMon
= false;
123 rxtid
->hold_q_sz
= 0;
126 A_FREE(rxtid
->hold_q
);
127 rxtid
->hold_q
= NULL
;
130 A_MEMZERO(stats
, sizeof(struct rxtid_stats
));
134 aggr_module_destroy(void *cntxt
)
136 struct aggr_info
*p_aggr
= (struct aggr_info
*)cntxt
;
139 A_PRINTF("%s(): aggr = %p\n",_A_FUNCNAME_
, p_aggr
);
143 if(p_aggr
->timerScheduled
) {
144 A_UNTIMEOUT(&p_aggr
->timer
);
145 p_aggr
->timerScheduled
= false;
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*/
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
);
157 A_FREE(rxtid
->hold_q
);
159 /* Free the dispatch q contents*/
160 while(A_NETBUF_QUEUE_SIZE(&rxtid
->q
)) {
161 A_NETBUF_FREE(A_NETBUF_DEQUEUE(&rxtid
->q
));
163 if (A_IS_MUTEX_VALID(&rxtid
->lock
)) {
164 A_MUTEX_DELETE(&rxtid
->lock
);
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
));
173 A_PRINTF("out aggr_module_destroy\n");
178 aggr_register_rx_dispatcher(void *cntxt
, void * dev
, RX_CALLBACK fn
)
180 struct aggr_info
*p_aggr
= (struct aggr_info
*)cntxt
;
182 A_ASSERT(p_aggr
&& fn
&& dev
);
190 aggr_process_bar(void *cntxt
, u8 tid
, u16 seq_no
)
192 struct aggr_info
*p_aggr
= (struct aggr_info
*)cntxt
;
193 struct rxtid_stats
*stats
;
196 stats
= AGGR_GET_RXTID_STATS(p_aggr
, tid
);
199 aggr_deque_frms(p_aggr
, tid
, seq_no
, ALL_SEQNO
);
204 aggr_recv_addba_req_evt(void *cntxt
, u8 tid
, u16 seq_no
, u8 win_sz
)
206 struct aggr_info
*p_aggr
= (struct aggr_info
*)cntxt
;
208 struct rxtid_stats
*stats
;
211 rxtid
= AGGR_GET_RXTID(p_aggr
, tid
);
212 stats
= AGGR_GET_RXTID_STATS(p_aggr
, tid
);
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
);
220 /* Just go and deliver all the frames up from this
221 * queue, as if we got DELBA and re-initialize the queue
223 aggr_delete_tid_state(p_aggr
, tid
);
226 rxtid
->seq_next
= seq_no
;
227 /* create these queues, only upon receiving of ADDBA for a
228 * tid, reducing memory requirement
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
);
235 A_MEMZERO(rxtid
->hold_q
, HOLD_Q_SZ(win_sz
));
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
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
247 if(A_NETBUF_QUEUE_SIZE(&rxtid
->q
) != 0) {
248 A_PRINTF("ERROR: Frames still on queue ?\n");
256 aggr_recv_delba_req_evt(void *cntxt
, u8 tid
)
258 struct aggr_info
*p_aggr
= (struct aggr_info
*)cntxt
;
262 A_PRINTF("%s(): tid %d\n", _A_FUNCNAME_
, tid
);
264 rxtid
= AGGR_GET_RXTID(p_aggr
, tid
);
267 aggr_delete_tid_state(p_aggr
, tid
);
272 aggr_deque_frms(struct aggr_info
*p_aggr
, u8 tid
, u16 seq_no
, u8 order
)
275 struct osbuf_hold_q
*node
;
276 u16 idx
, idx_end
, seq_end
;
277 struct rxtid_stats
*stats
;
280 rxtid
= AGGR_GET_RXTID(p_aggr
, tid
);
281 stats
= AGGR_GET_RXTID_STATS(p_aggr
, tid
);
283 /* idx is absolute location for first frame */
284 idx
= AGGR_WIN_IDX(rxtid
->seq_next
, rxtid
->hold_q_sz
);
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.
298 seq_end
= (seq_no
) ? seq_no
: rxtid
->seq_next
;
299 idx_end
= AGGR_WIN_IDX(seq_end
, rxtid
->hold_q_sz
);
301 /* Critical section begins */
302 A_MUTEX_LOCK(&rxtid
->lock
);
305 node
= &rxtid
->hold_q
[idx
];
307 if((order
== CONTIGUOUS_SEQNO
) && (!node
->osbuf
))
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
316 aggr_slice_amsdu(p_aggr
, rxtid
, &node
->osbuf
);
318 A_NETBUF_ENQUEUE(&rxtid
->q
, node
->osbuf
);
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
);
332 stats
->num_delivered
+= A_NETBUF_QUEUE_SIZE(&rxtid
->q
);
333 aggr_dispatch_frames(p_aggr
, &rxtid
->q
);
337 aggr_get_osbuf(struct aggr_info
*p_aggr
)
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
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
);
350 if (A_NETBUF_QUEUE_SIZE(&p_aggr
->freeQ
)) {
351 buf
= A_NETBUF_DEQUEUE(&p_aggr
->freeQ
);
359 aggr_slice_amsdu(struct aggr_info
*p_aggr
, struct rxtid
*rxtid
, void **osbuf
)
362 u16 frame_8023_len
, payload_8023_len
, mac_hdr_len
, amsdu_len
;
365 /* Frame format at this point:
366 * [DIX hdr | 802.3 | 802.3 | ... | 802.3]
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
377 * free the osbuf - to OS. It's been sliced.
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
;
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
);
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");
400 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
) != 0) {
403 A_PRINTF("dot3_2_dix err..\n");
404 A_NETBUF_FREE(new_buf
);
408 A_NETBUF_ENQUEUE(&rxtid
->q
, new_buf
);
410 /* Is this the last subframe within this aggregate ? */
411 if ((amsdu_len
- frame_8023_len
) == 0) {
415 /* Add the length of A-MSDU subframe padding bytes -
416 * Round to nearest word.
418 frame_8023_len
= ((frame_8023_len
+ 3) & ~3);
420 framep
+= frame_8023_len
;
421 amsdu_len
-= frame_8023_len
;
424 A_NETBUF_FREE(*osbuf
);
429 aggr_process_recv_frm(void *cntxt
, u8 tid
, u16 seq_no
, bool is_amsdu
, void **osbuf
)
431 struct aggr_info
*p_aggr
= (struct aggr_info
*)cntxt
;
433 struct rxtid_stats
*stats
;
434 u16 idx
, st
, cur
, end
;
436 struct osbuf_hold_q
*node
;
440 A_ASSERT(tid
< NUM_OF_TIDS
);
442 rxtid
= AGGR_GET_RXTID(p_aggr
, tid
);
443 stats
= AGGR_GET_RXTID_STATS(p_aggr
, tid
);
445 stats
->num_into_aggr
++;
449 aggr_slice_amsdu(p_aggr
, rxtid
, osbuf
);
451 aggr_dispatch_frames(p_aggr
, &rxtid
->q
);
456 /* Check the incoming sequence no, if it's in the window */
457 st
= rxtid
->seq_next
;
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
);
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.
477 extended_end
= (end
+ rxtid
->hold_q_sz
-1) & IEEE80211_MAX_SEQ_NO
;
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);
487 rxtid
->seq_next
= IEEE80211_MAX_SEQ_NO
- (rxtid
->hold_q_sz
-2 - cur
);
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);
494 st
= IEEE80211_MAX_SEQ_NO
- (rxtid
->hold_q_sz
-2 - cur
);
497 aggr_deque_frms(p_aggr
, tid
, st
, ALL_SEQNO
);
503 idx
= AGGR_WIN_IDX(seq_no
, rxtid
->hold_q_sz
);
505 /*enque the frame, in hold_q */
506 node
= &rxtid
->hold_q
[idx
];
508 A_MUTEX_LOCK(&rxtid
->lock
);
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.
521 A_NETBUF_FREE(node
->osbuf
);
525 node
->osbuf
= *osbuf
;
526 node
->is_amsdu
= is_amsdu
;
527 node
->seq_no
= seq_no
;
533 A_MUTEX_UNLOCK(&rxtid
->lock
);
536 aggr_deque_frms(p_aggr
, tid
, 0, CONTIGUOUS_SEQNO
);
538 if(p_aggr
->timerScheduled
) {
539 rxtid
->progress
= true;
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
546 p_aggr
->timerScheduled
= true;
547 A_TIMEOUT_MS(&p_aggr
->timer
, AGGR_RX_TIMEOUT
, 0);
548 rxtid
->progress
= false;
549 rxtid
->timerMon
= true;
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
562 aggr_reset_state(void *cntxt
)
565 struct aggr_info
*p_aggr
= (struct aggr_info
*)cntxt
;
569 for(tid
=0 ; tid
<NUM_OF_TIDS
; tid
++) {
570 aggr_delete_tid_state(p_aggr
, tid
);
576 aggr_timeout(A_ATH_TIMER arg
)
579 struct aggr_info
*p_aggr
= (struct aggr_info
*)arg
;
581 struct rxtid_stats
*stats
;
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.
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
);
591 if(rxtid
->aggr
== false ||
592 rxtid
->timerMon
== false ||
593 rxtid
->progress
== true) {
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
);
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
);
607 if(rxtid
->aggr
== true && rxtid
->hold_q
) {
608 for(j
= 0 ; j
< rxtid
->hold_q_sz
; j
++)
610 if(rxtid
->hold_q
[j
].osbuf
)
612 p_aggr
->timerScheduled
= true;
613 rxtid
->timerMon
= true;
614 rxtid
->progress
= false;
619 if(j
>= rxtid
->hold_q_sz
) {
620 rxtid
->timerMon
= false;
625 if(p_aggr
->timerScheduled
) {
627 A_TIMEOUT_MS(&p_aggr
->timer
, AGGR_RX_TIMEOUT
, 0);
633 aggr_dispatch_frames(struct aggr_info
*p_aggr
, A_NETBUF_QUEUE_T
*q
)
637 while((osbuf
= A_NETBUF_DEQUEUE(q
))) {
638 p_aggr
->rx_fn(p_aggr
->dev
, osbuf
);
643 aggr_dump_stats(void *cntxt
, PACKET_LOG
**log_buf
)
645 struct aggr_info
*p_aggr
= (struct aggr_info
*)cntxt
;
647 struct rxtid_stats
*stats
;
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
,
662 A_PRINTF("================================================\n\n");
666 #endif /* ATH_AR6K_11N_SUPPORT */