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