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