From acf8ed36e43b8ef60c60fdc53fffe7a31d8b2e1f Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Wed, 7 Sep 2011 10:30:45 -0700 Subject: [PATCH] net: wireless: bcmdhd: Update to version 5.90.125.78 - Add BT-Coex support to cfg80211 - Add private event logic to cfg80211 when FW hangs - Reduce passive dwell time to 130 ms - Fix proptx initialize fail issue - Implement codes for WPS2.0 using cfg80211 - Clean any left virtual interfaces in primary dhd_stop context - Skip waiting for rtnl_lock in cfg80211 callback if already taken - Skip writing to file FW trap info Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcmdhd/dhd.h | 4 +- drivers/net/wireless/bcmdhd/dhd_cdc.c | 412 +++++++++++++---- drivers/net/wireless/bcmdhd/dhd_common.c | 2 +- drivers/net/wireless/bcmdhd/dhd_linux.c | 136 ++++-- drivers/net/wireless/bcmdhd/dhd_linux_sched.c | 1 + drivers/net/wireless/bcmdhd/dhd_sdio.c | 155 ++++--- drivers/net/wireless/bcmdhd/dhd_wlfc.h | 22 +- drivers/net/wireless/bcmdhd/include/epivers.h | 8 +- drivers/net/wireless/bcmdhd/linux_osl.c | 67 ++- drivers/net/wireless/bcmdhd/wl_android.c | 4 + drivers/net/wireless/bcmdhd/wl_cfg80211.c | 618 ++++++++++++++++++++++++-- drivers/net/wireless/bcmdhd/wl_cfg80211.h | 29 +- drivers/net/wireless/bcmdhd/wl_cfgp2p.c | 93 ++-- drivers/net/wireless/bcmdhd/wl_iw.c | 35 +- 14 files changed, 1268 insertions(+), 318 deletions(-) diff --git a/drivers/net/wireless/bcmdhd/dhd.h b/drivers/net/wireless/bcmdhd/dhd.h index 735d39b..5d9e678 100644 --- a/drivers/net/wireless/bcmdhd/dhd.h +++ b/drivers/net/wireless/bcmdhd/dhd.h @@ -202,7 +202,7 @@ typedef struct dhd_pub { void* wlfc_state; #endif bool dongle_isolation; - + int hang_was_sent; #ifdef WLMEDIA_HTSF uint8 htsfdlystat_sz; /* Size of delay stats, max 255B */ #endif @@ -389,7 +389,7 @@ extern void dhd_os_sdlock_rxq(dhd_pub_t * pub); extern void dhd_os_sdunlock_rxq(dhd_pub_t * pub); extern void dhd_os_sdlock_sndup_rxq(dhd_pub_t * pub); extern void dhd_customer_gpio_wlan_ctrl(int onoff); -extern int dhd_custom_get_mac_address(unsigned char *buf); +extern int dhd_custom_get_mac_address(unsigned char *buf); extern void dhd_os_sdunlock_sndup_rxq(dhd_pub_t * pub); extern void dhd_os_sdlock_eventq(dhd_pub_t * pub); extern void dhd_os_sdunlock_eventq(dhd_pub_t * pub); diff --git a/drivers/net/wireless/bcmdhd/dhd_cdc.c b/drivers/net/wireless/bcmdhd/dhd_cdc.c index 54ce6ff..a86ea56 100644 --- a/drivers/net/wireless/bcmdhd/dhd_cdc.c +++ b/drivers/net/wireless/bcmdhd/dhd_cdc.c @@ -57,6 +57,17 @@ * round off at the end of buffer */ +#define BUS_RETRIES 1 /* # of retries before aborting a bus tx operation */ + +#ifdef PROP_TXSTATUS +typedef struct dhd_wlfc_commit_info { + uint8 needs_hdr; + uint8 ac_fifo_credit_spent; + ewlfc_packet_state_t pkt_type; + wlfc_mac_descriptor_t* mac_entry; + void* p; +} dhd_wlfc_commit_info_t; +#endif /* PROP_TXSTATUS */ typedef struct dhd_prot { uint16 reqid; uint8 pending; @@ -152,7 +163,8 @@ dhdcdc_query_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len, uin memcpy(prot->buf, buf, len); if ((ret = dhdcdc_msg(dhd)) < 0) { - DHD_ERROR(("dhdcdc_query_ioctl: dhdcdc_msg failed w/status %d\n", ret)); + if (!dhd->hang_was_sent) + DHD_ERROR(("dhdcdc_query_ioctl: dhdcdc_msg failed w/status %d\n", ret)); goto done; } @@ -206,6 +218,17 @@ dhdcdc_set_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len, uint8 DHD_TRACE(("%s: Enter\n", __FUNCTION__)); DHD_CTL(("%s: cmd %d len %d\n", __FUNCTION__, cmd, len)); + if (dhd->busstate == DHD_BUS_DOWN) { + DHD_ERROR(("%s : bus is down. we have nothing to do\n", __FUNCTION__)); + return -EIO; + } + + /* don't talk to the dongle if fw is about to be reloaded */ + if (dhd->hang_was_sent) { + DHD_ERROR(("%s: HANG was sent up earlier. Not talking to the chip\n", + __FUNCTION__)); + return -EIO; + } memset(msg, 0, sizeof(cdc_ioctl_t)); @@ -629,8 +652,8 @@ dhd_wlfc_hanger_get_free_slot(void* hanger) if (h->items[i].state == WLFC_HANGER_ITEM_STATE_FREE) return (uint16)i; } + h->failed_slotfind++; } - h->failed_slotfind++; return WLFC_HANGER_MAXITEMS; } @@ -1287,6 +1310,30 @@ _dhd_wlfc_mac_entry_update(athost_wl_status_info_t* ctx, wlfc_mac_descriptor_t* } int +_dhd_wlfc_borrow_credit(athost_wl_status_info_t* ctx, uint8 available_credit_map, int borrower_ac) +{ + int lender_ac; + int rc = BCME_ERROR; + + if (ctx == NULL || available_credit_map == 0) { + WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); + return BCME_BADARG; + } + + /* Borrow from lowest priority available AC (including BC/MC credits) */ + for (lender_ac = 0; lender_ac <= AC_COUNT; lender_ac++) { + if ((available_credit_map && (1 << lender_ac)) && + (ctx->FIFO_credit[lender_ac] > 0)) { + ctx->credits_borrowed[borrower_ac][lender_ac]++; + ctx->FIFO_credit[lender_ac]--; + rc = BCME_OK; + break; + } + } + + return rc; +} +int dhd_wlfc_interface_entry_update(void* state, ewlfc_mac_entry_action_t action, uint8 ifid, uint8 iftype, uint8* ea) { @@ -1313,6 +1360,7 @@ dhd_wlfc_FIFOcreditmap_update(void* state, uint8* credits) /* credit for bc/mc packets */ ctx->FIFO_credit[4] = credits[4]; /* credit for ATIM FIFO is not used yet. */ + ctx->FIFO_credit[5] = 0; return BCME_OK; } @@ -1345,17 +1393,68 @@ dhd_wlfc_enque_sendq(void* state, int prec, void* p) } int +_dhd_wlfc_handle_packet_commit(athost_wl_status_info_t* ctx, int ac, + dhd_wlfc_commit_info_t *commit_info, f_commitpkt_t fcommit, void* commit_ctx) +{ + uint32 hslot; + int rc; + + /* + if ac_fifo_credit_spent = 0 + + This packet will not count against the FIFO credit. + To ensure the txstatus corresponding to this packet + does not provide an implied credit (default behavior) + mark the packet accordingly. + + if ac_fifo_credit_spent = 1 + + This is a normal packet and it counts against the FIFO + credit count. + */ + DHD_PKTTAG_SETCREDITCHECK(PKTTAG(commit_info->p), commit_info->ac_fifo_credit_spent); + rc = _dhd_wlfc_pretx_pktprocess(ctx, commit_info->mac_entry, commit_info->p, + commit_info->needs_hdr, &hslot); + + if (rc == BCME_OK) + rc = fcommit(commit_ctx, commit_info->p); + else + ctx->stats.generic_error++; + + if (rc == BCME_OK) { + ctx->stats.pkt2bus++; + if (commit_info->ac_fifo_credit_spent) { + ctx->stats.sendq_pkts[ac]++; + WLFC_HOST_FIFO_CREDIT_INC_SENTCTRS(ctx, ac); + } + } + else { + /* + bus commit has failed, rollback. + - remove wl-header for a delayed packet + - save wl-header header for suppressed packets + */ + rc = _dhd_wlfc_rollback_packet_toq(ctx, commit_info->p, + (commit_info->pkt_type), hslot); + if (rc != BCME_OK) + ctx->stats.rollback_failed++; + + rc = BCME_ERROR; + } + + return rc; +} +int dhd_wlfc_commit_packets(void* state, f_commitpkt_t fcommit, void* commit_ctx) { int ac; int credit; - uint8 ac_fifo_credit_spent; - uint8 needs_hdr; - uint32 hslot; - void* p; int rc; + dhd_wlfc_commit_info_t commit_info; athost_wl_status_info_t* ctx = (athost_wl_status_info_t*)state; - wlfc_mac_descriptor_t* mac_entry; + int credit_count = 0; + int bus_retry_count = 0; + uint8 ac_available = 0; /* Bitmask for 4 ACs + BC/MC */ if ((state == NULL) || (fcommit == NULL)) { @@ -1363,8 +1462,12 @@ dhd_wlfc_commit_packets(void* state, f_commitpkt_t fcommit, void* commit_ctx) return BCME_BADARG; } - /* + memset(&commit_info, 0, sizeof(commit_info)); + + /* Commit packets for regular AC traffic. Higher priority first. + First, use up FIFO credits available to each AC. Based on distribution + and credits left, borrow from other ACs as applicable -NOTE: If the bus between the host and firmware is overwhelmed by the @@ -1373,96 +1476,189 @@ dhd_wlfc_commit_packets(void* state, f_commitpkt_t fcommit, void* commit_ctx) have to employ weighted round-robin or ucode scheme to avoid low priority packet starvation. */ + for (ac = AC_COUNT; ac >= 0; ac--) { - for (credit = 0; credit < ctx->FIFO_credit[ac];) { - p = _dhd_wlfc_deque_delayedq(ctx, ac, &ac_fifo_credit_spent, &needs_hdr, - &mac_entry); - if (p == NULL) - break; - /* - if ac_fifo_credit_spent = 0 - This packet will not count against the FIFO credit. - To ensure the txstatus corresponding to this packet - does not provide an implied credit (default behavior) - mark the packet accordingly. + int initial_credit_count = ctx->FIFO_credit[ac]; - if ac_fifo_credit_spent = 1 + for (credit = 0; credit < ctx->FIFO_credit[ac];) { + commit_info.p = _dhd_wlfc_deque_delayedq(ctx, ac, + &(commit_info.ac_fifo_credit_spent), + &(commit_info.needs_hdr), + &(commit_info.mac_entry)); - This is a normal packet and it counts against the FIFO - credit count. - */ - DHD_PKTTAG_SETCREDITCHECK(PKTTAG(p), ac_fifo_credit_spent); - rc = _dhd_wlfc_pretx_pktprocess(ctx, mac_entry, p, needs_hdr, &hslot); + if (commit_info.p == NULL) + break; - if (rc == BCME_OK) - rc = fcommit(commit_ctx, p); - else - ctx->stats.generic_error++; + commit_info.pkt_type = (commit_info.needs_hdr) ? eWLFC_PKTTYPE_DELAYED : + eWLFC_PKTTYPE_SUPPRESSED; + rc = _dhd_wlfc_handle_packet_commit(ctx, ac, &commit_info, + fcommit, commit_ctx); + + /* Bus commits may fail (e.g. flow control); abort after retries */ if (rc == BCME_OK) { - ctx->stats.pkt2bus++; - if (ac_fifo_credit_spent) { - ctx->stats.sendq_pkts[ac]++; - WLFC_HOST_FIFO_CREDIT_INC_SENTCTRS(ctx, ac); - /* - 1 FIFO credit has been spent by sending this packet - to the device. - */ + if (commit_info.ac_fifo_credit_spent) { credit++; } } else { - /* bus commit has failed, rollback. */ - rc = _dhd_wlfc_rollback_packet_toq(ctx, - p, - /* - - remove wl-header for a delayed packet - - save wl-header header for suppressed packets - */ - (needs_hdr ? eWLFC_PKTTYPE_DELAYED : - eWLFC_PKTTYPE_SUPPRESSED), - hslot); - if (rc != BCME_OK) - ctx->stats.rollback_failed++; + bus_retry_count++; + if (bus_retry_count >= BUS_RETRIES) { + DHD_ERROR(("dhd_wlfc_commit_packets(): bus error\n")); + ctx->FIFO_credit[ac] -= credit; + return rc; + } } } + ctx->FIFO_credit[ac] -= credit; - /* packets from SENDQ are fresh and they'd need header */ - needs_hdr = 1; + + /* packets from SENDQ are fresh and they'd need header and have no MAC entry */ + commit_info.needs_hdr = 1; + commit_info.mac_entry = NULL; + commit_info.pkt_type = eWLFC_PKTTYPE_NEW; + for (credit = 0; credit < ctx->FIFO_credit[ac];) { - p = _dhd_wlfc_deque_sendq(ctx, ac, &ac_fifo_credit_spent); - if (p == NULL) + commit_info.p = _dhd_wlfc_deque_sendq(ctx, ac, + &(commit_info.ac_fifo_credit_spent)); + if (commit_info.p == NULL) break; - DHD_PKTTAG_SETCREDITCHECK(PKTTAG(p), ac_fifo_credit_spent); - rc = _dhd_wlfc_pretx_pktprocess(ctx, NULL, p, needs_hdr, &hslot); - if (rc == BCME_OK) - rc = fcommit(commit_ctx, p); - else - ctx->stats.generic_error++; + rc = _dhd_wlfc_handle_packet_commit(ctx, ac, &commit_info, + fcommit, commit_ctx); + /* Bus commits may fail (e.g. flow control); abort after retries */ if (rc == BCME_OK) { - ctx->stats.pkt2bus++; - if (ac_fifo_credit_spent) { - WLFC_HOST_FIFO_CREDIT_INC_SENTCTRS(ctx, ac); - ctx->stats.sendq_pkts[ac]++; + if (commit_info.ac_fifo_credit_spent) { credit++; } } else { - /* bus commit has failed, rollback. */ - rc = _dhd_wlfc_rollback_packet_toq(ctx, - p, - /* remove wl-header while rolling back */ - eWLFC_PKTTYPE_NEW, - hslot); - if (rc != BCME_OK) - ctx->stats.rollback_failed++; + bus_retry_count++; + if (bus_retry_count >= BUS_RETRIES) { + DHD_ERROR(("dhd_wlfc_commit_packets(): bus error\n")); + ctx->FIFO_credit[ac] -= credit; + return rc; + } } } + ctx->FIFO_credit[ac] -= credit; + + /* If no credits were used, the queue is idle and can be re-used + Note that resv credits cannot be borrowed + */ + if (initial_credit_count == ctx->FIFO_credit[ac]) { + ac_available |= (1 << ac); + credit_count += ctx->FIFO_credit[ac]; + } + } + + /* We borrow only for AC_BE and only if no other traffic seen for DEFER_PERIOD + + Note that (ac_available & WLFC_AC_BE_TRAFFIC_ONLY) is done to: + a) ignore BC/MC for deferring borrow + b) ignore AC_BE being available along with other ACs + (this should happen only for pure BC/MC traffic) + + i.e. AC_VI, AC_VO, AC_BK all MUST be available (i.e. no traffic) and + we do not care if AC_BE and BC/MC are available or not + */ + if ((ac_available & WLFC_AC_BE_TRAFFIC_ONLY) == WLFC_AC_BE_TRAFFIC_ONLY) { + + if (ctx->allow_credit_borrow) { + ac = 1; /* Set ac to AC_BE and borrow credits */ + } + else { + int delta; + int curr_t = OSL_SYSUPTIME(); + + if (curr_t > ctx->borrow_defer_timestamp) + delta = curr_t - ctx->borrow_defer_timestamp; + else + delta = 0xffffffff + curr_t - ctx->borrow_defer_timestamp; + + if (delta >= WLFC_BORROW_DEFER_PERIOD_MS) { + /* Reset borrow but defer to next iteration (defensive borrowing) */ + ctx->allow_credit_borrow = TRUE; + ctx->borrow_defer_timestamp = 0; + } + return BCME_OK; + } } + else { + /* If we have multiple AC traffic, turn off borrowing, mark time and bail out */ + ctx->allow_credit_borrow = FALSE; + ctx->borrow_defer_timestamp = OSL_SYSUPTIME(); + return BCME_OK; + } + + /* At this point, borrow all credits only for "ac" (which should be set above to AC_BE) + Generically use "ac" only in case we extend to all ACs in future + */ + for (; (credit_count > 0);) { + + commit_info.p = _dhd_wlfc_deque_delayedq(ctx, ac, + &(commit_info.ac_fifo_credit_spent), + &(commit_info.needs_hdr), + &(commit_info.mac_entry)); + if (commit_info.p == NULL) + break; + + commit_info.pkt_type = (commit_info.needs_hdr) ? eWLFC_PKTTYPE_DELAYED : + eWLFC_PKTTYPE_SUPPRESSED; + + rc = _dhd_wlfc_handle_packet_commit(ctx, ac, &commit_info, + fcommit, commit_ctx); + + /* Bus commits may fail (e.g. flow control); abort after retries */ + if (rc == BCME_OK) { + if (commit_info.ac_fifo_credit_spent) { + (void) _dhd_wlfc_borrow_credit(ctx, ac_available, ac); + credit_count--; + } + } + else { + bus_retry_count++; + if (bus_retry_count >= BUS_RETRIES) { + DHD_ERROR(("dhd_wlfc_commit_packets(): bus error\n")); + return rc; + } + } + } + + /* packets from SENDQ are fresh and they'd need header and have no MAC entry */ + commit_info.needs_hdr = 1; + commit_info.mac_entry = NULL; + commit_info.pkt_type = eWLFC_PKTTYPE_NEW; + + for (; (credit_count > 0);) { + + commit_info.p = _dhd_wlfc_deque_sendq(ctx, ac, + &(commit_info.ac_fifo_credit_spent)); + if (commit_info.p == NULL) + break; + + rc = _dhd_wlfc_handle_packet_commit(ctx, ac, &commit_info, + fcommit, commit_ctx); + + /* Bus commits may fail (e.g. flow control); abort after retries */ + if (rc == BCME_OK) { + if (commit_info.ac_fifo_credit_spent) { + (void) _dhd_wlfc_borrow_credit(ctx, ac_available, ac); + credit_count--; + } + } + else { + bus_retry_count++; + if (bus_retry_count >= BUS_RETRIES) { + DHD_ERROR(("dhd_wlfc_commit_packets(): bus error\n")); + return rc; + } + } + } + return BCME_OK; } @@ -1489,6 +1685,7 @@ dhd_wlfc_txcomplete(dhd_pub_t *dhd, void *txp, bool success) athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*) dhd->wlfc_state; void* p; + int fifo_id; if (DHD_PKTTAG_SIGNALONLY(PKTTAG(txp))) { #ifdef PROP_TXSTATUS_DEBUG @@ -1506,11 +1703,29 @@ dhd_wlfc_txcomplete(dhd_pub_t *dhd, void *txp, bool success) /* indicate failure and free the packet */ dhd_txcomplete(dhd, txp, FALSE); - PKTFREE(wlfc->osh, txp, TRUE); /* return the credit, if necessary */ - if (DHD_PKTTAG_CREDITCHECK(PKTTAG(txp))) - wlfc->FIFO_credit[DHD_PKTTAG_FIFO(PKTTAG(txp))]++; + if (DHD_PKTTAG_CREDITCHECK(PKTTAG(txp))) { + int lender, credit_returned = 0; /* Note that borrower is fifo_id */ + + fifo_id = DHD_PKTTAG_FIFO(PKTTAG(txp)); + + /* Return credits to highest priority lender first */ + for (lender = AC_COUNT; lender >= 0; lender--) { + if (wlfc->credits_borrowed[fifo_id][lender] > 0) { + wlfc->FIFO_credit[lender]++; + wlfc->credits_borrowed[fifo_id][lender]--; + credit_returned = 1; + break; + } + } + + if (!credit_returned) { + wlfc->FIFO_credit[fifo_id]++; + } + } + + PKTFREE(wlfc->osh, txp, TRUE); } return; } @@ -1593,7 +1808,21 @@ dhd_wlfc_txstatus_update(dhd_pub_t *dhd, uint8* pkt_info) /* pick up the implicit credit from this packet */ if (DHD_PKTTAG_CREDITCHECK(PKTTAG(pktbuf))) { if (wlfc->proptxstatus_mode == WLFC_FCMODE_IMPLIED_CREDIT) { - wlfc->FIFO_credit[fifo_id]++; + int lender, credit_returned = 0; /* Note that borrower is fifo_id */ + + /* Return credits to highest priority lender first */ + for (lender = AC_COUNT; lender >= 0; lender--) { + if (wlfc->credits_borrowed[fifo_id][lender] > 0) { + wlfc->FIFO_credit[lender]++; + wlfc->credits_borrowed[fifo_id][lender]--; + credit_returned = 1; + break; + } + } + + if (!credit_returned) { + wlfc->FIFO_credit[fifo_id]++; + } } } else { @@ -1644,8 +1873,33 @@ dhd_wlfc_fifocreditback_indicate(dhd_pub_t *dhd, uint8* credits) #endif /* update FIFO credits */ if (wlfc->proptxstatus_mode == WLFC_FCMODE_EXPLICIT_CREDIT) - wlfc->FIFO_credit[i] += credits[i]; + { + int lender; /* Note that borrower is i */ + + /* Return credits to highest priority lender first */ + for (lender = AC_COUNT; (lender >= 0) && (credits[i] > 0); lender--) { + if (wlfc->credits_borrowed[i][lender] > 0) { + if (credits[i] >= wlfc->credits_borrowed[i][lender]) { + credits[i] -= wlfc->credits_borrowed[i][lender]; + wlfc->FIFO_credit[lender] += + wlfc->credits_borrowed[i][lender]; + wlfc->credits_borrowed[i][lender] = 0; + } + else { + wlfc->credits_borrowed[i][lender] -= credits[i]; + wlfc->FIFO_credit[lender] += credits[i]; + credits[i] = 0; + } + } + } + + /* If we have more credits left over, these must belong to the AC */ + if (credits[i] > 0) { + wlfc->FIFO_credit[i] += credits[i]; + } + } } + return BCME_OK; } @@ -1986,6 +2240,9 @@ dhd_wlfc_enable(dhd_pub_t *dhd) wlfc->proptxstatus_mode = WLFC_FCMODE_EXPLICIT_CREDIT; + wlfc->allow_credit_borrow = TRUE; + wlfc->borrow_defer_timestamp = 0; + return BCME_OK; } @@ -2165,7 +2422,7 @@ dhd_prot_hdrpull(dhd_pub_t *dhd, int *ifidx, void *pktbuf) dhd_wlfc_parse_header_info(dhd, pktbuf, (h->dataOffset << 2)); ((athost_wl_status_info_t*)dhd->wlfc_state)->stats.dhd_hdrpulls++; dhd_wlfc_commit_packets(dhd->wlfc_state, (f_commitpkt_t)dhd_bus_txdata, - dhd->bus); + (void *)dhd->bus); dhd_os_wlfc_unblock(dhd); } #endif /* PROP_TXSTATUS */ @@ -2232,7 +2489,6 @@ dhd_prot_dstats(dhd_pub_t *dhd) return; } - int dhd_prot_init(dhd_pub_t *dhd) { diff --git a/drivers/net/wireless/bcmdhd/dhd_common.c b/drivers/net/wireless/bcmdhd/dhd_common.c index 13791cd..94d3262 100644 --- a/drivers/net/wireless/bcmdhd/dhd_common.c +++ b/drivers/net/wireless/bcmdhd/dhd_common.c @@ -1970,7 +1970,7 @@ dhd_pno_set(dhd_pub_t *dhd, wlc_ssid_t* ssids_local, int nssid, ushort scan_fr, pfn_element.wpa_auth = htod32(WPA_AUTH_PFN_ANY); pfn_element.wsec = htod32(0); pfn_element.infra = htod32(1); - + pfn_element.flags = htod32(ENABLE << WL_PFN_HIDDEN_BIT); memcpy((char *)pfn_element.ssid.SSID, ssids_local[i].SSID, ssids_local[i].SSID_len); pfn_element.ssid.SSID_len = ssids_local[i].SSID_len; diff --git a/drivers/net/wireless/bcmdhd/dhd_linux.c b/drivers/net/wireless/bcmdhd/dhd_linux.c index 569bd7f..61b1aef 100644 --- a/drivers/net/wireless/bcmdhd/dhd_linux.c +++ b/drivers/net/wireless/bcmdhd/dhd_linux.c @@ -132,7 +132,11 @@ MODULE_LICENSE("GPL v2"); #include +#ifndef PROP_TXSTATUS #define DBUS_RX_BUFFER_SIZE_DHD(net) (net->mtu + net->hard_header_len + dhd->pub.hdrlen) +#else +#define DBUS_RX_BUFFER_SIZE_DHD(net) (net->mtu + net->hard_header_len + dhd->pub.hdrlen + 128) +#endif #if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 15) const char * @@ -218,7 +222,7 @@ typedef struct dhd_info { struct semaphore proto_sem; #ifdef PROP_TXSTATUS spinlock_t wlfc_spinlock; -#endif +#endif /* PROP_TXSTATUS */ #ifdef WLMEDIA_HTSF htsf_t htsf; #endif @@ -258,8 +262,6 @@ typedef struct dhd_info { int wakelock_counter; int wakelock_timeout_enable; - int hang_was_sent; - /* Thread to issue ioctl for multicast */ bool set_macaddress; struct ether_addr macvalue; @@ -498,7 +500,6 @@ static struct notifier_block dhd_sleep_pm_notifier = { extern int register_pm_notifier(struct notifier_block *nb); extern int unregister_pm_notifier(struct notifier_block *nb); #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) */ - /* && defined(DHD_GPL) */ static void dhd_set_packet_filter(int value, dhd_pub_t *dhd) { @@ -976,10 +977,8 @@ dhd_op_if(dhd_if_t *ifp) dhd_net_attach)) { ifp->state = 0; return; - } - + } #endif - if ((err = dhd_net_attach(&dhd->pub, ifp->idx)) != 0) { DHD_ERROR(("%s: dhd_net_attach failed, err %d\n", __FUNCTION__, err)); @@ -1037,7 +1036,7 @@ dhd_op_if(dhd_if_t *ifp) ap_net_dev = NULL; /* NULL SOFTAP global wl0.1 as well */ dhd_os_spin_unlock(&dhd->pub, flags); #endif /* SOFTAP */ - MFREE(dhd->pub.osh, ifp, sizeof(*ifp)); + MFREE(dhd->pub.osh, ifp, sizeof(*ifp)); } } @@ -1413,6 +1412,7 @@ dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt, uint8 chan) continue; } + pnext = PKTNEXT(dhdp->osh, pktbuf); PKTSETNEXT(wl->sh.osh, pktbuf, NULL); @@ -2078,6 +2078,14 @@ dhd_ioctl_entry(struct net_device *net, struct ifreq *ifr, int cmd) goto done; } + /* send to dongle only if we are not waiting for reload already */ + if (dhd->pub.hang_was_sent) { + DHD_ERROR(("%s: HANG was sent up earlier. Not talking to the chip\n", + __FUNCTION__)); + bcmerror = BCME_DONGLE_DOWN; + goto done; + } + /* check for local dhd ioctl and handle it */ if (driver == DHD_IOCTL_MAGIC) { bcmerror = dhd_ioctl((void *)&dhd->pub, &ioc, buf, buflen); @@ -2185,6 +2193,41 @@ done: } static int +dhd_cleanup_virt_ifaces(dhd_info_t *dhd) +{ + int i = 1; /* Leave ifidx 0 [Primary Interface] */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + int rollback_lock = FALSE; +#endif + + DHD_TRACE(("%s: Enter \n", __func__)); + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + /* release lock for unregister_netdev */ + if (rtnl_is_locked()) { + rtnl_unlock(); + rollback_lock = TRUE; + } +#endif + + for (i = 1; i < DHD_MAX_IFS; i++) { + if (dhd->iflist[i]) { + DHD_TRACE(("Deleting IF: %d \n", i)); + dhd->iflist[i]->state = WLC_E_IF_DEL; + dhd->iflist[i]->idx = i; + dhd_op_if(dhd->iflist[i]); + } + } + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + if (rollback_lock) + rtnl_lock(); +#endif + + return 0; +} + +static int dhd_stop(struct net_device *net) { int ifidx; @@ -2197,8 +2240,17 @@ dhd_stop(struct net_device *net) ifidx = dhd_net2idx(dhd, net); #ifdef WL_CFG80211 - if (ifidx == 0) + if (ifidx == 0) { wl_cfg80211_down(); + + /** For CFG80211: Clean up all the left over virtual interfaces + * when the primary Interface is brought down. [ifconfig wlan0 down] + */ + if ((dhd->dhd_state & DHD_ATTACH_STATE_ADD_IF) && + (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211)) { + dhd_cleanup_virt_ifaces(dhd); + } + } #endif #ifdef PROP_TXSTATUS @@ -2238,6 +2290,7 @@ dhd_open(struct net_device *net) strcpy(fw_path, firmware_path); firmware_path[0] = '\0'; } + #if !defined(WL_CFG80211) /** Force start if ifconfig_up gets called before START command * We keep WEXT's wl_control_wl_start to provide backward compatibility @@ -2262,7 +2315,9 @@ dhd_open(struct net_device *net) if (ifidx == 0) { atomic_set(&dhd->pend_8021x_cnt, 0); #if defined(WL_CFG80211) + DHD_ERROR(("\n%s\n", dhd_version)); wl_android_wifi_on(net); + dhd->pub.hang_was_sent = 0; #endif if (dhd->pub.busstate != DHD_BUS_DATA) { @@ -2610,7 +2665,7 @@ dhd_bus_start(dhd_pub_t *dhdp) ASSERT(dhd); - DHD_TRACE(("%s: \n", __FUNCTION__)); + DHD_TRACE(("Enter %s:\n", __FUNCTION__)); dhd_os_sdlock(dhdp); @@ -2706,6 +2761,7 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) #endif int scan_assoc_time = 40; int scan_unassoc_time = 40; + int scan_passive_time = 130; char buf[WLC_IOCTL_SMLEN]; char *ptr; uint32 listen_interval = LISTEN_INTERVAL; /* Default Listen Interval in Beacons */ @@ -2946,14 +3002,16 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) sizeof(scan_assoc_time), TRUE, 0); dhd_wl_ioctl_cmd(dhd, WLC_SET_SCAN_UNASSOC_TIME, (char *)&scan_unassoc_time, sizeof(scan_unassoc_time), TRUE, 0); + dhd_wl_ioctl_cmd(dhd, WLC_SET_SCAN_PASSIVE_TIME, (char *)&scan_passive_time, + sizeof(scan_passive_time), TRUE, 0); #ifdef ARP_OFFLOAD_SUPPORT /* Set and enable ARP offload feature for STA only */ - if (arpoe #if defined(SOFTAP) - && (!ap_fw_loaded) -#endif /* (OEM_ANDROID) && defined(SOFTAP) */ - ) { + if (arpoe && !ap_fw_loaded) { +#else + if (arpoe) { +#endif dhd_arp_offload_set(dhd, dhd_arp_mode); dhd_arp_offload_enable(dhd, arpoe); } else { @@ -2970,7 +3028,6 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) dhd->pktfilter[1] = NULL; dhd->pktfilter[2] = NULL; dhd->pktfilter[3] = NULL; - #if defined(SOFTAP) if (ap_fw_loaded) { int i; @@ -2979,13 +3036,12 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) 0, dhd_master_mode); } } -#endif /* (SOFTAP) */ +#endif /* defined(SOFTAP) */ #endif /* PKT_FILTER_SUPPORT */ /* Force STA UP */ - ret = dhd_wl_ioctl_cmd(dhd, WLC_UP, (char *)&up, sizeof(up), TRUE, 0); - if (ret < 0) - goto done; + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_UP, (char *)&up, sizeof(up), TRUE, 0)) < 0) + DHD_ERROR(("%s Setting WL UP failed %d\n", __FUNCTION__, ret)); done: @@ -3065,7 +3121,8 @@ int dhd_change_mtu(dhd_pub_t *dhdp, int new_mtu, int ifidx) #ifdef ARP_OFFLOAD_SUPPORT /* add or remove AOE host ip(s) (up to 8 IPs on the interface) */ -void aoe_update_host_ipv4_table(dhd_pub_t *dhd_pub, u32 ipa, bool add) +void +aoe_update_host_ipv4_table(dhd_pub_t *dhd_pub, u32 ipa, bool add) { u32 ipv4_buf[MAX_IPV4_ENTRIES]; /* temp save for AOE host_ip table */ int i; @@ -3088,14 +3145,11 @@ void aoe_update_host_ipv4_table(dhd_pub_t *dhd_pub, u32 ipa, bool add) } for (i = 0; i < MAX_IPV4_ENTRIES; i++) { - if (add && (ipv4_buf[i] == 0)) { - - ipv4_buf[i] = ipa; + ipv4_buf[i] = ipa; add = FALSE; /* added ipa to local table */ DHD_ARPOE(("%s: Saved new IP in temp arp_hostip[%d]\n", __FUNCTION__, i)); - } else if (ipv4_buf[i] == ipa) { ipv4_buf[i] = 0; DHD_ARPOE(("%s: removed IP:%x from temp table %d\n", @@ -3160,14 +3214,14 @@ static int dhd_device_event(struct notifier_block *this, #ifdef AOE_IP_ALIAS_SUPPORT if (!(ifa->ifa_label[strlen(ifa->ifa_label)-2] == 0x3a)) { DHD_ARPOE(("%s: primary interface is down, AOE clr all\n", - __FUNCTION__)); + __FUNCTION__)); dhd_aoe_hostip_clr(&dhd->pub); dhd_aoe_arp_clr(&dhd->pub); } else aoe_update_host_ipv4_table(dhd_pub, ifa->ifa_address, FALSE); #else - dhd_aoe_hostip_clr(&dhd->pub); - dhd_aoe_arp_clr(&dhd->pub); + dhd_aoe_hostip_clr(&dhd->pub); + dhd_aoe_arp_clr(&dhd->pub); #endif break; @@ -3221,9 +3275,9 @@ dhd_net_attach(dhd_pub_t *dhdp, int ifidx) net->netdev_ops = &dhd_ops_pri; #endif } else { - /* - * We have to use the primary MAC for virtual interfaces - */ + /* + * We have to use the primary MAC for virtual interfaces + */ memcpy(temp_addr, dhd->iflist[ifidx]->mac_addr, ETHER_ADDR_LEN); /* * Android sets the locally administered bit to indicate that this is a @@ -3362,15 +3416,10 @@ void dhd_detach(dhd_pub_t *dhdp) /* delete all interfaces, start with virtual */ if (dhd->dhd_state & DHD_ATTACH_STATE_ADD_IF) { - int i = 1; dhd_if_t *ifp; - for (i = 1; i < DHD_MAX_IFS; i++) - if (dhd->iflist[i]) { - dhd->iflist[i]->state = WLC_E_IF_DEL; - dhd->iflist[i]->idx = i; - dhd_op_if(dhd->iflist[i]); - } + /* Cleanup all virtual Interfaces */ + dhd_cleanup_virt_ifaces(dhd); /* delete primary interface 0 */ ifp = dhd->iflist[0]; @@ -3428,8 +3477,8 @@ void dhd_detach(dhd_pub_t *dhdp) #endif #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) - unregister_pm_notifier(&dhd_sleep_pm_notifier); -#endif + unregister_pm_notifier(&dhd_sleep_pm_notifier); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) */ if (dhd->dhd_state & DHD_ATTACH_STATE_WAKELOCKS_INIT) { #ifdef CONFIG_HAS_WAKELOCK @@ -3437,7 +3486,6 @@ void dhd_detach(dhd_pub_t *dhdp) wake_lock_destroy(&dhd->wl_rxwake); #endif } - } @@ -3509,7 +3557,6 @@ dhd_module_init(void) #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ error = dhd_bus_register(); - if (!error) printf("\n%s\n", dhd_version); else { @@ -4156,11 +4203,14 @@ int net_os_send_hang_message(struct net_device *dev) int ret = 0; if (dhd) { - if (!dhd->hang_was_sent) { - dhd->hang_was_sent = 1; + if (!dhd->pub.hang_was_sent) { + dhd->pub.hang_was_sent = 1; #if defined(CONFIG_WIRELESS_EXT) ret = wl_iw_send_priv_event(dev, "HANG"); #endif +#if defined(WL_CFG80211) + ret = wl_cfg80211_hang(dev, WLAN_REASON_UNSPECIFIED); +#endif } } return ret; diff --git a/drivers/net/wireless/bcmdhd/dhd_linux_sched.c b/drivers/net/wireless/bcmdhd/dhd_linux_sched.c index 72290b5..aadd122 100644 --- a/drivers/net/wireless/bcmdhd/dhd_linux_sched.c +++ b/drivers/net/wireless/bcmdhd/dhd_linux_sched.c @@ -26,6 +26,7 @@ #include #include #include +#include #include int setScheduler(struct task_struct *p, int policy, struct sched_param *param) diff --git a/drivers/net/wireless/bcmdhd/dhd_sdio.c b/drivers/net/wireless/bcmdhd/dhd_sdio.c index c412edd..cdb2448 100644 --- a/drivers/net/wireless/bcmdhd/dhd_sdio.c +++ b/drivers/net/wireless/bcmdhd/dhd_sdio.c @@ -147,6 +147,8 @@ extern void dhd_wlfc_txcomplete(dhd_pub_t *dhd, void *txp, bool success); #ifdef DHD_DEBUG /* Device console log buffer state */ +#define CONSOLE_LINE_MAX 192 +#define CONSOLE_BUFFER_MAX 2024 typedef struct dhd_console { uint count; /* Poll interval msec counter */ uint log_addr; /* Log struct address (fixed) */ @@ -429,7 +431,7 @@ do { \ * Mode 0: Dongle writes the software host mailbox and host is interrupted. * Mode 1: (sdiod core rev >= 4) * Device sets a new bit in the intstatus whenever there is a packet - * available in fifo. Host can't clear this specific status bit until all the + * available in fifo. Host can't clear this specific status bit until all the * packets are read from the FIFO. No need to ack dongle intstatus. * Mode 2: (sdiod core rev >= 4) * Device sets a bit in the intstatus, and host acks this by writing @@ -463,9 +465,9 @@ static void dhdsdio_sdtest_set(dhd_bus_t *bus, uint8 count); #ifdef DHD_DEBUG static int dhdsdio_checkdied(dhd_bus_t *bus, uint8 *data, uint size); -static int dhdsdio_mem_dump(dhd_bus_t *bus); static int dhd_serialconsole(dhd_bus_t *bus, bool get, bool enable, int *bcmerror); #endif /* DHD_DEBUG */ + static int dhdsdio_download_state(dhd_bus_t *bus, bool enter); static void dhdsdio_release(dhd_bus_t *bus, osl_t *osh); @@ -1374,7 +1376,8 @@ dhd_bus_txctl(struct dhd_bus *bus, uchar *msg, uint msglen) DHD_INFO(("%s: ctrl_frame_stat == FALSE\n", __FUNCTION__)); ret = 0; } else { - DHD_ERROR(("%s: ctrl_frame_stat == TRUE\n", __FUNCTION__)); + if (!bus->dhd->hang_was_sent) + DHD_ERROR(("%s: ctrl_frame_stat == TRUE\n", __FUNCTION__)); ret = -1; bus->ctrl_frame_stat = FALSE; goto done; @@ -1528,9 +1531,9 @@ enum { IOV_SD1IDLE, IOV_SLEEP, IOV_DONGLEISOLATION, - IOV_VARS + IOV_VARS, #ifdef SOFTAP - , IOV_FWPATH + IOV_FWPATH #endif }; @@ -1836,6 +1839,7 @@ dhdsdio_readshared(dhd_bus_t *bus, sdpcm_shared_t *sh) if ((sh->flags & SDPCM_SHARED_VERSION_MASK) == 3 && SDPCM_SHARED_VERSION == 1) return BCME_OK; + if ((sh->flags & SDPCM_SHARED_VERSION_MASK) != SDPCM_SHARED_VERSION) { DHD_ERROR(("%s: sdpcm_shared version %d in dhd " "is different than sdpcm_shared version %d in dongle\n", @@ -1847,7 +1851,6 @@ dhdsdio_readshared(dhd_bus_t *bus, sdpcm_shared_t *sh) return BCME_OK; } -#define CONSOLE_LINE_MAX 192 static int dhdsdio_readconsole(dhd_bus_t *bus) @@ -1925,11 +1928,16 @@ dhdsdio_checkdied(dhd_bus_t *bus, uint8 *data, uint size) int bcmerror = 0; uint msize = 512; char *mbuffer = NULL; + char *console_buffer = NULL; uint maxstrlen = 256; char *str = NULL; trap_t tr; sdpcm_shared_t sdpcm_shared; struct bcmstrbuf strbuf; + uint32 console_ptr, console_size, console_index; + uint8 line[CONSOLE_LINE_MAX], ch; + uint32 n, i, addr; + int rv; DHD_TRACE(("%s: Enter\n", __FUNCTION__)); @@ -2013,86 +2021,76 @@ dhdsdio_checkdied(dhd_bus_t *bus, uint8 *data, uint size) bcm_bprintf(&strbuf, "Dongle trap type 0x%x @ epc 0x%x, cpsr 0x%x, spsr 0x%x, sp 0x%x," "lp 0x%x, rpc 0x%x Trap offset 0x%x, " - "r0 0x%x, r1 0x%x, r2 0x%x, r3 0x%x, r4 0x%x, r5 0x%x, r6 0x%x, r7 0x%x\n", + "r0 0x%x, r1 0x%x, r2 0x%x, r3 0x%x, r4 0x%x, r5 0x%x, r6 0x%x, r7 0x%x\n\n", ltoh32(tr.type), ltoh32(tr.epc), ltoh32(tr.cpsr), ltoh32(tr.spsr), ltoh32(tr.r13), ltoh32(tr.r14), ltoh32(tr.pc), ltoh32(sdpcm_shared.trap_addr), ltoh32(tr.r0), ltoh32(tr.r1), ltoh32(tr.r2), ltoh32(tr.r3), ltoh32(tr.r4), ltoh32(tr.r5), ltoh32(tr.r6), ltoh32(tr.r7)); + + addr = sdpcm_shared.console_addr + OFFSETOF(hndrte_cons_t, log); + if ((rv = dhdsdio_membytes(bus, FALSE, addr, (uint8 *)&console_ptr, sizeof(console_ptr))) < 0) + goto printbuf; + + addr = sdpcm_shared.console_addr + OFFSETOF(hndrte_cons_t, log.buf_size); + if ((rv = dhdsdio_membytes(bus, FALSE, addr, (uint8 *)&console_size, sizeof(console_size))) < 0) + goto printbuf; + + addr = sdpcm_shared.console_addr + OFFSETOF(hndrte_cons_t, log.idx); + if ((rv = dhdsdio_membytes(bus, FALSE, addr, (uint8 *)&console_index, sizeof(console_index))) < 0) + goto printbuf; + + console_ptr = ltoh32(console_ptr); + console_size = ltoh32(console_size); + console_index = ltoh32(console_index); + + if (console_size > CONSOLE_BUFFER_MAX || !(console_buffer = MALLOC(bus->dhd->osh, console_size))) + goto printbuf; + + if ((rv = dhdsdio_membytes(bus, FALSE, console_ptr, (uint8 *)console_buffer, console_size)) < 0) + goto printbuf; + + for ( i = 0, n = 0; i < console_size; i += n + 1 ) { + for (n = 0; n < CONSOLE_LINE_MAX - 2; n++) { + ch = console_buffer[ (console_index + i + n) % console_size]; + if (ch == '\n') + break; + line[n] = ch; + } + + + if (n > 0) { + if (line[n - 1] == '\r') + n--; + line[n] = 0; + /* Don't use DHD_ERROR macro since we print a lot of information quickly */ + /* The macro will truncate a lot of the printfs */ + + if (dhd_msg_level & DHD_ERROR_VAL) + printf("CONSOLE: %s\n", line); + } + } } } +printbuf: if (sdpcm_shared.flags & (SDPCM_SHARED_ASSERT | SDPCM_SHARED_TRAP)) { DHD_ERROR(("%s: %s\n", __FUNCTION__, strbuf.origbuf)); } -#ifdef DHD_DEBUG - if (sdpcm_shared.flags & SDPCM_SHARED_TRAP) { - /* Mem dump to a file on device */ - dhdsdio_mem_dump(bus); - } -#endif /* DHD_DEBUG */ done: if (mbuffer) MFREE(bus->dhd->osh, mbuffer, msize); if (str) MFREE(bus->dhd->osh, str, maxstrlen); + if (console_buffer) + MFREE(bus->dhd->osh, console_buffer, console_size); return bcmerror; } +#endif /* #ifdef DHD_DEBUG */ -static int -dhdsdio_mem_dump(dhd_bus_t *bus) -{ - int ret = 0; - int size; /* Full mem size */ - int start = 0; /* Start address */ - int read_size = 0; /* Read size of each iteration */ - uint8 *buf = NULL, *databuf = NULL; - - /* Get full mem size */ - size = bus->ramsize; - buf = MALLOC(bus->dhd->osh, size); - if (!buf) { - printf("%s: Out of memory (%d bytes)\n", __FUNCTION__, size); - return -1; - } - - /* Read mem content */ - printf("Dump dongle memory"); - databuf = buf; - while (size) - { - read_size = MIN(MEMBLOCK, size); - if ((ret = dhdsdio_membytes(bus, FALSE, start, databuf, read_size))) - { - printf("%s: Error membytes %d\n", __FUNCTION__, ret); - if (buf) { - MFREE(bus->dhd->osh, buf, size); - } - return -1; - } - printf("."); - - /* Decrement size and increment start address */ - size -= read_size; - start += read_size; - databuf += read_size; - } - printf("Done\n"); - - /* free buf before return !!! */ - if (write_to_file(bus->dhd, buf, bus->ramsize)) - { - printf("%s: Error writing to files\n", __FUNCTION__); - return -1; - } - - /* buf free handled in write_to_file, not here */ - return 0; -} -#endif /* defined(DHD_DEBUG) */ int dhdsdio_downloadvars(dhd_bus_t *bus, void *arg, int len) @@ -3040,6 +3038,7 @@ dhd_bus_stop(struct dhd_bus *bus, bool enforce_mutex) dhd_os_sdunlock(bus->dhd); } + int dhd_bus_init(dhd_pub_t *dhdp, bool enforce_mutex) { @@ -4352,6 +4351,13 @@ dhdsdio_hostmail(dhd_bus_t *bus) bus->flowcontrol = fcbits; } +#ifdef DHD_DEBUG + /* At least print a message if FW halted */ + if (hmb_data & HMB_DATA_FWHALT) { + DHD_ERROR(("INTERNAL ERROR: FIRMWARE HALTED\n")); + dhdsdio_checkdied(bus, NULL, 0); + } +#endif /* DHD_DEBUG */ /* Shouldn't be any others */ if (hmb_data & ~(HMB_DATA_DEVREADY | @@ -5176,6 +5182,9 @@ dhdsdio_probe(uint16 venid, uint16 devid, uint16 bus_no, uint16 slot, #ifdef GET_CUSTOM_MAC_ENABLE struct ether_addr ea_addr; #endif /* GET_CUSTOM_MAC_ENABLE */ +#ifdef PROP_TXSTATUS + uint up = 0; +#endif /* Init global variables at run-time, not as part of the declaration. * This is required to support init/de-init of the driver. Initialization @@ -5342,6 +5351,10 @@ dhdsdio_probe(uint16 venid, uint16 devid, uint16 bus_no, uint16 slot, goto fail; } +#ifdef PROP_TXSTATUS + if (dhd_download_fw_on_driverload) + dhd_wl_ioctl_cmd(bus->dhd, WLC_UP, (char *)&up, sizeof(up), TRUE, 0); +#endif return bus; fail: @@ -5916,7 +5929,7 @@ err: return bcmerror; } -/* +/* EXAMPLE: nvram_array nvram_arry format: name=value @@ -6190,16 +6203,16 @@ dhd_bus_devreset(dhd_pub_t *dhdp, uint8 flag) dhd_enable_oob_intr(bus, TRUE); #endif /* defined(OOB_INTR_ONLY) */ - bus->dhd->dongle_reset = FALSE; - bus->dhd->up = TRUE; + bus->dhd->dongle_reset = FALSE; + bus->dhd->up = TRUE; #if !defined(IGNORE_ETH0_DOWN) - /* Restore flow control */ - dhd_txflowcontrol(bus->dhd, ALL_INTERFACES, OFF); + /* Restore flow control */ + dhd_txflowcontrol(bus->dhd, ALL_INTERFACES, OFF); #endif - dhd_os_wd_timer(dhdp, dhd_watchdog_ms); + dhd_os_wd_timer(dhdp, dhd_watchdog_ms); - DHD_TRACE(("%s: WLAN ON DONE\n", __FUNCTION__)); + DHD_TRACE(("%s: WLAN ON DONE\n", __FUNCTION__)); } else { dhd_bus_stop(bus, FALSE); dhdsdio_release_dongle(bus, bus->dhd->osh, diff --git a/drivers/net/wireless/bcmdhd/dhd_wlfc.h b/drivers/net/wireless/bcmdhd/dhd_wlfc.h index 53db62c..59d018b 100644 --- a/drivers/net/wireless/bcmdhd/dhd_wlfc.h +++ b/drivers/net/wireless/bcmdhd/dhd_wlfc.h @@ -201,6 +201,14 @@ typedef struct athost_wl_stat_counters { #define WLFC_FCMODE_IMPLIED_CREDIT 1 #define WLFC_FCMODE_EXPLICIT_CREDIT 2 +#define WLFC_BORROW_DEFER_PERIOD_MS 100 + +/* Mask to represent available ACs (note: BC/MC is ignored */ +#define WLFC_AC_MASK 0xF + +/* Mask to check for only on-going AC_BE traffic */ +#define WLFC_AC_BE_TRAFFIC_ONLY 0xD + typedef struct athost_wl_status_info { uint8 last_seqid_to_wlc; @@ -213,7 +221,11 @@ typedef struct athost_wl_status_info { athost_wl_stat_counters_t stats; /* the additional ones are for bc/mc and ATIM FIFO */ - int FIFO_credit[AC_COUNT + 2]; + int FIFO_credit[AC_COUNT + 2]; + + /* Credit borrow counts for each FIFO from each of the other FIFOs */ + int credits_borrowed[AC_COUNT + 2][AC_COUNT + 2]; + struct pktq SENDQ; /* packet hanger and MAC->handle lookup table */ @@ -228,7 +240,7 @@ typedef struct athost_wl_status_info { wlfc_mac_descriptor_t other; } destination_entries; /* token position for different priority packets */ - uint8 token_pos[AC_COUNT]; + uint8 token_pos[AC_COUNT+1]; /* ON/OFF state for flow control to the host network interface */ uint8 hostif_flow_state[WLFC_MAX_IFNUM]; uint8 host_ifidx; @@ -243,6 +255,12 @@ typedef struct athost_wl_status_info { 2 - Use explicit credit */ uint8 proptxstatus_mode; + + /* To borrow credits */ + uint8 allow_credit_borrow; + + /* Timestamp to compute how long to defer borrowing for */ + uint32 borrow_defer_timestamp; } athost_wl_status_info_t; #endif /* __wlfc_host_driver_definitions_h__ */ diff --git a/drivers/net/wireless/bcmdhd/include/epivers.h b/drivers/net/wireless/bcmdhd/include/epivers.h index ec060c9..ab96c07 100644 --- a/drivers/net/wireless/bcmdhd/include/epivers.h +++ b/drivers/net/wireless/bcmdhd/include/epivers.h @@ -33,17 +33,17 @@ #define EPI_RC_NUMBER 125 -#define EPI_INCREMENTAL_NUMBER 74 +#define EPI_INCREMENTAL_NUMBER 78 #define EPI_BUILD_NUMBER 0 -#define EPI_VERSION 5, 90, 125, 74 +#define EPI_VERSION 5, 90, 125, 78 -#define EPI_VERSION_NUM 0x055a7d4a +#define EPI_VERSION_NUM 0x055a7d4e #define EPI_VERSION_DEV 5.90.125 -#define EPI_VERSION_STR "5.90.125.74" +#define EPI_VERSION_STR "5.90.125.78" #endif diff --git a/drivers/net/wireless/bcmdhd/linux_osl.c b/drivers/net/wireless/bcmdhd/linux_osl.c index 239b15d..17b68e5 100644 --- a/drivers/net/wireless/bcmdhd/linux_osl.c +++ b/drivers/net/wireless/bcmdhd/linux_osl.c @@ -48,23 +48,25 @@ #define BCM_MEM_FILENAME_LEN 24 #ifdef DHD_USE_STATIC_BUF -#define MAX_STATIC_BUF_NUM 16 -#define STATIC_BUF_SIZE (PAGE_SIZE*2) -#define STATIC_BUF_TOTAL_LEN (MAX_STATIC_BUF_NUM*STATIC_BUF_SIZE) +#define STATIC_BUF_MAX_NUM 16 +#define STATIC_BUF_SIZE (PAGE_SIZE * 2) +#define STATIC_BUF_TOTAL_LEN (STATIC_BUF_MAX_NUM * STATIC_BUF_SIZE) + typedef struct bcm_static_buf { struct semaphore static_sem; unsigned char *buf_ptr; - unsigned char buf_use[MAX_STATIC_BUF_NUM]; + unsigned char buf_use[STATIC_BUF_MAX_NUM]; } bcm_static_buf_t; static bcm_static_buf_t *bcm_static_buf = 0; -#define MAX_STATIC_PKT_NUM 8 +#define STATIC_PKT_MAX_NUM 8 + typedef struct bcm_static_pkt { - struct sk_buff *skb_4k[MAX_STATIC_PKT_NUM]; - struct sk_buff *skb_8k[MAX_STATIC_PKT_NUM]; + struct sk_buff *skb_4k[STATIC_PKT_MAX_NUM]; + struct sk_buff *skb_8k[STATIC_PKT_MAX_NUM]; struct semaphore osl_pkt_sem; - unsigned char pkt_use[MAX_STATIC_PKT_NUM*2]; + unsigned char pkt_use[STATIC_PKT_MAX_NUM * 2]; } bcm_static_pkt_t; static bcm_static_pkt_t *bcm_static_skb = 0; #endif @@ -228,8 +230,8 @@ osl_attach(void *pdev, uint bustype, bool pkttag) bcm_static_skb = (bcm_static_pkt_t *)((char *)bcm_static_buf + 2048); skb_buff_ptr = dhd_os_prealloc(osh, 4, 0); - bcopy(skb_buff_ptr, bcm_static_skb, sizeof(struct sk_buff *)*16); - for (i = 0; i < MAX_STATIC_PKT_NUM*2; i++) + bcopy(skb_buff_ptr, bcm_static_skb, sizeof(struct sk_buff *) * 16); + for (i = 0; i < STATIC_PKT_MAX_NUM * 2; i++) bcm_static_skb->pkt_use[i] = 0; sema_init(&bcm_static_skb->osl_pkt_sem, 1); @@ -548,84 +550,69 @@ osl_pktget_static(osl_t *osh, uint len) struct sk_buff *skb; - if (len > (PAGE_SIZE*2)) - { + if (len > (PAGE_SIZE * 2)) { + printk("%s: attempt to allocate huge packet (0x%x)\n", __FUNCTION__, len); printk("Do we really need this big skb??\n"); return osl_pktget(osh, len); } down(&bcm_static_skb->osl_pkt_sem); - if (len <= PAGE_SIZE) - { - for (i = 0; i < MAX_STATIC_PKT_NUM; i++) - { + if (len <= PAGE_SIZE) { + for (i = 0; i < STATIC_PKT_MAX_NUM; i++) { if (bcm_static_skb->pkt_use[i] == 0) break; } - if (i != MAX_STATIC_PKT_NUM) - { + if (i != STATIC_PKT_MAX_NUM) { bcm_static_skb->pkt_use[i] = 1; up(&bcm_static_skb->osl_pkt_sem); - skb = bcm_static_skb->skb_4k[i]; skb->tail = skb->data + len; skb->len = len; - return skb; } } - for (i = 0; i < MAX_STATIC_PKT_NUM; i++) - { - if (bcm_static_skb->pkt_use[i+MAX_STATIC_PKT_NUM] == 0) + for (i = 0; i < STATIC_PKT_MAX_NUM; i++) { + if (bcm_static_skb->pkt_use[i+STATIC_PKT_MAX_NUM] == 0) break; } - if (i != MAX_STATIC_PKT_NUM) - { - bcm_static_skb->pkt_use[i+MAX_STATIC_PKT_NUM] = 1; + if (i != STATIC_PKT_MAX_NUM) { + bcm_static_skb->pkt_use[i+STATIC_PKT_MAX_NUM] = 1; up(&bcm_static_skb->osl_pkt_sem); skb = bcm_static_skb->skb_8k[i]; skb->tail = skb->data + len; skb->len = len; - return skb; } - up(&bcm_static_skb->osl_pkt_sem); - printk("all static pkt in use!\n"); + printk("%s: all static pkt in use!\n", __FUNCTION__); return osl_pktget(osh, len); } - void osl_pktfree_static(osl_t *osh, void *p, bool send) { int i; - for (i = 0; i < MAX_STATIC_PKT_NUM; i++) - { - if (p == bcm_static_skb->skb_4k[i]) - { + for (i = 0; i < STATIC_PKT_MAX_NUM; i++) { + if (p == bcm_static_skb->skb_4k[i]) { down(&bcm_static_skb->osl_pkt_sem); bcm_static_skb->pkt_use[i] = 0; up(&bcm_static_skb->osl_pkt_sem); - return; } } - for (i = 0; i < MAX_STATIC_PKT_NUM; i++) - { - if (p == bcm_static_skb->skb_8k[i]) - { + for (i = 0; i < STATIC_PKT_MAX_NUM; i++) { + if (p == bcm_static_skb->skb_8k[i]) { down(&bcm_static_skb->osl_pkt_sem); - bcm_static_skb->pkt_use[i + MAX_STATIC_PKT_NUM] = 0; + bcm_static_skb->pkt_use[i + STATIC_PKT_MAX_NUM] = 0; up(&bcm_static_skb->osl_pkt_sem); return; diff --git a/drivers/net/wireless/bcmdhd/wl_android.c b/drivers/net/wireless/bcmdhd/wl_android.c index cefa7f9..36110f95 100644 --- a/drivers/net/wireless/bcmdhd/wl_android.c +++ b/drivers/net/wireless/bcmdhd/wl_android.c @@ -108,6 +108,7 @@ uint dhd_dev_reset(struct net_device *dev, uint8 flag); void dhd_dev_init_ioctl(struct net_device *dev); #ifdef WL_CFG80211 int wl_cfg80211_get_p2p_dev_addr(struct net_device *net, struct ether_addr *p2pdev_addr); +int wl_cfg80211_set_btcoex_dhcp(struct net_device *dev, char *command); #else int wl_cfg80211_get_p2p_dev_addr(struct net_device *net, struct ether_addr *p2pdev_addr) { return 0; } #endif @@ -454,6 +455,9 @@ int wl_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd) net_os_set_packet_filter(net, 0); /* DHCP starts */ else net_os_set_packet_filter(net, 1); /* DHCP ends */ +#ifdef WL_CFG80211 + bytes_written = wl_cfg80211_set_btcoex_dhcp(net, command); +#endif } else if (strnicmp(command, CMD_SETSUSPENDOPT, strlen(CMD_SETSUSPENDOPT)) == 0) { bytes_written = wl_android_set_suspendopt(net, command, priv_cmd.total_len); diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.c b/drivers/net/wireless/bcmdhd/wl_cfg80211.c index 08a9a04..8a77231 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.c +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.c @@ -123,6 +123,32 @@ static ctl_table wl_sysctl_table[] = { static struct ctl_table_header *wl_sysctl_hdr; #endif /* CONFIG_SYSCTL */ +#define COEX_DHCP + +#if defined(COEX_DHCP) +#define BT_DHCP_eSCO_FIX /* use New SCO/eSCO smart YG + * suppression + */ +#define BT_DHCP_USE_FLAGS /* this flag boost wifi pkt priority + * to max, caution: -not fair to sco + */ +#define BT_DHCP_OPPR_WIN_TIME 2500 /* T1 start SCO/ESCo priority + * suppression + */ +#define BT_DHCP_FLAG_FORCE_TIME 5500 /* T2 turn off SCO/SCO supperesion + * is (timeout) + */ +enum wl_cfg80211_btcoex_status { + BT_DHCP_IDLE, + BT_DHCP_START, + BT_DHCP_OPPR_WIN, + BT_DHCP_FLAG_FORCE_TIMEOUT +}; + +static int wl_cfg80211_btcoex_init(struct wl_priv *wl); +static void wl_cfg80211_btcoex_deinit(struct wl_priv *wl); +#endif + /* This is to override regulatory domains defined in cfg80211 module (reg.c) * By default world regulatory domain defined in reg.c puts the flags NL80211_RRF_PASSIVE_SCAN * and NL80211_RRF_NO_IBSS for 5GHz channels (for 36..48 and 149..165). @@ -909,7 +935,6 @@ fail: return ERR_PTR(-ENODEV); } - static s32 wl_cfg80211_del_virtual_iface(struct wiphy *wiphy, struct net_device *dev) { @@ -944,7 +969,6 @@ wl_cfg80211_del_virtual_iface(struct wiphy *wiphy, struct net_device *dev) timeout = wait_event_interruptible_timeout(wl->dongle_event_wait, (wl->scan_request == false), msecs_to_jiffies(MAX_WAIT_TIME)); - if (timeout > 0 && (!wl->scan_request)) { WL_DBG(("IFDEL Operations Done")); } else { @@ -1063,6 +1087,7 @@ s32 wl_cfg80211_ifdel_ops(struct net_device *net) { struct wl_priv *wl = wlcfg_drv_priv; + int rollback_lock = FALSE; if (!net || !net->name) { WL_DBG(("net is NULL\n")); @@ -1073,10 +1098,15 @@ wl_cfg80211_ifdel_ops(struct net_device *net) /* Abort any pending scan requests */ wl->escan_info.escan_state = WL_ESCAN_STATE_IDLE; - rtnl_lock(); + if (!rtnl_is_locked()) { + rtnl_lock(); + rollback_lock = TRUE; + } WL_INFO(("ESCAN COMPLETED\n")); wl_notify_escan_complete(wl, true); - rtnl_unlock(); + + if (rollback_lock) + rtnl_unlock(); } /* Wake up any waiting thread */ @@ -1093,7 +1123,6 @@ wl_cfg80211_notify_ifdel(struct net_device *net) if (wl->p2p->vif_created) { s32 index = 0; - WL_DBG(("IF_DEL event called from dongle, net %x, vif name: %s\n", (unsigned int)net, wl->p2p->vir_ifname)); @@ -1459,6 +1488,7 @@ __wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, struct wl_priv *wl = wiphy_priv(wiphy); struct cfg80211_ssid *ssids; struct wl_scan_req *sr = wl_to_sr(wl); + wpa_ie_fixed_t *wps_ie; s32 passive_scan; bool iscan_req; bool escan_req; @@ -1466,7 +1496,8 @@ __wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, bool p2p_ssid; s32 err = 0; s32 i; - + u32 wpsie_len = 0; + u8 wpsie[IE_MAX_LEN]; if (unlikely(wl_get_drv_status(wl, SCANNING))) { WL_ERR(("Scanning already : status (%d)\n", (int)wl->status)); return -EAGAIN; @@ -1531,6 +1562,26 @@ __wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, } } } + if (!wl->p2p_supported || !p2p_scan(wl)) { + if (ndev == wl_to_prmry_ndev(wl)) { + /* find the WPSIE */ + memset(wpsie, 0, sizeof(wpsie)); + if ((wps_ie = wl_cfgp2p_find_wpsie( + (u8 *)request->ie, + request->ie_len)) != NULL) { + wpsie_len = + wps_ie->length + WPA_RSN_IE_TAG_FIXED_LEN; + memcpy(wpsie, wps_ie, wpsie_len); + } else { + wpsie_len = 0; + } + err = wl_cfgp2p_set_management_ie(wl, ndev, -1, + VNDR_IE_PRBREQ_FLAG, wpsie, wpsie_len); + if (unlikely(err)) { + goto scan_out; + } + } + } } } } else { /* scan in ibss */ @@ -1615,6 +1666,7 @@ wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, WL_DBG(("Enter \n")); CHECK_SYS_UP(wl); + err = __wl_cfg80211_scan(wiphy, ndev, request, NULL); if (unlikely(err)) { WL_ERR(("scan error (%d)\n", err)); @@ -2100,9 +2152,12 @@ wl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, size_t join_params_size; s32 err = 0; wpa_ie_fixed_t *wpa_ie; + wpa_ie_fixed_t *wps_ie; bcm_tlv_t *wpa2_ie; u8* wpaie = 0; u32 wpaie_len = 0; + u32 wpsie_len = 0; + u8 wpsie[IE_MAX_LEN]; WL_DBG(("In\n")); CHECK_SYS_UP(wl); @@ -2126,7 +2181,7 @@ wl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, wl_cfgp2p_set_management_ie(wl, dev, wl_cfgp2p_find_idx(wl, dev), VNDR_IE_ASSOCREQ_FLAG, sme->ie, sme->ie_len); } else if (p2p_on(wl) && (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2)) { - /* This is the connect req after WPS is done [credentials exchanged] + /* This is the connect req after WPS is done [credentials exchanged] * currently identified with WPA_VERSION_2 . * Update the previously set IEs with * the newly received IEs from Supplicant. This will remove the WPS IE from @@ -2158,8 +2213,21 @@ wl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, ioctlbuf, sizeof(ioctlbuf)); } + /* find the WPSIE */ + memset(wpsie, 0, sizeof(wpsie)); + if ((wps_ie = wl_cfgp2p_find_wpsie((u8 *)sme->ie, + sme->ie_len)) != NULL) { + wpsie_len = wps_ie->length +WPA_RSN_IE_TAG_FIXED_LEN; + memcpy(wpsie, wps_ie, wpsie_len); + } else { + wpsie_len = 0; + } + err = wl_cfgp2p_set_management_ie(wl, dev, -1, + VNDR_IE_ASSOCREQ_FLAG, wpsie, wpsie_len); + if (unlikely(err)) { + return err; + } } - if (unlikely(!sme->ssid)) { WL_ERR(("Invalid ssid\n")); return -EOPNOTSUPP; @@ -2594,17 +2662,6 @@ wl_cfg80211_del_key(struct wiphy *wiphy, struct net_device *dev, } return err; } - -#ifdef NOT_YET - /* TODO: Removed in P2P twig, check later --lin */ - val = 0; /* assume open key. otherwise 1 */ - val = htod32(val); - err = wldev_ioctl(dev, WLC_SET_AUTH, &val, sizeof(val), false); - if (unlikely(err)) { - WL_ERR(("WLC_SET_AUTH error (%d)\n", err)); - return err; - } -#endif return err; } @@ -3145,9 +3202,6 @@ wl_cfg80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, wl_cfgp2p_set_management_ie(wl, dev, bssidx, VNDR_IE_PRBRSP_FLAG, (u8 *)wps_ie, wpsie_len + p2pie_len); - /* remove WLC_E_PROBREQ_MSG event to prevent HOSTAPD - * from responding many probe request - */ } } cfg80211_mgmt_tx_status(dev, *cookie, buf, len, true, GFP_KERNEL); @@ -3198,7 +3252,7 @@ wl_cfg80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, wifi_p2p_pub_act_frame_t *act_frm = (wifi_p2p_pub_act_frame_t *) (action_frame->data); /* - * To make sure to send successfully action frame, we have to turn off mpc + * To make sure to send successfully action frame, we have to turn off mpc */ if ((act_frm->subtype == P2P_PAF_GON_REQ)|| (act_frm->subtype == P2P_PAF_GON_RSP)) { @@ -3702,8 +3756,11 @@ wl_cfg80211_set_beacon(struct wiphy *wiphy, struct net_device *dev, } else { WL_DBG(("No WPSIE in beacon \n")); } - wldev_ioctl(dev, WLC_UP, &ap, sizeof(s32), false); - + err = wldev_ioctl(dev, WLC_UP, &ap, sizeof(s32), false); + if (unlikely(err)) { + WL_ERR(("WLC_UP error (%d)\n", err)); + return err; + } memset(&join_params, 0, sizeof(join_params)); /* join parameters starts with ssid */ join_params_size = sizeof(join_params.ssid); @@ -4222,7 +4279,7 @@ wl_notify_connect_status(struct wl_priv *wl, struct net_device *ndev, } else { WL_DBG(("wl_notify_connect_status : event %d status : %d \n", - ntoh32(e->event_type), ntoh32(e->status))); + ntoh32(e->event_type), ntoh32(e->status))); if (wl_is_linkup(wl, e, ndev)) { wl_link_up(wl); act = true; @@ -4859,7 +4916,11 @@ static s32 wl_init_priv_mem(struct wl_priv *wl) WL_ERR(("pmk list alloc failed\n")); goto init_priv_mem_out; } - + wl->sta_info = (void *)kzalloc(sizeof(*wl->sta_info), GFP_KERNEL); + if (unlikely(!wl->sta_info)) { + WL_ERR(("sta info alloc failed\n")); + goto init_priv_mem_out; + } return 0; init_priv_mem_out: @@ -4892,6 +4953,8 @@ static void wl_deinit_priv_mem(struct wl_priv *wl) wl->fw = NULL; kfree(wl->pmk_list); wl->pmk_list = NULL; + kfree(wl->sta_info); + wl->sta_info = NULL; if (wl->ap_info) { kfree(wl->ap_info->wpa_ie); kfree(wl->ap_info->rsn_ie); @@ -5428,6 +5491,11 @@ s32 wl_cfg80211_attach(struct net_device *ndev, void *data) goto cfg80211_attach_out; } #endif +#if defined(COEX_DHCP) + if (wl_cfg80211_btcoex_init(wl)) + goto cfg80211_attach_out; +#endif /* COEX_DHCP */ + wlcfg_drv_priv = wl; return err; @@ -5444,6 +5512,11 @@ void wl_cfg80211_detach(void) wl = wlcfg_drv_priv; WL_TRACE(("In\n")); + +#if defined(COEX_DHCP) + wl_cfg80211_btcoex_deinit(wl); +#endif /* COEX_DHCP */ + #if defined(DHD_P2P_DEV_ADDR_FROM_SYSFS) && defined(CONFIG_SYSCTL) if (wl_sysctl_hdr) unregister_sysctl_table(wl_sysctl_hdr); @@ -6120,6 +6193,20 @@ s32 wl_cfg80211_up(void) return err; } +/* Private Event to Supplicant with indication that FW hangs */ +int wl_cfg80211_hang(struct net_device *dev, u16 reason) +{ + struct wl_priv *wl; + wl = wlcfg_drv_priv; + + WL_ERR(("In : FW crash Eventing\n")); + cfg80211_disconnected(dev, reason, NULL, 0, GFP_KERNEL); + if (wl != NULL) { + wl_link_down(wl); + } + return 0; +} + s32 wl_cfg80211_down(void) { struct wl_priv *wl; @@ -6547,3 +6634,480 @@ static int wl_setup_rfkill(struct wl_priv *wl, bool setup) err_out: return err; } + +#if defined(COEX_DHCP) +/* + * get named driver variable to uint register value and return error indication + * calling example: dev_wlc_intvar_get_reg(dev, "btc_params",66, ®_value) + */ +static int +dev_wlc_intvar_get_reg(struct net_device *dev, char *name, + uint reg, int *retval) +{ + union { + char buf[WLC_IOCTL_SMLEN]; + int val; + } var; + int error; + + bcm_mkiovar(name, (char *)(®), sizeof(reg), + (char *)(&var), sizeof(var.buf)); + error = wldev_ioctl(dev, WLC_GET_VAR, (char *)(&var), sizeof(var.buf), false); + + *retval = dtoh32(var.val); + return (error); +} + +static int +dev_wlc_bufvar_set(struct net_device *dev, char *name, char *buf, int len) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) + char ioctlbuf[1024]; +#else + static char ioctlbuf[1024]; +#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) */ + + bcm_mkiovar(name, buf, len, ioctlbuf, sizeof(ioctlbuf)); + + return (wldev_ioctl(dev, WLC_SET_VAR, ioctlbuf, sizeof(ioctlbuf), false)); +} +/* +get named driver variable to uint register value and return error indication +calling example: dev_wlc_intvar_set_reg(dev, "btc_params",66, value) +*/ +static int +dev_wlc_intvar_set_reg(struct net_device *dev, char *name, char *addr, char * val) +{ + char reg_addr[8]; + + memset(reg_addr, 0, sizeof(reg_addr)); + memcpy((char *)®_addr[0], (char *)addr, 4); + memcpy((char *)®_addr[4], (char *)val, 4); + + return (dev_wlc_bufvar_set(dev, name, (char *)®_addr[0], sizeof(reg_addr))); +} + +static bool btcoex_is_sco_active(struct net_device *dev) +{ + int ioc_res = 0; + bool res = FALSE; + int sco_id_cnt = 0; + int param27; + int i; + + for (i = 0; i < 12; i++) { + + ioc_res = dev_wlc_intvar_get_reg(dev, "btc_params", 27, ¶m27); + + WL_INFO(("%s, sample[%d], btc params: 27:%x\n", + __FUNCTION__, i, param27)); + + if (ioc_res < 0) { + WL_ERR(("%s ioc read btc params error\n", __FUNCTION__)); + break; + } + + if ((param27 & 0x6) == 2) { /* count both sco & esco */ + sco_id_cnt++; + } + + if (sco_id_cnt > 2) { + WL_INFO(("%s, sco/esco detected, pkt id_cnt:%d samples:%d\n", + __FUNCTION__, sco_id_cnt, i)); + res = TRUE; + break; + } + + msleep(5); + } + + return res; +} + +#if defined(BT_DHCP_eSCO_FIX) +/* Enhanced BT COEX settings for eSCO compatibility during DHCP window */ +static int set_btc_esco_params(struct net_device *dev, bool trump_sco) +{ + static bool saved_status = FALSE; + + char buf_reg50va_dhcp_on[8] = + { 50, 00, 00, 00, 0x22, 0x80, 0x00, 0x00 }; + char buf_reg51va_dhcp_on[8] = + { 51, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 }; + char buf_reg64va_dhcp_on[8] = + { 64, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 }; + char buf_reg65va_dhcp_on[8] = + { 65, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 }; + char buf_reg71va_dhcp_on[8] = + { 71, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 }; + uint32 regaddr; + static uint32 saved_reg50; + static uint32 saved_reg51; + static uint32 saved_reg64; + static uint32 saved_reg65; + static uint32 saved_reg71; + + if (trump_sco) { + /* this should reduce eSCO agressive retransmit + * w/o breaking it + */ + + /* 1st save current */ + WL_INFO(("Do new SCO/eSCO coex algo {save &" + "override}\n")); + if ((!dev_wlc_intvar_get_reg(dev, "btc_params", 50, &saved_reg50)) && + (!dev_wlc_intvar_get_reg(dev, "btc_params", 51, &saved_reg51)) && + (!dev_wlc_intvar_get_reg(dev, "btc_params", 64, &saved_reg64)) && + (!dev_wlc_intvar_get_reg(dev, "btc_params", 65, &saved_reg65)) && + (!dev_wlc_intvar_get_reg(dev, "btc_params", 71, &saved_reg71))) { + saved_status = TRUE; + WL_INFO(("%s saved bt_params[50,51,64,65,71]:" + "0x%x 0x%x 0x%x 0x%x 0x%x\n", + __FUNCTION__, saved_reg50, saved_reg51, + saved_reg64, saved_reg65, saved_reg71)); + } else { + WL_ERR((":%s: save btc_params failed\n", + __FUNCTION__)); + saved_status = FALSE; + return -1; + } + + WL_INFO(("override with [50,51,64,65,71]:" + "0x%x 0x%x 0x%x 0x%x 0x%x\n", + *(u32 *)(buf_reg50va_dhcp_on+4), + *(u32 *)(buf_reg51va_dhcp_on+4), + *(u32 *)(buf_reg64va_dhcp_on+4), + *(u32 *)(buf_reg65va_dhcp_on+4), + *(u32 *)(buf_reg71va_dhcp_on+4))); + + dev_wlc_bufvar_set(dev, "btc_params", + (char *)&buf_reg50va_dhcp_on[0], 8); + dev_wlc_bufvar_set(dev, "btc_params", + (char *)&buf_reg51va_dhcp_on[0], 8); + dev_wlc_bufvar_set(dev, "btc_params", + (char *)&buf_reg64va_dhcp_on[0], 8); + dev_wlc_bufvar_set(dev, "btc_params", + (char *)&buf_reg65va_dhcp_on[0], 8); + dev_wlc_bufvar_set(dev, "btc_params", + (char *)&buf_reg71va_dhcp_on[0], 8); + + saved_status = TRUE; + } else if (saved_status) { + /* restore previously saved bt params */ + WL_INFO(("Do new SCO/eSCO coex algo {save &" + "override}\n")); + + regaddr = 50; + dev_wlc_intvar_set_reg(dev, "btc_params", + (char *)®addr, (char *)&saved_reg50); + regaddr = 51; + dev_wlc_intvar_set_reg(dev, "btc_params", + (char *)®addr, (char *)&saved_reg51); + regaddr = 64; + dev_wlc_intvar_set_reg(dev, "btc_params", + (char *)®addr, (char *)&saved_reg64); + regaddr = 65; + dev_wlc_intvar_set_reg(dev, "btc_params", + (char *)®addr, (char *)&saved_reg65); + regaddr = 71; + dev_wlc_intvar_set_reg(dev, "btc_params", + (char *)®addr, (char *)&saved_reg71); + + WL_INFO(("restore bt_params[50,51,64,65,71]:" + "0x%x 0x%x 0x%x 0x%x 0x%x\n", + saved_reg50, saved_reg51, saved_reg64, + saved_reg65, saved_reg71)); + + saved_status = FALSE; + } else { + WL_ERR((":%s att to restore not saved BTCOEX params\n", + __FUNCTION__)); + return -1; + } + return 0; +} +#endif /* BT_DHCP_eSCO_FIX */ + +static void +wl_cfg80211_bt_setflag(struct net_device *dev, bool set) +{ +#if defined(BT_DHCP_USE_FLAGS) + char buf_flag7_dhcp_on[8] = { 7, 00, 00, 00, 0x1, 0x0, 0x00, 0x00 }; + char buf_flag7_default[8] = { 7, 00, 00, 00, 0x0, 0x00, 0x00, 0x00}; +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + rtnl_lock(); +#endif + +#if defined(BT_DHCP_eSCO_FIX) + /* set = 1, save & turn on 0 - off & restore prev settings */ + set_btc_esco_params(dev, set); +#endif + +#if defined(BT_DHCP_USE_FLAGS) + WL_TRACE(("WI-FI priority boost via bt flags, set:%d\n", set)); + if (set == TRUE) + /* Forcing bt_flag7 */ + dev_wlc_bufvar_set(dev, "btc_flags", + (char *)&buf_flag7_dhcp_on[0], + sizeof(buf_flag7_dhcp_on)); + else + /* Restoring default bt flag7 */ + dev_wlc_bufvar_set(dev, "btc_flags", + (char *)&buf_flag7_default[0], + sizeof(buf_flag7_default)); +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + rtnl_unlock(); +#endif +} + +static void wl_cfg80211_bt_timerfunc(ulong data) +{ + struct btcoex_info *bt_local = (struct btcoex_info *)data; + WL_TRACE(("%s\n", __FUNCTION__)); + bt_local->timer_on = 0; + schedule_work(&bt_local->work); +} + +static void wl_cfg80211_bt_handler(struct work_struct *work) +{ + struct btcoex_info *btcx_inf; + + btcx_inf = container_of(work, struct btcoex_info, work); + + if (btcx_inf->timer_on) { + btcx_inf->timer_on = 0; + del_timer_sync(&btcx_inf->timer); + } + + switch (btcx_inf->bt_state) { + case BT_DHCP_START: + /* DHCP started + * provide OPPORTUNITY window to get DHCP address + */ + WL_TRACE(("%s bt_dhcp stm: started \n", + __FUNCTION__)); + btcx_inf->bt_state = BT_DHCP_OPPR_WIN; + mod_timer(&btcx_inf->timer, + jiffies + BT_DHCP_OPPR_WIN_TIME*HZ/1000); + btcx_inf->timer_on = 1; + break; + + case BT_DHCP_OPPR_WIN: + if (btcx_inf->dhcp_done) { + WL_TRACE(("%s DHCP Done before T1 expiration\n", + __FUNCTION__)); + goto btc_coex_idle; + } + + /* DHCP is not over yet, start lowering BT priority + * enforce btc_params + flags if necessary + */ + WL_TRACE(("%s DHCP T1:%d expired\n", __FUNCTION__, + BT_DHCP_OPPR_WIN_TIME)); + if (btcx_inf->dev) + wl_cfg80211_bt_setflag(btcx_inf->dev, TRUE); + btcx_inf->bt_state = BT_DHCP_FLAG_FORCE_TIMEOUT; + mod_timer(&btcx_inf->timer, + jiffies + BT_DHCP_FLAG_FORCE_TIME*HZ/1000); + btcx_inf->timer_on = 1; + break; + + case BT_DHCP_FLAG_FORCE_TIMEOUT: + if (btcx_inf->dhcp_done) { + WL_TRACE(("%s DHCP Done before T2 expiration\n", + __FUNCTION__)); + } else { + /* Noo dhcp during T1+T2, restore BT priority */ + WL_TRACE(("%s DHCP wait interval T2:%d" + "msec expired\n", __FUNCTION__, + BT_DHCP_FLAG_FORCE_TIME)); + } + + /* Restoring default bt priority */ + if (btcx_inf->dev) + wl_cfg80211_bt_setflag(btcx_inf->dev, FALSE); +btc_coex_idle: + btcx_inf->bt_state = BT_DHCP_IDLE; + btcx_inf->timer_on = 0; + break; + + default: + WL_ERR(("%s error g_status=%d !!!\n", __FUNCTION__, + btcx_inf->bt_state)); + if (btcx_inf->dev) + wl_cfg80211_bt_setflag(btcx_inf->dev, FALSE); + btcx_inf->bt_state = BT_DHCP_IDLE; + btcx_inf->timer_on = 0; + break; + } + + net_os_wake_unlock(btcx_inf->dev); +} + +static int wl_cfg80211_btcoex_init(struct wl_priv *wl) +{ + struct btcoex_info *btco_inf = NULL; + + btco_inf = kmalloc(sizeof(struct btcoex_info), GFP_KERNEL); + if (!btco_inf) + return -ENOMEM; + + btco_inf->bt_state = BT_DHCP_IDLE; + btco_inf->ts_dhcp_start = 0; + btco_inf->ts_dhcp_ok = 0; + /* Set up timer for BT */ + btco_inf->timer_ms = 10; + init_timer(&btco_inf->timer); + btco_inf->timer.data = (ulong)btco_inf; + btco_inf->timer.function = wl_cfg80211_bt_timerfunc; + + btco_inf->dev = wl->wdev->netdev; + + INIT_WORK(&btco_inf->work, wl_cfg80211_bt_handler); + + wl->btcoex_info = btco_inf; + return 0; +} + +static void +wl_cfg80211_btcoex_deinit(struct wl_priv *wl) +{ + if (!wl->btcoex_info) + return; + + if (!wl->btcoex_info->timer_on) { + wl->btcoex_info->timer_on = 0; + del_timer_sync(&wl->btcoex_info->timer); + } + + cancel_work_sync(&wl->btcoex_info->work); + + kfree(wl->btcoex_info); + wl->btcoex_info = NULL; +} +#endif /* OEM_ANDROID */ + +int wl_cfg80211_set_btcoex_dhcp(struct net_device *dev, char *command) +{ + char powermode_val = 0; + char buf_reg66va_dhcp_on[8] = { 66, 00, 00, 00, 0x10, 0x27, 0x00, 0x00 }; + char buf_reg41va_dhcp_on[8] = { 41, 00, 00, 00, 0x33, 0x00, 0x00, 0x00 }; + char buf_reg68va_dhcp_on[8] = { 68, 00, 00, 00, 0x90, 0x01, 0x00, 0x00 }; + + uint32 regaddr; + static uint32 saved_reg66; + static uint32 saved_reg41; + static uint32 saved_reg68; + static bool saved_status = FALSE; + +#ifdef COEX_DHCP + char buf_flag7_default[8] = { 7, 00, 00, 00, 0x0, 0x00, 0x00, 0x00}; + struct btcoex_info *btco_inf = wlcfg_drv_priv->btcoex_info; +#endif /* COEX_DHCP */ + + /* Figure out powermode 1 or o command */ + strncpy((char *)&powermode_val, command + strlen("BTCOEXMODE") +1, 1); + + if (strnicmp((char *)&powermode_val, "1", strlen("1")) == 0) { + + WL_TRACE(("%s: DHCP session starts\n", __FUNCTION__)); + + /* Retrieve and saved orig regs value */ + if ((saved_status == FALSE) && + (!dev_wlc_intvar_get_reg(dev, "btc_params", 66, &saved_reg66)) && + (!dev_wlc_intvar_get_reg(dev, "btc_params", 41, &saved_reg41)) && + (!dev_wlc_intvar_get_reg(dev, "btc_params", 68, &saved_reg68))) { + saved_status = TRUE; + WL_INFO(("Saved 0x%x 0x%x 0x%x\n", + saved_reg66, saved_reg41, saved_reg68)); + + /* Disable PM mode during dhpc session */ + + /* Disable PM mode during dhpc session */ +#ifdef COEX_DHCP + /* Start BT timer only for SCO connection */ + if (btcoex_is_sco_active(dev)) { + /* btc_params 66 */ + dev_wlc_bufvar_set(dev, "btc_params", + (char *)&buf_reg66va_dhcp_on[0], + sizeof(buf_reg66va_dhcp_on)); + /* btc_params 41 0x33 */ + dev_wlc_bufvar_set(dev, "btc_params", + (char *)&buf_reg41va_dhcp_on[0], + sizeof(buf_reg41va_dhcp_on)); + /* btc_params 68 0x190 */ + dev_wlc_bufvar_set(dev, "btc_params", + (char *)&buf_reg68va_dhcp_on[0], + sizeof(buf_reg68va_dhcp_on)); + saved_status = TRUE; + + btco_inf->bt_state = BT_DHCP_START; + btco_inf->timer_on = 1; + mod_timer(&btco_inf->timer, btco_inf->timer.expires); + WL_INFO(("%s enable BT DHCP Timer\n", + __FUNCTION__)); + } +#endif /* COEX_DHCP */ + } + else if (saved_status == TRUE) { + WL_ERR(("%s was called w/o DHCP OFF. Continue\n", __FUNCTION__)); + } + } + else if (strnicmp((char *)&powermode_val, "2", strlen("2")) == 0) { + + + /* Restoring PM mode */ + +#ifdef COEX_DHCP + /* Stop any bt timer because DHCP session is done */ + WL_INFO(("%s disable BT DHCP Timer\n", __FUNCTION__)); + if (btco_inf->timer_on) { + btco_inf->timer_on = 0; + del_timer_sync(&btco_inf->timer); + + if (btco_inf->bt_state != BT_DHCP_IDLE) { + /* need to restore original btc flags & extra btc params */ + WL_INFO(("%s bt->bt_state:%d\n", + __FUNCTION__, btco_inf->bt_state)); + /* wake up btcoex thread to restore btlags+params */ + schedule_work(&btco_inf->work); + } + } + + /* Restoring btc_flag paramter anyway */ + if (saved_status == TRUE) + dev_wlc_bufvar_set(dev, "btc_flags", + (char *)&buf_flag7_default[0], sizeof(buf_flag7_default)); +#endif /* COEX_DHCP */ + + /* Restore original values */ + if (saved_status == TRUE) { + regaddr = 66; + dev_wlc_intvar_set_reg(dev, "btc_params", + (char *)®addr, (char *)&saved_reg66); + regaddr = 41; + dev_wlc_intvar_set_reg(dev, "btc_params", + (char *)®addr, (char *)&saved_reg41); + regaddr = 68; + dev_wlc_intvar_set_reg(dev, "btc_params", + (char *)®addr, (char *)&saved_reg68); + + WL_INFO(("restore regs {66,41,68} <- 0x%x 0x%x 0x%x\n", + saved_reg66, saved_reg41, saved_reg68)); + } + saved_status = FALSE; + + } + else { + WL_ERR(("%s Unkwown yet power setting, ignored\n", + __FUNCTION__)); + } + + snprintf(command, 3, "OK"); + + return (strlen("OK")); +} diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.h b/drivers/net/wireless/bcmdhd/wl_cfg80211.h index a563724..4159bd7 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.h +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.h @@ -323,6 +323,27 @@ struct ap_info { u8 *wps_ie; bool security_mode; }; +struct btcoex_info { + struct timer_list timer; + uint32 timer_ms; + uint32 timer_on; + uint32 ts_dhcp_start; /* ms ts ecord time stats */ + uint32 ts_dhcp_ok; /* ms ts ecord time stats */ + bool dhcp_done; /* flag, indicates that host done with + * dhcp before t1/t2 expiration + */ + int bt_state; + struct work_struct work; + struct net_device *dev; +}; + +struct sta_info { + /* Structure to hold WPS IE for a STA */ + u8 probe_req_ie[IE_MAX_LEN]; + u8 assoc_req_ie[IE_MAX_LEN]; + u32 probe_req_ie_len; + u32 assoc_req_ie_len; +}; /* dongle private data of cfg80211 interface */ struct wl_priv { struct wireless_dev *wdev; /* representing wl cfg80211 device */ @@ -343,8 +364,6 @@ struct wl_priv { struct wl_cfg80211_bss_info *bss_info; /* information element object for internal purpose */ struct wl_ie ie; - u8 scan_ie_buf[2048]; - int scan_ie_len; struct ether_addr bssid; /* bssid of currently engaged network */ /* for synchronization of main event thread */ @@ -357,7 +376,7 @@ struct wl_priv { /* control firwmare and nvram paramter downloading */ struct wl_fw_ctrl *fw; struct wl_pmk_list *pmk_list; /* wpa2 pmk list */ - tsk_ctl_t event_tsk; /* task of main event handler thread */ + tsk_ctl_t event_tsk; /* task of main event handler thread */ unsigned long status; /* current dongle status */ void *pub; u32 channel; /* current channel */ @@ -385,9 +404,10 @@ struct wl_priv { u64 cache_cookie; wait_queue_head_t dongle_event_wait; struct ap_info *ap_info; + struct sta_info *sta_info; struct p2p_info *p2p; bool p2p_supported; - s8 last_eventmask[WL_EVENTING_MASK_LEN]; + struct btcoex_info *btcoex_info; }; #define wl_to_wiphy(w) (w->wdev->wiphy) @@ -507,6 +527,7 @@ extern void wl_cfg80211_release_fw(void); extern s8 *wl_cfg80211_get_fwname(void); extern s8 *wl_cfg80211_get_nvramname(void); extern s32 wl_cfg80211_get_p2p_dev_addr(struct net_device *net, struct ether_addr *p2pdev_addr); +extern int wl_cfg80211_hang(struct net_device *dev, u16 reason); #ifdef CONFIG_SYSCTL extern s32 wl_cfg80211_sysctl_export_devaddr(void *data); #endif diff --git a/drivers/net/wireless/bcmdhd/wl_cfgp2p.c b/drivers/net/wireless/bcmdhd/wl_cfgp2p.c index a5ad9e0..2487a67 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfgp2p.c +++ b/drivers/net/wireless/bcmdhd/wl_cfgp2p.c @@ -627,16 +627,14 @@ wl_cfgp2p_set_management_ie(struct wl_priv *wl, struct net_device *ndev, s32 bss s32 ret = BCME_OK; u32 pos; u8 *ie_buf; - u8 *mgmt_ie_buf; - u32 mgmt_ie_buf_len; - u32 *mgmt_ie_len; + u8 *mgmt_ie_buf = NULL; + u32 mgmt_ie_buf_len = 0; + u32 *mgmt_ie_len = 0; u8 ie_id, ie_len; u8 delete = 0; #define IE_TYPE(type, bsstype) (wl_to_p2p_bss_saved_ie(wl, bsstype).p2p_ ## type ## _ie) #define IE_TYPE_LEN(type, bsstype) (wl_to_p2p_bss_saved_ie(wl, bsstype).p2p_ ## type ## _ie_len) - if (bssidx == -1) - return BCME_BADARG; - if (wl->p2p_supported && p2p_on(wl)) { + if (wl->p2p_supported && p2p_on(wl) && bssidx != -1) { if (bssidx == P2PAPI_BSSCFG_PRIMARY) bssidx = wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE); switch (pktflag) { @@ -671,7 +669,7 @@ wl_cfgp2p_set_management_ie(struct wl_priv *wl, struct net_device *ndev, s32 bss CFGP2P_ERR(("not suitable type\n")); return -1; } - } else { + } else if (get_mode_by_netdev(wl, ndev) == WL_MODE_AP) { switch (pktflag) { case VNDR_IE_PRBRSP_FLAG : mgmt_ie_buf = wl->ap_info->probe_res_ie; @@ -689,37 +687,63 @@ wl_cfgp2p_set_management_ie(struct wl_priv *wl, struct net_device *ndev, s32 bss CFGP2P_ERR(("not suitable type\n")); return -1; } + bssidx = 0; + } else if (bssidx == -1 && get_mode_by_netdev(wl, ndev) == WL_MODE_BSS) { + switch (pktflag) { + case VNDR_IE_PRBREQ_FLAG : + mgmt_ie_buf = wl->sta_info->probe_req_ie; + mgmt_ie_len = &wl->sta_info->probe_req_ie_len; + mgmt_ie_buf_len = sizeof(wl->sta_info->probe_req_ie); + break; + case VNDR_IE_ASSOCREQ_FLAG : + mgmt_ie_buf = wl->sta_info->assoc_req_ie; + mgmt_ie_len = &wl->sta_info->assoc_req_ie_len; + mgmt_ie_buf_len = sizeof(wl->sta_info->assoc_req_ie); + break; + default: + mgmt_ie_buf = NULL; + mgmt_ie_len = NULL; + CFGP2P_ERR(("not suitable type\n")); + return -1; + } + bssidx = 0; + } else { + CFGP2P_ERR(("not suitable type\n")); + return -1; } - /* Add if there is any extra IE */ - if (vndr_ie && vndr_ie_len) { - CFGP2P_INFO(("Request has extra IE")); - if (vndr_ie_len > mgmt_ie_buf_len) { - CFGP2P_ERR(("extra IE size too big\n")); - ret = -ENOMEM; - } else { - if (mgmt_ie_buf != NULL) { - if ((vndr_ie_len == *mgmt_ie_len) && - (memcmp(mgmt_ie_buf, vndr_ie, vndr_ie_len) == 0)) { - CFGP2P_INFO(("Previous mgmt IE is equals to current IE")); - goto exit; - } - pos = 0; - delete = 1; - ie_buf = (u8 *) mgmt_ie_buf; - while (pos < *mgmt_ie_len) { - ie_id = ie_buf[pos++]; - ie_len = ie_buf[pos++]; - CFGP2P_INFO(("DELELED ID(%d), Len(%d)," - "OUI(%02x:%02x:%02x)\n", - ie_id, ie_len, ie_buf[pos], + + if (vndr_ie_len > mgmt_ie_buf_len) { + CFGP2P_ERR(("extra IE size too big\n")); + ret = -ENOMEM; + } else { + if (mgmt_ie_buf != NULL) { + if (vndr_ie_len && (vndr_ie_len == *mgmt_ie_len) && + (memcmp(mgmt_ie_buf, vndr_ie, vndr_ie_len) == 0)) { + CFGP2P_INFO(("Previous mgmt IE is equals to current IE")); + goto exit; + } + pos = 0; + delete = 1; + ie_buf = (u8 *) mgmt_ie_buf; + while (pos < *mgmt_ie_len) { + ie_id = ie_buf[pos++]; + ie_len = ie_buf[pos++]; + if ((ie_id == DOT11_MNG_VS_ID) && + (wl_cfgp2p_is_wps_ie(&ie_buf[pos-2], NULL, 0) || + wl_cfgp2p_is_p2p_ie(&ie_buf[pos-2], NULL, 0))) { + CFGP2P_INFO(("DELELED ID : %d, Len : %d , OUI :" + "%02x:%02x:%02x\n", ie_id, ie_len, ie_buf[pos], ie_buf[pos+1], ie_buf[pos+2])); - ret = wl_cfgp2p_vndr_ie(ndev, bssidx, pktflag, - ie_buf+pos, VNDR_SPEC_ELEMENT_ID, - ie_buf+pos+3, ie_len-3, delete); - pos += ie_len; + ret = wl_cfgp2p_vndr_ie(ndev, bssidx, pktflag, ie_buf+pos, + VNDR_SPEC_ELEMENT_ID, ie_buf+pos+3, ie_len-3, delete); } - + pos += ie_len; } + + } + *mgmt_ie_len = 0; + /* Add if there is any extra IE */ + if (vndr_ie && vndr_ie_len) { /* save the current IE in wl struct */ memcpy(mgmt_ie_buf, vndr_ie, vndr_ie_len); *mgmt_ie_len = vndr_ie_len; @@ -741,7 +765,6 @@ wl_cfgp2p_set_management_ie(struct wl_priv *wl, struct net_device *ndev, s32 bss pos += ie_len; } } - } #undef IE_TYPE #undef IE_TYPE_LEN diff --git a/drivers/net/wireless/bcmdhd/wl_iw.c b/drivers/net/wireless/bcmdhd/wl_iw.c index 9b8184a2e..ef77cdc 100644 --- a/drivers/net/wireless/bcmdhd/wl_iw.c +++ b/drivers/net/wireless/bcmdhd/wl_iw.c @@ -1266,6 +1266,7 @@ wl_iw_set_dtim_skip( &iovbuf, sizeof(iovbuf))) >= 0) { p += snprintf(p, MAX_WX_STRING, "OK"); + net_os_set_dtim_skip(dev, bcn_li_dtim); WL_TRACE(("%s: set dtim_skip %d OK\n", __FUNCTION__, @@ -1737,6 +1738,7 @@ wl_iw_control_wl_off( sdioh_stop(NULL); #endif + net_os_set_dtim_skip(dev, 0); dhd_customer_gpio_wlan_ctrl(WLAN_RESET_OFF); @@ -2415,7 +2417,7 @@ wl_iw_get_range( list = (wl_uint32_list_t *)channels; dwrq->length = sizeof(struct iw_range); - memset(range, 0, sizeof(range)); + memset(range, 0, sizeof(*range)); range->min_nwid = range->max_nwid = 0; @@ -4472,7 +4474,7 @@ wl_iw_set_essid( g_ssid.SSID_len = htod32(g_ssid.SSID_len); - memset(join_params, 0, sizeof(join_params)); + memset(join_params, 0, sizeof(*join_params)); join_params_size = sizeof(join_params->ssid); memcpy(join_params->ssid.SSID, g_ssid.SSID, g_ssid.SSID_len); @@ -6297,6 +6299,8 @@ wl_iw_add_wps_probe_req_ie( str_ptr += WPS_PROBE_REQ_IE_CMD_LENGTH; datalen = wrqu->data.length - WPS_PROBE_REQ_IE_CMD_LENGTH; + + buflen = sizeof(vndr_ie_setbuf_t) + datalen - sizeof(vndr_ie_t); ie_setbuf = (vndr_ie_setbuf_t *)kmalloc(buflen, GFP_KERNEL); if (!ie_setbuf) { @@ -6306,16 +6310,21 @@ wl_iw_add_wps_probe_req_ie( memset(ie_setbuf, 0x00, buflen); + strncpy(ie_setbuf->cmd, "add", VNDR_IE_CMD_LEN - 1); ie_setbuf->cmd[VNDR_IE_CMD_LEN - 1] = '\0'; + iecount = htod32(1); memcpy((void *)&ie_setbuf->vndr_ie_buffer.iecount, &iecount, sizeof(int)); + pktflag = 0x10; - memcpy((void *)&ie_setbuf->vndr_ie_buffer.vndr_ie_list[0].pktflag, &pktflag, sizeof(uint32)); + memcpy((void *)&ie_setbuf->vndr_ie_buffer.vndr_ie_list[0].pktflag, + &pktflag, sizeof(uint32)); - memcpy((void *)&ie_setbuf->vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data, str_ptr, datalen); + memcpy((void *)&ie_setbuf->vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data, + str_ptr, datalen); total_len = strlen("vndr_ie ") + buflen; bufptr = (char *)kmalloc(total_len, GFP_KERNEL); @@ -7528,11 +7537,15 @@ wl_iw_set_priv( ret = wl_iw_set_cscan(dev, info, (union iwreq_data *)dwrq, extra); #endif #ifdef CONFIG_WPS2 - else if (strnicmp(extra, WPS_ADD_PROBE_REQ_IE_CMD, strlen(WPS_ADD_PROBE_REQ_IE_CMD)) == 0) - ret = wl_iw_add_wps_probe_req_ie(dev, info, (union iwreq_data *)dwrq, extra); - else if (strnicmp(extra, WPS_DEL_PROBE_REQ_IE_CMD, strlen(WPS_DEL_PROBE_REQ_IE_CMD)) == 0) - ret = wl_iw_del_wps_probe_req_ie(dev, info, (union iwreq_data *)dwrq, extra); -#endif + else if (strnicmp(extra, WPS_ADD_PROBE_REQ_IE_CMD, + strlen(WPS_ADD_PROBE_REQ_IE_CMD)) == 0) + ret = wl_iw_add_wps_probe_req_ie(dev, info, + (union iwreq_data *)dwrq, extra); + else if (strnicmp(extra, WPS_DEL_PROBE_REQ_IE_CMD, + strlen(WPS_DEL_PROBE_REQ_IE_CMD)) == 0) + ret = wl_iw_del_wps_probe_req_ie(dev, info, + (union iwreq_data *)dwrq, extra); +#endif else if (strnicmp(extra, "POWERMODE", strlen("POWERMODE")) == 0) ret = wl_iw_set_power_mode(dev, info, (union iwreq_data *)dwrq, extra); else if (strnicmp(extra, "BTCOEXMODE", strlen("BTCOEXMODE")) == 0) @@ -7761,8 +7774,8 @@ static const struct iw_priv_args wl_iw_priv_args[] = { WL_AP_STA_LIST, - 0, - IW_PRIV_TYPE_CHAR | 0, + IW_PRIV_TYPE_CHAR | 0, + IW_PRIV_TYPE_CHAR | 1024, "AP_GET_STA_LIST" }, -- 2.7.4