2 * DHD PROP_TXSTATUS Module.
4 * Copyright (C) 1999-2017, Broadcom Corporation
6 * Unless you and Broadcom execute a separate written software license
7 * agreement governing use of this software, this software is licensed to you
8 * under the terms of the GNU General Public License version 2 (the "GPL"),
9 * available at http://www.broadcom.com/licenses/GPLv2.php, with the
10 * following added to such license:
12 * As a special exception, the copyright holders of this software give you
13 * permission to link this software with independent modules, and to copy and
14 * distribute the resulting executable under terms of your choice, provided that
15 * you also meet, for each linked independent module, the terms and conditions of
16 * the license of that module. An independent module is a module which is not
17 * derived from this software. The special exception does not apply to any
18 * modifications of the software.
20 * Notwithstanding the above, under no circumstances may you combine this
21 * software in any way with any other Broadcom software provided under a license
22 * other than the GPL, without Broadcom's express prior written consent.
25 * <<Broadcom-WL-IPTag/Open:>>
27 * $Id: dhd_wlfc.c 679733 2017-01-17 06:40:39Z $
36 #include <bcmendian.h>
38 #include <dngl_stats.h>
44 #include <dhd_config.h>
46 #ifdef PROP_TXSTATUS /* a form of flow control between host and dongle */
47 #include <wlfc_proto.h>
51 #ifdef DHDTCPACK_SUPPRESS
53 #endif /* DHDTCPACK_SUPPRESS */
57 * wlfc naming and lock rules:
59 * 1. Private functions name like _dhd_wlfc_XXX, declared as static and avoid wlfc lock operation.
60 * 2. Public functions name like dhd_wlfc_XXX, use wlfc lock if needed.
61 * 3. Non-Proptxstatus module call public functions only and avoid wlfc lock operation.
65 #if defined(DHD_WLFC_THREAD)
66 #define WLFC_THREAD_QUICK_RETRY_WAIT_MS 10 /* 10 msec */
67 #define WLFC_THREAD_RETRY_WAIT_MS 10000 /* 10 sec */
68 #endif /* defined (DHD_WLFC_THREAD) */
73 #define DHD_WLFC_QMON_COMPLETE(entry)
76 /** reordering related */
78 #if defined(DHD_WLFC_THREAD)
80 _dhd_wlfc_thread_wakeup(dhd_pub_t
*dhdp
)
82 dhdp
->wlfc_thread_go
= TRUE
;
83 wake_up_interruptible(&dhdp
->wlfc_wqhead
);
85 #endif /* DHD_WLFC_THREAD */
88 _dhd_wlfc_adjusted_seq(void* p
, uint8 current_seq
)
96 seq
= WL_TXSTATUS_GET_FREERUNCTR(DHD_PKTTAG_H2DTAG(PKTTAG(p
)));
97 if (seq
< current_seq
) {
106 * Enqueue a caller supplied packet on a caller supplied precedence queue, optionally reorder
107 * suppressed packets.
108 * @param[in] pq caller supplied packet queue to enqueue the packet on
109 * @param[in] prec precedence of the to-be-queued packet
110 * @param[in] p transmit packet to enqueue
111 * @param[in] qHead if TRUE, enqueue to head instead of tail. Used to maintain d11 seq order.
112 * @param[in] current_seq
113 * @param[in] reOrder reOrder on odd precedence (=suppress queue)
116 _dhd_wlfc_prec_enque(struct pktq
*pq
, int prec
, void* p
, bool qHead
,
117 uint8 current_seq
, bool reOrder
)
126 ASSERT(prec
>= 0 && prec
< pq
->num_prec
);
127 ASSERT(PKTLINK(p
) == NULL
); /* queueing chains not allowed */
129 ASSERT(!pktq_full(pq
));
130 ASSERT(!pktq_pfull(pq
, prec
));
135 if (q
->head
== NULL
) {
140 if (reOrder
&& (prec
& 1)) {
141 seq
= _dhd_wlfc_adjusted_seq(p
, current_seq
);
142 p2
= qHead
? q
->head
: q
->tail
;
143 seq2
= _dhd_wlfc_adjusted_seq(p2
, current_seq
);
145 if ((qHead
&&((seq
+1) > seq2
)) || (!qHead
&& ((seq2
+1) > seq
))) {
149 seq2
= _dhd_wlfc_adjusted_seq(p2
, current_seq
);
157 seq2
= _dhd_wlfc_adjusted_seq(p2
, current_seq
);
160 if (p2_prev
== NULL
) {
162 PKTSETLINK(p
, q
->head
);
164 } else if (p2
== NULL
) {
166 PKTSETLINK(p2_prev
, p
);
169 /* insert after p2_prev */
170 PKTSETLINK(p
, PKTLINK(p2_prev
));
171 PKTSETLINK(p2_prev
, p
);
178 PKTSETLINK(p
, q
->head
);
181 PKTSETLINK(q
->tail
, p
);
191 if (pq
->hi_prec
< prec
)
192 pq
->hi_prec
= (uint8
)prec
;
193 } /* _dhd_wlfc_prec_enque */
196 * Create a place to store all packet pointers submitted to the firmware until a status comes back,
197 * suppress or otherwise.
199 * hang-er: noun, a contrivance on which things are hung, as a hook.
201 /** @deprecated soon */
203 _dhd_wlfc_hanger_create(dhd_pub_t
*dhd
, int max_items
)
206 wlfc_hanger_t
* hanger
;
208 /* allow only up to a specific size for now */
209 ASSERT(max_items
== WLFC_HANGER_MAXITEMS
);
211 if ((hanger
= (wlfc_hanger_t
*)DHD_OS_PREALLOC(dhd
, DHD_PREALLOC_DHD_WLFC_HANGER
,
212 WLFC_HANGER_SIZE(max_items
))) == NULL
) {
215 memset(hanger
, 0, WLFC_HANGER_SIZE(max_items
));
216 hanger
->max_items
= max_items
;
218 for (i
= 0; i
< hanger
->max_items
; i
++) {
219 hanger
->items
[i
].state
= WLFC_HANGER_ITEM_STATE_FREE
;
224 /** @deprecated soon */
226 _dhd_wlfc_hanger_delete(dhd_pub_t
*dhd
, void* hanger
)
228 wlfc_hanger_t
* h
= (wlfc_hanger_t
*)hanger
;
231 DHD_OS_PREFREE(dhd
, h
, WLFC_HANGER_SIZE(h
->max_items
));
237 /** @deprecated soon */
239 _dhd_wlfc_hanger_get_free_slot(void* hanger
)
242 wlfc_hanger_t
* h
= (wlfc_hanger_t
*)hanger
;
246 if (i
== h
->max_items
) {
249 while (i
!= h
->slot_pos
) {
250 if (h
->items
[i
].state
== WLFC_HANGER_ITEM_STATE_FREE
) {
255 if (i
== h
->max_items
)
258 h
->failed_slotfind
++;
260 return WLFC_HANGER_MAXITEMS
;
263 /** @deprecated soon */
265 _dhd_wlfc_hanger_get_genbit(void* hanger
, void* pkt
, uint32 slot_id
, int* gen
)
268 wlfc_hanger_t
* h
= (wlfc_hanger_t
*)hanger
;
272 /* this packet was not pushed at the time it went to the firmware */
273 if (slot_id
== WLFC_HANGER_MAXITEMS
)
274 return BCME_NOTFOUND
;
277 if (h
->items
[slot_id
].state
!= WLFC_HANGER_ITEM_STATE_FREE
) {
278 *gen
= h
->items
[slot_id
].gen
;
281 DHD_ERROR(("Error: %s():%d item not used\n",
282 __FUNCTION__
, __LINE__
));
293 /** @deprecated soon */
295 _dhd_wlfc_hanger_pushpkt(void* hanger
, void* pkt
, uint32 slot_id
)
298 wlfc_hanger_t
* h
= (wlfc_hanger_t
*)hanger
;
300 if (h
&& (slot_id
< WLFC_HANGER_MAXITEMS
)) {
301 if (h
->items
[slot_id
].state
== WLFC_HANGER_ITEM_STATE_FREE
) {
302 h
->items
[slot_id
].state
= WLFC_HANGER_ITEM_STATE_INUSE
;
303 h
->items
[slot_id
].pkt
= pkt
;
304 h
->items
[slot_id
].pkt_state
= 0;
305 h
->items
[slot_id
].pkt_txstatus
= 0;
318 /** @deprecated soon */
320 _dhd_wlfc_hanger_poppkt(void* hanger
, uint32 slot_id
, void** pktout
, bool remove_from_hanger
)
323 wlfc_hanger_t
* h
= (wlfc_hanger_t
*)hanger
;
327 /* this packet was not pushed at the time it went to the firmware */
328 if (slot_id
== WLFC_HANGER_MAXITEMS
)
329 return BCME_NOTFOUND
;
332 if (h
->items
[slot_id
].state
!= WLFC_HANGER_ITEM_STATE_FREE
) {
333 *pktout
= h
->items
[slot_id
].pkt
;
334 if (remove_from_hanger
) {
335 h
->items
[slot_id
].state
=
336 WLFC_HANGER_ITEM_STATE_FREE
;
337 h
->items
[slot_id
].pkt
= NULL
;
338 h
->items
[slot_id
].gen
= 0xff;
339 h
->items
[slot_id
].identifier
= 0;
353 /** @deprecated soon */
355 _dhd_wlfc_hanger_mark_suppressed(void* hanger
, uint32 slot_id
, uint8 gen
)
358 wlfc_hanger_t
* h
= (wlfc_hanger_t
*)hanger
;
360 /* this packet was not pushed at the time it went to the firmware */
361 if (slot_id
== WLFC_HANGER_MAXITEMS
)
362 return BCME_NOTFOUND
;
364 h
->items
[slot_id
].gen
= gen
;
365 if (h
->items
[slot_id
].state
== WLFC_HANGER_ITEM_STATE_INUSE
) {
366 h
->items
[slot_id
].state
= WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED
;
377 /** remove reference of specific packet in hanger */
378 /** @deprecated soon */
380 _dhd_wlfc_hanger_remove_reference(wlfc_hanger_t
* h
, void* pkt
)
388 i
= WL_TXSTATUS_GET_HSLOT(DHD_PKTTAG_H2DTAG(PKTTAG(pkt
)));
390 if ((i
< h
->max_items
) && (pkt
== h
->items
[i
].pkt
)) {
391 if (h
->items
[i
].state
== WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED
) {
392 h
->items
[i
].state
= WLFC_HANGER_ITEM_STATE_FREE
;
393 h
->items
[i
].pkt
= NULL
;
394 h
->items
[i
].gen
= 0xff;
395 h
->items
[i
].identifier
= 0;
398 DHD_ERROR(("Error: %s():%d item not suppressed\n",
399 __FUNCTION__
, __LINE__
));
406 /** afq = At Firmware Queue, queue containing packets pending in the dongle */
408 _dhd_wlfc_enque_afq(athost_wl_status_info_t
* ctx
, void *p
)
410 wlfc_mac_descriptor_t
* entry
;
411 uint16 entry_idx
= WL_TXSTATUS_GET_HSLOT(DHD_PKTTAG_H2DTAG(PKTTAG(p
)));
412 uint8 prec
= DHD_PKTTAG_FIFO(PKTTAG(p
));
414 if (entry_idx
< WLFC_MAC_DESC_TABLE_SIZE
)
415 entry
= &ctx
->destination_entries
.nodes
[entry_idx
];
416 else if (entry_idx
< (WLFC_MAC_DESC_TABLE_SIZE
+ WLFC_MAX_IFNUM
))
417 entry
= &ctx
->destination_entries
.interfaces
[entry_idx
- WLFC_MAC_DESC_TABLE_SIZE
];
419 entry
= &ctx
->destination_entries
.other
;
421 pktq_penq(&entry
->afq
, prec
, p
);
426 /** afq = At Firmware Queue, queue containing packets pending in the dongle */
428 _dhd_wlfc_deque_afq(athost_wl_status_info_t
* ctx
, uint16 hslot
, uint8 hcnt
, uint8 prec
,
431 wlfc_mac_descriptor_t
*entry
;
437 DHD_ERROR(("%s: ctx(%p), pktout(%p)\n", __FUNCTION__
, ctx
, pktout
));
445 ASSERT(hslot
< (WLFC_MAC_DESC_TABLE_SIZE
+ WLFC_MAX_IFNUM
+ 1));
447 if (hslot
< WLFC_MAC_DESC_TABLE_SIZE
)
448 entry
= &ctx
->destination_entries
.nodes
[hslot
];
449 else if (hslot
< (WLFC_MAC_DESC_TABLE_SIZE
+ WLFC_MAX_IFNUM
))
450 entry
= &ctx
->destination_entries
.interfaces
[hslot
- WLFC_MAC_DESC_TABLE_SIZE
];
452 entry
= &ctx
->destination_entries
.other
;
456 ASSERT(prec
< pq
->num_prec
);
463 while (p
&& (hcnt
!= WL_TXSTATUS_GET_FREERUNCTR(DHD_PKTTAG_H2DTAG(PKTTAG(p
)))))
470 /* none is matched */
472 DHD_ERROR(("%s: can't find matching seq(%d)\n", __FUNCTION__
, hcnt
));
474 DHD_ERROR(("%s: queue is empty\n", __FUNCTION__
));
480 bcm_pkt_validate_chk(p
);
483 /* head packet is matched */
484 if ((q
->head
= PKTLINK(p
)) == NULL
) {
488 /* middle packet is matched */
489 DHD_INFO(("%s: out of order, seq(%d), head_seq(%d)\n", __FUNCTION__
, hcnt
,
490 WL_TXSTATUS_GET_FREERUNCTR(DHD_PKTTAG_H2DTAG(PKTTAG(q
->head
)))));
491 ctx
->stats
.ooo_pkts
[prec
]++;
492 PKTSETLINK(b
, PKTLINK(p
));
493 if (PKTLINK(p
) == NULL
) {
508 } /* _dhd_wlfc_deque_afq */
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.
515 * @param[in/out] packet
516 * @param[in] tim_signal TRUE if parameter 'tim_bmp' is valid
518 * @param[in] mac_handle
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
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
)
528 uint32 wl_pktinfo
= 0;
530 uint8 dataOffset
= 0;
532 uint8 tim_signal_len
= 0;
533 dhd_pub_t
*dhdp
= (dhd_pub_t
*)ctx
->dhdp
;
535 struct bdc_header
*h
;
542 tim_signal_len
= TLV_HDR_LEN
+ WLFC_CTL_VALUE_LEN_PENDING_TRAFFIC_BMP
;
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
;
551 fillers
= ROUNDUP(dataOffset
, 4) - dataOffset
;
552 dataOffset
+= fillers
;
554 PKTPUSH(ctx
->osh
, p
, dataOffset
);
555 wlh
= (uint8
*) PKTDATA(ctx
->osh
, p
);
557 wl_pktinfo
= htol32(htodtag
);
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
));
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
);
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
;
579 memset(&wlh
[dataOffset
- fillers
], WLFC_CTL_TYPE_FILLER
, fillers
);
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
);
586 h
->flags
|= BDC_FLAG_SUM_NEEDED
;
589 h
->priority
= (PKTPRIO(p
) & BDC_PRIORITY_MASK
);
591 h
->dataOffset
= dataOffset
>> 2;
592 BDC_SET_IF_IDX(h
, DHD_PKTTAG_IF(PKTTAG(p
)));
595 } /* _dhd_wlfc_pushheader */
598 * Removes (PULLs) flow control related headers from the caller supplied packet, is invoked eg
599 * when a packet is about to be freed.
602 _dhd_wlfc_pullheader(athost_wl_status_info_t
* ctx
, void* pktbuf
)
604 struct bdc_header
*h
;
606 if (PKTLEN(ctx
->osh
, pktbuf
) < BDC_HEADER_LEN
) {
607 DHD_ERROR(("%s: rx data too short (%d < %d)\n", __FUNCTION__
,
608 PKTLEN(ctx
->osh
, pktbuf
), BDC_HEADER_LEN
));
611 h
= (struct bdc_header
*)PKTDATA(ctx
->osh
, pktbuf
);
613 /* pull BDC header */
614 PKTPULL(ctx
->osh
, pktbuf
, BDC_HEADER_LEN
);
616 if (PKTLEN(ctx
->osh
, pktbuf
) < (uint
)(h
->dataOffset
<< 2)) {
617 DHD_ERROR(("%s: rx data too short (%d < %d)\n", __FUNCTION__
,
618 PKTLEN(ctx
->osh
, pktbuf
), (h
->dataOffset
<< 2)));
623 PKTPULL(ctx
->osh
, pktbuf
, (h
->dataOffset
<< 2));
628 * @param[in/out] p packet
630 static wlfc_mac_descriptor_t
*
631 _dhd_wlfc_find_table_entry(athost_wl_status_info_t
* ctx
, void* p
)
634 wlfc_mac_descriptor_t
* table
= ctx
->destination_entries
.nodes
;
635 uint8 ifid
= DHD_PKTTAG_IF(PKTTAG(p
));
636 uint8
* dstn
= DHD_PKTTAG_DSTN(PKTTAG(p
));
637 wlfc_mac_descriptor_t
* entry
= DHD_PKTTAG_ENTRY(PKTTAG(p
));
638 int iftype
= ctx
->destination_entries
.interfaces
[ifid
].iftype
;
640 /* saved one exists, return it */
644 /* Multicast destination, STA and P2P clients get the interface entry.
645 * STA/GC gets the Mac Entry for TDLS destinations, TDLS destinations
646 * have their own entry.
648 if ((iftype
== WLC_E_IF_ROLE_STA
|| ETHER_ISMULTI(dstn
) ||
649 iftype
== WLC_E_IF_ROLE_P2P_CLIENT
) &&
650 (ctx
->destination_entries
.interfaces
[ifid
].occupied
)) {
651 entry
= &ctx
->destination_entries
.interfaces
[ifid
];
654 if (entry
&& ETHER_ISMULTI(dstn
)) {
655 DHD_PKTTAG_SET_ENTRY(PKTTAG(p
), entry
);
659 for (i
= 0; i
< WLFC_MAC_DESC_TABLE_SIZE
; i
++) {
660 if (table
[i
].occupied
) {
661 if (table
[i
].interface_id
== ifid
) {
662 if (!memcmp(table
[i
].ea
, dstn
, ETHER_ADDR_LEN
)) {
671 entry
= &ctx
->destination_entries
.other
;
673 DHD_PKTTAG_SET_ENTRY(PKTTAG(p
), entry
);
676 } /* _dhd_wlfc_find_table_entry */
679 * In case a packet must be dropped (because eg the queues are full), various tallies have to be
680 * be updated. Called from several other functions.
681 * @param[in] dhdp pointer to public DHD structure
682 * @param[in] prec precedence of the packet
683 * @param[in] p the packet to be dropped
684 * @param[in] bPktInQ TRUE if packet is part of a queue
687 _dhd_wlfc_prec_drop(dhd_pub_t
*dhdp
, int prec
, void* p
, bool bPktInQ
)
689 athost_wl_status_info_t
* ctx
;
693 ASSERT(prec
>= 0 && prec
<= WLFC_PSQ_PREC_COUNT
);
695 ctx
= (athost_wl_status_info_t
*)dhdp
->wlfc_state
;
697 if (!WLFC_GET_AFQ(dhdp
->wlfc_mode
) && (prec
& 1)) {
698 /* suppressed queue, need pop from hanger */
699 _dhd_wlfc_hanger_poppkt(ctx
->hanger
, WL_TXSTATUS_GET_HSLOT(DHD_PKTTAG_H2DTAG
700 (PKTTAG(p
))), &pout
, TRUE
);
705 #ifdef DHDTCPACK_SUPPRESS
706 /* pkt in delayed q, so fake push BDC header for
707 * dhd_tcpack_check_xmit() and dhd_txcomplete().
709 _dhd_wlfc_pushheader(ctx
, &p
, FALSE
, 0, 0, 0, 0, TRUE
);
711 /* This packet is about to be freed, so remove it from tcp_ack_info_tbl
712 * This must be one of...
713 * 1. A pkt already in delayQ is evicted by another pkt with higher precedence
714 * in _dhd_wlfc_prec_enq_with_drop()
715 * 2. A pkt could not be enqueued to delayQ because it is full,
716 * in _dhd_wlfc_enque_delayq().
717 * 3. A pkt could not be enqueued to delayQ because it is full,
718 * in _dhd_wlfc_rollback_packet_toq().
720 if (dhd_tcpack_check_xmit(dhdp
, p
) == BCME_ERROR
) {
721 DHD_ERROR(("%s %d: tcpack_suppress ERROR!!!"
723 __FUNCTION__
, __LINE__
));
724 dhd_tcpack_suppress_set(dhdp
, TCPACK_SUP_OFF
);
726 #endif /* DHDTCPACK_SUPPRESS */
730 ctx
->pkt_cnt_in_q
[DHD_PKTTAG_IF(PKTTAG(p
))][prec
>>1]--;
731 ctx
->pkt_cnt_per_ac
[prec
>>1]--;
732 ctx
->pkt_cnt_in_psq
--;
735 ctx
->pkt_cnt_in_drv
[DHD_PKTTAG_IF(PKTTAG(p
))][DHD_PKTTAG_FIFO(PKTTAG(p
))]--;
737 ctx
->stats
.drop_pkts
[prec
]++;
739 dhd_txcomplete(dhdp
, p
, FALSE
);
740 PKTFREE(ctx
->osh
, p
, TRUE
);
743 } /* _dhd_wlfc_prec_drop */
746 * Called when eg the host handed a new packet over to the driver, or when the dongle reported
747 * that a packet could currently not be transmitted (=suppressed). This function enqueues a transmit
748 * packet in the host driver to be (re)transmitted at a later opportunity.
749 * @param[in] dhdp pointer to public DHD structure
750 * @param[in] qHead When TRUE, queue packet at head instead of tail, to preserve d11 sequence
753 _dhd_wlfc_prec_enq_with_drop(dhd_pub_t
*dhdp
, struct pktq
*pq
, void *pkt
, int prec
, bool qHead
,
757 int eprec
= -1; /* precedence to evict from */
758 athost_wl_status_info_t
* ctx
;
760 ASSERT(dhdp
&& pq
&& pkt
);
761 ASSERT(prec
>= 0 && prec
< pq
->num_prec
);
763 ctx
= (athost_wl_status_info_t
*)dhdp
->wlfc_state
;
765 /* Fast case, precedence queue is not full and we are also not
766 * exceeding total queue length
768 if (!pktq_pfull(pq
, prec
) && !pktq_full(pq
)) {
772 /* Determine precedence from which to evict packet, if any */
773 if (pktq_pfull(pq
, prec
)) {
775 } else if (pktq_full(pq
)) {
776 p
= pktq_peek_tail(pq
, &eprec
);
778 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__
, __LINE__
));
781 if ((eprec
> prec
) || (eprec
< 0)) {
782 if (!pktq_pempty(pq
, prec
)) {
790 /* Evict if needed */
792 /* Detect queueing to unconfigured precedence */
793 ASSERT(!pktq_pempty(pq
, eprec
));
794 /* Evict all fragmented frames */
795 dhd_prec_drop_pkts(dhdp
, pq
, eprec
, _dhd_wlfc_prec_drop
);
800 _dhd_wlfc_prec_enque(pq
, prec
, pkt
, qHead
, current_seq
,
801 WLFC_GET_REORDERSUPP(dhdp
->wlfc_mode
));
802 ctx
->pkt_cnt_in_q
[DHD_PKTTAG_IF(PKTTAG(pkt
))][prec
>>1]++;
803 ctx
->pkt_cnt_per_ac
[prec
>>1]++;
804 ctx
->pkt_cnt_in_psq
++;
807 } /* _dhd_wlfc_prec_enq_with_drop */
810 * Called during eg the 'committing' of a transmit packet from the OS layer to a lower layer, in
811 * the event that this 'commit' failed.
814 _dhd_wlfc_rollback_packet_toq(athost_wl_status_info_t
* ctx
,
815 void* p
, ewlfc_packet_state_t pkt_type
, uint32 hslot
)
818 * put the packet back to the head of queue
819 * - suppressed packet goes back to suppress sub-queue
820 * - pull out the header, if new or delayed packet
822 * Note: hslot is used only when header removal is done.
824 wlfc_mac_descriptor_t
* entry
;
828 entry
= _dhd_wlfc_find_table_entry(ctx
, p
);
829 prec
= DHD_PKTTAG_FIFO(PKTTAG(p
));
831 if (pkt_type
== eWLFC_PKTTYPE_SUPPRESSED
)
835 if this packet did not count against FIFO credit, it must have
836 taken a requested_credit from the firmware (for pspoll etc.)
838 if ((prec
!= AC_COUNT
) && !DHD_PKTTAG_CREDITCHECK(PKTTAG(p
)))
839 entry
->requested_credit
++;
841 if (pkt_type
== eWLFC_PKTTYPE_DELAYED
) {
842 /* decrement sequence count */
843 WLFC_DECR_SEQCOUNT(entry
, prec
);
844 /* remove header first */
845 rc
= _dhd_wlfc_pullheader(ctx
, p
);
847 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__
, __LINE__
));
852 if (_dhd_wlfc_prec_enq_with_drop(ctx
->dhdp
, &entry
->psq
, p
, fifo_id
, TRUE
,
853 WLFC_SEQCOUNT(entry
, fifo_id
>>1))
856 DHD_ERROR(("Error: %s():%d, fifo_id(%d)\n",
857 __FUNCTION__
, __LINE__
, fifo_id
));
861 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__
, __LINE__
));
867 ctx
->stats
.rollback_failed
++;
868 _dhd_wlfc_prec_drop(ctx
->dhdp
, fifo_id
, p
, FALSE
);
870 ctx
->stats
.rollback
++;
874 } /* _dhd_wlfc_rollback_packet_toq */
876 /** Returns TRUE if host OS -> DHD flow control is allowed on the caller supplied interface */
878 _dhd_wlfc_allow_fc(athost_wl_status_info_t
* ctx
, uint8 ifid
)
880 int prec
, ac_traffic
= WLFC_NO_TRAFFIC
;
882 for (prec
= 0; prec
< AC_COUNT
; prec
++) {
883 if (ctx
->pkt_cnt_in_drv
[ifid
][prec
] > 0) {
884 if (ac_traffic
== WLFC_NO_TRAFFIC
)
885 ac_traffic
= prec
+ 1;
886 else if (ac_traffic
!= (prec
+ 1))
887 ac_traffic
= WLFC_MULTI_TRAFFIC
;
891 if (ac_traffic
>= 1 && ac_traffic
<= AC_COUNT
) {
892 /* single AC (BE/BK/VI/VO) in queue */
897 uint32 curr_t
= OSL_SYSUPTIME();
899 if (ctx
->fc_defer_timestamp
== 0) {
900 /* first single ac scenario */
901 ctx
->fc_defer_timestamp
= curr_t
;
905 /* single AC duration, this handles wrap around, e.g. 1 - ~0 = 2. */
906 delta
= curr_t
- ctx
->fc_defer_timestamp
;
907 if (delta
>= WLFC_FC_DEFER_PERIOD_MS
) {
908 ctx
->allow_fc
= TRUE
;
912 /* multiple ACs or BCMC in queue */
913 ctx
->allow_fc
= FALSE
;
914 ctx
->fc_defer_timestamp
= 0;
917 return ctx
->allow_fc
;
918 } /* _dhd_wlfc_allow_fc */
921 * Starts or stops the flow of transmit packets from the host OS towards the DHD, depending on
922 * low/high watermarks.
925 _dhd_wlfc_flow_control_check(athost_wl_status_info_t
* ctx
, struct pktq
* pq
, uint8 if_id
)
931 dhdp
= (dhd_pub_t
*)ctx
->dhdp
;
934 if (dhdp
->skip_fc
&& dhdp
->skip_fc((void *)dhdp
, if_id
))
937 if ((ctx
->hostif_flow_state
[if_id
] == OFF
) && !_dhd_wlfc_allow_fc(ctx
, if_id
))
940 if ((pq
->len
<= WLFC_FLOWCONTROL_LOWATER
) && (ctx
->hostif_flow_state
[if_id
] == ON
)) {
942 ctx
->hostif_flow_state
[if_id
] = OFF
;
944 WLFC_DBGMESG(("qlen:%02d, if:%02d, ->OFF, start traffic %s()\n",
945 pq->len, if_id, __FUNCTION__));
949 dhd_txflowcontrol(dhdp
, if_id
, OFF
);
951 ctx
->toggle_host_if
= 0;
954 if ((pq
->len
>= WLFC_FLOWCONTROL_HIWATER
) && (ctx
->hostif_flow_state
[if_id
] == OFF
)) {
956 ctx
->hostif_flow_state
[if_id
] = ON
;
958 WLFC_DBGMESG(("qlen:%02d, if:%02d, ->ON, stop traffic %s()\n",
959 pq->len, if_id, __FUNCTION__));
963 dhd_txflowcontrol(dhdp
, if_id
, ON
);
965 ctx
->host_ifidx
= if_id
;
966 ctx
->toggle_host_if
= 1;
970 } /* _dhd_wlfc_flow_control_check */
973 _dhd_wlfc_send_signalonly_packet(athost_wl_status_info_t
* ctx
, wlfc_mac_descriptor_t
* entry
,
978 int dummylen
= ((dhd_pub_t
*)ctx
->dhdp
)->hdrlen
+ 16;
979 dhd_pub_t
*dhdp
= (dhd_pub_t
*)ctx
->dhdp
;
981 if (dhdp
->proptxstatus_txoff
) {
982 rc
= BCME_NORESOURCE
;
986 /* allocate a dummy packet */
987 p
= PKTGET(ctx
->osh
, dummylen
, TRUE
);
989 PKTPULL(ctx
->osh
, p
, dummylen
);
990 DHD_PKTTAG_SET_H2DTAG(PKTTAG(p
), 0);
991 _dhd_wlfc_pushheader(ctx
, &p
, TRUE
, ta_bmp
, entry
->mac_handle
, 0, 0, FALSE
);
992 DHD_PKTTAG_SETSIGNALONLY(PKTTAG(p
), 1);
993 DHD_PKTTAG_WLFCPKT_SET(PKTTAG(p
), 1);
994 #ifdef PROP_TXSTATUS_DEBUG
995 ctx
->stats
.signal_only_pkts_sent
++;
999 rc
= dhd_bus_txdata(dhdp
->bus
, p
, ctx
->host_ifidx
);
1001 rc
= dhd_bus_txdata(dhdp
->bus
, p
);
1003 if (rc
!= BCME_OK
) {
1004 _dhd_wlfc_pullheader(ctx
, p
);
1005 PKTFREE(ctx
->osh
, p
, TRUE
);
1008 DHD_ERROR(("%s: couldn't allocate new %d-byte packet\n",
1009 __FUNCTION__
, dummylen
));
1011 dhdp
->tx_pktgetfail
++;
1015 } /* _dhd_wlfc_send_signalonly_packet */
1018 * Called on eg receiving 'mac close' indication from dongle. Updates the per-MAC administration
1019 * maintained in caller supplied parameter 'entry'.
1021 * @param[in/out] entry administration about a remote MAC entity
1022 * @param[in] prec precedence queue for this remote MAC entitity
1024 * Return value: TRUE if traffic availability changed
1027 _dhd_wlfc_traffic_pending_check(athost_wl_status_info_t
* ctx
, wlfc_mac_descriptor_t
* entry
,
1032 if (entry
->state
== WLFC_STATE_CLOSE
) {
1033 if ((pktq_plen(&entry
->psq
, (prec
<< 1)) == 0) &&
1034 (pktq_plen(&entry
->psq
, ((prec
<< 1) + 1)) == 0)) {
1035 /* no packets in both 'normal' and 'suspended' queues */
1036 if (entry
->traffic_pending_bmp
& NBITVAL(prec
)) {
1038 entry
->traffic_pending_bmp
=
1039 entry
->traffic_pending_bmp
& ~ NBITVAL(prec
);
1042 /* packets are queued in host for transmission to dongle */
1043 if (!(entry
->traffic_pending_bmp
& NBITVAL(prec
))) {
1045 entry
->traffic_pending_bmp
=
1046 entry
->traffic_pending_bmp
| NBITVAL(prec
);
1052 /* request a TIM update to firmware at the next piggyback opportunity */
1053 if (entry
->traffic_lastreported_bmp
!= entry
->traffic_pending_bmp
) {
1054 entry
->send_tim_signal
= 1;
1055 _dhd_wlfc_send_signalonly_packet(ctx
, entry
, entry
->traffic_pending_bmp
);
1056 entry
->traffic_lastreported_bmp
= entry
->traffic_pending_bmp
;
1057 entry
->send_tim_signal
= 0;
1064 } /* _dhd_wlfc_traffic_pending_check */
1067 * Called on receiving a 'd11 suppressed' or 'wl suppressed' tx status from the firmware. Enqueues
1068 * the packet to transmit to firmware again at a later opportunity.
1071 _dhd_wlfc_enque_suppressed(athost_wl_status_info_t
* ctx
, int prec
, void* p
)
1073 wlfc_mac_descriptor_t
* entry
;
1075 entry
= _dhd_wlfc_find_table_entry(ctx
, p
);
1076 if (entry
== NULL
) {
1077 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__
, __LINE__
));
1078 return BCME_NOTFOUND
;
1081 - suppressed packets go to sub_queue[2*prec + 1] AND
1082 - delayed packets go to sub_queue[2*prec + 0] to ensure
1085 if (_dhd_wlfc_prec_enq_with_drop(ctx
->dhdp
, &entry
->psq
, p
, ((prec
<< 1) + 1), FALSE
,
1086 WLFC_SEQCOUNT(entry
, prec
))
1088 ctx
->stats
.delayq_full_error
++;
1089 /* WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); */
1090 WLFC_DBGMESG(("s"));
1094 /* A packet has been pushed, update traffic availability bitmap, if applicable */
1095 _dhd_wlfc_traffic_pending_check(ctx
, entry
, prec
);
1096 _dhd_wlfc_flow_control_check(ctx
, &entry
->psq
, DHD_PKTTAG_IF(PKTTAG(p
)));
1101 * Called when a transmit packet is about to be 'committed' from the OS layer to a lower layer
1102 * towards the dongle (eg the DBUS layer). Updates wlfc administration. May modify packet.
1104 * @param[in/out] ctx driver specific flow control administration
1105 * @param[in/out] entry The remote MAC entity for which the packet is destined.
1106 * @param[in/out] packet Packet to send. This function optionally adds TLVs to the packet.
1107 * @param[in] header_needed True if packet is 'new' to flow control
1108 * @param[out] slot Handle to container in which the packet was 'parked'
1111 _dhd_wlfc_pretx_pktprocess(athost_wl_status_info_t
* ctx
,
1112 wlfc_mac_descriptor_t
* entry
, void** packet
, int header_needed
, uint32
* slot
)
1115 int hslot
= WLFC_HANGER_MAXITEMS
;
1116 bool send_tim_update
= FALSE
;
1121 dhd_pub_t
*dhdp
= (dhd_pub_t
*)ctx
->dhdp
;
1126 if (entry
== NULL
) {
1127 entry
= _dhd_wlfc_find_table_entry(ctx
, p
);
1130 if (entry
== NULL
) {
1131 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__
, __LINE__
));
1135 if (entry
->send_tim_signal
) {
1136 /* sends a traffic indication bitmap to the dongle */
1137 send_tim_update
= TRUE
;
1138 entry
->send_tim_signal
= 0;
1139 entry
->traffic_lastreported_bmp
= entry
->traffic_pending_bmp
;
1142 if (header_needed
) {
1143 if (WLFC_GET_AFQ(dhdp
->wlfc_mode
)) {
1144 hslot
= (uint
)(entry
- &ctx
->destination_entries
.nodes
[0]);
1146 hslot
= _dhd_wlfc_hanger_get_free_slot(ctx
->hanger
);
1148 gen
= entry
->generation
;
1149 free_ctr
= WLFC_SEQCOUNT(entry
, DHD_PKTTAG_FIFO(PKTTAG(p
)));
1151 if (WLFC_GET_REUSESEQ(dhdp
->wlfc_mode
)) {
1152 htodseq
= DHD_PKTTAG_H2DSEQ(PKTTAG(p
));
1155 hslot
= WL_TXSTATUS_GET_HSLOT(DHD_PKTTAG_H2DTAG(PKTTAG(p
)));
1157 if (WLFC_GET_REORDERSUPP(dhdp
->wlfc_mode
)) {
1158 gen
= entry
->generation
;
1159 } else if (WLFC_GET_AFQ(dhdp
->wlfc_mode
)) {
1160 gen
= WL_TXSTATUS_GET_GENERATION(DHD_PKTTAG_H2DTAG(PKTTAG(p
)));
1162 _dhd_wlfc_hanger_get_genbit(ctx
->hanger
, p
, hslot
, &gen
);
1165 free_ctr
= WL_TXSTATUS_GET_FREERUNCTR(DHD_PKTTAG_H2DTAG(PKTTAG(p
)));
1166 /* remove old header */
1167 _dhd_wlfc_pullheader(ctx
, p
);
1170 if (hslot
>= WLFC_HANGER_MAXITEMS
) {
1171 DHD_ERROR(("Error: %s():no hanger slot available\n", __FUNCTION__
));
1175 WL_TXSTATUS_SET_FREERUNCTR(htod
, free_ctr
);
1176 WL_TXSTATUS_SET_HSLOT(htod
, hslot
);
1177 WL_TXSTATUS_SET_FIFO(htod
, DHD_PKTTAG_FIFO(PKTTAG(p
)));
1178 WL_TXSTATUS_SET_FLAGS(htod
, WLFC_PKTFLAG_PKTFROMHOST
);
1179 WL_TXSTATUS_SET_GENERATION(htod
, gen
);
1180 DHD_PKTTAG_SETPKTDIR(PKTTAG(p
), 1);
1182 if (!DHD_PKTTAG_CREDITCHECK(PKTTAG(p
))) {
1184 Indicate that this packet is being sent in response to an
1185 explicit request from the firmware side.
1187 WLFC_PKTFLAG_SET_PKTREQUESTED(htod
);
1189 WLFC_PKTFLAG_CLR_PKTREQUESTED(htod
);
1192 rc
= _dhd_wlfc_pushheader(ctx
, &p
, send_tim_update
,
1193 entry
->traffic_lastreported_bmp
, entry
->mac_handle
, htod
, htodseq
, FALSE
);
1194 if (rc
== BCME_OK
) {
1195 DHD_PKTTAG_SET_H2DTAG(PKTTAG(p
), htod
);
1197 if (!WLFC_GET_AFQ(dhdp
->wlfc_mode
)) {
1198 wlfc_hanger_t
*h
= (wlfc_hanger_t
*)(ctx
->hanger
);
1199 if (header_needed
) {
1201 a new header was created for this packet.
1202 push to hanger slot and scrub q. Since bus
1203 send succeeded, increment seq number as well.
1205 rc
= _dhd_wlfc_hanger_pushpkt(ctx
->hanger
, p
, hslot
);
1206 if (rc
== BCME_OK
) {
1207 #ifdef PROP_TXSTATUS_DEBUG
1208 h
->items
[hslot
].push_time
=
1212 DHD_ERROR(("%s() hanger_pushpkt() failed, rc: %d\n",
1216 /* clear hanger state */
1217 if (((wlfc_hanger_t
*)(ctx
->hanger
))->items
[hslot
].pkt
!= p
)
1218 DHD_ERROR(("%s() pkt not match: cur %p, hanger pkt %p\n",
1219 __FUNCTION__
, p
, h
->items
[hslot
].pkt
));
1220 ASSERT(h
->items
[hslot
].pkt
== p
);
1221 bcm_object_feature_set(h
->items
[hslot
].pkt
,
1222 BCM_OBJECT_FEATURE_PKT_STATE
, 0);
1223 h
->items
[hslot
].pkt_state
= 0;
1224 h
->items
[hslot
].pkt_txstatus
= 0;
1225 h
->items
[hslot
].state
= WLFC_HANGER_ITEM_STATE_INUSE
;
1229 if ((rc
== BCME_OK
) && header_needed
) {
1230 /* increment free running sequence count */
1231 WLFC_INCR_SEQCOUNT(entry
, DHD_PKTTAG_FIFO(PKTTAG(p
)));
1237 } /* _dhd_wlfc_pretx_pktprocess */
1240 * A remote wireless mac may be temporarily 'closed' due to power management. Returns '1' if remote
1241 * mac is in the 'open' state, otherwise '0'.
1244 _dhd_wlfc_is_destination_open(athost_wl_status_info_t
* ctx
,
1245 wlfc_mac_descriptor_t
* entry
, int prec
)
1247 wlfc_mac_descriptor_t
* interfaces
= ctx
->destination_entries
.interfaces
;
1249 if (entry
->interface_id
>= WLFC_MAX_IFNUM
) {
1250 ASSERT(&ctx
->destination_entries
.other
== entry
);
1254 if (interfaces
[entry
->interface_id
].iftype
==
1255 WLC_E_IF_ROLE_P2P_GO
) {
1256 /* - destination interface is of type p2p GO.
1257 For a p2pGO interface, if the destination is OPEN but the interface is
1258 CLOSEd, do not send traffic. But if the dstn is CLOSEd while there is
1259 destination-specific-credit left send packets. This is because the
1260 firmware storing the destination-specific-requested packet in queue.
1262 if ((entry
->state
== WLFC_STATE_CLOSE
) && (entry
->requested_credit
== 0) &&
1263 (entry
->requested_packet
== 0)) {
1268 /* AP, p2p_go -> unicast desc entry, STA/p2p_cl -> interface desc. entry */
1269 if ((((entry
->state
== WLFC_STATE_CLOSE
) ||
1270 (interfaces
[entry
->interface_id
].state
== WLFC_STATE_CLOSE
)) &&
1271 (entry
->requested_credit
== 0) &&
1272 (entry
->requested_packet
== 0)) ||
1273 (!(entry
->ac_bitmap
& (1 << prec
)))) {
1278 } /* _dhd_wlfc_is_destination_open */
1281 * Dequeues a suppressed or delayed packet from a queue
1282 * @param[in/out] ctx Driver specific flow control administration
1283 * @param[in] prec Precedence of queue to dequeue from
1284 * @param[out] ac_credit_spent Boolean, returns 0 or 1
1285 * @param[out] needs_hdr Boolean, returns 0 or 1
1286 * @param[out] entry_out The remote MAC for which the packet is destined
1287 * @param[in] only_no_credit If TRUE, searches all entries instead of just the active ones
1289 * Return value: the dequeued packet
1292 _dhd_wlfc_deque_delayedq(athost_wl_status_info_t
* ctx
, int prec
,
1293 uint8
* ac_credit_spent
, uint8
* needs_hdr
, wlfc_mac_descriptor_t
** entry_out
,
1294 bool only_no_credit
)
1296 wlfc_mac_descriptor_t
* entry
;
1300 uint8 credit_spent
= ((prec
== AC_COUNT
) && !ctx
->bcmc_credit_supported
) ? 0 : 1;
1303 /* most cases a packet will count against FIFO credit */
1304 *ac_credit_spent
= credit_spent
;
1306 /* search all entries, include nodes as well as interfaces */
1307 if (only_no_credit
) {
1308 total_entries
= ctx
->requested_entry_count
;
1310 total_entries
= ctx
->active_entry_count
;
1313 for (i
= 0; i
< total_entries
; i
++) {
1314 if (only_no_credit
) {
1315 entry
= ctx
->requested_entry
[i
];
1317 entry
= ctx
->active_entry_head
;
1318 /* move head to ensure fair round-robin */
1319 ctx
->active_entry_head
= ctx
->active_entry_head
->next
;
1323 if (entry
->occupied
&& _dhd_wlfc_is_destination_open(ctx
, entry
, prec
) &&
1324 (entry
->transit_count
< WL_TXSTATUS_FREERUNCTR_MASK
) &&
1325 (!entry
->suppressed
)) {
1326 *ac_credit_spent
= credit_spent
;
1327 if (entry
->state
== WLFC_STATE_CLOSE
) {
1328 *ac_credit_spent
= 0;
1331 /* higher precedence will be picked up first,
1332 * i.e. suppressed packets before delayed ones
1334 p
= pktq_pdeq(&entry
->psq
, PSQ_SUP_IDX(prec
));
1337 /* De-Q from delay Q */
1338 p
= pktq_pdeq(&entry
->psq
, PSQ_DLY_IDX(prec
));
1343 bcm_pkt_validate_chk(p
);
1344 /* did the packet come from suppress sub-queue? */
1345 if (entry
->requested_credit
> 0) {
1346 entry
->requested_credit
--;
1347 #ifdef PROP_TXSTATUS_DEBUG
1348 entry
->dstncredit_sent_packets
++;
1350 } else if (entry
->requested_packet
> 0) {
1351 entry
->requested_packet
--;
1352 DHD_PKTTAG_SETONETIMEPKTRQST(PKTTAG(p
));
1356 ctx
->pkt_cnt_in_q
[DHD_PKTTAG_IF(PKTTAG(p
))][prec
]--;
1357 ctx
->pkt_cnt_per_ac
[prec
]--;
1358 ctx
->pkt_cnt_in_psq
--;
1359 _dhd_wlfc_flow_control_check(ctx
, &entry
->psq
,
1360 DHD_PKTTAG_IF(PKTTAG(p
)));
1362 * A packet has been picked up, update traffic availability bitmap,
1365 _dhd_wlfc_traffic_pending_check(ctx
, entry
, prec
);
1371 } /* _dhd_wlfc_deque_delayedq */
1373 /** Enqueues caller supplied packet on either a 'suppressed' or 'delayed' queue */
1375 _dhd_wlfc_enque_delayq(athost_wl_status_info_t
* ctx
, void* pktbuf
, int prec
)
1377 wlfc_mac_descriptor_t
* entry
;
1379 if (pktbuf
!= NULL
) {
1380 entry
= _dhd_wlfc_find_table_entry(ctx
, pktbuf
);
1381 if (entry
== NULL
) {
1382 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__
, __LINE__
));
1387 - suppressed packets go to sub_queue[2*prec + 1] AND
1388 - delayed packets go to sub_queue[2*prec + 0] to ensure
1391 if (_dhd_wlfc_prec_enq_with_drop(ctx
->dhdp
, &entry
->psq
, pktbuf
, (prec
<< 1),
1392 FALSE
, WLFC_SEQCOUNT(entry
, prec
))
1394 WLFC_DBGMESG(("D"));
1395 ctx
->stats
.delayq_full_error
++;
1400 /* A packet has been pushed, update traffic availability bitmap, if applicable */
1401 _dhd_wlfc_traffic_pending_check(ctx
, entry
, prec
);
1405 } /* _dhd_wlfc_enque_delayq */
1407 /** Returns TRUE if caller supplied packet is destined for caller supplied interface */
1408 static bool _dhd_wlfc_ifpkt_fn(void* p
, void *p_ifid
)
1413 return (DHD_PKTTAG_WLFCPKT(PKTTAG(p
))&& (*((uint8
*)p_ifid
) == DHD_PKTTAG_IF(PKTTAG(p
))));
1416 /** Returns TRUE if caller supplied packet is destined for caller supplied remote MAC */
1417 static bool _dhd_wlfc_entrypkt_fn(void* p
, void *entry
)
1422 return (DHD_PKTTAG_WLFCPKT(PKTTAG(p
))&& (entry
== DHD_PKTTAG_ENTRY(PKTTAG(p
))));
1426 _dhd_wlfc_return_implied_credit(athost_wl_status_info_t
* wlfc
, void* pkt
)
1429 bool credit_return
= FALSE
;
1431 if (!wlfc
|| !pkt
) {
1435 dhdp
= (dhd_pub_t
*)(wlfc
->dhdp
);
1436 if (dhdp
&& (dhdp
->proptxstatus_mode
== WLFC_FCMODE_IMPLIED_CREDIT
) &&
1437 DHD_PKTTAG_CREDITCHECK(PKTTAG(pkt
))) {
1438 int lender
, credit_returned
= 0;
1439 uint8 fifo_id
= DHD_PKTTAG_FIFO(PKTTAG(pkt
));
1441 credit_return
= TRUE
;
1443 /* Note that borrower is fifo_id */
1444 /* Return credits to highest priority lender first */
1445 for (lender
= AC_COUNT
; lender
>= 0; lender
--) {
1446 if (wlfc
->credits_borrowed
[fifo_id
][lender
] > 0) {
1447 wlfc
->FIFO_credit
[lender
]++;
1448 wlfc
->credits_borrowed
[fifo_id
][lender
]--;
1449 credit_returned
= 1;
1454 if (!credit_returned
) {
1455 wlfc
->FIFO_credit
[fifo_id
]++;
1459 BCM_REFERENCE(credit_return
);
1460 #if defined(DHD_WLFC_THREAD)
1461 if (credit_return
) {
1462 _dhd_wlfc_thread_wakeup(dhdp
);
1464 #endif /* defined(DHD_WLFC_THREAD) */
1467 /** Removes and frees a packet from the hanger. Called during eg tx complete. */
1469 _dhd_wlfc_hanger_free_pkt(athost_wl_status_info_t
* wlfc
, uint32 slot_id
, uint8 pkt_state
,
1472 wlfc_hanger_t
* hanger
;
1473 wlfc_hanger_item_t
* item
;
1478 hanger
= (wlfc_hanger_t
*)wlfc
->hanger
;
1482 if (slot_id
== WLFC_HANGER_MAXITEMS
)
1485 item
= &hanger
->items
[slot_id
];
1488 item
->pkt_state
|= pkt_state
;
1489 if (pkt_txstatus
!= -1)
1490 item
->pkt_txstatus
= (uint8
)pkt_txstatus
;
1491 bcm_object_feature_set(item
->pkt
, BCM_OBJECT_FEATURE_PKT_STATE
, item
->pkt_state
);
1492 if (item
->pkt_state
== WLFC_HANGER_PKT_STATE_COMPLETE
) {
1494 void *pkt
= item
->pkt
;
1495 uint8 old_state
= item
->state
;
1496 int ret
= _dhd_wlfc_hanger_poppkt(wlfc
->hanger
, slot_id
, &p
, TRUE
);
1499 ASSERT((ret
== BCME_OK
) && p
&& (pkt
== p
));
1500 if (old_state
== WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED
) {
1501 printf("ERROR: free a suppressed pkt %p state %d pkt_state %d\n",
1502 pkt
, old_state
, item
->pkt_state
);
1504 ASSERT(old_state
!= WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED
);
1507 wlfc
->pkt_cnt_in_drv
[DHD_PKTTAG_IF(PKTTAG(p
))]
1508 [DHD_PKTTAG_FIFO(PKTTAG(p
))]--;
1509 wlfc
->stats
.pktout
++;
1510 dhd_txcomplete((dhd_pub_t
*)wlfc
->dhdp
, p
, item
->pkt_txstatus
);
1511 PKTFREE(wlfc
->osh
, p
, TRUE
);
1515 if (item
->state
== WLFC_HANGER_ITEM_STATE_FREE
)
1516 DHD_ERROR(("Error: %s():%d Multiple TXSTATUS or BUSRETURNED: %d (%d)\n",
1517 __FUNCTION__
, __LINE__
, item
->pkt_state
, pkt_state
));
1518 item
->state
= WLFC_HANGER_ITEM_STATE_FREE
;
1520 } /* _dhd_wlfc_hanger_free_pkt */
1522 /** Called during eg detach() */
1524 _dhd_wlfc_pktq_flush(athost_wl_status_info_t
* ctx
, struct pktq
*pq
,
1525 bool dir
, f_processpkt_t fn
, void *arg
, q_type_t q_type
)
1528 dhd_pub_t
*dhdp
= (dhd_pub_t
*)ctx
->dhdp
;
1532 /* Optimize flush, if pktq len = 0, just return.
1533 * pktq len of 0 means pktq's prec q's are all empty.
1539 for (prec
= 0; prec
< pq
->num_prec
; prec
++) {
1540 struct pktq_prec
*q
;
1541 void *p
, *prev
= NULL
;
1546 bcm_pkt_validate_chk(p
);
1547 if (fn
== NULL
|| (*fn
)(p
, arg
)) {
1548 bool head
= (p
== q
->head
);
1550 q
->head
= PKTLINK(p
);
1552 PKTSETLINK(prev
, PKTLINK(p
));
1553 if (q_type
== Q_TYPE_PSQ
) {
1554 if (!WLFC_GET_AFQ(dhdp
->wlfc_mode
) && (prec
& 1)) {
1555 _dhd_wlfc_hanger_remove_reference(ctx
->hanger
, p
);
1557 ctx
->pkt_cnt_in_q
[DHD_PKTTAG_IF(PKTTAG(p
))][prec
>>1]--;
1558 ctx
->pkt_cnt_per_ac
[prec
>>1]--;
1559 ctx
->pkt_cnt_in_psq
--;
1560 ctx
->stats
.cleanup_psq_cnt
++;
1562 /* pkt in delayed q, so fake push BDC header for
1563 * dhd_tcpack_check_xmit() and dhd_txcomplete().
1565 _dhd_wlfc_pushheader(ctx
, &p
, FALSE
, 0, 0,
1567 #ifdef DHDTCPACK_SUPPRESS
1568 if (dhd_tcpack_check_xmit(dhdp
, p
) == BCME_ERROR
) {
1569 DHD_ERROR(("%s %d: tcpack_suppress ERROR!!!"
1571 __FUNCTION__
, __LINE__
));
1572 dhd_tcpack_suppress_set(dhdp
,
1575 #endif /* DHDTCPACK_SUPPRESS */
1577 } else if (q_type
== Q_TYPE_AFQ
) {
1578 wlfc_mac_descriptor_t
* entry
=
1579 _dhd_wlfc_find_table_entry(ctx
, p
);
1580 if (entry
->transit_count
)
1581 entry
->transit_count
--;
1582 if (entry
->suppr_transit_count
) {
1583 entry
->suppr_transit_count
--;
1584 if (entry
->suppressed
&&
1585 (!entry
->onbus_pkts_count
) &&
1586 (!entry
->suppr_transit_count
))
1587 entry
->suppressed
= FALSE
;
1589 _dhd_wlfc_return_implied_credit(ctx
, p
);
1590 ctx
->stats
.cleanup_fw_cnt
++;
1592 PKTSETLINK(p
, NULL
);
1594 ctx
->pkt_cnt_in_drv
[DHD_PKTTAG_IF(PKTTAG(p
))][prec
>>1]--;
1595 ctx
->stats
.pktout
++;
1596 dhd_txcomplete(dhdp
, p
, FALSE
);
1598 PKTFREE(ctx
->osh
, p
, dir
);
1602 p
= (head
? q
->head
: PKTLINK(prev
));
1609 if (q
->head
== NULL
) {
1610 ASSERT(q
->len
== 0);
1617 ASSERT(pq
->len
== 0);
1618 } /* _dhd_wlfc_pktq_flush */
1621 /** !BCMDBUS specific function. Dequeues a packet from the caller supplied queue. */
1623 _dhd_wlfc_pktq_pdeq_with_fn(struct pktq
*pq
, int prec
, f_processpkt_t fn
, void *arg
)
1625 struct pktq_prec
*q
;
1626 void *p
, *prev
= NULL
;
1628 ASSERT(prec
>= 0 && prec
< pq
->num_prec
);
1634 if (fn
== NULL
|| (*fn
)(p
, arg
)) {
1644 bcm_pkt_validate_chk(p
);
1647 if ((q
->head
= PKTLINK(p
)) == NULL
) {
1651 PKTSETLINK(prev
, PKTLINK(p
));
1661 PKTSETLINK(p
, NULL
);
1666 /** !BCMDBUS specific function */
1668 _dhd_wlfc_cleanup_txq(dhd_pub_t
*dhd
, f_processpkt_t fn
, void *arg
)
1671 void *pkt
= NULL
, *head
= NULL
, *tail
= NULL
;
1672 struct pktq
*txq
= (struct pktq
*)dhd_bus_txq(dhd
->bus
);
1673 athost_wl_status_info_t
* wlfc
= (athost_wl_status_info_t
*)dhd
->wlfc_state
;
1674 wlfc_hanger_t
* h
= (wlfc_hanger_t
*)wlfc
->hanger
;
1675 wlfc_mac_descriptor_t
* entry
;
1677 dhd_os_sdlock_txq(dhd
);
1678 for (prec
= 0; prec
< txq
->num_prec
; prec
++) {
1679 while ((pkt
= _dhd_wlfc_pktq_pdeq_with_fn(txq
, prec
, fn
, arg
))) {
1680 #ifdef DHDTCPACK_SUPPRESS
1681 if (dhd_tcpack_check_xmit(dhd
, pkt
) == BCME_ERROR
) {
1682 DHD_ERROR(("%s %d: tcpack_suppress ERROR!!! Stop using it\n",
1683 __FUNCTION__
, __LINE__
));
1684 dhd_tcpack_suppress_set(dhd
, TCPACK_SUP_OFF
);
1686 #endif /* DHDTCPACK_SUPPRESS */
1691 PKTSETLINK(tail
, pkt
);
1696 dhd_os_sdunlock_txq(dhd
);
1699 while ((pkt
= head
)) {
1700 head
= PKTLINK(pkt
);
1701 PKTSETLINK(pkt
, NULL
);
1702 entry
= _dhd_wlfc_find_table_entry(wlfc
, pkt
);
1704 if (!WLFC_GET_AFQ(dhd
->wlfc_mode
) &&
1705 !_dhd_wlfc_hanger_remove_reference(h
, pkt
)) {
1706 DHD_ERROR(("%s: can't find pkt(%p) in hanger, free it anyway\n",
1707 __FUNCTION__
, pkt
));
1709 if (entry
->transit_count
)
1710 entry
->transit_count
--;
1711 if (entry
->suppr_transit_count
) {
1712 entry
->suppr_transit_count
--;
1713 if (entry
->suppressed
&&
1714 (!entry
->onbus_pkts_count
) &&
1715 (!entry
->suppr_transit_count
))
1716 entry
->suppressed
= FALSE
;
1718 _dhd_wlfc_return_implied_credit(wlfc
, pkt
);
1719 wlfc
->pkt_cnt_in_drv
[DHD_PKTTAG_IF(PKTTAG(pkt
))][DHD_PKTTAG_FIFO(PKTTAG(pkt
))]--;
1720 wlfc
->stats
.pktout
++;
1721 wlfc
->stats
.cleanup_txq_cnt
++;
1722 dhd_txcomplete(dhd
, pkt
, FALSE
);
1723 PKTFREE(wlfc
->osh
, pkt
, TRUE
);
1725 } /* _dhd_wlfc_cleanup_txq */
1726 #endif /* !BCMDBUS */
1728 /** called during eg detach */
1730 _dhd_wlfc_cleanup(dhd_pub_t
*dhd
, f_processpkt_t fn
, void *arg
)
1734 athost_wl_status_info_t
* wlfc
= (athost_wl_status_info_t
*)dhd
->wlfc_state
;
1735 wlfc_mac_descriptor_t
* table
;
1736 wlfc_hanger_t
* h
= (wlfc_hanger_t
*)wlfc
->hanger
;
1738 wlfc
->stats
.cleanup_txq_cnt
= 0;
1739 wlfc
->stats
.cleanup_psq_cnt
= 0;
1740 wlfc
->stats
.cleanup_fw_cnt
= 0;
1743 * flush sequence should be txq -> psq -> hanger/afq, hanger has to be last one
1746 /* flush bus->txq */
1747 _dhd_wlfc_cleanup_txq(dhd
, fn
, arg
);
1748 #endif /* !BCMDBUS */
1750 /* flush psq, search all entries, include nodes as well as interfaces */
1751 total_entries
= sizeof(wlfc
->destination_entries
)/sizeof(wlfc_mac_descriptor_t
);
1752 table
= (wlfc_mac_descriptor_t
*)&wlfc
->destination_entries
;
1754 for (i
= 0; i
< total_entries
; i
++) {
1755 if (table
[i
].occupied
) {
1756 /* release packets held in PSQ (both delayed and suppressed) */
1757 if (table
[i
].psq
.len
) {
1758 WLFC_DBGMESG(("%s(): PSQ[%d].len = %d\n",
1759 __FUNCTION__
, i
, table
[i
].psq
.len
));
1760 _dhd_wlfc_pktq_flush(wlfc
, &table
[i
].psq
, TRUE
,
1761 fn
, arg
, Q_TYPE_PSQ
);
1764 /* free packets held in AFQ */
1765 if (WLFC_GET_AFQ(dhd
->wlfc_mode
) && (table
[i
].afq
.len
)) {
1766 _dhd_wlfc_pktq_flush(wlfc
, &table
[i
].afq
, TRUE
,
1767 fn
, arg
, Q_TYPE_AFQ
);
1770 if ((fn
== NULL
) && (&table
[i
] != &wlfc
->destination_entries
.other
)) {
1771 table
[i
].occupied
= 0;
1772 if (table
[i
].transit_count
|| table
[i
].suppr_transit_count
) {
1773 DHD_ERROR(("%s: table[%d] transit(%d), suppr_tansit(%d)\n",
1775 table
[i
].transit_count
,
1776 table
[i
].suppr_transit_count
));
1783 . flush remained pkt in hanger queue, not in bus->txq nor psq.
1784 . the remained pkt was successfully downloaded to dongle already.
1785 . hanger slot state cannot be set to free until receive txstatus update.
1787 if (!WLFC_GET_AFQ(dhd
->wlfc_mode
)) {
1788 for (i
= 0; i
< h
->max_items
; i
++) {
1789 if ((h
->items
[i
].state
== WLFC_HANGER_ITEM_STATE_INUSE
) ||
1790 (h
->items
[i
].state
== WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED
)) {
1791 if (fn
== NULL
|| (*fn
)(h
->items
[i
].pkt
, arg
)) {
1792 h
->items
[i
].state
= WLFC_HANGER_ITEM_STATE_FLUSHED
;
1799 } /* _dhd_wlfc_cleanup */
1801 /** Called after eg the dongle signalled a new remote MAC that it connected with to the DHD */
1803 _dhd_wlfc_mac_entry_update(athost_wl_status_info_t
* ctx
, wlfc_mac_descriptor_t
* entry
,
1804 uint8 action
, uint8 ifid
, uint8 iftype
, uint8
* ea
,
1805 f_processpkt_t fn
, void *arg
)
1810 if ((action
== eWLFC_MAC_ENTRY_ACTION_ADD
) || (action
== eWLFC_MAC_ENTRY_ACTION_UPDATE
)) {
1811 entry
->occupied
= 1;
1812 entry
->state
= WLFC_STATE_OPEN
;
1813 entry
->requested_credit
= 0;
1814 entry
->interface_id
= ifid
;
1815 entry
->iftype
= iftype
;
1816 entry
->ac_bitmap
= 0xff; /* update this when handling APSD */
1818 /* for an interface entry we may not care about the MAC address */
1820 memcpy(&entry
->ea
[0], ea
, ETHER_ADDR_LEN
);
1822 if (action
== eWLFC_MAC_ENTRY_ACTION_ADD
) {
1823 entry
->suppressed
= FALSE
;
1824 entry
->transit_count
= 0;
1825 entry
->suppr_transit_count
= 0;
1826 entry
->onbus_pkts_count
= 0;
1829 if (action
== eWLFC_MAC_ENTRY_ACTION_ADD
) {
1830 dhd_pub_t
*dhdp
= (dhd_pub_t
*)(ctx
->dhdp
);
1832 pktq_init(&entry
->psq
, WLFC_PSQ_PREC_COUNT
, WLFC_PSQ_LEN
);
1833 _dhd_wlfc_flow_control_check(ctx
, &entry
->psq
, ifid
);
1835 if (WLFC_GET_AFQ(dhdp
->wlfc_mode
)) {
1836 pktq_init(&entry
->afq
, WLFC_AFQ_PREC_COUNT
, WLFC_PSQ_LEN
);
1839 if (entry
->next
== NULL
) {
1840 /* not linked to anywhere, add to tail */
1841 if (ctx
->active_entry_head
) {
1842 entry
->prev
= ctx
->active_entry_head
->prev
;
1843 ctx
->active_entry_head
->prev
->next
= entry
;
1844 ctx
->active_entry_head
->prev
= entry
;
1845 entry
->next
= ctx
->active_entry_head
;
1847 ASSERT(ctx
->active_entry_count
== 0);
1848 entry
->prev
= entry
->next
= entry
;
1849 ctx
->active_entry_head
= entry
;
1851 ctx
->active_entry_count
++;
1853 DHD_ERROR(("%s():%d, entry(%d)\n", __FUNCTION__
, __LINE__
,
1854 (int)(entry
- &ctx
->destination_entries
.nodes
[0])));
1857 } else if (action
== eWLFC_MAC_ENTRY_ACTION_DEL
) {
1858 /* When the entry is deleted, the packets that are queued in the entry must be
1859 cleanup. The cleanup action should be before the occupied is set as 0.
1861 _dhd_wlfc_cleanup(ctx
->dhdp
, fn
, arg
);
1862 _dhd_wlfc_flow_control_check(ctx
, &entry
->psq
, ifid
);
1864 entry
->occupied
= 0;
1865 entry
->state
= WLFC_STATE_CLOSE
;
1866 memset(&entry
->ea
[0], 0, ETHER_ADDR_LEN
);
1869 /* not floating, remove from Q */
1870 if (ctx
->active_entry_count
<= 1) {
1872 ctx
->active_entry_head
= NULL
;
1873 ctx
->active_entry_count
= 0;
1875 entry
->prev
->next
= entry
->next
;
1876 entry
->next
->prev
= entry
->prev
;
1877 if (entry
== ctx
->active_entry_head
) {
1878 ctx
->active_entry_head
= entry
->next
;
1880 ctx
->active_entry_count
--;
1882 entry
->next
= entry
->prev
= NULL
;
1884 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__
, __LINE__
));
1888 } /* _dhd_wlfc_mac_entry_update */
1893 /** LIMIT_BORROW specific function */
1895 _dhd_wlfc_borrow_credit(athost_wl_status_info_t
* ctx
, int highest_lender_ac
, int borrower_ac
,
1898 int lender_ac
, borrow_limit
= 0;
1902 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__
, __LINE__
));
1906 /* Borrow from lowest priority available AC (including BC/MC credits) */
1907 for (lender_ac
= 0; lender_ac
<= highest_lender_ac
; lender_ac
++) {
1909 borrow_limit
= ctx
->Init_FIFO_credit
[lender_ac
]/WLFC_BORROW_LIMIT_RATIO
;
1914 if (ctx
->FIFO_credit
[lender_ac
] > borrow_limit
) {
1915 ctx
->credits_borrowed
[borrower_ac
][lender_ac
]++;
1916 ctx
->FIFO_credit
[lender_ac
]--;
1925 /** LIMIT_BORROW specific function */
1926 static int _dhd_wlfc_return_credit(athost_wl_status_info_t
* ctx
, int lender_ac
, int borrower_ac
)
1928 if ((ctx
== NULL
) || (lender_ac
< 0) || (lender_ac
> AC_COUNT
) ||
1929 (borrower_ac
< 0) || (borrower_ac
> AC_COUNT
)) {
1930 DHD_ERROR(("Error: %s():%d, ctx(%p), lender_ac(%d), borrower_ac(%d)\n",
1931 __FUNCTION__
, __LINE__
, ctx
, lender_ac
, borrower_ac
));
1936 ctx
->credits_borrowed
[borrower_ac
][lender_ac
]--;
1937 ctx
->FIFO_credit
[lender_ac
]++;
1942 #endif /* LIMIT_BORROW */
1945 * Called on an interface event (WLC_E_IF) indicated by firmware.
1946 * @param action : eg eWLFC_MAC_ENTRY_ACTION_UPDATE or eWLFC_MAC_ENTRY_ACTION_ADD
1949 _dhd_wlfc_interface_entry_update(void* state
,
1950 uint8 action
, uint8 ifid
, uint8 iftype
, uint8
* ea
)
1952 athost_wl_status_info_t
* ctx
= (athost_wl_status_info_t
*)state
;
1953 wlfc_mac_descriptor_t
* entry
;
1955 if (ifid
>= WLFC_MAX_IFNUM
)
1958 entry
= &ctx
->destination_entries
.interfaces
[ifid
];
1960 return _dhd_wlfc_mac_entry_update(ctx
, entry
, action
, ifid
, iftype
, ea
,
1961 _dhd_wlfc_ifpkt_fn
, &ifid
);
1965 * Called eg on receiving a WLC_E_BCMC_CREDIT_SUPPORT event from the dongle (broadcast/multicast
1969 _dhd_wlfc_BCMCCredit_support_update(void* state
)
1971 athost_wl_status_info_t
* ctx
= (athost_wl_status_info_t
*)state
;
1973 ctx
->bcmc_credit_supported
= TRUE
;
1977 /** Called eg on receiving a WLC_E_FIFO_CREDIT_MAP event from the dongle */
1979 _dhd_wlfc_FIFOcreditmap_update(void* state
, uint8
* credits
)
1981 athost_wl_status_info_t
* ctx
= (athost_wl_status_info_t
*)state
;
1984 for (i
= 0; i
<= 4; i
++) {
1985 if (ctx
->Init_FIFO_credit
[i
] != ctx
->FIFO_credit
[i
]) {
1986 DHD_ERROR(("%s: credit[i] is not returned, (%d %d)\n",
1987 __FUNCTION__
, ctx
->Init_FIFO_credit
[i
], ctx
->FIFO_credit
[i
]));
1991 /* update the AC FIFO credit map */
1992 ctx
->FIFO_credit
[0] += (credits
[0] - ctx
->Init_FIFO_credit
[0]);
1993 ctx
->FIFO_credit
[1] += (credits
[1] - ctx
->Init_FIFO_credit
[1]);
1994 ctx
->FIFO_credit
[2] += (credits
[2] - ctx
->Init_FIFO_credit
[2]);
1995 ctx
->FIFO_credit
[3] += (credits
[3] - ctx
->Init_FIFO_credit
[3]);
1996 ctx
->FIFO_credit
[4] += (credits
[4] - ctx
->Init_FIFO_credit
[4]);
1998 ctx
->Init_FIFO_credit
[0] = credits
[0];
1999 ctx
->Init_FIFO_credit
[1] = credits
[1];
2000 ctx
->Init_FIFO_credit
[2] = credits
[2];
2001 ctx
->Init_FIFO_credit
[3] = credits
[3];
2002 ctx
->Init_FIFO_credit
[4] = credits
[4];
2004 /* credit for ATIM FIFO is not used yet. */
2005 ctx
->Init_FIFO_credit
[5] = ctx
->FIFO_credit
[5] = 0;
2011 * Called during committing of a transmit packet from the OS DHD layer to the next layer towards
2012 * the dongle (eg the DBUS layer). All transmit packets flow via this function to the next layer.
2014 * @param[in/out] ctx Driver specific flow control administration
2015 * @param[in] ac Access Category (QoS) of called supplied packet
2016 * @param[in] commit_info Contains eg the packet to send
2017 * @param[in] fcommit Function pointer to transmit function of next software layer
2018 * @param[in] commit_ctx Opaque context used when calling next layer
2021 _dhd_wlfc_handle_packet_commit(athost_wl_status_info_t
* ctx
, int ac
,
2022 dhd_wlfc_commit_info_t
*commit_info
, f_commitpkt_t fcommit
, void* commit_ctx
)
2026 dhd_pub_t
*dhdp
= (dhd_pub_t
*)(ctx
->dhdp
);
2029 if ac_fifo_credit_spent = 0
2031 This packet will not count against the FIFO credit.
2032 To ensure the txstatus corresponding to this packet
2033 does not provide an implied credit (default behavior)
2034 mark the packet accordingly.
2036 if ac_fifo_credit_spent = 1
2038 This is a normal packet and it counts against the FIFO
2041 DHD_PKTTAG_SETCREDITCHECK(PKTTAG(commit_info
->p
), commit_info
->ac_fifo_credit_spent
);
2042 rc
= _dhd_wlfc_pretx_pktprocess(ctx
, commit_info
->mac_entry
, &commit_info
->p
,
2043 commit_info
->needs_hdr
, &hslot
);
2045 if (rc
== BCME_OK
) {
2046 rc
= fcommit(commit_ctx
, commit_info
->p
);
2047 if (rc
== BCME_OK
) {
2048 uint8 gen
= WL_TXSTATUS_GET_GENERATION(
2049 DHD_PKTTAG_H2DTAG(PKTTAG(commit_info
->p
)));
2050 ctx
->stats
.pkt2bus
++;
2051 if (commit_info
->ac_fifo_credit_spent
|| (ac
== AC_COUNT
)) {
2052 ctx
->stats
.send_pkts
[ac
]++;
2053 WLFC_HOST_FIFO_CREDIT_INC_SENTCTRS(ctx
, ac
);
2056 if (gen
!= commit_info
->mac_entry
->generation
) {
2057 /* will be suppressed back by design */
2058 if (!commit_info
->mac_entry
->suppressed
) {
2059 commit_info
->mac_entry
->suppressed
= TRUE
;
2061 commit_info
->mac_entry
->suppr_transit_count
++;
2063 commit_info
->mac_entry
->transit_count
++;
2064 commit_info
->mac_entry
->onbus_pkts_count
++;
2065 } else if (commit_info
->needs_hdr
) {
2066 if (!WLFC_GET_AFQ(dhdp
->wlfc_mode
)) {
2068 /* pop hanger for delayed packet */
2069 _dhd_wlfc_hanger_poppkt(ctx
->hanger
, WL_TXSTATUS_GET_HSLOT(
2070 DHD_PKTTAG_H2DTAG(PKTTAG(commit_info
->p
))), &pout
, TRUE
);
2071 ASSERT(commit_info
->p
== pout
);
2075 ctx
->stats
.generic_error
++;
2078 if (rc
!= BCME_OK
) {
2080 pretx pkt process or bus commit has failed, rollback.
2081 - remove wl-header for a delayed packet
2082 - save wl-header header for suppressed packets
2083 - reset credit check flag
2085 _dhd_wlfc_rollback_packet_toq(ctx
, commit_info
->p
, commit_info
->pkt_type
, hslot
);
2086 DHD_PKTTAG_SETCREDITCHECK(PKTTAG(commit_info
->p
), 0);
2090 } /* _dhd_wlfc_handle_packet_commit */
2092 /** Returns remote MAC descriptor for caller supplied MAC address */
2094 _dhd_wlfc_find_mac_desc_id_from_mac(dhd_pub_t
*dhdp
, uint8
*ea
)
2096 wlfc_mac_descriptor_t
* table
=
2097 ((athost_wl_status_info_t
*)dhdp
->wlfc_state
)->destination_entries
.nodes
;
2101 for (table_index
= 0; table_index
< WLFC_MAC_DESC_TABLE_SIZE
; table_index
++) {
2102 if ((memcmp(ea
, &table
[table_index
].ea
[0], ETHER_ADDR_LEN
) == 0) &&
2103 table
[table_index
].occupied
)
2107 return WLFC_MAC_DESC_ID_INVALID
;
2111 * Called when the host receives a WLFC_CTL_TYPE_TXSTATUS event from the dongle, indicating the
2112 * status of a frame that the dongle attempted to transmit over the wireless medium.
2115 dhd_wlfc_suppressed_acked_update(dhd_pub_t
*dhd
, uint16 hslot
, uint8 prec
, uint8 hcnt
)
2117 athost_wl_status_info_t
* ctx
;
2118 wlfc_mac_descriptor_t
* entry
= NULL
;
2120 struct pktq_prec
*q
;
2124 DHD_ERROR(("%s: dhd(%p)\n", __FUNCTION__
, dhd
));
2127 ctx
= (athost_wl_status_info_t
*)dhd
->wlfc_state
;
2129 DHD_ERROR(("%s: ctx(%p)\n", __FUNCTION__
, ctx
));
2133 ASSERT(hslot
< (WLFC_MAC_DESC_TABLE_SIZE
+ WLFC_MAX_IFNUM
+ 1));
2135 if (hslot
< WLFC_MAC_DESC_TABLE_SIZE
)
2136 entry
= &ctx
->destination_entries
.nodes
[hslot
];
2137 else if (hslot
< (WLFC_MAC_DESC_TABLE_SIZE
+ WLFC_MAX_IFNUM
))
2138 entry
= &ctx
->destination_entries
.interfaces
[hslot
- WLFC_MAC_DESC_TABLE_SIZE
];
2140 entry
= &ctx
->destination_entries
.other
;
2144 ASSERT(((prec
<< 1) + 1) < pq
->num_prec
);
2146 q
= &pq
->q
[((prec
<< 1) + 1)];
2151 while (p
&& (hcnt
!= WL_TXSTATUS_GET_FREERUNCTR(DHD_PKTTAG_H2DTAG(PKTTAG(p
))))) {
2157 /* none is matched */
2159 DHD_ERROR(("%s: can't find matching seq(%d)\n", __FUNCTION__
, hcnt
));
2161 DHD_ERROR(("%s: queue is empty\n", __FUNCTION__
));
2168 /* head packet is matched */
2169 if ((q
->head
= PKTLINK(p
)) == NULL
) {
2173 /* middle packet is matched */
2174 PKTSETLINK(b
, PKTLINK(p
));
2175 if (PKTLINK(p
) == NULL
) {
2182 ctx
->pkt_cnt_in_q
[DHD_PKTTAG_IF(PKTTAG(p
))][prec
]--;
2183 ctx
->pkt_cnt_per_ac
[prec
]--;
2185 PKTSETLINK(p
, NULL
);
2187 if (WLFC_GET_AFQ(dhd
->wlfc_mode
)) {
2188 _dhd_wlfc_enque_afq(ctx
, p
);
2190 _dhd_wlfc_hanger_pushpkt(ctx
->hanger
, p
, hslot
);
2193 entry
->transit_count
++;
2199 _dhd_wlfc_compressed_txstatus_update(dhd_pub_t
*dhd
, uint8
* pkt_info
, uint8 len
, void** p_mac
)
2201 uint8 status_flag_ori
, status_flag
;
2204 int remove_from_hanger_ori
, remove_from_hanger
= 1;
2205 void* pktbuf
= NULL
;
2206 uint8 fifo_id
= 0, gen
= 0, count
= 0, hcnt
;
2208 wlfc_mac_descriptor_t
* entry
= NULL
;
2209 athost_wl_status_info_t
* wlfc
= (athost_wl_status_info_t
*)dhd
->wlfc_state
;
2210 uint16 seq
= 0, seq_fromfw
= 0, seq_num
= 0;
2212 memcpy(&status
, pkt_info
, sizeof(uint32
));
2213 status
= ltoh32(status
);
2214 status_flag
= WL_TXSTATUS_GET_FLAGS(status
);
2215 hcnt
= WL_TXSTATUS_GET_FREERUNCTR(status
);
2216 hslot
= WL_TXSTATUS_GET_HSLOT(status
);
2217 fifo_id
= WL_TXSTATUS_GET_FIFO(status
);
2218 gen
= WL_TXSTATUS_GET_GENERATION(status
);
2220 if (WLFC_GET_REUSESEQ(dhd
->wlfc_mode
)) {
2221 memcpy(&seq
, pkt_info
+ WLFC_CTL_VALUE_LEN_TXSTATUS
, WLFC_CTL_VALUE_LEN_SEQ
);
2223 seq_fromfw
= GET_WL_HAS_ASSIGNED_SEQ(seq
);
2224 seq_num
= WL_SEQ_GET_NUM(seq
);
2227 wlfc
->stats
.txstatus_in
+= len
;
2229 if (status_flag
== WLFC_CTL_PKTFLAG_DISCARD
) {
2230 wlfc
->stats
.pkt_freed
+= len
;
2231 } else if (status_flag
== WLFC_CTL_PKTFLAG_DISCARD_NOACK
) {
2232 wlfc
->stats
.pkt_freed
+= len
;
2233 } else if (status_flag
== WLFC_CTL_PKTFLAG_D11SUPPRESS
) {
2234 wlfc
->stats
.d11_suppress
+= len
;
2235 remove_from_hanger
= 0;
2236 } else if (status_flag
== WLFC_CTL_PKTFLAG_WLSUPPRESS
) {
2237 wlfc
->stats
.wl_suppress
+= len
;
2238 remove_from_hanger
= 0;
2239 } else if (status_flag
== WLFC_CTL_PKTFLAG_TOSSED_BYWLC
) {
2240 wlfc
->stats
.wlc_tossed_pkts
+= len
;
2243 else if (status_flag
== WLFC_CTL_PKTFLAG_SUPPRESS_ACKED
) {
2244 wlfc
->stats
.pkt_freed
+= len
;
2247 if (dhd
->proptxstatus_txstatus_ignore
) {
2248 if (!remove_from_hanger
) {
2249 DHD_ERROR(("suppress txstatus: %d\n", status_flag
));
2254 status_flag_ori
= status_flag
;
2255 remove_from_hanger_ori
= remove_from_hanger
;
2257 while (count
< len
) {
2258 if (status_flag
== WLFC_CTL_PKTFLAG_SUPPRESS_ACKED
) {
2259 dhd_wlfc_suppressed_acked_update(dhd
, hslot
, fifo_id
, hcnt
);
2261 if (WLFC_GET_AFQ(dhd
->wlfc_mode
)) {
2262 ret
= _dhd_wlfc_deque_afq(wlfc
, hslot
, hcnt
, fifo_id
, &pktbuf
);
2264 status_flag
= status_flag_ori
;
2265 remove_from_hanger
= remove_from_hanger_ori
;
2266 ret
= _dhd_wlfc_hanger_poppkt(wlfc
->hanger
, hslot
, &pktbuf
, FALSE
);
2268 _dhd_wlfc_hanger_free_pkt(wlfc
, hslot
,
2269 WLFC_HANGER_PKT_STATE_TXSTATUS
, -1);
2272 wlfc_hanger_t
* h
= (wlfc_hanger_t
*)wlfc
->hanger
;
2273 if (h
->items
[hslot
].state
== WLFC_HANGER_ITEM_STATE_FLUSHED
) {
2274 status_flag
= WLFC_CTL_PKTFLAG_DISCARD
;
2275 remove_from_hanger
= 1;
2280 if ((ret
!= BCME_OK
) || !pktbuf
) {
2284 bcm_pkt_validate_chk(pktbuf
);
2286 /* set fifo_id to correct value because not all FW does that */
2287 fifo_id
= DHD_PKTTAG_FIFO(PKTTAG(pktbuf
));
2289 entry
= _dhd_wlfc_find_table_entry(wlfc
, pktbuf
);
2291 if (!remove_from_hanger
) {
2292 /* this packet was suppressed */
2293 if (!entry
->suppressed
|| (entry
->generation
!= gen
)) {
2294 if (!entry
->suppressed
) {
2295 entry
->suppr_transit_count
= entry
->transit_count
;
2300 DHD_ERROR(("gen(%d), entry->generation(%d)\n",
2301 gen
, entry
->generation
));
2303 entry
->suppressed
= TRUE
;
2306 entry
->generation
= gen
;
2309 #ifdef PROP_TXSTATUS_DEBUG
2310 if (!WLFC_GET_AFQ(dhd
->wlfc_mode
))
2312 uint32 new_t
= OSL_SYSUPTIME();
2315 old_t
= ((wlfc_hanger_t
*)(wlfc
->hanger
))->items
[hslot
].push_time
;
2318 wlfc
->stats
.latency_sample_count
++;
2320 delta
= new_t
- old_t
;
2322 delta
= 0xffffffff + new_t
- old_t
;
2323 wlfc
->stats
.total_status_latency
+= delta
;
2324 wlfc
->stats
.latency_most_recent
= delta
;
2326 wlfc
->stats
.deltas
[wlfc
->stats
.idx_delta
++] = delta
;
2327 if (wlfc
->stats
.idx_delta
== sizeof(wlfc
->stats
.deltas
)/sizeof(uint32
))
2328 wlfc
->stats
.idx_delta
= 0;
2330 #endif /* PROP_TXSTATUS_DEBUG */
2332 /* pick up the implicit credit from this packet */
2333 if (DHD_PKTTAG_CREDITCHECK(PKTTAG(pktbuf
))) {
2334 _dhd_wlfc_return_implied_credit(wlfc
, pktbuf
);
2337 if this packet did not count against FIFO credit, it must have
2338 taken a requested_credit from the destination entry (for pspoll etc.)
2340 if (!DHD_PKTTAG_ONETIMEPKTRQST(PKTTAG(pktbuf
))) {
2341 entry
->requested_credit
++;
2342 #if defined(DHD_WLFC_THREAD)
2343 _dhd_wlfc_thread_wakeup(dhd
);
2344 #endif /* DHD_WLFC_THREAD */
2346 #ifdef PROP_TXSTATUS_DEBUG
2347 entry
->dstncredit_acks
++;
2351 if ((status_flag
== WLFC_CTL_PKTFLAG_D11SUPPRESS
) ||
2352 (status_flag
== WLFC_CTL_PKTFLAG_WLSUPPRESS
)) {
2353 /* save generation bit inside packet */
2354 WL_TXSTATUS_SET_GENERATION(DHD_PKTTAG_H2DTAG(PKTTAG(pktbuf
)), gen
);
2356 if (WLFC_GET_REUSESEQ(dhd
->wlfc_mode
)) {
2357 WL_SEQ_SET_REUSE(DHD_PKTTAG_H2DSEQ(PKTTAG(pktbuf
)), seq_fromfw
);
2358 WL_SEQ_SET_NUM(DHD_PKTTAG_H2DSEQ(PKTTAG(pktbuf
)), seq_num
);
2361 ret
= _dhd_wlfc_enque_suppressed(wlfc
, fifo_id
, pktbuf
);
2362 if (ret
!= BCME_OK
) {
2363 /* delay q is full, drop this packet */
2364 DHD_WLFC_QMON_COMPLETE(entry
);
2365 _dhd_wlfc_prec_drop(dhd
, (fifo_id
<< 1) + 1, pktbuf
, FALSE
);
2367 if (!WLFC_GET_AFQ(dhd
->wlfc_mode
)) {
2368 /* Mark suppressed to avoid a double free
2371 _dhd_wlfc_hanger_mark_suppressed(wlfc
->hanger
, hslot
, gen
);
2376 DHD_WLFC_QMON_COMPLETE(entry
);
2378 if (!WLFC_GET_AFQ(dhd
->wlfc_mode
)) {
2379 _dhd_wlfc_hanger_free_pkt(wlfc
, hslot
,
2380 WLFC_HANGER_PKT_STATE_TXSTATUS
, TRUE
);
2382 dhd_txcomplete(dhd
, pktbuf
, TRUE
);
2383 wlfc
->pkt_cnt_in_drv
[DHD_PKTTAG_IF(PKTTAG(pktbuf
))]
2384 [DHD_PKTTAG_FIFO(PKTTAG(pktbuf
))]--;
2385 wlfc
->stats
.pktout
++;
2386 /* free the packet */
2387 PKTFREE(wlfc
->osh
, pktbuf
, TRUE
);
2390 /* pkt back from firmware side */
2391 if (entry
->transit_count
)
2392 entry
->transit_count
--;
2393 if (entry
->suppr_transit_count
) {
2394 entry
->suppr_transit_count
--;
2395 if (entry
->suppressed
&&
2396 (!entry
->onbus_pkts_count
) &&
2397 (!entry
->suppr_transit_count
))
2398 entry
->suppressed
= FALSE
;
2402 hcnt
= (hcnt
+ 1) & WL_TXSTATUS_FREERUNCTR_MASK
;
2403 if (!WLFC_GET_AFQ(dhd
->wlfc_mode
)) {
2404 hslot
= (hslot
+ 1) & WL_TXSTATUS_HSLOT_MASK
;
2407 if (WLFC_GET_REUSESEQ(dhd
->wlfc_mode
) && seq_fromfw
) {
2408 seq_num
= (seq_num
+ 1) & WL_SEQ_NUM_MASK
;
2415 } /* _dhd_wlfc_compressed_txstatus_update */
2418 * Called when eg host receives a 'WLFC_CTL_TYPE_FIFO_CREDITBACK' event from the dongle.
2419 * @param[in] credits caller supplied credit that will be added to the host credit.
2422 _dhd_wlfc_fifocreditback_indicate(dhd_pub_t
*dhd
, uint8
* credits
)
2425 athost_wl_status_info_t
* wlfc
= (athost_wl_status_info_t
*)dhd
->wlfc_state
;
2426 for (i
= 0; i
< WLFC_CTL_VALUE_LEN_FIFO_CREDITBACK
; i
++) {
2427 #ifdef PROP_TXSTATUS_DEBUG
2428 wlfc
->stats
.fifo_credits_back
[i
] += credits
[i
];
2431 /* update FIFO credits */
2432 if (dhd
->proptxstatus_mode
== WLFC_FCMODE_EXPLICIT_CREDIT
)
2434 int lender
; /* Note that borrower is i */
2436 /* Return credits to highest priority lender first */
2437 for (lender
= AC_COUNT
; (lender
>= 0) && (credits
[i
] > 0); lender
--) {
2438 if (wlfc
->credits_borrowed
[i
][lender
] > 0) {
2439 if (credits
[i
] >= wlfc
->credits_borrowed
[i
][lender
]) {
2441 (uint8
)wlfc
->credits_borrowed
[i
][lender
];
2442 wlfc
->FIFO_credit
[lender
] +=
2443 wlfc
->credits_borrowed
[i
][lender
];
2444 wlfc
->credits_borrowed
[i
][lender
] = 0;
2446 wlfc
->credits_borrowed
[i
][lender
] -= credits
[i
];
2447 wlfc
->FIFO_credit
[lender
] += credits
[i
];
2453 /* If we have more credits left over, these must belong to the AC */
2454 if (credits
[i
] > 0) {
2455 wlfc
->FIFO_credit
[i
] += credits
[i
];
2458 if (wlfc
->FIFO_credit
[i
] > wlfc
->Init_FIFO_credit
[i
]) {
2459 wlfc
->FIFO_credit
[i
] = wlfc
->Init_FIFO_credit
[i
];
2464 #if defined(DHD_WLFC_THREAD)
2465 _dhd_wlfc_thread_wakeup(dhd
);
2466 #endif /* defined(DHD_WLFC_THREAD) */
2469 } /* _dhd_wlfc_fifocreditback_indicate */
2472 /** !BCMDBUS specific function */
2474 _dhd_wlfc_suppress_txq(dhd_pub_t
*dhd
, f_processpkt_t fn
, void *arg
)
2476 athost_wl_status_info_t
* wlfc
= (athost_wl_status_info_t
*)dhd
->wlfc_state
;
2477 wlfc_mac_descriptor_t
* entry
;
2479 void *pkt
= NULL
, *head
= NULL
, *tail
= NULL
;
2480 struct pktq
*txq
= (struct pktq
*)dhd_bus_txq(dhd
->bus
);
2481 uint8 results
[WLFC_CTL_VALUE_LEN_TXSTATUS
+WLFC_CTL_VALUE_LEN_SEQ
];
2482 uint8 credits
[WLFC_CTL_VALUE_LEN_FIFO_CREDITBACK
] = {0};
2485 bool bCreditUpdate
= FALSE
;
2487 dhd_os_sdlock_txq(dhd
);
2488 for (prec
= 0; prec
< txq
->num_prec
; prec
++) {
2489 while ((pkt
= _dhd_wlfc_pktq_pdeq_with_fn(txq
, prec
, fn
, arg
))) {
2494 PKTSETLINK(tail
, pkt
);
2499 dhd_os_sdunlock_txq(dhd
);
2501 while ((pkt
= head
)) {
2502 head
= PKTLINK(pkt
);
2503 PKTSETLINK(pkt
, NULL
);
2505 entry
= _dhd_wlfc_find_table_entry(wlfc
, pkt
);
2507 PKTFREE(dhd
->osh
, pkt
, TRUE
);
2510 if (entry
->onbus_pkts_count
> 0) {
2511 entry
->onbus_pkts_count
--;
2513 if (entry
->suppressed
&&
2514 (!entry
->onbus_pkts_count
) &&
2515 (!entry
->suppr_transit_count
)) {
2516 entry
->suppressed
= FALSE
;
2518 /* fake a suppression txstatus */
2519 htod
= DHD_PKTTAG_H2DTAG(PKTTAG(pkt
));
2520 WL_TXSTATUS_SET_FLAGS(htod
, WLFC_CTL_PKTFLAG_WLSUPPRESS
);
2521 WL_TXSTATUS_SET_GENERATION(htod
, entry
->generation
);
2522 htod
= htol32(htod
);
2523 memcpy(results
, &htod
, WLFC_CTL_VALUE_LEN_TXSTATUS
);
2524 if (WLFC_GET_REUSESEQ(dhd
->wlfc_mode
)) {
2525 htodseq
= DHD_PKTTAG_H2DSEQ(PKTTAG(pkt
));
2526 if (IS_WL_TO_REUSE_SEQ(htodseq
)) {
2527 SET_WL_HAS_ASSIGNED_SEQ(htodseq
);
2528 RESET_WL_TO_REUSE_SEQ(htodseq
);
2530 htodseq
= htol16(htodseq
);
2531 memcpy(results
+ WLFC_CTL_VALUE_LEN_TXSTATUS
, &htodseq
,
2532 WLFC_CTL_VALUE_LEN_SEQ
);
2534 if (WLFC_GET_AFQ(dhd
->wlfc_mode
)) {
2535 _dhd_wlfc_enque_afq(wlfc
, pkt
);
2537 _dhd_wlfc_compressed_txstatus_update(dhd
, results
, 1, NULL
);
2539 /* fake a fifo credit back */
2540 if (DHD_PKTTAG_CREDITCHECK(PKTTAG(pkt
))) {
2541 credits
[DHD_PKTTAG_FIFO(PKTTAG(pkt
))]++;
2542 bCreditUpdate
= TRUE
;
2546 if (bCreditUpdate
) {
2547 _dhd_wlfc_fifocreditback_indicate(dhd
, credits
);
2549 } /* _dhd_wlfc_suppress_txq */
2550 #endif /* !BCMDBUS */
2553 _dhd_wlfc_dbg_senum_check(dhd_pub_t
*dhd
, uint8
*value
)
2559 bcopy(&value
[2], ×tamp
, sizeof(uint32
));
2560 timestamp
= ltoh32(timestamp
);
2561 DHD_INFO(("RXPKT: SEQ: %d, timestamp %d\n", value
[1], timestamp
));
2566 _dhd_wlfc_rssi_indicate(dhd_pub_t
*dhd
, uint8
* rssi
)
2574 _dhd_wlfc_add_requested_entry(athost_wl_status_info_t
* wlfc
, wlfc_mac_descriptor_t
* entry
)
2578 if (!wlfc
|| !entry
) {
2582 for (i
= 0; i
< wlfc
->requested_entry_count
; i
++) {
2583 if (entry
== wlfc
->requested_entry
[i
]) {
2588 if (i
== wlfc
->requested_entry_count
) {
2589 /* no match entry found */
2590 ASSERT(wlfc
->requested_entry_count
<= (WLFC_MAC_DESC_TABLE_SIZE
-1));
2591 wlfc
->requested_entry
[wlfc
->requested_entry_count
++] = entry
;
2595 /** called on eg receiving 'mac open' event from the dongle. */
2597 _dhd_wlfc_remove_requested_entry(athost_wl_status_info_t
* wlfc
, wlfc_mac_descriptor_t
* entry
)
2601 if (!wlfc
|| !entry
) {
2605 for (i
= 0; i
< wlfc
->requested_entry_count
; i
++) {
2606 if (entry
== wlfc
->requested_entry
[i
]) {
2611 if (i
< wlfc
->requested_entry_count
) {
2613 ASSERT(wlfc
->requested_entry_count
> 0);
2614 wlfc
->requested_entry_count
--;
2615 if (i
!= wlfc
->requested_entry_count
) {
2616 wlfc
->requested_entry
[i
] =
2617 wlfc
->requested_entry
[wlfc
->requested_entry_count
];
2619 wlfc
->requested_entry
[wlfc
->requested_entry_count
] = NULL
;
2623 /** called on eg receiving a WLFC_CTL_TYPE_MACDESC_ADD TLV from the dongle */
2625 _dhd_wlfc_mac_table_update(dhd_pub_t
*dhd
, uint8
* value
, uint8 type
)
2628 athost_wl_status_info_t
* wlfc
= (athost_wl_status_info_t
*)dhd
->wlfc_state
;
2629 wlfc_mac_descriptor_t
* table
;
2630 uint8 existing_index
;
2635 WLFC_DBGMESG(("%s(), mac [%02x:%02x:%02x:%02x:%02x:%02x],%s,idx:%d,id:0x%02x\n",
2636 __FUNCTION__
, value
[2], value
[3], value
[4], value
[5], value
[6], value
[7],
2637 ((type
== WLFC_CTL_TYPE_MACDESC_ADD
) ? "ADD":"DEL"),
2638 WLFC_MAC_DESC_GET_LOOKUP_INDEX(value
[0]), value
[0]));
2640 table
= wlfc
->destination_entries
.nodes
;
2641 table_index
= WLFC_MAC_DESC_GET_LOOKUP_INDEX(value
[0]);
2645 _dhd_wlfc_remove_requested_entry(wlfc
, &table
[table_index
]);
2646 if (type
== WLFC_CTL_TYPE_MACDESC_ADD
) {
2647 existing_index
= _dhd_wlfc_find_mac_desc_id_from_mac(dhd
, &value
[2]);
2648 if ((existing_index
!= WLFC_MAC_DESC_ID_INVALID
) &&
2649 (existing_index
!= table_index
) && table
[existing_index
].occupied
) {
2651 there is an existing different entry, free the old one
2652 and move it to new index if necessary.
2654 rc
= _dhd_wlfc_mac_entry_update(wlfc
, &table
[existing_index
],
2655 eWLFC_MAC_ENTRY_ACTION_DEL
, table
[existing_index
].interface_id
,
2656 table
[existing_index
].iftype
, NULL
, _dhd_wlfc_entrypkt_fn
,
2657 &table
[existing_index
]);
2660 if (!table
[table_index
].occupied
) {
2661 /* this new MAC entry does not exist, create one */
2662 table
[table_index
].mac_handle
= value
[0];
2663 rc
= _dhd_wlfc_mac_entry_update(wlfc
, &table
[table_index
],
2664 eWLFC_MAC_ENTRY_ACTION_ADD
, ifid
,
2665 wlfc
->destination_entries
.interfaces
[ifid
].iftype
,
2668 /* the space should have been empty, but it's not */
2669 wlfc
->stats
.mac_update_failed
++;
2673 if (type
== WLFC_CTL_TYPE_MACDESC_DEL
) {
2674 if (table
[table_index
].occupied
) {
2675 rc
= _dhd_wlfc_mac_entry_update(wlfc
, &table
[table_index
],
2676 eWLFC_MAC_ENTRY_ACTION_DEL
, ifid
,
2677 wlfc
->destination_entries
.interfaces
[ifid
].iftype
,
2678 ea
, _dhd_wlfc_entrypkt_fn
, &table
[table_index
]);
2680 /* the space should have been occupied, but it's not */
2681 wlfc
->stats
.mac_update_failed
++;
2686 } /* _dhd_wlfc_mac_table_update */
2688 /** Called on a 'mac open' or 'mac close' event indicated by the dongle */
2690 _dhd_wlfc_psmode_update(dhd_pub_t
*dhd
, uint8
* value
, uint8 type
)
2692 /* Handle PS on/off indication */
2693 athost_wl_status_info_t
* wlfc
= (athost_wl_status_info_t
*)dhd
->wlfc_state
;
2694 wlfc_mac_descriptor_t
* table
;
2695 wlfc_mac_descriptor_t
* desc
; /* a table maps from mac handle to mac descriptor */
2696 uint8 mac_handle
= value
[0];
2699 table
= wlfc
->destination_entries
.nodes
;
2700 desc
= &table
[WLFC_MAC_DESC_GET_LOOKUP_INDEX(mac_handle
)];
2701 if (desc
->occupied
) {
2702 if (type
== WLFC_CTL_TYPE_MAC_OPEN
) {
2703 desc
->state
= WLFC_STATE_OPEN
;
2704 desc
->ac_bitmap
= 0xff;
2705 DHD_WLFC_CTRINC_MAC_OPEN(desc
);
2706 desc
->requested_credit
= 0;
2707 desc
->requested_packet
= 0;
2708 _dhd_wlfc_remove_requested_entry(wlfc
, desc
);
2710 desc
->state
= WLFC_STATE_CLOSE
;
2711 DHD_WLFC_CTRINC_MAC_CLOSE(desc
);
2712 /* Indicate to firmware if there is any traffic pending. */
2713 for (i
= 0; i
< AC_COUNT
; i
++) {
2714 _dhd_wlfc_traffic_pending_check(wlfc
, desc
, i
);
2718 wlfc
->stats
.psmode_update_failed
++;
2722 } /* _dhd_wlfc_psmode_update */
2724 /** called upon receiving 'interface open' or 'interface close' event from the dongle */
2726 _dhd_wlfc_interface_update(dhd_pub_t
*dhd
, uint8
* value
, uint8 type
)
2728 /* Handle PS on/off indication */
2729 athost_wl_status_info_t
* wlfc
= (athost_wl_status_info_t
*)dhd
->wlfc_state
;
2730 wlfc_mac_descriptor_t
* table
;
2731 uint8 if_id
= value
[0];
2733 if (if_id
< WLFC_MAX_IFNUM
) {
2734 table
= wlfc
->destination_entries
.interfaces
;
2735 if (table
[if_id
].occupied
) {
2736 if (type
== WLFC_CTL_TYPE_INTERFACE_OPEN
) {
2737 table
[if_id
].state
= WLFC_STATE_OPEN
;
2738 /* WLFC_DBGMESG(("INTERFACE[%d] OPEN\n", if_id)); */
2740 table
[if_id
].state
= WLFC_STATE_CLOSE
;
2741 /* WLFC_DBGMESG(("INTERFACE[%d] CLOSE\n", if_id)); */
2746 wlfc
->stats
.interface_update_failed
++;
2751 /** Called on receiving a WLFC_CTL_TYPE_MAC_REQUEST_CREDIT TLV from the dongle */
2753 _dhd_wlfc_credit_request(dhd_pub_t
*dhd
, uint8
* value
)
2755 athost_wl_status_info_t
* wlfc
= (athost_wl_status_info_t
*)dhd
->wlfc_state
;
2756 wlfc_mac_descriptor_t
* table
;
2757 wlfc_mac_descriptor_t
* desc
;
2761 table
= wlfc
->destination_entries
.nodes
;
2762 mac_handle
= value
[1];
2765 desc
= &table
[WLFC_MAC_DESC_GET_LOOKUP_INDEX(mac_handle
)];
2766 if (desc
->occupied
) {
2767 desc
->requested_credit
= credit
;
2769 desc
->ac_bitmap
= value
[2] & (~(1<<AC_COUNT
));
2770 _dhd_wlfc_add_requested_entry(wlfc
, desc
);
2771 #if defined(DHD_WLFC_THREAD)
2773 _dhd_wlfc_thread_wakeup(dhd
);
2775 #endif /* DHD_WLFC_THREAD */
2777 wlfc
->stats
.credit_request_failed
++;
2783 /** Called on receiving a WLFC_CTL_TYPE_MAC_REQUEST_PACKET TLV from the dongle */
2785 _dhd_wlfc_packet_request(dhd_pub_t
*dhd
, uint8
* value
)
2787 athost_wl_status_info_t
* wlfc
= (athost_wl_status_info_t
*)dhd
->wlfc_state
;
2788 wlfc_mac_descriptor_t
* table
;
2789 wlfc_mac_descriptor_t
* desc
;
2793 table
= wlfc
->destination_entries
.nodes
;
2794 mac_handle
= value
[1];
2795 packet_count
= value
[0];
2797 desc
= &table
[WLFC_MAC_DESC_GET_LOOKUP_INDEX(mac_handle
)];
2798 if (desc
->occupied
) {
2799 desc
->requested_packet
= packet_count
;
2801 desc
->ac_bitmap
= value
[2] & (~(1<<AC_COUNT
));
2802 _dhd_wlfc_add_requested_entry(wlfc
, desc
);
2803 #if defined(DHD_WLFC_THREAD)
2805 _dhd_wlfc_thread_wakeup(dhd
);
2807 #endif /* DHD_WLFC_THREAD */
2809 wlfc
->stats
.packet_request_failed
++;
2815 /** Called when host receives a WLFC_CTL_TYPE_HOST_REORDER_RXPKTS TLV from the dongle */
2817 _dhd_wlfc_reorderinfo_indicate(uint8
*val
, uint8 len
, uchar
*info_buf
, uint
*info_len
)
2820 /* Check copy length to avoid buffer overrun. In case of length exceeding
2821 * WLHOST_REORDERDATA_TOTLEN, return failure instead sending incomplete result
2822 * of length WLHOST_REORDERDATA_TOTLEN
2824 if ((info_buf
) && (len
<= WLHOST_REORDERDATA_TOTLEN
)) {
2825 bcopy(val
, info_buf
, len
);
2837 bool dhd_wlfc_is_supported(dhd_pub_t
*dhd
)
2842 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__
, __LINE__
));
2846 dhd_os_wlfc_block(dhd
);
2848 if (!dhd
->wlfc_state
|| (dhd
->proptxstatus_mode
== WLFC_FCMODE_NONE
)) {
2852 dhd_os_wlfc_unblock(dhd
);
2857 int dhd_wlfc_enable(dhd_pub_t
*dhd
)
2859 int i
, rc
= BCME_OK
;
2860 athost_wl_status_info_t
* wlfc
;
2863 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__
, __LINE__
));
2867 dhd_os_wlfc_block(dhd
);
2869 if (!dhd
->wlfc_enabled
|| dhd
->wlfc_state
) {
2874 /* allocate space to track txstatus propagated from firmware */
2875 dhd
->wlfc_state
= DHD_OS_PREALLOC(dhd
, DHD_PREALLOC_DHD_WLFC_INFO
,
2876 sizeof(athost_wl_status_info_t
));
2877 if (dhd
->wlfc_state
== NULL
) {
2882 /* initialize state space */
2883 wlfc
= (athost_wl_status_info_t
*)dhd
->wlfc_state
;
2884 memset(wlfc
, 0, sizeof(athost_wl_status_info_t
));
2886 /* remember osh & dhdp */
2887 wlfc
->osh
= dhd
->osh
;
2890 if (!WLFC_GET_AFQ(dhd
->wlfc_mode
)) {
2891 wlfc
->hanger
= _dhd_wlfc_hanger_create(dhd
, WLFC_HANGER_MAXITEMS
);
2892 if (wlfc
->hanger
== NULL
) {
2893 DHD_OS_PREFREE(dhd
, dhd
->wlfc_state
,
2894 sizeof(athost_wl_status_info_t
));
2895 dhd
->wlfc_state
= NULL
;
2901 dhd
->proptxstatus_mode
= WLFC_FCMODE_EXPLICIT_CREDIT
;
2902 /* default to check rx pkt */
2903 dhd
->wlfc_rxpkt_chk
= TRUE
;
2904 if (dhd
->op_mode
& DHD_FLAG_IBSS_MODE
) {
2905 dhd
->wlfc_rxpkt_chk
= FALSE
;
2908 /* initialize all interfaces to accept traffic */
2909 for (i
= 0; i
< WLFC_MAX_IFNUM
; i
++) {
2910 wlfc
->hostif_flow_state
[i
] = OFF
;
2913 _dhd_wlfc_mac_entry_update(wlfc
, &wlfc
->destination_entries
.other
,
2914 eWLFC_MAC_ENTRY_ACTION_ADD
, 0xff, 0, NULL
, NULL
, NULL
);
2916 wlfc
->allow_credit_borrow
= 0;
2917 wlfc
->single_ac
= 0;
2918 wlfc
->single_ac_timestamp
= 0;
2922 DHD_ERROR(("%s: ret=%d\n", __FUNCTION__
, rc
));
2923 dhd_os_wlfc_unblock(dhd
);
2926 } /* dhd_wlfc_enable */
2928 #ifdef SUPPORT_P2P_GO_PS
2931 * Called when the host platform enters a lower power mode, eg right before a system hibernate.
2932 * SUPPORT_P2P_GO_PS specific function.
2935 dhd_wlfc_suspend(dhd_pub_t
*dhd
)
2939 DHD_TRACE(("%s: masking wlfc events\n", __FUNCTION__
));
2940 if (!dhd
->wlfc_enabled
)
2943 if (!dhd_wl_ioctl_get_intiovar(dhd
, "tlv", &tlv
, WLC_GET_VAR
, FALSE
, 0))
2945 if ((tlv
& (WLFC_FLAGS_RSSI_SIGNALS
| WLFC_FLAGS_XONXOFF_SIGNALS
)) == 0)
2947 tlv
&= ~(WLFC_FLAGS_RSSI_SIGNALS
| WLFC_FLAGS_XONXOFF_SIGNALS
);
2948 if (!dhd_wl_ioctl_set_intiovar(dhd
, "tlv", tlv
, WLC_SET_VAR
, TRUE
, 0))
2955 * Called when the host platform resumes from a power management operation, eg resume after a
2956 * system hibernate. SUPPORT_P2P_GO_PS specific function.
2959 dhd_wlfc_resume(dhd_pub_t
*dhd
)
2963 DHD_TRACE(("%s: unmasking wlfc events\n", __FUNCTION__
));
2964 if (!dhd
->wlfc_enabled
)
2967 if (!dhd_wl_ioctl_get_intiovar(dhd
, "tlv", &tlv
, WLC_GET_VAR
, FALSE
, 0))
2969 if ((tlv
& (WLFC_FLAGS_RSSI_SIGNALS
| WLFC_FLAGS_XONXOFF_SIGNALS
)) ==
2970 (WLFC_FLAGS_RSSI_SIGNALS
| WLFC_FLAGS_XONXOFF_SIGNALS
))
2972 tlv
|= (WLFC_FLAGS_RSSI_SIGNALS
| WLFC_FLAGS_XONXOFF_SIGNALS
);
2973 if (!dhd_wl_ioctl_set_intiovar(dhd
, "tlv", tlv
, WLC_SET_VAR
, TRUE
, 0))
2979 #endif /* SUPPORT_P2P_GO_PS */
2981 /** A flow control header was received from firmware, containing one or more TLVs */
2983 dhd_wlfc_parse_header_info(dhd_pub_t
*dhd
, void* pktbuf
, int tlv_hdr_len
, uchar
*reorder_info_buf
,
2984 uint
*reorder_info_len
)
2989 uint16 remainder
= (uint16
)tlv_hdr_len
;
2990 uint16 processed
= 0;
2991 athost_wl_status_info_t
* wlfc
= NULL
;
2994 if ((dhd
== NULL
) || (pktbuf
== NULL
)) {
2995 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__
, __LINE__
));
2999 dhd_os_wlfc_block(dhd
);
3001 if (dhd
->proptxstatus_mode
!= WLFC_ONLY_AMPDU_HOSTREORDER
) {
3002 if (!dhd
->wlfc_state
|| (dhd
->proptxstatus_mode
== WLFC_FCMODE_NONE
)) {
3003 dhd_os_wlfc_unblock(dhd
);
3004 return WLFC_UNSUPPORTED
;
3006 wlfc
= (athost_wl_status_info_t
*)dhd
->wlfc_state
;
3009 tmpbuf
= (uint8
*)PKTDATA(dhd
->osh
, pktbuf
);
3012 while ((processed
< (WLFC_MAX_PENDING_DATALEN
* 2)) && (remainder
> 0)) {
3013 type
= tmpbuf
[processed
];
3014 if (type
== WLFC_CTL_TYPE_FILLER
) {
3020 len
= tmpbuf
[processed
+ 1];
3021 value
= &tmpbuf
[processed
+ 2];
3023 if (remainder
< (2 + len
))
3026 remainder
-= 2 + len
;
3027 processed
+= 2 + len
;
3030 DHD_INFO(("%s():%d type %d remainder %d processed %d\n",
3031 __FUNCTION__
, __LINE__
, type
, remainder
, processed
));
3033 if (type
== WLFC_CTL_TYPE_HOST_REORDER_RXPKTS
)
3034 _dhd_wlfc_reorderinfo_indicate(value
, len
, reorder_info_buf
,
3038 ASSERT(dhd
->proptxstatus_mode
== WLFC_ONLY_AMPDU_HOSTREORDER
);
3040 if (type
!= WLFC_CTL_TYPE_HOST_REORDER_RXPKTS
&&
3041 type
!= WLFC_CTL_TYPE_TRANS_ID
)
3042 DHD_INFO(("%s():%d dhd->wlfc_state is NULL yet!"
3043 " type %d remainder %d processed %d\n",
3044 __FUNCTION__
, __LINE__
, type
, remainder
, processed
));
3048 if (type
== WLFC_CTL_TYPE_TXSTATUS
) {
3049 _dhd_wlfc_compressed_txstatus_update(dhd
, value
, 1, &entry
);
3050 } else if (type
== WLFC_CTL_TYPE_COMP_TXSTATUS
) {
3051 uint8 compcnt_offset
= WLFC_CTL_VALUE_LEN_TXSTATUS
;
3053 if (WLFC_GET_REUSESEQ(dhd
->wlfc_mode
)) {
3054 compcnt_offset
+= WLFC_CTL_VALUE_LEN_SEQ
;
3056 _dhd_wlfc_compressed_txstatus_update(dhd
, value
,
3057 value
[compcnt_offset
], &entry
);
3058 } else if (type
== WLFC_CTL_TYPE_FIFO_CREDITBACK
) {
3059 _dhd_wlfc_fifocreditback_indicate(dhd
, value
);
3060 } else if (type
== WLFC_CTL_TYPE_RSSI
) {
3061 _dhd_wlfc_rssi_indicate(dhd
, value
);
3062 } else if (type
== WLFC_CTL_TYPE_MAC_REQUEST_CREDIT
) {
3063 _dhd_wlfc_credit_request(dhd
, value
);
3064 } else if (type
== WLFC_CTL_TYPE_MAC_REQUEST_PACKET
) {
3065 _dhd_wlfc_packet_request(dhd
, value
);
3066 } else if ((type
== WLFC_CTL_TYPE_MAC_OPEN
) ||
3067 (type
== WLFC_CTL_TYPE_MAC_CLOSE
)) {
3068 _dhd_wlfc_psmode_update(dhd
, value
, type
);
3069 } else if ((type
== WLFC_CTL_TYPE_MACDESC_ADD
) ||
3070 (type
== WLFC_CTL_TYPE_MACDESC_DEL
)) {
3071 _dhd_wlfc_mac_table_update(dhd
, value
, type
);
3072 } else if (type
== WLFC_CTL_TYPE_TRANS_ID
) {
3073 _dhd_wlfc_dbg_senum_check(dhd
, value
);
3074 } else if ((type
== WLFC_CTL_TYPE_INTERFACE_OPEN
) ||
3075 (type
== WLFC_CTL_TYPE_INTERFACE_CLOSE
)) {
3076 _dhd_wlfc_interface_update(dhd
, value
, type
);
3080 if (entry
&& WLFC_GET_REORDERSUPP(dhd
->wlfc_mode
)) {
3081 /* suppress all packets for this mac entry from bus->txq */
3082 _dhd_wlfc_suppress_txq(dhd
, _dhd_wlfc_entrypkt_fn
, entry
);
3084 #endif /* !BCMDBUS */
3087 if (remainder
!= 0 && wlfc
) {
3088 /* trouble..., something is not right */
3089 wlfc
->stats
.tlv_parse_failed
++;
3094 wlfc
->stats
.dhd_hdrpulls
++;
3096 dhd_os_wlfc_unblock(dhd
);
3100 KERNEL_THREAD_RETURN_TYPE
3101 dhd_wlfc_transfer_packets(void *data
)
3103 dhd_pub_t
*dhdp
= (dhd_pub_t
*)data
;
3104 int ac
, single_ac
= 0, rc
= BCME_OK
;
3105 dhd_wlfc_commit_info_t commit_info
;
3106 athost_wl_status_info_t
* ctx
;
3107 int bus_retry_count
= 0;
3109 int pkt_send_per_ac
= 0;
3111 uint8 tx_map
= 0; /* packets (send + in queue), Bitmask for 4 ACs + BC/MC */
3112 uint8 rx_map
= 0; /* received packets, Bitmask for 4 ACs + BC/MC */
3113 uint8 packets_map
= 0; /* packets in queue, Bitmask for 4 ACs + BC/MC */
3114 bool no_credit
= FALSE
;
3118 #if defined(DHD_WLFC_THREAD)
3119 /* wait till someone wakeup me up, will change it at running time */
3120 int wait_msec
= msecs_to_jiffies(0xFFFFFFFF);
3121 #endif /* defined(DHD_WLFC_THREAD) */
3123 #if defined(DHD_WLFC_THREAD)
3125 bus_retry_count
= 0;
3130 wait_msec
= wait_event_interruptible_timeout(dhdp
->wlfc_wqhead
,
3131 dhdp
->wlfc_thread_go
, wait_msec
);
3132 if (kthread_should_stop()) {
3135 dhdp
->wlfc_thread_go
= FALSE
;
3137 dhd_os_wlfc_block(dhdp
);
3138 #endif /* defined(DHD_WLFC_THREAD) */
3139 ctx
= (athost_wl_status_info_t
*)dhdp
->wlfc_state
;
3140 #if defined(DHD_WLFC_THREAD)
3143 #endif /* defined(DHD_WLFC_THREAD) */
3145 memset(&commit_info
, 0, sizeof(commit_info
));
3148 Commit packets for regular AC traffic. Higher priority first.
3149 First, use up FIFO credits available to each AC. Based on distribution
3150 and credits left, borrow from other ACs as applicable
3153 If the bus between the host and firmware is overwhelmed by the
3154 traffic from host, it is possible that higher priority traffic
3155 starves the lower priority queue. If that occurs often, we may
3156 have to employ weighted round-robin or ucode scheme to avoid
3157 low priority packet starvation.
3160 for (ac
= AC_COUNT
; ac
>= 0; ac
--) {
3161 if (dhdp
->wlfc_rxpkt_chk
) {
3162 /* check rx packet */
3163 uint32 curr_t
= OSL_SYSUPTIME(), delta
;
3165 delta
= curr_t
- ctx
->rx_timestamp
[ac
];
3166 if (delta
< WLFC_RX_DETECTION_THRESHOLD_MS
) {
3167 rx_map
|= (1 << ac
);
3171 if (ctx
->pkt_cnt_per_ac
[ac
] == 0) {
3175 tx_map
|= (1 << ac
);
3177 pkt_send_per_ac
= 0;
3178 while ((FALSE
== dhdp
->proptxstatus_txoff
) &&
3179 (pkt_send_per_ac
< WLFC_PACKET_BOUND
)) {
3180 /* packets from delayQ with less priority are fresh and
3181 * they'd need header and have no MAC entry
3183 no_credit
= (ctx
->FIFO_credit
[ac
] < 1);
3184 if (dhdp
->proptxstatus_credit_ignore
||
3185 ((ac
== AC_COUNT
) && !ctx
->bcmc_credit_supported
)) {
3191 if (no_credit
&& (ac
< AC_COUNT
) && (tx_map
>= rx_map
) &&
3192 dhdp
->wlfc_borrow_allowed
) {
3193 /* try borrow from lower priority */
3194 lender
= _dhd_wlfc_borrow_credit(ctx
, ac
- 1, ac
, FALSE
);
3200 commit_info
.needs_hdr
= 1;
3201 commit_info
.mac_entry
= NULL
;
3202 commit_info
.p
= _dhd_wlfc_deque_delayedq(ctx
, ac
,
3203 &(commit_info
.ac_fifo_credit_spent
),
3204 &(commit_info
.needs_hdr
),
3205 &(commit_info
.mac_entry
),
3207 commit_info
.pkt_type
= (commit_info
.needs_hdr
) ? eWLFC_PKTTYPE_DELAYED
:
3208 eWLFC_PKTTYPE_SUPPRESSED
;
3210 if (commit_info
.p
== NULL
) {
3212 if (lender
!= -1 && dhdp
->wlfc_borrow_allowed
) {
3213 _dhd_wlfc_return_credit(ctx
, lender
, ac
);
3219 if (!dhdp
->proptxstatus_credit_ignore
&& (lender
== -1)) {
3220 ASSERT(ctx
->FIFO_credit
[ac
] >= commit_info
.ac_fifo_credit_spent
);
3222 /* here we can ensure have credit or no credit needed */
3223 rc
= _dhd_wlfc_handle_packet_commit(ctx
, ac
, &commit_info
,
3224 ctx
->fcommit
, ctx
->commit_ctx
);
3226 /* Bus commits may fail (e.g. flow control); abort after retries */
3227 if (rc
== BCME_OK
) {
3230 if (commit_info
.ac_fifo_credit_spent
&& (lender
== -1)) {
3231 ctx
->FIFO_credit
[ac
]--;
3234 else if (!commit_info
.ac_fifo_credit_spent
&& (lender
!= -1) &&
3235 dhdp
->wlfc_borrow_allowed
) {
3236 _dhd_wlfc_return_credit(ctx
, lender
, ac
);
3241 if (lender
!= -1 && dhdp
->wlfc_borrow_allowed
) {
3242 _dhd_wlfc_return_credit(ctx
, lender
, ac
);
3246 if (bus_retry_count
>= BUS_RETRIES
) {
3247 DHD_ERROR(("%s: bus error %d\n", __FUNCTION__
, rc
));
3253 if (ctx
->pkt_cnt_per_ac
[ac
]) {
3254 packets_map
|= (1 << ac
);
3258 if ((tx_map
== 0) || dhdp
->proptxstatus_credit_ignore
) {
3259 /* nothing send out or remain in queue */
3264 if (((tx_map
& (tx_map
- 1)) == 0) && (tx_map
>= rx_map
)) {
3265 /* only one tx ac exist and no higher rx ac */
3266 if ((single_ac
== ctx
->single_ac
) && ctx
->allow_credit_borrow
) {
3270 uint32 curr_t
= OSL_SYSUPTIME();
3272 if (single_ac
!= ctx
->single_ac
) {
3273 /* new single ac traffic (first single ac or different single ac) */
3274 ctx
->allow_credit_borrow
= 0;
3275 ctx
->single_ac_timestamp
= curr_t
;
3276 ctx
->single_ac
= (uint8
)single_ac
;
3280 /* same ac traffic, check if it lasts enough time */
3281 delta
= curr_t
- ctx
->single_ac_timestamp
;
3283 if (delta
>= WLFC_BORROW_DEFER_PERIOD_MS
) {
3284 /* wait enough time, can borrow now */
3285 ctx
->allow_credit_borrow
= 1;
3293 /* If we have multiple AC traffic, turn off borrowing, mark time and bail out */
3294 ctx
->allow_credit_borrow
= 0;
3295 ctx
->single_ac_timestamp
= 0;
3301 if (packets_map
== 0) {
3302 /* nothing to send, skip borrow */
3307 /* At this point, borrow all credits only for ac */
3308 while (FALSE
== dhdp
->proptxstatus_txoff
) {
3310 if (dhdp
->wlfc_borrow_allowed
) {
3311 if ((lender
= _dhd_wlfc_borrow_credit(ctx
, AC_COUNT
, ac
, TRUE
)) == -1) {
3318 commit_info
.p
= _dhd_wlfc_deque_delayedq(ctx
, ac
,
3319 &(commit_info
.ac_fifo_credit_spent
),
3320 &(commit_info
.needs_hdr
),
3321 &(commit_info
.mac_entry
),
3323 if (commit_info
.p
== NULL
) {
3324 /* before borrow only one ac exists and now this only ac is empty */
3326 _dhd_wlfc_return_credit(ctx
, lender
, ac
);
3331 commit_info
.pkt_type
= (commit_info
.needs_hdr
) ? eWLFC_PKTTYPE_DELAYED
:
3332 eWLFC_PKTTYPE_SUPPRESSED
;
3334 rc
= _dhd_wlfc_handle_packet_commit(ctx
, ac
, &commit_info
,
3335 ctx
->fcommit
, ctx
->commit_ctx
);
3337 /* Bus commits may fail (e.g. flow control); abort after retries */
3338 if (rc
== BCME_OK
) {
3340 if (commit_info
.ac_fifo_credit_spent
) {
3341 #ifndef LIMIT_BORROW
3342 ctx
->FIFO_credit
[ac
]--;
3346 _dhd_wlfc_return_credit(ctx
, lender
, ac
);
3351 _dhd_wlfc_return_credit(ctx
, lender
, ac
);
3354 if (bus_retry_count
>= BUS_RETRIES
) {
3355 DHD_ERROR(("%s: bus error %d\n", __FUNCTION__
, rc
));
3361 BCM_REFERENCE(pkt_send
);
3364 #if defined(DHD_WLFC_THREAD)
3365 dhd_os_wlfc_unblock(dhdp
);
3366 if (ctx
&& ctx
->pkt_cnt_in_psq
&& pkt_send
) {
3367 wait_msec
= msecs_to_jiffies(WLFC_THREAD_QUICK_RETRY_WAIT_MS
);
3369 wait_msec
= msecs_to_jiffies(WLFC_THREAD_RETRY_WAIT_MS
);
3375 #endif /* defined(DHD_WLFC_THREAD) */
3379 * Enqueues a transmit packet in the next layer towards the dongle, eg the DBUS layer. Called by
3381 * @param[in] dhdp Pointer to public DHD structure
3382 * @param[in] fcommit Pointer to transmit function of next layer
3383 * @param[in] commit_ctx Opaque context used when calling next layer
3384 * @param[in] pktbuf Packet to send
3385 * @param[in] need_toggle_host_if If TRUE, resets flag ctx->toggle_host_if
3388 dhd_wlfc_commit_packets(dhd_pub_t
*dhdp
, f_commitpkt_t fcommit
, void* commit_ctx
, void *pktbuf
,
3389 bool need_toggle_host_if
)
3392 athost_wl_status_info_t
* ctx
;
3394 #if defined(DHD_WLFC_THREAD)
3397 #endif /* defined(DHD_WLFC_THREAD) */
3399 if ((dhdp
== NULL
) || (fcommit
== NULL
)) {
3400 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__
, __LINE__
));
3404 dhd_os_wlfc_block(dhdp
);
3406 if (!dhdp
->wlfc_state
|| (dhdp
->proptxstatus_mode
== WLFC_FCMODE_NONE
)) {
3408 DHD_PKTTAG_WLFCPKT_SET(PKTTAG(pktbuf
), 0);
3410 rc
= WLFC_UNSUPPORTED
;
3414 ctx
= (athost_wl_status_info_t
*)dhdp
->wlfc_state
;
3417 if (!dhdp
->up
|| (dhdp
->busstate
== DHD_BUS_DOWN
)) {
3419 PKTFREE(ctx
->osh
, pktbuf
, TRUE
);
3424 #endif /* BCMDBUS */
3426 if (dhdp
->proptxstatus_module_ignore
) {
3429 WL_TXSTATUS_SET_FLAGS(htod
, WLFC_PKTFLAG_PKTFROMHOST
);
3430 _dhd_wlfc_pushheader(ctx
, &pktbuf
, FALSE
, 0, 0, htod
, 0, FALSE
);
3431 if (fcommit(commit_ctx
, pktbuf
)) {
3432 /* free it if failed, otherwise do it in tx complete cb */
3433 PKTFREE(ctx
->osh
, pktbuf
, TRUE
);
3441 int ac
= DHD_PKTTAG_FIFO(PKTTAG(pktbuf
));
3442 ASSERT(ac
<= AC_COUNT
);
3443 DHD_PKTTAG_WLFCPKT_SET(PKTTAG(pktbuf
), 1);
3444 /* en-queue the packets to respective queue. */
3445 rc
= _dhd_wlfc_enque_delayq(ctx
, pktbuf
, ac
);
3447 _dhd_wlfc_prec_drop(ctx
->dhdp
, (ac
<< 1), pktbuf
, FALSE
);
3450 ctx
->pkt_cnt_in_drv
[DHD_PKTTAG_IF(PKTTAG(pktbuf
))][ac
]++;
3454 if (!ctx
->fcommit
) {
3455 ctx
->fcommit
= fcommit
;
3457 ASSERT(ctx
->fcommit
== fcommit
);
3459 if (!ctx
->commit_ctx
) {
3460 ctx
->commit_ctx
= commit_ctx
;
3462 ASSERT(ctx
->commit_ctx
== commit_ctx
);
3465 #if defined(DHD_WLFC_THREAD)
3466 _dhd_wlfc_thread_wakeup(dhdp
);
3468 dhd_wlfc_transfer_packets(dhdp
);
3469 #endif /* defined(DHD_WLFC_THREAD) */
3472 dhd_os_wlfc_unblock(dhdp
);
3474 } /* dhd_wlfc_commit_packets */
3477 * Called when the (lower) DBUS layer indicates completion (succesfull or not) of a transmit packet
3480 dhd_wlfc_txcomplete(dhd_pub_t
*dhd
, void *txp
, bool success
)
3482 athost_wl_status_info_t
* wlfc
;
3483 wlfc_mac_descriptor_t
*entry
;
3486 if ((dhd
== NULL
) || (txp
== NULL
)) {
3487 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__
, __LINE__
));
3491 bcm_pkt_validate_chk(txp
);
3493 dhd_os_wlfc_block(dhd
);
3495 if (!dhd
->wlfc_state
|| (dhd
->proptxstatus_mode
== WLFC_FCMODE_NONE
)) {
3496 rtn
= WLFC_UNSUPPORTED
;
3500 wlfc
= (athost_wl_status_info_t
*)dhd
->wlfc_state
;
3501 if (DHD_PKTTAG_SIGNALONLY(PKTTAG(txp
))) {
3502 #ifdef PROP_TXSTATUS_DEBUG
3503 wlfc
->stats
.signal_only_pkts_freed
++;
3505 /* is this a signal-only packet? */
3506 _dhd_wlfc_pullheader(wlfc
, txp
);
3507 PKTFREE(wlfc
->osh
, txp
, TRUE
);
3511 entry
= _dhd_wlfc_find_table_entry(wlfc
, txp
);
3514 if (!success
|| dhd
->proptxstatus_txstatus_ignore
) {
3515 WLFC_DBGMESG(("At: %s():%d, bus_complete() failure for %p, htod_tag:0x%08x\n",
3516 __FUNCTION__
, __LINE__
, txp
, DHD_PKTTAG_H2DTAG(PKTTAG(txp
))));
3517 if (!WLFC_GET_AFQ(dhd
->wlfc_mode
)) {
3518 _dhd_wlfc_hanger_poppkt(wlfc
->hanger
, WL_TXSTATUS_GET_HSLOT(
3519 DHD_PKTTAG_H2DTAG(PKTTAG(txp
))), &pout
, TRUE
);
3520 ASSERT(txp
== pout
);
3523 /* indicate failure and free the packet */
3524 dhd_txcomplete(dhd
, txp
, success
);
3526 /* return the credit, if necessary */
3527 _dhd_wlfc_return_implied_credit(wlfc
, txp
);
3529 if (entry
->transit_count
)
3530 entry
->transit_count
--;
3531 if (entry
->suppr_transit_count
)
3532 entry
->suppr_transit_count
--;
3533 wlfc
->pkt_cnt_in_drv
[DHD_PKTTAG_IF(PKTTAG(txp
))][DHD_PKTTAG_FIFO(PKTTAG(txp
))]--;
3534 wlfc
->stats
.pktout
++;
3535 PKTFREE(wlfc
->osh
, txp
, TRUE
);
3537 /* bus confirmed pkt went to firmware side */
3538 if (WLFC_GET_AFQ(dhd
->wlfc_mode
)) {
3539 _dhd_wlfc_enque_afq(wlfc
, txp
);
3541 int hslot
= WL_TXSTATUS_GET_HSLOT(DHD_PKTTAG_H2DTAG(PKTTAG(txp
)));
3542 _dhd_wlfc_hanger_free_pkt(wlfc
, hslot
,
3543 WLFC_HANGER_PKT_STATE_BUSRETURNED
, -1);
3547 ASSERT(entry
->onbus_pkts_count
> 0);
3548 if (entry
->onbus_pkts_count
> 0)
3549 entry
->onbus_pkts_count
--;
3550 if (entry
->suppressed
&&
3551 (!entry
->onbus_pkts_count
) &&
3552 (!entry
->suppr_transit_count
))
3553 entry
->suppressed
= FALSE
;
3555 dhd_os_wlfc_unblock(dhd
);
3557 } /* dhd_wlfc_txcomplete */
3560 dhd_wlfc_init(dhd_pub_t
*dhd
)
3562 /* enable all signals & indicate host proptxstatus logic is active */
3563 uint32 tlv
, mode
, fw_caps
;
3567 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__
, __LINE__
));
3571 dhd_os_wlfc_block(dhd
);
3572 if (dhd
->wlfc_enabled
) {
3573 DHD_ERROR(("%s():%d, Already enabled!\n", __FUNCTION__
, __LINE__
));
3574 dhd_os_wlfc_unblock(dhd
);
3577 dhd
->wlfc_enabled
= TRUE
;
3578 dhd_os_wlfc_unblock(dhd
);
3580 tlv
= WLFC_FLAGS_RSSI_SIGNALS
|
3581 WLFC_FLAGS_XONXOFF_SIGNALS
|
3582 WLFC_FLAGS_CREDIT_STATUS_SIGNALS
|
3583 WLFC_FLAGS_HOST_PROPTXSTATUS_ACTIVE
|
3584 WLFC_FLAGS_HOST_RXRERODER_ACTIVE
;
3588 try to enable/disable signaling by sending "tlv" iovar. if that fails,
3589 fallback to no flow control? Print a message for now.
3592 /* enable proptxtstatus signaling by default */
3593 if (!dhd_wl_ioctl_set_intiovar(dhd
, "tlv", tlv
, WLC_SET_VAR
, TRUE
, 0)) {
3595 Leaving the message for now, it should be removed after a while; once
3596 the tlv situation is stable.
3598 DHD_ERROR(("dhd_wlfc_init(): successfully %s bdcv2 tlv signaling, %d\n",
3599 dhd
->wlfc_enabled
?"enabled":"disabled", tlv
));
3605 ret
= dhd_wl_ioctl_get_intiovar(dhd
, "wlfc_mode", &fw_caps
, WLC_GET_VAR
, FALSE
, 0);
3608 DHD_ERROR(("%s: query wlfc_mode succeed, fw_caps=0x%x\n", __FUNCTION__
, fw_caps
));
3610 if (WLFC_IS_OLD_DEF(fw_caps
)) {
3612 mode
= WLFC_MODE_HANGER
;
3614 /* enable proptxtstatus v2 by default */
3615 mode
= WLFC_MODE_AFQ
;
3616 #endif /* BCMDBUS */
3618 WLFC_SET_AFQ(mode
, WLFC_GET_AFQ(fw_caps
));
3620 WLFC_SET_AFQ(mode
, 0);
3621 #endif /* BCMDBUS */
3622 WLFC_SET_REUSESEQ(mode
, WLFC_GET_REUSESEQ(fw_caps
));
3623 WLFC_SET_REORDERSUPP(mode
, WLFC_GET_REORDERSUPP(fw_caps
));
3625 ret
= dhd_wl_ioctl_set_intiovar(dhd
, "wlfc_mode", mode
, WLC_SET_VAR
, TRUE
, 0);
3628 dhd_os_wlfc_block(dhd
);
3632 if (WLFC_IS_OLD_DEF(mode
)) {
3633 WLFC_SET_AFQ(dhd
->wlfc_mode
, (mode
== WLFC_MODE_AFQ
));
3635 dhd
->wlfc_mode
= mode
;
3639 DHD_ERROR(("dhd_wlfc_init(): wlfc_mode=0x%x, ret=%d\n", dhd
->wlfc_mode
, ret
));
3641 dhd
->wlfc_borrow_allowed
= TRUE
;
3643 dhd_os_wlfc_unblock(dhd
);
3646 dhd
->plat_init((void *)dhd
);
3649 } /* dhd_wlfc_init */
3651 /** AMPDU host reorder specific function */
3653 dhd_wlfc_hostreorder_init(dhd_pub_t
*dhd
)
3655 /* enable only ampdu hostreorder here */
3659 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__
, __LINE__
));
3663 DHD_TRACE(("%s():%d Enter\n", __FUNCTION__
, __LINE__
));
3665 tlv
= WLFC_FLAGS_HOST_RXRERODER_ACTIVE
;
3667 /* enable proptxtstatus signaling by default */
3668 if (dhd_wl_ioctl_set_intiovar(dhd
, "tlv", tlv
, WLC_SET_VAR
, TRUE
, 0)) {
3669 DHD_ERROR(("%s(): failed to enable/disable bdcv2 tlv signaling\n",
3673 Leaving the message for now, it should be removed after a while; once
3674 the tlv situation is stable.
3676 DHD_ERROR(("%s(): successful bdcv2 tlv signaling, %d\n",
3677 __FUNCTION__
, tlv
));
3680 dhd_os_wlfc_block(dhd
);
3681 dhd
->proptxstatus_mode
= WLFC_ONLY_AMPDU_HOSTREORDER
;
3682 dhd_os_wlfc_unblock(dhd
);
3683 /* terence 20161229: enable ampdu_hostreorder if tlv enable hostreorder */
3684 dhd_conf_set_intiovar(dhd
, WLC_SET_VAR
, "ampdu_hostreorder", 1, 0, TRUE
);
3690 dhd_wlfc_cleanup_txq(dhd_pub_t
*dhd
, f_processpkt_t fn
, void *arg
)
3693 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__
, __LINE__
));
3697 dhd_os_wlfc_block(dhd
);
3699 if (!dhd
->wlfc_state
|| (dhd
->proptxstatus_mode
== WLFC_FCMODE_NONE
)) {
3700 dhd_os_wlfc_unblock(dhd
);
3701 return WLFC_UNSUPPORTED
;
3705 _dhd_wlfc_cleanup_txq(dhd
, fn
, arg
);
3706 #endif /* !BCMDBUS */
3708 dhd_os_wlfc_unblock(dhd
);
3713 /** release all packet resources */
3715 dhd_wlfc_cleanup(dhd_pub_t
*dhd
, f_processpkt_t fn
, void *arg
)
3718 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__
, __LINE__
));
3722 dhd_os_wlfc_block(dhd
);
3724 if (!dhd
->wlfc_state
|| (dhd
->proptxstatus_mode
== WLFC_FCMODE_NONE
)) {
3725 dhd_os_wlfc_unblock(dhd
);
3726 return WLFC_UNSUPPORTED
;
3729 _dhd_wlfc_cleanup(dhd
, fn
, arg
);
3731 dhd_os_wlfc_unblock(dhd
);
3737 dhd_wlfc_deinit(dhd_pub_t
*dhd
)
3739 /* cleanup all psq related resources */
3740 athost_wl_status_info_t
* wlfc
;
3742 uint32 hostreorder
= 0;
3745 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__
, __LINE__
));
3749 dhd_os_wlfc_block(dhd
);
3750 if (!dhd
->wlfc_enabled
) {
3751 DHD_ERROR(("%s():%d, Already disabled!\n", __FUNCTION__
, __LINE__
));
3752 dhd_os_wlfc_unblock(dhd
);
3756 dhd
->wlfc_enabled
= FALSE
;
3757 dhd_os_wlfc_unblock(dhd
);
3759 /* query ampdu hostreorder */
3760 (void) dhd_wl_ioctl_get_intiovar(dhd
, "ampdu_hostreorder",
3761 &hostreorder
, WLC_GET_VAR
, FALSE
, 0);
3764 tlv
= WLFC_FLAGS_HOST_RXRERODER_ACTIVE
;
3765 DHD_ERROR(("%s():%d, maintain HOST RXRERODER flag in tvl\n",
3766 __FUNCTION__
, __LINE__
));
3769 /* Disable proptxtstatus signaling for deinit */
3770 (void) dhd_wl_ioctl_set_intiovar(dhd
, "tlv", tlv
, WLC_SET_VAR
, TRUE
, 0);
3772 dhd_os_wlfc_block(dhd
);
3774 if (!dhd
->wlfc_state
|| (dhd
->proptxstatus_mode
== WLFC_FCMODE_NONE
)) {
3775 dhd_os_wlfc_unblock(dhd
);
3776 return WLFC_UNSUPPORTED
;
3779 wlfc
= (athost_wl_status_info_t
*)dhd
->wlfc_state
;
3781 _dhd_wlfc_cleanup(dhd
, NULL
, NULL
);
3783 if (!WLFC_GET_AFQ(dhd
->wlfc_mode
)) {
3785 wlfc_hanger_t
* h
= (wlfc_hanger_t
*)wlfc
->hanger
;
3786 for (i
= 0; i
< h
->max_items
; i
++) {
3787 if (h
->items
[i
].state
!= WLFC_HANGER_ITEM_STATE_FREE
) {
3788 _dhd_wlfc_hanger_free_pkt(wlfc
, i
,
3789 WLFC_HANGER_PKT_STATE_COMPLETE
, TRUE
);
3794 _dhd_wlfc_hanger_delete(dhd
, h
);
3798 /* free top structure */
3799 DHD_OS_PREFREE(dhd
, dhd
->wlfc_state
,
3800 sizeof(athost_wl_status_info_t
));
3801 dhd
->wlfc_state
= NULL
;
3802 dhd
->proptxstatus_mode
= hostreorder
?
3803 WLFC_ONLY_AMPDU_HOSTREORDER
: WLFC_FCMODE_NONE
;
3805 DHD_ERROR(("%s: wlfc_mode=0x%x, tlv=%d\n", __FUNCTION__
, dhd
->wlfc_mode
, tlv
));
3807 dhd_os_wlfc_unblock(dhd
);
3809 if (dhd
->plat_deinit
)
3810 dhd
->plat_deinit((void *)dhd
);
3812 } /* dhd_wlfc_init */
3815 * Called on an interface event (WLC_E_IF) indicated by firmware
3816 * @param[in] dhdp Pointer to public DHD structure
3817 * @param[in] action eg eWLFC_MAC_ENTRY_ACTION_UPDATE or eWLFC_MAC_ENTRY_ACTION_ADD
3819 int dhd_wlfc_interface_event(dhd_pub_t
*dhdp
, uint8 action
, uint8 ifid
, uint8 iftype
, uint8
* ea
)
3824 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__
, __LINE__
));
3828 dhd_os_wlfc_block(dhdp
);
3830 if (!dhdp
->wlfc_state
|| (dhdp
->proptxstatus_mode
== WLFC_FCMODE_NONE
)) {
3831 dhd_os_wlfc_unblock(dhdp
);
3832 return WLFC_UNSUPPORTED
;
3835 rc
= _dhd_wlfc_interface_entry_update(dhdp
->wlfc_state
, action
, ifid
, iftype
, ea
);
3837 dhd_os_wlfc_unblock(dhdp
);
3841 /** Called eg on receiving a WLC_E_FIFO_CREDIT_MAP event from the dongle */
3842 int dhd_wlfc_FIFOcreditmap_event(dhd_pub_t
*dhdp
, uint8
* event_data
)
3847 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__
, __LINE__
));
3851 dhd_os_wlfc_block(dhdp
);
3853 if (!dhdp
->wlfc_state
|| (dhdp
->proptxstatus_mode
== WLFC_FCMODE_NONE
)) {
3854 dhd_os_wlfc_unblock(dhdp
);
3855 return WLFC_UNSUPPORTED
;
3858 rc
= _dhd_wlfc_FIFOcreditmap_update(dhdp
->wlfc_state
, event_data
);
3860 dhd_os_wlfc_unblock(dhdp
);
3865 int dhd_wlfc_disable_credit_borrow_event(dhd_pub_t
*dhdp
, uint8
* event_data
)
3867 if (dhdp
== NULL
|| event_data
== NULL
) {
3868 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__
, __LINE__
));
3871 dhd_os_wlfc_block(dhdp
);
3872 dhdp
->wlfc_borrow_allowed
= (bool)(*(uint32
*)event_data
);
3873 dhd_os_wlfc_unblock(dhdp
);
3877 #endif /* LIMIT_BORROW */
3880 * Called eg on receiving a WLC_E_BCMC_CREDIT_SUPPORT event from the dongle (broadcast/multicast
3883 int dhd_wlfc_BCMCCredit_support_event(dhd_pub_t
*dhdp
)
3888 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__
, __LINE__
));
3892 dhd_os_wlfc_block(dhdp
);
3894 if (!dhdp
->wlfc_state
|| (dhdp
->proptxstatus_mode
== WLFC_FCMODE_NONE
)) {
3895 dhd_os_wlfc_unblock(dhdp
);
3896 return WLFC_UNSUPPORTED
;
3899 rc
= _dhd_wlfc_BCMCCredit_support_update(dhdp
->wlfc_state
);
3901 dhd_os_wlfc_unblock(dhdp
);
3905 /** debug specific function */
3907 dhd_wlfc_dump(dhd_pub_t
*dhdp
, struct bcmstrbuf
*strbuf
)
3911 athost_wl_status_info_t
* wlfc
;
3913 wlfc_mac_descriptor_t
* mac_table
;
3914 wlfc_mac_descriptor_t
* interfaces
;
3915 char* iftypes
[] = {"STA", "AP", "WDS", "p2pGO", "p2pCL"};
3917 if (!dhdp
|| !strbuf
) {
3918 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__
, __LINE__
));
3922 dhd_os_wlfc_block(dhdp
);
3924 if (!dhdp
->wlfc_state
|| (dhdp
->proptxstatus_mode
== WLFC_FCMODE_NONE
)) {
3925 dhd_os_wlfc_unblock(dhdp
);
3926 return WLFC_UNSUPPORTED
;
3929 wlfc
= (athost_wl_status_info_t
*)dhdp
->wlfc_state
;
3931 h
= (wlfc_hanger_t
*)wlfc
->hanger
;
3933 bcm_bprintf(strbuf
, "wlfc-hanger not initialized yet\n");
3936 mac_table
= wlfc
->destination_entries
.nodes
;
3937 interfaces
= wlfc
->destination_entries
.interfaces
;
3938 bcm_bprintf(strbuf
, "---- wlfc stats ----\n");
3940 if (!WLFC_GET_AFQ(dhdp
->wlfc_mode
)) {
3941 h
= (wlfc_hanger_t
*)wlfc
->hanger
;
3943 bcm_bprintf(strbuf
, "wlfc-hanger not initialized yet\n");
3945 bcm_bprintf(strbuf
, "wlfc hanger (pushed,popped,f_push,"
3946 "f_pop,f_slot, pending) = (%d,%d,%d,%d,%d,%d)\n",
3952 (h
->pushed
- h
->popped
));
3956 bcm_bprintf(strbuf
, "wlfc fail(tlv,credit_rqst,mac_update,psmode_update), "
3957 "(dq_full,rollback_fail) = (%d,%d,%d,%d), (%d,%d)\n",
3958 wlfc
->stats
.tlv_parse_failed
,
3959 wlfc
->stats
.credit_request_failed
,
3960 wlfc
->stats
.mac_update_failed
,
3961 wlfc
->stats
.psmode_update_failed
,
3962 wlfc
->stats
.delayq_full_error
,
3963 wlfc
->stats
.rollback_failed
);
3965 bcm_bprintf(strbuf
, "PKTS (init_credit,credit,sent,drop_d,drop_s,outoforder) "
3966 "(AC0[%d,%d,%d,%d,%d,%d],AC1[%d,%d,%d,%d,%d,%d],AC2[%d,%d,%d,%d,%d,%d],"
3967 "AC3[%d,%d,%d,%d,%d,%d],BC_MC[%d,%d,%d,%d,%d,%d])\n",
3968 wlfc
->Init_FIFO_credit
[0], wlfc
->FIFO_credit
[0], wlfc
->stats
.send_pkts
[0],
3969 wlfc
->stats
.drop_pkts
[0], wlfc
->stats
.drop_pkts
[1], wlfc
->stats
.ooo_pkts
[0],
3970 wlfc
->Init_FIFO_credit
[1], wlfc
->FIFO_credit
[1], wlfc
->stats
.send_pkts
[1],
3971 wlfc
->stats
.drop_pkts
[2], wlfc
->stats
.drop_pkts
[3], wlfc
->stats
.ooo_pkts
[1],
3972 wlfc
->Init_FIFO_credit
[2], wlfc
->FIFO_credit
[2], wlfc
->stats
.send_pkts
[2],
3973 wlfc
->stats
.drop_pkts
[4], wlfc
->stats
.drop_pkts
[5], wlfc
->stats
.ooo_pkts
[2],
3974 wlfc
->Init_FIFO_credit
[3], wlfc
->FIFO_credit
[3], wlfc
->stats
.send_pkts
[3],
3975 wlfc
->stats
.drop_pkts
[6], wlfc
->stats
.drop_pkts
[7], wlfc
->stats
.ooo_pkts
[3],
3976 wlfc
->Init_FIFO_credit
[4], wlfc
->FIFO_credit
[4], wlfc
->stats
.send_pkts
[4],
3977 wlfc
->stats
.drop_pkts
[8], wlfc
->stats
.drop_pkts
[9], wlfc
->stats
.ooo_pkts
[4]);
3979 bcm_bprintf(strbuf
, "\n");
3980 for (i
= 0; i
< WLFC_MAX_IFNUM
; i
++) {
3981 if (interfaces
[i
].occupied
) {
3984 if (interfaces
[i
].iftype
> WLC_E_IF_ROLE_P2P_CLIENT
)
3985 iftype_desc
= "<Unknown";
3987 iftype_desc
= iftypes
[interfaces
[i
].iftype
];
3989 ea
= interfaces
[i
].ea
;
3990 bcm_bprintf(strbuf
, "INTERFACE[%d].ea = "
3991 "[%02x:%02x:%02x:%02x:%02x:%02x], if:%d, type: %s "
3992 "netif_flow_control:%s\n", i
,
3993 ea
[0], ea
[1], ea
[2], ea
[3], ea
[4], ea
[5],
3994 interfaces
[i
].interface_id
,
3995 iftype_desc
, ((wlfc
->hostif_flow_state
[i
] == OFF
)
3998 bcm_bprintf(strbuf
, "INTERFACE[%d].PSQ(len,state,credit),"
3999 "(trans,supp_trans,onbus)"
4000 "= (%d,%s,%d),(%d,%d,%d)\n",
4002 interfaces
[i
].psq
.len
,
4003 ((interfaces
[i
].state
==
4004 WLFC_STATE_OPEN
) ? "OPEN":"CLOSE"),
4005 interfaces
[i
].requested_credit
,
4006 interfaces
[i
].transit_count
,
4007 interfaces
[i
].suppr_transit_count
,
4008 interfaces
[i
].onbus_pkts_count
);
4010 bcm_bprintf(strbuf
, "INTERFACE[%d].PSQ"
4011 "(delay0,sup0,afq0),(delay1,sup1,afq1),(delay2,sup2,afq2),"
4012 "(delay3,sup3,afq3),(delay4,sup4,afq4) = (%d,%d,%d),"
4013 "(%d,%d,%d),(%d,%d,%d),(%d,%d,%d),(%d,%d,%d)\n",
4015 interfaces
[i
].psq
.q
[0].len
,
4016 interfaces
[i
].psq
.q
[1].len
,
4017 interfaces
[i
].afq
.q
[0].len
,
4018 interfaces
[i
].psq
.q
[2].len
,
4019 interfaces
[i
].psq
.q
[3].len
,
4020 interfaces
[i
].afq
.q
[1].len
,
4021 interfaces
[i
].psq
.q
[4].len
,
4022 interfaces
[i
].psq
.q
[5].len
,
4023 interfaces
[i
].afq
.q
[2].len
,
4024 interfaces
[i
].psq
.q
[6].len
,
4025 interfaces
[i
].psq
.q
[7].len
,
4026 interfaces
[i
].afq
.q
[3].len
,
4027 interfaces
[i
].psq
.q
[8].len
,
4028 interfaces
[i
].psq
.q
[9].len
,
4029 interfaces
[i
].afq
.q
[4].len
);
4033 bcm_bprintf(strbuf
, "\n");
4034 for (i
= 0; i
< WLFC_MAC_DESC_TABLE_SIZE
; i
++) {
4035 if (mac_table
[i
].occupied
) {
4036 ea
= mac_table
[i
].ea
;
4037 bcm_bprintf(strbuf
, "MAC_table[%d].ea = "
4038 "[%02x:%02x:%02x:%02x:%02x:%02x], if:%d \n", i
,
4039 ea
[0], ea
[1], ea
[2], ea
[3], ea
[4], ea
[5],
4040 mac_table
[i
].interface_id
);
4042 bcm_bprintf(strbuf
, "MAC_table[%d].PSQ(len,state,credit),"
4043 "(trans,supp_trans,onbus)"
4044 "= (%d,%s,%d),(%d,%d,%d)\n",
4046 mac_table
[i
].psq
.len
,
4047 ((mac_table
[i
].state
==
4048 WLFC_STATE_OPEN
) ? " OPEN":"CLOSE"),
4049 mac_table
[i
].requested_credit
,
4050 mac_table
[i
].transit_count
,
4051 mac_table
[i
].suppr_transit_count
,
4052 mac_table
[i
].onbus_pkts_count
);
4053 #ifdef PROP_TXSTATUS_DEBUG
4054 bcm_bprintf(strbuf
, "MAC_table[%d]: (opened, closed) = (%d, %d)\n",
4055 i
, mac_table
[i
].opened_ct
, mac_table
[i
].closed_ct
);
4057 bcm_bprintf(strbuf
, "MAC_table[%d].PSQ"
4058 "(delay0,sup0,afq0),(delay1,sup1,afq1),(delay2,sup2,afq2),"
4059 "(delay3,sup3,afq3),(delay4,sup4,afq4) =(%d,%d,%d),"
4060 "(%d,%d,%d),(%d,%d,%d),(%d,%d,%d),(%d,%d,%d)\n",
4062 mac_table
[i
].psq
.q
[0].len
,
4063 mac_table
[i
].psq
.q
[1].len
,
4064 mac_table
[i
].afq
.q
[0].len
,
4065 mac_table
[i
].psq
.q
[2].len
,
4066 mac_table
[i
].psq
.q
[3].len
,
4067 mac_table
[i
].afq
.q
[1].len
,
4068 mac_table
[i
].psq
.q
[4].len
,
4069 mac_table
[i
].psq
.q
[5].len
,
4070 mac_table
[i
].afq
.q
[2].len
,
4071 mac_table
[i
].psq
.q
[6].len
,
4072 mac_table
[i
].psq
.q
[7].len
,
4073 mac_table
[i
].afq
.q
[3].len
,
4074 mac_table
[i
].psq
.q
[8].len
,
4075 mac_table
[i
].psq
.q
[9].len
,
4076 mac_table
[i
].afq
.q
[4].len
);
4081 #ifdef PROP_TXSTATUS_DEBUG
4087 if (wlfc
->stats
.latency_sample_count
) {
4088 moving_samples
= sizeof(wlfc
->stats
.deltas
)/sizeof(uint32
);
4090 for (i
= 0; i
< moving_samples
; i
++)
4091 moving_avg
+= wlfc
->stats
.deltas
[i
];
4092 moving_avg
/= moving_samples
;
4094 avg
= (100 * wlfc
->stats
.total_status_latency
) /
4095 wlfc
->stats
.latency_sample_count
;
4096 bcm_bprintf(strbuf
, "txstatus latency (average, last, moving[%d]) = "
4097 "(%d.%d, %03d, %03d)\n",
4098 moving_samples
, avg
/100, (avg
- (avg
/100)*100),
4099 wlfc
->stats
.latency_most_recent
,
4104 bcm_bprintf(strbuf
, "wlfc- fifo[0-5] credit stats: sent = (%d,%d,%d,%d,%d,%d), "
4105 "back = (%d,%d,%d,%d,%d,%d)\n",
4106 wlfc
->stats
.fifo_credits_sent
[0],
4107 wlfc
->stats
.fifo_credits_sent
[1],
4108 wlfc
->stats
.fifo_credits_sent
[2],
4109 wlfc
->stats
.fifo_credits_sent
[3],
4110 wlfc
->stats
.fifo_credits_sent
[4],
4111 wlfc
->stats
.fifo_credits_sent
[5],
4113 wlfc
->stats
.fifo_credits_back
[0],
4114 wlfc
->stats
.fifo_credits_back
[1],
4115 wlfc
->stats
.fifo_credits_back
[2],
4116 wlfc
->stats
.fifo_credits_back
[3],
4117 wlfc
->stats
.fifo_credits_back
[4],
4118 wlfc
->stats
.fifo_credits_back
[5]);
4120 uint32 fifo_cr_sent
= 0;
4121 uint32 fifo_cr_acked
= 0;
4122 uint32 request_cr_sent
= 0;
4123 uint32 request_cr_ack
= 0;
4124 uint32 bc_mc_cr_ack
= 0;
4126 for (i
= 0; i
< sizeof(wlfc
->stats
.fifo_credits_sent
)/sizeof(uint32
); i
++) {
4127 fifo_cr_sent
+= wlfc
->stats
.fifo_credits_sent
[i
];
4130 for (i
= 0; i
< sizeof(wlfc
->stats
.fifo_credits_back
)/sizeof(uint32
); i
++) {
4131 fifo_cr_acked
+= wlfc
->stats
.fifo_credits_back
[i
];
4134 for (i
= 0; i
< WLFC_MAC_DESC_TABLE_SIZE
; i
++) {
4135 if (wlfc
->destination_entries
.nodes
[i
].occupied
) {
4137 wlfc
->destination_entries
.nodes
[i
].dstncredit_sent_packets
;
4140 for (i
= 0; i
< WLFC_MAX_IFNUM
; i
++) {
4141 if (wlfc
->destination_entries
.interfaces
[i
].occupied
) {
4143 wlfc
->destination_entries
.interfaces
[i
].dstncredit_sent_packets
;
4146 for (i
= 0; i
< WLFC_MAC_DESC_TABLE_SIZE
; i
++) {
4147 if (wlfc
->destination_entries
.nodes
[i
].occupied
) {
4149 wlfc
->destination_entries
.nodes
[i
].dstncredit_acks
;
4152 for (i
= 0; i
< WLFC_MAX_IFNUM
; i
++) {
4153 if (wlfc
->destination_entries
.interfaces
[i
].occupied
) {
4155 wlfc
->destination_entries
.interfaces
[i
].dstncredit_acks
;
4158 bcm_bprintf(strbuf
, "wlfc- (sent, status) => pq(%d,%d), vq(%d,%d),"
4159 "other:%d, bc_mc:%d, signal-only, (sent,freed): (%d,%d)",
4160 fifo_cr_sent
, fifo_cr_acked
,
4161 request_cr_sent
, request_cr_ack
,
4162 wlfc
->destination_entries
.other
.dstncredit_acks
,
4164 wlfc
->stats
.signal_only_pkts_sent
, wlfc
->stats
.signal_only_pkts_freed
);
4166 #endif /* PROP_TXSTATUS_DEBUG */
4167 bcm_bprintf(strbuf
, "\n");
4168 bcm_bprintf(strbuf
, "wlfc- pkt((in,2bus,txstats,hdrpull,out),(dropped,hdr_only,wlc_tossed)"
4169 "(freed,free_err,rollback)) = "
4170 "((%d,%d,%d,%d,%d),(%d,%d,%d),(%d,%d,%d))\n",
4172 wlfc
->stats
.pkt2bus
,
4173 wlfc
->stats
.txstatus_in
,
4174 wlfc
->stats
.dhd_hdrpulls
,
4177 wlfc
->stats
.pktdropped
,
4178 wlfc
->stats
.wlfc_header_only_pkt
,
4179 wlfc
->stats
.wlc_tossed_pkts
,
4181 wlfc
->stats
.pkt_freed
,
4182 wlfc
->stats
.pkt_free_err
, wlfc
->stats
.rollback
);
4184 bcm_bprintf(strbuf
, "wlfc- suppress((d11,wlc,err),enq(d11,wl,hq,mac?),retx(d11,wlc,hq)) = "
4185 "((%d,%d,%d),(%d,%d,%d,%d),(%d,%d,%d))\n",
4186 wlfc
->stats
.d11_suppress
,
4187 wlfc
->stats
.wl_suppress
,
4188 wlfc
->stats
.bad_suppress
,
4190 wlfc
->stats
.psq_d11sup_enq
,
4191 wlfc
->stats
.psq_wlsup_enq
,
4192 wlfc
->stats
.psq_hostq_enq
,
4193 wlfc
->stats
.mac_handle_notfound
,
4195 wlfc
->stats
.psq_d11sup_retx
,
4196 wlfc
->stats
.psq_wlsup_retx
,
4197 wlfc
->stats
.psq_hostq_retx
);
4199 bcm_bprintf(strbuf
, "wlfc- cleanup(txq,psq,fw) = (%d,%d,%d)\n",
4200 wlfc
->stats
.cleanup_txq_cnt
,
4201 wlfc
->stats
.cleanup_psq_cnt
,
4202 wlfc
->stats
.cleanup_fw_cnt
);
4204 bcm_bprintf(strbuf
, "wlfc- generic error: %d\n", wlfc
->stats
.generic_error
);
4206 for (i
= 0; i
< WLFC_MAX_IFNUM
; i
++) {
4207 bcm_bprintf(strbuf
, "wlfc- if[%d], pkt_cnt_in_q/AC[0-4] = (%d,%d,%d,%d,%d)\n", i
,
4208 wlfc
->pkt_cnt_in_q
[i
][0],
4209 wlfc
->pkt_cnt_in_q
[i
][1],
4210 wlfc
->pkt_cnt_in_q
[i
][2],
4211 wlfc
->pkt_cnt_in_q
[i
][3],
4212 wlfc
->pkt_cnt_in_q
[i
][4]);
4214 bcm_bprintf(strbuf
, "\n");
4216 dhd_os_wlfc_unblock(dhdp
);
4218 } /* dhd_wlfc_dump */
4220 int dhd_wlfc_clear_counts(dhd_pub_t
*dhd
)
4222 athost_wl_status_info_t
* wlfc
;
4223 wlfc_hanger_t
* hanger
;
4226 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__
, __LINE__
));
4230 dhd_os_wlfc_block(dhd
);
4232 if (!dhd
->wlfc_state
|| (dhd
->proptxstatus_mode
== WLFC_FCMODE_NONE
)) {
4233 dhd_os_wlfc_unblock(dhd
);
4234 return WLFC_UNSUPPORTED
;
4237 wlfc
= (athost_wl_status_info_t
*)dhd
->wlfc_state
;
4239 memset(&wlfc
->stats
, 0, sizeof(athost_wl_stat_counters_t
));
4241 if (!WLFC_GET_AFQ(dhd
->wlfc_mode
)) {
4242 hanger
= (wlfc_hanger_t
*)wlfc
->hanger
;
4246 hanger
->failed_slotfind
= 0;
4247 hanger
->failed_to_pop
= 0;
4248 hanger
->failed_to_push
= 0;
4251 dhd_os_wlfc_unblock(dhd
);
4256 /** returns TRUE if flow control is enabled */
4257 int dhd_wlfc_get_enable(dhd_pub_t
*dhd
, bool *val
)
4260 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__
, __LINE__
));
4264 dhd_os_wlfc_block(dhd
);
4266 *val
= dhd
->wlfc_enabled
;
4268 dhd_os_wlfc_unblock(dhd
);
4273 /** Called via an IOVAR */
4274 int dhd_wlfc_get_mode(dhd_pub_t
*dhd
, int *val
)
4277 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__
, __LINE__
));
4281 dhd_os_wlfc_block(dhd
);
4283 *val
= dhd
->wlfc_state
? dhd
->proptxstatus_mode
: 0;
4285 dhd_os_wlfc_unblock(dhd
);
4290 /** Called via an IOVAR */
4291 int dhd_wlfc_set_mode(dhd_pub_t
*dhd
, int val
)
4294 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__
, __LINE__
));
4298 dhd_os_wlfc_block(dhd
);
4300 if (dhd
->wlfc_state
) {
4301 dhd
->proptxstatus_mode
= val
& 0xff;
4304 dhd_os_wlfc_unblock(dhd
);
4309 /** Called when rx frame is received from the dongle */
4310 bool dhd_wlfc_is_header_only_pkt(dhd_pub_t
* dhd
, void *pktbuf
)
4312 athost_wl_status_info_t
* wlfc
;
4316 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__
, __LINE__
));
4320 dhd_os_wlfc_block(dhd
);
4322 if (!dhd
->wlfc_state
|| (dhd
->proptxstatus_mode
== WLFC_FCMODE_NONE
)) {
4323 dhd_os_wlfc_unblock(dhd
);
4327 wlfc
= (athost_wl_status_info_t
*)dhd
->wlfc_state
;
4329 if (PKTLEN(wlfc
->osh
, pktbuf
) == 0) {
4330 wlfc
->stats
.wlfc_header_only_pkt
++;
4334 dhd_os_wlfc_unblock(dhd
);
4339 int dhd_wlfc_flowcontrol(dhd_pub_t
*dhdp
, bool state
, bool bAcquireLock
)
4342 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__
, __LINE__
));
4347 dhd_os_wlfc_block(dhdp
);
4350 if (!dhdp
->wlfc_state
|| (dhdp
->proptxstatus_mode
== WLFC_FCMODE_NONE
) ||
4351 dhdp
->proptxstatus_module_ignore
) {
4353 dhd_os_wlfc_unblock(dhdp
);
4355 return WLFC_UNSUPPORTED
;
4358 if (state
!= dhdp
->proptxstatus_txoff
) {
4359 dhdp
->proptxstatus_txoff
= state
;
4363 dhd_os_wlfc_unblock(dhdp
);
4369 /** Called when eg an rx frame is received from the dongle */
4370 int dhd_wlfc_save_rxpath_ac_time(dhd_pub_t
* dhd
, uint8 prio
)
4372 athost_wl_status_info_t
* wlfc
;
4373 int rx_path_ac
= -1;
4375 if ((dhd
== NULL
) || (prio
>= NUMPRIO
)) {
4376 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__
, __LINE__
));
4380 dhd_os_wlfc_block(dhd
);
4382 if (!dhd
->wlfc_rxpkt_chk
) {
4383 dhd_os_wlfc_unblock(dhd
);
4387 if (!dhd
->wlfc_state
|| (dhd
->proptxstatus_mode
== WLFC_FCMODE_NONE
)) {
4388 dhd_os_wlfc_unblock(dhd
);
4389 return WLFC_UNSUPPORTED
;
4392 wlfc
= (athost_wl_status_info_t
*)dhd
->wlfc_state
;
4394 rx_path_ac
= prio2fifo
[prio
];
4395 wlfc
->rx_timestamp
[rx_path_ac
] = OSL_SYSUPTIME();
4397 dhd_os_wlfc_unblock(dhd
);
4402 /** called via an IOVAR */
4403 int dhd_wlfc_get_module_ignore(dhd_pub_t
*dhd
, int *val
)
4406 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__
, __LINE__
));
4410 dhd_os_wlfc_block(dhd
);
4412 *val
= dhd
->proptxstatus_module_ignore
;
4414 dhd_os_wlfc_unblock(dhd
);
4419 /** called via an IOVAR */
4420 int dhd_wlfc_set_module_ignore(dhd_pub_t
*dhd
, int val
)
4423 bool bChanged
= FALSE
;
4426 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__
, __LINE__
));
4430 dhd_os_wlfc_block(dhd
);
4432 if ((bool)val
!= dhd
->proptxstatus_module_ignore
) {
4433 dhd
->proptxstatus_module_ignore
= (val
!= 0);
4434 /* force txstatus_ignore sync with proptxstatus_module_ignore */
4435 dhd
->proptxstatus_txstatus_ignore
= dhd
->proptxstatus_module_ignore
;
4436 if (FALSE
== dhd
->proptxstatus_module_ignore
) {
4437 tlv
= WLFC_FLAGS_RSSI_SIGNALS
|
4438 WLFC_FLAGS_XONXOFF_SIGNALS
|
4439 WLFC_FLAGS_CREDIT_STATUS_SIGNALS
|
4440 WLFC_FLAGS_HOST_PROPTXSTATUS_ACTIVE
;
4442 /* always enable host reorder */
4443 tlv
|= WLFC_FLAGS_HOST_RXRERODER_ACTIVE
;
4447 dhd_os_wlfc_unblock(dhd
);
4450 /* select enable proptxtstatus signaling */
4451 if (dhd_wl_ioctl_set_intiovar(dhd
, "tlv", tlv
, WLC_SET_VAR
, TRUE
, 0)) {
4452 DHD_ERROR(("%s: failed to set bdcv2 tlv signaling to 0x%x\n",
4453 __FUNCTION__
, tlv
));
4455 DHD_ERROR(("%s: successfully set bdcv2 tlv signaling to 0x%x\n",
4456 __FUNCTION__
, tlv
));
4460 #if defined(DHD_WLFC_THREAD)
4461 _dhd_wlfc_thread_wakeup(dhd
);
4462 #endif /* defined(DHD_WLFC_THREAD) */
4467 /** called via an IOVAR */
4468 int dhd_wlfc_get_credit_ignore(dhd_pub_t
*dhd
, int *val
)
4471 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__
, __LINE__
));
4475 dhd_os_wlfc_block(dhd
);
4477 *val
= dhd
->proptxstatus_credit_ignore
;
4479 dhd_os_wlfc_unblock(dhd
);
4484 /** called via an IOVAR */
4485 int dhd_wlfc_set_credit_ignore(dhd_pub_t
*dhd
, int val
)
4488 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__
, __LINE__
));
4492 dhd_os_wlfc_block(dhd
);
4494 dhd
->proptxstatus_credit_ignore
= (val
!= 0);
4496 dhd_os_wlfc_unblock(dhd
);
4501 /** called via an IOVAR */
4502 int dhd_wlfc_get_txstatus_ignore(dhd_pub_t
*dhd
, int *val
)
4505 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__
, __LINE__
));
4509 dhd_os_wlfc_block(dhd
);
4511 *val
= dhd
->proptxstatus_txstatus_ignore
;
4513 dhd_os_wlfc_unblock(dhd
);
4518 /** called via an IOVAR */
4519 int dhd_wlfc_set_txstatus_ignore(dhd_pub_t
*dhd
, int val
)
4522 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__
, __LINE__
));
4526 dhd_os_wlfc_block(dhd
);
4528 dhd
->proptxstatus_txstatus_ignore
= (val
!= 0);
4530 dhd_os_wlfc_unblock(dhd
);
4535 /** called via an IOVAR */
4536 int dhd_wlfc_get_rxpkt_chk(dhd_pub_t
*dhd
, int *val
)
4539 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__
, __LINE__
));
4543 dhd_os_wlfc_block(dhd
);
4545 *val
= dhd
->wlfc_rxpkt_chk
;
4547 dhd_os_wlfc_unblock(dhd
);
4552 /** called via an IOVAR */
4553 int dhd_wlfc_set_rxpkt_chk(dhd_pub_t
*dhd
, int val
)
4556 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__
, __LINE__
));
4560 dhd_os_wlfc_block(dhd
);
4562 dhd
->wlfc_rxpkt_chk
= (val
!= 0);
4564 dhd_os_wlfc_unblock(dhd
);
4569 #endif /* PROP_TXSTATUS */