dhd_cdc.c revision 100a2d309a0ff5c4808c91d1b113d13cd236e38a
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 341930 2012-06-29 04:51:25Z $ 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_get_genbit(void* hanger, void* pkt, uint32 slot_id, int* gen) 685{ 686 int rc = BCME_OK; 687 wlfc_hanger_t* h = (wlfc_hanger_t*)hanger; 688 689 *gen = 0xff; 690 691 /* this packet was not pushed at the time it went to the firmware */ 692 if (slot_id == WLFC_HANGER_MAXITEMS) 693 return BCME_NOTFOUND; 694 695 if (h) { 696 if ((h->items[slot_id].state == WLFC_HANGER_ITEM_STATE_INUSE) || 697 (h->items[slot_id].state == WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED)) { 698 *gen = h->items[slot_id].gen; 699 } 700 else { 701 rc = BCME_NOTFOUND; 702 } 703 } 704 else 705 rc = BCME_BADARG; 706 return rc; 707} 708 709static int 710dhd_wlfc_hanger_pushpkt(void* hanger, void* pkt, uint32 slot_id) 711{ 712 int rc = BCME_OK; 713 wlfc_hanger_t* h = (wlfc_hanger_t*)hanger; 714 715 if (h && (slot_id < WLFC_HANGER_MAXITEMS)) { 716 if (h->items[slot_id].state == WLFC_HANGER_ITEM_STATE_FREE) { 717 h->items[slot_id].state = WLFC_HANGER_ITEM_STATE_INUSE; 718 h->items[slot_id].pkt = pkt; 719 h->items[slot_id].identifier = slot_id; 720 h->pushed++; 721 } 722 else { 723 h->failed_to_push++; 724 rc = BCME_NOTFOUND; 725 } 726 } 727 else 728 rc = BCME_BADARG; 729 return rc; 730} 731 732static int 733dhd_wlfc_hanger_poppkt(void* hanger, uint32 slot_id, void** pktout, int remove_from_hanger) 734{ 735 int rc = BCME_OK; 736 wlfc_hanger_t* h = (wlfc_hanger_t*)hanger; 737 738 /* this packet was not pushed at the time it went to the firmware */ 739 if (slot_id == WLFC_HANGER_MAXITEMS) 740 return BCME_NOTFOUND; 741 742 if (h) { 743 if (h->items[slot_id].state != WLFC_HANGER_ITEM_STATE_FREE) { 744 *pktout = h->items[slot_id].pkt; 745 if (remove_from_hanger) { 746 h->items[slot_id].state = 747 WLFC_HANGER_ITEM_STATE_FREE; 748 h->items[slot_id].pkt = NULL; 749 h->items[slot_id].identifier = 0; 750 h->items[slot_id].gen = 0xff; 751 h->popped++; 752 } 753 } 754 else { 755 h->failed_to_pop++; 756 rc = BCME_NOTFOUND; 757 } 758 } 759 else 760 rc = BCME_BADARG; 761 return rc; 762} 763 764static int 765dhd_wlfc_hanger_mark_suppressed(void* hanger, uint32 slot_id, uint8 gen) 766{ 767 int rc = BCME_OK; 768 wlfc_hanger_t* h = (wlfc_hanger_t*)hanger; 769 770 /* this packet was not pushed at the time it went to the firmware */ 771 if (slot_id == WLFC_HANGER_MAXITEMS) 772 return BCME_NOTFOUND; 773 774 h->items[slot_id].gen = gen; 775 if (h && (h->items[slot_id].state == WLFC_HANGER_ITEM_STATE_INUSE)) { 776 h->items[slot_id].state = WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED; 777 } 778 else 779 rc = BCME_BADARG; 780 return rc; 781} 782 783static int 784_dhd_wlfc_pushheader(athost_wl_status_info_t* ctx, void* p, bool tim_signal, 785 uint8 tim_bmp, uint8 mac_handle, uint32 htodtag) 786{ 787 uint32 wl_pktinfo = 0; 788 uint8* wlh; 789 uint8 dataOffset; 790 uint8 fillers; 791 uint8 tim_signal_len = 0; 792 793 struct bdc_header *h; 794 795 if (tim_signal) { 796 tim_signal_len = 1 + 1 + WLFC_CTL_VALUE_LEN_PENDING_TRAFFIC_BMP; 797 } 798 799 /* +2 is for Type[1] and Len[1] in TLV, plus TIM signal */ 800 dataOffset = WLFC_CTL_VALUE_LEN_PKTTAG + 2 + tim_signal_len; 801 fillers = ROUNDUP(dataOffset, 4) - dataOffset; 802 dataOffset += fillers; 803 804 PKTPUSH(ctx->osh, p, dataOffset); 805 wlh = (uint8*) PKTDATA(ctx->osh, p); 806 807 wl_pktinfo = htol32(htodtag); 808 809 wlh[0] = WLFC_CTL_TYPE_PKTTAG; 810 wlh[1] = WLFC_CTL_VALUE_LEN_PKTTAG; 811 memcpy(&wlh[2], &wl_pktinfo, sizeof(uint32)); 812 813 if (tim_signal_len) { 814 wlh[dataOffset - fillers - tim_signal_len ] = 815 WLFC_CTL_TYPE_PENDING_TRAFFIC_BMP; 816 wlh[dataOffset - fillers - tim_signal_len + 1] = 817 WLFC_CTL_VALUE_LEN_PENDING_TRAFFIC_BMP; 818 wlh[dataOffset - fillers - tim_signal_len + 2] = mac_handle; 819 wlh[dataOffset - fillers - tim_signal_len + 3] = tim_bmp; 820 } 821 if (fillers) 822 memset(&wlh[dataOffset - fillers], WLFC_CTL_TYPE_FILLER, fillers); 823 824 PKTPUSH(ctx->osh, p, BDC_HEADER_LEN); 825 h = (struct bdc_header *)PKTDATA(ctx->osh, p); 826 h->flags = (BDC_PROTO_VER << BDC_FLAG_VER_SHIFT); 827 if (PKTSUMNEEDED(p)) 828 h->flags |= BDC_FLAG_SUM_NEEDED; 829 830 831 h->priority = (PKTPRIO(p) & BDC_PRIORITY_MASK); 832 h->flags2 = 0; 833 h->dataOffset = dataOffset >> 2; 834 BDC_SET_IF_IDX(h, DHD_PKTTAG_IF(PKTTAG(p))); 835 return BCME_OK; 836} 837 838static int 839_dhd_wlfc_pullheader(athost_wl_status_info_t* ctx, void* pktbuf) 840{ 841 struct bdc_header *h; 842 843 if (PKTLEN(ctx->osh, pktbuf) < BDC_HEADER_LEN) { 844 WLFC_DBGMESG(("%s: rx data too short (%d < %d)\n", __FUNCTION__, 845 PKTLEN(ctx->osh, pktbuf), BDC_HEADER_LEN)); 846 return BCME_ERROR; 847 } 848 h = (struct bdc_header *)PKTDATA(ctx->osh, pktbuf); 849 850 /* pull BDC header */ 851 PKTPULL(ctx->osh, pktbuf, BDC_HEADER_LEN); 852 /* pull wl-header */ 853 PKTPULL(ctx->osh, pktbuf, (h->dataOffset << 2)); 854 return BCME_OK; 855} 856 857static wlfc_mac_descriptor_t* 858_dhd_wlfc_find_table_entry(athost_wl_status_info_t* ctx, void* p) 859{ 860 int i; 861 wlfc_mac_descriptor_t* table = ctx->destination_entries.nodes; 862 uint8 ifid = DHD_PKTTAG_IF(PKTTAG(p)); 863 uint8* dstn = DHD_PKTTAG_DSTN(PKTTAG(p)); 864 865 if (((ctx->destination_entries.interfaces[ifid].iftype == WLC_E_IF_ROLE_STA) || 866 ETHER_ISMULTI(dstn) || 867 (ctx->destination_entries.interfaces[ifid].iftype == WLC_E_IF_ROLE_P2P_CLIENT)) && 868 (ctx->destination_entries.interfaces[ifid].occupied)) { 869 return &ctx->destination_entries.interfaces[ifid]; 870 } 871 872 for (i = 0; i < WLFC_MAC_DESC_TABLE_SIZE; i++) { 873 if (table[i].occupied) { 874 if (table[i].interface_id == ifid) { 875 if (!memcmp(table[i].ea, dstn, ETHER_ADDR_LEN)) 876 return &table[i]; 877 } 878 } 879 } 880 return &ctx->destination_entries.other; 881} 882 883static int 884_dhd_wlfc_rollback_packet_toq(athost_wl_status_info_t* ctx, 885 void* p, ewlfc_packet_state_t pkt_type, uint32 hslot) 886{ 887 /* 888 put the packet back to the head of queue 889 890 - a packet from send-q will need to go back to send-q and not delay-q 891 since that will change the order of packets. 892 - suppressed packet goes back to suppress sub-queue 893 - pull out the header, if new or delayed packet 894 895 Note: hslot is used only when header removal is done. 896 */ 897 wlfc_mac_descriptor_t* entry; 898 void* pktout; 899 int rc = BCME_OK; 900 int prec; 901 902 entry = _dhd_wlfc_find_table_entry(ctx, p); 903 prec = DHD_PKTTAG_FIFO(PKTTAG(p)); 904 if (entry != NULL) { 905 if (pkt_type == eWLFC_PKTTYPE_SUPPRESSED) { 906 /* wl-header is saved for suppressed packets */ 907 if (WLFC_PKTQ_PENQ_HEAD(&entry->psq, ((prec << 1) + 1), p) == NULL) { 908 WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); 909 rc = BCME_ERROR; 910 } 911 } 912 else { 913 /* remove header first */ 914 _dhd_wlfc_pullheader(ctx, p); 915 916 if (pkt_type == eWLFC_PKTTYPE_DELAYED) { 917 /* delay-q packets are going to delay-q */ 918 if (WLFC_PKTQ_PENQ_HEAD(&entry->psq, (prec << 1), p) == NULL) { 919 WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); 920 rc = BCME_ERROR; 921 } 922 } 923 else { 924 /* these are going to SENDQ */ 925 if (WLFC_PKTQ_PENQ_HEAD(&ctx->SENDQ, prec, p) == NULL) { 926 WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); 927 rc = BCME_ERROR; 928 } 929 } 930 /* free the hanger slot */ 931 dhd_wlfc_hanger_poppkt(ctx->hanger, hslot, &pktout, 1); 932 933 /* decrement sequence count */ 934 WLFC_DECR_SEQCOUNT(entry, prec); 935 } 936 /* 937 if this packet did not count against FIFO credit, it must have 938 taken a requested_credit from the firmware (for pspoll etc.) 939 */ 940 if (!DHD_PKTTAG_CREDITCHECK(PKTTAG(p))) { 941 entry->requested_credit++; 942 } 943 } 944 else { 945 WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); 946 rc = BCME_ERROR; 947 } 948 if (rc != BCME_OK) 949 ctx->stats.rollback_failed++; 950 else 951 ctx->stats.rollback++; 952 953 return rc; 954} 955 956static void 957_dhd_wlfc_flow_control_check(athost_wl_status_info_t* ctx, struct pktq* pq, uint8 if_id) 958{ 959 if ((pq->len <= WLFC_FLOWCONTROL_LOWATER) && (ctx->hostif_flow_state[if_id] == ON)) { 960 /* start traffic */ 961 ctx->hostif_flow_state[if_id] = OFF; 962 /* 963 WLFC_DBGMESG(("qlen:%02d, if:%02d, ->OFF, start traffic %s()\n", 964 pq->len, if_id, __FUNCTION__)); 965 */ 966 WLFC_DBGMESG(("F")); 967 dhd_txflowcontrol(ctx->dhdp, if_id, OFF); 968 ctx->toggle_host_if = 0; 969 } 970 if ((pq->len >= WLFC_FLOWCONTROL_HIWATER) && (ctx->hostif_flow_state[if_id] == OFF)) { 971 /* stop traffic */ 972 ctx->hostif_flow_state[if_id] = ON; 973 /* 974 WLFC_DBGMESG(("qlen:%02d, if:%02d, ->ON, stop traffic %s()\n", 975 pq->len, if_id, __FUNCTION__)); 976 */ 977 WLFC_DBGMESG(("N")); 978 dhd_txflowcontrol(ctx->dhdp, if_id, ON); 979 ctx->host_ifidx = if_id; 980 ctx->toggle_host_if = 1; 981 } 982 return; 983} 984 985static int 986_dhd_wlfc_send_signalonly_packet(athost_wl_status_info_t* ctx, wlfc_mac_descriptor_t* entry, 987 uint8 ta_bmp) 988{ 989 int rc = BCME_OK; 990 void* p = NULL; 991 int dummylen = ((dhd_pub_t *)ctx->dhdp)->hdrlen+ 12; 992 993 /* allocate a dummy packet */ 994 p = PKTGET(ctx->osh, dummylen, TRUE); 995 if (p) { 996 PKTPULL(ctx->osh, p, dummylen); 997 DHD_PKTTAG_SET_H2DTAG(PKTTAG(p), 0); 998 _dhd_wlfc_pushheader(ctx, p, TRUE, ta_bmp, entry->mac_handle, 0); 999 DHD_PKTTAG_SETSIGNALONLY(PKTTAG(p), 1); 1000#ifdef PROP_TXSTATUS_DEBUG 1001 ctx->stats.signal_only_pkts_sent++; 1002#endif 1003 rc = dhd_bus_txdata(((dhd_pub_t *)ctx->dhdp)->bus, p); 1004 if (rc != BCME_OK) { 1005 PKTFREE(ctx->osh, p, TRUE); 1006 } 1007 } 1008 else { 1009 DHD_ERROR(("%s: couldn't allocate new %d-byte packet\n", 1010 __FUNCTION__, dummylen)); 1011 rc = BCME_NOMEM; 1012 } 1013 return rc; 1014} 1015 1016/* Return TRUE if traffic availability changed */ 1017static bool 1018_dhd_wlfc_traffic_pending_check(athost_wl_status_info_t* ctx, wlfc_mac_descriptor_t* entry, 1019 int prec) 1020{ 1021 bool rc = FALSE; 1022 1023 if (entry->state == WLFC_STATE_CLOSE) { 1024 if ((pktq_plen(&entry->psq, (prec << 1)) == 0) && 1025 (pktq_plen(&entry->psq, ((prec << 1) + 1)) == 0)) { 1026 1027 if (entry->traffic_pending_bmp & NBITVAL(prec)) { 1028 rc = TRUE; 1029 entry->traffic_pending_bmp = 1030 entry->traffic_pending_bmp & ~ NBITVAL(prec); 1031 } 1032 } 1033 else { 1034 if (!(entry->traffic_pending_bmp & NBITVAL(prec))) { 1035 rc = TRUE; 1036 entry->traffic_pending_bmp = 1037 entry->traffic_pending_bmp | NBITVAL(prec); 1038 } 1039 } 1040 } 1041 if (rc) { 1042 /* request a TIM update to firmware at the next piggyback opportunity */ 1043 if (entry->traffic_lastreported_bmp != entry->traffic_pending_bmp) { 1044 entry->send_tim_signal = 1; 1045 _dhd_wlfc_send_signalonly_packet(ctx, entry, entry->traffic_pending_bmp); 1046 entry->traffic_lastreported_bmp = entry->traffic_pending_bmp; 1047 entry->send_tim_signal = 0; 1048 } 1049 else { 1050 rc = FALSE; 1051 } 1052 } 1053 return rc; 1054} 1055 1056static int 1057_dhd_wlfc_enque_suppressed(athost_wl_status_info_t* ctx, int prec, void* p, uint8 gen) 1058{ 1059 wlfc_mac_descriptor_t* entry; 1060 1061 entry = _dhd_wlfc_find_table_entry(ctx, p); 1062 if (entry == NULL) { 1063 WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); 1064 return BCME_NOTFOUND; 1065 } 1066 /* 1067 - suppressed packets go to sub_queue[2*prec + 1] AND 1068 - delayed packets go to sub_queue[2*prec + 0] to ensure 1069 order of delivery. 1070 */ 1071 if (WLFC_PKTQ_PENQ(&entry->psq, ((prec << 1) + 1), p) == NULL) { 1072 ctx->stats.delayq_full_error++; 1073 /* WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); */ 1074 WLFC_DBGMESG(("s")); 1075 return BCME_ERROR; 1076 } 1077 /* A packet has been pushed, update traffic availability bitmap, if applicable */ 1078 _dhd_wlfc_traffic_pending_check(ctx, entry, prec); 1079 _dhd_wlfc_flow_control_check(ctx, &entry->psq, DHD_PKTTAG_IF(PKTTAG(p))); 1080 return BCME_OK; 1081} 1082 1083static int 1084_dhd_wlfc_pretx_pktprocess(athost_wl_status_info_t* ctx, 1085 wlfc_mac_descriptor_t* entry, void* p, int header_needed, uint32* slot, int prec) 1086{ 1087 int rc = BCME_OK; 1088 int hslot = WLFC_HANGER_MAXITEMS; 1089 bool send_tim_update = FALSE; 1090 uint32 htod = 0; 1091 uint8 free_ctr; 1092 1093 *slot = hslot; 1094 1095 if (entry == NULL) { 1096 entry = _dhd_wlfc_find_table_entry(ctx, p); 1097 } 1098 1099 if (entry == NULL) { 1100 WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); 1101 return BCME_ERROR; 1102 } 1103 if (entry->send_tim_signal) { 1104 send_tim_update = TRUE; 1105 entry->send_tim_signal = 0; 1106 entry->traffic_lastreported_bmp = entry->traffic_pending_bmp; 1107 } 1108 if (header_needed) { 1109 hslot = dhd_wlfc_hanger_get_free_slot(ctx->hanger); 1110 free_ctr = WLFC_SEQCOUNT(entry, DHD_PKTTAG_FIFO(PKTTAG(p))); 1111 DHD_PKTTAG_SET_H2DTAG(PKTTAG(p), htod); 1112 WLFC_PKTFLAG_SET_GENERATION(htod, entry->generation); 1113 entry->transit_count++; 1114 } 1115 else { 1116 hslot = WLFC_PKTID_HSLOT_GET(DHD_PKTTAG_H2DTAG(PKTTAG(p))); 1117 free_ctr = WLFC_PKTID_FREERUNCTR_GET(DHD_PKTTAG_H2DTAG(PKTTAG(p))); 1118 } 1119 WLFC_PKTID_HSLOT_SET(htod, hslot); 1120 WLFC_PKTID_FREERUNCTR_SET(htod, free_ctr); 1121 DHD_PKTTAG_SETPKTDIR(PKTTAG(p), 1); 1122 WL_TXSTATUS_SET_FLAGS(htod, WLFC_PKTFLAG_PKTFROMHOST); 1123 WL_TXSTATUS_SET_FIFO(htod, DHD_PKTTAG_FIFO(PKTTAG(p))); 1124 1125 if (!DHD_PKTTAG_CREDITCHECK(PKTTAG(p))) { 1126 /* 1127 Indicate that this packet is being sent in response to an 1128 explicit request from the firmware side. 1129 */ 1130 WLFC_PKTFLAG_SET_PKTREQUESTED(htod); 1131 } 1132 else { 1133 WLFC_PKTFLAG_CLR_PKTREQUESTED(htod); 1134 } 1135 if (header_needed) { 1136 rc = _dhd_wlfc_pushheader(ctx, p, send_tim_update, 1137 entry->traffic_lastreported_bmp, entry->mac_handle, htod); 1138 if (rc == BCME_OK) { 1139 DHD_PKTTAG_SET_H2DTAG(PKTTAG(p), htod); 1140 /* 1141 a new header was created for this packet. 1142 push to hanger slot and scrub q. Since bus 1143 send succeeded, increment seq number as well. 1144 */ 1145 rc = dhd_wlfc_hanger_pushpkt(ctx->hanger, p, hslot); 1146 if (rc == BCME_OK) { 1147 /* increment free running sequence count */ 1148 WLFC_INCR_SEQCOUNT(entry, DHD_PKTTAG_FIFO(PKTTAG(p))); 1149#ifdef PROP_TXSTATUS_DEBUG 1150 ((wlfc_hanger_t*)(ctx->hanger))->items[hslot].push_time = 1151 OSL_SYSUPTIME(); 1152#endif 1153 } 1154 else { 1155 WLFC_DBGMESG(("%s() hanger_pushpkt() failed, rc: %d\n", 1156 __FUNCTION__, rc)); 1157 } 1158 } 1159 } 1160 else { 1161 int gen; 1162 1163 /* remove old header */ 1164 _dhd_wlfc_pullheader(ctx, p); 1165 1166 hslot = WLFC_PKTID_HSLOT_GET(DHD_PKTTAG_H2DTAG(PKTTAG(p))); 1167 dhd_wlfc_hanger_get_genbit(ctx->hanger, p, hslot, &gen); 1168 1169 WLFC_PKTFLAG_SET_GENERATION(htod, gen); 1170 free_ctr = WLFC_PKTID_FREERUNCTR_GET(DHD_PKTTAG_H2DTAG(PKTTAG(p))); 1171 /* push new header */ 1172 _dhd_wlfc_pushheader(ctx, p, send_tim_update, 1173 entry->traffic_lastreported_bmp, entry->mac_handle, htod); 1174 } 1175 *slot = hslot; 1176 return rc; 1177} 1178 1179static int 1180_dhd_wlfc_is_destination_closed(athost_wl_status_info_t* ctx, 1181 wlfc_mac_descriptor_t* entry, int prec) 1182{ 1183 if (ctx->destination_entries.interfaces[entry->interface_id].iftype == 1184 WLC_E_IF_ROLE_P2P_GO) { 1185 /* - destination interface is of type p2p GO. 1186 For a p2pGO interface, if the destination is OPEN but the interface is 1187 CLOSEd, do not send traffic. But if the dstn is CLOSEd while there is 1188 destination-specific-credit left send packets. This is because the 1189 firmware storing the destination-specific-requested packet in queue. 1190 */ 1191 if ((entry->state == WLFC_STATE_CLOSE) && (entry->requested_credit == 0) && 1192 (entry->requested_packet == 0)) 1193 return 1; 1194 } 1195 /* AP, p2p_go -> unicast desc entry, STA/p2p_cl -> interface desc. entry */ 1196 if (((entry->state == WLFC_STATE_CLOSE) && (entry->requested_credit == 0) && 1197 (entry->requested_packet == 0)) || 1198 (!(entry->ac_bitmap & (1 << prec)))) 1199 return 1; 1200 1201 return 0; 1202} 1203 1204static void* 1205_dhd_wlfc_deque_delayedq(athost_wl_status_info_t* ctx, 1206 int prec, uint8* ac_credit_spent, uint8* needs_hdr, wlfc_mac_descriptor_t** entry_out) 1207{ 1208 wlfc_mac_descriptor_t* entry; 1209 wlfc_mac_descriptor_t* table; 1210 uint8 token_pos; 1211 int total_entries; 1212 void* p = NULL; 1213 int pout; 1214 int i; 1215 1216 *entry_out = NULL; 1217 token_pos = ctx->token_pos[prec]; 1218 /* most cases a packet will count against FIFO credit */ 1219 *ac_credit_spent = 1; 1220 *needs_hdr = 1; 1221 1222 /* search all entries, include nodes as well as interfaces */ 1223 table = (wlfc_mac_descriptor_t*)&ctx->destination_entries; 1224 total_entries = sizeof(ctx->destination_entries)/sizeof(wlfc_mac_descriptor_t); 1225 1226 for (i = 0; i < total_entries; i++) { 1227 entry = &table[(token_pos + i) % total_entries]; 1228 if (entry->occupied) { 1229 if (!_dhd_wlfc_is_destination_closed(ctx, entry, prec)) { 1230 p = pktq_mdeq(&entry->psq, 1231 /* higher precedence will be picked up first, 1232 i.e. suppressed packets before delayed ones 1233 */ 1234 NBITVAL((prec << 1) + 1), 1235 &pout); 1236 *needs_hdr = 0; 1237 1238 if (p == NULL) { 1239 if (entry->suppressed == TRUE) { 1240 if ((entry->suppr_transit_count <= 1241 entry->suppress_count)) { 1242 entry->suppressed = FALSE; 1243 } else { 1244 return NULL; 1245 } 1246 } 1247 /* De-Q from delay Q */ 1248 p = pktq_mdeq(&entry->psq, 1249 NBITVAL((prec << 1)), 1250 &pout); 1251 *needs_hdr = 1; 1252 } 1253 1254 if (p != NULL) { 1255 /* did the packet come from suppress sub-queue? */ 1256 if (entry->requested_credit > 0) { 1257 entry->requested_credit--; 1258#ifdef PROP_TXSTATUS_DEBUG 1259 entry->dstncredit_sent_packets++; 1260#endif 1261 /* 1262 if the packet was pulled out while destination is in 1263 closed state but had a non-zero packets requested, 1264 then this should not count against the FIFO credit. 1265 That is due to the fact that the firmware will 1266 most likely hold onto this packet until a suitable 1267 time later to push it to the appropriate AC FIFO. 1268 */ 1269 if (entry->state == WLFC_STATE_CLOSE) 1270 *ac_credit_spent = 0; 1271 } 1272 else if (entry->requested_packet > 0) { 1273 entry->requested_packet--; 1274 DHD_PKTTAG_SETONETIMEPKTRQST(PKTTAG(p)); 1275 if (entry->state == WLFC_STATE_CLOSE) 1276 *ac_credit_spent = 0; 1277 } 1278 /* move token to ensure fair round-robin */ 1279 ctx->token_pos[prec] = 1280 (token_pos + i + 1) % total_entries; 1281 *entry_out = entry; 1282 _dhd_wlfc_flow_control_check(ctx, &entry->psq, 1283 DHD_PKTTAG_IF(PKTTAG(p))); 1284 /* 1285 A packet has been picked up, update traffic 1286 availability bitmap, if applicable 1287 */ 1288 _dhd_wlfc_traffic_pending_check(ctx, entry, prec); 1289 return p; 1290 } 1291 } 1292 } 1293 } 1294 return NULL; 1295} 1296 1297static void* 1298_dhd_wlfc_deque_sendq(athost_wl_status_info_t* ctx, int prec) 1299{ 1300 wlfc_mac_descriptor_t* entry; 1301 void* p; 1302 1303 1304 p = pktq_pdeq(&ctx->SENDQ, prec); 1305 if (p != NULL) { 1306 if (ETHER_ISMULTI(DHD_PKTTAG_DSTN(PKTTAG(p)))) 1307 /* bc/mc packets do not have a delay queue */ 1308 return p; 1309 1310 entry = _dhd_wlfc_find_table_entry(ctx, p); 1311 1312 if (entry == NULL) { 1313 WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); 1314 return p; 1315 } 1316 1317 while ((p != NULL)) { 1318 /* 1319 - suppressed packets go to sub_queue[2*prec + 1] AND 1320 - delayed packets go to sub_queue[2*prec + 0] to ensure 1321 order of delivery. 1322 */ 1323 if (WLFC_PKTQ_PENQ(&entry->psq, (prec << 1), p) == NULL) { 1324 WLFC_DBGMESG(("D")); 1325 /* dhd_txcomplete(ctx->dhdp, p, FALSE); */ 1326 PKTFREE(ctx->osh, p, TRUE); 1327 ctx->stats.delayq_full_error++; 1328 } 1329 /* 1330 A packet has been pushed, update traffic availability bitmap, 1331 if applicable 1332 */ 1333 _dhd_wlfc_traffic_pending_check(ctx, entry, prec); 1334 1335 p = pktq_pdeq(&ctx->SENDQ, prec); 1336 if (p == NULL) 1337 break; 1338 1339 entry = _dhd_wlfc_find_table_entry(ctx, p); 1340 1341 if ((entry == NULL) || (ETHER_ISMULTI(DHD_PKTTAG_DSTN(PKTTAG(p))))) { 1342 return p; 1343 } 1344 } 1345 } 1346 return p; 1347} 1348 1349static int 1350_dhd_wlfc_mac_entry_update(athost_wl_status_info_t* ctx, wlfc_mac_descriptor_t* entry, 1351 ewlfc_mac_entry_action_t action, uint8 ifid, uint8 iftype, uint8* ea) 1352{ 1353 int rc = BCME_OK; 1354 1355 if (action == eWLFC_MAC_ENTRY_ACTION_ADD) { 1356 entry->occupied = 1; 1357 entry->state = WLFC_STATE_OPEN; 1358 entry->requested_credit = 0; 1359 entry->interface_id = ifid; 1360 entry->iftype = iftype; 1361 entry->ac_bitmap = 0xff; /* update this when handling APSD */ 1362 /* for an interface entry we may not care about the MAC address */ 1363 if (ea != NULL) 1364 memcpy(&entry->ea[0], ea, ETHER_ADDR_LEN); 1365 pktq_init(&entry->psq, WLFC_PSQ_PREC_COUNT, WLFC_PSQ_LEN); 1366 } 1367 else if (action == eWLFC_MAC_ENTRY_ACTION_UPDATE) { 1368 entry->occupied = 1; 1369 entry->state = WLFC_STATE_OPEN; 1370 entry->requested_credit = 0; 1371 entry->interface_id = ifid; 1372 entry->iftype = iftype; 1373 entry->ac_bitmap = 0xff; /* update this when handling APSD */ 1374 /* for an interface entry we may not care about the MAC address */ 1375 if (ea != NULL) 1376 memcpy(&entry->ea[0], ea, ETHER_ADDR_LEN); 1377 } 1378 else if (action == eWLFC_MAC_ENTRY_ACTION_DEL) { 1379 entry->occupied = 0; 1380 entry->state = WLFC_STATE_CLOSE; 1381 entry->requested_credit = 0; 1382 /* enable after packets are queued-deqeued properly. 1383 pktq_flush(dhd->osh, &entry->psq, FALSE, NULL, 0); 1384 */ 1385 } 1386 return rc; 1387} 1388 1389int 1390_dhd_wlfc_borrow_credit(athost_wl_status_info_t* ctx, uint8 available_credit_map, int borrower_ac) 1391{ 1392 int lender_ac; 1393 int rc = BCME_ERROR; 1394 1395 if (ctx == NULL || available_credit_map == 0) { 1396 WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); 1397 return BCME_BADARG; 1398 } 1399 1400 /* Borrow from lowest priority available AC (including BC/MC credits) */ 1401 for (lender_ac = 0; lender_ac <= AC_COUNT; lender_ac++) { 1402 if ((available_credit_map && (1 << lender_ac)) && 1403 (ctx->FIFO_credit[lender_ac] > 0)) { 1404 ctx->credits_borrowed[borrower_ac][lender_ac]++; 1405 ctx->FIFO_credit[lender_ac]--; 1406 rc = BCME_OK; 1407 break; 1408 } 1409 } 1410 1411 return rc; 1412} 1413 1414int 1415dhd_wlfc_interface_entry_update(void* state, 1416 ewlfc_mac_entry_action_t action, uint8 ifid, uint8 iftype, uint8* ea) 1417{ 1418 athost_wl_status_info_t* ctx = (athost_wl_status_info_t*)state; 1419 wlfc_mac_descriptor_t* entry; 1420 1421 if (ifid >= WLFC_MAX_IFNUM) 1422 return BCME_BADARG; 1423 1424 entry = &ctx->destination_entries.interfaces[ifid]; 1425 return _dhd_wlfc_mac_entry_update(ctx, entry, action, ifid, iftype, ea); 1426} 1427 1428int 1429dhd_wlfc_FIFOcreditmap_update(void* state, uint8* credits) 1430{ 1431 athost_wl_status_info_t* ctx = (athost_wl_status_info_t*)state; 1432 1433 /* update the AC FIFO credit map */ 1434 ctx->FIFO_credit[0] = credits[0]; 1435 ctx->FIFO_credit[1] = credits[1]; 1436 ctx->FIFO_credit[2] = credits[2]; 1437 ctx->FIFO_credit[3] = credits[3]; 1438 /* credit for bc/mc packets */ 1439 ctx->FIFO_credit[4] = credits[4]; 1440 /* credit for ATIM FIFO is not used yet. */ 1441 ctx->FIFO_credit[5] = 0; 1442 return BCME_OK; 1443} 1444 1445int 1446dhd_wlfc_enque_sendq(void* state, int prec, void* p) 1447{ 1448 athost_wl_status_info_t* ctx = (athost_wl_status_info_t*)state; 1449 1450 if ((state == NULL) || 1451 /* prec = AC_COUNT is used for bc/mc queue */ 1452 (prec > AC_COUNT) || 1453 (p == NULL)) { 1454 WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); 1455 return BCME_BADARG; 1456 } 1457 if (FALSE == dhd_prec_enq(ctx->dhdp, &ctx->SENDQ, p, prec)) { 1458 ctx->stats.sendq_full_error++; 1459 /* 1460 WLFC_DBGMESG(("Error: %s():%d, qlen:%d\n", 1461 __FUNCTION__, __LINE__, ctx->SENDQ.len)); 1462 */ 1463 WLFC_HOST_FIFO_DROPPEDCTR_INC(ctx, prec); 1464 WLFC_DBGMESG(("Q")); 1465 PKTFREE(ctx->osh, p, TRUE); 1466 return BCME_ERROR; 1467 } 1468 ctx->stats.pktin++; 1469 /* _dhd_wlfc_flow_control_check(ctx, &ctx->SENDQ, DHD_PKTTAG_IF(PKTTAG(p))); */ 1470 return BCME_OK; 1471} 1472 1473int 1474_dhd_wlfc_handle_packet_commit(athost_wl_status_info_t* ctx, int ac, 1475 dhd_wlfc_commit_info_t *commit_info, f_commitpkt_t fcommit, void* commit_ctx) 1476{ 1477 uint32 hslot; 1478 int rc; 1479 1480 /* 1481 if ac_fifo_credit_spent = 0 1482 1483 This packet will not count against the FIFO credit. 1484 To ensure the txstatus corresponding to this packet 1485 does not provide an implied credit (default behavior) 1486 mark the packet accordingly. 1487 1488 if ac_fifo_credit_spent = 1 1489 1490 This is a normal packet and it counts against the FIFO 1491 credit count. 1492 */ 1493 DHD_PKTTAG_SETCREDITCHECK(PKTTAG(commit_info->p), commit_info->ac_fifo_credit_spent); 1494 rc = _dhd_wlfc_pretx_pktprocess(ctx, commit_info->mac_entry, commit_info->p, 1495 commit_info->needs_hdr, &hslot, ac); 1496 1497 if (rc == BCME_OK) 1498 rc = fcommit(commit_ctx, commit_info->p); 1499 else 1500 ctx->stats.generic_error++; 1501 1502 if (rc == BCME_OK) { 1503 ctx->stats.pkt2bus++; 1504 if (commit_info->ac_fifo_credit_spent) { 1505 ctx->stats.sendq_pkts[ac]++; 1506 WLFC_HOST_FIFO_CREDIT_INC_SENTCTRS(ctx, ac); 1507 } 1508 } 1509 else { 1510 /* 1511 bus commit has failed, rollback. 1512 - remove wl-header for a delayed packet 1513 - save wl-header header for suppressed packets 1514 */ 1515 rc = _dhd_wlfc_rollback_packet_toq(ctx, commit_info->p, 1516 (commit_info->pkt_type), hslot); 1517 if (rc != BCME_OK) 1518 ctx->stats.rollback_failed++; 1519 1520 rc = BCME_ERROR; 1521 } 1522 1523 return rc; 1524} 1525 1526int 1527dhd_wlfc_commit_packets(void* state, f_commitpkt_t fcommit, void* commit_ctx) 1528{ 1529 int ac; 1530 int credit; 1531 int rc; 1532 dhd_wlfc_commit_info_t commit_info; 1533 athost_wl_status_info_t* ctx = (athost_wl_status_info_t*)state; 1534 int credit_count = 0; 1535 int bus_retry_count = 0; 1536 uint8 ac_available = 0; /* Bitmask for 4 ACs + BC/MC */ 1537 1538 if ((state == NULL) || 1539 (fcommit == NULL)) { 1540 WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); 1541 return BCME_BADARG; 1542 } 1543 1544 memset(&commit_info, 0, sizeof(commit_info)); 1545 1546 /* 1547 Commit packets for regular AC traffic. Higher priority first. 1548 First, use up FIFO credits available to each AC. Based on distribution 1549 and credits left, borrow from other ACs as applicable 1550 1551 -NOTE: 1552 If the bus between the host and firmware is overwhelmed by the 1553 traffic from host, it is possible that higher priority traffic 1554 starves the lower priority queue. If that occurs often, we may 1555 have to employ weighted round-robin or ucode scheme to avoid 1556 low priority packet starvation. 1557 */ 1558 1559 for (ac = AC_COUNT; ac >= 0; ac--) { 1560 1561 int initial_credit_count = ctx->FIFO_credit[ac]; 1562 1563 /* packets from SENDQ are fresh and they'd need header and have no MAC entry */ 1564 commit_info.needs_hdr = 1; 1565 commit_info.mac_entry = NULL; 1566 commit_info.pkt_type = eWLFC_PKTTYPE_NEW; 1567 1568 do { 1569 commit_info.p = _dhd_wlfc_deque_sendq(ctx, ac); 1570 if (commit_info.p == NULL) 1571 break; 1572 else if (ETHER_ISMULTI(DHD_PKTTAG_DSTN(PKTTAG(commit_info.p)))) { 1573 ASSERT(ac == AC_COUNT); 1574 1575 if (ctx->FIFO_credit[ac]) { 1576 rc = _dhd_wlfc_handle_packet_commit(ctx, ac, &commit_info, 1577 fcommit, commit_ctx); 1578 1579 /* Bus commits may fail (e.g. flow control); abort after retries */ 1580 if (rc == BCME_OK) { 1581 if (commit_info.ac_fifo_credit_spent) { 1582 (void) _dhd_wlfc_borrow_credit(ctx, 1583 ac_available, ac); 1584 credit_count--; 1585 } 1586 } else { 1587 bus_retry_count++; 1588 if (bus_retry_count >= BUS_RETRIES) { 1589 DHD_ERROR((" %s: bus error\n", 1590 __FUNCTION__)); 1591 return rc; 1592 } 1593 } 1594 } 1595 } 1596 1597 } while (commit_info.p); 1598 1599 for (credit = 0; credit < ctx->FIFO_credit[ac];) { 1600 commit_info.p = _dhd_wlfc_deque_delayedq(ctx, ac, 1601 &(commit_info.ac_fifo_credit_spent), 1602 &(commit_info.needs_hdr), 1603 &(commit_info.mac_entry)); 1604 1605 if (commit_info.p == NULL) 1606 break; 1607 1608 commit_info.pkt_type = (commit_info.needs_hdr) ? eWLFC_PKTTYPE_DELAYED : 1609 eWLFC_PKTTYPE_SUPPRESSED; 1610 1611 rc = _dhd_wlfc_handle_packet_commit(ctx, ac, &commit_info, 1612 fcommit, commit_ctx); 1613 1614 /* Bus commits may fail (e.g. flow control); abort after retries */ 1615 if (rc == BCME_OK) { 1616 if (commit_info.ac_fifo_credit_spent) { 1617 credit++; 1618 } 1619 } 1620 else { 1621 bus_retry_count++; 1622 if (bus_retry_count >= BUS_RETRIES) { 1623 DHD_ERROR(("dhd_wlfc_commit_packets(): bus error\n")); 1624 ctx->FIFO_credit[ac] -= credit; 1625 return rc; 1626 } 1627 } 1628 } 1629 1630 ctx->FIFO_credit[ac] -= credit; 1631 1632 1633 /* If no credits were used, the queue is idle and can be re-used 1634 Note that resv credits cannot be borrowed 1635 */ 1636 if (initial_credit_count == ctx->FIFO_credit[ac]) { 1637 ac_available |= (1 << ac); 1638 credit_count += ctx->FIFO_credit[ac]; 1639 } 1640 } 1641 1642 /* We borrow only for AC_BE and only if no other traffic seen for DEFER_PERIOD 1643 1644 Note that (ac_available & WLFC_AC_BE_TRAFFIC_ONLY) is done to: 1645 a) ignore BC/MC for deferring borrow 1646 b) ignore AC_BE being available along with other ACs 1647 (this should happen only for pure BC/MC traffic) 1648 1649 i.e. AC_VI, AC_VO, AC_BK all MUST be available (i.e. no traffic) and 1650 we do not care if AC_BE and BC/MC are available or not 1651 */ 1652 if ((ac_available & WLFC_AC_BE_TRAFFIC_ONLY) == WLFC_AC_BE_TRAFFIC_ONLY) { 1653 1654 if (ctx->allow_credit_borrow) { 1655 ac = 1; /* Set ac to AC_BE and borrow credits */ 1656 } 1657 else { 1658 int delta; 1659 int curr_t = OSL_SYSUPTIME(); 1660 1661 if (curr_t > ctx->borrow_defer_timestamp) 1662 delta = curr_t - ctx->borrow_defer_timestamp; 1663 else 1664 delta = 0xffffffff + curr_t - ctx->borrow_defer_timestamp; 1665 1666 if (delta >= WLFC_BORROW_DEFER_PERIOD_MS) { 1667 /* Reset borrow but defer to next iteration (defensive borrowing) */ 1668 ctx->allow_credit_borrow = TRUE; 1669 ctx->borrow_defer_timestamp = 0; 1670 } 1671 return BCME_OK; 1672 } 1673 } 1674 else { 1675 /* If we have multiple AC traffic, turn off borrowing, mark time and bail out */ 1676 ctx->allow_credit_borrow = FALSE; 1677 ctx->borrow_defer_timestamp = OSL_SYSUPTIME(); 1678 return BCME_OK; 1679 } 1680 1681 /* At this point, borrow all credits only for "ac" (which should be set above to AC_BE) 1682 Generically use "ac" only in case we extend to all ACs in future 1683 */ 1684 for (; (credit_count > 0);) { 1685 1686 commit_info.p = _dhd_wlfc_deque_delayedq(ctx, ac, 1687 &(commit_info.ac_fifo_credit_spent), 1688 &(commit_info.needs_hdr), 1689 &(commit_info.mac_entry)); 1690 if (commit_info.p == NULL) 1691 break; 1692 1693 commit_info.pkt_type = (commit_info.needs_hdr) ? eWLFC_PKTTYPE_DELAYED : 1694 eWLFC_PKTTYPE_SUPPRESSED; 1695 1696 rc = _dhd_wlfc_handle_packet_commit(ctx, ac, &commit_info, 1697 fcommit, commit_ctx); 1698 1699 /* Bus commits may fail (e.g. flow control); abort after retries */ 1700 if (rc == BCME_OK) { 1701 if (commit_info.ac_fifo_credit_spent) { 1702 (void) _dhd_wlfc_borrow_credit(ctx, ac_available, ac); 1703 credit_count--; 1704 } 1705 } 1706 else { 1707 bus_retry_count++; 1708 if (bus_retry_count >= BUS_RETRIES) { 1709 DHD_ERROR(("dhd_wlfc_commit_packets(): bus error\n")); 1710 return rc; 1711 } 1712 } 1713 } 1714 1715 return BCME_OK; 1716} 1717 1718static uint8 1719dhd_wlfc_find_mac_desc_id_from_mac(dhd_pub_t *dhdp, uint8* ea) 1720{ 1721 wlfc_mac_descriptor_t* table = 1722 ((athost_wl_status_info_t*)dhdp->wlfc_state)->destination_entries.nodes; 1723 uint8 table_index; 1724 1725 if (ea != NULL) { 1726 for (table_index = 0; table_index < WLFC_MAC_DESC_TABLE_SIZE; table_index++) { 1727 if ((memcmp(ea, &table[table_index].ea[0], ETHER_ADDR_LEN) == 0) && 1728 table[table_index].occupied) 1729 return table_index; 1730 } 1731 } 1732 return WLFC_MAC_DESC_ID_INVALID; 1733} 1734 1735void 1736dhd_wlfc_txcomplete(dhd_pub_t *dhd, void *txp, bool success) 1737{ 1738 athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*) 1739 dhd->wlfc_state; 1740 void* p; 1741 int fifo_id; 1742 1743 if (DHD_PKTTAG_SIGNALONLY(PKTTAG(txp))) { 1744#ifdef PROP_TXSTATUS_DEBUG 1745 wlfc->stats.signal_only_pkts_freed++; 1746#endif 1747 /* is this a signal-only packet? */ 1748 PKTFREE(wlfc->osh, txp, TRUE); 1749 return; 1750 } 1751 if (!success) { 1752 WLFC_DBGMESG(("At: %s():%d, bus_complete() failure for %p, htod_tag:0x%08x\n", 1753 __FUNCTION__, __LINE__, txp, DHD_PKTTAG_H2DTAG(PKTTAG(txp)))); 1754 dhd_wlfc_hanger_poppkt(wlfc->hanger, WLFC_PKTID_HSLOT_GET(DHD_PKTTAG_H2DTAG 1755 (PKTTAG(txp))), &p, 1); 1756 1757 /* indicate failure and free the packet */ 1758 dhd_txcomplete(dhd, txp, FALSE); 1759 1760 /* return the credit, if necessary */ 1761 if (DHD_PKTTAG_CREDITCHECK(PKTTAG(txp))) { 1762 int lender, credit_returned = 0; /* Note that borrower is fifo_id */ 1763 1764 fifo_id = DHD_PKTTAG_FIFO(PKTTAG(txp)); 1765 1766 /* Return credits to highest priority lender first */ 1767 for (lender = AC_COUNT; lender >= 0; lender--) { 1768 if (wlfc->credits_borrowed[fifo_id][lender] > 0) { 1769 wlfc->FIFO_credit[lender]++; 1770 wlfc->credits_borrowed[fifo_id][lender]--; 1771 credit_returned = 1; 1772 break; 1773 } 1774 } 1775 1776 if (!credit_returned) { 1777 wlfc->FIFO_credit[fifo_id]++; 1778 } 1779 } 1780 1781 PKTFREE(wlfc->osh, txp, TRUE); 1782 } 1783 return; 1784} 1785 1786/* Handle discard or suppress indication */ 1787static int 1788dhd_wlfc_txstatus_update(dhd_pub_t *dhd, uint8* pkt_info) 1789{ 1790 uint8 status_flag; 1791 uint32 status; 1792 int ret; 1793 int remove_from_hanger = 1; 1794 void* pktbuf; 1795 uint8 fifo_id; 1796 wlfc_mac_descriptor_t* entry = NULL; 1797 athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*) 1798 dhd->wlfc_state; 1799 1800 memcpy(&status, pkt_info, sizeof(uint32)); 1801 status_flag = WL_TXSTATUS_GET_FLAGS(status); 1802 wlfc->stats.txstatus_in++; 1803 1804 if (status_flag == WLFC_CTL_PKTFLAG_DISCARD) { 1805 wlfc->stats.pkt_freed++; 1806 } 1807 1808 else if (status_flag == WLFC_CTL_PKTFLAG_D11SUPPRESS) { 1809 wlfc->stats.d11_suppress++; 1810 remove_from_hanger = 0; 1811 } 1812 1813 else if (status_flag == WLFC_CTL_PKTFLAG_WLSUPPRESS) { 1814 wlfc->stats.wl_suppress++; 1815 remove_from_hanger = 0; 1816 } 1817 1818 else if (status_flag == WLFC_CTL_PKTFLAG_TOSSED_BYWLC) { 1819 wlfc->stats.wlc_tossed_pkts++; 1820 } 1821 1822 ret = dhd_wlfc_hanger_poppkt(wlfc->hanger, 1823 WLFC_PKTID_HSLOT_GET(status), &pktbuf, remove_from_hanger); 1824 if (ret != BCME_OK) { 1825 /* do something */ 1826 return ret; 1827 } 1828 1829 entry = _dhd_wlfc_find_table_entry(wlfc, pktbuf); 1830 1831 if (!remove_from_hanger) { 1832 /* this packet was suppressed */ 1833 if (!entry->suppressed || 1834 entry->generation != WLFC_PKTID_GEN(status)) { 1835 entry->suppressed = TRUE; 1836 entry->suppress_count = pktq_mlen(&entry->psq, 1837 NBITVAL((WL_TXSTATUS_GET_FIFO(status) << 1) + 1)); 1838 entry->suppr_transit_count = entry->transit_count; 1839 1840 } 1841 1842 entry->generation = WLFC_PKTID_GEN(status); 1843 } 1844 1845#ifdef PROP_TXSTATUS_DEBUG 1846 { 1847 uint32 new_t = OSL_SYSUPTIME(); 1848 uint32 old_t; 1849 uint32 delta; 1850 old_t = ((wlfc_hanger_t*)(wlfc->hanger))->items[ 1851 WLFC_PKTID_HSLOT_GET(status)].push_time; 1852 1853 1854 wlfc->stats.latency_sample_count++; 1855 if (new_t > old_t) 1856 delta = new_t - old_t; 1857 else 1858 delta = 0xffffffff + new_t - old_t; 1859 wlfc->stats.total_status_latency += delta; 1860 wlfc->stats.latency_most_recent = delta; 1861 1862 wlfc->stats.deltas[wlfc->stats.idx_delta++] = delta; 1863 if (wlfc->stats.idx_delta == sizeof(wlfc->stats.deltas)/sizeof(uint32)) 1864 wlfc->stats.idx_delta = 0; 1865 } 1866#endif /* PROP_TXSTATUS_DEBUG */ 1867 1868 fifo_id = DHD_PKTTAG_FIFO(PKTTAG(pktbuf)); 1869 1870 /* pick up the implicit credit from this packet */ 1871 if (DHD_PKTTAG_CREDITCHECK(PKTTAG(pktbuf))) { 1872 if (wlfc->proptxstatus_mode == WLFC_FCMODE_IMPLIED_CREDIT) { 1873 1874 int lender, credit_returned = 0; /* Note that borrower is fifo_id */ 1875 1876 /* Return credits to highest priority lender first */ 1877 for (lender = AC_COUNT; lender >= 0; lender--) { 1878 if (wlfc->credits_borrowed[fifo_id][lender] > 0) { 1879 wlfc->FIFO_credit[lender]++; 1880 wlfc->credits_borrowed[fifo_id][lender]--; 1881 credit_returned = 1; 1882 break; 1883 } 1884 } 1885 1886 if (!credit_returned) { 1887 wlfc->FIFO_credit[fifo_id]++; 1888 } 1889 } 1890 } 1891 else { 1892 /* 1893 if this packet did not count against FIFO credit, it must have 1894 taken a requested_credit from the destination entry (for pspoll etc.) 1895 */ 1896 if (!entry) { 1897 1898 entry = _dhd_wlfc_find_table_entry(wlfc, pktbuf); 1899 } 1900 if (!DHD_PKTTAG_ONETIMEPKTRQST(PKTTAG(pktbuf))) 1901 entry->requested_credit++; 1902#ifdef PROP_TXSTATUS_DEBUG 1903 entry->dstncredit_acks++; 1904#endif 1905 } 1906 if ((status_flag == WLFC_CTL_PKTFLAG_D11SUPPRESS) || 1907 (status_flag == WLFC_CTL_PKTFLAG_WLSUPPRESS)) { 1908 ret = _dhd_wlfc_enque_suppressed(wlfc, fifo_id, pktbuf, WLFC_PKTID_GEN(status)); 1909 if (ret != BCME_OK) { 1910 /* delay q is full, drop this packet */ 1911 dhd_wlfc_hanger_poppkt(wlfc->hanger, WLFC_PKTID_HSLOT_GET(status), 1912 &pktbuf, 1); 1913 1914 /* indicate failure and free the packet */ 1915 dhd_txcomplete(dhd, pktbuf, FALSE); 1916 entry->transit_count--; 1917 /* This packet is transmitted Successfully by 1918 * dongle even after first suppress. 1919 */ 1920 if (entry->suppressed) { 1921 entry->suppr_transit_count--; 1922 } 1923 PKTFREE(wlfc->osh, pktbuf, TRUE); 1924 } else { 1925 /* Mark suppressed to avoid a double free during wlfc cleanup */ 1926 dhd_wlfc_hanger_mark_suppressed(wlfc->hanger, 1927 WLFC_PKTID_HSLOT_GET(status), WLFC_PKTID_GEN(status)); 1928 entry->suppress_count++; 1929 } 1930 } 1931 else { 1932 dhd_txcomplete(dhd, pktbuf, TRUE); 1933 entry->transit_count--; 1934 1935 /* This packet is transmitted Successfully by dongle even after first suppress. */ 1936 if (entry->suppressed) { 1937 entry->suppr_transit_count--; 1938 } 1939 /* free the packet */ 1940 PKTFREE(wlfc->osh, pktbuf, TRUE); 1941 } 1942 return BCME_OK; 1943} 1944 1945static int 1946dhd_wlfc_fifocreditback_indicate(dhd_pub_t *dhd, uint8* credits) 1947{ 1948 int i; 1949 athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*) 1950 dhd->wlfc_state; 1951 for (i = 0; i < WLFC_CTL_VALUE_LEN_FIFO_CREDITBACK; i++) { 1952#ifdef PROP_TXSTATUS_DEBUG 1953 wlfc->stats.fifo_credits_back[i] += credits[i]; 1954#endif 1955 /* update FIFO credits */ 1956 if (wlfc->proptxstatus_mode == WLFC_FCMODE_EXPLICIT_CREDIT) 1957 { 1958 int lender; /* Note that borrower is i */ 1959 1960 /* Return credits to highest priority lender first */ 1961 for (lender = AC_COUNT; (lender >= 0) && (credits[i] > 0); lender--) { 1962 if (wlfc->credits_borrowed[i][lender] > 0) { 1963 if (credits[i] >= wlfc->credits_borrowed[i][lender]) { 1964 credits[i] -= wlfc->credits_borrowed[i][lender]; 1965 wlfc->FIFO_credit[lender] += 1966 wlfc->credits_borrowed[i][lender]; 1967 wlfc->credits_borrowed[i][lender] = 0; 1968 } 1969 else { 1970 wlfc->credits_borrowed[i][lender] -= credits[i]; 1971 wlfc->FIFO_credit[lender] += credits[i]; 1972 credits[i] = 0; 1973 } 1974 } 1975 } 1976 1977 /* If we have more credits left over, these must belong to the AC */ 1978 if (credits[i] > 0) { 1979 wlfc->FIFO_credit[i] += credits[i]; 1980 } 1981 } 1982 } 1983 1984 return BCME_OK; 1985} 1986 1987static int 1988dhd_wlfc_rssi_indicate(dhd_pub_t *dhd, uint8* rssi) 1989{ 1990 (void)dhd; 1991 (void)rssi; 1992 return BCME_OK; 1993} 1994 1995static int 1996dhd_wlfc_mac_table_update(dhd_pub_t *dhd, uint8* value, uint8 type) 1997{ 1998 int rc; 1999 athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*) 2000 dhd->wlfc_state; 2001 wlfc_mac_descriptor_t* table; 2002 uint8 existing_index; 2003 uint8 table_index; 2004 uint8 ifid; 2005 uint8* ea; 2006 2007 WLFC_DBGMESG(("%s(), mac [%02x:%02x:%02x:%02x:%02x:%02x],%s,idx:%d,id:0x%02x\n", 2008 __FUNCTION__, value[2], value[3], value[4], value[5], value[6], value[7], 2009 ((type == WLFC_CTL_TYPE_MACDESC_ADD) ? "ADD":"DEL"), 2010 WLFC_MAC_DESC_GET_LOOKUP_INDEX(value[0]), value[0])); 2011 2012 table = wlfc->destination_entries.nodes; 2013 table_index = WLFC_MAC_DESC_GET_LOOKUP_INDEX(value[0]); 2014 ifid = value[1]; 2015 ea = &value[2]; 2016 2017 if (type == WLFC_CTL_TYPE_MACDESC_ADD) { 2018 existing_index = dhd_wlfc_find_mac_desc_id_from_mac(dhd, &value[2]); 2019 if (existing_index == WLFC_MAC_DESC_ID_INVALID) { 2020 /* this MAC entry does not exist, create one */ 2021 if (!table[table_index].occupied) { 2022 table[table_index].mac_handle = value[0]; 2023 rc = _dhd_wlfc_mac_entry_update(wlfc, &table[table_index], 2024 eWLFC_MAC_ENTRY_ACTION_ADD, ifid, 2025 wlfc->destination_entries.interfaces[ifid].iftype, 2026 ea); 2027 } 2028 else { 2029 /* the space should have been empty, but it's not */ 2030 wlfc->stats.mac_update_failed++; 2031 } 2032 } 2033 else { 2034 /* 2035 there is an existing entry, move it to new index 2036 if necessary. 2037 */ 2038 if (existing_index != table_index) { 2039 /* if we already have an entry, free the old one */ 2040 table[existing_index].occupied = 0; 2041 table[existing_index].state = WLFC_STATE_CLOSE; 2042 table[existing_index].requested_credit = 0; 2043 table[existing_index].interface_id = 0; 2044 /* enable after packets are queued-deqeued properly. 2045 pktq_flush(dhd->osh, &table[existing_index].psq, FALSE, NULL, 0); 2046 */ 2047 } 2048 } 2049 } 2050 if (type == WLFC_CTL_TYPE_MACDESC_DEL) { 2051 if (table[table_index].occupied) { 2052 rc = _dhd_wlfc_mac_entry_update(wlfc, &table[table_index], 2053 eWLFC_MAC_ENTRY_ACTION_DEL, ifid, 2054 wlfc->destination_entries.interfaces[ifid].iftype, 2055 ea); 2056 } 2057 else { 2058 /* the space should have been occupied, but it's not */ 2059 wlfc->stats.mac_update_failed++; 2060 } 2061 } 2062 BCM_REFERENCE(rc); 2063 return BCME_OK; 2064} 2065 2066static int 2067dhd_wlfc_psmode_update(dhd_pub_t *dhd, uint8* value, uint8 type) 2068{ 2069 /* Handle PS on/off indication */ 2070 athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*) 2071 dhd->wlfc_state; 2072 wlfc_mac_descriptor_t* table; 2073 wlfc_mac_descriptor_t* desc; 2074 uint8 mac_handle = value[0]; 2075 int i; 2076 2077 table = wlfc->destination_entries.nodes; 2078 desc = &table[WLFC_MAC_DESC_GET_LOOKUP_INDEX(mac_handle)]; 2079 if (desc->occupied) { 2080 /* a fresh PS mode should wipe old ps credits? */ 2081 desc->requested_credit = 0; 2082 if (type == WLFC_CTL_TYPE_MAC_OPEN) { 2083 desc->state = WLFC_STATE_OPEN; 2084 DHD_WLFC_CTRINC_MAC_OPEN(desc); 2085 } 2086 else { 2087 desc->state = WLFC_STATE_CLOSE; 2088 DHD_WLFC_CTRINC_MAC_CLOSE(desc); 2089 /* 2090 Indicate to firmware if there is any traffic pending. 2091 */ 2092 for (i = AC_BE; i < AC_COUNT; i++) { 2093 _dhd_wlfc_traffic_pending_check(wlfc, desc, i); 2094 } 2095 } 2096 } 2097 else { 2098 wlfc->stats.psmode_update_failed++; 2099 } 2100 return BCME_OK; 2101} 2102 2103static int 2104dhd_wlfc_interface_update(dhd_pub_t *dhd, uint8* value, uint8 type) 2105{ 2106 /* Handle PS on/off indication */ 2107 athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*) 2108 dhd->wlfc_state; 2109 wlfc_mac_descriptor_t* table; 2110 uint8 if_id = value[0]; 2111 2112 if (if_id < WLFC_MAX_IFNUM) { 2113 table = wlfc->destination_entries.interfaces; 2114 if (table[if_id].occupied) { 2115 if (type == WLFC_CTL_TYPE_INTERFACE_OPEN) { 2116 table[if_id].state = WLFC_STATE_OPEN; 2117 /* WLFC_DBGMESG(("INTERFACE[%d] OPEN\n", if_id)); */ 2118 } 2119 else { 2120 table[if_id].state = WLFC_STATE_CLOSE; 2121 /* WLFC_DBGMESG(("INTERFACE[%d] CLOSE\n", if_id)); */ 2122 } 2123 return BCME_OK; 2124 } 2125 } 2126 wlfc->stats.interface_update_failed++; 2127 2128 return BCME_OK; 2129} 2130 2131static int 2132dhd_wlfc_credit_request(dhd_pub_t *dhd, uint8* value) 2133{ 2134 athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*) 2135 dhd->wlfc_state; 2136 wlfc_mac_descriptor_t* table; 2137 wlfc_mac_descriptor_t* desc; 2138 uint8 mac_handle; 2139 uint8 credit; 2140 2141 table = wlfc->destination_entries.nodes; 2142 mac_handle = value[1]; 2143 credit = value[0]; 2144 2145 desc = &table[WLFC_MAC_DESC_GET_LOOKUP_INDEX(mac_handle)]; 2146 if (desc->occupied) { 2147 desc->requested_credit = credit; 2148 2149 desc->ac_bitmap = value[2]; 2150 } 2151 else { 2152 wlfc->stats.credit_request_failed++; 2153 } 2154 return BCME_OK; 2155} 2156 2157static int 2158dhd_wlfc_packet_request(dhd_pub_t *dhd, uint8* value) 2159{ 2160 athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*) 2161 dhd->wlfc_state; 2162 wlfc_mac_descriptor_t* table; 2163 wlfc_mac_descriptor_t* desc; 2164 uint8 mac_handle; 2165 uint8 packet_count; 2166 2167 table = wlfc->destination_entries.nodes; 2168 mac_handle = value[1]; 2169 packet_count = value[0]; 2170 2171 desc = &table[WLFC_MAC_DESC_GET_LOOKUP_INDEX(mac_handle)]; 2172 if (desc->occupied) { 2173 desc->requested_packet = packet_count; 2174 2175 desc->ac_bitmap = value[2]; 2176 } 2177 else { 2178 wlfc->stats.packet_request_failed++; 2179 } 2180 return BCME_OK; 2181} 2182 2183static void 2184dhd_wlfc_reorderinfo_indicate(uint8 *val, uint8 len, uchar *info_buf, uint *info_len) 2185{ 2186 if (info_len) { 2187 if (info_buf) { 2188 bcopy(val, info_buf, len); 2189 *info_len = len; 2190 } 2191 else 2192 *info_len = 0; 2193 } 2194} 2195 2196static int 2197dhd_wlfc_parse_header_info(dhd_pub_t *dhd, void* pktbuf, int tlv_hdr_len, uchar *reorder_info_buf, 2198 uint *reorder_info_len) 2199{ 2200 uint8 type, len; 2201 uint8* value; 2202 uint8* tmpbuf; 2203 uint16 remainder = tlv_hdr_len; 2204 uint16 processed = 0; 2205 athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*) 2206 dhd->wlfc_state; 2207 tmpbuf = (uint8*)PKTDATA(dhd->osh, pktbuf); 2208 if (remainder) { 2209 while ((processed < (WLFC_MAX_PENDING_DATALEN * 2)) && (remainder > 0)) { 2210 type = tmpbuf[processed]; 2211 if (type == WLFC_CTL_TYPE_FILLER) { 2212 remainder -= 1; 2213 processed += 1; 2214 continue; 2215 } 2216 2217 len = tmpbuf[processed + 1]; 2218 value = &tmpbuf[processed + 2]; 2219 2220 if (remainder < (2 + len)) 2221 break; 2222 2223 remainder -= 2 + len; 2224 processed += 2 + len; 2225 if (type == WLFC_CTL_TYPE_TXSTATUS) 2226 dhd_wlfc_txstatus_update(dhd, value); 2227 2228 else if (type == WLFC_CTL_TYPE_HOST_REORDER_RXPKTS) 2229 dhd_wlfc_reorderinfo_indicate(value, len, reorder_info_buf, 2230 reorder_info_len); 2231 else if (type == WLFC_CTL_TYPE_FIFO_CREDITBACK) 2232 dhd_wlfc_fifocreditback_indicate(dhd, value); 2233 2234 else if (type == WLFC_CTL_TYPE_RSSI) 2235 dhd_wlfc_rssi_indicate(dhd, value); 2236 2237 else if (type == WLFC_CTL_TYPE_MAC_REQUEST_CREDIT) 2238 dhd_wlfc_credit_request(dhd, value); 2239 2240 else if (type == WLFC_CTL_TYPE_MAC_REQUEST_PACKET) 2241 dhd_wlfc_packet_request(dhd, value); 2242 2243 else if ((type == WLFC_CTL_TYPE_MAC_OPEN) || 2244 (type == WLFC_CTL_TYPE_MAC_CLOSE)) 2245 dhd_wlfc_psmode_update(dhd, value, type); 2246 2247 else if ((type == WLFC_CTL_TYPE_MACDESC_ADD) || 2248 (type == WLFC_CTL_TYPE_MACDESC_DEL)) 2249 dhd_wlfc_mac_table_update(dhd, value, type); 2250 2251 else if ((type == WLFC_CTL_TYPE_INTERFACE_OPEN) || 2252 (type == WLFC_CTL_TYPE_INTERFACE_CLOSE)) { 2253 dhd_wlfc_interface_update(dhd, value, type); 2254 } 2255 } 2256 if (remainder != 0) { 2257 /* trouble..., something is not right */ 2258 wlfc->stats.tlv_parse_failed++; 2259 } 2260 } 2261 return BCME_OK; 2262} 2263 2264int 2265dhd_wlfc_init(dhd_pub_t *dhd) 2266{ 2267 char iovbuf[12]; /* Room for "tlv" + '\0' + parameter */ 2268 /* enable all signals & indicate host proptxstatus logic is active */ 2269 uint32 tlv = dhd->wlfc_enabled? 2270 WLFC_FLAGS_RSSI_SIGNALS | 2271 WLFC_FLAGS_XONXOFF_SIGNALS | 2272 WLFC_FLAGS_CREDIT_STATUS_SIGNALS | 2273 WLFC_FLAGS_HOST_PROPTXSTATUS_ACTIVE | 2274 WLFC_FLAGS_HOST_RXRERODER_ACTIVE : 0; 2275 /* WLFC_FLAGS_HOST_PROPTXSTATUS_ACTIVE | WLFC_FLAGS_HOST_RXRERODER_ACTIVE : 0; */ 2276 2277 2278 /* 2279 try to enable/disable signaling by sending "tlv" iovar. if that fails, 2280 fallback to no flow control? Print a message for now. 2281 */ 2282 2283 /* enable proptxtstatus signaling by default */ 2284 bcm_mkiovar("tlv", (char *)&tlv, 4, iovbuf, sizeof(iovbuf)); 2285 if (dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0) < 0) { 2286 DHD_ERROR(("dhd_wlfc_init(): failed to enable/disable bdcv2 tlv signaling\n")); 2287 } 2288 else { 2289 /* 2290 Leaving the message for now, it should be removed after a while; once 2291 the tlv situation is stable. 2292 */ 2293 DHD_ERROR(("dhd_wlfc_init(): successfully %s bdcv2 tlv signaling, %d\n", 2294 dhd->wlfc_enabled?"enabled":"disabled", tlv)); 2295 } 2296 return BCME_OK; 2297} 2298 2299int 2300dhd_wlfc_enable(dhd_pub_t *dhd) 2301{ 2302 int i; 2303 athost_wl_status_info_t* wlfc; 2304 2305 if (!dhd->wlfc_enabled || dhd->wlfc_state) 2306 return BCME_OK; 2307 2308 /* allocate space to track txstatus propagated from firmware */ 2309 dhd->wlfc_state = MALLOC(dhd->osh, sizeof(athost_wl_status_info_t)); 2310 if (dhd->wlfc_state == NULL) 2311 return BCME_NOMEM; 2312 2313 /* initialize state space */ 2314 wlfc = (athost_wl_status_info_t*)dhd->wlfc_state; 2315 memset(wlfc, 0, sizeof(athost_wl_status_info_t)); 2316 2317 /* remember osh & dhdp */ 2318 wlfc->osh = dhd->osh; 2319 wlfc->dhdp = dhd; 2320 2321 wlfc->hanger = 2322 dhd_wlfc_hanger_create(dhd->osh, WLFC_HANGER_MAXITEMS); 2323 if (wlfc->hanger == NULL) { 2324 MFREE(dhd->osh, dhd->wlfc_state, sizeof(athost_wl_status_info_t)); 2325 dhd->wlfc_state = NULL; 2326 return BCME_NOMEM; 2327 } 2328 2329 /* initialize all interfaces to accept traffic */ 2330 for (i = 0; i < WLFC_MAX_IFNUM; i++) { 2331 wlfc->hostif_flow_state[i] = OFF; 2332 } 2333 2334 /* 2335 create the SENDQ containing 2336 sub-queues for all AC precedences + 1 for bc/mc traffic 2337 */ 2338 pktq_init(&wlfc->SENDQ, (AC_COUNT + 1), WLFC_SENDQ_LEN); 2339 2340 wlfc->destination_entries.other.state = WLFC_STATE_OPEN; 2341 /* bc/mc FIFO is always open [credit aside], i.e. b[5] */ 2342 wlfc->destination_entries.other.ac_bitmap = 0x1f; 2343 wlfc->destination_entries.other.interface_id = 0; 2344 2345 wlfc->proptxstatus_mode = WLFC_FCMODE_EXPLICIT_CREDIT; 2346 2347 wlfc->allow_credit_borrow = TRUE; 2348 wlfc->borrow_defer_timestamp = 0; 2349 2350 return BCME_OK; 2351} 2352 2353/* release all packet resources */ 2354void 2355dhd_wlfc_cleanup(dhd_pub_t *dhd) 2356{ 2357 int i; 2358 int total_entries; 2359 athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*) 2360 dhd->wlfc_state; 2361 wlfc_mac_descriptor_t* table; 2362 wlfc_hanger_t* h; 2363 2364 if (dhd->wlfc_state == NULL) 2365 return; 2366 2367 total_entries = sizeof(wlfc->destination_entries)/sizeof(wlfc_mac_descriptor_t); 2368 /* search all entries, include nodes as well as interfaces */ 2369 table = (wlfc_mac_descriptor_t*)&wlfc->destination_entries; 2370 2371 for (i = 0; i < total_entries; i++) { 2372 if (table[i].occupied) { 2373 if (table[i].psq.len) { 2374 WLFC_DBGMESG(("%s(): DELAYQ[%d].len = %d\n", 2375 __FUNCTION__, i, table[i].psq.len)); 2376 /* release packets held in DELAYQ */ 2377 pktq_flush(wlfc->osh, &table[i].psq, TRUE, NULL, 0); 2378 } 2379 table[i].occupied = 0; 2380 } 2381 } 2382 /* release packets held in SENDQ */ 2383 if (wlfc->SENDQ.len) 2384 pktq_flush(wlfc->osh, &wlfc->SENDQ, TRUE, NULL, 0); 2385 /* any in the hanger? */ 2386 h = (wlfc_hanger_t*)wlfc->hanger; 2387 for (i = 0; i < h->max_items; i++) { 2388 if (h->items[i].state == WLFC_HANGER_ITEM_STATE_INUSE) { 2389 PKTFREE(wlfc->osh, h->items[i].pkt, TRUE); 2390 h->items[i].state = WLFC_HANGER_ITEM_STATE_FREE; 2391 } else if (h->items[i].state == WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED) { 2392 /* These are freed from the psq so no need to free again */ 2393 h->items[i].state = WLFC_HANGER_ITEM_STATE_FREE; 2394 } 2395 } 2396 return; 2397} 2398 2399void 2400dhd_wlfc_deinit(dhd_pub_t *dhd) 2401{ 2402 /* cleanup all psq related resources */ 2403 athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*) 2404 dhd->wlfc_state; 2405 2406 if (dhd->wlfc_state == NULL) 2407 return; 2408 2409#ifdef PROP_TXSTATUS_DEBUG 2410 { 2411 int i; 2412 wlfc_hanger_t* h = (wlfc_hanger_t*)wlfc->hanger; 2413 for (i = 0; i < h->max_items; i++) { 2414 if (h->items[i].state != WLFC_HANGER_ITEM_STATE_FREE) { 2415 WLFC_DBGMESG(("%s() pkt[%d] = 0x%p, FIFO_credit_used:%d\n", 2416 __FUNCTION__, i, h->items[i].pkt, 2417 DHD_PKTTAG_CREDITCHECK(PKTTAG(h->items[i].pkt)))); 2418 } 2419 } 2420 } 2421#endif 2422 /* delete hanger */ 2423 dhd_wlfc_hanger_delete(dhd->osh, wlfc->hanger); 2424 2425 /* free top structure */ 2426 MFREE(dhd->osh, dhd->wlfc_state, sizeof(athost_wl_status_info_t)); 2427 dhd->wlfc_state = NULL; 2428 return; 2429} 2430#endif /* PROP_TXSTATUS */ 2431 2432void 2433dhd_prot_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf) 2434{ 2435 bcm_bprintf(strbuf, "Protocol CDC: reqid %d\n", dhdp->prot->reqid); 2436#ifdef PROP_TXSTATUS 2437 if (dhdp->wlfc_state) 2438 dhd_wlfc_dump(dhdp, strbuf); 2439#endif 2440} 2441 2442void 2443dhd_prot_hdrpush(dhd_pub_t *dhd, int ifidx, void *pktbuf) 2444{ 2445#ifdef BDC 2446 struct bdc_header *h; 2447#endif /* BDC */ 2448 2449 DHD_TRACE(("%s: Enter\n", __FUNCTION__)); 2450 2451#ifdef BDC 2452 /* Push BDC header used to convey priority for buses that don't */ 2453 2454 PKTPUSH(dhd->osh, pktbuf, BDC_HEADER_LEN); 2455 2456 h = (struct bdc_header *)PKTDATA(dhd->osh, pktbuf); 2457 2458 h->flags = (BDC_PROTO_VER << BDC_FLAG_VER_SHIFT); 2459 if (PKTSUMNEEDED(pktbuf)) 2460 h->flags |= BDC_FLAG_SUM_NEEDED; 2461 2462 2463 h->priority = (PKTPRIO(pktbuf) & BDC_PRIORITY_MASK); 2464 h->flags2 = 0; 2465 h->dataOffset = 0; 2466#endif /* BDC */ 2467 BDC_SET_IF_IDX(h, ifidx); 2468} 2469 2470int 2471dhd_prot_hdrpull(dhd_pub_t *dhd, int *ifidx, void *pktbuf, uchar *reorder_buf_info, 2472 uint *reorder_info_len) 2473{ 2474#ifdef BDC 2475 struct bdc_header *h; 2476#endif 2477 2478 DHD_TRACE(("%s: Enter\n", __FUNCTION__)); 2479 2480#ifdef BDC 2481 if (reorder_info_len) 2482 *reorder_info_len = 0; 2483 /* Pop BDC header used to convey priority for buses that don't */ 2484 2485 if (PKTLEN(dhd->osh, pktbuf) < BDC_HEADER_LEN) { 2486 DHD_ERROR(("%s: rx data too short (%d < %d)\n", __FUNCTION__, 2487 PKTLEN(dhd->osh, pktbuf), BDC_HEADER_LEN)); 2488 return BCME_ERROR; 2489 } 2490 2491 h = (struct bdc_header *)PKTDATA(dhd->osh, pktbuf); 2492 2493 if ((*ifidx = BDC_GET_IF_IDX(h)) >= DHD_MAX_IFS) { 2494 DHD_ERROR(("%s: rx data ifnum out of range (%d)\n", 2495 __FUNCTION__, *ifidx)); 2496 return BCME_ERROR; 2497 } 2498 2499#if defined(NDIS630) 2500 h->dataOffset = 0; 2501#endif 2502 if (((h->flags & BDC_FLAG_VER_MASK) >> BDC_FLAG_VER_SHIFT) != BDC_PROTO_VER) { 2503 DHD_ERROR(("%s: non-BDC packet received, flags = 0x%x\n", 2504 dhd_ifname(dhd, *ifidx), h->flags)); 2505 if (((h->flags & BDC_FLAG_VER_MASK) >> BDC_FLAG_VER_SHIFT) == BDC_PROTO_VER_1) 2506 h->dataOffset = 0; 2507 else 2508 return BCME_ERROR; 2509 } 2510 2511 if (h->flags & BDC_FLAG_SUM_GOOD) { 2512 DHD_INFO(("%s: BDC packet received with good rx-csum, flags 0x%x\n", 2513 dhd_ifname(dhd, *ifidx), h->flags)); 2514 PKTSETSUMGOOD(pktbuf, TRUE); 2515 } 2516 2517 PKTSETPRIO(pktbuf, (h->priority & BDC_PRIORITY_MASK)); 2518 PKTPULL(dhd->osh, pktbuf, BDC_HEADER_LEN); 2519#endif /* BDC */ 2520 2521#if !defined(NDIS630) 2522 if (PKTLEN(dhd->osh, pktbuf) < (uint32) (h->dataOffset << 2)) { 2523 DHD_ERROR(("%s: rx data too short (%d < %d)\n", __FUNCTION__, 2524 PKTLEN(dhd->osh, pktbuf), (h->dataOffset * 4))); 2525 return BCME_ERROR; 2526 } 2527#endif 2528#ifdef PROP_TXSTATUS 2529 if (dhd->wlfc_state && 2530 ((athost_wl_status_info_t*)dhd->wlfc_state)->proptxstatus_mode 2531 != WLFC_FCMODE_NONE && 2532 (!DHD_PKTTAG_PKTDIR(PKTTAG(pktbuf)))) { 2533 /* 2534 - parse txstatus only for packets that came from the firmware 2535 */ 2536 dhd_os_wlfc_block(dhd); 2537 dhd_wlfc_parse_header_info(dhd, pktbuf, (h->dataOffset << 2), 2538 reorder_buf_info, reorder_info_len); 2539 ((athost_wl_status_info_t*)dhd->wlfc_state)->stats.dhd_hdrpulls++; 2540 dhd_wlfc_commit_packets(dhd->wlfc_state, (f_commitpkt_t)dhd_bus_txdata, 2541 (void *)dhd->bus); 2542 dhd_os_wlfc_unblock(dhd); 2543 } 2544#endif /* PROP_TXSTATUS */ 2545#if !defined(NDIS630) 2546 PKTPULL(dhd->osh, pktbuf, (h->dataOffset << 2)); 2547#endif 2548 return 0; 2549} 2550 2551int 2552dhd_prot_attach(dhd_pub_t *dhd) 2553{ 2554 dhd_prot_t *cdc; 2555 2556 if (!(cdc = (dhd_prot_t *)DHD_OS_PREALLOC(dhd->osh, DHD_PREALLOC_PROT, 2557 sizeof(dhd_prot_t)))) { 2558 DHD_ERROR(("%s: kmalloc failed\n", __FUNCTION__)); 2559 goto fail; 2560 } 2561 memset(cdc, 0, sizeof(dhd_prot_t)); 2562 2563 /* ensure that the msg buf directly follows the cdc msg struct */ 2564 if ((uintptr)(&cdc->msg + 1) != (uintptr)cdc->buf) { 2565 DHD_ERROR(("dhd_prot_t is not correctly defined\n")); 2566 goto fail; 2567 } 2568 2569 dhd->prot = cdc; 2570#ifdef BDC 2571 dhd->hdrlen += BDC_HEADER_LEN; 2572#endif 2573 dhd->maxctl = WLC_IOCTL_MAXLEN + sizeof(cdc_ioctl_t) + ROUND_UP_MARGIN; 2574 return 0; 2575 2576fail: 2577#ifndef CONFIG_DHD_USE_STATIC_BUF 2578 if (cdc != NULL) 2579 MFREE(dhd->osh, cdc, sizeof(dhd_prot_t)); 2580#endif /* CONFIG_DHD_USE_STATIC_BUF */ 2581 return BCME_NOMEM; 2582} 2583 2584/* ~NOTE~ What if another thread is waiting on the semaphore? Holding it? */ 2585void 2586dhd_prot_detach(dhd_pub_t *dhd) 2587{ 2588#ifdef PROP_TXSTATUS 2589 dhd_wlfc_deinit(dhd); 2590#endif 2591#ifndef CONFIG_DHD_USE_STATIC_BUF 2592 MFREE(dhd->osh, dhd->prot, sizeof(dhd_prot_t)); 2593#endif /* CONFIG_DHD_USE_STATIC_BUF */ 2594 dhd->prot = NULL; 2595} 2596 2597void 2598dhd_prot_dstats(dhd_pub_t *dhd) 2599{ 2600 /* No stats from dongle added yet, copy bus stats */ 2601 dhd->dstats.tx_packets = dhd->tx_packets; 2602 dhd->dstats.tx_errors = dhd->tx_errors; 2603 dhd->dstats.rx_packets = dhd->rx_packets; 2604 dhd->dstats.rx_errors = dhd->rx_errors; 2605 dhd->dstats.rx_dropped = dhd->rx_dropped; 2606 dhd->dstats.multicast = dhd->rx_multicast; 2607 return; 2608} 2609 2610int 2611dhd_prot_init(dhd_pub_t *dhd) 2612{ 2613 int ret = 0; 2614 wlc_rev_info_t revinfo; 2615 DHD_TRACE(("%s: Enter\n", __FUNCTION__)); 2616 2617 2618 /* Get the device rev info */ 2619 memset(&revinfo, 0, sizeof(revinfo)); 2620 ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_REVINFO, &revinfo, sizeof(revinfo), FALSE, 0); 2621 if (ret < 0) 2622 goto done; 2623 2624 2625#ifdef PROP_TXSTATUS 2626 ret = dhd_wlfc_init(dhd); 2627#endif 2628 2629#if defined(WL_CFG80211) 2630 if (dhd_download_fw_on_driverload) 2631#endif /* defined(WL_CFG80211) */ 2632 ret = dhd_preinit_ioctls(dhd); 2633 2634 /* Always assumes wl for now */ 2635 dhd->iswl = TRUE; 2636 2637done: 2638 return ret; 2639} 2640 2641void 2642dhd_prot_stop(dhd_pub_t *dhd) 2643{ 2644 /* Nothing to do for CDC */ 2645} 2646 2647 2648static void 2649dhd_get_hostreorder_pkts(void *osh, struct reorder_info *ptr, void **pkt, 2650 uint32 *pkt_count, void **pplast, uint8 start, uint8 end) 2651{ 2652 uint i; 2653 void *plast = NULL, *p; 2654 uint32 pkt_cnt = 0; 2655 2656 if (ptr->pend_pkts == 0) { 2657 DHD_REORDER(("%s: no packets in reorder queue \n", __FUNCTION__)); 2658 *pplast = NULL; 2659 *pkt_count = 0; 2660 *pkt = NULL; 2661 return; 2662 } 2663 if (start == end) 2664 i = ptr->max_idx + 1; 2665 else { 2666 if (start > end) 2667 i = (ptr->max_idx - end) + start; 2668 else 2669 i = end - start; 2670 } 2671 while (i) { 2672 p = (void *)(ptr->p[start]); 2673 ptr->p[start] = NULL; 2674 2675 if (p != NULL) { 2676 if (plast == NULL) 2677 *pkt = p; 2678 else 2679 PKTSETNEXT(osh, plast, p); 2680 2681 plast = p; 2682 pkt_cnt++; 2683 } 2684 i--; 2685 if (start++ == ptr->max_idx) 2686 start = 0; 2687 } 2688 *pplast = plast; 2689 *pkt_count = (uint32)pkt_cnt; 2690} 2691 2692int 2693dhd_process_pkt_reorder_info(dhd_pub_t *dhd, uchar *reorder_info_buf, uint reorder_info_len, 2694 void **pkt, uint32 *pkt_count) 2695{ 2696 uint8 flow_id, max_idx, cur_idx, exp_idx; 2697 struct reorder_info *ptr; 2698 uint8 flags; 2699 void *cur_pkt, *plast = NULL; 2700 uint32 cnt = 0; 2701 2702 if (pkt == NULL) { 2703 if (pkt_count != NULL) 2704 *pkt_count = 0; 2705 return 0; 2706 } 2707 cur_pkt = *pkt; 2708 *pkt = NULL; 2709 2710 flow_id = reorder_info_buf[WLHOST_REORDERDATA_FLOWID_OFFSET]; 2711 flags = reorder_info_buf[WLHOST_REORDERDATA_FLAGS_OFFSET]; 2712 2713 DHD_REORDER(("flow_id %d, flags 0x%02x, idx(%d, %d, %d)\n", flow_id, flags, 2714 reorder_info_buf[WLHOST_REORDERDATA_CURIDX_OFFSET], 2715 reorder_info_buf[WLHOST_REORDERDATA_EXPIDX_OFFSET], 2716 reorder_info_buf[WLHOST_REORDERDATA_MAXIDX_OFFSET])); 2717 2718 /* validate flags and flow id */ 2719 if (flags == 0xFF) { 2720 DHD_ERROR(("%s: invalid flags...so ignore this packet\n", __FUNCTION__)); 2721 *pkt_count = 1; 2722 return 0; 2723 } 2724 2725 ptr = dhd->reorder_bufs[flow_id]; 2726 if (flags & WLHOST_REORDERDATA_DEL_FLOW) { 2727 uint32 buf_size = sizeof(struct reorder_info); 2728 2729 DHD_REORDER(("%s: Flags indicating to delete a flow id %d\n", 2730 __FUNCTION__, flow_id)); 2731 2732 if (ptr == NULL) { 2733 DHD_ERROR(("%s: received flags to cleanup, but no flow (%d) yet\n", 2734 __FUNCTION__, flow_id)); 2735 *pkt_count = 1; 2736 return 0; 2737 } 2738 2739 dhd_get_hostreorder_pkts(dhd->osh, ptr, pkt, &cnt, &plast, 2740 ptr->exp_idx, ptr->exp_idx); 2741 /* set it to the last packet */ 2742 if (plast) { 2743 PKTSETNEXT(dhd->osh, plast, cur_pkt); 2744 cnt++; 2745 } 2746 else { 2747 if (cnt != 0) { 2748 DHD_ERROR(("%s: del flow: something fishy, pending packets %d\n", 2749 __FUNCTION__, cnt)); 2750 } 2751 *pkt = cur_pkt; 2752 cnt = 1; 2753 } 2754 buf_size += ((ptr->max_idx + 1) * sizeof(void *)); 2755 MFREE(dhd->osh, ptr, buf_size); 2756 dhd->reorder_bufs[flow_id] = NULL; 2757 *pkt_count = cnt; 2758 return 0; 2759 } 2760 /* all the other cases depend on the existance of the reorder struct for that flow id */ 2761 if (ptr == NULL) { 2762 uint32 buf_size_alloc = sizeof(reorder_info_t); 2763 max_idx = reorder_info_buf[WLHOST_REORDERDATA_MAXIDX_OFFSET]; 2764 2765 buf_size_alloc += ((max_idx + 1) * sizeof(void*)); 2766 /* allocate space to hold the buffers, index etc */ 2767 2768 DHD_REORDER(("%s: alloc buffer of size %d size, reorder info id %d, maxidx %d\n", 2769 __FUNCTION__, buf_size_alloc, flow_id, max_idx)); 2770 ptr = (struct reorder_info *)MALLOC(dhd->osh, buf_size_alloc); 2771 if (ptr == NULL) { 2772 DHD_ERROR(("%s: Malloc failed to alloc buffer\n", __FUNCTION__)); 2773 *pkt_count = 1; 2774 return 0; 2775 } 2776 bzero(ptr, buf_size_alloc); 2777 dhd->reorder_bufs[flow_id] = ptr; 2778 ptr->p = (void *)(ptr+1); 2779 ptr->max_idx = max_idx; 2780 } 2781 if (flags & WLHOST_REORDERDATA_NEW_HOLE) { 2782 DHD_REORDER(("%s: new hole, so cleanup pending buffers\n", __FUNCTION__)); 2783 if (ptr->pend_pkts) { 2784 dhd_get_hostreorder_pkts(dhd->osh, ptr, pkt, &cnt, &plast, 2785 ptr->exp_idx, ptr->exp_idx); 2786 ptr->pend_pkts = 0; 2787 } 2788 ptr->cur_idx = reorder_info_buf[WLHOST_REORDERDATA_CURIDX_OFFSET]; 2789 ptr->exp_idx = reorder_info_buf[WLHOST_REORDERDATA_EXPIDX_OFFSET]; 2790 ptr->max_idx = reorder_info_buf[WLHOST_REORDERDATA_MAXIDX_OFFSET]; 2791 ptr->p[ptr->cur_idx] = cur_pkt; 2792 ptr->pend_pkts++; 2793 *pkt_count = cnt; 2794 } 2795 else if (flags & WLHOST_REORDERDATA_CURIDX_VALID) { 2796 cur_idx = reorder_info_buf[WLHOST_REORDERDATA_CURIDX_OFFSET]; 2797 exp_idx = reorder_info_buf[WLHOST_REORDERDATA_EXPIDX_OFFSET]; 2798 2799 2800 if ((exp_idx == ptr->exp_idx) && (cur_idx != ptr->exp_idx)) { 2801 /* still in the current hole */ 2802 /* enqueue the current on the buffer chain */ 2803 if (ptr->p[cur_idx] != NULL) { 2804 DHD_REORDER(("%s: HOLE: ERROR buffer pending..free it\n", 2805 __FUNCTION__)); 2806 PKTFREE(dhd->osh, ptr->p[cur_idx], TRUE); 2807 ptr->p[cur_idx] = NULL; 2808 } 2809 ptr->p[cur_idx] = cur_pkt; 2810 ptr->pend_pkts++; 2811 ptr->cur_idx = cur_idx; 2812 DHD_REORDER(("%s: fill up a hole..pending packets is %d\n", 2813 __FUNCTION__, ptr->pend_pkts)); 2814 *pkt_count = 0; 2815 *pkt = NULL; 2816 } 2817 else if (ptr->exp_idx == cur_idx) { 2818 /* got the right one ..flush from cur to exp and update exp */ 2819 DHD_REORDER(("%s: got the right one now, cur_idx is %d\n", 2820 __FUNCTION__, cur_idx)); 2821 if (ptr->p[cur_idx] != NULL) { 2822 DHD_REORDER(("%s: Error buffer pending..free it\n", 2823 __FUNCTION__)); 2824 PKTFREE(dhd->osh, ptr->p[cur_idx], TRUE); 2825 ptr->p[cur_idx] = NULL; 2826 } 2827 ptr->p[cur_idx] = cur_pkt; 2828 ptr->pend_pkts++; 2829 2830 ptr->cur_idx = cur_idx; 2831 ptr->exp_idx = exp_idx; 2832 2833 dhd_get_hostreorder_pkts(dhd->osh, ptr, pkt, &cnt, &plast, 2834 cur_idx, exp_idx); 2835 ptr->pend_pkts -= (uint8)cnt; 2836 *pkt_count = cnt; 2837 DHD_REORDER(("%s: freeing up buffers %d, still pending %d\n", 2838 __FUNCTION__, cnt, ptr->pend_pkts)); 2839 } 2840 else { 2841 uint8 end_idx; 2842 bool flush_current = FALSE; 2843 /* both cur and exp are moved now .. */ 2844 DHD_REORDER(("%s:, flow %d, both moved, cur %d(%d), exp %d(%d)\n", 2845 __FUNCTION__, flow_id, ptr->cur_idx, cur_idx, 2846 ptr->exp_idx, exp_idx)); 2847 if (flags & WLHOST_REORDERDATA_FLUSH_ALL) 2848 end_idx = ptr->exp_idx; 2849 else 2850 end_idx = exp_idx; 2851 2852 /* flush pkts first */ 2853 dhd_get_hostreorder_pkts(dhd->osh, ptr, pkt, &cnt, &plast, 2854 ptr->exp_idx, end_idx); 2855 2856 if (cur_idx == ptr->max_idx) { 2857 if (exp_idx == 0) 2858 flush_current = TRUE; 2859 } else { 2860 if (exp_idx == cur_idx + 1) 2861 flush_current = TRUE; 2862 } 2863 if (flush_current) { 2864 if (plast) 2865 PKTSETNEXT(dhd->osh, plast, cur_pkt); 2866 else 2867 *pkt = cur_pkt; 2868 cnt++; 2869 } 2870 else { 2871 ptr->p[cur_idx] = cur_pkt; 2872 ptr->pend_pkts++; 2873 } 2874 ptr->exp_idx = exp_idx; 2875 ptr->cur_idx = cur_idx; 2876 *pkt_count = cnt; 2877 } 2878 } 2879 else { 2880 uint8 end_idx; 2881 /* no real packet but update to exp_seq...that means explicit window move */ 2882 exp_idx = reorder_info_buf[WLHOST_REORDERDATA_EXPIDX_OFFSET]; 2883 2884 DHD_REORDER(("%s: move the window, cur_idx is %d, exp is %d, new exp is %d\n", 2885 __FUNCTION__, ptr->cur_idx, ptr->exp_idx, exp_idx)); 2886 if (flags & WLHOST_REORDERDATA_FLUSH_ALL) 2887 end_idx = ptr->exp_idx; 2888 else 2889 end_idx = exp_idx; 2890 2891 dhd_get_hostreorder_pkts(dhd->osh, ptr, pkt, &cnt, &plast, ptr->exp_idx, end_idx); 2892 ptr->pend_pkts -= (uint8)cnt; 2893 if (plast) 2894 PKTSETNEXT(dhd->osh, plast, cur_pkt); 2895 else 2896 *pkt = cur_pkt; 2897 cnt++; 2898 *pkt_count = cnt; 2899 /* set the new expected idx */ 2900 ptr->exp_idx = exp_idx; 2901 } 2902 return 0; 2903} 2904