1 /*
2 * DHD PROP_TXSTATUS Module.
3 *
4 * Copyright (C) 1999-2013, Broadcom Corporation
5 *
6 * Unless you and Broadcom execute a separate written software license
7 * agreement governing use of this software, this software is licensed to you
8 * under the terms of the GNU General Public License version 2 (the "GPL"),
9 * available at http://www.broadcom.com/licenses/GPLv2.php, with the
10 * following added to such license:
11 *
12 * As a special exception, the copyright holders of this software give you
13 * permission to link this software with independent modules, and to copy and
14 * distribute the resulting executable under terms of your choice, provided that
15 * you also meet, for each linked independent module, the terms and conditions of
16 * the license of that module. An independent module is a module which is not
17 * derived from this software. The special exception does not apply to any
18 * modifications of the software.
19 *
20 * Notwithstanding the above, under no circumstances may you combine this
21 * software in any way with any other Broadcom software provided under a license
22 * other than the GPL, without Broadcom's express prior written consent.
23 *
24 * $Id: dhd_wlfc.c 412994 2013-07-17 12:38:03Z $
25 *
26 */
27
28 #include <typedefs.h>
29 #include <osl.h>
30
31 #include <bcmutils.h>
32 #include <bcmendian.h>
33
34 #include <dngl_stats.h>
35 #include <dhd.h>
36
37 #include <dhd_bus.h>
38 #include <dhd_dbg.h>
39
40 #ifdef PROP_TXSTATUS
41 #include <wlfc_proto.h>
42 #include <dhd_wlfc.h>
43 #endif
44
45
46
47
48 #define BUS_RETRIES 1 /* # of retries before aborting a bus tx operation */
49
50 #ifdef PROP_TXSTATUS
51 typedef struct dhd_wlfc_commit_info {
52 uint8 needs_hdr;
53 uint8 ac_fifo_credit_spent;
54 ewlfc_packet_state_t pkt_type;
55 wlfc_mac_descriptor_t* mac_entry;
56 void* p;
57 } dhd_wlfc_commit_info_t;
58 #endif /* PROP_TXSTATUS */
59
60
61 #ifdef PROP_TXSTATUS
62
63 #define DHD_WLFC_QMON_COMPLETE(entry)
64
65 void
dhd_wlfc_dump(dhd_pub_t * dhdp,struct bcmstrbuf * strbuf)66 dhd_wlfc_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf)
67 {
68 int i;
69 uint8* ea;
70 athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)
71 dhdp->wlfc_state;
72 wlfc_hanger_t* h;
73 wlfc_mac_descriptor_t* mac_table;
74 wlfc_mac_descriptor_t* interfaces;
75 char* iftypes[] = {"STA", "AP", "WDS", "p2pGO", "p2pCL"};
76
77 if (wlfc == NULL) {
78 bcm_bprintf(strbuf, "wlfc not initialized yet\n");
79 return;
80 }
81 h = (wlfc_hanger_t*)wlfc->hanger;
82 if (h == NULL) {
83 bcm_bprintf(strbuf, "wlfc-hanger not initialized yet\n");
84 }
85
86 mac_table = wlfc->destination_entries.nodes;
87 interfaces = wlfc->destination_entries.interfaces;
88 bcm_bprintf(strbuf, "---- wlfc stats ----\n");
89 if (h) {
90 bcm_bprintf(strbuf, "wlfc hanger (pushed,popped,f_push,"
91 "f_pop,f_slot, pending) = (%d,%d,%d,%d,%d,%d)\n",
92 h->pushed,
93 h->popped,
94 h->failed_to_push,
95 h->failed_to_pop,
96 h->failed_slotfind,
97 (h->pushed - h->popped));
98 }
99
100 bcm_bprintf(strbuf, "wlfc fail(tlv,credit_rqst,mac_update,psmode_update), "
101 "(dq_full,rollback_fail) = (%d,%d,%d,%d), (%d,%d)\n",
102 wlfc->stats.tlv_parse_failed,
103 wlfc->stats.credit_request_failed,
104 wlfc->stats.mac_update_failed,
105 wlfc->stats.psmode_update_failed,
106 wlfc->stats.delayq_full_error,
107 wlfc->stats.rollback_failed);
108
109 bcm_bprintf(strbuf, "PKTS (credit,sent) "
110 "(AC0[%d,%d],AC1[%d,%d],AC2[%d,%d],AC3[%d,%d],BC_MC[%d,%d])\n",
111 wlfc->FIFO_credit[0], wlfc->stats.send_pkts[0],
112 wlfc->FIFO_credit[1], wlfc->stats.send_pkts[1],
113 wlfc->FIFO_credit[2], wlfc->stats.send_pkts[2],
114 wlfc->FIFO_credit[3], wlfc->stats.send_pkts[3],
115 wlfc->FIFO_credit[4], wlfc->stats.send_pkts[4]);
116
117 bcm_bprintf(strbuf, "\n");
118 for (i = 0; i < WLFC_MAX_IFNUM; i++) {
119 if (interfaces[i].occupied) {
120 char* iftype_desc;
121
122 if (interfaces[i].iftype > WLC_E_IF_ROLE_P2P_CLIENT)
123 iftype_desc = "<Unknown";
124 else
125 iftype_desc = iftypes[interfaces[i].iftype];
126
127 ea = interfaces[i].ea;
128 bcm_bprintf(strbuf, "INTERFACE[%d].ea = "
129 "[%02x:%02x:%02x:%02x:%02x:%02x], if:%d, type: %s"
130 "netif_flow_control:%s\n", i,
131 ea[0], ea[1], ea[2], ea[3], ea[4], ea[5],
132 interfaces[i].interface_id,
133 iftype_desc, ((wlfc->hostif_flow_state[i] == OFF)
134 ? " OFF":" ON"));
135
136 bcm_bprintf(strbuf, "INTERFACE[%d].DELAYQ(len,state,credit)"
137 "= (%d,%s,%d)\n",
138 i,
139 interfaces[i].psq.len,
140 ((interfaces[i].state ==
141 WLFC_STATE_OPEN) ? " OPEN":"CLOSE"),
142 interfaces[i].requested_credit);
143
144 bcm_bprintf(strbuf, "INTERFACE[%d].DELAYQ"
145 "(sup,ac0),(sup,ac1),(sup,ac2),(sup,ac3) = "
146 "(%d,%d),(%d,%d),(%d,%d),(%d,%d)\n",
147 i,
148 interfaces[i].psq.q[0].len,
149 interfaces[i].psq.q[1].len,
150 interfaces[i].psq.q[2].len,
151 interfaces[i].psq.q[3].len,
152 interfaces[i].psq.q[4].len,
153 interfaces[i].psq.q[5].len,
154 interfaces[i].psq.q[6].len,
155 interfaces[i].psq.q[7].len);
156 }
157 }
158
159 bcm_bprintf(strbuf, "\n");
160 for (i = 0; i < WLFC_MAC_DESC_TABLE_SIZE; i++) {
161 if (mac_table[i].occupied) {
162 ea = mac_table[i].ea;
163 bcm_bprintf(strbuf, "MAC_table[%d].ea = "
164 "[%02x:%02x:%02x:%02x:%02x:%02x], if:%d \n", i,
165 ea[0], ea[1], ea[2], ea[3], ea[4], ea[5],
166 mac_table[i].interface_id);
167
168 bcm_bprintf(strbuf, "MAC_table[%d].DELAYQ(len,state,credit)"
169 "= (%d,%s,%d)\n",
170 i,
171 mac_table[i].psq.len,
172 ((mac_table[i].state ==
173 WLFC_STATE_OPEN) ? " OPEN":"CLOSE"),
174 mac_table[i].requested_credit);
175 #ifdef PROP_TXSTATUS_DEBUG
176 bcm_bprintf(strbuf, "MAC_table[%d]: (opened, closed) = (%d, %d)\n",
177 i, mac_table[i].opened_ct, mac_table[i].closed_ct);
178 #endif
179 bcm_bprintf(strbuf, "MAC_table[%d].DELAYQ"
180 "(sup,ac0),(sup,ac1),(sup,ac2),(sup,ac3) = "
181 "(%d,%d),(%d,%d),(%d,%d),(%d,%d)\n",
182 i,
183 mac_table[i].psq.q[0].len,
184 mac_table[i].psq.q[1].len,
185 mac_table[i].psq.q[2].len,
186 mac_table[i].psq.q[3].len,
187 mac_table[i].psq.q[4].len,
188 mac_table[i].psq.q[5].len,
189 mac_table[i].psq.q[6].len,
190 mac_table[i].psq.q[7].len);
191 }
192 }
193
194 #ifdef PROP_TXSTATUS_DEBUG
195 {
196 int avg;
197 int moving_avg = 0;
198 int moving_samples;
199
200 if (wlfc->stats.latency_sample_count) {
201 moving_samples = sizeof(wlfc->stats.deltas)/sizeof(uint32);
202
203 for (i = 0; i < moving_samples; i++)
204 moving_avg += wlfc->stats.deltas[i];
205 moving_avg /= moving_samples;
206
207 avg = (100 * wlfc->stats.total_status_latency) /
208 wlfc->stats.latency_sample_count;
209 bcm_bprintf(strbuf, "txstatus latency (average, last, moving[%d]) = "
210 "(%d.%d, %03d, %03d)\n",
211 moving_samples, avg/100, (avg - (avg/100)*100),
212 wlfc->stats.latency_most_recent,
213 moving_avg);
214 }
215 }
216
217 bcm_bprintf(strbuf, "wlfc- fifo[0-5] credit stats: sent = (%d,%d,%d,%d,%d,%d), "
218 "back = (%d,%d,%d,%d,%d,%d)\n",
219 wlfc->stats.fifo_credits_sent[0],
220 wlfc->stats.fifo_credits_sent[1],
221 wlfc->stats.fifo_credits_sent[2],
222 wlfc->stats.fifo_credits_sent[3],
223 wlfc->stats.fifo_credits_sent[4],
224 wlfc->stats.fifo_credits_sent[5],
225
226 wlfc->stats.fifo_credits_back[0],
227 wlfc->stats.fifo_credits_back[1],
228 wlfc->stats.fifo_credits_back[2],
229 wlfc->stats.fifo_credits_back[3],
230 wlfc->stats.fifo_credits_back[4],
231 wlfc->stats.fifo_credits_back[5]);
232 {
233 uint32 fifo_cr_sent = 0;
234 uint32 fifo_cr_acked = 0;
235 uint32 request_cr_sent = 0;
236 uint32 request_cr_ack = 0;
237 uint32 bc_mc_cr_ack = 0;
238
239 for (i = 0; i < sizeof(wlfc->stats.fifo_credits_sent)/sizeof(uint32); i++) {
240 fifo_cr_sent += wlfc->stats.fifo_credits_sent[i];
241 }
242
243 for (i = 0; i < sizeof(wlfc->stats.fifo_credits_back)/sizeof(uint32); i++) {
244 fifo_cr_acked += wlfc->stats.fifo_credits_back[i];
245 }
246
247 for (i = 0; i < WLFC_MAC_DESC_TABLE_SIZE; i++) {
248 if (wlfc->destination_entries.nodes[i].occupied) {
249 request_cr_sent +=
250 wlfc->destination_entries.nodes[i].dstncredit_sent_packets;
251 }
252 }
253 for (i = 0; i < WLFC_MAX_IFNUM; i++) {
254 if (wlfc->destination_entries.interfaces[i].occupied) {
255 request_cr_sent +=
256 wlfc->destination_entries.interfaces[i].dstncredit_sent_packets;
257 }
258 }
259 for (i = 0; i < WLFC_MAC_DESC_TABLE_SIZE; i++) {
260 if (wlfc->destination_entries.nodes[i].occupied) {
261 request_cr_ack +=
262 wlfc->destination_entries.nodes[i].dstncredit_acks;
263 }
264 }
265 for (i = 0; i < WLFC_MAX_IFNUM; i++) {
266 if (wlfc->destination_entries.interfaces[i].occupied) {
267 request_cr_ack +=
268 wlfc->destination_entries.interfaces[i].dstncredit_acks;
269 }
270 }
271 bcm_bprintf(strbuf, "wlfc- (sent, status) => pq(%d,%d), vq(%d,%d),"
272 "other:%d, bc_mc:%d, signal-only, (sent,freed): (%d,%d)",
273 fifo_cr_sent, fifo_cr_acked,
274 request_cr_sent, request_cr_ack,
275 wlfc->destination_entries.other.dstncredit_acks,
276 bc_mc_cr_ack,
277 wlfc->stats.signal_only_pkts_sent, wlfc->stats.signal_only_pkts_freed);
278 }
279 #endif /* PROP_TXSTATUS_DEBUG */
280 bcm_bprintf(strbuf, "\n");
281 bcm_bprintf(strbuf, "wlfc- pkt((in,2bus,txstats,hdrpull),(dropped,hdr_only,wlc_tossed)"
282 "(freed,free_err,rollback)) = "
283 "((%d,%d,%d,%d),(%d,%d,%d),(%d,%d,%d))\n",
284 wlfc->stats.pktin,
285 wlfc->stats.pkt2bus,
286 wlfc->stats.txstatus_in,
287 wlfc->stats.dhd_hdrpulls,
288
289 wlfc->stats.pktdropped,
290 wlfc->stats.wlfc_header_only_pkt,
291 wlfc->stats.wlc_tossed_pkts,
292
293 wlfc->stats.pkt_freed,
294 wlfc->stats.pkt_free_err, wlfc->stats.rollback);
295
296 bcm_bprintf(strbuf, "wlfc- suppress((d11,wlc,err),enq(d11,wl,hq,mac?),retx(d11,wlc,hq)) = "
297 "((%d,%d,%d),(%d,%d,%d,%d),(%d,%d,%d))\n",
298
299 wlfc->stats.d11_suppress,
300 wlfc->stats.wl_suppress,
301 wlfc->stats.bad_suppress,
302
303 wlfc->stats.psq_d11sup_enq,
304 wlfc->stats.psq_wlsup_enq,
305 wlfc->stats.psq_hostq_enq,
306 wlfc->stats.mac_handle_notfound,
307
308 wlfc->stats.psq_d11sup_retx,
309 wlfc->stats.psq_wlsup_retx,
310 wlfc->stats.psq_hostq_retx);
311 bcm_bprintf(strbuf, "wlfc- generic error: %d", wlfc->stats.generic_error);
312
313 return;
314 }
315
316 /* Create a place to store all packet pointers submitted to the firmware until
317 a status comes back, suppress or otherwise.
318
319 hang-er: noun, a contrivance on which things are hung, as a hook.
320 */
321 static void*
dhd_wlfc_hanger_create(osl_t * osh,int max_items)322 dhd_wlfc_hanger_create(osl_t *osh, int max_items)
323 {
324 int i;
325 wlfc_hanger_t* hanger;
326
327 /* allow only up to a specific size for now */
328 ASSERT(max_items == WLFC_HANGER_MAXITEMS);
329
330 if ((hanger = (wlfc_hanger_t*)MALLOC(osh, WLFC_HANGER_SIZE(max_items))) == NULL)
331 return NULL;
332
333 memset(hanger, 0, WLFC_HANGER_SIZE(max_items));
334 hanger->max_items = max_items;
335
336 for (i = 0; i < hanger->max_items; i++) {
337 hanger->items[i].state = WLFC_HANGER_ITEM_STATE_FREE;
338 }
339 return hanger;
340 }
341
342 static int
dhd_wlfc_hanger_delete(osl_t * osh,void * hanger)343 dhd_wlfc_hanger_delete(osl_t *osh, void* hanger)
344 {
345 wlfc_hanger_t* h = (wlfc_hanger_t*)hanger;
346
347 if (h) {
348 MFREE(osh, h, WLFC_HANGER_SIZE(h->max_items));
349 return BCME_OK;
350 }
351 return BCME_BADARG;
352 }
353
354 static uint16
dhd_wlfc_hanger_get_free_slot(void * hanger)355 dhd_wlfc_hanger_get_free_slot(void* hanger)
356 {
357 uint32 i;
358 wlfc_hanger_t* h = (wlfc_hanger_t*)hanger;
359
360 if (h) {
361 i = h->slot_pos + 1;
362 if (i == h->max_items) {
363 i = 0;
364 }
365 while (i != h->slot_pos) {
366 if (h->items[i].state == WLFC_HANGER_ITEM_STATE_FREE) {
367 h->slot_pos = i;
368 return (uint16)i;
369 }
370 i++;
371 if (i == h->max_items)
372 i = 0;
373 }
374 h->failed_slotfind++;
375 }
376 return WLFC_HANGER_MAXITEMS;
377 }
378
379 static int
dhd_wlfc_hanger_get_genbit(void * hanger,void * pkt,uint32 slot_id,int * gen)380 dhd_wlfc_hanger_get_genbit(void* hanger, void* pkt, uint32 slot_id, int* gen)
381 {
382 int rc = BCME_OK;
383 wlfc_hanger_t* h = (wlfc_hanger_t*)hanger;
384
385 *gen = 0xff;
386
387 /* this packet was not pushed at the time it went to the firmware */
388 if (slot_id == WLFC_HANGER_MAXITEMS)
389 return BCME_NOTFOUND;
390
391 if (h) {
392 if ((h->items[slot_id].state == WLFC_HANGER_ITEM_STATE_INUSE) ||
393 (h->items[slot_id].state == WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED)) {
394 *gen = h->items[slot_id].gen;
395 }
396 else {
397 rc = BCME_NOTFOUND;
398 }
399 }
400 else
401 rc = BCME_BADARG;
402 return rc;
403 }
404
405 static int
dhd_wlfc_hanger_pushpkt(void * hanger,void * pkt,uint32 slot_id)406 dhd_wlfc_hanger_pushpkt(void* hanger, void* pkt, uint32 slot_id)
407 {
408 int rc = BCME_OK;
409 wlfc_hanger_t* h = (wlfc_hanger_t*)hanger;
410
411 if (h && (slot_id < WLFC_HANGER_MAXITEMS)) {
412 if (h->items[slot_id].state == WLFC_HANGER_ITEM_STATE_FREE) {
413 h->items[slot_id].state = WLFC_HANGER_ITEM_STATE_INUSE;
414 h->items[slot_id].pkt = pkt;
415 h->items[slot_id].identifier = slot_id;
416 h->pushed++;
417 }
418 else {
419 h->failed_to_push++;
420 rc = BCME_NOTFOUND;
421 }
422 }
423 else
424 rc = BCME_BADARG;
425 return rc;
426 }
427
428 static int
dhd_wlfc_hanger_poppkt(void * hanger,uint32 slot_id,void ** pktout,int remove_from_hanger)429 dhd_wlfc_hanger_poppkt(void* hanger, uint32 slot_id, void** pktout, int remove_from_hanger)
430 {
431 int rc = BCME_OK;
432 wlfc_hanger_t* h = (wlfc_hanger_t*)hanger;
433
434 /* this packet was not pushed at the time it went to the firmware */
435 if (slot_id == WLFC_HANGER_MAXITEMS)
436 return BCME_NOTFOUND;
437
438 if (h) {
439 if (h->items[slot_id].state != WLFC_HANGER_ITEM_STATE_FREE) {
440 *pktout = h->items[slot_id].pkt;
441 if (remove_from_hanger) {
442 h->items[slot_id].state =
443 WLFC_HANGER_ITEM_STATE_FREE;
444 h->items[slot_id].pkt = NULL;
445 h->items[slot_id].identifier = 0;
446 h->items[slot_id].gen = 0xff;
447 h->popped++;
448 }
449 }
450 else {
451 h->failed_to_pop++;
452 rc = BCME_NOTFOUND;
453 }
454 }
455 else
456 rc = BCME_BADARG;
457 return rc;
458 }
459
460 static int
dhd_wlfc_hanger_mark_suppressed(void * hanger,uint32 slot_id,uint8 gen)461 dhd_wlfc_hanger_mark_suppressed(void* hanger, uint32 slot_id, uint8 gen)
462 {
463 int rc = BCME_OK;
464 wlfc_hanger_t* h = (wlfc_hanger_t*)hanger;
465
466 /* this packet was not pushed at the time it went to the firmware */
467 if (slot_id == WLFC_HANGER_MAXITEMS)
468 return BCME_NOTFOUND;
469 if (h) {
470 h->items[slot_id].gen = gen;
471 if (h->items[slot_id].state == WLFC_HANGER_ITEM_STATE_INUSE) {
472 h->items[slot_id].state = WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED;
473 }
474 else
475 rc = BCME_BADARG;
476 }
477 else
478 rc = BCME_BADARG;
479
480 return rc;
481 }
482
483 static int
_dhd_wlfc_pushheader(athost_wl_status_info_t * ctx,void * p,bool tim_signal,uint8 tim_bmp,uint8 mac_handle,uint32 htodtag)484 _dhd_wlfc_pushheader(athost_wl_status_info_t* ctx, void* p, bool tim_signal,
485 uint8 tim_bmp, uint8 mac_handle, uint32 htodtag)
486 {
487 uint32 wl_pktinfo = 0;
488 uint8* wlh;
489 uint8 dataOffset;
490 uint8 fillers;
491 uint8 tim_signal_len = 0;
492
493 struct bdc_header *h;
494
495 if (tim_signal) {
496 tim_signal_len = 1 + 1 + WLFC_CTL_VALUE_LEN_PENDING_TRAFFIC_BMP;
497 }
498
499 /* +2 is for Type[1] and Len[1] in TLV, plus TIM signal */
500 dataOffset = WLFC_CTL_VALUE_LEN_PKTTAG + 2 + tim_signal_len;
501 fillers = ROUNDUP(dataOffset, 4) - dataOffset;
502 dataOffset += fillers;
503
504 PKTPUSH(ctx->osh, p, dataOffset);
505 wlh = (uint8*) PKTDATA(ctx->osh, p);
506
507 wl_pktinfo = htol32(htodtag);
508
509 wlh[0] = WLFC_CTL_TYPE_PKTTAG;
510 wlh[1] = WLFC_CTL_VALUE_LEN_PKTTAG;
511 memcpy(&wlh[2], &wl_pktinfo, sizeof(uint32));
512
513 if (tim_signal_len) {
514 wlh[dataOffset - fillers - tim_signal_len ] =
515 WLFC_CTL_TYPE_PENDING_TRAFFIC_BMP;
516 wlh[dataOffset - fillers - tim_signal_len + 1] =
517 WLFC_CTL_VALUE_LEN_PENDING_TRAFFIC_BMP;
518 wlh[dataOffset - fillers - tim_signal_len + 2] = mac_handle;
519 wlh[dataOffset - fillers - tim_signal_len + 3] = tim_bmp;
520 }
521 if (fillers)
522 memset(&wlh[dataOffset - fillers], WLFC_CTL_TYPE_FILLER, fillers);
523
524 PKTPUSH(ctx->osh, p, BDC_HEADER_LEN);
525 h = (struct bdc_header *)PKTDATA(ctx->osh, p);
526 h->flags = (BDC_PROTO_VER << BDC_FLAG_VER_SHIFT);
527 if (PKTSUMNEEDED(p))
528 h->flags |= BDC_FLAG_SUM_NEEDED;
529
530
531 h->priority = (PKTPRIO(p) & BDC_PRIORITY_MASK);
532 h->flags2 = 0;
533 h->dataOffset = dataOffset >> 2;
534 BDC_SET_IF_IDX(h, DHD_PKTTAG_IF(PKTTAG(p)));
535 return BCME_OK;
536 }
537
538 static int
_dhd_wlfc_pullheader(athost_wl_status_info_t * ctx,void * pktbuf)539 _dhd_wlfc_pullheader(athost_wl_status_info_t* ctx, void* pktbuf)
540 {
541 struct bdc_header *h;
542
543 if (PKTLEN(ctx->osh, pktbuf) < BDC_HEADER_LEN) {
544 WLFC_DBGMESG(("%s: rx data too short (%d < %d)\n", __FUNCTION__,
545 PKTLEN(ctx->osh, pktbuf), BDC_HEADER_LEN));
546 return BCME_ERROR;
547 }
548 h = (struct bdc_header *)PKTDATA(ctx->osh, pktbuf);
549
550 /* pull BDC header */
551 PKTPULL(ctx->osh, pktbuf, BDC_HEADER_LEN);
552
553 if (PKTLEN(ctx->osh, pktbuf) < (h->dataOffset << 2)) {
554 WLFC_DBGMESG(("%s: rx data too short (%d < %d)\n", __FUNCTION__,
555 PKTLEN(ctx->osh, pktbuf), (h->dataOffset << 2)));
556 return BCME_ERROR;
557 }
558
559 /* pull wl-header */
560 PKTPULL(ctx->osh, pktbuf, (h->dataOffset << 2));
561 return BCME_OK;
562 }
563
564 static wlfc_mac_descriptor_t*
_dhd_wlfc_find_table_entry(athost_wl_status_info_t * ctx,void * p)565 _dhd_wlfc_find_table_entry(athost_wl_status_info_t* ctx, void* p)
566 {
567 int i;
568 wlfc_mac_descriptor_t* table = ctx->destination_entries.nodes;
569 uint8 ifid = DHD_PKTTAG_IF(PKTTAG(p));
570 uint8* dstn = DHD_PKTTAG_DSTN(PKTTAG(p));
571 wlfc_mac_descriptor_t* entry = NULL;
572 int iftype = ctx->destination_entries.interfaces[ifid].iftype;
573
574 /* Multicast destination and P2P clients get the interface entry.
575 * STA gets the interface entry if there is no exact match. For
576 * example, TDLS destinations have their own entry.
577 */
578 if ((iftype == WLC_E_IF_ROLE_STA || ETHER_ISMULTI(dstn) ||
579 iftype == WLC_E_IF_ROLE_P2P_CLIENT) &&
580 (ctx->destination_entries.interfaces[ifid].occupied)) {
581 entry = &ctx->destination_entries.interfaces[ifid];
582 }
583
584 if (entry != NULL && ETHER_ISMULTI(dstn))
585 return entry;
586
587 for (i = 0; i < WLFC_MAC_DESC_TABLE_SIZE; i++) {
588 if (table[i].occupied) {
589 if (table[i].interface_id == ifid) {
590 if (!memcmp(table[i].ea, dstn, ETHER_ADDR_LEN)) {
591 entry = &table[i];
592 break;
593 }
594 }
595 }
596 }
597
598 return entry != NULL ? entry : &ctx->destination_entries.other;
599 }
600
601 static int
_dhd_wlfc_rollback_packet_toq(athost_wl_status_info_t * ctx,void * p,ewlfc_packet_state_t pkt_type,uint32 hslot)602 _dhd_wlfc_rollback_packet_toq(athost_wl_status_info_t* ctx,
603 void* p, ewlfc_packet_state_t pkt_type, uint32 hslot)
604 {
605 /*
606 put the packet back to the head of queue
607
608 - suppressed packet goes back to suppress sub-queue
609 - pull out the header, if new or delayed packet
610
611 Note: hslot is used only when header removal is done.
612 */
613 wlfc_mac_descriptor_t* entry;
614 void* pktout;
615 int rc = BCME_OK;
616 int prec;
617
618 entry = _dhd_wlfc_find_table_entry(ctx, p);
619 prec = DHD_PKTTAG_FIFO(PKTTAG(p));
620 if (entry != NULL) {
621 if (pkt_type == eWLFC_PKTTYPE_SUPPRESSED) {
622 /* wl-header is saved for suppressed packets */
623 if (WLFC_PKTQ_PENQ_HEAD(&entry->psq, ((prec << 1) + 1), p) == NULL) {
624 WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__));
625 rc = BCME_ERROR;
626 }
627 }
628 else {
629 /* remove header first */
630 rc = _dhd_wlfc_pullheader(ctx, p);
631 if (rc != BCME_OK) {
632 WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__));
633 /* free the hanger slot */
634 dhd_wlfc_hanger_poppkt(ctx->hanger, hslot, &pktout, 1);
635 PKTFREE(ctx->osh, p, TRUE);
636 ctx->stats.rollback_failed++;
637 return BCME_ERROR;
638 }
639
640 if (pkt_type == eWLFC_PKTTYPE_DELAYED) {
641 /* delay-q packets are going to delay-q */
642 if (WLFC_PKTQ_PENQ_HEAD(&entry->psq, (prec << 1), p) == NULL) {
643 WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__));
644 rc = BCME_ERROR;
645 }
646 }
647
648 /* free the hanger slot */
649 dhd_wlfc_hanger_poppkt(ctx->hanger, hslot, &pktout, 1);
650
651 /* decrement sequence count */
652 WLFC_DECR_SEQCOUNT(entry, prec);
653 }
654 /*
655 if this packet did not count against FIFO credit, it must have
656 taken a requested_credit from the firmware (for pspoll etc.)
657 */
658 if (!DHD_PKTTAG_CREDITCHECK(PKTTAG(p))) {
659 entry->requested_credit++;
660 }
661 }
662 else {
663 WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__));
664 rc = BCME_ERROR;
665 }
666 if (rc != BCME_OK)
667 ctx->stats.rollback_failed++;
668 else
669 ctx->stats.rollback++;
670
671 return rc;
672 }
673
674 static void
_dhd_wlfc_flow_control_check(athost_wl_status_info_t * ctx,struct pktq * pq,uint8 if_id)675 _dhd_wlfc_flow_control_check(athost_wl_status_info_t* ctx, struct pktq* pq, uint8 if_id)
676 {
677 dhd_pub_t *dhdp;
678
679 ASSERT(ctx);
680
681 dhdp = (dhd_pub_t *)ctx->dhdp;
682
683 if (dhdp && dhdp->skip_fc && dhdp->skip_fc())
684 return;
685
686 if ((pq->len <= WLFC_FLOWCONTROL_LOWATER) && (ctx->hostif_flow_state[if_id] == ON)) {
687 /* start traffic */
688 ctx->hostif_flow_state[if_id] = OFF;
689 /*
690 WLFC_DBGMESG(("qlen:%02d, if:%02d, ->OFF, start traffic %s()\n",
691 pq->len, if_id, __FUNCTION__));
692 */
693 WLFC_DBGMESG(("F"));
694
695 dhd_txflowcontrol(ctx->dhdp, if_id, OFF);
696
697 ctx->toggle_host_if = 0;
698 }
699 if ((pq->len >= WLFC_FLOWCONTROL_HIWATER) && (ctx->hostif_flow_state[if_id] == OFF)) {
700 /* stop traffic */
701 ctx->hostif_flow_state[if_id] = ON;
702 /*
703 WLFC_DBGMESG(("qlen:%02d, if:%02d, ->ON, stop traffic %s()\n",
704 pq->len, if_id, __FUNCTION__));
705 */
706 WLFC_DBGMESG(("N"));
707
708 dhd_txflowcontrol(ctx->dhdp, if_id, ON);
709
710 ctx->host_ifidx = if_id;
711 ctx->toggle_host_if = 1;
712 }
713
714 return;
715 }
716
717 static int
_dhd_wlfc_send_signalonly_packet(athost_wl_status_info_t * ctx,wlfc_mac_descriptor_t * entry,uint8 ta_bmp)718 _dhd_wlfc_send_signalonly_packet(athost_wl_status_info_t* ctx, wlfc_mac_descriptor_t* entry,
719 uint8 ta_bmp)
720 {
721 int rc = BCME_OK;
722 void* p = NULL;
723 int dummylen = ((dhd_pub_t *)ctx->dhdp)->hdrlen+ 12;
724
725 /* allocate a dummy packet */
726 p = PKTGET(ctx->osh, dummylen, TRUE);
727 if (p) {
728 PKTPULL(ctx->osh, p, dummylen);
729 DHD_PKTTAG_SET_H2DTAG(PKTTAG(p), 0);
730 _dhd_wlfc_pushheader(ctx, p, TRUE, ta_bmp, entry->mac_handle, 0);
731 DHD_PKTTAG_SETSIGNALONLY(PKTTAG(p), 1);
732 #ifdef PROP_TXSTATUS_DEBUG
733 ctx->stats.signal_only_pkts_sent++;
734 #endif
735 rc = dhd_bus_txdata(((dhd_pub_t *)ctx->dhdp)->bus, p);
736 if (rc != BCME_OK) {
737 PKTFREE(ctx->osh, p, TRUE);
738 }
739 }
740 else {
741 DHD_ERROR(("%s: couldn't allocate new %d-byte packet\n",
742 __FUNCTION__, dummylen));
743 rc = BCME_NOMEM;
744 }
745 return rc;
746 }
747
748 /* Return TRUE if traffic availability changed */
749 static bool
_dhd_wlfc_traffic_pending_check(athost_wl_status_info_t * ctx,wlfc_mac_descriptor_t * entry,int prec)750 _dhd_wlfc_traffic_pending_check(athost_wl_status_info_t* ctx, wlfc_mac_descriptor_t* entry,
751 int prec)
752 {
753 bool rc = FALSE;
754
755 if (entry->state == WLFC_STATE_CLOSE) {
756 if ((pktq_plen(&entry->psq, (prec << 1)) == 0) &&
757 (pktq_plen(&entry->psq, ((prec << 1) + 1)) == 0)) {
758
759 if (entry->traffic_pending_bmp & NBITVAL(prec)) {
760 rc = TRUE;
761 entry->traffic_pending_bmp =
762 entry->traffic_pending_bmp & ~ NBITVAL(prec);
763 }
764 }
765 else {
766 if (!(entry->traffic_pending_bmp & NBITVAL(prec))) {
767 rc = TRUE;
768 entry->traffic_pending_bmp =
769 entry->traffic_pending_bmp | NBITVAL(prec);
770 }
771 }
772 }
773 if (rc) {
774 /* request a TIM update to firmware at the next piggyback opportunity */
775 if (entry->traffic_lastreported_bmp != entry->traffic_pending_bmp) {
776 entry->send_tim_signal = 1;
777 _dhd_wlfc_send_signalonly_packet(ctx, entry, entry->traffic_pending_bmp);
778 entry->traffic_lastreported_bmp = entry->traffic_pending_bmp;
779 entry->send_tim_signal = 0;
780 }
781 else {
782 rc = FALSE;
783 }
784 }
785 return rc;
786 }
787
788 static int
_dhd_wlfc_enque_suppressed(athost_wl_status_info_t * ctx,int prec,void * p)789 _dhd_wlfc_enque_suppressed(athost_wl_status_info_t* ctx, int prec, void* p)
790 {
791 wlfc_mac_descriptor_t* entry;
792
793 entry = _dhd_wlfc_find_table_entry(ctx, p);
794 if (entry == NULL) {
795 WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__));
796 return BCME_NOTFOUND;
797 }
798 /*
799 - suppressed packets go to sub_queue[2*prec + 1] AND
800 - delayed packets go to sub_queue[2*prec + 0] to ensure
801 order of delivery.
802 */
803 if (WLFC_PKTQ_PENQ(&entry->psq, ((prec << 1) + 1), p) == NULL) {
804 ctx->stats.delayq_full_error++;
805 /* WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); */
806 WLFC_DBGMESG(("s"));
807 return BCME_ERROR;
808 }
809 /* A packet has been pushed, update traffic availability bitmap, if applicable */
810 _dhd_wlfc_traffic_pending_check(ctx, entry, prec);
811 _dhd_wlfc_flow_control_check(ctx, &entry->psq, DHD_PKTTAG_IF(PKTTAG(p)));
812 return BCME_OK;
813 }
814
815 static int
_dhd_wlfc_pretx_pktprocess(athost_wl_status_info_t * ctx,wlfc_mac_descriptor_t * entry,void * p,int header_needed,uint32 * slot)816 _dhd_wlfc_pretx_pktprocess(athost_wl_status_info_t* ctx,
817 wlfc_mac_descriptor_t* entry, void* p, int header_needed, uint32* slot)
818 {
819 int rc = BCME_OK;
820 int hslot = WLFC_HANGER_MAXITEMS;
821 bool send_tim_update = FALSE;
822 uint32 htod = 0;
823 uint8 free_ctr;
824
825 *slot = hslot;
826
827 if (entry == NULL) {
828 entry = _dhd_wlfc_find_table_entry(ctx, p);
829 }
830
831 if (entry == NULL) {
832 WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__));
833 return BCME_ERROR;
834 }
835 if (entry->send_tim_signal) {
836 send_tim_update = TRUE;
837 entry->send_tim_signal = 0;
838 entry->traffic_lastreported_bmp = entry->traffic_pending_bmp;
839 }
840 if (header_needed) {
841 hslot = dhd_wlfc_hanger_get_free_slot(ctx->hanger);
842 free_ctr = WLFC_SEQCOUNT(entry, DHD_PKTTAG_FIFO(PKTTAG(p)));
843 DHD_PKTTAG_SET_H2DTAG(PKTTAG(p), htod);
844 WLFC_PKTFLAG_SET_GENERATION(htod, entry->generation);
845 entry->transit_count++;
846 }
847 else {
848 hslot = WLFC_PKTID_HSLOT_GET(DHD_PKTTAG_H2DTAG(PKTTAG(p)));
849 free_ctr = WLFC_PKTID_FREERUNCTR_GET(DHD_PKTTAG_H2DTAG(PKTTAG(p)));
850 }
851 WLFC_PKTID_HSLOT_SET(htod, hslot);
852 WLFC_PKTID_FREERUNCTR_SET(htod, free_ctr);
853 DHD_PKTTAG_SETPKTDIR(PKTTAG(p), 1);
854 WL_TXSTATUS_SET_FLAGS(htod, WLFC_PKTFLAG_PKTFROMHOST);
855 WL_TXSTATUS_SET_FIFO(htod, DHD_PKTTAG_FIFO(PKTTAG(p)));
856
857
858 if (!DHD_PKTTAG_CREDITCHECK(PKTTAG(p))) {
859 /*
860 Indicate that this packet is being sent in response to an
861 explicit request from the firmware side.
862 */
863 WLFC_PKTFLAG_SET_PKTREQUESTED(htod);
864 }
865 else {
866 WLFC_PKTFLAG_CLR_PKTREQUESTED(htod);
867 }
868 if (header_needed) {
869 rc = _dhd_wlfc_pushheader(ctx, p, send_tim_update,
870 entry->traffic_lastreported_bmp, entry->mac_handle, htod);
871 if (rc == BCME_OK) {
872 DHD_PKTTAG_SET_H2DTAG(PKTTAG(p), htod);
873 /*
874 a new header was created for this packet.
875 push to hanger slot and scrub q. Since bus
876 send succeeded, increment seq number as well.
877 */
878 rc = dhd_wlfc_hanger_pushpkt(ctx->hanger, p, hslot);
879 if (rc == BCME_OK) {
880 /* increment free running sequence count */
881 WLFC_INCR_SEQCOUNT(entry, DHD_PKTTAG_FIFO(PKTTAG(p)));
882 #ifdef PROP_TXSTATUS_DEBUG
883 ((wlfc_hanger_t*)(ctx->hanger))->items[hslot].push_time =
884 OSL_SYSUPTIME();
885 #endif
886 }
887 else {
888 WLFC_DBGMESG(("%s() hanger_pushpkt() failed, rc: %d\n",
889 __FUNCTION__, rc));
890 }
891 }
892 }
893 else {
894 int gen;
895
896 /* remove old header */
897 rc = _dhd_wlfc_pullheader(ctx, p);
898 if (rc == BCME_OK) {
899 hslot = WLFC_PKTID_HSLOT_GET(DHD_PKTTAG_H2DTAG(PKTTAG(p)));
900 dhd_wlfc_hanger_get_genbit(ctx->hanger, p, hslot, &gen);
901
902 WLFC_PKTFLAG_SET_GENERATION(htod, gen);
903 free_ctr = WLFC_PKTID_FREERUNCTR_GET(DHD_PKTTAG_H2DTAG(PKTTAG(p)));
904 /* push new header */
905 _dhd_wlfc_pushheader(ctx, p, send_tim_update,
906 entry->traffic_lastreported_bmp, entry->mac_handle, htod);
907 }
908 }
909 *slot = hslot;
910 return rc;
911 }
912
913 static int
_dhd_wlfc_is_destination_closed(athost_wl_status_info_t * ctx,wlfc_mac_descriptor_t * entry,int prec)914 _dhd_wlfc_is_destination_closed(athost_wl_status_info_t* ctx,
915 wlfc_mac_descriptor_t* entry, int prec)
916 {
917 if (ctx->destination_entries.interfaces[entry->interface_id].iftype ==
918 WLC_E_IF_ROLE_P2P_GO) {
919 /* - destination interface is of type p2p GO.
920 For a p2pGO interface, if the destination is OPEN but the interface is
921 CLOSEd, do not send traffic. But if the dstn is CLOSEd while there is
922 destination-specific-credit left send packets. This is because the
923 firmware storing the destination-specific-requested packet in queue.
924 */
925 if ((entry->state == WLFC_STATE_CLOSE) && (entry->requested_credit == 0) &&
926 (entry->requested_packet == 0))
927 return 1;
928 }
929 /* AP, p2p_go -> unicast desc entry, STA/p2p_cl -> interface desc. entry */
930 if (((entry->state == WLFC_STATE_CLOSE) && (entry->requested_credit == 0) &&
931 (entry->requested_packet == 0)) ||
932 (!(entry->ac_bitmap & (1 << prec))))
933 return 1;
934
935 return 0;
936 }
937
938 static void*
_dhd_wlfc_deque_delayedq(athost_wl_status_info_t * ctx,int prec,uint8 * ac_credit_spent,uint8 * needs_hdr,wlfc_mac_descriptor_t ** entry_out)939 _dhd_wlfc_deque_delayedq(athost_wl_status_info_t* ctx,
940 int prec, uint8* ac_credit_spent, uint8* needs_hdr, wlfc_mac_descriptor_t** entry_out)
941 {
942 wlfc_mac_descriptor_t* entry;
943 wlfc_mac_descriptor_t* table;
944 uint8 token_pos;
945 int total_entries;
946 void* p = NULL;
947 int pout;
948 int i;
949
950 *entry_out = NULL;
951 token_pos = ctx->token_pos[prec];
952 /* most cases a packet will count against FIFO credit */
953 *ac_credit_spent = 1;
954 *needs_hdr = 1;
955
956 /* search all entries, include nodes as well as interfaces */
957 table = (wlfc_mac_descriptor_t*)&ctx->destination_entries;
958 total_entries = sizeof(ctx->destination_entries)/sizeof(wlfc_mac_descriptor_t);
959
960 for (i = 0; i < total_entries; i++) {
961 entry = &table[(token_pos + i) % total_entries];
962 if (entry->occupied && !entry->deleting) {
963 if (!_dhd_wlfc_is_destination_closed(ctx, entry, prec)) {
964 p = pktq_mdeq(&entry->psq,
965 /* higher precedence will be picked up first,
966 * i.e. suppressed packets before delayed ones
967 */
968 NBITVAL((prec << 1) + 1), &pout);
969 *needs_hdr = 0;
970
971 if (p == NULL) {
972 if (entry->suppressed == TRUE) {
973 if ((entry->suppr_transit_count <=
974 entry->suppress_count)) {
975 entry->suppressed = FALSE;
976 } else {
977 return NULL;
978 }
979 }
980 /* De-Q from delay Q */
981 p = pktq_mdeq(&entry->psq,
982 NBITVAL((prec << 1)),
983 &pout);
984 *needs_hdr = 1;
985 }
986
987 if (p != NULL) {
988 /* did the packet come from suppress sub-queue? */
989 if (entry->requested_credit > 0) {
990 entry->requested_credit--;
991 #ifdef PROP_TXSTATUS_DEBUG
992 entry->dstncredit_sent_packets++;
993 #endif
994 /*
995 if the packet was pulled out while destination is in
996 closed state but had a non-zero packets requested,
997 then this should not count against the FIFO credit.
998 That is due to the fact that the firmware will
999 most likely hold onto this packet until a suitable
1000 time later to push it to the appropriate AC FIFO.
1001 */
1002 if (entry->state == WLFC_STATE_CLOSE)
1003 *ac_credit_spent = 0;
1004 }
1005 else if (entry->requested_packet > 0) {
1006 entry->requested_packet--;
1007 DHD_PKTTAG_SETONETIMEPKTRQST(PKTTAG(p));
1008 if (entry->state == WLFC_STATE_CLOSE)
1009 *ac_credit_spent = 0;
1010 }
1011 /* move token to ensure fair round-robin */
1012 ctx->token_pos[prec] =
1013 (token_pos + i + 1) % total_entries;
1014 *entry_out = entry;
1015 _dhd_wlfc_flow_control_check(ctx, &entry->psq,
1016 DHD_PKTTAG_IF(PKTTAG(p)));
1017 /*
1018 A packet has been picked up, update traffic
1019 availability bitmap, if applicable
1020 */
1021 _dhd_wlfc_traffic_pending_check(ctx, entry, prec);
1022 return p;
1023 }
1024 }
1025 }
1026 }
1027 return NULL;
1028 }
1029
1030 void *
_dhd_wlfc_pktq_peek_tail(struct pktq * pq,int * prec_out)1031 _dhd_wlfc_pktq_peek_tail(struct pktq *pq, int *prec_out)
1032 {
1033 int prec;
1034
1035 ASSERT(pq);
1036
1037 if (pq->len == 0)
1038 return NULL;
1039
1040 for (prec = 0; prec < pq->hi_prec; prec++)
1041 /* only pick packets from dealyed-q */
1042 if (((prec & 1) == 0) && pq->q[prec].head)
1043 break;
1044
1045 if (prec_out)
1046 *prec_out = prec;
1047
1048 return (pq->q[prec].tail);
1049 }
1050
1051 bool
_dhd_wlfc_prec_enq_with_drop(dhd_pub_t * dhdp,struct pktq * pq,void * pkt,int prec)1052 _dhd_wlfc_prec_enq_with_drop(dhd_pub_t *dhdp, struct pktq *pq, void *pkt, int prec)
1053 {
1054 void *p = NULL;
1055 int eprec = -1; /* precedence to evict from */
1056
1057 ASSERT(dhdp && pq && pkt);
1058 ASSERT(prec >= 0 && prec < pq->num_prec);
1059
1060 /* Fast case, precedence queue is not full and we are also not
1061 * exceeding total queue length
1062 */
1063 if (!pktq_pfull(pq, prec) && !pktq_full(pq)) {
1064 pktq_penq(pq, prec, pkt);
1065 return TRUE;
1066 }
1067
1068 /* Determine precedence from which to evict packet, if any */
1069 if (pktq_pfull(pq, prec))
1070 eprec = prec;
1071 else if (pktq_full(pq)) {
1072 p = _dhd_wlfc_pktq_peek_tail(pq, &eprec);
1073 if (!p) {
1074 WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__));
1075 return FALSE;
1076 }
1077 if ((eprec > prec) || (eprec < 0)) {
1078 if (!pktq_pempty(pq, prec)) {
1079 eprec = prec;
1080 } else {
1081 return FALSE;
1082 }
1083 }
1084 }
1085
1086 /* Evict if needed */
1087 if (eprec >= 0) {
1088 /* Detect queueing to unconfigured precedence */
1089 ASSERT(!pktq_pempty(pq, eprec));
1090 /* Evict all fragmented frames */
1091 dhd_prec_drop_pkts(dhdp->osh, pq, eprec);
1092 }
1093
1094 /* Enqueue */
1095 p = pktq_penq(pq, prec, pkt);
1096 if (!p) {
1097 WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__));
1098 return FALSE;
1099 }
1100
1101 return TRUE;
1102 }
1103
1104 static int
_dhd_wlfc_enque_delayq(athost_wl_status_info_t * ctx,void * pktbuf,int prec)1105 _dhd_wlfc_enque_delayq(athost_wl_status_info_t* ctx, void* pktbuf, int prec)
1106 {
1107 wlfc_mac_descriptor_t* entry;
1108
1109 if (pktbuf != NULL) {
1110 entry = _dhd_wlfc_find_table_entry(ctx, pktbuf);
1111
1112 if (entry == NULL) {
1113 WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__));
1114 return BCME_ERROR;
1115 }
1116
1117 /*
1118 - suppressed packets go to sub_queue[2*prec + 1] AND
1119 - delayed packets go to sub_queue[2*prec + 0] to ensure
1120 order of delivery.
1121 */
1122 if (_dhd_wlfc_prec_enq_with_drop(ctx->dhdp, &entry->psq, pktbuf, (prec << 1))
1123 == FALSE) {
1124 WLFC_DBGMESG(("D"));
1125 /* dhd_txcomplete(ctx->dhdp, pktbuf, FALSE); */
1126 PKTFREE(ctx->osh, pktbuf, TRUE);
1127 ctx->stats.delayq_full_error++;
1128 return BCME_ERROR;
1129 }
1130
1131 /*
1132 A packet has been pushed, update traffic availability bitmap,
1133 if applicable
1134 */
1135 _dhd_wlfc_traffic_pending_check(ctx, entry, prec);
1136
1137 }
1138 return BCME_OK;
1139 }
1140
ifpkt_fn(void * p,int ifid)1141 bool ifpkt_fn(void* p, int ifid)
1142 {
1143 return (ifid == DHD_PKTTAG_IF(PKTTAG(p)));
1144 }
1145
1146 static int
_dhd_wlfc_mac_entry_update(athost_wl_status_info_t * ctx,wlfc_mac_descriptor_t * entry,ewlfc_mac_entry_action_t action,uint8 ifid,uint8 iftype,uint8 * ea)1147 _dhd_wlfc_mac_entry_update(athost_wl_status_info_t* ctx, wlfc_mac_descriptor_t* entry,
1148 ewlfc_mac_entry_action_t action, uint8 ifid, uint8 iftype, uint8* ea)
1149 {
1150 int rc = BCME_OK;
1151
1152 if (action == eWLFC_MAC_ENTRY_ACTION_ADD) {
1153 entry->occupied = 1;
1154 entry->state = WLFC_STATE_OPEN;
1155 entry->requested_credit = 0;
1156 entry->interface_id = ifid;
1157 entry->iftype = iftype;
1158 entry->ac_bitmap = 0xff; /* update this when handling APSD */
1159 /* for an interface entry we may not care about the MAC address */
1160 if (ea != NULL)
1161 memcpy(&entry->ea[0], ea, ETHER_ADDR_LEN);
1162 pktq_init(&entry->psq, WLFC_PSQ_PREC_COUNT, WLFC_PSQ_LEN);
1163 }
1164 else if (action == eWLFC_MAC_ENTRY_ACTION_UPDATE) {
1165 entry->occupied = 1;
1166 entry->state = WLFC_STATE_OPEN;
1167 entry->requested_credit = 0;
1168 entry->interface_id = ifid;
1169 entry->iftype = iftype;
1170 entry->ac_bitmap = 0xff; /* update this when handling APSD */
1171 /* for an interface entry we may not care about the MAC address */
1172 if (ea != NULL)
1173 memcpy(&entry->ea[0], ea, ETHER_ADDR_LEN);
1174 }
1175 else if (action == eWLFC_MAC_ENTRY_ACTION_DEL) {
1176 /* When the entry is deleted, the packets that are queued in the entry must be
1177 cleanup. The cleanup action should be before the occupied is set as 0. The
1178 flag deleting is set to avoid de-queue action when these queues are being
1179 cleanup
1180 */
1181 entry->deleting = 1;
1182 dhd_wlfc_cleanup(ctx->dhdp, ifpkt_fn, ifid);
1183 _dhd_wlfc_flow_control_check(ctx, &entry->psq, ifid);
1184 entry->deleting = 0;
1185
1186 entry->occupied = 0;
1187 entry->suppressed = 0;
1188 entry->state = WLFC_STATE_CLOSE;
1189 entry->requested_credit = 0;
1190 entry->transit_count = 0;
1191 entry->suppr_transit_count = 0;
1192 entry->suppress_count = 0;
1193 memset(&entry->ea[0], 0, ETHER_ADDR_LEN);
1194
1195 /* enable after packets are queued-deqeued properly.
1196 pktq_flush(dhd->osh, &entry->psq, FALSE, NULL, 0);
1197 */
1198 }
1199 return rc;
1200 }
1201
1202 int
_dhd_wlfc_borrow_credit(athost_wl_status_info_t * ctx,uint8 available_credit_map,int borrower_ac)1203 _dhd_wlfc_borrow_credit(athost_wl_status_info_t* ctx, uint8 available_credit_map, int borrower_ac)
1204 {
1205 int lender_ac;
1206 int rc = BCME_ERROR;
1207
1208 if (ctx == NULL || available_credit_map == 0) {
1209 WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__));
1210 return BCME_BADARG;
1211 }
1212
1213 /* Borrow from lowest priority available AC (including BC/MC credits) */
1214 for (lender_ac = 0; lender_ac <= AC_COUNT; lender_ac++) {
1215 if ((available_credit_map && (1 << lender_ac)) &&
1216 (ctx->FIFO_credit[lender_ac] > 0)) {
1217 ctx->credits_borrowed[borrower_ac][lender_ac]++;
1218 ctx->FIFO_credit[lender_ac]--;
1219 rc = BCME_OK;
1220 break;
1221 }
1222 }
1223
1224 return rc;
1225 }
1226
1227 int
dhd_wlfc_interface_entry_update(void * state,ewlfc_mac_entry_action_t action,uint8 ifid,uint8 iftype,uint8 * ea)1228 dhd_wlfc_interface_entry_update(void* state,
1229 ewlfc_mac_entry_action_t action, uint8 ifid, uint8 iftype, uint8* ea)
1230 {
1231 athost_wl_status_info_t* ctx = (athost_wl_status_info_t*)state;
1232 wlfc_mac_descriptor_t* entry;
1233 int ret;
1234
1235 if (ifid >= WLFC_MAX_IFNUM)
1236 return BCME_BADARG;
1237
1238 entry = &ctx->destination_entries.interfaces[ifid];
1239 ret = _dhd_wlfc_mac_entry_update(ctx, entry, action, ifid, iftype, ea);
1240 return ret;
1241 }
1242
1243 int
dhd_wlfc_FIFOcreditmap_update(void * state,uint8 * credits)1244 dhd_wlfc_FIFOcreditmap_update(void* state, uint8* credits)
1245 {
1246 athost_wl_status_info_t* ctx = (athost_wl_status_info_t*)state;
1247
1248 /* update the AC FIFO credit map */
1249 ctx->FIFO_credit[0] = credits[0];
1250 ctx->FIFO_credit[1] = credits[1];
1251 ctx->FIFO_credit[2] = credits[2];
1252 ctx->FIFO_credit[3] = credits[3];
1253 /* credit for bc/mc packets */
1254 ctx->FIFO_credit[4] = credits[4];
1255 /* credit for ATIM FIFO is not used yet. */
1256 ctx->FIFO_credit[5] = 0;
1257 return BCME_OK;
1258 }
1259
1260 int
_dhd_wlfc_handle_packet_commit(athost_wl_status_info_t * ctx,int ac,dhd_wlfc_commit_info_t * commit_info,f_commitpkt_t fcommit,void * commit_ctx)1261 _dhd_wlfc_handle_packet_commit(athost_wl_status_info_t* ctx, int ac,
1262 dhd_wlfc_commit_info_t *commit_info, f_commitpkt_t fcommit, void* commit_ctx)
1263 {
1264 uint32 hslot;
1265 int rc;
1266
1267 /*
1268 if ac_fifo_credit_spent = 0
1269
1270 This packet will not count against the FIFO credit.
1271 To ensure the txstatus corresponding to this packet
1272 does not provide an implied credit (default behavior)
1273 mark the packet accordingly.
1274
1275 if ac_fifo_credit_spent = 1
1276
1277 This is a normal packet and it counts against the FIFO
1278 credit count.
1279 */
1280 DHD_PKTTAG_SETCREDITCHECK(PKTTAG(commit_info->p), commit_info->ac_fifo_credit_spent);
1281 rc = _dhd_wlfc_pretx_pktprocess(ctx, commit_info->mac_entry, commit_info->p,
1282 commit_info->needs_hdr, &hslot);
1283
1284 if (rc == BCME_OK)
1285 rc = fcommit(commit_ctx, commit_info->p);
1286 else
1287 ctx->stats.generic_error++;
1288
1289 if (rc == BCME_OK) {
1290 ctx->stats.pkt2bus++;
1291 if (commit_info->ac_fifo_credit_spent) {
1292 ctx->stats.send_pkts[ac]++;
1293 WLFC_HOST_FIFO_CREDIT_INC_SENTCTRS(ctx, ac);
1294 }
1295 } else if (rc == BCME_NORESOURCE)
1296 rc = BCME_ERROR;
1297 else {
1298 /*
1299 bus commit has failed, rollback.
1300 - remove wl-header for a delayed packet
1301 - save wl-header header for suppressed packets
1302 */
1303 rc = _dhd_wlfc_rollback_packet_toq(ctx, commit_info->p,
1304 (commit_info->pkt_type), hslot);
1305
1306 rc = BCME_ERROR;
1307 }
1308
1309 return rc;
1310 }
1311
1312 int
dhd_wlfc_commit_packets(void * state,f_commitpkt_t fcommit,void * commit_ctx,void * pktbuf)1313 dhd_wlfc_commit_packets(void* state, f_commitpkt_t fcommit, void* commit_ctx, void *pktbuf)
1314 {
1315 int ac;
1316 int credit;
1317 int rc;
1318 dhd_wlfc_commit_info_t commit_info;
1319 athost_wl_status_info_t* ctx = (athost_wl_status_info_t*)state;
1320 int credit_count = 0;
1321 int bus_retry_count = 0;
1322 uint8 ac_available = 0; /* Bitmask for 4 ACs + BC/MC */
1323
1324 if ((state == NULL) ||
1325 (fcommit == NULL)) {
1326 WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__));
1327 return BCME_BADARG;
1328 }
1329
1330 memset(&commit_info, 0, sizeof(commit_info));
1331
1332 /*
1333 Commit packets for regular AC traffic. Higher priority first.
1334 First, use up FIFO credits available to each AC. Based on distribution
1335 and credits left, borrow from other ACs as applicable
1336
1337 -NOTE:
1338 If the bus between the host and firmware is overwhelmed by the
1339 traffic from host, it is possible that higher priority traffic
1340 starves the lower priority queue. If that occurs often, we may
1341 have to employ weighted round-robin or ucode scheme to avoid
1342 low priority packet starvation.
1343 */
1344
1345 if (pktbuf) {
1346 ac = DHD_PKTTAG_FIFO(PKTTAG(pktbuf));
1347 if (ETHER_ISMULTI(DHD_PKTTAG_DSTN(PKTTAG(pktbuf)))) {
1348 ASSERT(ac == AC_COUNT);
1349 commit_info.needs_hdr = 1;
1350 commit_info.mac_entry = NULL;
1351 commit_info.pkt_type = eWLFC_PKTTYPE_NEW;
1352 commit_info.p = pktbuf;
1353 if (ctx->FIFO_credit[ac]) {
1354 rc = _dhd_wlfc_handle_packet_commit(ctx, ac, &commit_info,
1355 fcommit, commit_ctx);
1356
1357 /* Bus commits may fail (e.g. flow control); abort after retries */
1358 if (rc == BCME_OK) {
1359 if (commit_info.ac_fifo_credit_spent) {
1360 (void) _dhd_wlfc_borrow_credit(ctx,
1361 ac_available, ac);
1362 credit_count--;
1363 }
1364 } else {
1365 bus_retry_count++;
1366 if (bus_retry_count >= BUS_RETRIES) {
1367 DHD_ERROR((" %s: bus error %d\n",
1368 __FUNCTION__, rc));
1369 return rc;
1370 }
1371 }
1372 }
1373 }
1374 else {
1375 /* en-queue the packets to respective queue. */
1376 rc = _dhd_wlfc_enque_delayq(ctx, pktbuf, ac);
1377 }
1378 }
1379
1380 for (ac = AC_COUNT; ac >= 0; ac--) {
1381
1382 bool bQueueIdle = TRUE;
1383
1384 /* packets from delayQ with less priority are fresh and they'd need header and
1385 * have no MAC entry
1386 */
1387 commit_info.needs_hdr = 1;
1388 commit_info.mac_entry = NULL;
1389 commit_info.pkt_type = eWLFC_PKTTYPE_NEW;
1390
1391 for (credit = 0; credit < ctx->FIFO_credit[ac];) {
1392 commit_info.p = _dhd_wlfc_deque_delayedq(ctx, ac,
1393 &(commit_info.ac_fifo_credit_spent),
1394 &(commit_info.needs_hdr),
1395 &(commit_info.mac_entry));
1396
1397 if (commit_info.p == NULL)
1398 break;
1399
1400 bQueueIdle = FALSE;
1401
1402 commit_info.pkt_type = (commit_info.needs_hdr) ? eWLFC_PKTTYPE_DELAYED :
1403 eWLFC_PKTTYPE_SUPPRESSED;
1404
1405 rc = _dhd_wlfc_handle_packet_commit(ctx, ac, &commit_info,
1406 fcommit, commit_ctx);
1407
1408 /* Bus commits may fail (e.g. flow control); abort after retries */
1409 if (rc == BCME_OK) {
1410 if (commit_info.ac_fifo_credit_spent) {
1411 credit++;
1412 }
1413 }
1414 else {
1415 bus_retry_count++;
1416 if (bus_retry_count >= BUS_RETRIES) {
1417 DHD_ERROR(("%s: bus error %d\n", __FUNCTION__, rc));
1418 ctx->FIFO_credit[ac] -= credit;
1419 return rc;
1420 }
1421 }
1422 }
1423
1424 ctx->FIFO_credit[ac] -= credit;
1425
1426
1427 /* If no pkts can be dequed, the credit can be borrowed */
1428 if (bQueueIdle) {
1429 ac_available |= (1 << ac);
1430 credit_count += ctx->FIFO_credit[ac];
1431 }
1432 }
1433
1434 /* We borrow only for AC_BE and only if no other traffic seen for DEFER_PERIOD
1435
1436 Note that (ac_available & WLFC_AC_BE_TRAFFIC_ONLY) is done to:
1437 a) ignore BC/MC for deferring borrow
1438 b) ignore AC_BE being available along with other ACs
1439 (this should happen only for pure BC/MC traffic)
1440
1441 i.e. AC_VI, AC_VO, AC_BK all MUST be available (i.e. no traffic) and
1442 we do not care if AC_BE and BC/MC are available or not
1443 */
1444 if ((ac_available & WLFC_AC_BE_TRAFFIC_ONLY) == WLFC_AC_BE_TRAFFIC_ONLY) {
1445
1446 if (ctx->allow_credit_borrow) {
1447 ac = 1; /* Set ac to AC_BE and borrow credits */
1448 }
1449 else {
1450 int delta;
1451 int curr_t = OSL_SYSUPTIME();
1452
1453 if (curr_t > ctx->borrow_defer_timestamp)
1454 delta = curr_t - ctx->borrow_defer_timestamp;
1455 else
1456 delta = 0xffffffff + curr_t - ctx->borrow_defer_timestamp;
1457
1458 if (delta >= WLFC_BORROW_DEFER_PERIOD_MS) {
1459 /* Reset borrow but defer to next iteration (defensive borrowing) */
1460 ctx->allow_credit_borrow = TRUE;
1461 ctx->borrow_defer_timestamp = 0;
1462 }
1463 return BCME_OK;
1464 }
1465 }
1466 else {
1467 /* If we have multiple AC traffic, turn off borrowing, mark time and bail out */
1468 ctx->allow_credit_borrow = FALSE;
1469 ctx->borrow_defer_timestamp = OSL_SYSUPTIME();
1470 return BCME_OK;
1471 }
1472
1473 /* At this point, borrow all credits only for "ac" (which should be set above to AC_BE)
1474 Generically use "ac" only in case we extend to all ACs in future
1475 */
1476 for (; (credit_count > 0);) {
1477
1478 commit_info.p = _dhd_wlfc_deque_delayedq(ctx, ac,
1479 &(commit_info.ac_fifo_credit_spent),
1480 &(commit_info.needs_hdr),
1481 &(commit_info.mac_entry));
1482 if (commit_info.p == NULL)
1483 break;
1484
1485 commit_info.pkt_type = (commit_info.needs_hdr) ? eWLFC_PKTTYPE_DELAYED :
1486 eWLFC_PKTTYPE_SUPPRESSED;
1487
1488 rc = _dhd_wlfc_handle_packet_commit(ctx, ac, &commit_info,
1489 fcommit, commit_ctx);
1490
1491 /* Bus commits may fail (e.g. flow control); abort after retries */
1492 if (rc == BCME_OK) {
1493 if (commit_info.ac_fifo_credit_spent) {
1494 (void) _dhd_wlfc_borrow_credit(ctx, ac_available, ac);
1495 credit_count--;
1496 }
1497 }
1498 else {
1499 bus_retry_count++;
1500 if (bus_retry_count >= BUS_RETRIES) {
1501 DHD_ERROR(("%s: bus error %d\n", __FUNCTION__, rc));
1502 return rc;
1503 }
1504 }
1505 }
1506 return BCME_OK;
1507 }
1508
1509 static uint8
dhd_wlfc_find_mac_desc_id_from_mac(dhd_pub_t * dhdp,uint8 * ea)1510 dhd_wlfc_find_mac_desc_id_from_mac(dhd_pub_t *dhdp, uint8* ea)
1511 {
1512 wlfc_mac_descriptor_t* table =
1513 ((athost_wl_status_info_t*)dhdp->wlfc_state)->destination_entries.nodes;
1514 uint8 table_index;
1515
1516 if (ea != NULL) {
1517 for (table_index = 0; table_index < WLFC_MAC_DESC_TABLE_SIZE; table_index++) {
1518 if ((memcmp(ea, &table[table_index].ea[0], ETHER_ADDR_LEN) == 0) &&
1519 table[table_index].occupied)
1520 return table_index;
1521 }
1522 }
1523 return WLFC_MAC_DESC_ID_INVALID;
1524 }
1525
1526 void
dhd_wlfc_txcomplete(dhd_pub_t * dhd,void * txp,bool success)1527 dhd_wlfc_txcomplete(dhd_pub_t *dhd, void *txp, bool success)
1528 {
1529 athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)
1530 dhd->wlfc_state;
1531 void* p;
1532 int fifo_id;
1533
1534 if (DHD_PKTTAG_SIGNALONLY(PKTTAG(txp))) {
1535 #ifdef PROP_TXSTATUS_DEBUG
1536 wlfc->stats.signal_only_pkts_freed++;
1537 #endif
1538 /* is this a signal-only packet? */
1539 if (success)
1540 PKTFREE(wlfc->osh, txp, TRUE);
1541 return;
1542 }
1543 if (!success) {
1544 WLFC_DBGMESG(("At: %s():%d, bus_complete() failure for %p, htod_tag:0x%08x\n",
1545 __FUNCTION__, __LINE__, txp, DHD_PKTTAG_H2DTAG(PKTTAG(txp))));
1546 dhd_wlfc_hanger_poppkt(wlfc->hanger, WLFC_PKTID_HSLOT_GET(DHD_PKTTAG_H2DTAG
1547 (PKTTAG(txp))), &p, 1);
1548
1549 /* indicate failure and free the packet */
1550 dhd_txcomplete(dhd, txp, FALSE);
1551
1552 /* return the credit, if necessary */
1553 if (DHD_PKTTAG_CREDITCHECK(PKTTAG(txp))) {
1554 int lender, credit_returned = 0; /* Note that borrower is fifo_id */
1555
1556 fifo_id = DHD_PKTTAG_FIFO(PKTTAG(txp));
1557
1558 /* Return credits to highest priority lender first */
1559 for (lender = AC_COUNT; lender >= 0; lender--) {
1560 if (wlfc->credits_borrowed[fifo_id][lender] > 0) {
1561 wlfc->FIFO_credit[lender]++;
1562 wlfc->credits_borrowed[fifo_id][lender]--;
1563 credit_returned = 1;
1564 break;
1565 }
1566 }
1567
1568 if (!credit_returned) {
1569 wlfc->FIFO_credit[fifo_id]++;
1570 }
1571 }
1572
1573 PKTFREE(wlfc->osh, txp, TRUE);
1574 }
1575 return;
1576 }
1577
1578 static int
dhd_wlfc_compressed_txstatus_update(dhd_pub_t * dhd,uint8 * pkt_info,uint8 len)1579 dhd_wlfc_compressed_txstatus_update(dhd_pub_t *dhd, uint8* pkt_info, uint8 len)
1580 {
1581 uint8 status_flag;
1582 uint32 status;
1583 int ret;
1584 int remove_from_hanger = 1;
1585 void* pktbuf;
1586 uint8 fifo_id;
1587 uint8 count = 0;
1588 uint32 status_g;
1589 uint32 hslot, hcnt;
1590 wlfc_mac_descriptor_t* entry = NULL;
1591 athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)
1592 dhd->wlfc_state;
1593
1594 memcpy(&status, pkt_info, sizeof(uint32));
1595 status_flag = WL_TXSTATUS_GET_FLAGS(status);
1596 status_g = status & 0xff000000;
1597 hslot = (status & 0x00ffff00) >> 8;
1598 hcnt = status & 0xff;
1599 len = pkt_info[4];
1600
1601 wlfc->stats.txstatus_in++;
1602
1603 if (status_flag == WLFC_CTL_PKTFLAG_DISCARD) {
1604 wlfc->stats.pkt_freed++;
1605 }
1606
1607 else if (status_flag == WLFC_CTL_PKTFLAG_D11SUPPRESS) {
1608 wlfc->stats.d11_suppress++;
1609 remove_from_hanger = 0;
1610 }
1611
1612 else if (status_flag == WLFC_CTL_PKTFLAG_WLSUPPRESS) {
1613 wlfc->stats.wl_suppress++;
1614 remove_from_hanger = 0;
1615 }
1616
1617 else if (status_flag == WLFC_CTL_PKTFLAG_TOSSED_BYWLC) {
1618 wlfc->stats.wlc_tossed_pkts++;
1619 }
1620 while (count < len) {
1621 status = (status_g << 24) | (hslot << 8) | (hcnt);
1622 count++;
1623 hslot++;
1624 hcnt++;
1625
1626 ret = dhd_wlfc_hanger_poppkt(wlfc->hanger,
1627 WLFC_PKTID_HSLOT_GET(status), &pktbuf, remove_from_hanger);
1628 if (ret != BCME_OK) {
1629 /* do something */
1630 continue;
1631 }
1632
1633 entry = _dhd_wlfc_find_table_entry(wlfc, pktbuf);
1634
1635 if (!remove_from_hanger) {
1636 /* this packet was suppressed */
1637 if (!entry->suppressed || entry->generation != WLFC_PKTID_GEN(status)) {
1638 entry->suppressed = TRUE;
1639 entry->suppress_count = pktq_mlen(&entry->psq,
1640 NBITVAL((WL_TXSTATUS_GET_FIFO(status) << 1) + 1));
1641 entry->suppr_transit_count = entry->transit_count;
1642 }
1643 entry->generation = WLFC_PKTID_GEN(status);
1644 }
1645
1646 #ifdef PROP_TXSTATUS_DEBUG
1647 {
1648 uint32 new_t = OSL_SYSUPTIME();
1649 uint32 old_t;
1650 uint32 delta;
1651 old_t = ((wlfc_hanger_t*)(wlfc->hanger))->items[
1652 WLFC_PKTID_HSLOT_GET(status)].push_time;
1653
1654
1655 wlfc->stats.latency_sample_count++;
1656 if (new_t > old_t)
1657 delta = new_t - old_t;
1658 else
1659 delta = 0xffffffff + new_t - old_t;
1660 wlfc->stats.total_status_latency += delta;
1661 wlfc->stats.latency_most_recent = delta;
1662
1663 wlfc->stats.deltas[wlfc->stats.idx_delta++] = delta;
1664 if (wlfc->stats.idx_delta == sizeof(wlfc->stats.deltas)/sizeof(uint32))
1665 wlfc->stats.idx_delta = 0;
1666 }
1667 #endif /* PROP_TXSTATUS_DEBUG */
1668
1669 fifo_id = DHD_PKTTAG_FIFO(PKTTAG(pktbuf));
1670
1671 /* pick up the implicit credit from this packet */
1672 if (DHD_PKTTAG_CREDITCHECK(PKTTAG(pktbuf))) {
1673 if (wlfc->proptxstatus_mode == WLFC_FCMODE_IMPLIED_CREDIT) {
1674
1675 int lender, credit_returned = 0; /* Note that borrower is fifo_id */
1676
1677 /* Return credits to highest priority lender first */
1678 for (lender = AC_COUNT; lender >= 0; lender--) {
1679 if (wlfc->credits_borrowed[fifo_id][lender] > 0) {
1680 wlfc->FIFO_credit[lender]++;
1681 wlfc->credits_borrowed[fifo_id][lender]--;
1682 credit_returned = 1;
1683 break;
1684 }
1685 }
1686
1687 if (!credit_returned) {
1688 wlfc->FIFO_credit[fifo_id]++;
1689 }
1690 }
1691 }
1692 else {
1693 /*
1694 if this packet did not count against FIFO credit, it must have
1695 taken a requested_credit from the destination entry (for pspoll etc.)
1696 */
1697 if (!entry) {
1698
1699 entry = _dhd_wlfc_find_table_entry(wlfc, pktbuf);
1700 }
1701 if (!DHD_PKTTAG_ONETIMEPKTRQST(PKTTAG(pktbuf)))
1702 entry->requested_credit++;
1703 #ifdef PROP_TXSTATUS_DEBUG
1704 entry->dstncredit_acks++;
1705 #endif
1706 }
1707 if ((status_flag == WLFC_CTL_PKTFLAG_D11SUPPRESS) ||
1708 (status_flag == WLFC_CTL_PKTFLAG_WLSUPPRESS)) {
1709
1710 ret = _dhd_wlfc_enque_suppressed(wlfc, fifo_id, pktbuf);
1711 if (ret != BCME_OK) {
1712 /* delay q is full, drop this packet */
1713 dhd_wlfc_hanger_poppkt(wlfc->hanger, WLFC_PKTID_HSLOT_GET(status),
1714 &pktbuf, 1);
1715
1716 /* indicate failure and free the packet */
1717 dhd_txcomplete(dhd, pktbuf, FALSE);
1718 entry->transit_count--;
1719 DHD_WLFC_QMON_COMPLETE(entry);
1720 /* packet is transmitted Successfully by dongle
1721 * after first suppress.
1722 */
1723 if (entry->suppressed) {
1724 entry->suppr_transit_count--;
1725 }
1726 PKTFREE(wlfc->osh, pktbuf, TRUE);
1727 } else {
1728 /* Mark suppressed to avoid a double free during wlfc cleanup */
1729
1730 dhd_wlfc_hanger_mark_suppressed(wlfc->hanger,
1731 WLFC_PKTID_HSLOT_GET(status), WLFC_PKTID_GEN(status));
1732 entry->suppress_count++;
1733 }
1734 }
1735 else {
1736 dhd_txcomplete(dhd, pktbuf, TRUE);
1737 entry->transit_count--;
1738 DHD_WLFC_QMON_COMPLETE(entry);
1739
1740 /* This packet is transmitted Successfully by dongle
1741 * even after first suppress.
1742 */
1743 if (entry->suppressed) {
1744 entry->suppr_transit_count--;
1745 }
1746 /* free the packet */
1747 PKTFREE(wlfc->osh, pktbuf, TRUE);
1748 }
1749 }
1750 return BCME_OK;
1751 }
1752
1753 /* Handle discard or suppress indication */
1754 static int
dhd_wlfc_txstatus_update(dhd_pub_t * dhd,uint8 * pkt_info)1755 dhd_wlfc_txstatus_update(dhd_pub_t *dhd, uint8* pkt_info)
1756 {
1757 uint8 status_flag;
1758 uint32 status;
1759 int ret;
1760 int remove_from_hanger = 1;
1761 void* pktbuf;
1762 uint8 fifo_id;
1763 wlfc_mac_descriptor_t* entry = NULL;
1764 athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)
1765 dhd->wlfc_state;
1766
1767 memcpy(&status, pkt_info, sizeof(uint32));
1768 status_flag = WL_TXSTATUS_GET_FLAGS(status);
1769 wlfc->stats.txstatus_in++;
1770
1771 if (status_flag == WLFC_CTL_PKTFLAG_DISCARD) {
1772 wlfc->stats.pkt_freed++;
1773 }
1774
1775 else if (status_flag == WLFC_CTL_PKTFLAG_D11SUPPRESS) {
1776 wlfc->stats.d11_suppress++;
1777 remove_from_hanger = 0;
1778 }
1779
1780 else if (status_flag == WLFC_CTL_PKTFLAG_WLSUPPRESS) {
1781 wlfc->stats.wl_suppress++;
1782 remove_from_hanger = 0;
1783 }
1784
1785 else if (status_flag == WLFC_CTL_PKTFLAG_TOSSED_BYWLC) {
1786 wlfc->stats.wlc_tossed_pkts++;
1787 }
1788
1789 ret = dhd_wlfc_hanger_poppkt(wlfc->hanger,
1790 WLFC_PKTID_HSLOT_GET(status), &pktbuf, remove_from_hanger);
1791 if (ret != BCME_OK) {
1792 /* do something */
1793 return ret;
1794 }
1795
1796 entry = _dhd_wlfc_find_table_entry(wlfc, pktbuf);
1797
1798 if (!remove_from_hanger) {
1799 /* this packet was suppressed */
1800 if (!entry->suppressed || entry->generation != WLFC_PKTID_GEN(status)) {
1801 entry->suppressed = TRUE;
1802 entry->suppress_count = pktq_mlen(&entry->psq,
1803 NBITVAL((WL_TXSTATUS_GET_FIFO(status) << 1) + 1));
1804 entry->suppr_transit_count = entry->transit_count;
1805 }
1806 entry->generation = WLFC_PKTID_GEN(status);
1807 }
1808
1809 #ifdef PROP_TXSTATUS_DEBUG
1810 {
1811 uint32 new_t = OSL_SYSUPTIME();
1812 uint32 old_t;
1813 uint32 delta;
1814 old_t = ((wlfc_hanger_t*)(wlfc->hanger))->items[
1815 WLFC_PKTID_HSLOT_GET(status)].push_time;
1816
1817
1818 wlfc->stats.latency_sample_count++;
1819 if (new_t > old_t)
1820 delta = new_t - old_t;
1821 else
1822 delta = 0xffffffff + new_t - old_t;
1823 wlfc->stats.total_status_latency += delta;
1824 wlfc->stats.latency_most_recent = delta;
1825
1826 wlfc->stats.deltas[wlfc->stats.idx_delta++] = delta;
1827 if (wlfc->stats.idx_delta == sizeof(wlfc->stats.deltas)/sizeof(uint32))
1828 wlfc->stats.idx_delta = 0;
1829 }
1830 #endif /* PROP_TXSTATUS_DEBUG */
1831
1832 fifo_id = DHD_PKTTAG_FIFO(PKTTAG(pktbuf));
1833
1834 /* pick up the implicit credit from this packet */
1835 if (DHD_PKTTAG_CREDITCHECK(PKTTAG(pktbuf))) {
1836 if (wlfc->proptxstatus_mode == WLFC_FCMODE_IMPLIED_CREDIT) {
1837
1838 int lender, credit_returned = 0; /* Note that borrower is fifo_id */
1839
1840 /* Return credits to highest priority lender first */
1841 for (lender = AC_COUNT; lender >= 0; lender--) {
1842 if (wlfc->credits_borrowed[fifo_id][lender] > 0) {
1843 wlfc->FIFO_credit[lender]++;
1844 wlfc->credits_borrowed[fifo_id][lender]--;
1845 credit_returned = 1;
1846 break;
1847 }
1848 }
1849
1850 if (!credit_returned) {
1851 wlfc->FIFO_credit[fifo_id]++;
1852 }
1853 }
1854 }
1855 else {
1856 /*
1857 if this packet did not count against FIFO credit, it must have
1858 taken a requested_credit from the destination entry (for pspoll etc.)
1859 */
1860 if (!entry) {
1861
1862 entry = _dhd_wlfc_find_table_entry(wlfc, pktbuf);
1863 }
1864 if (!DHD_PKTTAG_ONETIMEPKTRQST(PKTTAG(pktbuf)))
1865 entry->requested_credit++;
1866 #ifdef PROP_TXSTATUS_DEBUG
1867 entry->dstncredit_acks++;
1868 #endif
1869 }
1870 if ((status_flag == WLFC_CTL_PKTFLAG_D11SUPPRESS) ||
1871 (status_flag == WLFC_CTL_PKTFLAG_WLSUPPRESS)) {
1872
1873 ret = _dhd_wlfc_enque_suppressed(wlfc, fifo_id, pktbuf);
1874 if (ret != BCME_OK) {
1875 /* delay q is full, drop this packet */
1876 dhd_wlfc_hanger_poppkt(wlfc->hanger, WLFC_PKTID_HSLOT_GET(status),
1877 &pktbuf, 1);
1878
1879 /* indicate failure and free the packet */
1880 dhd_txcomplete(dhd, pktbuf, FALSE);
1881 entry->transit_count--;
1882 DHD_WLFC_QMON_COMPLETE(entry);
1883 /* This packet is transmitted Successfully by
1884 * dongle even after first suppress.
1885 */
1886 if (entry->suppressed) {
1887 entry->suppr_transit_count--;
1888 }
1889 PKTFREE(wlfc->osh, pktbuf, TRUE);
1890 } else {
1891 /* Mark suppressed to avoid a double free during wlfc cleanup */
1892
1893 dhd_wlfc_hanger_mark_suppressed(wlfc->hanger,
1894 WLFC_PKTID_HSLOT_GET(status), WLFC_PKTID_GEN(status));
1895 entry->suppress_count++;
1896 }
1897 }
1898 else {
1899 dhd_txcomplete(dhd, pktbuf, TRUE);
1900 entry->transit_count--;
1901 DHD_WLFC_QMON_COMPLETE(entry);
1902
1903 /* This packet is transmitted Successfully by dongle even after first suppress. */
1904 if (entry->suppressed) {
1905 entry->suppr_transit_count--;
1906 }
1907 /* free the packet */
1908 PKTFREE(wlfc->osh, pktbuf, TRUE);
1909 }
1910 return BCME_OK;
1911 }
1912
1913 static int
dhd_wlfc_fifocreditback_indicate(dhd_pub_t * dhd,uint8 * credits)1914 dhd_wlfc_fifocreditback_indicate(dhd_pub_t *dhd, uint8* credits)
1915 {
1916 int i;
1917 athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)
1918 dhd->wlfc_state;
1919 for (i = 0; i < WLFC_CTL_VALUE_LEN_FIFO_CREDITBACK; i++) {
1920 #ifdef PROP_TXSTATUS_DEBUG
1921 wlfc->stats.fifo_credits_back[i] += credits[i];
1922 #endif
1923 /* update FIFO credits */
1924 if (wlfc->proptxstatus_mode == WLFC_FCMODE_EXPLICIT_CREDIT)
1925 {
1926 int lender; /* Note that borrower is i */
1927
1928 /* Return credits to highest priority lender first */
1929 for (lender = AC_COUNT; (lender >= 0) && (credits[i] > 0); lender--) {
1930 if (wlfc->credits_borrowed[i][lender] > 0) {
1931 if (credits[i] >= wlfc->credits_borrowed[i][lender]) {
1932 credits[i] -= wlfc->credits_borrowed[i][lender];
1933 wlfc->FIFO_credit[lender] +=
1934 wlfc->credits_borrowed[i][lender];
1935 wlfc->credits_borrowed[i][lender] = 0;
1936 }
1937 else {
1938 wlfc->credits_borrowed[i][lender] -= credits[i];
1939 wlfc->FIFO_credit[lender] += credits[i];
1940 credits[i] = 0;
1941 }
1942 }
1943 }
1944
1945 /* If we have more credits left over, these must belong to the AC */
1946 if (credits[i] > 0) {
1947 wlfc->FIFO_credit[i] += credits[i];
1948 }
1949 }
1950 }
1951
1952 return BCME_OK;
1953 }
1954
1955 static int
dhd_wlfc_dbg_senum_check(dhd_pub_t * dhd,uint8 * value)1956 dhd_wlfc_dbg_senum_check(dhd_pub_t *dhd, uint8 *value)
1957 {
1958 uint32 timestamp;
1959
1960 (void)dhd;
1961
1962 bcopy(&value[2], ×tamp, sizeof(uint32));
1963 DHD_INFO(("RXPKT: SEQ: %d, timestamp %d\n", value[1], timestamp));
1964 return BCME_OK;
1965 }
1966
1967
1968 static int
dhd_wlfc_rssi_indicate(dhd_pub_t * dhd,uint8 * rssi)1969 dhd_wlfc_rssi_indicate(dhd_pub_t *dhd, uint8* rssi)
1970 {
1971 (void)dhd;
1972 (void)rssi;
1973 return BCME_OK;
1974 }
1975
1976 static int
dhd_wlfc_mac_table_update(dhd_pub_t * dhd,uint8 * value,uint8 type)1977 dhd_wlfc_mac_table_update(dhd_pub_t *dhd, uint8* value, uint8 type)
1978 {
1979 int rc;
1980 athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)
1981 dhd->wlfc_state;
1982 wlfc_mac_descriptor_t* table;
1983 uint8 existing_index;
1984 uint8 table_index;
1985 uint8 ifid;
1986 uint8* ea;
1987
1988 WLFC_DBGMESG(("%s(), mac [%02x:%02x:%02x:%02x:%02x:%02x],%s,idx:%d,id:0x%02x\n",
1989 __FUNCTION__, value[2], value[3], value[4], value[5], value[6], value[7],
1990 ((type == WLFC_CTL_TYPE_MACDESC_ADD) ? "ADD":"DEL"),
1991 WLFC_MAC_DESC_GET_LOOKUP_INDEX(value[0]), value[0]));
1992
1993 table = wlfc->destination_entries.nodes;
1994 table_index = WLFC_MAC_DESC_GET_LOOKUP_INDEX(value[0]);
1995 ifid = value[1];
1996 ea = &value[2];
1997
1998 if (type == WLFC_CTL_TYPE_MACDESC_ADD) {
1999 existing_index = dhd_wlfc_find_mac_desc_id_from_mac(dhd, &value[2]);
2000 if (existing_index == WLFC_MAC_DESC_ID_INVALID) {
2001 /* this MAC entry does not exist, create one */
2002 if (!table[table_index].occupied) {
2003 table[table_index].mac_handle = value[0];
2004 rc = _dhd_wlfc_mac_entry_update(wlfc, &table[table_index],
2005 eWLFC_MAC_ENTRY_ACTION_ADD, ifid,
2006 wlfc->destination_entries.interfaces[ifid].iftype,
2007 ea);
2008 }
2009 else {
2010 /* the space should have been empty, but it's not */
2011 wlfc->stats.mac_update_failed++;
2012 }
2013 }
2014 else {
2015 /*
2016 there is an existing entry, move it to new index
2017 if necessary.
2018 */
2019 if (existing_index != table_index) {
2020 /* if we already have an entry, free the old one */
2021 table[existing_index].occupied = 0;
2022 table[existing_index].state = WLFC_STATE_CLOSE;
2023 table[existing_index].requested_credit = 0;
2024 table[existing_index].interface_id = 0;
2025 /* enable after packets are queued-deqeued properly.
2026 pktq_flush(dhd->osh, &table[existing_index].psq, FALSE, NULL, 0);
2027 */
2028 }
2029 }
2030 }
2031 if (type == WLFC_CTL_TYPE_MACDESC_DEL) {
2032 if (table[table_index].occupied) {
2033 rc = _dhd_wlfc_mac_entry_update(wlfc, &table[table_index],
2034 eWLFC_MAC_ENTRY_ACTION_DEL, ifid,
2035 wlfc->destination_entries.interfaces[ifid].iftype,
2036 ea);
2037 }
2038 else {
2039 /* the space should have been occupied, but it's not */
2040 wlfc->stats.mac_update_failed++;
2041 }
2042 }
2043 BCM_REFERENCE(rc);
2044 return BCME_OK;
2045 }
2046
2047 static int
dhd_wlfc_psmode_update(dhd_pub_t * dhd,uint8 * value,uint8 type)2048 dhd_wlfc_psmode_update(dhd_pub_t *dhd, uint8* value, uint8 type)
2049 {
2050 /* Handle PS on/off indication */
2051 athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)
2052 dhd->wlfc_state;
2053 wlfc_mac_descriptor_t* table;
2054 wlfc_mac_descriptor_t* desc;
2055 uint8 mac_handle = value[0];
2056 int i;
2057
2058 table = wlfc->destination_entries.nodes;
2059 desc = &table[WLFC_MAC_DESC_GET_LOOKUP_INDEX(mac_handle)];
2060 if (desc->occupied) {
2061 /* a fresh PS mode should wipe old ps credits? */
2062 desc->requested_credit = 0;
2063 if (type == WLFC_CTL_TYPE_MAC_OPEN) {
2064 desc->state = WLFC_STATE_OPEN;
2065 DHD_WLFC_CTRINC_MAC_OPEN(desc);
2066 }
2067 else {
2068 desc->state = WLFC_STATE_CLOSE;
2069 DHD_WLFC_CTRINC_MAC_CLOSE(desc);
2070 /*
2071 Indicate to firmware if there is any traffic pending.
2072 */
2073 for (i = AC_BE; i < AC_COUNT; i++) {
2074 _dhd_wlfc_traffic_pending_check(wlfc, desc, i);
2075 }
2076 }
2077 }
2078 else {
2079 wlfc->stats.psmode_update_failed++;
2080 }
2081 return BCME_OK;
2082 }
2083
2084 static int
dhd_wlfc_interface_update(dhd_pub_t * dhd,uint8 * value,uint8 type)2085 dhd_wlfc_interface_update(dhd_pub_t *dhd, uint8* value, uint8 type)
2086 {
2087 /* Handle PS on/off indication */
2088 athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)
2089 dhd->wlfc_state;
2090 wlfc_mac_descriptor_t* table;
2091 uint8 if_id = value[0];
2092
2093 if (if_id < WLFC_MAX_IFNUM) {
2094 table = wlfc->destination_entries.interfaces;
2095 if (table[if_id].occupied) {
2096 if (type == WLFC_CTL_TYPE_INTERFACE_OPEN) {
2097 table[if_id].state = WLFC_STATE_OPEN;
2098 /* WLFC_DBGMESG(("INTERFACE[%d] OPEN\n", if_id)); */
2099 }
2100 else {
2101 table[if_id].state = WLFC_STATE_CLOSE;
2102 /* WLFC_DBGMESG(("INTERFACE[%d] CLOSE\n", if_id)); */
2103 }
2104 return BCME_OK;
2105 }
2106 }
2107 wlfc->stats.interface_update_failed++;
2108
2109 return BCME_OK;
2110 }
2111
2112 static int
dhd_wlfc_credit_request(dhd_pub_t * dhd,uint8 * value)2113 dhd_wlfc_credit_request(dhd_pub_t *dhd, uint8* value)
2114 {
2115 athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)
2116 dhd->wlfc_state;
2117 wlfc_mac_descriptor_t* table;
2118 wlfc_mac_descriptor_t* desc;
2119 uint8 mac_handle;
2120 uint8 credit;
2121
2122 table = wlfc->destination_entries.nodes;
2123 mac_handle = value[1];
2124 credit = value[0];
2125
2126 desc = &table[WLFC_MAC_DESC_GET_LOOKUP_INDEX(mac_handle)];
2127 if (desc->occupied) {
2128 desc->requested_credit = credit;
2129
2130 desc->ac_bitmap = value[2];
2131 }
2132 else {
2133 wlfc->stats.credit_request_failed++;
2134 }
2135 return BCME_OK;
2136 }
2137
2138 static int
dhd_wlfc_packet_request(dhd_pub_t * dhd,uint8 * value)2139 dhd_wlfc_packet_request(dhd_pub_t *dhd, uint8* value)
2140 {
2141 athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)
2142 dhd->wlfc_state;
2143 wlfc_mac_descriptor_t* table;
2144 wlfc_mac_descriptor_t* desc;
2145 uint8 mac_handle;
2146 uint8 packet_count;
2147
2148 table = wlfc->destination_entries.nodes;
2149 mac_handle = value[1];
2150 packet_count = value[0];
2151
2152 desc = &table[WLFC_MAC_DESC_GET_LOOKUP_INDEX(mac_handle)];
2153 if (desc->occupied) {
2154 desc->requested_packet = packet_count;
2155
2156 desc->ac_bitmap = value[2];
2157 }
2158 else {
2159 wlfc->stats.packet_request_failed++;
2160 }
2161 return BCME_OK;
2162 }
2163
2164 static void
dhd_wlfc_reorderinfo_indicate(uint8 * val,uint8 len,uchar * info_buf,uint * info_len)2165 dhd_wlfc_reorderinfo_indicate(uint8 *val, uint8 len, uchar *info_buf, uint *info_len)
2166 {
2167 if (info_len) {
2168 if (info_buf) {
2169 bcopy(val, info_buf, len);
2170 *info_len = len;
2171 }
2172 else
2173 *info_len = 0;
2174 }
2175 }
2176
2177 int
dhd_wlfc_parse_header_info(dhd_pub_t * dhd,void * pktbuf,int tlv_hdr_len,uchar * reorder_info_buf,uint * reorder_info_len)2178 dhd_wlfc_parse_header_info(dhd_pub_t *dhd, void* pktbuf, int tlv_hdr_len, uchar *reorder_info_buf,
2179 uint *reorder_info_len)
2180 {
2181 uint8 type, len;
2182 uint8* value;
2183 uint8* tmpbuf;
2184 uint16 remainder = tlv_hdr_len;
2185 uint16 processed = 0;
2186 athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)
2187 dhd->wlfc_state;
2188 tmpbuf = (uint8*)PKTDATA(dhd->osh, pktbuf);
2189 if (remainder) {
2190 while ((processed < (WLFC_MAX_PENDING_DATALEN * 2)) && (remainder > 0)) {
2191 type = tmpbuf[processed];
2192 if (type == WLFC_CTL_TYPE_FILLER) {
2193 remainder -= 1;
2194 processed += 1;
2195 continue;
2196 }
2197
2198 len = tmpbuf[processed + 1];
2199 value = &tmpbuf[processed + 2];
2200
2201 if (remainder < (2 + len))
2202 break;
2203
2204 remainder -= 2 + len;
2205 processed += 2 + len;
2206 if (type == WLFC_CTL_TYPE_TXSTATUS)
2207 dhd_wlfc_txstatus_update(dhd, value);
2208 if (type == WLFC_CTL_TYPE_COMP_TXSTATUS)
2209 dhd_wlfc_compressed_txstatus_update(dhd, value, len);
2210
2211 else if (type == WLFC_CTL_TYPE_HOST_REORDER_RXPKTS)
2212 dhd_wlfc_reorderinfo_indicate(value, len, reorder_info_buf,
2213 reorder_info_len);
2214 else if (type == WLFC_CTL_TYPE_FIFO_CREDITBACK)
2215 dhd_wlfc_fifocreditback_indicate(dhd, value);
2216
2217 else if (type == WLFC_CTL_TYPE_RSSI)
2218 dhd_wlfc_rssi_indicate(dhd, value);
2219
2220 else if (type == WLFC_CTL_TYPE_MAC_REQUEST_CREDIT)
2221 dhd_wlfc_credit_request(dhd, value);
2222
2223 else if (type == WLFC_CTL_TYPE_MAC_REQUEST_PACKET)
2224 dhd_wlfc_packet_request(dhd, value);
2225
2226 else if ((type == WLFC_CTL_TYPE_MAC_OPEN) ||
2227 (type == WLFC_CTL_TYPE_MAC_CLOSE))
2228 dhd_wlfc_psmode_update(dhd, value, type);
2229
2230 else if ((type == WLFC_CTL_TYPE_MACDESC_ADD) ||
2231 (type == WLFC_CTL_TYPE_MACDESC_DEL))
2232 dhd_wlfc_mac_table_update(dhd, value, type);
2233
2234 else if (type == WLFC_CTL_TYPE_TRANS_ID)
2235 dhd_wlfc_dbg_senum_check(dhd, value);
2236
2237 else if ((type == WLFC_CTL_TYPE_INTERFACE_OPEN) ||
2238 (type == WLFC_CTL_TYPE_INTERFACE_CLOSE)) {
2239 dhd_wlfc_interface_update(dhd, value, type);
2240 }
2241 }
2242 if (remainder != 0) {
2243 /* trouble..., something is not right */
2244 wlfc->stats.tlv_parse_failed++;
2245 }
2246 }
2247 return BCME_OK;
2248 }
2249
2250 int
dhd_wlfc_init(dhd_pub_t * dhd)2251 dhd_wlfc_init(dhd_pub_t *dhd)
2252 {
2253 char iovbuf[12]; /* Room for "tlv" + '\0' + parameter */
2254 /* enable all signals & indicate host proptxstatus logic is active */
2255 uint32 tlv = dhd->wlfc_enabled?
2256 WLFC_FLAGS_RSSI_SIGNALS |
2257 WLFC_FLAGS_XONXOFF_SIGNALS |
2258 WLFC_FLAGS_CREDIT_STATUS_SIGNALS |
2259 WLFC_FLAGS_HOST_PROPTXSTATUS_ACTIVE |
2260 WLFC_FLAGS_HOST_RXRERODER_ACTIVE : 0;
2261 /* WLFC_FLAGS_HOST_PROPTXSTATUS_ACTIVE | WLFC_FLAGS_HOST_RXRERODER_ACTIVE : 0; */
2262
2263
2264 /*
2265 try to enable/disable signaling by sending "tlv" iovar. if that fails,
2266 fallback to no flow control? Print a message for now.
2267 */
2268
2269 /* enable proptxtstatus signaling by default */
2270 bcm_mkiovar("tlv", (char *)&tlv, 4, iovbuf, sizeof(iovbuf));
2271 if (dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0) < 0) {
2272 DHD_ERROR(("dhd_wlfc_init(): failed to enable/disable bdcv2 tlv signaling\n"));
2273 }
2274 else {
2275 /*
2276 Leaving the message for now, it should be removed after a while; once
2277 the tlv situation is stable.
2278 */
2279 DHD_ERROR(("dhd_wlfc_init(): successfully %s bdcv2 tlv signaling, %d\n",
2280 dhd->wlfc_enabled?"enabled":"disabled", tlv));
2281 }
2282 return BCME_OK;
2283 }
2284
2285 int
dhd_wlfc_enable(dhd_pub_t * dhd)2286 dhd_wlfc_enable(dhd_pub_t *dhd)
2287 {
2288 int i;
2289 athost_wl_status_info_t* wlfc;
2290
2291 if (!dhd->wlfc_enabled || dhd->wlfc_state)
2292 return BCME_OK;
2293
2294 /* allocate space to track txstatus propagated from firmware */
2295 dhd->wlfc_state = MALLOC(dhd->osh, sizeof(athost_wl_status_info_t));
2296 if (dhd->wlfc_state == NULL)
2297 return BCME_NOMEM;
2298
2299 /* initialize state space */
2300 wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
2301 memset(wlfc, 0, sizeof(athost_wl_status_info_t));
2302
2303 /* remember osh & dhdp */
2304 wlfc->osh = dhd->osh;
2305 wlfc->dhdp = dhd;
2306
2307 wlfc->hanger =
2308 dhd_wlfc_hanger_create(dhd->osh, WLFC_HANGER_MAXITEMS);
2309 if (wlfc->hanger == NULL) {
2310 MFREE(dhd->osh, dhd->wlfc_state, sizeof(athost_wl_status_info_t));
2311 dhd->wlfc_state = NULL;
2312 DHD_ERROR(("Failed to malloc dhd->wlfc_state\n"));
2313 return BCME_NOMEM;
2314 }
2315
2316 /* initialize all interfaces to accept traffic */
2317 for (i = 0; i < WLFC_MAX_IFNUM; i++) {
2318 wlfc->hostif_flow_state[i] = OFF;
2319 }
2320
2321 wlfc->destination_entries.other.state = WLFC_STATE_OPEN;
2322 /* bc/mc FIFO is always open [credit aside], i.e. b[5] */
2323 wlfc->destination_entries.other.ac_bitmap = 0x1f;
2324 wlfc->destination_entries.other.interface_id = 0;
2325
2326 wlfc->proptxstatus_mode = WLFC_FCMODE_EXPLICIT_CREDIT;
2327
2328 wlfc->allow_credit_borrow = TRUE;
2329 wlfc->borrow_defer_timestamp = 0;
2330
2331 return BCME_OK;
2332 }
2333
2334 /* release all packet resources */
2335 void
dhd_wlfc_cleanup(dhd_pub_t * dhd,ifpkt_cb_t fn,int arg)2336 dhd_wlfc_cleanup(dhd_pub_t *dhd, ifpkt_cb_t fn, int arg)
2337 {
2338 int i;
2339 int total_entries;
2340 athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)
2341 dhd->wlfc_state;
2342 wlfc_mac_descriptor_t* table;
2343 wlfc_hanger_t* h;
2344 int prec;
2345 void *pkt = NULL;
2346 struct pktq *txq = NULL;
2347 if (dhd->wlfc_state == NULL)
2348 return;
2349 /* flush bus->txq */
2350 txq = dhd_bus_txq(dhd->bus);
2351 /* any in the hanger? */
2352 h = (wlfc_hanger_t*)wlfc->hanger;
2353 total_entries = sizeof(wlfc->destination_entries)/sizeof(wlfc_mac_descriptor_t);
2354 /* search all entries, include nodes as well as interfaces */
2355 table = (wlfc_mac_descriptor_t*)&wlfc->destination_entries;
2356
2357 for (i = 0; i < total_entries; i++) {
2358 if (table[i].occupied && (fn == NULL || (arg == table[i].interface_id))) {
2359 if (table[i].psq.len) {
2360 WLFC_DBGMESG(("%s(): DELAYQ[%d].len = %d\n",
2361 __FUNCTION__, i, table[i].psq.len));
2362 /* release packets held in DELAYQ */
2363 pktq_flush(wlfc->osh, &table[i].psq, TRUE, fn, arg);
2364 }
2365 if (fn == NULL)
2366 table[i].occupied = 0;
2367 }
2368 }
2369 for (prec = 0; prec < txq->num_prec; prec++) {
2370 pkt = pktq_pdeq_with_fn(txq, prec, fn, arg);
2371 while (pkt) {
2372 for (i = 0; i < h->max_items; i++) {
2373 if (pkt == h->items[i].pkt) {
2374 if (h->items[i].state == WLFC_HANGER_ITEM_STATE_INUSE) {
2375 PKTFREE(wlfc->osh, h->items[i].pkt, TRUE);
2376 h->items[i].state = WLFC_HANGER_ITEM_STATE_FREE;
2377 } else if (h->items[i].state ==
2378 WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED) {
2379 /* These are already freed from the psq */
2380 h->items[i].state = WLFC_HANGER_ITEM_STATE_FREE;
2381 }
2382 break;
2383 }
2384 }
2385 pkt = pktq_pdeq(txq, prec);
2386 }
2387 }
2388 /* flush remained pkt in hanger queue, not in bus->txq */
2389 for (i = 0; i < h->max_items; i++) {
2390 if (h->items[i].state == WLFC_HANGER_ITEM_STATE_INUSE) {
2391 if (fn == NULL || (*fn)(h->items[i].pkt, arg)) {
2392 PKTFREE(wlfc->osh, h->items[i].pkt, TRUE);
2393 h->items[i].state = WLFC_HANGER_ITEM_STATE_FREE;
2394 }
2395 } else if (h->items[i].state == WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED) {
2396 if (fn == NULL || (*fn)(h->items[i].pkt, arg)) {
2397 /* These are freed from the psq so no need to free again */
2398 h->items[i].state = WLFC_HANGER_ITEM_STATE_FREE;
2399 }
2400 }
2401 }
2402 return;
2403 }
2404
2405 void
dhd_wlfc_deinit(dhd_pub_t * dhd)2406 dhd_wlfc_deinit(dhd_pub_t *dhd)
2407 {
2408 /* cleanup all psq related resources */
2409 athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)
2410 dhd->wlfc_state;
2411
2412 dhd_os_wlfc_block(dhd);
2413 if (dhd->wlfc_state == NULL) {
2414 dhd_os_wlfc_unblock(dhd);
2415 return;
2416 }
2417
2418 #ifdef PROP_TXSTATUS_DEBUG
2419 {
2420 int i;
2421 wlfc_hanger_t* h = (wlfc_hanger_t*)wlfc->hanger;
2422 for (i = 0; i < h->max_items; i++) {
2423 if (h->items[i].state != WLFC_HANGER_ITEM_STATE_FREE) {
2424 WLFC_DBGMESG(("%s() pkt[%d] = 0x%p, FIFO_credit_used:%d\n",
2425 __FUNCTION__, i, h->items[i].pkt,
2426 DHD_PKTTAG_CREDITCHECK(PKTTAG(h->items[i].pkt))));
2427 }
2428 }
2429 }
2430 #endif
2431 /* delete hanger */
2432 dhd_wlfc_hanger_delete(dhd->osh, wlfc->hanger);
2433
2434 /* free top structure */
2435 MFREE(dhd->osh, dhd->wlfc_state, sizeof(athost_wl_status_info_t));
2436 dhd->wlfc_state = NULL;
2437 dhd_os_wlfc_unblock(dhd);
2438
2439 return;
2440 }
2441 #endif /* PROP_TXSTATUS */
2442