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