1 /*
2 * DHD PROP_TXSTATUS Module.
3 *
4 * Copyright (C) 1999-2017, Broadcom Corporation
5 *
6 * Unless you and Broadcom execute a separate written software license
7 * agreement governing use of this software, this software is licensed to you
8 * under the terms of the GNU General Public License version 2 (the "GPL"),
9 * available at http://www.broadcom.com/licenses/GPLv2.php, with the
10 * following added to such license:
11 *
12 * As a special exception, the copyright holders of this software give you
13 * permission to link this software with independent modules, and to copy and
14 * distribute the resulting executable under terms of your choice, provided that
15 * you also meet, for each linked independent module, the terms and conditions of
16 * the license of that module. An independent module is a module which is not
17 * derived from this software. The special exception does not apply to any
18 * modifications of the software.
19 *
20 * Notwithstanding the above, under no circumstances may you combine this
21 * software in any way with any other Broadcom software provided under a license
22 * other than the GPL, without Broadcom's express prior written consent.
23 *
24 *
25 * <<Broadcom-WL-IPTag/Open:>>
26 *
27 * $Id: dhd_wlfc.c 679733 2017-01-17 06:40:39Z $
28 *
29 */
30
31
32 #include <typedefs.h>
33 #include <osl.h>
34
35 #include <bcmutils.h>
36 #include <bcmendian.h>
37
38 #include <dngl_stats.h>
39 #include <dhd.h>
40
41 #include <dhd_bus.h>
42
43 #include <dhd_dbg.h>
44 #include <dhd_config.h>
45
46 #ifdef PROP_TXSTATUS /* a form of flow control between host and dongle */
47 #include <wlfc_proto.h>
48 #include <dhd_wlfc.h>
49 #endif
50
51 #ifdef DHDTCPACK_SUPPRESS
52 #include <dhd_ip.h>
53 #endif /* DHDTCPACK_SUPPRESS */
54
55
56 /*
57 * wlfc naming and lock rules:
58 *
59 * 1. Private functions name like _dhd_wlfc_XXX, declared as static and avoid wlfc lock operation.
60 * 2. Public functions name like dhd_wlfc_XXX, use wlfc lock if needed.
61 * 3. Non-Proptxstatus module call public functions only and avoid wlfc lock operation.
62 *
63 */
64
65 #if defined(DHD_WLFC_THREAD)
66 #define WLFC_THREAD_QUICK_RETRY_WAIT_MS 10 /* 10 msec */
67 #define WLFC_THREAD_RETRY_WAIT_MS 10000 /* 10 sec */
68 #endif /* defined (DHD_WLFC_THREAD) */
69
70
71 #ifdef PROP_TXSTATUS
72
73 #define DHD_WLFC_QMON_COMPLETE(entry)
74
75
76 /** reordering related */
77
78 #if defined(DHD_WLFC_THREAD)
79 static void
_dhd_wlfc_thread_wakeup(dhd_pub_t * dhdp)80 _dhd_wlfc_thread_wakeup(dhd_pub_t *dhdp)
81 {
82 dhdp->wlfc_thread_go = TRUE;
83 wake_up_interruptible(&dhdp->wlfc_wqhead);
84 }
85 #endif /* DHD_WLFC_THREAD */
86
87 static uint16
_dhd_wlfc_adjusted_seq(void * p,uint8 current_seq)88 _dhd_wlfc_adjusted_seq(void* p, uint8 current_seq)
89 {
90 uint16 seq;
91
92 if (!p) {
93 return 0xffff;
94 }
95
96 seq = WL_TXSTATUS_GET_FREERUNCTR(DHD_PKTTAG_H2DTAG(PKTTAG(p)));
97 if (seq < current_seq) {
98 /* wrap around */
99 seq += 256;
100 }
101
102 return seq;
103 }
104
105 /**
106 * Enqueue a caller supplied packet on a caller supplied precedence queue, optionally reorder
107 * suppressed packets.
108 * @param[in] pq caller supplied packet queue to enqueue the packet on
109 * @param[in] prec precedence of the to-be-queued packet
110 * @param[in] p transmit packet to enqueue
111 * @param[in] qHead if TRUE, enqueue to head instead of tail. Used to maintain d11 seq order.
112 * @param[in] current_seq
113 * @param[in] reOrder reOrder on odd precedence (=suppress queue)
114 */
115 static void
_dhd_wlfc_prec_enque(struct pktq * pq,int prec,void * p,bool qHead,uint8 current_seq,bool reOrder)116 _dhd_wlfc_prec_enque(struct pktq *pq, int prec, void* p, bool qHead,
117 uint8 current_seq, bool reOrder)
118 {
119 struct pktq_prec *q;
120 uint16 seq, seq2;
121 void *p2, *p2_prev;
122
123 if (!p)
124 return;
125
126 ASSERT(prec >= 0 && prec < pq->num_prec);
127 ASSERT(PKTLINK(p) == NULL); /* queueing chains not allowed */
128
129 ASSERT(!pktq_full(pq));
130 ASSERT(!pktq_pfull(pq, prec));
131
132 q = &pq->q[prec];
133
134 PKTSETLINK(p, NULL);
135 if (q->head == NULL) {
136 /* empty queue */
137 q->head = p;
138 q->tail = p;
139 } else {
140 if (reOrder && (prec & 1)) {
141 seq = _dhd_wlfc_adjusted_seq(p, current_seq);
142 p2 = qHead ? q->head : q->tail;
143 seq2 = _dhd_wlfc_adjusted_seq(p2, current_seq);
144
145 if ((qHead &&((seq+1) > seq2)) || (!qHead && ((seq2+1) > seq))) {
146 /* need reorder */
147 p2 = q->head;
148 p2_prev = NULL;
149 seq2 = _dhd_wlfc_adjusted_seq(p2, current_seq);
150
151 while (seq > seq2) {
152 p2_prev = p2;
153 p2 = PKTLINK(p2);
154 if (!p2) {
155 break;
156 }
157 seq2 = _dhd_wlfc_adjusted_seq(p2, current_seq);
158 }
159
160 if (p2_prev == NULL) {
161 /* insert head */
162 PKTSETLINK(p, q->head);
163 q->head = p;
164 } else if (p2 == NULL) {
165 /* insert tail */
166 PKTSETLINK(p2_prev, p);
167 q->tail = p;
168 } else {
169 /* insert after p2_prev */
170 PKTSETLINK(p, PKTLINK(p2_prev));
171 PKTSETLINK(p2_prev, p);
172 }
173 goto exit;
174 }
175 }
176
177 if (qHead) {
178 PKTSETLINK(p, q->head);
179 q->head = p;
180 } else {
181 PKTSETLINK(q->tail, p);
182 q->tail = p;
183 }
184 }
185
186 exit:
187
188 q->len++;
189 pq->len++;
190
191 if (pq->hi_prec < prec)
192 pq->hi_prec = (uint8)prec;
193 } /* _dhd_wlfc_prec_enque */
194
195 /**
196 * Create a place to store all packet pointers submitted to the firmware until a status comes back,
197 * suppress or otherwise.
198 *
199 * hang-er: noun, a contrivance on which things are hung, as a hook.
200 */
201 /** @deprecated soon */
202 static void*
_dhd_wlfc_hanger_create(dhd_pub_t * dhd,int max_items)203 _dhd_wlfc_hanger_create(dhd_pub_t *dhd, int max_items)
204 {
205 int i;
206 wlfc_hanger_t* hanger;
207
208 /* allow only up to a specific size for now */
209 ASSERT(max_items == WLFC_HANGER_MAXITEMS);
210
211 if ((hanger = (wlfc_hanger_t*)DHD_OS_PREALLOC(dhd, DHD_PREALLOC_DHD_WLFC_HANGER,
212 WLFC_HANGER_SIZE(max_items))) == NULL) {
213 return NULL;
214 }
215 memset(hanger, 0, WLFC_HANGER_SIZE(max_items));
216 hanger->max_items = max_items;
217
218 for (i = 0; i < hanger->max_items; i++) {
219 hanger->items[i].state = WLFC_HANGER_ITEM_STATE_FREE;
220 }
221 return hanger;
222 }
223
224 /** @deprecated soon */
225 static int
_dhd_wlfc_hanger_delete(dhd_pub_t * dhd,void * hanger)226 _dhd_wlfc_hanger_delete(dhd_pub_t *dhd, void* hanger)
227 {
228 wlfc_hanger_t* h = (wlfc_hanger_t*)hanger;
229
230 if (h) {
231 DHD_OS_PREFREE(dhd, h, WLFC_HANGER_SIZE(h->max_items));
232 return BCME_OK;
233 }
234 return BCME_BADARG;
235 }
236
237 /** @deprecated soon */
238 static uint16
_dhd_wlfc_hanger_get_free_slot(void * hanger)239 _dhd_wlfc_hanger_get_free_slot(void* hanger)
240 {
241 uint32 i;
242 wlfc_hanger_t* h = (wlfc_hanger_t*)hanger;
243
244 if (h) {
245 i = h->slot_pos + 1;
246 if (i == h->max_items) {
247 i = 0;
248 }
249 while (i != h->slot_pos) {
250 if (h->items[i].state == WLFC_HANGER_ITEM_STATE_FREE) {
251 h->slot_pos = i;
252 return (uint16)i;
253 }
254 i++;
255 if (i == h->max_items)
256 i = 0;
257 }
258 h->failed_slotfind++;
259 }
260 return WLFC_HANGER_MAXITEMS;
261 }
262
263 /** @deprecated soon */
264 static int
_dhd_wlfc_hanger_get_genbit(void * hanger,void * pkt,uint32 slot_id,int * gen)265 _dhd_wlfc_hanger_get_genbit(void* hanger, void* pkt, uint32 slot_id, int* gen)
266 {
267 int rc = BCME_OK;
268 wlfc_hanger_t* h = (wlfc_hanger_t*)hanger;
269
270 *gen = 0xff;
271
272 /* this packet was not pushed at the time it went to the firmware */
273 if (slot_id == WLFC_HANGER_MAXITEMS)
274 return BCME_NOTFOUND;
275
276 if (h) {
277 if (h->items[slot_id].state != WLFC_HANGER_ITEM_STATE_FREE) {
278 *gen = h->items[slot_id].gen;
279 }
280 else {
281 DHD_ERROR(("Error: %s():%d item not used\n",
282 __FUNCTION__, __LINE__));
283 rc = BCME_NOTFOUND;
284 }
285 } else {
286 rc = BCME_BADARG;
287 }
288
289 return rc;
290 }
291
292 /** @deprecated soon */
293 static int
_dhd_wlfc_hanger_pushpkt(void * hanger,void * pkt,uint32 slot_id)294 _dhd_wlfc_hanger_pushpkt(void* hanger, void* pkt, uint32 slot_id)
295 {
296 int rc = BCME_OK;
297 wlfc_hanger_t* h = (wlfc_hanger_t*)hanger;
298
299 if (h && (slot_id < WLFC_HANGER_MAXITEMS)) {
300 if (h->items[slot_id].state == WLFC_HANGER_ITEM_STATE_FREE) {
301 h->items[slot_id].state = WLFC_HANGER_ITEM_STATE_INUSE;
302 h->items[slot_id].pkt = pkt;
303 h->items[slot_id].pkt_state = 0;
304 h->items[slot_id].pkt_txstatus = 0;
305 h->pushed++;
306 } else {
307 h->failed_to_push++;
308 rc = BCME_NOTFOUND;
309 }
310 } else {
311 rc = BCME_BADARG;
312 }
313
314 return rc;
315 }
316
317 /** @deprecated soon */
318 static int
_dhd_wlfc_hanger_poppkt(void * hanger,uint32 slot_id,void ** pktout,bool remove_from_hanger)319 _dhd_wlfc_hanger_poppkt(void* hanger, uint32 slot_id, void** pktout, bool remove_from_hanger)
320 {
321 int rc = BCME_OK;
322 wlfc_hanger_t* h = (wlfc_hanger_t*)hanger;
323
324 *pktout = NULL;
325
326 /* this packet was not pushed at the time it went to the firmware */
327 if (slot_id == WLFC_HANGER_MAXITEMS)
328 return BCME_NOTFOUND;
329
330 if (h) {
331 if (h->items[slot_id].state != WLFC_HANGER_ITEM_STATE_FREE) {
332 *pktout = h->items[slot_id].pkt;
333 if (remove_from_hanger) {
334 h->items[slot_id].state =
335 WLFC_HANGER_ITEM_STATE_FREE;
336 h->items[slot_id].pkt = NULL;
337 h->items[slot_id].gen = 0xff;
338 h->items[slot_id].identifier = 0;
339 h->popped++;
340 }
341 } else {
342 h->failed_to_pop++;
343 rc = BCME_NOTFOUND;
344 }
345 } else {
346 rc = BCME_BADARG;
347 }
348
349 return rc;
350 }
351
352 /** @deprecated soon */
353 static int
_dhd_wlfc_hanger_mark_suppressed(void * hanger,uint32 slot_id,uint8 gen)354 _dhd_wlfc_hanger_mark_suppressed(void* hanger, uint32 slot_id, uint8 gen)
355 {
356 int rc = BCME_OK;
357 wlfc_hanger_t* h = (wlfc_hanger_t*)hanger;
358
359 /* this packet was not pushed at the time it went to the firmware */
360 if (slot_id == WLFC_HANGER_MAXITEMS)
361 return BCME_NOTFOUND;
362 if (h) {
363 h->items[slot_id].gen = gen;
364 if (h->items[slot_id].state == WLFC_HANGER_ITEM_STATE_INUSE) {
365 h->items[slot_id].state = WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED;
366 } else {
367 rc = BCME_BADARG;
368 }
369 } else {
370 rc = BCME_BADARG;
371 }
372
373 return rc;
374 }
375
376 /** remove reference of specific packet in hanger */
377 /** @deprecated soon */
378 static bool
_dhd_wlfc_hanger_remove_reference(wlfc_hanger_t * h,void * pkt)379 _dhd_wlfc_hanger_remove_reference(wlfc_hanger_t* h, void* pkt)
380 {
381 int i;
382
383 if (!h || !pkt) {
384 return FALSE;
385 }
386
387 i = WL_TXSTATUS_GET_HSLOT(DHD_PKTTAG_H2DTAG(PKTTAG(pkt)));
388
389 if ((i < h->max_items) && (pkt == h->items[i].pkt)) {
390 if (h->items[i].state == WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED) {
391 h->items[i].state = WLFC_HANGER_ITEM_STATE_FREE;
392 h->items[i].pkt = NULL;
393 h->items[i].gen = 0xff;
394 h->items[i].identifier = 0;
395 return TRUE;
396 } else {
397 DHD_ERROR(("Error: %s():%d item not suppressed\n",
398 __FUNCTION__, __LINE__));
399 }
400 }
401
402 return FALSE;
403 }
404
405 /** afq = At Firmware Queue, queue containing packets pending in the dongle */
406 static int
_dhd_wlfc_enque_afq(athost_wl_status_info_t * ctx,void * p)407 _dhd_wlfc_enque_afq(athost_wl_status_info_t* ctx, void *p)
408 {
409 wlfc_mac_descriptor_t* entry;
410 uint16 entry_idx = WL_TXSTATUS_GET_HSLOT(DHD_PKTTAG_H2DTAG(PKTTAG(p)));
411 uint8 prec = DHD_PKTTAG_FIFO(PKTTAG(p));
412
413 if (entry_idx < WLFC_MAC_DESC_TABLE_SIZE)
414 entry = &ctx->destination_entries.nodes[entry_idx];
415 else if (entry_idx < (WLFC_MAC_DESC_TABLE_SIZE + WLFC_MAX_IFNUM))
416 entry = &ctx->destination_entries.interfaces[entry_idx - WLFC_MAC_DESC_TABLE_SIZE];
417 else
418 entry = &ctx->destination_entries.other;
419
420 pktq_penq(&entry->afq, prec, p);
421
422 return BCME_OK;
423 }
424
425 /** afq = At Firmware Queue, queue containing packets pending in the dongle */
426 static int
_dhd_wlfc_deque_afq(athost_wl_status_info_t * ctx,uint16 hslot,uint8 hcnt,uint8 prec,void ** pktout)427 _dhd_wlfc_deque_afq(athost_wl_status_info_t* ctx, uint16 hslot, uint8 hcnt, uint8 prec,
428 void **pktout)
429 {
430 wlfc_mac_descriptor_t *entry;
431 struct pktq *pq;
432 struct pktq_prec *q;
433 void *p, *b;
434
435 if (!ctx) {
436 DHD_ERROR(("%s: ctx(%p), pktout(%p)\n", __FUNCTION__, ctx, pktout));
437 return BCME_BADARG;
438 }
439
440 if (pktout) {
441 *pktout = NULL;
442 }
443
444 ASSERT(hslot < (WLFC_MAC_DESC_TABLE_SIZE + WLFC_MAX_IFNUM + 1));
445
446 if (hslot < WLFC_MAC_DESC_TABLE_SIZE)
447 entry = &ctx->destination_entries.nodes[hslot];
448 else if (hslot < (WLFC_MAC_DESC_TABLE_SIZE + WLFC_MAX_IFNUM))
449 entry = &ctx->destination_entries.interfaces[hslot - WLFC_MAC_DESC_TABLE_SIZE];
450 else
451 entry = &ctx->destination_entries.other;
452
453 pq = &entry->afq;
454
455 ASSERT(prec < pq->num_prec);
456
457 q = &pq->q[prec];
458
459 b = NULL;
460 p = q->head;
461
462 while (p && (hcnt != WL_TXSTATUS_GET_FREERUNCTR(DHD_PKTTAG_H2DTAG(PKTTAG(p)))))
463 {
464 b = p;
465 p = PKTLINK(p);
466 }
467
468 if (p == NULL) {
469 /* none is matched */
470 if (b) {
471 DHD_ERROR(("%s: can't find matching seq(%d)\n", __FUNCTION__, hcnt));
472 } else {
473 DHD_ERROR(("%s: queue is empty\n", __FUNCTION__));
474 }
475
476 return BCME_ERROR;
477 }
478
479 bcm_pkt_validate_chk(p);
480
481 if (!b) {
482 /* head packet is matched */
483 if ((q->head = PKTLINK(p)) == NULL) {
484 q->tail = NULL;
485 }
486 } else {
487 /* middle packet is matched */
488 DHD_INFO(("%s: out of order, seq(%d), head_seq(%d)\n", __FUNCTION__, hcnt,
489 WL_TXSTATUS_GET_FREERUNCTR(DHD_PKTTAG_H2DTAG(PKTTAG(q->head)))));
490 ctx->stats.ooo_pkts[prec]++;
491 PKTSETLINK(b, PKTLINK(p));
492 if (PKTLINK(p) == NULL) {
493 q->tail = b;
494 }
495 }
496
497 q->len--;
498 pq->len--;
499
500 PKTSETLINK(p, NULL);
501
502 if (pktout) {
503 *pktout = p;
504 }
505
506 return BCME_OK;
507 } /* _dhd_wlfc_deque_afq */
508
509 /**
510 * Flow control information piggy backs on packets, in the form of one or more TLVs. This function
511 * pushes one or more TLVs onto a packet that is going to be sent towards the dongle.
512 *
513 * @param[in] ctx
514 * @param[in/out] packet
515 * @param[in] tim_signal TRUE if parameter 'tim_bmp' is valid
516 * @param[in] tim_bmp
517 * @param[in] mac_handle
518 * @param[in] htodtag
519 * @param[in] htodseq d11 seqno for seqno reuse, only used if 'seq reuse' was agreed upon
520 * earlier between host and firmware.
521 * @param[in] skip_wlfc_hdr
522 */
523 static int
_dhd_wlfc_pushheader(athost_wl_status_info_t * ctx,void ** packet,bool tim_signal,uint8 tim_bmp,uint8 mac_handle,uint32 htodtag,uint16 htodseq,bool skip_wlfc_hdr)524 _dhd_wlfc_pushheader(athost_wl_status_info_t* ctx, void** packet, bool tim_signal,
525 uint8 tim_bmp, uint8 mac_handle, uint32 htodtag, uint16 htodseq, bool skip_wlfc_hdr)
526 {
527 uint32 wl_pktinfo = 0;
528 uint8* wlh;
529 uint8 dataOffset = 0;
530 uint8 fillers;
531 uint8 tim_signal_len = 0;
532 dhd_pub_t *dhdp = (dhd_pub_t *)ctx->dhdp;
533
534 struct bdc_header *h;
535 void *p = *packet;
536
537 if (skip_wlfc_hdr)
538 goto push_bdc_hdr;
539
540 if (tim_signal) {
541 tim_signal_len = TLV_HDR_LEN + WLFC_CTL_VALUE_LEN_PENDING_TRAFFIC_BMP;
542 }
543
544 /* +2 is for Type[1] and Len[1] in TLV, plus TIM signal */
545 dataOffset = WLFC_CTL_VALUE_LEN_PKTTAG + TLV_HDR_LEN + tim_signal_len;
546 if (WLFC_GET_REUSESEQ(dhdp->wlfc_mode)) {
547 dataOffset += WLFC_CTL_VALUE_LEN_SEQ;
548 }
549
550 fillers = ROUNDUP(dataOffset, 4) - dataOffset;
551 dataOffset += fillers;
552
553 PKTPUSH(ctx->osh, p, dataOffset);
554 wlh = (uint8*) PKTDATA(ctx->osh, p);
555
556 wl_pktinfo = htol32(htodtag);
557
558 wlh[TLV_TAG_OFF] = WLFC_CTL_TYPE_PKTTAG;
559 wlh[TLV_LEN_OFF] = WLFC_CTL_VALUE_LEN_PKTTAG;
560 memcpy(&wlh[TLV_HDR_LEN] /* dst */, &wl_pktinfo, sizeof(uint32));
561
562 if (WLFC_GET_REUSESEQ(dhdp->wlfc_mode)) {
563 uint16 wl_seqinfo = htol16(htodseq);
564 wlh[TLV_LEN_OFF] += WLFC_CTL_VALUE_LEN_SEQ;
565 memcpy(&wlh[TLV_HDR_LEN + WLFC_CTL_VALUE_LEN_PKTTAG], &wl_seqinfo,
566 WLFC_CTL_VALUE_LEN_SEQ);
567 }
568
569 if (tim_signal_len) {
570 wlh[dataOffset - fillers - tim_signal_len ] =
571 WLFC_CTL_TYPE_PENDING_TRAFFIC_BMP;
572 wlh[dataOffset - fillers - tim_signal_len + 1] =
573 WLFC_CTL_VALUE_LEN_PENDING_TRAFFIC_BMP;
574 wlh[dataOffset - fillers - tim_signal_len + 2] = mac_handle;
575 wlh[dataOffset - fillers - tim_signal_len + 3] = tim_bmp;
576 }
577 if (fillers)
578 memset(&wlh[dataOffset - fillers], WLFC_CTL_TYPE_FILLER, fillers);
579
580 push_bdc_hdr:
581 PKTPUSH(ctx->osh, p, BDC_HEADER_LEN);
582 h = (struct bdc_header *)PKTDATA(ctx->osh, p);
583 h->flags = (BDC_PROTO_VER << BDC_FLAG_VER_SHIFT);
584 if (PKTSUMNEEDED(p))
585 h->flags |= BDC_FLAG_SUM_NEEDED;
586
587
588 h->priority = (PKTPRIO(p) & BDC_PRIORITY_MASK);
589 h->flags2 = 0;
590 h->dataOffset = dataOffset >> 2;
591 BDC_SET_IF_IDX(h, DHD_PKTTAG_IF(PKTTAG(p)));
592 *packet = p;
593 return BCME_OK;
594 } /* _dhd_wlfc_pushheader */
595
596 /**
597 * Removes (PULLs) flow control related headers from the caller supplied packet, is invoked eg
598 * when a packet is about to be freed.
599 */
600 static int
_dhd_wlfc_pullheader(athost_wl_status_info_t * ctx,void * pktbuf)601 _dhd_wlfc_pullheader(athost_wl_status_info_t* ctx, void* pktbuf)
602 {
603 struct bdc_header *h;
604
605 if (PKTLEN(ctx->osh, pktbuf) < BDC_HEADER_LEN) {
606 DHD_ERROR(("%s: rx data too short (%d < %d)\n", __FUNCTION__,
607 PKTLEN(ctx->osh, pktbuf), BDC_HEADER_LEN));
608 return BCME_ERROR;
609 }
610 h = (struct bdc_header *)PKTDATA(ctx->osh, pktbuf);
611
612 /* pull BDC header */
613 PKTPULL(ctx->osh, pktbuf, BDC_HEADER_LEN);
614
615 if (PKTLEN(ctx->osh, pktbuf) < (uint)(h->dataOffset << 2)) {
616 DHD_ERROR(("%s: rx data too short (%d < %d)\n", __FUNCTION__,
617 PKTLEN(ctx->osh, pktbuf), (h->dataOffset << 2)));
618 return BCME_ERROR;
619 }
620
621 /* pull wl-header */
622 PKTPULL(ctx->osh, pktbuf, (h->dataOffset << 2));
623 return BCME_OK;
624 }
625
626 /**
627 * @param[in/out] p packet
628 */
629 static wlfc_mac_descriptor_t*
_dhd_wlfc_find_table_entry(athost_wl_status_info_t * ctx,void * p)630 _dhd_wlfc_find_table_entry(athost_wl_status_info_t* ctx, void* p)
631 {
632 int i;
633 wlfc_mac_descriptor_t* table = ctx->destination_entries.nodes;
634 uint8 ifid = DHD_PKTTAG_IF(PKTTAG(p));
635 uint8* dstn = DHD_PKTTAG_DSTN(PKTTAG(p));
636 wlfc_mac_descriptor_t* entry = DHD_PKTTAG_ENTRY(PKTTAG(p));
637 int iftype = ctx->destination_entries.interfaces[ifid].iftype;
638
639 /* saved one exists, return it */
640 if (entry)
641 return entry;
642
643 /* Multicast destination, STA and P2P clients get the interface entry.
644 * STA/GC gets the Mac Entry for TDLS destinations, TDLS destinations
645 * have their own entry.
646 */
647 if ((iftype == WLC_E_IF_ROLE_STA || ETHER_ISMULTI(dstn) ||
648 iftype == WLC_E_IF_ROLE_P2P_CLIENT) &&
649 (ctx->destination_entries.interfaces[ifid].occupied)) {
650 entry = &ctx->destination_entries.interfaces[ifid];
651 }
652
653 if (entry && ETHER_ISMULTI(dstn)) {
654 DHD_PKTTAG_SET_ENTRY(PKTTAG(p), entry);
655 return entry;
656 }
657
658 for (i = 0; i < WLFC_MAC_DESC_TABLE_SIZE; i++) {
659 if (table[i].occupied) {
660 if (table[i].interface_id == ifid) {
661 if (!memcmp(table[i].ea, dstn, ETHER_ADDR_LEN)) {
662 entry = &table[i];
663 break;
664 }
665 }
666 }
667 }
668
669 if (entry == NULL)
670 entry = &ctx->destination_entries.other;
671
672 DHD_PKTTAG_SET_ENTRY(PKTTAG(p), entry);
673
674 return entry;
675 } /* _dhd_wlfc_find_table_entry */
676
677 /**
678 * In case a packet must be dropped (because eg the queues are full), various tallies have to be
679 * be updated. Called from several other functions.
680 * @param[in] dhdp pointer to public DHD structure
681 * @param[in] prec precedence of the packet
682 * @param[in] p the packet to be dropped
683 * @param[in] bPktInQ TRUE if packet is part of a queue
684 */
685 static int
_dhd_wlfc_prec_drop(dhd_pub_t * dhdp,int prec,void * p,bool bPktInQ)686 _dhd_wlfc_prec_drop(dhd_pub_t *dhdp, int prec, void* p, bool bPktInQ)
687 {
688 athost_wl_status_info_t* ctx;
689 void *pout = NULL;
690
691 ASSERT(dhdp && p);
692 ASSERT(prec >= 0 && prec <= WLFC_PSQ_PREC_COUNT);
693
694 ctx = (athost_wl_status_info_t*)dhdp->wlfc_state;
695
696 if (!WLFC_GET_AFQ(dhdp->wlfc_mode) && (prec & 1)) {
697 /* suppressed queue, need pop from hanger */
698 _dhd_wlfc_hanger_poppkt(ctx->hanger, WL_TXSTATUS_GET_HSLOT(DHD_PKTTAG_H2DTAG
699 (PKTTAG(p))), &pout, TRUE);
700 ASSERT(p == pout);
701 }
702
703 if (!(prec & 1)) {
704 #ifdef DHDTCPACK_SUPPRESS
705 /* pkt in delayed q, so fake push BDC header for
706 * dhd_tcpack_check_xmit() and dhd_txcomplete().
707 */
708 _dhd_wlfc_pushheader(ctx, &p, FALSE, 0, 0, 0, 0, TRUE);
709
710 /* This packet is about to be freed, so remove it from tcp_ack_info_tbl
711 * This must be one of...
712 * 1. A pkt already in delayQ is evicted by another pkt with higher precedence
713 * in _dhd_wlfc_prec_enq_with_drop()
714 * 2. A pkt could not be enqueued to delayQ because it is full,
715 * in _dhd_wlfc_enque_delayq().
716 * 3. A pkt could not be enqueued to delayQ because it is full,
717 * in _dhd_wlfc_rollback_packet_toq().
718 */
719 if (dhd_tcpack_check_xmit(dhdp, p) == BCME_ERROR) {
720 DHD_ERROR(("%s %d: tcpack_suppress ERROR!!!"
721 " Stop using it\n",
722 __FUNCTION__, __LINE__));
723 dhd_tcpack_suppress_set(dhdp, TCPACK_SUP_OFF);
724 }
725 #endif /* DHDTCPACK_SUPPRESS */
726 }
727
728 if (bPktInQ) {
729 ctx->pkt_cnt_in_q[DHD_PKTTAG_IF(PKTTAG(p))][prec>>1]--;
730 ctx->pkt_cnt_per_ac[prec>>1]--;
731 ctx->pkt_cnt_in_psq--;
732 }
733
734 ctx->pkt_cnt_in_drv[DHD_PKTTAG_IF(PKTTAG(p))][DHD_PKTTAG_FIFO(PKTTAG(p))]--;
735 ctx->stats.pktout++;
736 ctx->stats.drop_pkts[prec]++;
737
738 dhd_txcomplete(dhdp, p, FALSE);
739 PKTFREE(ctx->osh, p, TRUE);
740
741 return 0;
742 } /* _dhd_wlfc_prec_drop */
743
744 /**
745 * Called when eg the host handed a new packet over to the driver, or when the dongle reported
746 * that a packet could currently not be transmitted (=suppressed). This function enqueues a transmit
747 * packet in the host driver to be (re)transmitted at a later opportunity.
748 * @param[in] dhdp pointer to public DHD structure
749 * @param[in] qHead When TRUE, queue packet at head instead of tail, to preserve d11 sequence
750 */
751 static bool
_dhd_wlfc_prec_enq_with_drop(dhd_pub_t * dhdp,struct pktq * pq,void * pkt,int prec,bool qHead,uint8 current_seq)752 _dhd_wlfc_prec_enq_with_drop(dhd_pub_t *dhdp, struct pktq *pq, void *pkt, int prec, bool qHead,
753 uint8 current_seq)
754 {
755 void *p = NULL;
756 int eprec = -1; /* precedence to evict from */
757 athost_wl_status_info_t* ctx;
758
759 ASSERT(dhdp && pq && pkt);
760 ASSERT(prec >= 0 && prec < pq->num_prec);
761
762 ctx = (athost_wl_status_info_t*)dhdp->wlfc_state;
763
764 /* Fast case, precedence queue is not full and we are also not
765 * exceeding total queue length
766 */
767 if (!pktq_pfull(pq, prec) && !pktq_full(pq)) {
768 goto exit;
769 }
770
771 /* Determine precedence from which to evict packet, if any */
772 if (pktq_pfull(pq, prec)) {
773 eprec = prec;
774 } else if (pktq_full(pq)) {
775 p = pktq_peek_tail(pq, &eprec);
776 if (!p) {
777 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
778 return FALSE;
779 }
780 if ((eprec > prec) || (eprec < 0)) {
781 if (!pktq_pempty(pq, prec)) {
782 eprec = prec;
783 } else {
784 return FALSE;
785 }
786 }
787 }
788
789 /* Evict if needed */
790 if (eprec >= 0) {
791 /* Detect queueing to unconfigured precedence */
792 ASSERT(!pktq_pempty(pq, eprec));
793 /* Evict all fragmented frames */
794 dhd_prec_drop_pkts(dhdp, pq, eprec, _dhd_wlfc_prec_drop);
795 }
796
797 exit:
798 /* Enqueue */
799 _dhd_wlfc_prec_enque(pq, prec, pkt, qHead, current_seq,
800 WLFC_GET_REORDERSUPP(dhdp->wlfc_mode));
801 ctx->pkt_cnt_in_q[DHD_PKTTAG_IF(PKTTAG(pkt))][prec>>1]++;
802 ctx->pkt_cnt_per_ac[prec>>1]++;
803 ctx->pkt_cnt_in_psq++;
804
805 return TRUE;
806 } /* _dhd_wlfc_prec_enq_with_drop */
807
808 /**
809 * Called during eg the 'committing' of a transmit packet from the OS layer to a lower layer, in
810 * the event that this 'commit' failed.
811 */
812 static int
_dhd_wlfc_rollback_packet_toq(athost_wl_status_info_t * ctx,void * p,ewlfc_packet_state_t pkt_type,uint32 hslot)813 _dhd_wlfc_rollback_packet_toq(athost_wl_status_info_t* ctx,
814 void* p, ewlfc_packet_state_t pkt_type, uint32 hslot)
815 {
816 /*
817 * put the packet back to the head of queue
818 * - suppressed packet goes back to suppress sub-queue
819 * - pull out the header, if new or delayed packet
820 *
821 * Note: hslot is used only when header removal is done.
822 */
823 wlfc_mac_descriptor_t* entry;
824 int rc = BCME_OK;
825 int prec, fifo_id;
826
827 entry = _dhd_wlfc_find_table_entry(ctx, p);
828 prec = DHD_PKTTAG_FIFO(PKTTAG(p));
829 fifo_id = prec << 1;
830 if (pkt_type == eWLFC_PKTTYPE_SUPPRESSED)
831 fifo_id += 1;
832 if (entry != NULL) {
833 /*
834 if this packet did not count against FIFO credit, it must have
835 taken a requested_credit from the firmware (for pspoll etc.)
836 */
837 if ((prec != AC_COUNT) && !DHD_PKTTAG_CREDITCHECK(PKTTAG(p)))
838 entry->requested_credit++;
839
840 if (pkt_type == eWLFC_PKTTYPE_DELAYED) {
841 /* decrement sequence count */
842 WLFC_DECR_SEQCOUNT(entry, prec);
843 /* remove header first */
844 rc = _dhd_wlfc_pullheader(ctx, p);
845 if (rc != BCME_OK) {
846 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
847 goto exit;
848 }
849 }
850
851 if (_dhd_wlfc_prec_enq_with_drop(ctx->dhdp, &entry->psq, p, fifo_id, TRUE,
852 WLFC_SEQCOUNT(entry, fifo_id>>1))
853 == FALSE) {
854 /* enque failed */
855 DHD_ERROR(("Error: %s():%d, fifo_id(%d)\n",
856 __FUNCTION__, __LINE__, fifo_id));
857 rc = BCME_ERROR;
858 }
859 } else {
860 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
861 rc = BCME_ERROR;
862 }
863
864 exit:
865 if (rc != BCME_OK) {
866 ctx->stats.rollback_failed++;
867 _dhd_wlfc_prec_drop(ctx->dhdp, fifo_id, p, FALSE);
868 } else {
869 ctx->stats.rollback++;
870 }
871
872 return rc;
873 } /* _dhd_wlfc_rollback_packet_toq */
874
875 /** Returns TRUE if host OS -> DHD flow control is allowed on the caller supplied interface */
876 static bool
_dhd_wlfc_allow_fc(athost_wl_status_info_t * ctx,uint8 ifid)877 _dhd_wlfc_allow_fc(athost_wl_status_info_t* ctx, uint8 ifid)
878 {
879 int prec, ac_traffic = WLFC_NO_TRAFFIC;
880
881 for (prec = 0; prec < AC_COUNT; prec++) {
882 if (ctx->pkt_cnt_in_drv[ifid][prec] > 0) {
883 if (ac_traffic == WLFC_NO_TRAFFIC)
884 ac_traffic = prec + 1;
885 else if (ac_traffic != (prec + 1))
886 ac_traffic = WLFC_MULTI_TRAFFIC;
887 }
888 }
889
890 if (ac_traffic >= 1 && ac_traffic <= AC_COUNT) {
891 /* single AC (BE/BK/VI/VO) in queue */
892 if (ctx->allow_fc) {
893 return TRUE;
894 } else {
895 uint32 delta;
896 uint32 curr_t = OSL_SYSUPTIME();
897
898 if (ctx->fc_defer_timestamp == 0) {
899 /* first single ac scenario */
900 ctx->fc_defer_timestamp = curr_t;
901 return FALSE;
902 }
903
904 /* single AC duration, this handles wrap around, e.g. 1 - ~0 = 2. */
905 delta = curr_t - ctx->fc_defer_timestamp;
906 if (delta >= WLFC_FC_DEFER_PERIOD_MS) {
907 ctx->allow_fc = TRUE;
908 }
909 }
910 } else {
911 /* multiple ACs or BCMC in queue */
912 ctx->allow_fc = FALSE;
913 ctx->fc_defer_timestamp = 0;
914 }
915
916 return ctx->allow_fc;
917 } /* _dhd_wlfc_allow_fc */
918
919 /**
920 * Starts or stops the flow of transmit packets from the host OS towards the DHD, depending on
921 * low/high watermarks.
922 */
923 static void
_dhd_wlfc_flow_control_check(athost_wl_status_info_t * ctx,struct pktq * pq,uint8 if_id)924 _dhd_wlfc_flow_control_check(athost_wl_status_info_t* ctx, struct pktq* pq, uint8 if_id)
925 {
926 dhd_pub_t *dhdp;
927
928 ASSERT(ctx);
929
930 dhdp = (dhd_pub_t *)ctx->dhdp;
931 ASSERT(dhdp);
932
933 if (dhdp->skip_fc && dhdp->skip_fc((void *)dhdp, if_id))
934 return;
935
936 if ((ctx->hostif_flow_state[if_id] == OFF) && !_dhd_wlfc_allow_fc(ctx, if_id))
937 return;
938
939 if ((pq->len <= WLFC_FLOWCONTROL_LOWATER) && (ctx->hostif_flow_state[if_id] == ON)) {
940 /* start traffic */
941 ctx->hostif_flow_state[if_id] = OFF;
942 WLFC_DBGMESG(("F"));
943
944 dhd_txflowcontrol(dhdp, if_id, OFF);
945
946 ctx->toggle_host_if = 0;
947 }
948
949 if ((pq->len >= WLFC_FLOWCONTROL_HIWATER) && (ctx->hostif_flow_state[if_id] == OFF)) {
950 /* stop traffic */
951 ctx->hostif_flow_state[if_id] = ON;
952 WLFC_DBGMESG(("N"));
953
954 dhd_txflowcontrol(dhdp, if_id, ON);
955
956 ctx->host_ifidx = if_id;
957 ctx->toggle_host_if = 1;
958 }
959
960 return;
961 } /* _dhd_wlfc_flow_control_check */
962
963 static int
_dhd_wlfc_send_signalonly_packet(athost_wl_status_info_t * ctx,wlfc_mac_descriptor_t * entry,uint8 ta_bmp)964 _dhd_wlfc_send_signalonly_packet(athost_wl_status_info_t* ctx, wlfc_mac_descriptor_t* entry,
965 uint8 ta_bmp)
966 {
967 int rc = BCME_OK;
968 void* p = NULL;
969 int dummylen = ((dhd_pub_t *)ctx->dhdp)->hdrlen+ 16;
970 dhd_pub_t *dhdp = (dhd_pub_t *)ctx->dhdp;
971
972 if (dhdp->proptxstatus_txoff) {
973 rc = BCME_NORESOURCE;
974 return rc;
975 }
976
977 /* allocate a dummy packet */
978 p = PKTGET(ctx->osh, dummylen, TRUE);
979 if (p) {
980 PKTPULL(ctx->osh, p, dummylen);
981 DHD_PKTTAG_SET_H2DTAG(PKTTAG(p), 0);
982 _dhd_wlfc_pushheader(ctx, &p, TRUE, ta_bmp, entry->mac_handle, 0, 0, FALSE);
983 DHD_PKTTAG_SETSIGNALONLY(PKTTAG(p), 1);
984 DHD_PKTTAG_WLFCPKT_SET(PKTTAG(p), 1);
985 #ifdef PROP_TXSTATUS_DEBUG
986 ctx->stats.signal_only_pkts_sent++;
987 #endif
988
989 #if defined(BCMPCIE)
990 rc = dhd_bus_txdata(dhdp->bus, p, ctx->host_ifidx);
991 #else
992 rc = dhd_bus_txdata(dhdp->bus, p);
993 #endif
994 if (rc != BCME_OK) {
995 _dhd_wlfc_pullheader(ctx, p);
996 PKTFREE(ctx->osh, p, TRUE);
997 }
998 } else {
999 DHD_ERROR(("%s: couldn't allocate new %d-byte packet\n",
1000 __FUNCTION__, dummylen));
1001 rc = BCME_NOMEM;
1002 dhdp->tx_pktgetfail++;
1003 }
1004
1005 return rc;
1006 } /* _dhd_wlfc_send_signalonly_packet */
1007
1008 /**
1009 * Called on eg receiving 'mac close' indication from dongle. Updates the per-MAC administration
1010 * maintained in caller supplied parameter 'entry'.
1011 *
1012 * @param[in/out] entry administration about a remote MAC entity
1013 * @param[in] prec precedence queue for this remote MAC entitity
1014 *
1015 * Return value: TRUE if traffic availability changed
1016 */
1017 static bool
_dhd_wlfc_traffic_pending_check(athost_wl_status_info_t * ctx,wlfc_mac_descriptor_t * entry,int prec)1018 _dhd_wlfc_traffic_pending_check(athost_wl_status_info_t* ctx, wlfc_mac_descriptor_t* entry,
1019 int prec)
1020 {
1021 bool rc = FALSE;
1022
1023 if (entry->state == WLFC_STATE_CLOSE) {
1024 if ((pktq_plen(&entry->psq, (prec << 1)) == 0) &&
1025 (pktq_plen(&entry->psq, ((prec << 1) + 1)) == 0)) {
1026 /* no packets in both 'normal' and 'suspended' queues */
1027 if (entry->traffic_pending_bmp & NBITVAL(prec)) {
1028 rc = TRUE;
1029 entry->traffic_pending_bmp =
1030 entry->traffic_pending_bmp & ~ NBITVAL(prec);
1031 }
1032 } else {
1033 /* packets are queued in host for transmission to dongle */
1034 if (!(entry->traffic_pending_bmp & NBITVAL(prec))) {
1035 rc = TRUE;
1036 entry->traffic_pending_bmp =
1037 entry->traffic_pending_bmp | NBITVAL(prec);
1038 }
1039 }
1040 }
1041
1042 if (rc) {
1043 /* request a TIM update to firmware at the next piggyback opportunity */
1044 if (entry->traffic_lastreported_bmp != entry->traffic_pending_bmp) {
1045 entry->send_tim_signal = 1;
1046 _dhd_wlfc_send_signalonly_packet(ctx, entry, entry->traffic_pending_bmp);
1047 entry->traffic_lastreported_bmp = entry->traffic_pending_bmp;
1048 entry->send_tim_signal = 0;
1049 } else {
1050 rc = FALSE;
1051 }
1052 }
1053
1054 return rc;
1055 } /* _dhd_wlfc_traffic_pending_check */
1056
1057 /**
1058 * Called on receiving a 'd11 suppressed' or 'wl suppressed' tx status from the firmware. Enqueues
1059 * the packet to transmit to firmware again at a later opportunity.
1060 */
1061 static int
_dhd_wlfc_enque_suppressed(athost_wl_status_info_t * ctx,int prec,void * p)1062 _dhd_wlfc_enque_suppressed(athost_wl_status_info_t* ctx, int prec, void* p)
1063 {
1064 wlfc_mac_descriptor_t* entry;
1065
1066 entry = _dhd_wlfc_find_table_entry(ctx, p);
1067 if (entry == NULL) {
1068 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
1069 return BCME_NOTFOUND;
1070 }
1071 /*
1072 - suppressed packets go to sub_queue[2*prec + 1] AND
1073 - delayed packets go to sub_queue[2*prec + 0] to ensure
1074 order of delivery.
1075 */
1076 if (_dhd_wlfc_prec_enq_with_drop(ctx->dhdp, &entry->psq, p, ((prec << 1) + 1), FALSE,
1077 WLFC_SEQCOUNT(entry, prec))
1078 == FALSE) {
1079 ctx->stats.delayq_full_error++;
1080 WLFC_DBGMESG(("s"));
1081 return BCME_ERROR;
1082 }
1083
1084 /* A packet has been pushed, update traffic availability bitmap, if applicable */
1085 _dhd_wlfc_traffic_pending_check(ctx, entry, prec);
1086 _dhd_wlfc_flow_control_check(ctx, &entry->psq, DHD_PKTTAG_IF(PKTTAG(p)));
1087 return BCME_OK;
1088 }
1089
1090 /**
1091 * Called when a transmit packet is about to be 'committed' from the OS layer to a lower layer
1092 * towards the dongle (eg the DBUS layer). Updates wlfc administration. May modify packet.
1093 *
1094 * @param[in/out] ctx driver specific flow control administration
1095 * @param[in/out] entry The remote MAC entity for which the packet is destined.
1096 * @param[in/out] packet Packet to send. This function optionally adds TLVs to the packet.
1097 * @param[in] header_needed True if packet is 'new' to flow control
1098 * @param[out] slot Handle to container in which the packet was 'parked'
1099 */
1100 static int
_dhd_wlfc_pretx_pktprocess(athost_wl_status_info_t * ctx,wlfc_mac_descriptor_t * entry,void ** packet,int header_needed,uint32 * slot)1101 _dhd_wlfc_pretx_pktprocess(athost_wl_status_info_t* ctx,
1102 wlfc_mac_descriptor_t* entry, void** packet, int header_needed, uint32* slot)
1103 {
1104 int rc = BCME_OK;
1105 int hslot = WLFC_HANGER_MAXITEMS;
1106 bool send_tim_update = FALSE;
1107 uint32 htod = 0;
1108 uint16 htodseq = 0;
1109 uint8 free_ctr;
1110 int gen = 0xff;
1111 dhd_pub_t *dhdp = (dhd_pub_t *)ctx->dhdp;
1112 void * p = *packet;
1113
1114 *slot = hslot;
1115
1116 if (entry == NULL) {
1117 entry = _dhd_wlfc_find_table_entry(ctx, p);
1118 }
1119
1120 if (entry == NULL) {
1121 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
1122 return BCME_ERROR;
1123 }
1124
1125 if (entry->send_tim_signal) {
1126 /* sends a traffic indication bitmap to the dongle */
1127 send_tim_update = TRUE;
1128 entry->send_tim_signal = 0;
1129 entry->traffic_lastreported_bmp = entry->traffic_pending_bmp;
1130 }
1131
1132 if (header_needed) {
1133 if (WLFC_GET_AFQ(dhdp->wlfc_mode)) {
1134 hslot = (uint)(entry - &ctx->destination_entries.nodes[0]);
1135 } else {
1136 hslot = _dhd_wlfc_hanger_get_free_slot(ctx->hanger);
1137 }
1138 gen = entry->generation;
1139 free_ctr = WLFC_SEQCOUNT(entry, DHD_PKTTAG_FIFO(PKTTAG(p)));
1140 } else {
1141 if (WLFC_GET_REUSESEQ(dhdp->wlfc_mode)) {
1142 htodseq = DHD_PKTTAG_H2DSEQ(PKTTAG(p));
1143 }
1144
1145 hslot = WL_TXSTATUS_GET_HSLOT(DHD_PKTTAG_H2DTAG(PKTTAG(p)));
1146
1147 if (WLFC_GET_REORDERSUPP(dhdp->wlfc_mode)) {
1148 gen = entry->generation;
1149 } else if (WLFC_GET_AFQ(dhdp->wlfc_mode)) {
1150 gen = WL_TXSTATUS_GET_GENERATION(DHD_PKTTAG_H2DTAG(PKTTAG(p)));
1151 } else {
1152 _dhd_wlfc_hanger_get_genbit(ctx->hanger, p, hslot, &gen);
1153 }
1154
1155 free_ctr = WL_TXSTATUS_GET_FREERUNCTR(DHD_PKTTAG_H2DTAG(PKTTAG(p)));
1156 /* remove old header */
1157 _dhd_wlfc_pullheader(ctx, p);
1158 }
1159
1160 if (hslot >= WLFC_HANGER_MAXITEMS) {
1161 DHD_ERROR(("Error: %s():no hanger slot available\n", __FUNCTION__));
1162 return BCME_ERROR;
1163 }
1164
1165 WL_TXSTATUS_SET_FREERUNCTR(htod, free_ctr);
1166 WL_TXSTATUS_SET_HSLOT(htod, hslot);
1167 WL_TXSTATUS_SET_FIFO(htod, DHD_PKTTAG_FIFO(PKTTAG(p)));
1168 WL_TXSTATUS_SET_FLAGS(htod, WLFC_PKTFLAG_PKTFROMHOST);
1169 WL_TXSTATUS_SET_GENERATION(htod, gen);
1170 DHD_PKTTAG_SETPKTDIR(PKTTAG(p), 1);
1171
1172 if (!DHD_PKTTAG_CREDITCHECK(PKTTAG(p))) {
1173 /*
1174 Indicate that this packet is being sent in response to an
1175 explicit request from the firmware side.
1176 */
1177 WLFC_PKTFLAG_SET_PKTREQUESTED(htod);
1178 } else {
1179 WLFC_PKTFLAG_CLR_PKTREQUESTED(htod);
1180 }
1181
1182 rc = _dhd_wlfc_pushheader(ctx, &p, send_tim_update,
1183 entry->traffic_lastreported_bmp, entry->mac_handle, htod, htodseq, FALSE);
1184 if (rc == BCME_OK) {
1185 DHD_PKTTAG_SET_H2DTAG(PKTTAG(p), htod);
1186
1187 if (!WLFC_GET_AFQ(dhdp->wlfc_mode)) {
1188 wlfc_hanger_t *h = (wlfc_hanger_t*)(ctx->hanger);
1189 if (header_needed) {
1190 /*
1191 a new header was created for this packet.
1192 push to hanger slot and scrub q. Since bus
1193 send succeeded, increment seq number as well.
1194 */
1195 rc = _dhd_wlfc_hanger_pushpkt(ctx->hanger, p, hslot);
1196 if (rc == BCME_OK) {
1197 #ifdef PROP_TXSTATUS_DEBUG
1198 h->items[hslot].push_time =
1199 OSL_SYSUPTIME();
1200 #endif
1201 } else {
1202 DHD_ERROR(("%s() hanger_pushpkt() failed, rc: %d\n",
1203 __FUNCTION__, rc));
1204 }
1205 } else {
1206 /* clear hanger state */
1207 if (((wlfc_hanger_t*)(ctx->hanger))->items[hslot].pkt != p)
1208 DHD_ERROR(("%s() pkt not match: cur %p, hanger pkt %p\n",
1209 __FUNCTION__, p, h->items[hslot].pkt));
1210 ASSERT(h->items[hslot].pkt == p);
1211 bcm_object_feature_set(h->items[hslot].pkt,
1212 BCM_OBJECT_FEATURE_PKT_STATE, 0);
1213 h->items[hslot].pkt_state = 0;
1214 h->items[hslot].pkt_txstatus = 0;
1215 h->items[hslot].state = WLFC_HANGER_ITEM_STATE_INUSE;
1216 }
1217 }
1218
1219 if ((rc == BCME_OK) && header_needed) {
1220 /* increment free running sequence count */
1221 WLFC_INCR_SEQCOUNT(entry, DHD_PKTTAG_FIFO(PKTTAG(p)));
1222 }
1223 }
1224 *slot = hslot;
1225 *packet = p;
1226 return rc;
1227 } /* _dhd_wlfc_pretx_pktprocess */
1228
1229 /**
1230 * A remote wireless mac may be temporarily 'closed' due to power management. Returns '1' if remote
1231 * mac is in the 'open' state, otherwise '0'.
1232 */
1233 static int
_dhd_wlfc_is_destination_open(athost_wl_status_info_t * ctx,wlfc_mac_descriptor_t * entry,int prec)1234 _dhd_wlfc_is_destination_open(athost_wl_status_info_t* ctx,
1235 wlfc_mac_descriptor_t* entry, int prec)
1236 {
1237 wlfc_mac_descriptor_t* interfaces = ctx->destination_entries.interfaces;
1238
1239 if (entry->interface_id >= WLFC_MAX_IFNUM) {
1240 ASSERT(&ctx->destination_entries.other == entry);
1241 return 1;
1242 }
1243
1244 if (interfaces[entry->interface_id].iftype ==
1245 WLC_E_IF_ROLE_P2P_GO) {
1246 /* - destination interface is of type p2p GO.
1247 For a p2pGO interface, if the destination is OPEN but the interface is
1248 CLOSEd, do not send traffic. But if the dstn is CLOSEd while there is
1249 destination-specific-credit left send packets. This is because the
1250 firmware storing the destination-specific-requested packet in queue.
1251 */
1252 if ((entry->state == WLFC_STATE_CLOSE) && (entry->requested_credit == 0) &&
1253 (entry->requested_packet == 0)) {
1254 return 0;
1255 }
1256 }
1257
1258 /* AP, p2p_go -> unicast desc entry, STA/p2p_cl -> interface desc. entry */
1259 if ((((entry->state == WLFC_STATE_CLOSE) ||
1260 (interfaces[entry->interface_id].state == WLFC_STATE_CLOSE)) &&
1261 (entry->requested_credit == 0) &&
1262 (entry->requested_packet == 0)) ||
1263 (!(entry->ac_bitmap & (1 << prec)))) {
1264 return 0;
1265 }
1266
1267 return 1;
1268 } /* _dhd_wlfc_is_destination_open */
1269
1270 /**
1271 * Dequeues a suppressed or delayed packet from a queue
1272 * @param[in/out] ctx Driver specific flow control administration
1273 * @param[in] prec Precedence of queue to dequeue from
1274 * @param[out] ac_credit_spent Boolean, returns 0 or 1
1275 * @param[out] needs_hdr Boolean, returns 0 or 1
1276 * @param[out] entry_out The remote MAC for which the packet is destined
1277 * @param[in] only_no_credit If TRUE, searches all entries instead of just the active ones
1278 *
1279 * Return value: the dequeued packet
1280 */
1281 static void*
_dhd_wlfc_deque_delayedq(athost_wl_status_info_t * ctx,int prec,uint8 * ac_credit_spent,uint8 * needs_hdr,wlfc_mac_descriptor_t ** entry_out,bool only_no_credit)1282 _dhd_wlfc_deque_delayedq(athost_wl_status_info_t* ctx, int prec,
1283 uint8* ac_credit_spent, uint8* needs_hdr, wlfc_mac_descriptor_t** entry_out,
1284 bool only_no_credit)
1285 {
1286 wlfc_mac_descriptor_t* entry;
1287 int total_entries;
1288 void* p = NULL;
1289 int i;
1290 uint8 credit_spent = ((prec == AC_COUNT) && !ctx->bcmc_credit_supported) ? 0 : 1;
1291
1292 *entry_out = NULL;
1293 /* most cases a packet will count against FIFO credit */
1294 *ac_credit_spent = credit_spent;
1295
1296 /* search all entries, include nodes as well as interfaces */
1297 if (only_no_credit) {
1298 total_entries = ctx->requested_entry_count;
1299 } else {
1300 total_entries = ctx->active_entry_count;
1301 }
1302
1303 for (i = 0; i < total_entries; i++) {
1304 if (only_no_credit) {
1305 entry = ctx->requested_entry[i];
1306 } else {
1307 entry = ctx->active_entry_head;
1308 /* move head to ensure fair round-robin */
1309 ctx->active_entry_head = ctx->active_entry_head->next;
1310 }
1311 ASSERT(entry);
1312
1313 if (entry->occupied && _dhd_wlfc_is_destination_open(ctx, entry, prec) &&
1314 (entry->transit_count < WL_TXSTATUS_FREERUNCTR_MASK) &&
1315 (!entry->suppressed)) {
1316 *ac_credit_spent = credit_spent;
1317 if (entry->state == WLFC_STATE_CLOSE) {
1318 *ac_credit_spent = 0;
1319 }
1320
1321 /* higher precedence will be picked up first,
1322 * i.e. suppressed packets before delayed ones
1323 */
1324 p = pktq_pdeq(&entry->psq, PSQ_SUP_IDX(prec));
1325 *needs_hdr = 0;
1326 if (p == NULL) {
1327 /* De-Q from delay Q */
1328 p = pktq_pdeq(&entry->psq, PSQ_DLY_IDX(prec));
1329 *needs_hdr = 1;
1330 }
1331
1332 if (p != NULL) {
1333 bcm_pkt_validate_chk(p);
1334 /* did the packet come from suppress sub-queue? */
1335 if (entry->requested_credit > 0) {
1336 entry->requested_credit--;
1337 #ifdef PROP_TXSTATUS_DEBUG
1338 entry->dstncredit_sent_packets++;
1339 #endif
1340 } else if (entry->requested_packet > 0) {
1341 entry->requested_packet--;
1342 DHD_PKTTAG_SETONETIMEPKTRQST(PKTTAG(p));
1343 }
1344
1345 *entry_out = entry;
1346 ctx->pkt_cnt_in_q[DHD_PKTTAG_IF(PKTTAG(p))][prec]--;
1347 ctx->pkt_cnt_per_ac[prec]--;
1348 ctx->pkt_cnt_in_psq--;
1349 _dhd_wlfc_flow_control_check(ctx, &entry->psq,
1350 DHD_PKTTAG_IF(PKTTAG(p)));
1351 /*
1352 * A packet has been picked up, update traffic availability bitmap,
1353 * if applicable.
1354 */
1355 _dhd_wlfc_traffic_pending_check(ctx, entry, prec);
1356 return p;
1357 }
1358 }
1359 }
1360 return NULL;
1361 } /* _dhd_wlfc_deque_delayedq */
1362
1363 /** Enqueues caller supplied packet on either a 'suppressed' or 'delayed' queue */
1364 static int
_dhd_wlfc_enque_delayq(athost_wl_status_info_t * ctx,void * pktbuf,int prec)1365 _dhd_wlfc_enque_delayq(athost_wl_status_info_t* ctx, void* pktbuf, int prec)
1366 {
1367 wlfc_mac_descriptor_t* entry;
1368
1369 if (pktbuf != NULL) {
1370 entry = _dhd_wlfc_find_table_entry(ctx, pktbuf);
1371 if (entry == NULL) {
1372 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
1373 return BCME_ERROR;
1374 }
1375
1376 /*
1377 - suppressed packets go to sub_queue[2*prec + 1] AND
1378 - delayed packets go to sub_queue[2*prec + 0] to ensure
1379 order of delivery.
1380 */
1381 if (_dhd_wlfc_prec_enq_with_drop(ctx->dhdp, &entry->psq, pktbuf, (prec << 1),
1382 FALSE, WLFC_SEQCOUNT(entry, prec))
1383 == FALSE) {
1384 WLFC_DBGMESG(("D"));
1385 ctx->stats.delayq_full_error++;
1386 return BCME_ERROR;
1387 }
1388
1389
1390 /* A packet has been pushed, update traffic availability bitmap, if applicable */
1391 _dhd_wlfc_traffic_pending_check(ctx, entry, prec);
1392 }
1393
1394 return BCME_OK;
1395 } /* _dhd_wlfc_enque_delayq */
1396
1397 /** Returns TRUE if caller supplied packet is destined for caller supplied interface */
_dhd_wlfc_ifpkt_fn(void * p,void * p_ifid)1398 static bool _dhd_wlfc_ifpkt_fn(void* p, void *p_ifid)
1399 {
1400 if (!p || !p_ifid)
1401 return FALSE;
1402
1403 return (DHD_PKTTAG_WLFCPKT(PKTTAG(p))&& (*((uint8 *)p_ifid) == DHD_PKTTAG_IF(PKTTAG(p))));
1404 }
1405
1406 /** Returns TRUE if caller supplied packet is destined for caller supplied remote MAC */
_dhd_wlfc_entrypkt_fn(void * p,void * entry)1407 static bool _dhd_wlfc_entrypkt_fn(void* p, void *entry)
1408 {
1409 if (!p || !entry)
1410 return FALSE;
1411
1412 return (DHD_PKTTAG_WLFCPKT(PKTTAG(p))&& (entry == DHD_PKTTAG_ENTRY(PKTTAG(p))));
1413 }
1414
1415 static void
_dhd_wlfc_return_implied_credit(athost_wl_status_info_t * wlfc,void * pkt)1416 _dhd_wlfc_return_implied_credit(athost_wl_status_info_t* wlfc, void* pkt)
1417 {
1418 dhd_pub_t *dhdp;
1419 bool credit_return = FALSE;
1420
1421 if (!wlfc || !pkt) {
1422 return;
1423 }
1424
1425 dhdp = (dhd_pub_t *)(wlfc->dhdp);
1426 if (dhdp && (dhdp->proptxstatus_mode == WLFC_FCMODE_IMPLIED_CREDIT) &&
1427 DHD_PKTTAG_CREDITCHECK(PKTTAG(pkt))) {
1428 int lender, credit_returned = 0;
1429 uint8 fifo_id = DHD_PKTTAG_FIFO(PKTTAG(pkt));
1430
1431 credit_return = TRUE;
1432
1433 /* Note that borrower is fifo_id */
1434 /* Return credits to highest priority lender first */
1435 for (lender = AC_COUNT; lender >= 0; lender--) {
1436 if (wlfc->credits_borrowed[fifo_id][lender] > 0) {
1437 wlfc->FIFO_credit[lender]++;
1438 wlfc->credits_borrowed[fifo_id][lender]--;
1439 credit_returned = 1;
1440 break;
1441 }
1442 }
1443
1444 if (!credit_returned) {
1445 wlfc->FIFO_credit[fifo_id]++;
1446 }
1447 }
1448
1449 BCM_REFERENCE(credit_return);
1450 #if defined(DHD_WLFC_THREAD)
1451 if (credit_return) {
1452 _dhd_wlfc_thread_wakeup(dhdp);
1453 }
1454 #endif /* defined(DHD_WLFC_THREAD) */
1455 }
1456
1457 /** Removes and frees a packet from the hanger. Called during eg tx complete. */
1458 static void
_dhd_wlfc_hanger_free_pkt(athost_wl_status_info_t * wlfc,uint32 slot_id,uint8 pkt_state,int pkt_txstatus)1459 _dhd_wlfc_hanger_free_pkt(athost_wl_status_info_t* wlfc, uint32 slot_id, uint8 pkt_state,
1460 int pkt_txstatus)
1461 {
1462 wlfc_hanger_t* hanger;
1463 wlfc_hanger_item_t* item;
1464
1465 if (!wlfc)
1466 return;
1467
1468 hanger = (wlfc_hanger_t*)wlfc->hanger;
1469 if (!hanger)
1470 return;
1471
1472 if (slot_id == WLFC_HANGER_MAXITEMS)
1473 return;
1474
1475 item = &hanger->items[slot_id];
1476
1477 if (item->pkt) {
1478 item->pkt_state |= pkt_state;
1479 if (pkt_txstatus != -1)
1480 item->pkt_txstatus = (uint8)pkt_txstatus;
1481 bcm_object_feature_set(item->pkt, BCM_OBJECT_FEATURE_PKT_STATE, item->pkt_state);
1482 if (item->pkt_state == WLFC_HANGER_PKT_STATE_COMPLETE) {
1483 void *p = NULL;
1484 void *pkt = item->pkt;
1485 uint8 old_state = item->state;
1486 int ret = _dhd_wlfc_hanger_poppkt(wlfc->hanger, slot_id, &p, TRUE);
1487 BCM_REFERENCE(ret);
1488 BCM_REFERENCE(pkt);
1489 ASSERT((ret == BCME_OK) && p && (pkt == p));
1490 if (old_state == WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED) {
1491 printf("ERROR: free a suppressed pkt %p state %d pkt_state %d\n",
1492 pkt, old_state, item->pkt_state);
1493 }
1494 ASSERT(old_state != WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED);
1495
1496 /* free packet */
1497 wlfc->pkt_cnt_in_drv[DHD_PKTTAG_IF(PKTTAG(p))]
1498 [DHD_PKTTAG_FIFO(PKTTAG(p))]--;
1499 wlfc->stats.pktout++;
1500 dhd_txcomplete((dhd_pub_t *)wlfc->dhdp, p, item->pkt_txstatus);
1501 PKTFREE(wlfc->osh, p, TRUE);
1502 }
1503 } else {
1504 /* free slot */
1505 if (item->state == WLFC_HANGER_ITEM_STATE_FREE)
1506 DHD_ERROR(("Error: %s():%d Multiple TXSTATUS or BUSRETURNED: %d (%d)\n",
1507 __FUNCTION__, __LINE__, item->pkt_state, pkt_state));
1508 item->state = WLFC_HANGER_ITEM_STATE_FREE;
1509 }
1510 } /* _dhd_wlfc_hanger_free_pkt */
1511
1512 /** Called during eg detach() */
1513 static void
_dhd_wlfc_pktq_flush(athost_wl_status_info_t * ctx,struct pktq * pq,bool dir,f_processpkt_t fn,void * arg,q_type_t q_type)1514 _dhd_wlfc_pktq_flush(athost_wl_status_info_t* ctx, struct pktq *pq,
1515 bool dir, f_processpkt_t fn, void *arg, q_type_t q_type)
1516 {
1517 int prec;
1518 dhd_pub_t *dhdp = (dhd_pub_t *)ctx->dhdp;
1519
1520 ASSERT(dhdp);
1521
1522 /* Optimize flush, if pktq len = 0, just return.
1523 * pktq len of 0 means pktq's prec q's are all empty.
1524 */
1525 if (pq->len == 0) {
1526 return;
1527 }
1528
1529 for (prec = 0; prec < pq->num_prec; prec++) {
1530 struct pktq_prec *q;
1531 void *p, *prev = NULL;
1532
1533 q = &pq->q[prec];
1534 p = q->head;
1535 while (p) {
1536 bcm_pkt_validate_chk(p);
1537 if (fn == NULL || (*fn)(p, arg)) {
1538 bool head = (p == q->head);
1539 if (head)
1540 q->head = PKTLINK(p);
1541 else
1542 PKTSETLINK(prev, PKTLINK(p));
1543 if (q_type == Q_TYPE_PSQ) {
1544 if (!WLFC_GET_AFQ(dhdp->wlfc_mode) && (prec & 1)) {
1545 _dhd_wlfc_hanger_remove_reference(ctx->hanger, p);
1546 }
1547 ctx->pkt_cnt_in_q[DHD_PKTTAG_IF(PKTTAG(p))][prec>>1]--;
1548 ctx->pkt_cnt_per_ac[prec>>1]--;
1549 ctx->pkt_cnt_in_psq--;
1550 ctx->stats.cleanup_psq_cnt++;
1551 if (!(prec & 1)) {
1552 /* pkt in delayed q, so fake push BDC header for
1553 * dhd_tcpack_check_xmit() and dhd_txcomplete().
1554 */
1555 _dhd_wlfc_pushheader(ctx, &p, FALSE, 0, 0,
1556 0, 0, TRUE);
1557 #ifdef DHDTCPACK_SUPPRESS
1558 if (dhd_tcpack_check_xmit(dhdp, p) == BCME_ERROR) {
1559 DHD_ERROR(("%s %d: tcpack_suppress ERROR!!!"
1560 " Stop using it\n",
1561 __FUNCTION__, __LINE__));
1562 dhd_tcpack_suppress_set(dhdp,
1563 TCPACK_SUP_OFF);
1564 }
1565 #endif /* DHDTCPACK_SUPPRESS */
1566 }
1567 } else if (q_type == Q_TYPE_AFQ) {
1568 wlfc_mac_descriptor_t* entry =
1569 _dhd_wlfc_find_table_entry(ctx, p);
1570 if (entry->transit_count)
1571 entry->transit_count--;
1572 if (entry->suppr_transit_count) {
1573 entry->suppr_transit_count--;
1574 if (entry->suppressed &&
1575 (!entry->onbus_pkts_count) &&
1576 (!entry->suppr_transit_count))
1577 entry->suppressed = FALSE;
1578 }
1579 _dhd_wlfc_return_implied_credit(ctx, p);
1580 ctx->stats.cleanup_fw_cnt++;
1581 }
1582 PKTSETLINK(p, NULL);
1583 if (dir) {
1584 ctx->pkt_cnt_in_drv[DHD_PKTTAG_IF(PKTTAG(p))][prec>>1]--;
1585 ctx->stats.pktout++;
1586 dhd_txcomplete(dhdp, p, FALSE);
1587 }
1588 PKTFREE(ctx->osh, p, dir);
1589
1590 q->len--;
1591 pq->len--;
1592 p = (head ? q->head : PKTLINK(prev));
1593 } else {
1594 prev = p;
1595 p = PKTLINK(p);
1596 }
1597 }
1598
1599 if (q->head == NULL) {
1600 ASSERT(q->len == 0);
1601 q->tail = NULL;
1602 }
1603 }
1604
1605 if (fn == NULL)
1606 ASSERT(pq->len == 0);
1607 } /* _dhd_wlfc_pktq_flush */
1608
1609 #ifndef BCMDBUS
1610 /** !BCMDBUS specific function. Dequeues a packet from the caller supplied queue. */
1611 static void*
_dhd_wlfc_pktq_pdeq_with_fn(struct pktq * pq,int prec,f_processpkt_t fn,void * arg)1612 _dhd_wlfc_pktq_pdeq_with_fn(struct pktq *pq, int prec, f_processpkt_t fn, void *arg)
1613 {
1614 struct pktq_prec *q;
1615 void *p, *prev = NULL;
1616
1617 ASSERT(prec >= 0 && prec < pq->num_prec);
1618
1619 q = &pq->q[prec];
1620 p = q->head;
1621
1622 while (p) {
1623 if (fn == NULL || (*fn)(p, arg)) {
1624 break;
1625 } else {
1626 prev = p;
1627 p = PKTLINK(p);
1628 }
1629 }
1630 if (p == NULL)
1631 return NULL;
1632
1633 bcm_pkt_validate_chk(p);
1634
1635 if (prev == NULL) {
1636 if ((q->head = PKTLINK(p)) == NULL) {
1637 q->tail = NULL;
1638 }
1639 } else {
1640 PKTSETLINK(prev, PKTLINK(p));
1641 if (q->tail == p) {
1642 q->tail = prev;
1643 }
1644 }
1645
1646 q->len--;
1647
1648 pq->len--;
1649
1650 PKTSETLINK(p, NULL);
1651
1652 return p;
1653 }
1654
1655 /** !BCMDBUS specific function */
1656 static void
_dhd_wlfc_cleanup_txq(dhd_pub_t * dhd,f_processpkt_t fn,void * arg)1657 _dhd_wlfc_cleanup_txq(dhd_pub_t *dhd, f_processpkt_t fn, void *arg)
1658 {
1659 int prec;
1660 void *pkt = NULL, *head = NULL, *tail = NULL;
1661 struct pktq *txq = (struct pktq *)dhd_bus_txq(dhd->bus);
1662 athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
1663 wlfc_hanger_t* h = (wlfc_hanger_t*)wlfc->hanger;
1664 wlfc_mac_descriptor_t* entry;
1665
1666 dhd_os_sdlock_txq(dhd);
1667 for (prec = 0; prec < txq->num_prec; prec++) {
1668 while ((pkt = _dhd_wlfc_pktq_pdeq_with_fn(txq, prec, fn, arg))) {
1669 #ifdef DHDTCPACK_SUPPRESS
1670 if (dhd_tcpack_check_xmit(dhd, pkt) == BCME_ERROR) {
1671 DHD_ERROR(("%s %d: tcpack_suppress ERROR!!! Stop using it\n",
1672 __FUNCTION__, __LINE__));
1673 dhd_tcpack_suppress_set(dhd, TCPACK_SUP_OFF);
1674 }
1675 #endif /* DHDTCPACK_SUPPRESS */
1676 if (!head) {
1677 head = pkt;
1678 }
1679 if (tail) {
1680 PKTSETLINK(tail, pkt);
1681 }
1682 tail = pkt;
1683 }
1684 }
1685 dhd_os_sdunlock_txq(dhd);
1686
1687
1688 while ((pkt = head)) {
1689 head = PKTLINK(pkt);
1690 PKTSETLINK(pkt, NULL);
1691 entry = _dhd_wlfc_find_table_entry(wlfc, pkt);
1692
1693 if (!WLFC_GET_AFQ(dhd->wlfc_mode) &&
1694 !_dhd_wlfc_hanger_remove_reference(h, pkt)) {
1695 DHD_ERROR(("%s: can't find pkt(%p) in hanger, free it anyway\n",
1696 __FUNCTION__, pkt));
1697 }
1698 if (entry->transit_count)
1699 entry->transit_count--;
1700 if (entry->suppr_transit_count) {
1701 entry->suppr_transit_count--;
1702 if (entry->suppressed &&
1703 (!entry->onbus_pkts_count) &&
1704 (!entry->suppr_transit_count))
1705 entry->suppressed = FALSE;
1706 }
1707 _dhd_wlfc_return_implied_credit(wlfc, pkt);
1708 wlfc->pkt_cnt_in_drv[DHD_PKTTAG_IF(PKTTAG(pkt))][DHD_PKTTAG_FIFO(PKTTAG(pkt))]--;
1709 wlfc->stats.pktout++;
1710 wlfc->stats.cleanup_txq_cnt++;
1711 dhd_txcomplete(dhd, pkt, FALSE);
1712 PKTFREE(wlfc->osh, pkt, TRUE);
1713 }
1714 } /* _dhd_wlfc_cleanup_txq */
1715 #endif /* !BCMDBUS */
1716
1717 /** called during eg detach */
1718 void
_dhd_wlfc_cleanup(dhd_pub_t * dhd,f_processpkt_t fn,void * arg)1719 _dhd_wlfc_cleanup(dhd_pub_t *dhd, f_processpkt_t fn, void *arg)
1720 {
1721 int i;
1722 int total_entries;
1723 athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
1724 wlfc_mac_descriptor_t* table;
1725 wlfc_hanger_t* h = (wlfc_hanger_t*)wlfc->hanger;
1726
1727 wlfc->stats.cleanup_txq_cnt = 0;
1728 wlfc->stats.cleanup_psq_cnt = 0;
1729 wlfc->stats.cleanup_fw_cnt = 0;
1730
1731 /*
1732 * flush sequence should be txq -> psq -> hanger/afq, hanger has to be last one
1733 */
1734 #ifndef BCMDBUS
1735 /* flush bus->txq */
1736 _dhd_wlfc_cleanup_txq(dhd, fn, arg);
1737 #endif /* !BCMDBUS */
1738
1739 /* flush psq, search all entries, include nodes as well as interfaces */
1740 total_entries = sizeof(wlfc->destination_entries)/sizeof(wlfc_mac_descriptor_t);
1741 table = (wlfc_mac_descriptor_t*)&wlfc->destination_entries;
1742
1743 for (i = 0; i < total_entries; i++) {
1744 if (table[i].occupied) {
1745 /* release packets held in PSQ (both delayed and suppressed) */
1746 if (table[i].psq.len) {
1747 WLFC_DBGMESG(("%s(): PSQ[%d].len = %d\n",
1748 __FUNCTION__, i, table[i].psq.len));
1749 _dhd_wlfc_pktq_flush(wlfc, &table[i].psq, TRUE,
1750 fn, arg, Q_TYPE_PSQ);
1751 }
1752
1753 /* free packets held in AFQ */
1754 if (WLFC_GET_AFQ(dhd->wlfc_mode) && (table[i].afq.len)) {
1755 _dhd_wlfc_pktq_flush(wlfc, &table[i].afq, TRUE,
1756 fn, arg, Q_TYPE_AFQ);
1757 }
1758
1759 if ((fn == NULL) && (&table[i] != &wlfc->destination_entries.other)) {
1760 table[i].occupied = 0;
1761 if (table[i].transit_count || table[i].suppr_transit_count) {
1762 DHD_ERROR(("%s: table[%d] transit(%d), suppr_tansit(%d)\n",
1763 __FUNCTION__, i,
1764 table[i].transit_count,
1765 table[i].suppr_transit_count));
1766 }
1767 }
1768 }
1769 }
1770
1771 /*
1772 . flush remained pkt in hanger queue, not in bus->txq nor psq.
1773 . the remained pkt was successfully downloaded to dongle already.
1774 . hanger slot state cannot be set to free until receive txstatus update.
1775 */
1776 if (!WLFC_GET_AFQ(dhd->wlfc_mode)) {
1777 for (i = 0; i < h->max_items; i++) {
1778 if ((h->items[i].state == WLFC_HANGER_ITEM_STATE_INUSE) ||
1779 (h->items[i].state == WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED)) {
1780 if (fn == NULL || (*fn)(h->items[i].pkt, arg)) {
1781 h->items[i].state = WLFC_HANGER_ITEM_STATE_FLUSHED;
1782 }
1783 }
1784 }
1785 }
1786
1787 return;
1788 } /* _dhd_wlfc_cleanup */
1789
1790 /** Called after eg the dongle signalled a new remote MAC that it connected with to the DHD */
1791 static int
_dhd_wlfc_mac_entry_update(athost_wl_status_info_t * ctx,wlfc_mac_descriptor_t * entry,uint8 action,uint8 ifid,uint8 iftype,uint8 * ea,f_processpkt_t fn,void * arg)1792 _dhd_wlfc_mac_entry_update(athost_wl_status_info_t* ctx, wlfc_mac_descriptor_t* entry,
1793 uint8 action, uint8 ifid, uint8 iftype, uint8* ea,
1794 f_processpkt_t fn, void *arg)
1795 {
1796 int rc = BCME_OK;
1797
1798
1799 if ((action == eWLFC_MAC_ENTRY_ACTION_ADD) || (action == eWLFC_MAC_ENTRY_ACTION_UPDATE)) {
1800 entry->occupied = 1;
1801 entry->state = WLFC_STATE_OPEN;
1802 entry->requested_credit = 0;
1803 entry->interface_id = ifid;
1804 entry->iftype = iftype;
1805 entry->ac_bitmap = 0xff; /* update this when handling APSD */
1806
1807 /* for an interface entry we may not care about the MAC address */
1808 if (ea != NULL)
1809 memcpy(&entry->ea[0], ea, ETHER_ADDR_LEN);
1810
1811 if (action == eWLFC_MAC_ENTRY_ACTION_ADD) {
1812 entry->suppressed = FALSE;
1813 entry->transit_count = 0;
1814 entry->suppr_transit_count = 0;
1815 entry->onbus_pkts_count = 0;
1816 }
1817
1818 if (action == eWLFC_MAC_ENTRY_ACTION_ADD) {
1819 dhd_pub_t *dhdp = (dhd_pub_t *)(ctx->dhdp);
1820
1821 pktq_init(&entry->psq, WLFC_PSQ_PREC_COUNT, WLFC_PSQ_LEN);
1822 _dhd_wlfc_flow_control_check(ctx, &entry->psq, ifid);
1823
1824 if (WLFC_GET_AFQ(dhdp->wlfc_mode)) {
1825 pktq_init(&entry->afq, WLFC_AFQ_PREC_COUNT, WLFC_PSQ_LEN);
1826 }
1827
1828 if (entry->next == NULL) {
1829 /* not linked to anywhere, add to tail */
1830 if (ctx->active_entry_head) {
1831 entry->prev = ctx->active_entry_head->prev;
1832 ctx->active_entry_head->prev->next = entry;
1833 ctx->active_entry_head->prev = entry;
1834 entry->next = ctx->active_entry_head;
1835 } else {
1836 ASSERT(ctx->active_entry_count == 0);
1837 entry->prev = entry->next = entry;
1838 ctx->active_entry_head = entry;
1839 }
1840 ctx->active_entry_count++;
1841 } else {
1842 DHD_ERROR(("%s():%d, entry(%d)\n", __FUNCTION__, __LINE__,
1843 (int)(entry - &ctx->destination_entries.nodes[0])));
1844 }
1845 }
1846 } else if (action == eWLFC_MAC_ENTRY_ACTION_DEL) {
1847 /* When the entry is deleted, the packets that are queued in the entry must be
1848 cleanup. The cleanup action should be before the occupied is set as 0.
1849 */
1850 _dhd_wlfc_cleanup(ctx->dhdp, fn, arg);
1851 _dhd_wlfc_flow_control_check(ctx, &entry->psq, ifid);
1852
1853 entry->occupied = 0;
1854 entry->state = WLFC_STATE_CLOSE;
1855 memset(&entry->ea[0], 0, ETHER_ADDR_LEN);
1856
1857 if (entry->next) {
1858 /* not floating, remove from Q */
1859 if (ctx->active_entry_count <= 1) {
1860 /* last item */
1861 ctx->active_entry_head = NULL;
1862 ctx->active_entry_count = 0;
1863 } else {
1864 entry->prev->next = entry->next;
1865 entry->next->prev = entry->prev;
1866 if (entry == ctx->active_entry_head) {
1867 ctx->active_entry_head = entry->next;
1868 }
1869 ctx->active_entry_count--;
1870 }
1871 entry->next = entry->prev = NULL;
1872 } else {
1873 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
1874 }
1875 }
1876 return rc;
1877 } /* _dhd_wlfc_mac_entry_update */
1878
1879
1880 #ifdef LIMIT_BORROW
1881
1882 /** LIMIT_BORROW specific function */
1883 static int
_dhd_wlfc_borrow_credit(athost_wl_status_info_t * ctx,int highest_lender_ac,int borrower_ac,bool bBorrowAll)1884 _dhd_wlfc_borrow_credit(athost_wl_status_info_t* ctx, int highest_lender_ac, int borrower_ac,
1885 bool bBorrowAll)
1886 {
1887 int lender_ac, borrow_limit = 0;
1888 int rc = -1;
1889
1890 if (ctx == NULL) {
1891 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
1892 return -1;
1893 }
1894
1895 /* Borrow from lowest priority available AC (including BC/MC credits) */
1896 for (lender_ac = 0; lender_ac <= highest_lender_ac; lender_ac++) {
1897 if (!bBorrowAll) {
1898 borrow_limit = ctx->Init_FIFO_credit[lender_ac]/WLFC_BORROW_LIMIT_RATIO;
1899 } else {
1900 borrow_limit = 0;
1901 }
1902
1903 if (ctx->FIFO_credit[lender_ac] > borrow_limit) {
1904 ctx->credits_borrowed[borrower_ac][lender_ac]++;
1905 ctx->FIFO_credit[lender_ac]--;
1906 rc = lender_ac;
1907 break;
1908 }
1909 }
1910
1911 return rc;
1912 }
1913
1914 /** LIMIT_BORROW specific function */
_dhd_wlfc_return_credit(athost_wl_status_info_t * ctx,int lender_ac,int borrower_ac)1915 static int _dhd_wlfc_return_credit(athost_wl_status_info_t* ctx, int lender_ac, int borrower_ac)
1916 {
1917 if ((ctx == NULL) || (lender_ac < 0) || (lender_ac > AC_COUNT) ||
1918 (borrower_ac < 0) || (borrower_ac > AC_COUNT)) {
1919 DHD_ERROR(("Error: %s():%d, ctx(%p), lender_ac(%d), borrower_ac(%d)\n",
1920 __FUNCTION__, __LINE__, ctx, lender_ac, borrower_ac));
1921
1922 return BCME_BADARG;
1923 }
1924
1925 ctx->credits_borrowed[borrower_ac][lender_ac]--;
1926 ctx->FIFO_credit[lender_ac]++;
1927
1928 return BCME_OK;
1929 }
1930
1931 #endif /* LIMIT_BORROW */
1932
1933 /**
1934 * Called on an interface event (WLC_E_IF) indicated by firmware.
1935 * @param action : eg eWLFC_MAC_ENTRY_ACTION_UPDATE or eWLFC_MAC_ENTRY_ACTION_ADD
1936 */
1937 static int
_dhd_wlfc_interface_entry_update(void * state,uint8 action,uint8 ifid,uint8 iftype,uint8 * ea)1938 _dhd_wlfc_interface_entry_update(void* state,
1939 uint8 action, uint8 ifid, uint8 iftype, uint8* ea)
1940 {
1941 athost_wl_status_info_t* ctx = (athost_wl_status_info_t*)state;
1942 wlfc_mac_descriptor_t* entry;
1943
1944 if (ifid >= WLFC_MAX_IFNUM)
1945 return BCME_BADARG;
1946
1947 entry = &ctx->destination_entries.interfaces[ifid];
1948
1949 return _dhd_wlfc_mac_entry_update(ctx, entry, action, ifid, iftype, ea,
1950 _dhd_wlfc_ifpkt_fn, &ifid);
1951 }
1952
1953 /**
1954 * Called eg on receiving a WLC_E_BCMC_CREDIT_SUPPORT event from the dongle (broadcast/multicast
1955 * specific)
1956 */
1957 static int
_dhd_wlfc_BCMCCredit_support_update(void * state)1958 _dhd_wlfc_BCMCCredit_support_update(void* state)
1959 {
1960 athost_wl_status_info_t* ctx = (athost_wl_status_info_t*)state;
1961
1962 ctx->bcmc_credit_supported = TRUE;
1963 return BCME_OK;
1964 }
1965
1966 /** Called eg on receiving a WLC_E_FIFO_CREDIT_MAP event from the dongle */
1967 static int
_dhd_wlfc_FIFOcreditmap_update(void * state,uint8 * credits)1968 _dhd_wlfc_FIFOcreditmap_update(void* state, uint8* credits)
1969 {
1970 athost_wl_status_info_t* ctx = (athost_wl_status_info_t*)state;
1971 int i;
1972
1973 for (i = 0; i <= 4; i++) {
1974 if (ctx->Init_FIFO_credit[i] != ctx->FIFO_credit[i]) {
1975 DHD_ERROR(("%s: credit[i] is not returned, (%d %d)\n",
1976 __FUNCTION__, ctx->Init_FIFO_credit[i], ctx->FIFO_credit[i]));
1977 }
1978 }
1979
1980 /* update the AC FIFO credit map */
1981 ctx->FIFO_credit[0] += (credits[0] - ctx->Init_FIFO_credit[0]);
1982 ctx->FIFO_credit[1] += (credits[1] - ctx->Init_FIFO_credit[1]);
1983 ctx->FIFO_credit[2] += (credits[2] - ctx->Init_FIFO_credit[2]);
1984 ctx->FIFO_credit[3] += (credits[3] - ctx->Init_FIFO_credit[3]);
1985 ctx->FIFO_credit[4] += (credits[4] - ctx->Init_FIFO_credit[4]);
1986
1987 ctx->Init_FIFO_credit[0] = credits[0];
1988 ctx->Init_FIFO_credit[1] = credits[1];
1989 ctx->Init_FIFO_credit[2] = credits[2];
1990 ctx->Init_FIFO_credit[3] = credits[3];
1991 ctx->Init_FIFO_credit[4] = credits[4];
1992
1993 /* credit for ATIM FIFO is not used yet. */
1994 ctx->Init_FIFO_credit[5] = ctx->FIFO_credit[5] = 0;
1995
1996 return BCME_OK;
1997 }
1998
1999 /**
2000 * Called during committing of a transmit packet from the OS DHD layer to the next layer towards
2001 * the dongle (eg the DBUS layer). All transmit packets flow via this function to the next layer.
2002 *
2003 * @param[in/out] ctx Driver specific flow control administration
2004 * @param[in] ac Access Category (QoS) of called supplied packet
2005 * @param[in] commit_info Contains eg the packet to send
2006 * @param[in] fcommit Function pointer to transmit function of next software layer
2007 * @param[in] commit_ctx Opaque context used when calling next layer
2008 */
2009 static int
_dhd_wlfc_handle_packet_commit(athost_wl_status_info_t * ctx,int ac,dhd_wlfc_commit_info_t * commit_info,f_commitpkt_t fcommit,void * commit_ctx)2010 _dhd_wlfc_handle_packet_commit(athost_wl_status_info_t* ctx, int ac,
2011 dhd_wlfc_commit_info_t *commit_info, f_commitpkt_t fcommit, void* commit_ctx)
2012 {
2013 uint32 hslot;
2014 int rc;
2015 dhd_pub_t *dhdp = (dhd_pub_t *)(ctx->dhdp);
2016
2017 /*
2018 if ac_fifo_credit_spent = 0
2019
2020 This packet will not count against the FIFO credit.
2021 To ensure the txstatus corresponding to this packet
2022 does not provide an implied credit (default behavior)
2023 mark the packet accordingly.
2024
2025 if ac_fifo_credit_spent = 1
2026
2027 This is a normal packet and it counts against the FIFO
2028 credit count.
2029 */
2030 DHD_PKTTAG_SETCREDITCHECK(PKTTAG(commit_info->p), commit_info->ac_fifo_credit_spent);
2031 rc = _dhd_wlfc_pretx_pktprocess(ctx, commit_info->mac_entry, &commit_info->p,
2032 commit_info->needs_hdr, &hslot);
2033
2034 if (rc == BCME_OK) {
2035 rc = fcommit(commit_ctx, commit_info->p);
2036 if (rc == BCME_OK) {
2037 uint8 gen = WL_TXSTATUS_GET_GENERATION(
2038 DHD_PKTTAG_H2DTAG(PKTTAG(commit_info->p)));
2039 ctx->stats.pkt2bus++;
2040 if (commit_info->ac_fifo_credit_spent || (ac == AC_COUNT)) {
2041 ctx->stats.send_pkts[ac]++;
2042 WLFC_HOST_FIFO_CREDIT_INC_SENTCTRS(ctx, ac);
2043 }
2044
2045 if (gen != commit_info->mac_entry->generation) {
2046 /* will be suppressed back by design */
2047 if (!commit_info->mac_entry->suppressed) {
2048 commit_info->mac_entry->suppressed = TRUE;
2049 }
2050 commit_info->mac_entry->suppr_transit_count++;
2051 }
2052 commit_info->mac_entry->transit_count++;
2053 commit_info->mac_entry->onbus_pkts_count++;
2054 } else if (commit_info->needs_hdr) {
2055 if (!WLFC_GET_AFQ(dhdp->wlfc_mode)) {
2056 void *pout = NULL;
2057 /* pop hanger for delayed packet */
2058 _dhd_wlfc_hanger_poppkt(ctx->hanger, WL_TXSTATUS_GET_HSLOT(
2059 DHD_PKTTAG_H2DTAG(PKTTAG(commit_info->p))), &pout, TRUE);
2060 ASSERT(commit_info->p == pout);
2061 }
2062 }
2063 } else {
2064 ctx->stats.generic_error++;
2065 }
2066
2067 if (rc != BCME_OK) {
2068 /*
2069 pretx pkt process or bus commit has failed, rollback.
2070 - remove wl-header for a delayed packet
2071 - save wl-header header for suppressed packets
2072 - reset credit check flag
2073 */
2074 _dhd_wlfc_rollback_packet_toq(ctx, commit_info->p, commit_info->pkt_type, hslot);
2075 DHD_PKTTAG_SETCREDITCHECK(PKTTAG(commit_info->p), 0);
2076 }
2077
2078 return rc;
2079 } /* _dhd_wlfc_handle_packet_commit */
2080
2081 /** Returns remote MAC descriptor for caller supplied MAC address */
2082 static uint8
_dhd_wlfc_find_mac_desc_id_from_mac(dhd_pub_t * dhdp,uint8 * ea)2083 _dhd_wlfc_find_mac_desc_id_from_mac(dhd_pub_t *dhdp, uint8 *ea)
2084 {
2085 wlfc_mac_descriptor_t* table =
2086 ((athost_wl_status_info_t*)dhdp->wlfc_state)->destination_entries.nodes;
2087 uint8 table_index;
2088
2089 if (ea != NULL) {
2090 for (table_index = 0; table_index < WLFC_MAC_DESC_TABLE_SIZE; table_index++) {
2091 if ((memcmp(ea, &table[table_index].ea[0], ETHER_ADDR_LEN) == 0) &&
2092 table[table_index].occupied)
2093 return table_index;
2094 }
2095 }
2096 return WLFC_MAC_DESC_ID_INVALID;
2097 }
2098
2099 /**
2100 * Called when the host receives a WLFC_CTL_TYPE_TXSTATUS event from the dongle, indicating the
2101 * status of a frame that the dongle attempted to transmit over the wireless medium.
2102 */
2103 static int
dhd_wlfc_suppressed_acked_update(dhd_pub_t * dhd,uint16 hslot,uint8 prec,uint8 hcnt)2104 dhd_wlfc_suppressed_acked_update(dhd_pub_t *dhd, uint16 hslot, uint8 prec, uint8 hcnt)
2105 {
2106 athost_wl_status_info_t* ctx;
2107 wlfc_mac_descriptor_t* entry = NULL;
2108 struct pktq *pq;
2109 struct pktq_prec *q;
2110 void *p, *b;
2111
2112 if (!dhd) {
2113 DHD_ERROR(("%s: dhd(%p)\n", __FUNCTION__, dhd));
2114 return BCME_BADARG;
2115 }
2116 ctx = (athost_wl_status_info_t*)dhd->wlfc_state;
2117 if (!ctx) {
2118 DHD_ERROR(("%s: ctx(%p)\n", __FUNCTION__, ctx));
2119 return BCME_ERROR;
2120 }
2121
2122 ASSERT(hslot < (WLFC_MAC_DESC_TABLE_SIZE + WLFC_MAX_IFNUM + 1));
2123
2124 if (hslot < WLFC_MAC_DESC_TABLE_SIZE)
2125 entry = &ctx->destination_entries.nodes[hslot];
2126 else if (hslot < (WLFC_MAC_DESC_TABLE_SIZE + WLFC_MAX_IFNUM))
2127 entry = &ctx->destination_entries.interfaces[hslot - WLFC_MAC_DESC_TABLE_SIZE];
2128 else
2129 entry = &ctx->destination_entries.other;
2130
2131 pq = &entry->psq;
2132
2133 ASSERT(((prec << 1) + 1) < pq->num_prec);
2134
2135 q = &pq->q[((prec << 1) + 1)];
2136
2137 b = NULL;
2138 p = q->head;
2139
2140 while (p && (hcnt != WL_TXSTATUS_GET_FREERUNCTR(DHD_PKTTAG_H2DTAG(PKTTAG(p))))) {
2141 b = p;
2142 p = PKTLINK(p);
2143 }
2144
2145 if (p == NULL) {
2146 /* none is matched */
2147 if (b) {
2148 DHD_ERROR(("%s: can't find matching seq(%d)\n", __FUNCTION__, hcnt));
2149 } else {
2150 DHD_ERROR(("%s: queue is empty\n", __FUNCTION__));
2151 }
2152
2153 return BCME_ERROR;
2154 }
2155
2156 if (!b) {
2157 /* head packet is matched */
2158 if ((q->head = PKTLINK(p)) == NULL) {
2159 q->tail = NULL;
2160 }
2161 } else {
2162 /* middle packet is matched */
2163 PKTSETLINK(b, PKTLINK(p));
2164 if (PKTLINK(p) == NULL) {
2165 q->tail = b;
2166 }
2167 }
2168
2169 q->len--;
2170 pq->len--;
2171 ctx->pkt_cnt_in_q[DHD_PKTTAG_IF(PKTTAG(p))][prec]--;
2172 ctx->pkt_cnt_per_ac[prec]--;
2173
2174 PKTSETLINK(p, NULL);
2175
2176 if (WLFC_GET_AFQ(dhd->wlfc_mode)) {
2177 _dhd_wlfc_enque_afq(ctx, p);
2178 } else {
2179 _dhd_wlfc_hanger_pushpkt(ctx->hanger, p, hslot);
2180 }
2181
2182 entry->transit_count++;
2183
2184 return BCME_OK;
2185 }
2186
2187 static int
_dhd_wlfc_compressed_txstatus_update(dhd_pub_t * dhd,uint8 * pkt_info,uint8 len,void ** p_mac)2188 _dhd_wlfc_compressed_txstatus_update(dhd_pub_t *dhd, uint8* pkt_info, uint8 len, void** p_mac)
2189 {
2190 uint8 status_flag_ori, status_flag;
2191 uint32 status;
2192 int ret = BCME_OK;
2193 int remove_from_hanger_ori, remove_from_hanger = 1;
2194 void* pktbuf = NULL;
2195 uint8 fifo_id = 0, gen = 0, count = 0, hcnt;
2196 uint16 hslot;
2197 wlfc_mac_descriptor_t* entry = NULL;
2198 athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
2199 uint16 seq = 0, seq_fromfw = 0, seq_num = 0;
2200
2201 memcpy(&status, pkt_info, sizeof(uint32));
2202 status = ltoh32(status);
2203 status_flag = WL_TXSTATUS_GET_FLAGS(status);
2204 hcnt = WL_TXSTATUS_GET_FREERUNCTR(status);
2205 hslot = WL_TXSTATUS_GET_HSLOT(status);
2206 fifo_id = WL_TXSTATUS_GET_FIFO(status);
2207 gen = WL_TXSTATUS_GET_GENERATION(status);
2208
2209 if (WLFC_GET_REUSESEQ(dhd->wlfc_mode)) {
2210 memcpy(&seq, pkt_info + WLFC_CTL_VALUE_LEN_TXSTATUS, WLFC_CTL_VALUE_LEN_SEQ);
2211 seq = ltoh16(seq);
2212 seq_fromfw = GET_WL_HAS_ASSIGNED_SEQ(seq);
2213 seq_num = WL_SEQ_GET_NUM(seq);
2214 }
2215
2216 wlfc->stats.txstatus_in += len;
2217
2218 if (status_flag == WLFC_CTL_PKTFLAG_DISCARD) {
2219 wlfc->stats.pkt_freed += len;
2220 } else if (status_flag == WLFC_CTL_PKTFLAG_DISCARD_NOACK) {
2221 wlfc->stats.pkt_freed += len;
2222 } else if (status_flag == WLFC_CTL_PKTFLAG_D11SUPPRESS) {
2223 wlfc->stats.d11_suppress += len;
2224 remove_from_hanger = 0;
2225 } else if (status_flag == WLFC_CTL_PKTFLAG_WLSUPPRESS) {
2226 wlfc->stats.wl_suppress += len;
2227 remove_from_hanger = 0;
2228 } else if (status_flag == WLFC_CTL_PKTFLAG_TOSSED_BYWLC) {
2229 wlfc->stats.wlc_tossed_pkts += len;
2230 }
2231
2232 else if (status_flag == WLFC_CTL_PKTFLAG_SUPPRESS_ACKED) {
2233 wlfc->stats.pkt_freed += len;
2234 }
2235
2236 if (dhd->proptxstatus_txstatus_ignore) {
2237 if (!remove_from_hanger) {
2238 DHD_ERROR(("suppress txstatus: %d\n", status_flag));
2239 }
2240 return BCME_OK;
2241 }
2242
2243 status_flag_ori = status_flag;
2244 remove_from_hanger_ori = remove_from_hanger;
2245
2246 while (count < len) {
2247 if (status_flag == WLFC_CTL_PKTFLAG_SUPPRESS_ACKED) {
2248 dhd_wlfc_suppressed_acked_update(dhd, hslot, fifo_id, hcnt);
2249 }
2250 if (WLFC_GET_AFQ(dhd->wlfc_mode)) {
2251 ret = _dhd_wlfc_deque_afq(wlfc, hslot, hcnt, fifo_id, &pktbuf);
2252 } else {
2253 status_flag = status_flag_ori;
2254 remove_from_hanger = remove_from_hanger_ori;
2255 ret = _dhd_wlfc_hanger_poppkt(wlfc->hanger, hslot, &pktbuf, FALSE);
2256 if (!pktbuf) {
2257 _dhd_wlfc_hanger_free_pkt(wlfc, hslot,
2258 WLFC_HANGER_PKT_STATE_TXSTATUS, -1);
2259 goto cont;
2260 } else {
2261 wlfc_hanger_t* h = (wlfc_hanger_t*)wlfc->hanger;
2262 if (h->items[hslot].state == WLFC_HANGER_ITEM_STATE_FLUSHED) {
2263 status_flag = WLFC_CTL_PKTFLAG_DISCARD;
2264 remove_from_hanger = 1;
2265 }
2266 }
2267 }
2268
2269 if ((ret != BCME_OK) || !pktbuf) {
2270 goto cont;
2271 }
2272
2273 bcm_pkt_validate_chk(pktbuf);
2274
2275 /* set fifo_id to correct value because not all FW does that */
2276 fifo_id = DHD_PKTTAG_FIFO(PKTTAG(pktbuf));
2277
2278 entry = _dhd_wlfc_find_table_entry(wlfc, pktbuf);
2279
2280 if (!remove_from_hanger) {
2281 /* this packet was suppressed */
2282 if (!entry->suppressed || (entry->generation != gen)) {
2283 if (!entry->suppressed) {
2284 entry->suppr_transit_count = entry->transit_count;
2285 if (p_mac) {
2286 *p_mac = entry;
2287 }
2288 } else {
2289 DHD_ERROR(("gen(%d), entry->generation(%d)\n",
2290 gen, entry->generation));
2291 }
2292 entry->suppressed = TRUE;
2293 }
2294 entry->generation = gen;
2295 }
2296
2297 #ifdef PROP_TXSTATUS_DEBUG
2298 if (!WLFC_GET_AFQ(dhd->wlfc_mode))
2299 {
2300 uint32 new_t = OSL_SYSUPTIME();
2301 uint32 old_t;
2302 uint32 delta;
2303 old_t = ((wlfc_hanger_t*)(wlfc->hanger))->items[hslot].push_time;
2304
2305
2306 wlfc->stats.latency_sample_count++;
2307 if (new_t > old_t)
2308 delta = new_t - old_t;
2309 else
2310 delta = 0xffffffff + new_t - old_t;
2311 wlfc->stats.total_status_latency += delta;
2312 wlfc->stats.latency_most_recent = delta;
2313
2314 wlfc->stats.deltas[wlfc->stats.idx_delta++] = delta;
2315 if (wlfc->stats.idx_delta == sizeof(wlfc->stats.deltas)/sizeof(uint32))
2316 wlfc->stats.idx_delta = 0;
2317 }
2318 #endif /* PROP_TXSTATUS_DEBUG */
2319
2320 /* pick up the implicit credit from this packet */
2321 if (DHD_PKTTAG_CREDITCHECK(PKTTAG(pktbuf))) {
2322 _dhd_wlfc_return_implied_credit(wlfc, pktbuf);
2323 } else {
2324 /*
2325 if this packet did not count against FIFO credit, it must have
2326 taken a requested_credit from the destination entry (for pspoll etc.)
2327 */
2328 if (!DHD_PKTTAG_ONETIMEPKTRQST(PKTTAG(pktbuf))) {
2329 entry->requested_credit++;
2330 #if defined(DHD_WLFC_THREAD)
2331 _dhd_wlfc_thread_wakeup(dhd);
2332 #endif /* DHD_WLFC_THREAD */
2333 }
2334 #ifdef PROP_TXSTATUS_DEBUG
2335 entry->dstncredit_acks++;
2336 #endif
2337 }
2338
2339 if ((status_flag == WLFC_CTL_PKTFLAG_D11SUPPRESS) ||
2340 (status_flag == WLFC_CTL_PKTFLAG_WLSUPPRESS)) {
2341 /* save generation bit inside packet */
2342 WL_TXSTATUS_SET_GENERATION(DHD_PKTTAG_H2DTAG(PKTTAG(pktbuf)), gen);
2343
2344 if (WLFC_GET_REUSESEQ(dhd->wlfc_mode)) {
2345 WL_SEQ_SET_REUSE(DHD_PKTTAG_H2DSEQ(PKTTAG(pktbuf)), seq_fromfw);
2346 WL_SEQ_SET_NUM(DHD_PKTTAG_H2DSEQ(PKTTAG(pktbuf)), seq_num);
2347 }
2348
2349 ret = _dhd_wlfc_enque_suppressed(wlfc, fifo_id, pktbuf);
2350 if (ret != BCME_OK) {
2351 /* delay q is full, drop this packet */
2352 DHD_WLFC_QMON_COMPLETE(entry);
2353 _dhd_wlfc_prec_drop(dhd, (fifo_id << 1) + 1, pktbuf, FALSE);
2354 } else {
2355 if (!WLFC_GET_AFQ(dhd->wlfc_mode)) {
2356 /* Mark suppressed to avoid a double free
2357 during wlfc cleanup
2358 */
2359 _dhd_wlfc_hanger_mark_suppressed(wlfc->hanger, hslot, gen);
2360 }
2361 }
2362 } else {
2363 DHD_WLFC_QMON_COMPLETE(entry);
2364
2365 if (!WLFC_GET_AFQ(dhd->wlfc_mode)) {
2366 _dhd_wlfc_hanger_free_pkt(wlfc, hslot,
2367 WLFC_HANGER_PKT_STATE_TXSTATUS, TRUE);
2368 } else {
2369 dhd_txcomplete(dhd, pktbuf, TRUE);
2370 wlfc->pkt_cnt_in_drv[DHD_PKTTAG_IF(PKTTAG(pktbuf))]
2371 [DHD_PKTTAG_FIFO(PKTTAG(pktbuf))]--;
2372 wlfc->stats.pktout++;
2373 /* free the packet */
2374 PKTFREE(wlfc->osh, pktbuf, TRUE);
2375 }
2376 }
2377 /* pkt back from firmware side */
2378 if (entry->transit_count)
2379 entry->transit_count--;
2380 if (entry->suppr_transit_count) {
2381 entry->suppr_transit_count--;
2382 if (entry->suppressed &&
2383 (!entry->onbus_pkts_count) &&
2384 (!entry->suppr_transit_count))
2385 entry->suppressed = FALSE;
2386 }
2387
2388 cont:
2389 hcnt = (hcnt + 1) & WL_TXSTATUS_FREERUNCTR_MASK;
2390 if (!WLFC_GET_AFQ(dhd->wlfc_mode)) {
2391 hslot = (hslot + 1) & WL_TXSTATUS_HSLOT_MASK;
2392 }
2393
2394 if (WLFC_GET_REUSESEQ(dhd->wlfc_mode) && seq_fromfw) {
2395 seq_num = (seq_num + 1) & WL_SEQ_NUM_MASK;
2396 }
2397
2398 count++;
2399 }
2400
2401 return BCME_OK;
2402 } /* _dhd_wlfc_compressed_txstatus_update */
2403
2404 /**
2405 * Called when eg host receives a 'WLFC_CTL_TYPE_FIFO_CREDITBACK' event from the dongle.
2406 * @param[in] credits caller supplied credit that will be added to the host credit.
2407 */
2408 static int
_dhd_wlfc_fifocreditback_indicate(dhd_pub_t * dhd,uint8 * credits)2409 _dhd_wlfc_fifocreditback_indicate(dhd_pub_t *dhd, uint8* credits)
2410 {
2411 int i;
2412 athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
2413 for (i = 0; i < WLFC_CTL_VALUE_LEN_FIFO_CREDITBACK; i++) {
2414 #ifdef PROP_TXSTATUS_DEBUG
2415 wlfc->stats.fifo_credits_back[i] += credits[i];
2416 #endif
2417
2418 /* update FIFO credits */
2419 if (dhd->proptxstatus_mode == WLFC_FCMODE_EXPLICIT_CREDIT)
2420 {
2421 int lender; /* Note that borrower is i */
2422
2423 /* Return credits to highest priority lender first */
2424 for (lender = AC_COUNT; (lender >= 0) && (credits[i] > 0); lender--) {
2425 if (wlfc->credits_borrowed[i][lender] > 0) {
2426 if (credits[i] >= wlfc->credits_borrowed[i][lender]) {
2427 credits[i] -=
2428 (uint8)wlfc->credits_borrowed[i][lender];
2429 wlfc->FIFO_credit[lender] +=
2430 wlfc->credits_borrowed[i][lender];
2431 wlfc->credits_borrowed[i][lender] = 0;
2432 } else {
2433 wlfc->credits_borrowed[i][lender] -= credits[i];
2434 wlfc->FIFO_credit[lender] += credits[i];
2435 credits[i] = 0;
2436 }
2437 }
2438 }
2439
2440 /* If we have more credits left over, these must belong to the AC */
2441 if (credits[i] > 0) {
2442 wlfc->FIFO_credit[i] += credits[i];
2443 }
2444
2445 if (wlfc->FIFO_credit[i] > wlfc->Init_FIFO_credit[i]) {
2446 wlfc->FIFO_credit[i] = wlfc->Init_FIFO_credit[i];
2447 }
2448 }
2449 }
2450
2451 #if defined(DHD_WLFC_THREAD)
2452 _dhd_wlfc_thread_wakeup(dhd);
2453 #endif /* defined(DHD_WLFC_THREAD) */
2454
2455 return BCME_OK;
2456 } /* _dhd_wlfc_fifocreditback_indicate */
2457
2458 #ifndef BCMDBUS
2459 /** !BCMDBUS specific function */
2460 static void
_dhd_wlfc_suppress_txq(dhd_pub_t * dhd,f_processpkt_t fn,void * arg)2461 _dhd_wlfc_suppress_txq(dhd_pub_t *dhd, f_processpkt_t fn, void *arg)
2462 {
2463 athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
2464 wlfc_mac_descriptor_t* entry;
2465 int prec;
2466 void *pkt = NULL, *head = NULL, *tail = NULL;
2467 struct pktq *txq = (struct pktq *)dhd_bus_txq(dhd->bus);
2468 uint8 results[WLFC_CTL_VALUE_LEN_TXSTATUS+WLFC_CTL_VALUE_LEN_SEQ];
2469 uint8 credits[WLFC_CTL_VALUE_LEN_FIFO_CREDITBACK] = {0};
2470 uint32 htod = 0;
2471 uint16 htodseq = 0;
2472 bool bCreditUpdate = FALSE;
2473
2474 dhd_os_sdlock_txq(dhd);
2475 for (prec = 0; prec < txq->num_prec; prec++) {
2476 while ((pkt = _dhd_wlfc_pktq_pdeq_with_fn(txq, prec, fn, arg))) {
2477 if (!head) {
2478 head = pkt;
2479 }
2480 if (tail) {
2481 PKTSETLINK(tail, pkt);
2482 }
2483 tail = pkt;
2484 }
2485 }
2486 dhd_os_sdunlock_txq(dhd);
2487
2488 while ((pkt = head)) {
2489 head = PKTLINK(pkt);
2490 PKTSETLINK(pkt, NULL);
2491
2492 entry = _dhd_wlfc_find_table_entry(wlfc, pkt);
2493 if (!entry) {
2494 PKTFREE(dhd->osh, pkt, TRUE);
2495 continue;
2496 }
2497 if (entry->onbus_pkts_count > 0) {
2498 entry->onbus_pkts_count--;
2499 }
2500 if (entry->suppressed &&
2501 (!entry->onbus_pkts_count) &&
2502 (!entry->suppr_transit_count)) {
2503 entry->suppressed = FALSE;
2504 }
2505 /* fake a suppression txstatus */
2506 htod = DHD_PKTTAG_H2DTAG(PKTTAG(pkt));
2507 WL_TXSTATUS_SET_FLAGS(htod, WLFC_CTL_PKTFLAG_WLSUPPRESS);
2508 WL_TXSTATUS_SET_GENERATION(htod, entry->generation);
2509 htod = htol32(htod);
2510 memcpy(results, &htod, WLFC_CTL_VALUE_LEN_TXSTATUS);
2511 if (WLFC_GET_REUSESEQ(dhd->wlfc_mode)) {
2512 htodseq = DHD_PKTTAG_H2DSEQ(PKTTAG(pkt));
2513 if (IS_WL_TO_REUSE_SEQ(htodseq)) {
2514 SET_WL_HAS_ASSIGNED_SEQ(htodseq);
2515 RESET_WL_TO_REUSE_SEQ(htodseq);
2516 }
2517 htodseq = htol16(htodseq);
2518 memcpy(results + WLFC_CTL_VALUE_LEN_TXSTATUS, &htodseq,
2519 WLFC_CTL_VALUE_LEN_SEQ);
2520 }
2521 if (WLFC_GET_AFQ(dhd->wlfc_mode)) {
2522 _dhd_wlfc_enque_afq(wlfc, pkt);
2523 }
2524 _dhd_wlfc_compressed_txstatus_update(dhd, results, 1, NULL);
2525
2526 /* fake a fifo credit back */
2527 if (DHD_PKTTAG_CREDITCHECK(PKTTAG(pkt))) {
2528 credits[DHD_PKTTAG_FIFO(PKTTAG(pkt))]++;
2529 bCreditUpdate = TRUE;
2530 }
2531 }
2532
2533 if (bCreditUpdate) {
2534 _dhd_wlfc_fifocreditback_indicate(dhd, credits);
2535 }
2536 } /* _dhd_wlfc_suppress_txq */
2537 #endif /* !BCMDBUS */
2538
2539 static int
_dhd_wlfc_dbg_senum_check(dhd_pub_t * dhd,uint8 * value)2540 _dhd_wlfc_dbg_senum_check(dhd_pub_t *dhd, uint8 *value)
2541 {
2542 uint32 timestamp;
2543
2544 (void)dhd;
2545
2546 bcopy(&value[2], ×tamp, sizeof(uint32));
2547 timestamp = ltoh32(timestamp);
2548 DHD_INFO(("RXPKT: SEQ: %d, timestamp %d\n", value[1], timestamp));
2549 return BCME_OK;
2550 }
2551
2552 static int
_dhd_wlfc_rssi_indicate(dhd_pub_t * dhd,uint8 * rssi)2553 _dhd_wlfc_rssi_indicate(dhd_pub_t *dhd, uint8* rssi)
2554 {
2555 (void)dhd;
2556 (void)rssi;
2557 return BCME_OK;
2558 }
2559
2560 static void
_dhd_wlfc_add_requested_entry(athost_wl_status_info_t * wlfc,wlfc_mac_descriptor_t * entry)2561 _dhd_wlfc_add_requested_entry(athost_wl_status_info_t* wlfc, wlfc_mac_descriptor_t* entry)
2562 {
2563 int i;
2564
2565 if (!wlfc || !entry) {
2566 return;
2567 }
2568
2569 for (i = 0; i < wlfc->requested_entry_count; i++) {
2570 if (entry == wlfc->requested_entry[i]) {
2571 break;
2572 }
2573 }
2574
2575 if (i == wlfc->requested_entry_count) {
2576 /* no match entry found */
2577 ASSERT(wlfc->requested_entry_count <= (WLFC_MAC_DESC_TABLE_SIZE-1));
2578 wlfc->requested_entry[wlfc->requested_entry_count++] = entry;
2579 }
2580 }
2581
2582 /** called on eg receiving 'mac open' event from the dongle. */
2583 static void
_dhd_wlfc_remove_requested_entry(athost_wl_status_info_t * wlfc,wlfc_mac_descriptor_t * entry)2584 _dhd_wlfc_remove_requested_entry(athost_wl_status_info_t* wlfc, wlfc_mac_descriptor_t* entry)
2585 {
2586 int i;
2587
2588 if (!wlfc || !entry) {
2589 return;
2590 }
2591
2592 for (i = 0; i < wlfc->requested_entry_count; i++) {
2593 if (entry == wlfc->requested_entry[i]) {
2594 break;
2595 }
2596 }
2597
2598 if (i < wlfc->requested_entry_count) {
2599 /* found */
2600 ASSERT(wlfc->requested_entry_count > 0);
2601 wlfc->requested_entry_count--;
2602 if (i != wlfc->requested_entry_count) {
2603 wlfc->requested_entry[i] =
2604 wlfc->requested_entry[wlfc->requested_entry_count];
2605 }
2606 wlfc->requested_entry[wlfc->requested_entry_count] = NULL;
2607 }
2608 }
2609
2610 /** called on eg receiving a WLFC_CTL_TYPE_MACDESC_ADD TLV from the dongle */
2611 static int
_dhd_wlfc_mac_table_update(dhd_pub_t * dhd,uint8 * value,uint8 type)2612 _dhd_wlfc_mac_table_update(dhd_pub_t *dhd, uint8* value, uint8 type)
2613 {
2614 int rc;
2615 athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
2616 wlfc_mac_descriptor_t* table;
2617 uint8 existing_index;
2618 uint8 table_index;
2619 uint8 ifid;
2620 uint8* ea;
2621
2622 WLFC_DBGMESG(("%s(), mac [%02x:%02x:%02x:%02x:%02x:%02x],%s,idx:%d,id:0x%02x\n",
2623 __FUNCTION__, value[2], value[3], value[4], value[5], value[6], value[7],
2624 ((type == WLFC_CTL_TYPE_MACDESC_ADD) ? "ADD":"DEL"),
2625 WLFC_MAC_DESC_GET_LOOKUP_INDEX(value[0]), value[0]));
2626
2627 table = wlfc->destination_entries.nodes;
2628 table_index = WLFC_MAC_DESC_GET_LOOKUP_INDEX(value[0]);
2629 ifid = value[1];
2630 ea = &value[2];
2631
2632 _dhd_wlfc_remove_requested_entry(wlfc, &table[table_index]);
2633 if (type == WLFC_CTL_TYPE_MACDESC_ADD) {
2634 existing_index = _dhd_wlfc_find_mac_desc_id_from_mac(dhd, &value[2]);
2635 if ((existing_index != WLFC_MAC_DESC_ID_INVALID) &&
2636 (existing_index != table_index) && table[existing_index].occupied) {
2637 /*
2638 there is an existing different entry, free the old one
2639 and move it to new index if necessary.
2640 */
2641 rc = _dhd_wlfc_mac_entry_update(wlfc, &table[existing_index],
2642 eWLFC_MAC_ENTRY_ACTION_DEL, table[existing_index].interface_id,
2643 table[existing_index].iftype, NULL, _dhd_wlfc_entrypkt_fn,
2644 &table[existing_index]);
2645 }
2646
2647 if (!table[table_index].occupied) {
2648 /* this new MAC entry does not exist, create one */
2649 table[table_index].mac_handle = value[0];
2650 rc = _dhd_wlfc_mac_entry_update(wlfc, &table[table_index],
2651 eWLFC_MAC_ENTRY_ACTION_ADD, ifid,
2652 wlfc->destination_entries.interfaces[ifid].iftype,
2653 ea, NULL, NULL);
2654 } else {
2655 /* the space should have been empty, but it's not */
2656 wlfc->stats.mac_update_failed++;
2657 }
2658 }
2659
2660 if (type == WLFC_CTL_TYPE_MACDESC_DEL) {
2661 if (table[table_index].occupied) {
2662 rc = _dhd_wlfc_mac_entry_update(wlfc, &table[table_index],
2663 eWLFC_MAC_ENTRY_ACTION_DEL, ifid,
2664 wlfc->destination_entries.interfaces[ifid].iftype,
2665 ea, _dhd_wlfc_entrypkt_fn, &table[table_index]);
2666 } else {
2667 /* the space should have been occupied, but it's not */
2668 wlfc->stats.mac_update_failed++;
2669 }
2670 }
2671 BCM_REFERENCE(rc);
2672 return BCME_OK;
2673 } /* _dhd_wlfc_mac_table_update */
2674
2675 /** Called on a 'mac open' or 'mac close' event indicated by the dongle */
2676 static int
_dhd_wlfc_psmode_update(dhd_pub_t * dhd,uint8 * value,uint8 type)2677 _dhd_wlfc_psmode_update(dhd_pub_t *dhd, uint8* value, uint8 type)
2678 {
2679 /* Handle PS on/off indication */
2680 athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
2681 wlfc_mac_descriptor_t* table;
2682 wlfc_mac_descriptor_t* desc; /* a table maps from mac handle to mac descriptor */
2683 uint8 mac_handle = value[0];
2684 int i;
2685
2686 table = wlfc->destination_entries.nodes;
2687 desc = &table[WLFC_MAC_DESC_GET_LOOKUP_INDEX(mac_handle)];
2688 if (desc->occupied) {
2689 if (type == WLFC_CTL_TYPE_MAC_OPEN) {
2690 desc->state = WLFC_STATE_OPEN;
2691 desc->ac_bitmap = 0xff;
2692 DHD_WLFC_CTRINC_MAC_OPEN(desc);
2693 desc->requested_credit = 0;
2694 desc->requested_packet = 0;
2695 _dhd_wlfc_remove_requested_entry(wlfc, desc);
2696 } else {
2697 desc->state = WLFC_STATE_CLOSE;
2698 DHD_WLFC_CTRINC_MAC_CLOSE(desc);
2699 /* Indicate to firmware if there is any traffic pending. */
2700 for (i = 0; i < AC_COUNT; i++) {
2701 _dhd_wlfc_traffic_pending_check(wlfc, desc, i);
2702 }
2703 }
2704 } else {
2705 wlfc->stats.psmode_update_failed++;
2706 }
2707
2708 return BCME_OK;
2709 } /* _dhd_wlfc_psmode_update */
2710
2711 /** called upon receiving 'interface open' or 'interface close' event from the dongle */
2712 static int
_dhd_wlfc_interface_update(dhd_pub_t * dhd,uint8 * value,uint8 type)2713 _dhd_wlfc_interface_update(dhd_pub_t *dhd, uint8* value, uint8 type)
2714 {
2715 /* Handle PS on/off indication */
2716 athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
2717 wlfc_mac_descriptor_t* table;
2718 uint8 if_id = value[0];
2719
2720 if (if_id < WLFC_MAX_IFNUM) {
2721 table = wlfc->destination_entries.interfaces;
2722 if (table[if_id].occupied) {
2723 if (type == WLFC_CTL_TYPE_INTERFACE_OPEN) {
2724 table[if_id].state = WLFC_STATE_OPEN;
2725 } else {
2726 table[if_id].state = WLFC_STATE_CLOSE;
2727 }
2728 return BCME_OK;
2729 }
2730 }
2731 wlfc->stats.interface_update_failed++;
2732
2733 return BCME_OK;
2734 }
2735
2736 /** Called on receiving a WLFC_CTL_TYPE_MAC_REQUEST_CREDIT TLV from the dongle */
2737 static int
_dhd_wlfc_credit_request(dhd_pub_t * dhd,uint8 * value)2738 _dhd_wlfc_credit_request(dhd_pub_t *dhd, uint8* value)
2739 {
2740 athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
2741 wlfc_mac_descriptor_t* table;
2742 wlfc_mac_descriptor_t* desc;
2743 uint8 mac_handle;
2744 uint8 credit;
2745
2746 table = wlfc->destination_entries.nodes;
2747 mac_handle = value[1];
2748 credit = value[0];
2749
2750 desc = &table[WLFC_MAC_DESC_GET_LOOKUP_INDEX(mac_handle)];
2751 if (desc->occupied) {
2752 desc->requested_credit = credit;
2753
2754 desc->ac_bitmap = value[2] & (~(1<<AC_COUNT));
2755 _dhd_wlfc_add_requested_entry(wlfc, desc);
2756 #if defined(DHD_WLFC_THREAD)
2757 if (credit) {
2758 _dhd_wlfc_thread_wakeup(dhd);
2759 }
2760 #endif /* DHD_WLFC_THREAD */
2761 } else {
2762 wlfc->stats.credit_request_failed++;
2763 }
2764
2765 return BCME_OK;
2766 }
2767
2768 /** Called on receiving a WLFC_CTL_TYPE_MAC_REQUEST_PACKET TLV from the dongle */
2769 static int
_dhd_wlfc_packet_request(dhd_pub_t * dhd,uint8 * value)2770 _dhd_wlfc_packet_request(dhd_pub_t *dhd, uint8* value)
2771 {
2772 athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
2773 wlfc_mac_descriptor_t* table;
2774 wlfc_mac_descriptor_t* desc;
2775 uint8 mac_handle;
2776 uint8 packet_count;
2777
2778 table = wlfc->destination_entries.nodes;
2779 mac_handle = value[1];
2780 packet_count = value[0];
2781
2782 desc = &table[WLFC_MAC_DESC_GET_LOOKUP_INDEX(mac_handle)];
2783 if (desc->occupied) {
2784 desc->requested_packet = packet_count;
2785
2786 desc->ac_bitmap = value[2] & (~(1<<AC_COUNT));
2787 _dhd_wlfc_add_requested_entry(wlfc, desc);
2788 #if defined(DHD_WLFC_THREAD)
2789 if (packet_count) {
2790 _dhd_wlfc_thread_wakeup(dhd);
2791 }
2792 #endif /* DHD_WLFC_THREAD */
2793 } else {
2794 wlfc->stats.packet_request_failed++;
2795 }
2796
2797 return BCME_OK;
2798 }
2799
2800 /** Called when host receives a WLFC_CTL_TYPE_HOST_REORDER_RXPKTS TLV from the dongle */
2801 static void
_dhd_wlfc_reorderinfo_indicate(uint8 * val,uint8 len,uchar * info_buf,uint * info_len)2802 _dhd_wlfc_reorderinfo_indicate(uint8 *val, uint8 len, uchar *info_buf, uint *info_len)
2803 {
2804 if (info_len) {
2805 /* Check copy length to avoid buffer overrun. In case of length exceeding
2806 * WLHOST_REORDERDATA_TOTLEN, return failure instead sending incomplete result
2807 * of length WLHOST_REORDERDATA_TOTLEN
2808 */
2809 if ((info_buf) && (len <= WLHOST_REORDERDATA_TOTLEN)) {
2810 bcopy(val, info_buf, len);
2811 *info_len = len;
2812 } else {
2813 *info_len = 0;
2814 }
2815 }
2816 }
2817
2818 /*
2819 * public functions
2820 */
2821
dhd_wlfc_is_supported(dhd_pub_t * dhd)2822 bool dhd_wlfc_is_supported(dhd_pub_t *dhd)
2823 {
2824 bool rc = TRUE;
2825
2826 if (dhd == NULL) {
2827 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
2828 return FALSE;
2829 }
2830
2831 dhd_os_wlfc_block(dhd);
2832
2833 if (!dhd->wlfc_state || (dhd->proptxstatus_mode == WLFC_FCMODE_NONE)) {
2834 rc = FALSE;
2835 }
2836
2837 dhd_os_wlfc_unblock(dhd);
2838
2839 return rc;
2840 }
2841
dhd_wlfc_enable(dhd_pub_t * dhd)2842 int dhd_wlfc_enable(dhd_pub_t *dhd)
2843 {
2844 int i, rc = BCME_OK;
2845 athost_wl_status_info_t* wlfc;
2846
2847 if (dhd == NULL) {
2848 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
2849 return BCME_BADARG;
2850 }
2851
2852 dhd_os_wlfc_block(dhd);
2853
2854 if (!dhd->wlfc_enabled || dhd->wlfc_state) {
2855 rc = BCME_OK;
2856 goto exit;
2857 }
2858
2859 /* allocate space to track txstatus propagated from firmware */
2860 dhd->wlfc_state = DHD_OS_PREALLOC(dhd, DHD_PREALLOC_DHD_WLFC_INFO,
2861 sizeof(athost_wl_status_info_t));
2862 if (dhd->wlfc_state == NULL) {
2863 rc = BCME_NOMEM;
2864 goto exit;
2865 }
2866
2867 /* initialize state space */
2868 wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
2869 memset(wlfc, 0, sizeof(athost_wl_status_info_t));
2870
2871 /* remember osh & dhdp */
2872 wlfc->osh = dhd->osh;
2873 wlfc->dhdp = dhd;
2874
2875 if (!WLFC_GET_AFQ(dhd->wlfc_mode)) {
2876 wlfc->hanger = _dhd_wlfc_hanger_create(dhd, WLFC_HANGER_MAXITEMS);
2877 if (wlfc->hanger == NULL) {
2878 DHD_OS_PREFREE(dhd, dhd->wlfc_state,
2879 sizeof(athost_wl_status_info_t));
2880 dhd->wlfc_state = NULL;
2881 rc = BCME_NOMEM;
2882 goto exit;
2883 }
2884 }
2885
2886 dhd->proptxstatus_mode = WLFC_FCMODE_EXPLICIT_CREDIT;
2887 /* default to check rx pkt */
2888 dhd->wlfc_rxpkt_chk = TRUE;
2889 if (dhd->op_mode & DHD_FLAG_IBSS_MODE) {
2890 dhd->wlfc_rxpkt_chk = FALSE;
2891 }
2892
2893 /* initialize all interfaces to accept traffic */
2894 for (i = 0; i < WLFC_MAX_IFNUM; i++) {
2895 wlfc->hostif_flow_state[i] = OFF;
2896 }
2897
2898 _dhd_wlfc_mac_entry_update(wlfc, &wlfc->destination_entries.other,
2899 eWLFC_MAC_ENTRY_ACTION_ADD, 0xff, 0, NULL, NULL, NULL);
2900
2901 wlfc->allow_credit_borrow = 0;
2902 wlfc->single_ac = 0;
2903 wlfc->single_ac_timestamp = 0;
2904
2905
2906 exit:
2907 DHD_ERROR(("%s: ret=%d\n", __FUNCTION__, rc));
2908 dhd_os_wlfc_unblock(dhd);
2909
2910 return rc;
2911 } /* dhd_wlfc_enable */
2912
2913 #ifdef SUPPORT_P2P_GO_PS
2914
2915 /**
2916 * Called when the host platform enters a lower power mode, eg right before a system hibernate.
2917 * SUPPORT_P2P_GO_PS specific function.
2918 */
2919 int
dhd_wlfc_suspend(dhd_pub_t * dhd)2920 dhd_wlfc_suspend(dhd_pub_t *dhd)
2921 {
2922 uint32 tlv = 0;
2923
2924 DHD_TRACE(("%s: masking wlfc events\n", __FUNCTION__));
2925 if (!dhd->wlfc_enabled)
2926 return -1;
2927
2928 if (!dhd_wl_ioctl_get_intiovar(dhd, "tlv", &tlv, WLC_GET_VAR, FALSE, 0))
2929 return -1;
2930 if ((tlv & (WLFC_FLAGS_RSSI_SIGNALS | WLFC_FLAGS_XONXOFF_SIGNALS)) == 0)
2931 return 0;
2932 tlv &= ~(WLFC_FLAGS_RSSI_SIGNALS | WLFC_FLAGS_XONXOFF_SIGNALS);
2933 if (!dhd_wl_ioctl_set_intiovar(dhd, "tlv", tlv, WLC_SET_VAR, TRUE, 0))
2934 return -1;
2935
2936 return 0;
2937 }
2938
2939 /**
2940 * Called when the host platform resumes from a power management operation, eg resume after a
2941 * system hibernate. SUPPORT_P2P_GO_PS specific function.
2942 */
2943 int
dhd_wlfc_resume(dhd_pub_t * dhd)2944 dhd_wlfc_resume(dhd_pub_t *dhd)
2945 {
2946 uint32 tlv = 0;
2947
2948 DHD_TRACE(("%s: unmasking wlfc events\n", __FUNCTION__));
2949 if (!dhd->wlfc_enabled)
2950 return -1;
2951
2952 if (!dhd_wl_ioctl_get_intiovar(dhd, "tlv", &tlv, WLC_GET_VAR, FALSE, 0))
2953 return -1;
2954 if ((tlv & (WLFC_FLAGS_RSSI_SIGNALS | WLFC_FLAGS_XONXOFF_SIGNALS)) ==
2955 (WLFC_FLAGS_RSSI_SIGNALS | WLFC_FLAGS_XONXOFF_SIGNALS))
2956 return 0;
2957 tlv |= (WLFC_FLAGS_RSSI_SIGNALS | WLFC_FLAGS_XONXOFF_SIGNALS);
2958 if (!dhd_wl_ioctl_set_intiovar(dhd, "tlv", tlv, WLC_SET_VAR, TRUE, 0))
2959 return -1;
2960
2961 return 0;
2962 }
2963
2964 #endif /* SUPPORT_P2P_GO_PS */
2965
2966 /** A flow control header was received from firmware, containing one or more TLVs */
2967 int
dhd_wlfc_parse_header_info(dhd_pub_t * dhd,void * pktbuf,int tlv_hdr_len,uchar * reorder_info_buf,uint * reorder_info_len)2968 dhd_wlfc_parse_header_info(dhd_pub_t *dhd, void* pktbuf, int tlv_hdr_len, uchar *reorder_info_buf,
2969 uint *reorder_info_len)
2970 {
2971 uint8 type, len;
2972 uint8* value;
2973 uint8* tmpbuf;
2974 uint16 remainder = (uint16)tlv_hdr_len;
2975 uint16 processed = 0;
2976 athost_wl_status_info_t* wlfc = NULL;
2977 void* entry;
2978
2979 if ((dhd == NULL) || (pktbuf == NULL)) {
2980 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
2981 return BCME_BADARG;
2982 }
2983
2984 dhd_os_wlfc_block(dhd);
2985
2986 if (dhd->proptxstatus_mode != WLFC_ONLY_AMPDU_HOSTREORDER) {
2987 if (!dhd->wlfc_state || (dhd->proptxstatus_mode == WLFC_FCMODE_NONE)) {
2988 dhd_os_wlfc_unblock(dhd);
2989 return WLFC_UNSUPPORTED;
2990 }
2991 wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
2992 }
2993
2994 tmpbuf = (uint8*)PKTDATA(dhd->osh, pktbuf);
2995
2996 if (remainder) {
2997 while ((processed < (WLFC_MAX_PENDING_DATALEN * 2)) && (remainder > 0)) {
2998 type = tmpbuf[processed];
2999 if (type == WLFC_CTL_TYPE_FILLER) {
3000 remainder -= 1;
3001 processed += 1;
3002 continue;
3003 }
3004
3005 len = tmpbuf[processed + 1];
3006 value = &tmpbuf[processed + 2];
3007
3008 if (remainder < (2 + len))
3009 break;
3010
3011 remainder -= 2 + len;
3012 processed += 2 + len;
3013 entry = NULL;
3014
3015 DHD_INFO(("%s():%d type %d remainder %d processed %d\n",
3016 __FUNCTION__, __LINE__, type, remainder, processed));
3017
3018 if (type == WLFC_CTL_TYPE_HOST_REORDER_RXPKTS)
3019 _dhd_wlfc_reorderinfo_indicate(value, len, reorder_info_buf,
3020 reorder_info_len);
3021
3022 if (wlfc == NULL) {
3023 ASSERT(dhd->proptxstatus_mode == WLFC_ONLY_AMPDU_HOSTREORDER);
3024
3025 if (type != WLFC_CTL_TYPE_HOST_REORDER_RXPKTS &&
3026 type != WLFC_CTL_TYPE_TRANS_ID)
3027 DHD_INFO(("%s():%d dhd->wlfc_state is NULL yet!"
3028 " type %d remainder %d processed %d\n",
3029 __FUNCTION__, __LINE__, type, remainder, processed));
3030 continue;
3031 }
3032
3033 if (type == WLFC_CTL_TYPE_TXSTATUS) {
3034 _dhd_wlfc_compressed_txstatus_update(dhd, value, 1, &entry);
3035 } else if (type == WLFC_CTL_TYPE_COMP_TXSTATUS) {
3036 uint8 compcnt_offset = WLFC_CTL_VALUE_LEN_TXSTATUS;
3037
3038 if (WLFC_GET_REUSESEQ(dhd->wlfc_mode)) {
3039 compcnt_offset += WLFC_CTL_VALUE_LEN_SEQ;
3040 }
3041 _dhd_wlfc_compressed_txstatus_update(dhd, value,
3042 value[compcnt_offset], &entry);
3043 } else if (type == WLFC_CTL_TYPE_FIFO_CREDITBACK) {
3044 _dhd_wlfc_fifocreditback_indicate(dhd, value);
3045 } else if (type == WLFC_CTL_TYPE_RSSI) {
3046 _dhd_wlfc_rssi_indicate(dhd, value);
3047 } else if (type == WLFC_CTL_TYPE_MAC_REQUEST_CREDIT) {
3048 _dhd_wlfc_credit_request(dhd, value);
3049 } else if (type == WLFC_CTL_TYPE_MAC_REQUEST_PACKET) {
3050 _dhd_wlfc_packet_request(dhd, value);
3051 } else if ((type == WLFC_CTL_TYPE_MAC_OPEN) ||
3052 (type == WLFC_CTL_TYPE_MAC_CLOSE)) {
3053 _dhd_wlfc_psmode_update(dhd, value, type);
3054 } else if ((type == WLFC_CTL_TYPE_MACDESC_ADD) ||
3055 (type == WLFC_CTL_TYPE_MACDESC_DEL)) {
3056 _dhd_wlfc_mac_table_update(dhd, value, type);
3057 } else if (type == WLFC_CTL_TYPE_TRANS_ID) {
3058 _dhd_wlfc_dbg_senum_check(dhd, value);
3059 } else if ((type == WLFC_CTL_TYPE_INTERFACE_OPEN) ||
3060 (type == WLFC_CTL_TYPE_INTERFACE_CLOSE)) {
3061 _dhd_wlfc_interface_update(dhd, value, type);
3062 }
3063
3064 #ifndef BCMDBUS
3065 if (entry && WLFC_GET_REORDERSUPP(dhd->wlfc_mode)) {
3066 /* suppress all packets for this mac entry from bus->txq */
3067 _dhd_wlfc_suppress_txq(dhd, _dhd_wlfc_entrypkt_fn, entry);
3068 }
3069 #endif /* !BCMDBUS */
3070 } /* while */
3071
3072 if (remainder != 0 && wlfc) {
3073 /* trouble..., something is not right */
3074 wlfc->stats.tlv_parse_failed++;
3075 }
3076 } /* if */
3077
3078 if (wlfc)
3079 wlfc->stats.dhd_hdrpulls++;
3080
3081 dhd_os_wlfc_unblock(dhd);
3082 return BCME_OK;
3083 }
3084
3085 KERNEL_THREAD_RETURN_TYPE
dhd_wlfc_transfer_packets(void * data)3086 dhd_wlfc_transfer_packets(void *data)
3087 {
3088 dhd_pub_t *dhdp = (dhd_pub_t *)data;
3089 int ac, single_ac = 0, rc = BCME_OK;
3090 dhd_wlfc_commit_info_t commit_info;
3091 athost_wl_status_info_t* ctx;
3092 int bus_retry_count = 0;
3093 int pkt_send = 0;
3094 int pkt_send_per_ac = 0;
3095
3096 uint8 tx_map = 0; /* packets (send + in queue), Bitmask for 4 ACs + BC/MC */
3097 uint8 rx_map = 0; /* received packets, Bitmask for 4 ACs + BC/MC */
3098 uint8 packets_map = 0; /* packets in queue, Bitmask for 4 ACs + BC/MC */
3099 bool no_credit = FALSE;
3100
3101 int lender;
3102
3103 #if defined(DHD_WLFC_THREAD)
3104 /* wait till someone wakeup me up, will change it at running time */
3105 int wait_msec = msecs_to_jiffies(0xFFFFFFFF);
3106 #endif /* defined(DHD_WLFC_THREAD) */
3107
3108 #if defined(DHD_WLFC_THREAD)
3109 while (1) {
3110 bus_retry_count = 0;
3111 pkt_send = 0;
3112 tx_map = 0;
3113 rx_map = 0;
3114 packets_map = 0;
3115 wait_msec = wait_event_interruptible_timeout(dhdp->wlfc_wqhead,
3116 dhdp->wlfc_thread_go, wait_msec);
3117 if (kthread_should_stop()) {
3118 break;
3119 }
3120 dhdp->wlfc_thread_go = FALSE;
3121
3122 dhd_os_wlfc_block(dhdp);
3123 #endif /* defined(DHD_WLFC_THREAD) */
3124 ctx = (athost_wl_status_info_t*)dhdp->wlfc_state;
3125 #if defined(DHD_WLFC_THREAD)
3126 if (!ctx)
3127 goto exit;
3128 #endif /* defined(DHD_WLFC_THREAD) */
3129
3130 memset(&commit_info, 0, sizeof(commit_info));
3131
3132 /*
3133 Commit packets for regular AC traffic. Higher priority first.
3134 First, use up FIFO credits available to each AC. Based on distribution
3135 and credits left, borrow from other ACs as applicable
3136
3137 -NOTE:
3138 If the bus between the host and firmware is overwhelmed by the
3139 traffic from host, it is possible that higher priority traffic
3140 starves the lower priority queue. If that occurs often, we may
3141 have to employ weighted round-robin or ucode scheme to avoid
3142 low priority packet starvation.
3143 */
3144
3145 for (ac = AC_COUNT; ac >= 0; ac--) {
3146 if (dhdp->wlfc_rxpkt_chk) {
3147 /* check rx packet */
3148 uint32 curr_t = OSL_SYSUPTIME(), delta;
3149
3150 delta = curr_t - ctx->rx_timestamp[ac];
3151 if (delta < WLFC_RX_DETECTION_THRESHOLD_MS) {
3152 rx_map |= (1 << ac);
3153 }
3154 }
3155
3156 if (ctx->pkt_cnt_per_ac[ac] == 0) {
3157 continue;
3158 }
3159
3160 tx_map |= (1 << ac);
3161 single_ac = ac + 1;
3162 pkt_send_per_ac = 0;
3163 while ((FALSE == dhdp->proptxstatus_txoff) &&
3164 (pkt_send_per_ac < WLFC_PACKET_BOUND)) {
3165 /* packets from delayQ with less priority are fresh and
3166 * they'd need header and have no MAC entry
3167 */
3168 no_credit = (ctx->FIFO_credit[ac] < 1);
3169 if (dhdp->proptxstatus_credit_ignore ||
3170 ((ac == AC_COUNT) && !ctx->bcmc_credit_supported)) {
3171 no_credit = FALSE;
3172 }
3173
3174 lender = -1;
3175 #ifdef LIMIT_BORROW
3176 if (no_credit && (ac < AC_COUNT) && (tx_map >= rx_map) &&
3177 dhdp->wlfc_borrow_allowed) {
3178 /* try borrow from lower priority */
3179 lender = _dhd_wlfc_borrow_credit(ctx, ac - 1, ac, FALSE);
3180 if (lender != -1) {
3181 no_credit = FALSE;
3182 }
3183 }
3184 #endif
3185 commit_info.needs_hdr = 1;
3186 commit_info.mac_entry = NULL;
3187 commit_info.p = _dhd_wlfc_deque_delayedq(ctx, ac,
3188 &(commit_info.ac_fifo_credit_spent),
3189 &(commit_info.needs_hdr),
3190 &(commit_info.mac_entry),
3191 no_credit);
3192 commit_info.pkt_type = (commit_info.needs_hdr) ? eWLFC_PKTTYPE_DELAYED :
3193 eWLFC_PKTTYPE_SUPPRESSED;
3194
3195 if (commit_info.p == NULL) {
3196 #ifdef LIMIT_BORROW
3197 if (lender != -1 && dhdp->wlfc_borrow_allowed) {
3198 _dhd_wlfc_return_credit(ctx, lender, ac);
3199 }
3200 #endif
3201 break;
3202 }
3203
3204 if (!dhdp->proptxstatus_credit_ignore && (lender == -1)) {
3205 ASSERT(ctx->FIFO_credit[ac] >= commit_info.ac_fifo_credit_spent);
3206 }
3207 /* here we can ensure have credit or no credit needed */
3208 rc = _dhd_wlfc_handle_packet_commit(ctx, ac, &commit_info,
3209 ctx->fcommit, ctx->commit_ctx);
3210
3211 /* Bus commits may fail (e.g. flow control); abort after retries */
3212 if (rc == BCME_OK) {
3213 pkt_send++;
3214 pkt_send_per_ac++;
3215 if (commit_info.ac_fifo_credit_spent && (lender == -1)) {
3216 ctx->FIFO_credit[ac]--;
3217 }
3218 #ifdef LIMIT_BORROW
3219 else if (!commit_info.ac_fifo_credit_spent && (lender != -1) &&
3220 dhdp->wlfc_borrow_allowed) {
3221 _dhd_wlfc_return_credit(ctx, lender, ac);
3222 }
3223 #endif
3224 } else {
3225 #ifdef LIMIT_BORROW
3226 if (lender != -1 && dhdp->wlfc_borrow_allowed) {
3227 _dhd_wlfc_return_credit(ctx, lender, ac);
3228 }
3229 #endif
3230 bus_retry_count++;
3231 if (bus_retry_count >= BUS_RETRIES) {
3232 DHD_ERROR(("%s: bus error %d\n", __FUNCTION__, rc));
3233 goto exit;
3234 }
3235 }
3236 }
3237
3238 if (ctx->pkt_cnt_per_ac[ac]) {
3239 packets_map |= (1 << ac);
3240 }
3241 }
3242
3243 if ((tx_map == 0) || dhdp->proptxstatus_credit_ignore) {
3244 /* nothing send out or remain in queue */
3245 rc = BCME_OK;
3246 goto exit;
3247 }
3248
3249 if (((tx_map & (tx_map - 1)) == 0) && (tx_map >= rx_map)) {
3250 /* only one tx ac exist and no higher rx ac */
3251 if ((single_ac == ctx->single_ac) && ctx->allow_credit_borrow) {
3252 ac = single_ac - 1;
3253 } else {
3254 uint32 delta;
3255 uint32 curr_t = OSL_SYSUPTIME();
3256
3257 if (single_ac != ctx->single_ac) {
3258 /* new single ac traffic (first single ac or different single ac) */
3259 ctx->allow_credit_borrow = 0;
3260 ctx->single_ac_timestamp = curr_t;
3261 ctx->single_ac = (uint8)single_ac;
3262 rc = BCME_OK;
3263 goto exit;
3264 }
3265 /* same ac traffic, check if it lasts enough time */
3266 delta = curr_t - ctx->single_ac_timestamp;
3267
3268 if (delta >= WLFC_BORROW_DEFER_PERIOD_MS) {
3269 /* wait enough time, can borrow now */
3270 ctx->allow_credit_borrow = 1;
3271 ac = single_ac - 1;
3272 } else {
3273 rc = BCME_OK;
3274 goto exit;
3275 }
3276 }
3277 } else {
3278 /* If we have multiple AC traffic, turn off borrowing, mark time and bail out */
3279 ctx->allow_credit_borrow = 0;
3280 ctx->single_ac_timestamp = 0;
3281 ctx->single_ac = 0;
3282 rc = BCME_OK;
3283 goto exit;
3284 }
3285
3286 if (packets_map == 0) {
3287 /* nothing to send, skip borrow */
3288 rc = BCME_OK;
3289 goto exit;
3290 }
3291
3292 /* At this point, borrow all credits only for ac */
3293 while (FALSE == dhdp->proptxstatus_txoff) {
3294 #ifdef LIMIT_BORROW
3295 if (dhdp->wlfc_borrow_allowed) {
3296 if ((lender = _dhd_wlfc_borrow_credit(ctx, AC_COUNT, ac, TRUE)) == -1) {
3297 break;
3298 }
3299 }
3300 else
3301 break;
3302 #endif
3303 commit_info.p = _dhd_wlfc_deque_delayedq(ctx, ac,
3304 &(commit_info.ac_fifo_credit_spent),
3305 &(commit_info.needs_hdr),
3306 &(commit_info.mac_entry),
3307 FALSE);
3308 if (commit_info.p == NULL) {
3309 /* before borrow only one ac exists and now this only ac is empty */
3310 #ifdef LIMIT_BORROW
3311 _dhd_wlfc_return_credit(ctx, lender, ac);
3312 #endif
3313 break;
3314 }
3315
3316 commit_info.pkt_type = (commit_info.needs_hdr) ? eWLFC_PKTTYPE_DELAYED :
3317 eWLFC_PKTTYPE_SUPPRESSED;
3318
3319 rc = _dhd_wlfc_handle_packet_commit(ctx, ac, &commit_info,
3320 ctx->fcommit, ctx->commit_ctx);
3321
3322 /* Bus commits may fail (e.g. flow control); abort after retries */
3323 if (rc == BCME_OK) {
3324 pkt_send++;
3325 if (commit_info.ac_fifo_credit_spent) {
3326 #ifndef LIMIT_BORROW
3327 ctx->FIFO_credit[ac]--;
3328 #endif
3329 } else {
3330 #ifdef LIMIT_BORROW
3331 _dhd_wlfc_return_credit(ctx, lender, ac);
3332 #endif
3333 }
3334 } else {
3335 #ifdef LIMIT_BORROW
3336 _dhd_wlfc_return_credit(ctx, lender, ac);
3337 #endif
3338 bus_retry_count++;
3339 if (bus_retry_count >= BUS_RETRIES) {
3340 DHD_ERROR(("%s: bus error %d\n", __FUNCTION__, rc));
3341 goto exit;
3342 }
3343 }
3344 }
3345
3346 BCM_REFERENCE(pkt_send);
3347
3348 exit:
3349 #if defined(DHD_WLFC_THREAD)
3350 dhd_os_wlfc_unblock(dhdp);
3351 if (ctx && ctx->pkt_cnt_in_psq && pkt_send) {
3352 wait_msec = msecs_to_jiffies(WLFC_THREAD_QUICK_RETRY_WAIT_MS);
3353 } else {
3354 wait_msec = msecs_to_jiffies(WLFC_THREAD_RETRY_WAIT_MS);
3355 }
3356 }
3357 return 0;
3358 #else
3359 return rc;
3360 #endif /* defined(DHD_WLFC_THREAD) */
3361 }
3362
3363 /**
3364 * Enqueues a transmit packet in the next layer towards the dongle, eg the DBUS layer. Called by
3365 * eg dhd_sendpkt().
3366 * @param[in] dhdp Pointer to public DHD structure
3367 * @param[in] fcommit Pointer to transmit function of next layer
3368 * @param[in] commit_ctx Opaque context used when calling next layer
3369 * @param[in] pktbuf Packet to send
3370 * @param[in] need_toggle_host_if If TRUE, resets flag ctx->toggle_host_if
3371 */
3372 int
dhd_wlfc_commit_packets(dhd_pub_t * dhdp,f_commitpkt_t fcommit,void * commit_ctx,void * pktbuf,bool need_toggle_host_if)3373 dhd_wlfc_commit_packets(dhd_pub_t *dhdp, f_commitpkt_t fcommit, void* commit_ctx, void *pktbuf,
3374 bool need_toggle_host_if)
3375 {
3376 int rc = BCME_OK;
3377 athost_wl_status_info_t* ctx;
3378
3379 #if defined(DHD_WLFC_THREAD)
3380 if (!pktbuf)
3381 return BCME_OK;
3382 #endif /* defined(DHD_WLFC_THREAD) */
3383
3384 if ((dhdp == NULL) || (fcommit == NULL)) {
3385 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
3386 return BCME_BADARG;
3387 }
3388
3389 dhd_os_wlfc_block(dhdp);
3390
3391 if (!dhdp->wlfc_state || (dhdp->proptxstatus_mode == WLFC_FCMODE_NONE)) {
3392 if (pktbuf) {
3393 DHD_PKTTAG_WLFCPKT_SET(PKTTAG(pktbuf), 0);
3394 }
3395 rc = WLFC_UNSUPPORTED;
3396 goto exit;
3397 }
3398
3399 ctx = (athost_wl_status_info_t*)dhdp->wlfc_state;
3400
3401 #ifdef BCMDBUS
3402 if (!dhdp->up || (dhdp->busstate == DHD_BUS_DOWN)) {
3403 if (pktbuf) {
3404 PKTFREE(ctx->osh, pktbuf, TRUE);
3405 rc = BCME_OK;
3406 }
3407 goto exit;
3408 }
3409 #endif /* BCMDBUS */
3410
3411 if (dhdp->proptxstatus_module_ignore) {
3412 if (pktbuf) {
3413 uint32 htod = 0;
3414 WL_TXSTATUS_SET_FLAGS(htod, WLFC_PKTFLAG_PKTFROMHOST);
3415 _dhd_wlfc_pushheader(ctx, &pktbuf, FALSE, 0, 0, htod, 0, FALSE);
3416 if (fcommit(commit_ctx, pktbuf)) {
3417 /* free it if failed, otherwise do it in tx complete cb */
3418 PKTFREE(ctx->osh, pktbuf, TRUE);
3419 }
3420 rc = BCME_OK;
3421 }
3422 goto exit;
3423 }
3424
3425 if (pktbuf) {
3426 int ac = DHD_PKTTAG_FIFO(PKTTAG(pktbuf));
3427 ASSERT(ac <= AC_COUNT);
3428 DHD_PKTTAG_WLFCPKT_SET(PKTTAG(pktbuf), 1);
3429 /* en-queue the packets to respective queue. */
3430 rc = _dhd_wlfc_enque_delayq(ctx, pktbuf, ac);
3431 if (rc) {
3432 _dhd_wlfc_prec_drop(ctx->dhdp, (ac << 1), pktbuf, FALSE);
3433 } else {
3434 ctx->stats.pktin++;
3435 ctx->pkt_cnt_in_drv[DHD_PKTTAG_IF(PKTTAG(pktbuf))][ac]++;
3436 }
3437 }
3438
3439 if (!ctx->fcommit) {
3440 ctx->fcommit = fcommit;
3441 } else {
3442 ASSERT(ctx->fcommit == fcommit);
3443 }
3444 if (!ctx->commit_ctx) {
3445 ctx->commit_ctx = commit_ctx;
3446 } else {
3447 ASSERT(ctx->commit_ctx == commit_ctx);
3448 }
3449
3450 #if defined(DHD_WLFC_THREAD)
3451 _dhd_wlfc_thread_wakeup(dhdp);
3452 #else
3453 dhd_wlfc_transfer_packets(dhdp);
3454 #endif /* defined(DHD_WLFC_THREAD) */
3455
3456 exit:
3457 dhd_os_wlfc_unblock(dhdp);
3458 return rc;
3459 } /* dhd_wlfc_commit_packets */
3460
3461 /**
3462 * Called when the (lower) DBUS layer indicates completion (succesfull or not) of a transmit packet
3463 */
3464 int
dhd_wlfc_txcomplete(dhd_pub_t * dhd,void * txp,bool success)3465 dhd_wlfc_txcomplete(dhd_pub_t *dhd, void *txp, bool success)
3466 {
3467 athost_wl_status_info_t* wlfc;
3468 wlfc_mac_descriptor_t *entry;
3469 void* pout = NULL;
3470 int rtn = BCME_OK;
3471 if ((dhd == NULL) || (txp == NULL)) {
3472 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
3473 return BCME_BADARG;
3474 }
3475
3476 bcm_pkt_validate_chk(txp);
3477
3478 dhd_os_wlfc_block(dhd);
3479
3480 if (!dhd->wlfc_state || (dhd->proptxstatus_mode == WLFC_FCMODE_NONE)) {
3481 rtn = WLFC_UNSUPPORTED;
3482 goto EXIT;
3483 }
3484
3485 wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
3486 if (DHD_PKTTAG_SIGNALONLY(PKTTAG(txp))) {
3487 #ifdef PROP_TXSTATUS_DEBUG
3488 wlfc->stats.signal_only_pkts_freed++;
3489 #endif
3490 /* is this a signal-only packet? */
3491 _dhd_wlfc_pullheader(wlfc, txp);
3492 PKTFREE(wlfc->osh, txp, TRUE);
3493 goto EXIT;
3494 }
3495
3496 entry = _dhd_wlfc_find_table_entry(wlfc, txp);
3497 ASSERT(entry);
3498
3499 if (!success || dhd->proptxstatus_txstatus_ignore) {
3500 WLFC_DBGMESG(("At: %s():%d, bus_complete() failure for %p, htod_tag:0x%08x\n",
3501 __FUNCTION__, __LINE__, txp, DHD_PKTTAG_H2DTAG(PKTTAG(txp))));
3502 if (!WLFC_GET_AFQ(dhd->wlfc_mode)) {
3503 _dhd_wlfc_hanger_poppkt(wlfc->hanger, WL_TXSTATUS_GET_HSLOT(
3504 DHD_PKTTAG_H2DTAG(PKTTAG(txp))), &pout, TRUE);
3505 ASSERT(txp == pout);
3506 }
3507
3508 /* indicate failure and free the packet */
3509 dhd_txcomplete(dhd, txp, success);
3510
3511 /* return the credit, if necessary */
3512 _dhd_wlfc_return_implied_credit(wlfc, txp);
3513
3514 if (entry->transit_count)
3515 entry->transit_count--;
3516 if (entry->suppr_transit_count)
3517 entry->suppr_transit_count--;
3518 wlfc->pkt_cnt_in_drv[DHD_PKTTAG_IF(PKTTAG(txp))][DHD_PKTTAG_FIFO(PKTTAG(txp))]--;
3519 wlfc->stats.pktout++;
3520 PKTFREE(wlfc->osh, txp, TRUE);
3521 } else {
3522 /* bus confirmed pkt went to firmware side */
3523 if (WLFC_GET_AFQ(dhd->wlfc_mode)) {
3524 _dhd_wlfc_enque_afq(wlfc, txp);
3525 } else {
3526 int hslot = WL_TXSTATUS_GET_HSLOT(DHD_PKTTAG_H2DTAG(PKTTAG(txp)));
3527 _dhd_wlfc_hanger_free_pkt(wlfc, hslot,
3528 WLFC_HANGER_PKT_STATE_BUSRETURNED, -1);
3529 }
3530 }
3531
3532 ASSERT(entry->onbus_pkts_count > 0);
3533 if (entry->onbus_pkts_count > 0)
3534 entry->onbus_pkts_count--;
3535 if (entry->suppressed &&
3536 (!entry->onbus_pkts_count) &&
3537 (!entry->suppr_transit_count))
3538 entry->suppressed = FALSE;
3539 EXIT:
3540 dhd_os_wlfc_unblock(dhd);
3541 return rtn;
3542 } /* dhd_wlfc_txcomplete */
3543
3544 int
dhd_wlfc_init(dhd_pub_t * dhd)3545 dhd_wlfc_init(dhd_pub_t *dhd)
3546 {
3547 /* enable all signals & indicate host proptxstatus logic is active */
3548 uint32 tlv, mode, fw_caps;
3549 int ret = 0;
3550
3551 if (dhd == NULL) {
3552 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
3553 return BCME_BADARG;
3554 }
3555
3556 dhd_os_wlfc_block(dhd);
3557 if (dhd->wlfc_enabled) {
3558 DHD_ERROR(("%s():%d, Already enabled!\n", __FUNCTION__, __LINE__));
3559 dhd_os_wlfc_unblock(dhd);
3560 return BCME_OK;
3561 }
3562 dhd->wlfc_enabled = TRUE;
3563 dhd_os_wlfc_unblock(dhd);
3564
3565 tlv = WLFC_FLAGS_RSSI_SIGNALS |
3566 WLFC_FLAGS_XONXOFF_SIGNALS |
3567 WLFC_FLAGS_CREDIT_STATUS_SIGNALS |
3568 WLFC_FLAGS_HOST_PROPTXSTATUS_ACTIVE |
3569 WLFC_FLAGS_HOST_RXRERODER_ACTIVE;
3570
3571
3572 /*
3573 try to enable/disable signaling by sending "tlv" iovar. if that fails,
3574 fallback to no flow control? Print a message for now.
3575 */
3576
3577 /* enable proptxtstatus signaling by default */
3578 if (!dhd_wl_ioctl_set_intiovar(dhd, "tlv", tlv, WLC_SET_VAR, TRUE, 0)) {
3579 /*
3580 Leaving the message for now, it should be removed after a while; once
3581 the tlv situation is stable.
3582 */
3583 DHD_ERROR(("dhd_wlfc_init(): successfully %s bdcv2 tlv signaling, %d\n",
3584 dhd->wlfc_enabled?"enabled":"disabled", tlv));
3585 }
3586
3587 mode = 0;
3588
3589 /* query caps */
3590 ret = dhd_wl_ioctl_get_intiovar(dhd, "wlfc_mode", &fw_caps, WLC_GET_VAR, FALSE, 0);
3591
3592 if (!ret) {
3593 DHD_ERROR(("%s: query wlfc_mode succeed, fw_caps=0x%x\n", __FUNCTION__, fw_caps));
3594
3595 if (WLFC_IS_OLD_DEF(fw_caps)) {
3596 #ifdef BCMDBUS
3597 mode = WLFC_MODE_HANGER;
3598 #else
3599 /* enable proptxtstatus v2 by default */
3600 mode = WLFC_MODE_AFQ;
3601 #endif /* BCMDBUS */
3602 } else {
3603 WLFC_SET_AFQ(mode, WLFC_GET_AFQ(fw_caps));
3604 #ifdef BCMDBUS
3605 WLFC_SET_AFQ(mode, 0);
3606 #endif /* BCMDBUS */
3607 WLFC_SET_REUSESEQ(mode, WLFC_GET_REUSESEQ(fw_caps));
3608 WLFC_SET_REORDERSUPP(mode, WLFC_GET_REORDERSUPP(fw_caps));
3609 }
3610 ret = dhd_wl_ioctl_set_intiovar(dhd, "wlfc_mode", mode, WLC_SET_VAR, TRUE, 0);
3611 }
3612
3613 dhd_os_wlfc_block(dhd);
3614
3615 dhd->wlfc_mode = 0;
3616 if (ret >= 0) {
3617 if (WLFC_IS_OLD_DEF(mode)) {
3618 WLFC_SET_AFQ(dhd->wlfc_mode, (mode == WLFC_MODE_AFQ));
3619 } else {
3620 dhd->wlfc_mode = mode;
3621 }
3622 }
3623
3624 DHD_ERROR(("dhd_wlfc_init(): wlfc_mode=0x%x, ret=%d\n", dhd->wlfc_mode, ret));
3625 #ifdef LIMIT_BORROW
3626 dhd->wlfc_borrow_allowed = TRUE;
3627 #endif
3628 dhd_os_wlfc_unblock(dhd);
3629
3630 if (dhd->plat_init)
3631 dhd->plat_init((void *)dhd);
3632
3633 return BCME_OK;
3634 } /* dhd_wlfc_init */
3635
3636 /** AMPDU host reorder specific function */
3637 int
dhd_wlfc_hostreorder_init(dhd_pub_t * dhd)3638 dhd_wlfc_hostreorder_init(dhd_pub_t *dhd)
3639 {
3640 /* enable only ampdu hostreorder here */
3641 uint32 tlv;
3642
3643 if (dhd == NULL) {
3644 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
3645 return BCME_BADARG;
3646 }
3647
3648 DHD_TRACE(("%s():%d Enter\n", __FUNCTION__, __LINE__));
3649
3650 tlv = WLFC_FLAGS_HOST_RXRERODER_ACTIVE;
3651
3652 /* enable proptxtstatus signaling by default */
3653 if (dhd_wl_ioctl_set_intiovar(dhd, "tlv", tlv, WLC_SET_VAR, TRUE, 0)) {
3654 DHD_ERROR(("%s(): failed to enable/disable bdcv2 tlv signaling\n",
3655 __FUNCTION__));
3656 } else {
3657 /*
3658 Leaving the message for now, it should be removed after a while; once
3659 the tlv situation is stable.
3660 */
3661 DHD_ERROR(("%s(): successful bdcv2 tlv signaling, %d\n",
3662 __FUNCTION__, tlv));
3663 }
3664
3665 dhd_os_wlfc_block(dhd);
3666 dhd->proptxstatus_mode = WLFC_ONLY_AMPDU_HOSTREORDER;
3667 dhd_os_wlfc_unblock(dhd);
3668 /* terence 20161229: enable ampdu_hostreorder if tlv enable hostreorder */
3669 dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "ampdu_hostreorder", 1, 0, TRUE);
3670
3671 return BCME_OK;
3672 }
3673
3674 int
dhd_wlfc_cleanup_txq(dhd_pub_t * dhd,f_processpkt_t fn,void * arg)3675 dhd_wlfc_cleanup_txq(dhd_pub_t *dhd, f_processpkt_t fn, void *arg)
3676 {
3677 if (dhd == NULL) {
3678 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
3679 return BCME_BADARG;
3680 }
3681
3682 dhd_os_wlfc_block(dhd);
3683
3684 if (!dhd->wlfc_state || (dhd->proptxstatus_mode == WLFC_FCMODE_NONE)) {
3685 dhd_os_wlfc_unblock(dhd);
3686 return WLFC_UNSUPPORTED;
3687 }
3688
3689 #ifndef BCMDBUS
3690 _dhd_wlfc_cleanup_txq(dhd, fn, arg);
3691 #endif /* !BCMDBUS */
3692
3693 dhd_os_wlfc_unblock(dhd);
3694
3695 return BCME_OK;
3696 }
3697
3698 /** release all packet resources */
3699 int
dhd_wlfc_cleanup(dhd_pub_t * dhd,f_processpkt_t fn,void * arg)3700 dhd_wlfc_cleanup(dhd_pub_t *dhd, f_processpkt_t fn, void *arg)
3701 {
3702 if (dhd == NULL) {
3703 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
3704 return BCME_BADARG;
3705 }
3706
3707 dhd_os_wlfc_block(dhd);
3708
3709 if (!dhd->wlfc_state || (dhd->proptxstatus_mode == WLFC_FCMODE_NONE)) {
3710 dhd_os_wlfc_unblock(dhd);
3711 return WLFC_UNSUPPORTED;
3712 }
3713
3714 _dhd_wlfc_cleanup(dhd, fn, arg);
3715
3716 dhd_os_wlfc_unblock(dhd);
3717
3718 return BCME_OK;
3719 }
3720
3721 int
dhd_wlfc_deinit(dhd_pub_t * dhd)3722 dhd_wlfc_deinit(dhd_pub_t *dhd)
3723 {
3724 /* cleanup all psq related resources */
3725 athost_wl_status_info_t* wlfc;
3726 uint32 tlv = 0;
3727 uint32 hostreorder = 0;
3728
3729 if (dhd == NULL) {
3730 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
3731 return BCME_BADARG;
3732 }
3733
3734 dhd_os_wlfc_block(dhd);
3735 if (!dhd->wlfc_enabled) {
3736 DHD_ERROR(("%s():%d, Already disabled!\n", __FUNCTION__, __LINE__));
3737 dhd_os_wlfc_unblock(dhd);
3738 return BCME_OK;
3739 }
3740
3741 dhd->wlfc_enabled = FALSE;
3742 dhd_os_wlfc_unblock(dhd);
3743
3744 /* query ampdu hostreorder */
3745 (void) dhd_wl_ioctl_get_intiovar(dhd, "ampdu_hostreorder",
3746 &hostreorder, WLC_GET_VAR, FALSE, 0);
3747
3748 if (hostreorder) {
3749 tlv = WLFC_FLAGS_HOST_RXRERODER_ACTIVE;
3750 DHD_ERROR(("%s():%d, maintain HOST RXRERODER flag in tvl\n",
3751 __FUNCTION__, __LINE__));
3752 }
3753
3754 /* Disable proptxtstatus signaling for deinit */
3755 (void) dhd_wl_ioctl_set_intiovar(dhd, "tlv", tlv, WLC_SET_VAR, TRUE, 0);
3756
3757 dhd_os_wlfc_block(dhd);
3758
3759 if (!dhd->wlfc_state || (dhd->proptxstatus_mode == WLFC_FCMODE_NONE)) {
3760 dhd_os_wlfc_unblock(dhd);
3761 return WLFC_UNSUPPORTED;
3762 }
3763
3764 wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
3765
3766 _dhd_wlfc_cleanup(dhd, NULL, NULL);
3767
3768 if (!WLFC_GET_AFQ(dhd->wlfc_mode)) {
3769 int i;
3770 wlfc_hanger_t* h = (wlfc_hanger_t*)wlfc->hanger;
3771 for (i = 0; i < h->max_items; i++) {
3772 if (h->items[i].state != WLFC_HANGER_ITEM_STATE_FREE) {
3773 _dhd_wlfc_hanger_free_pkt(wlfc, i,
3774 WLFC_HANGER_PKT_STATE_COMPLETE, TRUE);
3775 }
3776 }
3777
3778 /* delete hanger */
3779 _dhd_wlfc_hanger_delete(dhd, h);
3780 }
3781
3782
3783 /* free top structure */
3784 DHD_OS_PREFREE(dhd, dhd->wlfc_state,
3785 sizeof(athost_wl_status_info_t));
3786 dhd->wlfc_state = NULL;
3787 dhd->proptxstatus_mode = hostreorder ?
3788 WLFC_ONLY_AMPDU_HOSTREORDER : WLFC_FCMODE_NONE;
3789
3790 DHD_ERROR(("%s: wlfc_mode=0x%x, tlv=%d\n", __FUNCTION__, dhd->wlfc_mode, tlv));
3791
3792 dhd_os_wlfc_unblock(dhd);
3793
3794 if (dhd->plat_deinit)
3795 dhd->plat_deinit((void *)dhd);
3796 return BCME_OK;
3797 } /* dhd_wlfc_init */
3798
3799 /**
3800 * Called on an interface event (WLC_E_IF) indicated by firmware
3801 * @param[in] dhdp Pointer to public DHD structure
3802 * @param[in] action eg eWLFC_MAC_ENTRY_ACTION_UPDATE or eWLFC_MAC_ENTRY_ACTION_ADD
3803 */
dhd_wlfc_interface_event(dhd_pub_t * dhdp,uint8 action,uint8 ifid,uint8 iftype,uint8 * ea)3804 int dhd_wlfc_interface_event(dhd_pub_t *dhdp, uint8 action, uint8 ifid, uint8 iftype, uint8* ea)
3805 {
3806 int rc;
3807
3808 if (dhdp == NULL) {
3809 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
3810 return BCME_BADARG;
3811 }
3812
3813 dhd_os_wlfc_block(dhdp);
3814
3815 if (!dhdp->wlfc_state || (dhdp->proptxstatus_mode == WLFC_FCMODE_NONE)) {
3816 dhd_os_wlfc_unblock(dhdp);
3817 return WLFC_UNSUPPORTED;
3818 }
3819
3820 rc = _dhd_wlfc_interface_entry_update(dhdp->wlfc_state, action, ifid, iftype, ea);
3821
3822 dhd_os_wlfc_unblock(dhdp);
3823 return rc;
3824 }
3825
3826 /** Called eg on receiving a WLC_E_FIFO_CREDIT_MAP event from the dongle */
dhd_wlfc_FIFOcreditmap_event(dhd_pub_t * dhdp,uint8 * event_data)3827 int dhd_wlfc_FIFOcreditmap_event(dhd_pub_t *dhdp, uint8* event_data)
3828 {
3829 int rc;
3830
3831 if (dhdp == NULL) {
3832 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
3833 return BCME_BADARG;
3834 }
3835
3836 dhd_os_wlfc_block(dhdp);
3837
3838 if (!dhdp->wlfc_state || (dhdp->proptxstatus_mode == WLFC_FCMODE_NONE)) {
3839 dhd_os_wlfc_unblock(dhdp);
3840 return WLFC_UNSUPPORTED;
3841 }
3842
3843 rc = _dhd_wlfc_FIFOcreditmap_update(dhdp->wlfc_state, event_data);
3844
3845 dhd_os_wlfc_unblock(dhdp);
3846
3847 return rc;
3848 }
3849 #ifdef LIMIT_BORROW
dhd_wlfc_disable_credit_borrow_event(dhd_pub_t * dhdp,uint8 * event_data)3850 int dhd_wlfc_disable_credit_borrow_event(dhd_pub_t *dhdp, uint8* event_data)
3851 {
3852 if (dhdp == NULL || event_data == NULL) {
3853 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
3854 return BCME_BADARG;
3855 }
3856 dhd_os_wlfc_block(dhdp);
3857 dhdp->wlfc_borrow_allowed = (bool)(*(uint32 *)event_data);
3858 dhd_os_wlfc_unblock(dhdp);
3859
3860 return BCME_OK;
3861 }
3862 #endif /* LIMIT_BORROW */
3863
3864 /**
3865 * Called eg on receiving a WLC_E_BCMC_CREDIT_SUPPORT event from the dongle (broadcast/multicast
3866 * specific)
3867 */
dhd_wlfc_BCMCCredit_support_event(dhd_pub_t * dhdp)3868 int dhd_wlfc_BCMCCredit_support_event(dhd_pub_t *dhdp)
3869 {
3870 int rc;
3871
3872 if (dhdp == NULL) {
3873 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
3874 return BCME_BADARG;
3875 }
3876
3877 dhd_os_wlfc_block(dhdp);
3878
3879 if (!dhdp->wlfc_state || (dhdp->proptxstatus_mode == WLFC_FCMODE_NONE)) {
3880 dhd_os_wlfc_unblock(dhdp);
3881 return WLFC_UNSUPPORTED;
3882 }
3883
3884 rc = _dhd_wlfc_BCMCCredit_support_update(dhdp->wlfc_state);
3885
3886 dhd_os_wlfc_unblock(dhdp);
3887 return rc;
3888 }
3889
3890 /** debug specific function */
3891 int
dhd_wlfc_dump(dhd_pub_t * dhdp,struct bcmstrbuf * strbuf)3892 dhd_wlfc_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf)
3893 {
3894 int i;
3895 uint8* ea;
3896 athost_wl_status_info_t* wlfc;
3897 wlfc_hanger_t* h;
3898 wlfc_mac_descriptor_t* mac_table;
3899 wlfc_mac_descriptor_t* interfaces;
3900 char* iftypes[] = {"STA", "AP", "WDS", "p2pGO", "p2pCL"};
3901
3902 if (!dhdp || !strbuf) {
3903 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
3904 return BCME_BADARG;
3905 }
3906
3907 dhd_os_wlfc_block(dhdp);
3908
3909 if (!dhdp->wlfc_state || (dhdp->proptxstatus_mode == WLFC_FCMODE_NONE)) {
3910 dhd_os_wlfc_unblock(dhdp);
3911 return WLFC_UNSUPPORTED;
3912 }
3913
3914 wlfc = (athost_wl_status_info_t*)dhdp->wlfc_state;
3915
3916 h = (wlfc_hanger_t*)wlfc->hanger;
3917 if (h == NULL) {
3918 bcm_bprintf(strbuf, "wlfc-hanger not initialized yet\n");
3919 }
3920
3921 mac_table = wlfc->destination_entries.nodes;
3922 interfaces = wlfc->destination_entries.interfaces;
3923 bcm_bprintf(strbuf, "---- wlfc stats ----\n");
3924
3925 if (!WLFC_GET_AFQ(dhdp->wlfc_mode)) {
3926 h = (wlfc_hanger_t*)wlfc->hanger;
3927 if (h == NULL) {
3928 bcm_bprintf(strbuf, "wlfc-hanger not initialized yet\n");
3929 } else {
3930 bcm_bprintf(strbuf, "wlfc hanger (pushed,popped,f_push,"
3931 "f_pop,f_slot, pending) = (%d,%d,%d,%d,%d,%d)\n",
3932 h->pushed,
3933 h->popped,
3934 h->failed_to_push,
3935 h->failed_to_pop,
3936 h->failed_slotfind,
3937 (h->pushed - h->popped));
3938 }
3939 }
3940
3941 bcm_bprintf(strbuf, "wlfc fail(tlv,credit_rqst,mac_update,psmode_update), "
3942 "(dq_full,rollback_fail) = (%d,%d,%d,%d), (%d,%d)\n",
3943 wlfc->stats.tlv_parse_failed,
3944 wlfc->stats.credit_request_failed,
3945 wlfc->stats.mac_update_failed,
3946 wlfc->stats.psmode_update_failed,
3947 wlfc->stats.delayq_full_error,
3948 wlfc->stats.rollback_failed);
3949
3950 bcm_bprintf(strbuf, "PKTS (init_credit,credit,sent,drop_d,drop_s,outoforder) "
3951 "(AC0[%d,%d,%d,%d,%d,%d],AC1[%d,%d,%d,%d,%d,%d],AC2[%d,%d,%d,%d,%d,%d],"
3952 "AC3[%d,%d,%d,%d,%d,%d],BC_MC[%d,%d,%d,%d,%d,%d])\n",
3953 wlfc->Init_FIFO_credit[0], wlfc->FIFO_credit[0], wlfc->stats.send_pkts[0],
3954 wlfc->stats.drop_pkts[0], wlfc->stats.drop_pkts[1], wlfc->stats.ooo_pkts[0],
3955 wlfc->Init_FIFO_credit[1], wlfc->FIFO_credit[1], wlfc->stats.send_pkts[1],
3956 wlfc->stats.drop_pkts[2], wlfc->stats.drop_pkts[3], wlfc->stats.ooo_pkts[1],
3957 wlfc->Init_FIFO_credit[2], wlfc->FIFO_credit[2], wlfc->stats.send_pkts[2],
3958 wlfc->stats.drop_pkts[4], wlfc->stats.drop_pkts[5], wlfc->stats.ooo_pkts[2],
3959 wlfc->Init_FIFO_credit[3], wlfc->FIFO_credit[3], wlfc->stats.send_pkts[3],
3960 wlfc->stats.drop_pkts[6], wlfc->stats.drop_pkts[7], wlfc->stats.ooo_pkts[3],
3961 wlfc->Init_FIFO_credit[4], wlfc->FIFO_credit[4], wlfc->stats.send_pkts[4],
3962 wlfc->stats.drop_pkts[8], wlfc->stats.drop_pkts[9], wlfc->stats.ooo_pkts[4]);
3963
3964 bcm_bprintf(strbuf, "\n");
3965 for (i = 0; i < WLFC_MAX_IFNUM; i++) {
3966 if (interfaces[i].occupied) {
3967 char* iftype_desc;
3968
3969 if (interfaces[i].iftype > WLC_E_IF_ROLE_P2P_CLIENT)
3970 iftype_desc = "<Unknown";
3971 else
3972 iftype_desc = iftypes[interfaces[i].iftype];
3973
3974 ea = interfaces[i].ea;
3975 bcm_bprintf(strbuf, "INTERFACE[%d].ea = "
3976 "[%02x:%02x:%02x:%02x:%02x:%02x], if:%d, type: %s "
3977 "netif_flow_control:%s\n", i,
3978 ea[0], ea[1], ea[2], ea[3], ea[4], ea[5],
3979 interfaces[i].interface_id,
3980 iftype_desc, ((wlfc->hostif_flow_state[i] == OFF)
3981 ? " OFF":" ON"));
3982
3983 bcm_bprintf(strbuf, "INTERFACE[%d].PSQ(len,state,credit),"
3984 "(trans,supp_trans,onbus)"
3985 "= (%d,%s,%d),(%d,%d,%d)\n",
3986 i,
3987 interfaces[i].psq.len,
3988 ((interfaces[i].state ==
3989 WLFC_STATE_OPEN) ? "OPEN":"CLOSE"),
3990 interfaces[i].requested_credit,
3991 interfaces[i].transit_count,
3992 interfaces[i].suppr_transit_count,
3993 interfaces[i].onbus_pkts_count);
3994
3995 bcm_bprintf(strbuf, "INTERFACE[%d].PSQ"
3996 "(delay0,sup0,afq0),(delay1,sup1,afq1),(delay2,sup2,afq2),"
3997 "(delay3,sup3,afq3),(delay4,sup4,afq4) = (%d,%d,%d),"
3998 "(%d,%d,%d),(%d,%d,%d),(%d,%d,%d),(%d,%d,%d)\n",
3999 i,
4000 interfaces[i].psq.q[0].len,
4001 interfaces[i].psq.q[1].len,
4002 interfaces[i].afq.q[0].len,
4003 interfaces[i].psq.q[2].len,
4004 interfaces[i].psq.q[3].len,
4005 interfaces[i].afq.q[1].len,
4006 interfaces[i].psq.q[4].len,
4007 interfaces[i].psq.q[5].len,
4008 interfaces[i].afq.q[2].len,
4009 interfaces[i].psq.q[6].len,
4010 interfaces[i].psq.q[7].len,
4011 interfaces[i].afq.q[3].len,
4012 interfaces[i].psq.q[8].len,
4013 interfaces[i].psq.q[9].len,
4014 interfaces[i].afq.q[4].len);
4015 }
4016 }
4017
4018 bcm_bprintf(strbuf, "\n");
4019 for (i = 0; i < WLFC_MAC_DESC_TABLE_SIZE; i++) {
4020 if (mac_table[i].occupied) {
4021 ea = mac_table[i].ea;
4022 bcm_bprintf(strbuf, "MAC_table[%d].ea = "
4023 "[%02x:%02x:%02x:%02x:%02x:%02x], if:%d \n", i,
4024 ea[0], ea[1], ea[2], ea[3], ea[4], ea[5],
4025 mac_table[i].interface_id);
4026
4027 bcm_bprintf(strbuf, "MAC_table[%d].PSQ(len,state,credit),"
4028 "(trans,supp_trans,onbus)"
4029 "= (%d,%s,%d),(%d,%d,%d)\n",
4030 i,
4031 mac_table[i].psq.len,
4032 ((mac_table[i].state ==
4033 WLFC_STATE_OPEN) ? " OPEN":"CLOSE"),
4034 mac_table[i].requested_credit,
4035 mac_table[i].transit_count,
4036 mac_table[i].suppr_transit_count,
4037 mac_table[i].onbus_pkts_count);
4038 #ifdef PROP_TXSTATUS_DEBUG
4039 bcm_bprintf(strbuf, "MAC_table[%d]: (opened, closed) = (%d, %d)\n",
4040 i, mac_table[i].opened_ct, mac_table[i].closed_ct);
4041 #endif
4042 bcm_bprintf(strbuf, "MAC_table[%d].PSQ"
4043 "(delay0,sup0,afq0),(delay1,sup1,afq1),(delay2,sup2,afq2),"
4044 "(delay3,sup3,afq3),(delay4,sup4,afq4) =(%d,%d,%d),"
4045 "(%d,%d,%d),(%d,%d,%d),(%d,%d,%d),(%d,%d,%d)\n",
4046 i,
4047 mac_table[i].psq.q[0].len,
4048 mac_table[i].psq.q[1].len,
4049 mac_table[i].afq.q[0].len,
4050 mac_table[i].psq.q[2].len,
4051 mac_table[i].psq.q[3].len,
4052 mac_table[i].afq.q[1].len,
4053 mac_table[i].psq.q[4].len,
4054 mac_table[i].psq.q[5].len,
4055 mac_table[i].afq.q[2].len,
4056 mac_table[i].psq.q[6].len,
4057 mac_table[i].psq.q[7].len,
4058 mac_table[i].afq.q[3].len,
4059 mac_table[i].psq.q[8].len,
4060 mac_table[i].psq.q[9].len,
4061 mac_table[i].afq.q[4].len);
4062 }
4063 }
4064
4065 #ifdef PROP_TXSTATUS_DEBUG
4066 {
4067 int avg;
4068 int moving_avg = 0;
4069 int moving_samples;
4070
4071 if (wlfc->stats.latency_sample_count) {
4072 moving_samples = sizeof(wlfc->stats.deltas)/sizeof(uint32);
4073
4074 for (i = 0; i < moving_samples; i++)
4075 moving_avg += wlfc->stats.deltas[i];
4076 moving_avg /= moving_samples;
4077
4078 avg = (100 * wlfc->stats.total_status_latency) /
4079 wlfc->stats.latency_sample_count;
4080 bcm_bprintf(strbuf, "txstatus latency (average, last, moving[%d]) = "
4081 "(%d.%d, %03d, %03d)\n",
4082 moving_samples, avg/100, (avg - (avg/100)*100),
4083 wlfc->stats.latency_most_recent,
4084 moving_avg);
4085 }
4086 }
4087
4088 bcm_bprintf(strbuf, "wlfc- fifo[0-5] credit stats: sent = (%d,%d,%d,%d,%d,%d), "
4089 "back = (%d,%d,%d,%d,%d,%d)\n",
4090 wlfc->stats.fifo_credits_sent[0],
4091 wlfc->stats.fifo_credits_sent[1],
4092 wlfc->stats.fifo_credits_sent[2],
4093 wlfc->stats.fifo_credits_sent[3],
4094 wlfc->stats.fifo_credits_sent[4],
4095 wlfc->stats.fifo_credits_sent[5],
4096
4097 wlfc->stats.fifo_credits_back[0],
4098 wlfc->stats.fifo_credits_back[1],
4099 wlfc->stats.fifo_credits_back[2],
4100 wlfc->stats.fifo_credits_back[3],
4101 wlfc->stats.fifo_credits_back[4],
4102 wlfc->stats.fifo_credits_back[5]);
4103 {
4104 uint32 fifo_cr_sent = 0;
4105 uint32 fifo_cr_acked = 0;
4106 uint32 request_cr_sent = 0;
4107 uint32 request_cr_ack = 0;
4108 uint32 bc_mc_cr_ack = 0;
4109
4110 for (i = 0; i < sizeof(wlfc->stats.fifo_credits_sent)/sizeof(uint32); i++) {
4111 fifo_cr_sent += wlfc->stats.fifo_credits_sent[i];
4112 }
4113
4114 for (i = 0; i < sizeof(wlfc->stats.fifo_credits_back)/sizeof(uint32); i++) {
4115 fifo_cr_acked += wlfc->stats.fifo_credits_back[i];
4116 }
4117
4118 for (i = 0; i < WLFC_MAC_DESC_TABLE_SIZE; i++) {
4119 if (wlfc->destination_entries.nodes[i].occupied) {
4120 request_cr_sent +=
4121 wlfc->destination_entries.nodes[i].dstncredit_sent_packets;
4122 }
4123 }
4124 for (i = 0; i < WLFC_MAX_IFNUM; i++) {
4125 if (wlfc->destination_entries.interfaces[i].occupied) {
4126 request_cr_sent +=
4127 wlfc->destination_entries.interfaces[i].dstncredit_sent_packets;
4128 }
4129 }
4130 for (i = 0; i < WLFC_MAC_DESC_TABLE_SIZE; i++) {
4131 if (wlfc->destination_entries.nodes[i].occupied) {
4132 request_cr_ack +=
4133 wlfc->destination_entries.nodes[i].dstncredit_acks;
4134 }
4135 }
4136 for (i = 0; i < WLFC_MAX_IFNUM; i++) {
4137 if (wlfc->destination_entries.interfaces[i].occupied) {
4138 request_cr_ack +=
4139 wlfc->destination_entries.interfaces[i].dstncredit_acks;
4140 }
4141 }
4142 bcm_bprintf(strbuf, "wlfc- (sent, status) => pq(%d,%d), vq(%d,%d),"
4143 "other:%d, bc_mc:%d, signal-only, (sent,freed): (%d,%d)",
4144 fifo_cr_sent, fifo_cr_acked,
4145 request_cr_sent, request_cr_ack,
4146 wlfc->destination_entries.other.dstncredit_acks,
4147 bc_mc_cr_ack,
4148 wlfc->stats.signal_only_pkts_sent, wlfc->stats.signal_only_pkts_freed);
4149 }
4150 #endif /* PROP_TXSTATUS_DEBUG */
4151 bcm_bprintf(strbuf, "\n");
4152 bcm_bprintf(strbuf, "wlfc- pkt((in,2bus,txstats,hdrpull,out),(dropped,hdr_only,wlc_tossed)"
4153 "(freed,free_err,rollback)) = "
4154 "((%d,%d,%d,%d,%d),(%d,%d,%d),(%d,%d,%d))\n",
4155 wlfc->stats.pktin,
4156 wlfc->stats.pkt2bus,
4157 wlfc->stats.txstatus_in,
4158 wlfc->stats.dhd_hdrpulls,
4159 wlfc->stats.pktout,
4160
4161 wlfc->stats.pktdropped,
4162 wlfc->stats.wlfc_header_only_pkt,
4163 wlfc->stats.wlc_tossed_pkts,
4164
4165 wlfc->stats.pkt_freed,
4166 wlfc->stats.pkt_free_err, wlfc->stats.rollback);
4167
4168 bcm_bprintf(strbuf, "wlfc- suppress((d11,wlc,err),enq(d11,wl,hq,mac?),retx(d11,wlc,hq)) = "
4169 "((%d,%d,%d),(%d,%d,%d,%d),(%d,%d,%d))\n",
4170 wlfc->stats.d11_suppress,
4171 wlfc->stats.wl_suppress,
4172 wlfc->stats.bad_suppress,
4173
4174 wlfc->stats.psq_d11sup_enq,
4175 wlfc->stats.psq_wlsup_enq,
4176 wlfc->stats.psq_hostq_enq,
4177 wlfc->stats.mac_handle_notfound,
4178
4179 wlfc->stats.psq_d11sup_retx,
4180 wlfc->stats.psq_wlsup_retx,
4181 wlfc->stats.psq_hostq_retx);
4182
4183 bcm_bprintf(strbuf, "wlfc- cleanup(txq,psq,fw) = (%d,%d,%d)\n",
4184 wlfc->stats.cleanup_txq_cnt,
4185 wlfc->stats.cleanup_psq_cnt,
4186 wlfc->stats.cleanup_fw_cnt);
4187
4188 bcm_bprintf(strbuf, "wlfc- generic error: %d\n", wlfc->stats.generic_error);
4189
4190 for (i = 0; i < WLFC_MAX_IFNUM; i++) {
4191 bcm_bprintf(strbuf, "wlfc- if[%d], pkt_cnt_in_q/AC[0-4] = (%d,%d,%d,%d,%d)\n", i,
4192 wlfc->pkt_cnt_in_q[i][0],
4193 wlfc->pkt_cnt_in_q[i][1],
4194 wlfc->pkt_cnt_in_q[i][2],
4195 wlfc->pkt_cnt_in_q[i][3],
4196 wlfc->pkt_cnt_in_q[i][4]);
4197 }
4198 bcm_bprintf(strbuf, "\n");
4199
4200 dhd_os_wlfc_unblock(dhdp);
4201 return BCME_OK;
4202 } /* dhd_wlfc_dump */
4203
dhd_wlfc_clear_counts(dhd_pub_t * dhd)4204 int dhd_wlfc_clear_counts(dhd_pub_t *dhd)
4205 {
4206 athost_wl_status_info_t* wlfc;
4207 wlfc_hanger_t* hanger;
4208
4209 if (dhd == NULL) {
4210 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4211 return BCME_BADARG;
4212 }
4213
4214 dhd_os_wlfc_block(dhd);
4215
4216 if (!dhd->wlfc_state || (dhd->proptxstatus_mode == WLFC_FCMODE_NONE)) {
4217 dhd_os_wlfc_unblock(dhd);
4218 return WLFC_UNSUPPORTED;
4219 }
4220
4221 wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
4222
4223 memset(&wlfc->stats, 0, sizeof(athost_wl_stat_counters_t));
4224
4225 if (!WLFC_GET_AFQ(dhd->wlfc_mode)) {
4226 hanger = (wlfc_hanger_t*)wlfc->hanger;
4227
4228 hanger->pushed = 0;
4229 hanger->popped = 0;
4230 hanger->failed_slotfind = 0;
4231 hanger->failed_to_pop = 0;
4232 hanger->failed_to_push = 0;
4233 }
4234
4235 dhd_os_wlfc_unblock(dhd);
4236
4237 return BCME_OK;
4238 }
4239
4240 /** returns TRUE if flow control is enabled */
dhd_wlfc_get_enable(dhd_pub_t * dhd,bool * val)4241 int dhd_wlfc_get_enable(dhd_pub_t *dhd, bool *val)
4242 {
4243 if (!dhd || !val) {
4244 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4245 return BCME_BADARG;
4246 }
4247
4248 dhd_os_wlfc_block(dhd);
4249
4250 *val = dhd->wlfc_enabled;
4251
4252 dhd_os_wlfc_unblock(dhd);
4253
4254 return BCME_OK;
4255 }
4256
4257 /** Called via an IOVAR */
dhd_wlfc_get_mode(dhd_pub_t * dhd,int * val)4258 int dhd_wlfc_get_mode(dhd_pub_t *dhd, int *val)
4259 {
4260 if (!dhd || !val) {
4261 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4262 return BCME_BADARG;
4263 }
4264
4265 dhd_os_wlfc_block(dhd);
4266
4267 *val = dhd->wlfc_state ? dhd->proptxstatus_mode : 0;
4268
4269 dhd_os_wlfc_unblock(dhd);
4270
4271 return BCME_OK;
4272 }
4273
4274 /** Called via an IOVAR */
dhd_wlfc_set_mode(dhd_pub_t * dhd,int val)4275 int dhd_wlfc_set_mode(dhd_pub_t *dhd, int val)
4276 {
4277 if (!dhd) {
4278 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4279 return BCME_BADARG;
4280 }
4281
4282 dhd_os_wlfc_block(dhd);
4283
4284 if (dhd->wlfc_state) {
4285 dhd->proptxstatus_mode = val & 0xff;
4286 }
4287
4288 dhd_os_wlfc_unblock(dhd);
4289
4290 return BCME_OK;
4291 }
4292
4293 /** Called when rx frame is received from the dongle */
dhd_wlfc_is_header_only_pkt(dhd_pub_t * dhd,void * pktbuf)4294 bool dhd_wlfc_is_header_only_pkt(dhd_pub_t * dhd, void *pktbuf)
4295 {
4296 athost_wl_status_info_t* wlfc;
4297 bool rc = FALSE;
4298
4299 if (dhd == NULL) {
4300 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4301 return FALSE;
4302 }
4303
4304 dhd_os_wlfc_block(dhd);
4305
4306 if (!dhd->wlfc_state || (dhd->proptxstatus_mode == WLFC_FCMODE_NONE)) {
4307 dhd_os_wlfc_unblock(dhd);
4308 return FALSE;
4309 }
4310
4311 wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
4312
4313 if (PKTLEN(wlfc->osh, pktbuf) == 0) {
4314 wlfc->stats.wlfc_header_only_pkt++;
4315 rc = TRUE;
4316 }
4317
4318 dhd_os_wlfc_unblock(dhd);
4319
4320 return rc;
4321 }
4322
dhd_wlfc_flowcontrol(dhd_pub_t * dhdp,bool state,bool bAcquireLock)4323 int dhd_wlfc_flowcontrol(dhd_pub_t *dhdp, bool state, bool bAcquireLock)
4324 {
4325 if (dhdp == NULL) {
4326 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4327 return BCME_BADARG;
4328 }
4329
4330 if (bAcquireLock) {
4331 dhd_os_wlfc_block(dhdp);
4332 }
4333
4334 if (!dhdp->wlfc_state || (dhdp->proptxstatus_mode == WLFC_FCMODE_NONE) ||
4335 dhdp->proptxstatus_module_ignore) {
4336 if (bAcquireLock) {
4337 dhd_os_wlfc_unblock(dhdp);
4338 }
4339 return WLFC_UNSUPPORTED;
4340 }
4341
4342 if (state != dhdp->proptxstatus_txoff) {
4343 dhdp->proptxstatus_txoff = state;
4344 }
4345
4346 if (bAcquireLock) {
4347 dhd_os_wlfc_unblock(dhdp);
4348 }
4349
4350 return BCME_OK;
4351 }
4352
4353 /** Called when eg an rx frame is received from the dongle */
dhd_wlfc_save_rxpath_ac_time(dhd_pub_t * dhd,uint8 prio)4354 int dhd_wlfc_save_rxpath_ac_time(dhd_pub_t * dhd, uint8 prio)
4355 {
4356 athost_wl_status_info_t* wlfc;
4357 int rx_path_ac = -1;
4358
4359 if ((dhd == NULL) || (prio >= NUMPRIO)) {
4360 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4361 return BCME_BADARG;
4362 }
4363
4364 dhd_os_wlfc_block(dhd);
4365
4366 if (!dhd->wlfc_rxpkt_chk) {
4367 dhd_os_wlfc_unblock(dhd);
4368 return BCME_OK;
4369 }
4370
4371 if (!dhd->wlfc_state || (dhd->proptxstatus_mode == WLFC_FCMODE_NONE)) {
4372 dhd_os_wlfc_unblock(dhd);
4373 return WLFC_UNSUPPORTED;
4374 }
4375
4376 wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
4377
4378 rx_path_ac = prio2fifo[prio];
4379 wlfc->rx_timestamp[rx_path_ac] = OSL_SYSUPTIME();
4380
4381 dhd_os_wlfc_unblock(dhd);
4382
4383 return BCME_OK;
4384 }
4385
4386 /** called via an IOVAR */
dhd_wlfc_get_module_ignore(dhd_pub_t * dhd,int * val)4387 int dhd_wlfc_get_module_ignore(dhd_pub_t *dhd, int *val)
4388 {
4389 if (!dhd || !val) {
4390 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4391 return BCME_BADARG;
4392 }
4393
4394 dhd_os_wlfc_block(dhd);
4395
4396 *val = dhd->proptxstatus_module_ignore;
4397
4398 dhd_os_wlfc_unblock(dhd);
4399
4400 return BCME_OK;
4401 }
4402
4403 /** called via an IOVAR */
dhd_wlfc_set_module_ignore(dhd_pub_t * dhd,int val)4404 int dhd_wlfc_set_module_ignore(dhd_pub_t *dhd, int val)
4405 {
4406 uint32 tlv = 0;
4407 bool bChanged = FALSE;
4408
4409 if (!dhd) {
4410 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4411 return BCME_BADARG;
4412 }
4413
4414 dhd_os_wlfc_block(dhd);
4415
4416 if ((bool)val != dhd->proptxstatus_module_ignore) {
4417 dhd->proptxstatus_module_ignore = (val != 0);
4418 /* force txstatus_ignore sync with proptxstatus_module_ignore */
4419 dhd->proptxstatus_txstatus_ignore = dhd->proptxstatus_module_ignore;
4420 if (FALSE == dhd->proptxstatus_module_ignore) {
4421 tlv = WLFC_FLAGS_RSSI_SIGNALS |
4422 WLFC_FLAGS_XONXOFF_SIGNALS |
4423 WLFC_FLAGS_CREDIT_STATUS_SIGNALS |
4424 WLFC_FLAGS_HOST_PROPTXSTATUS_ACTIVE;
4425 }
4426 /* always enable host reorder */
4427 tlv |= WLFC_FLAGS_HOST_RXRERODER_ACTIVE;
4428 bChanged = TRUE;
4429 }
4430
4431 dhd_os_wlfc_unblock(dhd);
4432
4433 if (bChanged) {
4434 /* select enable proptxtstatus signaling */
4435 if (dhd_wl_ioctl_set_intiovar(dhd, "tlv", tlv, WLC_SET_VAR, TRUE, 0)) {
4436 DHD_ERROR(("%s: failed to set bdcv2 tlv signaling to 0x%x\n",
4437 __FUNCTION__, tlv));
4438 } else {
4439 DHD_ERROR(("%s: successfully set bdcv2 tlv signaling to 0x%x\n",
4440 __FUNCTION__, tlv));
4441 }
4442 }
4443
4444 #if defined(DHD_WLFC_THREAD)
4445 _dhd_wlfc_thread_wakeup(dhd);
4446 #endif /* defined(DHD_WLFC_THREAD) */
4447
4448 return BCME_OK;
4449 }
4450
4451 /** called via an IOVAR */
dhd_wlfc_get_credit_ignore(dhd_pub_t * dhd,int * val)4452 int dhd_wlfc_get_credit_ignore(dhd_pub_t *dhd, int *val)
4453 {
4454 if (!dhd || !val) {
4455 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4456 return BCME_BADARG;
4457 }
4458
4459 dhd_os_wlfc_block(dhd);
4460
4461 *val = dhd->proptxstatus_credit_ignore;
4462
4463 dhd_os_wlfc_unblock(dhd);
4464
4465 return BCME_OK;
4466 }
4467
4468 /** called via an IOVAR */
dhd_wlfc_set_credit_ignore(dhd_pub_t * dhd,int val)4469 int dhd_wlfc_set_credit_ignore(dhd_pub_t *dhd, int val)
4470 {
4471 if (!dhd) {
4472 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4473 return BCME_BADARG;
4474 }
4475
4476 dhd_os_wlfc_block(dhd);
4477
4478 dhd->proptxstatus_credit_ignore = (val != 0);
4479
4480 dhd_os_wlfc_unblock(dhd);
4481
4482 return BCME_OK;
4483 }
4484
4485 /** called via an IOVAR */
dhd_wlfc_get_txstatus_ignore(dhd_pub_t * dhd,int * val)4486 int dhd_wlfc_get_txstatus_ignore(dhd_pub_t *dhd, int *val)
4487 {
4488 if (!dhd || !val) {
4489 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4490 return BCME_BADARG;
4491 }
4492
4493 dhd_os_wlfc_block(dhd);
4494
4495 *val = dhd->proptxstatus_txstatus_ignore;
4496
4497 dhd_os_wlfc_unblock(dhd);
4498
4499 return BCME_OK;
4500 }
4501
4502 /** called via an IOVAR */
dhd_wlfc_set_txstatus_ignore(dhd_pub_t * dhd,int val)4503 int dhd_wlfc_set_txstatus_ignore(dhd_pub_t *dhd, int val)
4504 {
4505 if (!dhd) {
4506 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4507 return BCME_BADARG;
4508 }
4509
4510 dhd_os_wlfc_block(dhd);
4511
4512 dhd->proptxstatus_txstatus_ignore = (val != 0);
4513
4514 dhd_os_wlfc_unblock(dhd);
4515
4516 return BCME_OK;
4517 }
4518
4519 /** called via an IOVAR */
dhd_wlfc_get_rxpkt_chk(dhd_pub_t * dhd,int * val)4520 int dhd_wlfc_get_rxpkt_chk(dhd_pub_t *dhd, int *val)
4521 {
4522 if (!dhd || !val) {
4523 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4524 return BCME_BADARG;
4525 }
4526
4527 dhd_os_wlfc_block(dhd);
4528
4529 *val = dhd->wlfc_rxpkt_chk;
4530
4531 dhd_os_wlfc_unblock(dhd);
4532
4533 return BCME_OK;
4534 }
4535
4536 /** called via an IOVAR */
dhd_wlfc_set_rxpkt_chk(dhd_pub_t * dhd,int val)4537 int dhd_wlfc_set_rxpkt_chk(dhd_pub_t *dhd, int val)
4538 {
4539 if (!dhd) {
4540 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4541 return BCME_BADARG;
4542 }
4543
4544 dhd_os_wlfc_block(dhd);
4545
4546 dhd->wlfc_rxpkt_chk = (val != 0);
4547
4548 dhd_os_wlfc_unblock(dhd);
4549
4550 return BCME_OK;
4551 }
4552
4553 #endif /* PROP_TXSTATUS */
4554