diff --git a/bin/firmware-overlay/brcm/config.txt b/bin/firmware-overlay/brcm/config.txt new file mode 100644 index 000000000..ede140bf2 --- /dev/null +++ b/bin/firmware-overlay/brcm/config.txt @@ -0,0 +1,3 @@ +nv_by_chip=1 \ +43362 1 nvram_ap6210.txt +43430 0 nvram_ap6212.txt diff --git a/patch/kernel/sun8i-default/m2-update-bcmdhd-driver.patch b/patch/kernel/sun8i-default/m2-update-bcmdhd-driver.patch new file mode 100644 index 000000000..af57a5a8d --- /dev/null +++ b/patch/kernel/sun8i-default/m2-update-bcmdhd-driver.patch @@ -0,0 +1,40326 @@ +diff -Nur a/drivers/net/wireless/bcmdhd/aiutils.c c/drivers/net/wireless/bcmdhd/aiutils.c +--- a/drivers/net/wireless/bcmdhd/aiutils.c 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/aiutils.c 2016-05-13 09:48:20.000000000 +0200 +@@ -1094,4 +1094,4 @@ + R_REG(osh, &ai->itcr)); + } + } +-#endif ++#endif +diff -Nur a/drivers/net/wireless/bcmdhd/bcmevent.c c/drivers/net/wireless/bcmdhd/bcmevent.c +--- a/drivers/net/wireless/bcmdhd/bcmevent.c 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/bcmevent.c 2016-05-13 09:48:20.000000000 +0200 +@@ -2,7 +2,7 @@ + * bcmevent read-only data shared by kernel or app layers + * + * $Copyright Open Broadcom Corporation$ +- * $Id: bcmevent.c 487838 2014-06-27 05:51:44Z $ ++ * $Id: bcmevent.c 492377 2014-07-21 19:54:06Z $ + */ + + #include +@@ -90,19 +90,6 @@ + BCMEVENT_NAME(WLC_E_ACTION_FRAME_RX), + BCMEVENT_NAME(WLC_E_ACTION_FRAME_COMPLETE), + #endif +-#if 0 && (NDISVER >= 0x0620) +- BCMEVENT_NAME(WLC_E_PRE_ASSOC_IND), +- BCMEVENT_NAME(WLC_E_PRE_REASSOC_IND), +- BCMEVENT_NAME(WLC_E_CHANNEL_ADOPTED), +- BCMEVENT_NAME(WLC_E_AP_STARTED), +- BCMEVENT_NAME(WLC_E_DFS_AP_STOP), +- BCMEVENT_NAME(WLC_E_DFS_AP_RESUME), +- BCMEVENT_NAME(WLC_E_ASSOC_IND_NDIS), +- BCMEVENT_NAME(WLC_E_REASSOC_IND_NDIS), +- BCMEVENT_NAME(WLC_E_ACTION_FRAME_RX_NDIS), +- BCMEVENT_NAME(WLC_E_AUTH_REQ), +- BCMEVENT_NAME(WLC_E_IBSS_COALESCE), +-#endif + #ifdef BCMWAPI_WAI + BCMEVENT_NAME(WLC_E_WAI_STA_EVENT), + BCMEVENT_NAME(WLC_E_WAI_MSG), +diff -Nur a/drivers/net/wireless/bcmdhd/bcmsdh.c c/drivers/net/wireless/bcmdhd/bcmsdh.c +--- a/drivers/net/wireless/bcmdhd/bcmsdh.c 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/bcmsdh.c 2016-05-13 09:48:20.000000000 +0200 +@@ -36,7 +36,7 @@ + extern SDIOH_API_RC sdioh_detach(osl_t *osh, sdioh_info_t *sd); + #endif + +-#if defined(OOB_INTR_ONLY) && defined(HW_OOB) ++#if defined(OOB_INTR_ONLY) && defined(HW_OOB) || defined(FORCE_WOWLAN) + extern int + sdioh_enable_hw_oob_intr(void *sdioh, bool enable); + +@@ -696,3 +696,64 @@ + + return sdioh_gpioout(sd, gpio, enab); + } ++ ++uint ++bcmsdh_set_mode(void *sdh, uint mode) ++{ ++ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; ++ return (sdioh_set_mode(bcmsdh->sdioh, mode)); ++} ++ ++#if defined(SWTXGLOM) ++int ++bcmsdh_send_swtxglom_buf(void *sdh, uint32 addr, uint fn, uint flags, ++ uint8 *buf, uint nbytes, void *pkt, ++ bcmsdh_cmplt_fn_t complete_fn, void *handle) ++{ ++ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; ++ SDIOH_API_RC status; ++ uint incr_fix; ++ uint width; ++ int err = 0; ++ ++ ASSERT(bcmsdh); ++ ASSERT(bcmsdh->init_success); ++ ++ BCMSDH_INFO(("%s:fun = %d, addr = 0x%x, size = %d\n", ++ __FUNCTION__, fn, addr, nbytes)); ++ ++ /* Async not implemented yet */ ++ ASSERT(!(flags & SDIO_REQ_ASYNC)); ++ if (flags & SDIO_REQ_ASYNC) ++ return BCME_UNSUPPORTED; ++ ++ if ((err = bcmsdhsdio_set_sbaddr_window(bcmsdh, addr, FALSE))) ++ return err; ++ ++ addr &= SBSDIO_SB_OFT_ADDR_MASK; ++ ++ incr_fix = (flags & SDIO_REQ_FIXED) ? SDIOH_DATA_FIX : SDIOH_DATA_INC; ++ width = (flags & SDIO_REQ_4BYTE) ? 4 : 2; ++ if (width == 4) ++ addr |= SBSDIO_SB_ACCESS_2_4B_FLAG; ++ ++ status = sdioh_request_swtxglom_buffer(bcmsdh->sdioh, SDIOH_DATA_PIO, incr_fix, ++ SDIOH_WRITE, fn, addr, width, nbytes, buf, pkt); ++ ++ return (SDIOH_API_SUCCESS(status) ? 0 : BCME_ERROR); ++} ++ ++void ++bcmsdh_glom_post(void *sdh, uint8 *frame, void *pkt, uint len) ++{ ++ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; ++ sdioh_glom_post(bcmsdh->sdioh, frame, pkt, len); ++} ++ ++void ++bcmsdh_glom_clear(void *sdh) ++{ ++ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; ++ sdioh_glom_clear(bcmsdh->sdioh); ++} ++#endif /* SWTXGLOM */ +diff -Nur a/drivers/net/wireless/bcmdhd/bcmsdh_linux.c c/drivers/net/wireless/bcmdhd/bcmsdh_linux.c +--- a/drivers/net/wireless/bcmdhd/bcmsdh_linux.c 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/bcmsdh_linux.c 2016-05-13 09:48:20.000000000 +0200 +@@ -319,10 +319,17 @@ + SDLX_MSG(("%s: irq is already registered\n", __FUNCTION__)); + return -EBUSY; + } ++#ifdef HW_OOB ++ printf("%s: HW_OOB enabled\n", __FUNCTION__); ++#else ++ printf("%s: SW_OOB enabled\n", __FUNCTION__); ++#endif + SDLX_MSG(("%s OOB irq=%d flags=%X\n", __FUNCTION__, + (int)bcmsdh_osinfo->oob_irq_num, (int)bcmsdh_osinfo->oob_irq_flags)); + bcmsdh_osinfo->oob_irq_handler = oob_irq_handler; + bcmsdh_osinfo->oob_irq_handler_context = oob_irq_handler_context; ++ bcmsdh_osinfo->oob_irq_enabled = TRUE; ++ bcmsdh_osinfo->oob_irq_registered = TRUE; + #if defined(CONFIG_ARCH_ODIN) + err = odin_gpio_sms_request_irq(bcmsdh_osinfo->oob_irq_num, wlan_oob_irq, + bcmsdh_osinfo->oob_irq_flags, "bcmsdh_sdmmc", bcmsdh); +@@ -331,17 +338,15 @@ + bcmsdh_osinfo->oob_irq_flags, "bcmsdh_sdmmc", bcmsdh); + #endif /* defined(CONFIG_ARCH_ODIN) */ + if (err) { ++ bcmsdh_osinfo->oob_irq_enabled = FALSE; ++ bcmsdh_osinfo->oob_irq_registered = FALSE; + SDLX_MSG(("%s: request_irq failed with %d\n", __FUNCTION__, err)); + return err; + } + + #if defined(DISABLE_WOWLAN) + SDLX_MSG(("%s: disable_irq_wake\n", __FUNCTION__)); +- err = disable_irq_wake(bcmsdh_osinfo->oob_irq_num); +- if (err) +- SDLX_MSG(("%s: disable_irq_wake failed with %d\n", __FUNCTION__, err)); +- else +- bcmsdh_osinfo->oob_irq_wake_enabled = FALSE; ++ bcmsdh_osinfo->oob_irq_wake_enabled = FALSE; + #else + SDLX_MSG(("%s: enable_irq_wake\n", __FUNCTION__)); + err = enable_irq_wake(bcmsdh_osinfo->oob_irq_num); +@@ -350,8 +355,6 @@ + else + bcmsdh_osinfo->oob_irq_wake_enabled = TRUE; + #endif +- bcmsdh_osinfo->oob_irq_enabled = TRUE; +- bcmsdh_osinfo->oob_irq_registered = TRUE; + + return 0; + } +diff -Nur a/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc.c c/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc.c +--- a/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc.c 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc.c 2016-09-30 00:38:40.014529783 +0200 +@@ -2,13 +2,13 @@ + * BCMSDH Function Driver for the native SDIO/MMC driver in the Linux Kernel + * + * Copyright (C) 1999-2014, Broadcom Corporation +- * ++ * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: +- * ++ * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that +@@ -16,7 +16,7 @@ + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. +- * ++ * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. +@@ -59,12 +59,20 @@ + static void IRQHandlerF2(struct sdio_func *func); + #endif /* !defined(OOB_INTR_ONLY) */ + static int sdioh_sdmmc_get_cisaddr(sdioh_info_t *sd, uint32 regaddr); ++#if defined(ENABLE_INSMOD_NO_FW_LOAD) + extern int sdio_reset_comm(struct mmc_card *card); ++#else ++int sdio_reset_comm(struct mmc_card *card) ++{ ++ return 0; ++} ++#endif + #ifdef CONFIG_ARCH_SUNXI + extern int sunxi_mci_check_r1_ready(struct mmc_host* mmc, unsigned ms); + #endif +- ++#ifdef GLOBAL_SDMMC_INSTANCE + extern PBCMSDH_SDMMC_INSTANCE gInstance; ++#endif + + #define DEFAULT_SDIO_F2_BLKSIZE 512 + #ifndef CUSTOM_SDIO_F2_BLKSIZE +@@ -158,10 +166,16 @@ + sd->fake_func0.num = 0; + sd->fake_func0.card = func->card; + sd->func[0] = &sd->fake_func0; ++#ifdef GLOBAL_SDMMC_INSTANCE + if (func->num == 2) + sd->func[1] = gInstance->func[1]; ++#else ++ sd->func[1] = func->card->sdio_func[0]; ++#endif + sd->func[2] = func->card->sdio_func[1]; ++#ifdef GLOBAL_SDMMC_INSTANCE + sd->func[func->num] = func; ++#endif + sd->num_funcs = 2; + sd->sd_blockmode = TRUE; + sd->use_client_ints = TRUE; +@@ -184,6 +198,7 @@ + + sdio_claim_host(sd->func[2]); + sd->client_block_size[2] = sd_f2_blocksize; ++ printf("%s: set sd_f2_blocksize %d\n", __FUNCTION__, sd_f2_blocksize); + err_ret = sdio_set_block_size(sd->func[2], sd_f2_blocksize); + sdio_release_host(sd->func[2]); + if (err_ret) { +@@ -692,7 +707,7 @@ + return bcmerror; + } + +-#if defined(OOB_INTR_ONLY) && defined(HW_OOB) ++#if (defined(OOB_INTR_ONLY) && defined(HW_OOB)) || defined(FORCE_WOWLAN) + + SDIOH_API_RC + sdioh_enable_hw_oob_intr(sdioh_info_t *sd, bool enable) +@@ -791,11 +806,16 @@ + #ifdef CONFIG_ARCH_SUNXI + int ret = 0; + #endif ++ struct timespec now, before; ++ ++ if (sd_msglevel && SDH_COST_VAL) ++ getnstimeofday(&before); ++ + sd_info(("%s: rw=%d, func=%d, addr=0x%05x\n", __FUNCTION__, rw, func, regaddr)); + + DHD_PM_RESUME_WAIT(sdioh_request_byte_wait); + DHD_PM_RESUME_RETURN_ERROR(SDIOH_API_RC_FAIL); +- if(rw) { /* CMD52 Write */ ++ if (rw) { /* CMD52 Write */ + if (func == 0) { + /* Can only directly write to some F0 registers. Handle F2 enable + * as a special case. +@@ -880,16 +900,416 @@ + #endif + + if (err_ret) { +- if ((regaddr == 0x1001F) && ((err_ret == -ETIMEDOUT) || (err_ret == -EILSEQ))) { ++ if ((regaddr == 0x1001F) && ((err_ret == -ETIMEDOUT) || (err_ret == -EILSEQ) ++ || (err_ret == -EIO))) { + } else { + sd_err(("bcmsdh_sdmmc: Failed to %s byte F%d:@0x%05x=%02x, Err: %d\n", + rw ? "Write" : "Read", func, regaddr, *byte, err_ret)); + } + } + ++ if (sd_msglevel && SDH_COST_VAL) ++ getnstimeofday(&now); ++ sd_cost(("%s: len=1 cost=%lds %luus\n", __FUNCTION__, ++ now.tv_sec-before.tv_sec, now.tv_nsec/1000-before.tv_nsec/1000)); ++ ++ return ((err_ret == 0) ? SDIOH_API_RC_SUCCESS : SDIOH_API_RC_FAIL); ++} ++ ++#if defined(SWTXGLOM) ++static INLINE int sdioh_request_packet_align(uint pkt_len, uint write, uint func, int blk_size) ++{ ++ /* Align Patch */ ++ if (!write || pkt_len < 32) ++ pkt_len = (pkt_len + 3) & 0xFFFFFFFC; ++ else if ((pkt_len > blk_size) && (pkt_len % blk_size)) { ++ if (func == SDIO_FUNC_2) { ++ sd_err(("%s: [%s] dhd_sdio must align %d bytes" ++ " packet larger than a %d bytes blk size by a blk size\n", ++ __FUNCTION__, write ? "W" : "R", pkt_len, blk_size)); ++ } ++ pkt_len += blk_size - (pkt_len % blk_size); ++ } ++#ifdef CONFIG_MMC_MSM7X00A ++ if ((pkt_len % 64) == 32) { ++ sd_err(("%s: Rounding up TX packet +=32\n", __FUNCTION__)); ++ pkt_len += 32; ++ } ++#endif /* CONFIG_MMC_MSM7X00A */ ++ return pkt_len; ++} ++ ++void ++sdioh_glom_post(sdioh_info_t *sd, uint8 *frame, void *pkt, uint len) ++{ ++ void *phead = sd->glom_info.glom_pkt_head; ++ void *ptail = sd->glom_info.glom_pkt_tail; ++ ++ BCM_REFERENCE(frame); ++ ++ ASSERT(!PKTLINK(pkt)); ++ if (!phead) { ++ ASSERT(!phead); ++ sd->glom_info.glom_pkt_head = sd->glom_info.glom_pkt_tail = pkt; ++ } ++ else { ++ ASSERT(ptail); ++ PKTSETNEXT(sd->osh, ptail, pkt); ++ sd->glom_info.glom_pkt_tail = pkt; ++ } ++ sd->glom_info.count++; ++} ++ ++void ++sdioh_glom_clear(sdioh_info_t *sd) ++{ ++ void *pnow, *pnext; ++ ++ pnext = sd->glom_info.glom_pkt_head; ++ ++ if (!pnext) { ++ sd_err(("sdioh_glom_clear: no first packet to clear!\n")); ++ return; ++ } ++ ++ while (pnext) { ++ pnow = pnext; ++ pnext = PKTNEXT(sd->osh, pnow); ++ PKTSETNEXT(sd->osh, pnow, NULL); ++ sd->glom_info.count--; ++ } ++ ++ sd->glom_info.glom_pkt_head = NULL; ++ sd->glom_info.glom_pkt_tail = NULL; ++ if (sd->glom_info.count != 0) { ++ sd_err(("sdioh_glom_clear: glom count mismatch!\n")); ++ sd->glom_info.count = 0; ++ } ++} ++ ++static SDIOH_API_RC ++sdioh_request_swtxglom_packet(sdioh_info_t *sd, uint fix_inc, uint write, uint func, ++ uint addr, void *pkt) ++{ ++ bool fifo = (fix_inc == SDIOH_DATA_FIX); ++ uint32 SGCount = 0; ++ int err_ret = 0; ++ void *pnext; ++ uint ttl_len, dma_len, lft_len, xfred_len, pkt_len; ++ uint blk_num; ++ int blk_size; ++ struct mmc_request mmc_req; ++ struct mmc_command mmc_cmd; ++ struct mmc_data mmc_dat; ++#ifdef BCMSDIOH_TXGLOM ++ uint8 *localbuf = NULL; ++ uint local_plen = 0; ++ bool need_txglom = write && ++ (pkt == sd->glom_info.glom_pkt_tail) && ++ (sd->glom_info.glom_pkt_head != sd->glom_info.glom_pkt_tail); ++#endif /* BCMSDIOH_TXGLOM */ ++ ++ sd_trace(("%s: Enter\n", __FUNCTION__)); ++ ++ ASSERT(pkt); ++ DHD_PM_RESUME_WAIT(sdioh_request_packet_wait); ++ DHD_PM_RESUME_RETURN_ERROR(SDIOH_API_RC_FAIL); ++ ++ ttl_len = xfred_len = 0; ++#ifdef BCMSDIOH_TXGLOM ++ if (need_txglom) { ++ pkt = sd->glom_info.glom_pkt_head; ++ } ++#endif /* BCMSDIOH_TXGLOM */ ++ ++ /* at least 4 bytes alignment of skb buff is guaranteed */ ++ for (pnext = pkt; pnext; pnext = PKTNEXT(sd->osh, pnext)) ++ ttl_len += PKTLEN(sd->osh, pnext); ++ ++ blk_size = sd->client_block_size[func]; ++ if (((!write && sd->use_rxchain) || ++#ifdef BCMSDIOH_TXGLOM ++ (need_txglom && sd->txglom_mode == SDPCM_TXGLOM_MDESC) || ++#endif ++ 0) && (ttl_len >= blk_size)) { ++ blk_num = ttl_len / blk_size; ++ dma_len = blk_num * blk_size; ++ } else { ++ blk_num = 0; ++ dma_len = 0; ++ } ++ ++ lft_len = ttl_len - dma_len; ++ ++ sd_trace(("%s: %s %dB to func%d:%08x, %d blks with DMA, %dB leftover\n", ++ __FUNCTION__, write ? "W" : "R", ++ ttl_len, func, addr, blk_num, lft_len)); ++ ++ if (0 != dma_len) { ++ memset(&mmc_req, 0, sizeof(struct mmc_request)); ++ memset(&mmc_cmd, 0, sizeof(struct mmc_command)); ++ memset(&mmc_dat, 0, sizeof(struct mmc_data)); ++ ++ /* Set up DMA descriptors */ ++ for (pnext = pkt; ++ pnext && dma_len; ++ pnext = PKTNEXT(sd->osh, pnext)) { ++ pkt_len = PKTLEN(sd->osh, pnext); ++ ++ if (dma_len > pkt_len) ++ dma_len -= pkt_len; ++ else { ++ pkt_len = xfred_len = dma_len; ++ dma_len = 0; ++ pkt = pnext; ++ } ++ ++ sg_set_buf(&sd->sg_list[SGCount++], ++ (uint8*)PKTDATA(sd->osh, pnext), ++ pkt_len); ++ ++ if (SGCount >= SDIOH_SDMMC_MAX_SG_ENTRIES) { ++ sd_err(("%s: sg list entries exceed limit\n", ++ __FUNCTION__)); ++ return (SDIOH_API_RC_FAIL); ++ } ++ } ++ ++ mmc_dat.sg = sd->sg_list; ++ mmc_dat.sg_len = SGCount; ++ mmc_dat.blksz = blk_size; ++ mmc_dat.blocks = blk_num; ++ mmc_dat.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ; ++ ++ mmc_cmd.opcode = 53; /* SD_IO_RW_EXTENDED */ ++ mmc_cmd.arg = write ? 1<<31 : 0; ++ mmc_cmd.arg |= (func & 0x7) << 28; ++ mmc_cmd.arg |= 1<<27; ++ mmc_cmd.arg |= fifo ? 0 : 1<<26; ++ mmc_cmd.arg |= (addr & 0x1FFFF) << 9; ++ mmc_cmd.arg |= blk_num & 0x1FF; ++ mmc_cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC; ++ ++ mmc_req.cmd = &mmc_cmd; ++ mmc_req.data = &mmc_dat; ++ ++ sdio_claim_host(sd->func[func]); ++ mmc_set_data_timeout(&mmc_dat, sd->func[func]->card); ++ mmc_wait_for_req(sd->func[func]->card->host, &mmc_req); ++ sdio_release_host(sd->func[func]); ++ ++ err_ret = mmc_cmd.error? mmc_cmd.error : mmc_dat.error; ++ if (0 != err_ret) { ++ sd_err(("%s:CMD53 %s failed with code %d\n", ++ __FUNCTION__, ++ write ? "write" : "read", ++ err_ret)); ++ } ++ if (!fifo) { ++ addr = addr + ttl_len - lft_len - dma_len; ++ } ++ } ++ ++ /* PIO mode */ ++ if (0 != lft_len) { ++ /* Claim host controller */ ++ sdio_claim_host(sd->func[func]); ++ for (pnext = pkt; pnext; pnext = PKTNEXT(sd->osh, pnext)) { ++ uint8 *buf = (uint8*)PKTDATA(sd->osh, pnext) + ++ xfred_len; ++ uint pad = 0; ++ pkt_len = PKTLEN(sd->osh, pnext); ++ if (0 != xfred_len) { ++ pkt_len -= xfred_len; ++ xfred_len = 0; ++ } ++#ifdef BCMSDIOH_TXGLOM ++ if (need_txglom) { ++ if (!localbuf) { ++ uint prev_lft_len = lft_len; ++ lft_len = sdioh_request_packet_align(lft_len, write, ++ func, blk_size); ++ ++ if (lft_len > prev_lft_len) { ++ sd_err(("%s: padding is unexpected! lft_len %d," ++ " prev_lft_len %d %s\n", ++ __FUNCTION__, lft_len, prev_lft_len, ++ write ? "Write" : "Read")); ++ } ++ ++ localbuf = (uint8 *)MALLOC(sd->osh, lft_len); ++ if (localbuf == NULL) { ++ sd_err(("%s: %s TXGLOM: localbuf malloc FAILED\n", ++ __FUNCTION__, (write) ? "TX" : "RX")); ++ need_txglom = FALSE; ++ goto txglomfail; ++ } ++ } ++ bcopy(buf, (localbuf + local_plen), pkt_len); ++ local_plen += pkt_len; ++ ++ if (PKTNEXT(sd->osh, pnext)) { ++ continue; ++ } ++ ++ buf = localbuf; ++ pkt_len = local_plen; ++ } ++ ++txglomfail: ++#endif /* BCMSDIOH_TXGLOM */ ++ ++ if ( ++#ifdef BCMSDIOH_TXGLOM ++ !need_txglom && ++#endif ++ TRUE) { ++ pkt_len = sdioh_request_packet_align(pkt_len, write, ++ func, blk_size); ++ ++ pad = pkt_len - PKTLEN(sd->osh, pnext); ++ ++ if (pad > 0) { ++ if (func == SDIO_FUNC_2) { ++ sd_err(("%s: padding is unexpected! pkt_len %d," ++ " PKTLEN %d lft_len %d %s\n", ++ __FUNCTION__, pkt_len, PKTLEN(sd->osh, pnext), ++ lft_len, write ? "Write" : "Read")); ++ } ++ if (PKTTAILROOM(sd->osh, pkt) < pad) { ++ sd_info(("%s: insufficient tailroom %d, pad %d," ++ " lft_len %d pktlen %d, func %d %s\n", ++ __FUNCTION__, (int)PKTTAILROOM(sd->osh, pkt), ++ pad, lft_len, PKTLEN(sd->osh, pnext), func, ++ write ? "W" : "R")); ++ if (PKTPADTAILROOM(sd->osh, pkt, pad)) { ++ sd_err(("%s: padding error size %d.\n", ++ __FUNCTION__, pad)); ++ return SDIOH_API_RC_FAIL; ++ } ++ } ++ } ++ } ++ ++ if ((write) && (!fifo)) ++ err_ret = sdio_memcpy_toio( ++ sd->func[func], ++ addr, buf, pkt_len); ++ else if (write) ++ err_ret = sdio_memcpy_toio( ++ sd->func[func], ++ addr, buf, pkt_len); ++ else if (fifo) ++ err_ret = sdio_readsb( ++ sd->func[func], ++ buf, addr, pkt_len); ++ else ++ err_ret = sdio_memcpy_fromio( ++ sd->func[func], ++ buf, addr, pkt_len); ++ ++ if (err_ret) ++ sd_err(("%s: %s FAILED %p[%d], addr=0x%05x, pkt_len=%d, ERR=%d\n", ++ __FUNCTION__, ++ (write) ? "TX" : "RX", ++ pnext, SGCount, addr, pkt_len, err_ret)); ++ else ++ sd_trace(("%s: %s xfr'd %p[%d], addr=0x%05x, len=%d\n", ++ __FUNCTION__, ++ (write) ? "TX" : "RX", ++ pnext, SGCount, addr, pkt_len)); ++ ++ if (!fifo) ++ addr += pkt_len; ++ SGCount ++; ++ } ++ sdio_release_host(sd->func[func]); ++ } ++#ifdef BCMSDIOH_TXGLOM ++ if (localbuf) ++ MFREE(sd->osh, localbuf, lft_len); ++#endif /* BCMSDIOH_TXGLOM */ ++ ++ sd_trace(("%s: Exit\n", __FUNCTION__)); + return ((err_ret == 0) ? SDIOH_API_RC_SUCCESS : SDIOH_API_RC_FAIL); + } + ++/* ++ * This function takes a buffer or packet, and fixes everything up so that in the ++ * end, a DMA-able packet is created. ++ * ++ * A buffer does not have an associated packet pointer, and may or may not be aligned. ++ * A packet may consist of a single packet, or a packet chain. If it is a packet chain, ++ * then all the packets in the chain must be properly aligned. If the packet data is not ++ * aligned, then there may only be one packet, and in this case, it is copied to a new ++ * aligned packet. ++ * ++ */ ++extern SDIOH_API_RC ++sdioh_request_swtxglom_buffer(sdioh_info_t *sd, uint pio_dma, uint fix_inc, uint write, uint func, ++ uint addr, uint reg_width, uint buflen_u, uint8 *buffer, void *pkt) ++{ ++ SDIOH_API_RC Status; ++ void *tmppkt; ++ void *orig_buf = NULL; ++ uint copylen = 0; ++ ++ sd_trace(("%s: Enter\n", __FUNCTION__)); ++ ++ DHD_PM_RESUME_WAIT(sdioh_request_buffer_wait); ++ DHD_PM_RESUME_RETURN_ERROR(SDIOH_API_RC_FAIL); ++ ++ if (pkt == NULL) { ++ /* Case 1: we don't have a packet. */ ++ orig_buf = buffer; ++ copylen = buflen_u; ++ } else if ((ulong)PKTDATA(sd->osh, pkt) & DMA_ALIGN_MASK) { ++ /* Case 2: We have a packet, but it is unaligned. ++ * in this case, we cannot have a chain. ++ */ ++ ASSERT(PKTNEXT(sd->osh, pkt) == NULL); ++ ++ orig_buf = PKTDATA(sd->osh, pkt); ++ copylen = PKTLEN(sd->osh, pkt); ++ } ++ ++ tmppkt = pkt; ++ if (copylen) { ++ tmppkt = PKTGET_STATIC(sd->osh, copylen, write ? TRUE : FALSE); ++ if (tmppkt == NULL) { ++ sd_err(("%s: PKTGET failed: len %d\n", __FUNCTION__, copylen)); ++ return SDIOH_API_RC_FAIL; ++ } ++ /* For a write, copy the buffer data into the packet. */ ++ if (write) ++ bcopy(orig_buf, PKTDATA(sd->osh, tmppkt), copylen); ++ } ++ ++ Status = sdioh_request_swtxglom_packet(sd, fix_inc, write, func, addr, tmppkt); ++ ++ if (copylen) { ++ /* For a read, copy the packet data back to the buffer. */ ++ if (!write) ++ bcopy(PKTDATA(sd->osh, tmppkt), orig_buf, PKTLEN(sd->osh, tmppkt)); ++ PKTFREE_STATIC(sd->osh, tmppkt, write ? TRUE : FALSE); ++ } ++ ++ return (Status); ++} ++#endif ++ ++uint ++sdioh_set_mode(sdioh_info_t *sd, uint mode) ++{ ++ if (mode == SDPCM_TXGLOM_CPY) ++ sd->txglom_mode = mode; ++ else if (mode == SDPCM_TXGLOM_MDESC) ++ sd->txglom_mode = mode; ++ printf("%s: set txglom_mode to %s\n", __FUNCTION__, mode==SDPCM_TXGLOM_MDESC?"multi-desc":"copy"); ++ ++ return (sd->txglom_mode); ++} ++ + extern SDIOH_API_RC + sdioh_request_word(sdioh_info_t *sd, uint cmd_type, uint rw, uint func, uint addr, + uint32 *word, uint nbytes) +@@ -902,6 +1322,11 @@ + #ifdef CONFIG_ARCH_SUNXI + int ret = 0; + #endif ++ struct timespec now, before; ++ ++ if (sd_msglevel && SDH_COST_VAL) ++ getnstimeofday(&before); ++ + if (func == 0) { + sd_err(("%s: Only CMD52 allowed to F0.\n", __FUNCTION__)); + return SDIOH_API_RC_FAIL; +@@ -915,7 +1340,7 @@ + /* Claim host controller */ + sdio_claim_host(sd->func[func]); + +- if(rw) { /* CMD52 Write */ ++ if (rw) { /* CMD52 Write */ + if (nbytes == 4) { + sdio_writel(sd->func[func], *word, addr, &err_ret); + } else if (nbytes == 2) { +@@ -968,11 +1393,16 @@ + if (err_ret) + #endif /* MMC_SDIO_ABORT */ + { +- sd_err(("bcmsdh_sdmmc: Failed to %s word, Err: 0x%08x\n", +- rw ? "Write" : "Read", err_ret)); ++ sd_err(("bcmsdh_sdmmc: Failed to %s word F%d:@0x%05x=%02x, Err: 0x%08x\n", ++ rw ? "Write" : "Read", func, addr, *word, err_ret)); + } + } + ++ if (sd_msglevel && SDH_COST_VAL) ++ getnstimeofday(&now); ++ sd_cost(("%s: len=%d cost=%lds %luus\n", __FUNCTION__, ++ nbytes, now.tv_sec-before.tv_sec, now.tv_nsec/1000 - before.tv_nsec/1000)); ++ + return (((err_ret == 0)&&(err_ret2 == 0)) ? SDIOH_API_RC_SUCCESS : SDIOH_API_RC_FAIL); + } + +@@ -994,12 +1424,21 @@ + uint32 sg_count; + struct sdio_func *sdio_func = sd->func[func]; + struct mmc_host *host = sdio_func->card->host; ++#ifdef BCMSDIOH_TXGLOM ++ uint8 *localbuf = NULL; ++ uint local_plen = 0; ++ uint pkt_len = 0; ++#endif /* BCMSDIOH_TXGLOM */ ++ struct timespec now, before; + + sd_trace(("%s: Enter\n", __FUNCTION__)); + ASSERT(pkt); + DHD_PM_RESUME_WAIT(sdioh_request_packet_wait); + DHD_PM_RESUME_RETURN_ERROR(SDIOH_API_RC_FAIL); + ++ if (sd_msglevel && SDH_COST_VAL) ++ getnstimeofday(&before); ++ + blk_size = sd->client_block_size[func]; + max_blk_count = min(host->max_blk_count, (uint)MAX_IO_RW_EXTENDED_BLK); + max_req_size = min(max_blk_count * blk_size, host->max_req_size); +@@ -1007,6 +1446,11 @@ + pkt_offset = 0; + pnext = pkt; + ++#ifdef BCMSDIOH_TXGLOM ++ ttl_len = 0; ++ sg_count = 0; ++ if(sd->txglom_mode == SDPCM_TXGLOM_MDESC) { ++#endif + while (pnext != NULL) { + ttl_len = 0; + sg_count = 0; +@@ -1094,6 +1538,86 @@ + return SDIOH_API_RC_FAIL; + } + } ++#ifdef BCMSDIOH_TXGLOM ++ } else if(sd->txglom_mode == SDPCM_TXGLOM_CPY) { ++ for (pnext = pkt; pnext; pnext = PKTNEXT(sd->osh, pnext)) { ++ ttl_len += PKTLEN(sd->osh, pnext); ++ } ++ /* Claim host controller */ ++ sdio_claim_host(sd->func[func]); ++ for (pnext = pkt; pnext; pnext = PKTNEXT(sd->osh, pnext)) { ++ uint8 *buf = (uint8*)PKTDATA(sd->osh, pnext); ++ pkt_len = PKTLEN(sd->osh, pnext); ++ ++ if (!localbuf) { ++ localbuf = (uint8 *)MALLOC(sd->osh, ttl_len); ++ if (localbuf == NULL) { ++ sd_err(("%s: %s TXGLOM: localbuf malloc FAILED\n", ++ __FUNCTION__, (write) ? "TX" : "RX")); ++ goto txglomfail; ++ } ++ } ++ ++ bcopy(buf, (localbuf + local_plen), pkt_len); ++ local_plen += pkt_len; ++ if (PKTNEXT(sd->osh, pnext)) ++ continue; ++ ++ buf = localbuf; ++ pkt_len = local_plen; ++txglomfail: ++ /* Align Patch */ ++ if (!write || pkt_len < 32) ++ pkt_len = (pkt_len + 3) & 0xFFFFFFFC; ++ else if (pkt_len % blk_size) ++ pkt_len += blk_size - (pkt_len % blk_size); ++ ++ if ((write) && (!fifo)) ++ err_ret = sdio_memcpy_toio( ++ sd->func[func], ++ addr, buf, pkt_len); ++ else if (write) ++ err_ret = sdio_memcpy_toio( ++ sd->func[func], ++ addr, buf, pkt_len); ++ else if (fifo) ++ err_ret = sdio_readsb( ++ sd->func[func], ++ buf, addr, pkt_len); ++ else ++ err_ret = sdio_memcpy_fromio( ++ sd->func[func], ++ buf, addr, pkt_len); ++ ++ if (err_ret) ++ sd_err(("%s: %s FAILED %p[%d], addr=0x%05x, pkt_len=%d, ERR=%d\n", ++ __FUNCTION__, ++ (write) ? "TX" : "RX", ++ pnext, sg_count, addr, pkt_len, err_ret)); ++ else ++ sd_trace(("%s: %s xfr'd %p[%d], addr=0x%05x, len=%d\n", ++ __FUNCTION__, ++ (write) ? "TX" : "RX", ++ pnext, sg_count, addr, pkt_len)); ++ ++ if (!fifo) ++ addr += pkt_len; ++ sg_count ++; ++ } ++ sdio_release_host(sd->func[func]); ++ } else { ++ sd_err(("%s: set to wrong glom mode %d\n", __FUNCTION__, sd->txglom_mode)); ++ return SDIOH_API_RC_FAIL; ++ } ++ ++ if (localbuf) ++ MFREE(sd->osh, localbuf, ttl_len); ++#endif /* BCMSDIOH_TXGLOM */ ++ ++ if (sd_msglevel && SDH_COST_VAL) ++ getnstimeofday(&now); ++ sd_cost(("%s: cost=%lds %luus\n", __FUNCTION__, ++ now.tv_sec-before.tv_sec, now.tv_nsec/1000-before.tv_nsec/1000)); + + sd_trace(("%s: Exit\n", __FUNCTION__)); + return SDIOH_API_RC_SUCCESS; +@@ -1105,12 +1629,17 @@ + { + bool fifo = (fix_inc == SDIOH_DATA_FIX); + int err_ret = 0; ++ struct timespec now, before; + #ifdef CONFIG_ARCH_SUNXI + int ret = 0; + #endif ++ + sd_trace(("%s: Enter\n", __FUNCTION__)); + ASSERT(buf); + ++ if (sd_msglevel && SDH_COST_VAL) ++ getnstimeofday(&before); ++ + /* NOTE: + * For all writes, each packet length is aligned to 32 (or 4) + * bytes in dhdsdio_txpkt_preprocess, and for glom the last packet length +@@ -1147,6 +1676,11 @@ + (write) ? "TX" : "RX", buf, addr, len)); + + sd_trace(("%s: Exit\n", __FUNCTION__)); ++ ++ if (sd_msglevel && SDH_COST_VAL) ++ getnstimeofday(&now); ++ sd_cost(("%s: len=%d cost=%lds %luus\n", __FUNCTION__, ++ len, now.tv_sec-before.tv_sec, now.tv_nsec/1000 - before.tv_nsec/1000)); + return ((err_ret == 0) ? SDIOH_API_RC_SUCCESS : SDIOH_API_RC_FAIL); + } + +@@ -1168,11 +1702,15 @@ + { + SDIOH_API_RC status; + void *tmppkt; ++ struct timespec now, before; + + sd_trace(("%s: Enter\n", __FUNCTION__)); + DHD_PM_RESUME_WAIT(sdioh_request_buffer_wait); + DHD_PM_RESUME_RETURN_ERROR(SDIOH_API_RC_FAIL); + ++ if (sd_msglevel && SDH_COST_VAL) ++ getnstimeofday(&before); ++ + if (pkt) { + /* packet chain, only used for tx/rx glom, all packets length + * are aligned, total length is a block multiple +@@ -1214,6 +1752,11 @@ + + PKTFREE_STATIC(sd->osh, tmppkt, write ? TRUE : FALSE); + ++ if (sd_msglevel && SDH_COST_VAL) ++ getnstimeofday(&now); ++ sd_cost(("%s: len=%d cost=%lds %luus\n", __FUNCTION__, ++ buf_len, now.tv_sec-before.tv_sec, now.tv_nsec/1000 - before.tv_nsec/1000)); ++ + return status; + } + +@@ -1399,6 +1942,7 @@ + sdio_claim_host(sd->func[2]); + + sd->client_block_size[2] = sd_f2_blocksize; ++ printf("%s: set sd_f2_blocksize %d\n", __FUNCTION__, sd_f2_blocksize); + ret = sdio_set_block_size(sd->func[2], sd_f2_blocksize); + if (ret) { + sd_err(("bcmsdh_sdmmc: Failed to set F2 " +diff -Nur a/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c c/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c +--- a/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c 2016-05-13 09:48:20.000000000 +0200 +@@ -2,13 +2,13 @@ + * BCMSDH Function Driver for the native SDIO/MMC driver in the Linux Kernel + * + * Copyright (C) 1999-2014, Broadcom Corporation +- * ++ * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: +- * ++ * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that +@@ -16,7 +16,7 @@ + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. +- * ++ * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. +@@ -91,7 +91,9 @@ + module_param(clockoverride, int, 0644); + MODULE_PARM_DESC(clockoverride, "SDIO card clock override"); + ++#ifdef GLOBAL_SDMMC_INSTANCE + PBCMSDH_SDMMC_INSTANCE gInstance; ++#endif + + /* Maximum number of bcmsdh_sdmmc devices supported by driver */ + #define BCMSDH_SDMMC_MAX_DEVICES 1 +@@ -156,6 +158,7 @@ + sd_err(("%s: error, no sdioh handler found\n", __FUNCTION__)); + return; + } ++ sd_err(("%s: Enter\n", __FUNCTION__)); + + osh = sdioh->osh; + bcmsdh_remove(sdioh->bcmsdh); +@@ -177,7 +180,9 @@ + sd_info(("sdio_device: 0x%04x\n", func->device)); + sd_info(("Function#: 0x%04x\n", func->num)); + ++#ifdef GLOBAL_SDMMC_INSTANCE + gInstance->func[func->num] = func; ++#endif + + /* 4318 doesn't have function 2 */ + if ((func->num == 2) || (func->num == 1 && func->device == 0x4)) +@@ -228,14 +233,14 @@ + struct sdio_func *func = dev_to_sdio_func(pdev); + mmc_pm_flag_t sdio_flags; + +- printk("%s Enter\n", __FUNCTION__); ++ printf("%s Enter func->num=%d\n", __FUNCTION__, func->num); + if (func->num != 2) + return 0; + + sdioh = sdio_get_drvdata(func); + err = bcmsdh_suspend(sdioh->bcmsdh); + if (err) { +- printk("%s bcmsdh_suspend err=%d\n", __FUNCTION__, err); ++ printf("%s bcmsdh_suspend err=%d\n", __FUNCTION__, err); + return err; + } + +@@ -257,7 +262,7 @@ + dhd_mmc_suspend = TRUE; + smp_mb(); + +- printk("%s Exit\n", __FUNCTION__); ++ printf("%s Exit\n", __FUNCTION__); + return 0; + } + +@@ -268,7 +273,7 @@ + #endif + struct sdio_func *func = dev_to_sdio_func(pdev); + +- printk("%s Enter\n", __FUNCTION__); ++ printf("%s Enter func->num=%d\n", __FUNCTION__, func->num); + if (func->num != 2) + return 0; + +@@ -279,7 +284,7 @@ + #endif + + smp_mb(); +- printk("%s Exit\n", __FUNCTION__); ++ printf("%s Exit\n", __FUNCTION__); + return 0; + } + +@@ -287,7 +292,7 @@ + .suspend = bcmsdh_sdmmc_suspend, + .resume = bcmsdh_sdmmc_resume, + }; +-#endif /* (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM) */ ++#endif /* (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM) */ + + #if defined(BCMLXSDMMC) + static struct semaphore *notify_semaphore = NULL; +@@ -339,7 +344,7 @@ + .pm = &bcmsdh_sdmmc_pm_ops, + }, + #endif /* (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM) */ +- }; ++}; + + struct sdos_info { + sdioh_info_t *sd; +@@ -385,9 +390,11 @@ + */ + int bcmsdh_register_client_driver(void) + { ++#ifdef GLOBAL_SDMMC_INSTANCE + gInstance = kzalloc(sizeof(BCMSDH_SDMMC_INSTANCE), GFP_KERNEL); + if (!gInstance) + return -ENOMEM; ++#endif + + return sdio_register_driver(&bcmsdh_sdmmc_driver); + } +@@ -398,6 +405,8 @@ + void bcmsdh_unregister_client_driver(void) + { + sdio_unregister_driver(&bcmsdh_sdmmc_driver); ++#ifdef GLOBAL_SDMMC_INSTANCE + if (gInstance) + kfree(gInstance); ++#endif + } +diff -Nur a/drivers/net/wireless/bcmdhd/bcmutils.c c/drivers/net/wireless/bcmdhd/bcmutils.c +--- a/drivers/net/wireless/bcmdhd/bcmutils.c 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/bcmutils.c 2016-05-13 09:48:20.000000000 +0200 +@@ -2,7 +2,7 @@ + * Driver O/S-independent utility routines + * + * $Copyright Open Broadcom Corporation$ +- * $Id: bcmutils.c 488316 2014-06-30 15:22:21Z $ ++ * $Id: bcmutils.c 496061 2014-08-11 06:14:48Z $ + */ + + #include +@@ -735,7 +735,7 @@ + for (p = p0; p; p = PKTNEXT(osh, p)) + prhex(NULL, PKTDATA(osh, p), PKTLEN(osh, p)); + } +-#endif ++#endif + + /* Takes an Ethernet frame and sets out-of-bound PKTPRIO. + * Also updates the inplace vlan tag if requested. +@@ -868,6 +868,23 @@ + return rc; + } + ++/* Add to adjust the 802.1x priority */ ++void ++pktset8021xprio(void *pkt, int prio) ++{ ++ struct ether_header *eh; ++ uint8 *pktdata; ++ if(prio == PKTPRIO(pkt)) ++ return; ++ pktdata = (uint8 *)PKTDATA(OSH_NULL, pkt); ++ ASSERT(ISALIGNED((uintptr)pktdata, sizeof(uint16))); ++ eh = (struct ether_header *) pktdata; ++ if (eh->ether_type == hton16(ETHER_TYPE_802_1X)) { ++ ASSERT(prio >= 0 && prio <= MAXPRIO); ++ PKTSETPRIO(pkt, prio); ++ } ++} ++ + /* The 0.5KB string table is not removed by compiler even though it's unused */ + + static char bcm_undeferrstr[32]; +@@ -1526,7 +1543,7 @@ + + return (int)(p - buf); + } +-#endif ++#endif + + /* print bytes formatted as hex to a string. return the resulting string length */ + int +@@ -1972,7 +1989,7 @@ + + return (int)(p - buf); + } +-#endif ++#endif + + #endif /* BCMDRIVER */ + +@@ -2003,9 +2020,9 @@ + for (n=1; n 0); ++ ASSERT((start_val16 + total_ids) < ID16_INVALID); ++ ++ id16_map = (id16_map_t *)id16_map_hndl; ++ if (id16_map == NULL) { ++ return; ++ } ++ ++ id16_map->total = total_ids; ++ id16_map->start = start_val16; ++ id16_map->failures = 0; ++ ++ /* Populate stack with 16bit id values, commencing with start_val16 */ ++ id16_map->stack_idx = 0; ++ val16 = start_val16; ++ ++ for (idx = 0; idx < total_ids; idx++, val16++) { ++ id16_map->stack_idx = idx; ++ id16_map->stack[id16_map->stack_idx] = val16; ++ } ++ ++#if defined(BCM_DBG) && defined(BCM_DBG_ID16) ++ if (id16_map->dbg) { ++ id16_map_dbg_t *id16_map_dbg = (id16_map_dbg_t *)id16_map->dbg; ++ ++ id16_map_dbg->total = total_ids; ++ for (idx = 0; idx < total_ids; idx++) { ++ id16_map_dbg->avail[idx] = TRUE; ++ } ++ } ++#endif /* BCM_DBG && BCM_DBG_ID16 */ ++} ++ + uint16 BCMFASTPATH /* Allocate a unique 16bit id */ + id16_map_alloc(void * id16_map_hndl) + { +diff -Nur a/drivers/net/wireless/bcmdhd/dhd_bus.h c/drivers/net/wireless/bcmdhd/dhd_bus.h +--- a/drivers/net/wireless/bcmdhd/dhd_bus.h 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/dhd_bus.h 2016-05-13 09:48:20.000000000 +0200 +@@ -6,7 +6,7 @@ + * + * $Copyright Open Broadcom Corporation$ + * +- * $Id: dhd_bus.h 491657 2014-07-17 06:29:40Z $ ++ * $Id: dhd_bus.h 497466 2014-08-19 15:41:01Z $ + */ + + #ifndef _dhd_bus_h_ +@@ -150,7 +150,7 @@ + extern void dhd_bus_read_flow_ring_states(struct dhd_bus *bus, + void * data, uint8 flowid); + extern int dhd_bus_flow_ring_create_request(struct dhd_bus *bus, void *flow_ring_node); +-extern void dhd_bus_clean_flow_ring(struct dhd_bus *bus, uint16 flowid); ++extern void dhd_bus_clean_flow_ring(struct dhd_bus *bus, void *flow_ring_node); + extern void dhd_bus_flow_ring_create_response(struct dhd_bus *bus, uint16 flow_id, int32 status); + extern int dhd_bus_flow_ring_delete_request(struct dhd_bus *bus, void *flow_ring_node); + extern void dhd_bus_flow_ring_delete_response(struct dhd_bus *bus, uint16 flowid, uint32 status); +@@ -167,6 +167,7 @@ + extern void dhdpcie_bus_free_resource(struct dhd_bus *bus); + extern bool dhdpcie_bus_dongle_attach(struct dhd_bus *bus); + extern int dhd_bus_release_dongle(struct dhd_bus *bus); ++extern int dhd_bus_request_irq(struct dhd_bus *bus); + + + #endif /* BCMPCIE */ +diff -Nur a/drivers/net/wireless/bcmdhd/dhd_cdc.c c/drivers/net/wireless/bcmdhd/dhd_cdc.c +--- a/drivers/net/wireless/bcmdhd/dhd_cdc.c 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/dhd_cdc.c 2016-05-13 09:48:20.000000000 +0200 +@@ -3,7 +3,7 @@ + * + * $Copyright Open Broadcom Corporation$ + * +- * $Id: dhd_cdc.c 472193 2014-04-23 06:27:38Z $ ++ * $Id: dhd_cdc.c 492377 2014-07-21 19:54:06Z $ + * + * BDC is like CDC, except it includes a header for data packets to convey + * packet priority over the bus, and flags (e.g. to indicate checksum status +@@ -437,12 +437,14 @@ + PKTPULL(dhd->osh, pktbuf, BDC_HEADER_LEN); + #endif /* BDC */ + +-#if defined(NDISVER) && (NDISVER < 0x0630) ++#if defined(NDISVER) ++#if (NDISVER < 0x0630) + if (PKTLEN(dhd->osh, pktbuf) < (uint32) (data_offset << 2)) { + DHD_ERROR(("%s: rx data too short (%d < %d)\n", __FUNCTION__, + PKTLEN(dhd->osh, pktbuf), (data_offset * 4))); + return BCME_ERROR; + } ++#endif /* #if defined(NDISVER) */ + #endif /* (NDISVER < 0x0630) */ + + #ifdef PROP_TXSTATUS +diff -Nur a/drivers/net/wireless/bcmdhd/dhd_cfg_vendor.c c/drivers/net/wireless/bcmdhd/dhd_cfg_vendor.c +--- a/drivers/net/wireless/bcmdhd/dhd_cfg_vendor.c 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/dhd_cfg_vendor.c 2016-05-13 09:48:20.000000000 +0200 +@@ -3,9 +3,10 @@ + * + * $Copyright Open Broadcom Corporation$ + * +- * $Id: dhd_cfg_vendor.c 487126 2014-06-24 23:06:12Z $ ++ * $Id: dhd_cfg_vendor.c 495605 2014-08-07 18:41:34Z $ + */ + ++#include + #include + #include + #include +@@ -15,6 +16,7 @@ + #include + #include + #include ++#include + #include + #include + +diff -Nur a/drivers/net/wireless/bcmdhd/dhd_common.c c/drivers/net/wireless/bcmdhd/dhd_common.c +--- a/drivers/net/wireless/bcmdhd/dhd_common.c 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/dhd_common.c 2016-05-13 09:48:20.000000000 +0200 +@@ -3,7 +3,7 @@ + * + * $Copyright Open Broadcom Corporation$ + * +- * $Id: dhd_common.c 490628 2014-07-11 07:13:31Z $ ++ * $Id: dhd_common.c 492215 2014-07-20 16:44:15Z $ + */ + #include + #include +@@ -104,7 +104,7 @@ + + #if defined(DHD_DEBUG) + const char dhd_version[] = "Dongle Host Driver, version " EPI_VERSION_STR +- DHD_COMPILED " on " __DATE__ " at " __TIME__; ++ DHD_COMPILED ;//" on " __DATE__ " at " __TIME__; + #else + const char dhd_version[] = "\nDongle Host Driver, version " EPI_VERSION_STR "\nCompiled from "; + #endif +@@ -411,6 +411,10 @@ + return BCME_OK; + } + ++#ifdef PKT_STATICS ++extern pkt_statics_t tx_statics; ++extern void dhdsdio_txpktstatics(void); ++#endif + static int + dhd_doiovar(dhd_pub_t *dhd_pub, const bcm_iovar_t *vi, uint32 actionid, const char *name, + void *params, int plen, void *arg, int len, int val_size) +@@ -431,36 +435,42 @@ + case IOV_GVAL(IOV_VERSION): + /* Need to have checked buffer length */ + bcm_strncpy_s((char*)arg, len, dhd_version, len); ++#ifdef PKT_STATICS ++ memset((uint8*) &tx_statics, 0, sizeof(pkt_statics_t)); ++#endif + break; + + case IOV_GVAL(IOV_WLMSGLEVEL): +- printk("android_msg_level=0x%x\n", android_msg_level); +- printk("config_msg_level=0x%x\n", config_msg_level); ++ printf("android_msg_level=0x%x\n", android_msg_level); ++ printf("config_msg_level=0x%x\n", config_msg_level); + #if defined(WL_WIRELESS_EXT) + int_val = (int32)iw_msg_level; + bcopy(&int_val, arg, val_size); +- printk("iw_msg_level=0x%x\n", iw_msg_level); ++ printf("iw_msg_level=0x%x\n", iw_msg_level); + #endif + #ifdef WL_CFG80211 + int_val = (int32)wl_dbg_level; + bcopy(&int_val, arg, val_size); +- printk("cfg_msg_level=0x%x\n", wl_dbg_level); ++ printf("cfg_msg_level=0x%x\n", wl_dbg_level); ++#endif ++#ifdef PKT_STATICS ++ dhdsdio_txpktstatics(); + #endif + break; + + case IOV_SVAL(IOV_WLMSGLEVEL): + if (int_val & DHD_ANDROID_VAL) { + android_msg_level = (uint)(int_val & 0xFFFF); +- printk("android_msg_level=0x%x\n", android_msg_level); ++ printf("android_msg_level=0x%x\n", android_msg_level); + } + if (int_val & DHD_CONFIG_VAL) { + config_msg_level = (uint)(int_val & 0xFFFF); +- printk("config_msg_level=0x%x\n", config_msg_level); ++ printf("config_msg_level=0x%x\n", config_msg_level); + } + #if defined(WL_WIRELESS_EXT) + if (int_val & DHD_IW_VAL) { + iw_msg_level = (uint)(int_val & 0xFFFF); +- printk("iw_msg_level=0x%x\n", iw_msg_level); ++ printf("iw_msg_level=0x%x\n", iw_msg_level); + } + #endif + #ifdef WL_CFG80211 +@@ -1822,7 +1832,7 @@ + case WLC_E_PFN_BEST_BATCHING: + dhd_pno_event_handler(dhd_pub, event, (void *)event_data); + break; +-#endif ++#endif + /* These are what external supplicant/authenticator wants */ + case WLC_E_ASSOC_IND: + case WLC_E_AUTH_IND: +@@ -1852,7 +1862,7 @@ + if (type != WLC_E_LINK) { + uint8 ifindex = (uint8)hostidx; + uint8 role = dhd_flow_rings_ifindex2role(dhd_pub, ifindex); +- if (role == WLC_E_IF_ROLE_STA) { ++ if (DHD_IF_ROLE_STA(role)) { + dhd_flow_rings_delete(dhd_pub, ifindex); + } else { + dhd_flow_rings_delete_for_peer(dhd_pub, ifindex, +@@ -2007,7 +2017,7 @@ + rc = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, buf, buf_len, TRUE, 0); + rc = rc >= 0 ? 0 : rc; + if (rc) +- DHD_TRACE(("%s: failed to %s pktfilter %s, retcode = %d\n", ++ DHD_ERROR(("%s: failed to %s pktfilter %s, retcode = %d\n", + __FUNCTION__, enable?"enable":"disable", arg, rc)); + else + DHD_TRACE(("%s: successfully %s pktfilter %s\n", +@@ -2151,7 +2161,7 @@ + rc = rc >= 0 ? 0 : rc; + + if (rc) +- DHD_TRACE(("%s: failed to add pktfilter %s, retcode = %d\n", ++ DHD_ERROR(("%s: failed to add pktfilter %s, retcode = %d\n", + __FUNCTION__, arg, rc)); + else + DHD_TRACE(("%s: successfully added pktfilter %s\n", +@@ -2853,7 +2863,7 @@ + char* str; + char* endptr = NULL; + +- if ((list_str == NULL)||(*list_str == NULL)) ++ if ((list_str == NULL) || (*list_str == NULL)) + return -1; + + str = *list_str; +diff -Nur a/drivers/net/wireless/bcmdhd/dhd_config.c c/drivers/net/wireless/bcmdhd/dhd_config.c +--- a/drivers/net/wireless/bcmdhd/dhd_config.c 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/dhd_config.c 2016-05-13 09:48:20.000000000 +0200 +@@ -4,7 +4,8 @@ + + #include + #include +-#if defined(HW_OOB) ++#include ++#if defined(HW_OOB) || defined(FORCE_WOWLAN) + #include + #include + #include +@@ -35,28 +36,34 @@ + } \ + } while (0) + +-#define SBSDIO_CIS_SIZE_LIMIT 0x200 /* maximum bytes in one CIS */ + #define MAXSZ_BUF 1000 + #define MAXSZ_CONFIG 4096 + +-#define BCM43362A0_CHIP_REV 0 +-#define BCM43362A2_CHIP_REV 1 +-#define BCM43430A0_CHIP_REV 0 +-#define BCM4330B2_CHIP_REV 4 +-#define BCM43340B0_CHIP_REV 2 +-#define BCM43341B0_CHIP_REV 2 +-#define BCM43241B4_CHIP_REV 5 +-#define BCM4335A0_CHIP_REV 2 +-#define BCM4339A0_CHIP_REV 1 +-#define BCM4354A1_CHIP_REV 1 +-#define BCM4356A2_CHIP_REV 2 +- + #define FW_TYPE_STA 0 + #define FW_TYPE_APSTA 1 + #define FW_TYPE_P2P 2 + #define FW_TYPE_MFG 3 + #define FW_TYPE_G 0 + #define FW_TYPE_AG 1 ++ ++#ifdef CONFIG_PATH_AUTO_SELECT ++#define BCM4330B2_CONF_NAME "config_40183b2.txt" ++#define BCM43362A0_CONF_NAME "config_40181a0.txt" ++#define BCM43362A2_CONF_NAME "config_40181a2.txt" ++#define BCM43438A0_CONF_NAME "config_43438a0.txt" ++#define BCM43438A1_CONF_NAME "config_43438a1.txt" ++#define BCM4334B1_CONF_NAME "config_4334b1.txt" ++#define BCM43341B0_CONF_NAME "config_43341b0.txt" ++#define BCM43241B4_CONF_NAME "config_43241b4.txt" ++#define BCM4339A0_CONF_NAME "config_4339a0.txt" ++#define BCM43455C0_CONF_NAME "config_43455c0.txt" ++#define BCM4354A1_CONF_NAME "config_4354a1.txt" ++#define BCM4356A2_CONF_NAME "config_4356a2.txt" ++#define BCM4359B1_CONF_NAME "config_4359b1.txt" ++#endif ++ ++#ifdef BCMSDIO ++#define SBSDIO_CIS_SIZE_LIMIT 0x200 /* maximum bytes in one CIS */ + + const static char *bcm4330b2_fw_name[] = { + "fw_bcm40183b2.bin", +@@ -86,6 +93,13 @@ + "fw_bcm40181a2_mfg.bin" + }; + ++const static char *bcm4334b1_ag_fw_name[] = { ++ "fw_bcm4334b1_ag.bin", ++ "fw_bcm4334b1_ag_apsta.bin", ++ "fw_bcm4334b1_ag_p2p.bin", ++ "fw_bcm4334b1_ag_mfg.bin" ++}; ++ + const static char *bcm43438a0_fw_name[] = { + "fw_bcm43438a0.bin", + "fw_bcm43438a0_apsta.bin", +@@ -93,6 +107,13 @@ + "fw_bcm43438a0_mfg.bin" + }; + ++const static char *bcm43438a1_fw_name[] = { ++ "fw_bcm43438a1.bin", ++ "fw_bcm43438a1_apsta.bin", ++ "fw_bcm43438a1_p2p.bin", ++ "fw_bcm43438a1_mfg.bin" ++}; ++ + const static char *bcm43341b0_ag_fw_name[] = { + "fw_bcm43341b0_ag.bin", + "fw_bcm43341b0_ag_apsta.bin", +@@ -114,6 +135,13 @@ + "fw_bcm4339a0_ag_mfg.bin" + }; + ++const static char *bcm43455c0_ag_fw_name[] = { ++ "fw_bcm43455c0_ag.bin", ++ "fw_bcm43455c0_ag_apsta.bin", ++ "fw_bcm43455c0_ag_p2p.bin", ++ "fw_bcm43455c0_ag_mfg.bin" ++}; ++ + const static char *bcm4354a1_ag_fw_name[] = { + "fw_bcm4354a1_ag.bin", + "fw_bcm4354a1_ag_apsta.bin", +@@ -127,6 +155,22 @@ + "fw_bcm4356a2_ag_p2p.bin", + "fw_bcm4356a2_ag_mfg.bin" + }; ++ ++const static char *bcm4359b1_ag_fw_name[] = { ++ "fw_bcm4359b1_ag.bin", ++ "fw_bcm4359b1_ag_apsta.bin", ++ "fw_bcm4359b1_ag_p2p.bin", ++ "fw_bcm4359b1_ag_mfg.bin" ++}; ++#endif ++#ifdef BCMPCIE ++const static char *bcm4356a2_pcie_ag_fw_name[] = { ++ "fw_bcm4356a2_pcie_ag.bin", ++ "fw_bcm4356a2_pcie_ag_apsta.bin", ++ "fw_bcm4356a2_pcie_ag_p2p.bin", ++ "fw_bcm4356a2_pcie_ag_mfg.bin" ++}; ++#endif + + #define htod32(i) i + #define htod16(i) i +@@ -135,22 +179,57 @@ + #define htodchanspec(i) i + #define dtohchanspec(i) i + ++#ifdef BCMSDIO + void + dhd_conf_free_mac_list(wl_mac_list_ctrl_t *mac_list) + { +- CONFIG_TRACE(("%s called\n", __FUNCTION__)); ++ int i; + ++ CONFIG_TRACE(("%s called\n", __FUNCTION__)); + if (mac_list->m_mac_list_head) { +- CONFIG_TRACE(("%s Free %p\n", __FUNCTION__, mac_list->m_mac_list_head)); +- if (mac_list->m_mac_list_head->mac) { +- CONFIG_TRACE(("%s Free %p\n", __FUNCTION__, mac_list->m_mac_list_head->mac)); +- kfree(mac_list->m_mac_list_head->mac); ++ for (i = 0; i < mac_list->count; i++) { ++ if (mac_list->m_mac_list_head[i].mac) { ++ CONFIG_TRACE(("%s Free mac %p\n", __FUNCTION__, mac_list->m_mac_list_head[i].mac)); ++ kfree(mac_list->m_mac_list_head[i].mac); ++ } + } ++ CONFIG_TRACE(("%s Free m_mac_list_head %p\n", __FUNCTION__, mac_list->m_mac_list_head)); + kfree(mac_list->m_mac_list_head); + } + mac_list->count = 0; + } + ++void ++dhd_conf_free_chip_nv_path_list(wl_chip_nv_path_list_ctrl_t *chip_nv_list) ++{ ++ CONFIG_TRACE(("%s called\n", __FUNCTION__)); ++ ++ if (chip_nv_list->m_chip_nv_path_head) { ++ CONFIG_TRACE(("%s Free %p\n", __FUNCTION__, chip_nv_list->m_chip_nv_path_head)); ++ kfree(chip_nv_list->m_chip_nv_path_head); ++ } ++ chip_nv_list->count = 0; ++} ++ ++#if defined(HW_OOB) || defined(FORCE_WOWLAN) ++void ++dhd_conf_set_hw_oob_intr(bcmsdh_info_t *sdh, uint chip) ++{ ++ uint32 gpiocontrol, addr; ++ ++ if (CHIPID(chip) == BCM43362_CHIP_ID) { ++ printf("%s: Enable HW OOB for 43362\n", __FUNCTION__); ++ addr = SI_ENUM_BASE + OFFSETOF(chipcregs_t, gpiocontrol); ++ gpiocontrol = bcmsdh_reg_read(sdh, addr, 4); ++ gpiocontrol |= 0x2; ++ bcmsdh_reg_write(sdh, addr, 4, gpiocontrol); ++ bcmsdh_cfg_write(sdh, SDIO_FUNC_1, 0x10005, 0xf, NULL); ++ bcmsdh_cfg_write(sdh, SDIO_FUNC_1, 0x10006, 0x0, NULL); ++ bcmsdh_cfg_write(sdh, SDIO_FUNC_1, 0x10007, 0x2, NULL); ++ } ++} ++#endif ++ + int + dhd_conf_get_mac(dhd_pub_t *dhd, bcmsdh_info_t *sdh, uint8 *mac) + { +@@ -192,13 +271,13 @@ + if (config_msg_level & CONFIG_TRACE_LEVEL) { + printf("%s: tpl_code=0x%02x, tpl_link=0x%02x, tag=0x%02x\n", + __FUNCTION__, tpl_code, tpl_link, *ptr); +- printf("%s: value:", __FUNCTION__); ++ printk("%s: value:", __FUNCTION__); + for (i=0; i0){ ++ while (i > 0) { + if (fw_path[i] == '/') break; + i--; + } +@@ -318,7 +397,7 @@ + + /* find out the last '/' */ + i = strlen(nv_path); +- while (i>0){ ++ while (i > 0) { + if (nv_path[i] == '/') break; + i--; + } +@@ -340,6 +419,7 @@ + } + } + } ++#endif + + void + dhd_conf_set_fw_name_by_chip(dhd_pub_t *dhd, char *fw_path) +@@ -367,7 +447,7 @@ + + /* find out the last '/' */ + i = strlen(fw_path); +- while (i>0){ ++ while (i > 0) { + if (fw_path[i] == '/') break; + i--; + } +@@ -382,6 +462,7 @@ + FW_TYPE_P2P : FW_TYPE_STA))); + + switch (chip) { ++#ifdef BCMSDIO + case BCM4330_CHIP_ID: + if (ag_type == FW_TYPE_G) { + if (chiprev == BCM4330B2_CHIP_REV) +@@ -401,11 +482,14 @@ + case BCM43430_CHIP_ID: + if (chiprev == BCM43430A0_CHIP_REV) + strcpy(&fw_path[i+1], bcm43438a0_fw_name[fw_type]); ++ else if (chiprev == BCM43430A1_CHIP_REV) ++ strcpy(&fw_path[i+1], bcm43438a1_fw_name[fw_type]); + break; +- case BCM43340_CHIP_ID: +- if (chiprev == BCM43340B0_CHIP_REV) +- strcpy(&fw_path[i+1], bcm43341b0_ag_fw_name[fw_type]); ++ case BCM4334_CHIP_ID: ++ if (chiprev == BCM4334B1_CHIP_REV) ++ strcpy(&fw_path[i+1], bcm4334b1_ag_fw_name[fw_type]); + break; ++ case BCM43340_CHIP_ID: + case BCM43341_CHIP_ID: + if (chiprev == BCM43341B0_CHIP_REV) + strcpy(&fw_path[i+1], bcm43341b0_ag_fw_name[fw_type]); +@@ -418,6 +502,11 @@ + if (chiprev == BCM4335A0_CHIP_REV) + strcpy(&fw_path[i+1], bcm4339a0_ag_fw_name[fw_type]); + break; ++ case BCM4345_CHIP_ID: ++ case BCM43454_CHIP_ID: ++ if (chiprev == BCM43455C0_CHIP_REV) ++ strcpy(&fw_path[i+1], bcm43455c0_ag_fw_name[fw_type]); ++ break; + case BCM4339_CHIP_ID: + if (chiprev == BCM4339A0_CHIP_REV) + strcpy(&fw_path[i+1], bcm4339a0_ag_fw_name[fw_type]); +@@ -428,57 +517,261 @@ + else if (chiprev == BCM4356A2_CHIP_REV) + strcpy(&fw_path[i+1], bcm4356a2_ag_fw_name[fw_type]); + break; ++ case BCM4356_CHIP_ID: ++ case BCM4371_CHIP_ID: ++ if (chiprev == BCM4356A2_CHIP_REV) ++ strcpy(&fw_path[i+1], bcm4356a2_ag_fw_name[fw_type]); ++ break; ++ case BCM4359_CHIP_ID: ++ if (chiprev == BCM4359B1_CHIP_REV) ++ strcpy(&fw_path[i+1], bcm4359b1_ag_fw_name[fw_type]); ++ break; ++#endif ++#ifdef BCMPCIE ++ case BCM4356_CHIP_ID: ++ if (chiprev == BCM4356A2_CHIP_REV) ++ strcpy(&fw_path[i+1], bcm4356a2_pcie_ag_fw_name[fw_type]); ++ break; ++#endif + } + + printf("%s: firmware_path=%s\n", __FUNCTION__, fw_path); + } + +-#if defined(HW_OOB) + void +-dhd_conf_set_hw_oob_intr(bcmsdh_info_t *sdh, uint chip) ++dhd_conf_set_nv_name_by_chip(dhd_pub_t *dhd, char *nv_path) + { +- uint32 gpiocontrol, addr; ++ int matched=-1; ++ uint chip, chiprev; ++ int i; + +- if (CHIPID(chip) == BCM43362_CHIP_ID) { +- printf("%s: Enable HW OOB for 43362\n", __FUNCTION__); +- addr = SI_ENUM_BASE + OFFSETOF(chipcregs_t, gpiocontrol); +- gpiocontrol = bcmsdh_reg_read(sdh, addr, 4); +- gpiocontrol |= 0x2; +- bcmsdh_reg_write(sdh, addr, 4, gpiocontrol); +- bcmsdh_cfg_write(sdh, SDIO_FUNC_1, 0x10005, 0xf, NULL); +- bcmsdh_cfg_write(sdh, SDIO_FUNC_1, 0x10006, 0x0, NULL); +- bcmsdh_cfg_write(sdh, SDIO_FUNC_1, 0x10007, 0x2, NULL); ++ chip = dhd->conf->chip; ++ chiprev = dhd->conf->chiprev; ++ ++ for (i = 0;i < dhd->conf->nv_by_chip.count;i++) { ++ if (chip == dhd->conf->nv_by_chip.m_chip_nv_path_head[i].chip && ++ chiprev==dhd->conf->nv_by_chip.m_chip_nv_path_head[i].chiprev) { ++ matched = i; ++ break; ++ } + } +-} ++ if (matched < 0) ++ return; ++ ++ if (nv_path[0] == '\0') { ++#ifdef CONFIG_BCMDHD_NVRAM_PATH ++ bcm_strncpy_s(nv_path, MOD_PARAM_PATHLEN-1, CONFIG_BCMDHD_NVRAM_PATH, MOD_PARAM_PATHLEN-1); ++ if (nv_path[0] == '\0') + #endif ++ { ++ printf("nvram path is null\n"); ++ return; ++ } ++ } + +-void +-dhd_conf_set_fw_path(dhd_pub_t *dhd, char *fw_path) +-{ +- if (dhd->conf->fw_path[0]) { +- strcpy(fw_path, dhd->conf->fw_path); +- printf("%s: fw_path is changed to %s\n", __FUNCTION__, fw_path); ++ /* find out the last '/' */ ++ i = strlen(nv_path); ++ while (i > 0) { ++ if (nv_path[i] == '/') break; ++ i--; + } ++ ++ strcpy(&nv_path[i+1], dhd->conf->nv_by_chip.m_chip_nv_path_head[matched].name); ++ ++ printf("%s: nvram_path=%s\n", __FUNCTION__, nv_path); + } + + void +-dhd_conf_set_nv_path(dhd_pub_t *dhd, char *nv_path) ++dhd_conf_set_conf_path_by_nv_path(dhd_pub_t *dhd, char *conf_path, char *nv_path) + { +- if (dhd->conf->nv_path[0]) { +- strcpy(nv_path, dhd->conf->nv_path); +- printf("%s: nv_path is changed to %s\n", __FUNCTION__, nv_path); ++ int i; ++ ++ if (nv_path[0] == '\0') { ++#ifdef CONFIG_BCMDHD_NVRAM_PATH ++ bcm_strncpy_s(conf_path, MOD_PARAM_PATHLEN-1, CONFIG_BCMDHD_NVRAM_PATH, MOD_PARAM_PATHLEN-1); ++ if (nv_path[0] == '\0') ++#endif ++ { ++ printf("nvram path is null\n"); ++ return; ++ } ++ } else ++ strcpy(conf_path, nv_path); ++ ++ /* find out the last '/' */ ++ i = strlen(conf_path); ++ while (i > 0) { ++ if (conf_path[i] == '/') break; ++ i--; + } ++ strcpy(&conf_path[i+1], "config.txt"); ++ ++ printf("%s: config_path=%s\n", __FUNCTION__, conf_path); + } + ++#ifdef CONFIG_PATH_AUTO_SELECT ++void ++dhd_conf_set_conf_name_by_chip(dhd_pub_t *dhd, char *conf_path) ++{ ++ uint chip, chiprev; ++ int i; ++ ++ chip = dhd->conf->chip; ++ chiprev = dhd->conf->chiprev; ++ ++ if (conf_path[0] == '\0') { ++ printf("config path is null\n"); ++ return; ++ } ++ ++ /* find out the last '/' */ ++ i = strlen(conf_path); ++ while (i > 0) { ++ if (conf_path[i] == '/') break; ++ i--; ++ } ++ ++ switch (chip) { ++#ifdef BCMSDIO ++ case BCM4330_CHIP_ID: ++ if (chiprev == BCM4330B2_CHIP_REV) ++ strcpy(&conf_path[i+1], BCM4330B2_CONF_NAME); ++ break; ++ case BCM43362_CHIP_ID: ++ if (chiprev == BCM43362A0_CHIP_REV) ++ strcpy(&conf_path[i+1], BCM43362A0_CONF_NAME); ++ else ++ strcpy(&conf_path[i+1], BCM43362A2_CONF_NAME); ++ break; ++ case BCM43430_CHIP_ID: ++ if (chiprev == BCM43430A0_CHIP_REV) ++ strcpy(&conf_path[i+1], BCM43438A0_CONF_NAME); ++ else if (chiprev == BCM43430A1_CHIP_REV) ++ strcpy(&conf_path[i+1], BCM43438A1_CONF_NAME); ++ break; ++ case BCM4334_CHIP_ID: ++ if (chiprev == BCM4334B1_CHIP_REV) ++ strcpy(&conf_path[i+1], BCM4334B1_CONF_NAME); ++ break; ++ case BCM43340_CHIP_ID: ++ case BCM43341_CHIP_ID: ++ if (chiprev == BCM43341B0_CHIP_REV) ++ strcpy(&conf_path[i+1], BCM43341B0_CONF_NAME); ++ break; ++ case BCM4324_CHIP_ID: ++ if (chiprev == BCM43241B4_CHIP_REV) ++ strcpy(&conf_path[i+1], BCM43241B4_CONF_NAME); ++ break; ++ case BCM4335_CHIP_ID: ++ if (chiprev == BCM4335A0_CHIP_REV) ++ strcpy(&conf_path[i+1], BCM4339A0_CONF_NAME); ++ break; ++ case BCM4345_CHIP_ID: ++ case BCM43454_CHIP_ID: ++ if (chiprev == BCM43455C0_CHIP_REV) ++ strcpy(&conf_path[i+1], BCM43455C0_CONF_NAME); ++ break; ++ case BCM4339_CHIP_ID: ++ if (chiprev == BCM4339A0_CHIP_REV) ++ strcpy(&conf_path[i+1], BCM4339A0_CONF_NAME); ++ break; ++ case BCM4354_CHIP_ID: ++ if (chiprev == BCM4354A1_CHIP_REV) ++ strcpy(&conf_path[i+1], BCM4354A1_CONF_NAME); ++ else if (chiprev == BCM4356A2_CHIP_REV) ++ strcpy(&conf_path[i+1], BCM4356A2_CONF_NAME); ++ break; ++ case BCM4356_CHIP_ID: ++ case BCM4371_CHIP_ID: ++ if (chiprev == BCM4356A2_CHIP_REV) ++ strcpy(&conf_path[i+1], BCM4356A2_CONF_NAME); ++ break; ++ case BCM4359_CHIP_ID: ++ if (chiprev == BCM4359B1_CHIP_REV) ++ strcpy(&conf_path[i+1], BCM4359B1_CONF_NAME); ++ break; ++#endif ++#ifdef BCMPCIE ++ case BCM4356_CHIP_ID: ++ if (chiprev == BCM4356A2_CHIP_REV) ++ strcpy(&conf_path[i+1], BCM4356A2_CONF_NAME); ++ break; ++#endif ++ } ++ ++ printf("%s: config_path=%s\n", __FUNCTION__, conf_path); ++} ++#endif ++ + int +-dhd_conf_set_band(dhd_pub_t *dhd) ++dhd_conf_set_fw_int_cmd(dhd_pub_t *dhd, char *name, uint cmd, int val, ++ int def, bool down) + { + int bcmerror = -1; + +- printf("%s: Set band %d\n", __FUNCTION__, dhd->conf->band); +- if ((bcmerror = dhd_wl_ioctl_cmd(dhd, WLC_SET_BAND, &dhd->conf->band, +- sizeof(dhd->conf->band), TRUE, 0)) < 0) +- CONFIG_ERROR(("%s: WLC_SET_BAND setting failed %d\n", __FUNCTION__, bcmerror)); ++ if (val >= def) { ++ if (down) { ++ if ((bcmerror = dhd_wl_ioctl_cmd(dhd, WLC_DOWN, NULL, 0, TRUE, 0)) < 0) ++ CONFIG_ERROR(("%s: WLC_DOWN setting failed %d\n", __FUNCTION__, bcmerror)); ++ } ++ printf("%s: set %s %d %d\n", __FUNCTION__, name, cmd, val); ++ if ((bcmerror = dhd_wl_ioctl_cmd(dhd, cmd, &val, sizeof(val), TRUE, 0)) < 0) ++ CONFIG_ERROR(("%s: %s setting failed %d\n", __FUNCTION__, name, bcmerror)); ++ } ++ return bcmerror; ++} ++ ++int ++dhd_conf_set_fw_int_struct_cmd(dhd_pub_t *dhd, char *name, uint cmd, ++ int *val, int len, bool down) ++{ ++ int bcmerror = -1; ++ ++ if (down) { ++ if ((bcmerror = dhd_wl_ioctl_cmd(dhd, WLC_DOWN, NULL, 0, TRUE, 0)) < 0) ++ CONFIG_ERROR(("%s: WLC_DOWN setting failed %d\n", __FUNCTION__, bcmerror)); ++ } ++ if ((bcmerror = dhd_wl_ioctl_cmd(dhd, cmd, val, len, TRUE, 0)) < 0) ++ CONFIG_ERROR(("%s: %s setting failed %d\n", __FUNCTION__, name, bcmerror)); ++ ++ return bcmerror; ++} ++ ++int ++dhd_conf_set_fw_string_cmd(dhd_pub_t *dhd, char *cmd, int val, int def, ++ bool down) ++{ ++ int bcmerror = -1; ++ char iovbuf[WL_EVENTING_MASK_LEN + 12]; /* Room for "event_msgs" + '\0' + bitvec */ ++ ++ if (val >= def) { ++ if (down) { ++ if ((bcmerror = dhd_wl_ioctl_cmd(dhd, WLC_DOWN, NULL, 0, TRUE, 0)) < 0) ++ CONFIG_ERROR(("%s: WLC_DOWN setting failed %d\n", __FUNCTION__, bcmerror)); ++ } ++ printf("%s: set %s %d\n", __FUNCTION__, cmd, val); ++ bcm_mkiovar(cmd, (char *)&val, 4, iovbuf, sizeof(iovbuf)); ++ if ((bcmerror = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) ++ CONFIG_ERROR(("%s: %s setting failed %d\n", __FUNCTION__, cmd, bcmerror)); ++ } ++ return bcmerror; ++} ++ ++int ++dhd_conf_set_fw_string_struct_cmd(dhd_pub_t *dhd, char *cmd, char *val, ++ int len, bool down) ++{ ++ int bcmerror = -1; ++ char iovbuf[WLC_IOCTL_SMLEN]; ++ ++ if (down) { ++ if ((bcmerror = dhd_wl_ioctl_cmd(dhd, WLC_DOWN, NULL, 0, TRUE, 0)) < 0) ++ CONFIG_ERROR(("%s: WLC_DOWN setting failed %d\n", __FUNCTION__, bcmerror)); ++ } ++ printf("%s: set %s\n", __FUNCTION__, cmd); ++ bcm_mkiovar(cmd, val, len, iovbuf, sizeof(iovbuf)); ++ if ((bcmerror = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) ++ CONFIG_ERROR(("%s: %s setting failed %d\n", __FUNCTION__, cmd, bcmerror)); + + return bcmerror; + } +@@ -500,15 +793,11 @@ + dhd_conf_set_country(dhd_pub_t *dhd) + { + int bcmerror = -1; +- char iovbuf[WL_EVENTING_MASK_LEN + 12]; /* Room for "event_msgs" + '\0' + bitvec */ + + memset(&dhd->dhd_cspec, 0, sizeof(wl_country_t)); +- printf("%s: Set country %s, revision %d\n", __FUNCTION__, ++ printf("%s: set country %s, revision %d\n", __FUNCTION__, + dhd->conf->cspec.ccode, dhd->conf->cspec.rev); +- bcm_mkiovar("country", (char *)&dhd->conf->cspec, +- sizeof(wl_country_t), iovbuf, sizeof(iovbuf)); +- if ((bcmerror = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) +- printf("%s: country code setting failed %d\n", __FUNCTION__, bcmerror); ++ dhd_conf_set_fw_string_struct_cmd(dhd, "country", (char *)&dhd->conf->cspec, sizeof(wl_country_t), FALSE); + + return bcmerror; + } +@@ -521,9 +810,28 @@ + memset(cspec, 0, sizeof(wl_country_t)); + bcm_mkiovar("country", NULL, 0, (char*)cspec, sizeof(wl_country_t)); + if ((bcmerror = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, cspec, sizeof(wl_country_t), FALSE, 0)) < 0) +- printf("%s: country code getting failed %d\n", __FUNCTION__, bcmerror); ++ CONFIG_ERROR(("%s: country code getting failed %d\n", __FUNCTION__, bcmerror)); + else + printf("Country code: %s (%s/%d)\n", cspec->country_abbrev, cspec->ccode, cspec->rev); ++ ++ return bcmerror; ++} ++ ++int ++dhd_conf_get_country_from_config(dhd_pub_t *dhd, wl_country_t *cspec) ++{ ++ int bcmerror = -1, i; ++ struct dhd_conf *conf = dhd->conf; ++ ++ for (i = 0; i < conf->country_list.count; i++) { ++ if (strcmp(cspec->country_abbrev, conf->country_list.cspec[i].country_abbrev) == 0) { ++ memcpy(cspec->ccode, ++ conf->country_list.cspec[i].ccode, WLC_CNTRY_BUF_SZ); ++ cspec->rev = conf->country_list.cspec[i].rev; ++ printf("%s: %s/%d\n", __FUNCTION__, cspec->ccode, cspec->rev); ++ return 0; ++ } ++ } + + return bcmerror; + } +@@ -549,7 +857,7 @@ + + band = dhd_conf_get_band(dhd); + +- if (bcmerror || ((band==WLC_BAND_AUTO || band==WLC_BAND_2G) && ++ if (bcmerror || ((band == WLC_BAND_AUTO || band == WLC_BAND_2G) && + dtoh32(list->count)<11)) { + CONFIG_ERROR(("%s: bcmerror=%d, # of channels %d\n", + __FUNCTION__, bcmerror, dtoh32(list->count))); +@@ -589,72 +897,31 @@ + dhd_conf_set_roam(dhd_pub_t *dhd) + { + int bcmerror = -1; +- char iovbuf[WL_EVENTING_MASK_LEN + 12]; /* Room for "event_msgs" + '\0' + bitvec */ + struct dhd_conf *conf = dhd->conf; + +- printf("%s: Set roam_off %d\n", __FUNCTION__, conf->roam_off); + dhd_roam_disable = conf->roam_off; +- bcm_mkiovar("roam_off", (char *)&conf->roam_off, 4, iovbuf, sizeof(iovbuf)); +- dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); ++ dhd_conf_set_fw_string_cmd(dhd, "roam_off", dhd->conf->roam_off, 0, FALSE); + + if (!conf->roam_off || !conf->roam_off_suspend) { +- printf("%s: Set roam_trigger %d\n", __FUNCTION__, conf->roam_trigger[0]); +- if ((bcmerror = dhd_wl_ioctl_cmd(dhd, WLC_SET_ROAM_TRIGGER, conf->roam_trigger, +- sizeof(conf->roam_trigger), TRUE, 0)) < 0) +- CONFIG_ERROR(("%s: roam trigger setting failed %d\n", __FUNCTION__, bcmerror)); +- +- printf("%s: Set roam_scan_period %d\n", __FUNCTION__, conf->roam_scan_period[0]); +- if ((bcmerror = dhd_wl_ioctl_cmd(dhd, WLC_SET_ROAM_SCAN_PERIOD, conf->roam_scan_period, +- sizeof(conf->roam_scan_period), TRUE, 0)) < 0) +- CONFIG_ERROR(("%s: roam scan period setting failed %d\n", __FUNCTION__, bcmerror)); +- +- printf("%s: Set roam_delta %d\n", __FUNCTION__, conf->roam_delta[0]); +- if ((bcmerror = dhd_wl_ioctl_cmd(dhd, WLC_SET_ROAM_DELTA, conf->roam_delta, +- sizeof(conf->roam_delta), TRUE, 0)) < 0) +- CONFIG_ERROR(("%s: roam delta setting failed %d\n", __FUNCTION__, bcmerror)); +- +- printf("%s: Set fullroamperiod %d\n", __FUNCTION__, conf->fullroamperiod); +- bcm_mkiovar("fullroamperiod", (char *)&conf->fullroamperiod, 4, iovbuf, sizeof(iovbuf)); +- if ((bcmerror = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) +- CONFIG_ERROR(("%s: roam fullscan period setting failed %d\n", __FUNCTION__, bcmerror)); ++ printf("%s: set roam_trigger %d\n", __FUNCTION__, conf->roam_trigger[0]); ++ dhd_conf_set_fw_int_struct_cmd(dhd, "WLC_SET_ROAM_TRIGGER", WLC_SET_ROAM_TRIGGER, ++ conf->roam_trigger, sizeof(conf->roam_trigger), FALSE); ++ ++ printf("%s: set roam_scan_period %d\n", __FUNCTION__, conf->roam_scan_period[0]); ++ dhd_conf_set_fw_int_struct_cmd(dhd, "WLC_SET_ROAM_SCAN_PERIOD", WLC_SET_ROAM_SCAN_PERIOD, ++ conf->roam_scan_period, sizeof(conf->roam_scan_period), FALSE); ++ ++ printf("%s: set roam_delta %d\n", __FUNCTION__, conf->roam_delta[0]); ++ dhd_conf_set_fw_int_struct_cmd(dhd, "WLC_SET_ROAM_DELTA", WLC_SET_ROAM_DELTA, ++ conf->roam_delta, sizeof(conf->roam_delta), FALSE); ++ ++ dhd_conf_set_fw_string_cmd(dhd, "fullroamperiod", dhd->conf->fullroamperiod, 1, FALSE); + } + + return bcmerror; + } + + void +-dhd_conf_set_mimo_bw_cap(dhd_pub_t *dhd) +-{ +- int bcmerror = -1; +- char iovbuf[WL_EVENTING_MASK_LEN + 12]; /* Room for "event_msgs" + '\0' + bitvec */ +- uint32 mimo_bw_cap; +- +- if (dhd->conf->mimo_bw_cap >= 0) { +- mimo_bw_cap = (uint)dhd->conf->mimo_bw_cap; +- if ((bcmerror = dhd_wl_ioctl_cmd(dhd, WLC_DOWN, NULL, 0, TRUE, 0)) < 0) +- CONFIG_ERROR(("%s: WLC_DOWN setting failed %d\n", __FUNCTION__, bcmerror)); +- /* 0:HT20 in ALL, 1:HT40 in ALL, 2: HT20 in 2G HT40 in 5G */ +- printf("%s: Set mimo_bw_cap %d\n", __FUNCTION__, mimo_bw_cap); +- bcm_mkiovar("mimo_bw_cap", (char *)&mimo_bw_cap, 4, iovbuf, sizeof(iovbuf)); +- if ((bcmerror = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) +- CONFIG_ERROR(("%s: mimo_bw_cap setting failed %d\n", __FUNCTION__, bcmerror)); +- } +-} +- +-void +-dhd_conf_force_wme(dhd_pub_t *dhd) +-{ +- int bcmerror = -1; +- char iovbuf[WL_EVENTING_MASK_LEN + 12]; /* Room for "event_msgs" + '\0' + bitvec */ +- +- if (dhd->conf->chip == BCM43362_CHIP_ID && dhd->conf->force_wme_ac) { +- bcm_mkiovar("force_wme_ac", (char *)&dhd->conf->force_wme_ac, 4, iovbuf, sizeof(iovbuf)); +- if ((bcmerror = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) +- CONFIG_ERROR(("%s: force_wme_ac setting failed %d\n", __FUNCTION__, bcmerror)); +- } +-} +- +-void + dhd_conf_get_wme(dhd_pub_t *dhd, edcf_acparam_t *acp) + { + int bcmerror = -1; +@@ -678,22 +945,22 @@ + CONFIG_TRACE(("%s: BK: aci %d aifsn %d ecwmin %d ecwmax %d size %d\n", __FUNCTION__, + acparam->ACI, acparam->ACI&EDCF_AIFSN_MASK, + acparam->ECW&EDCF_ECWMIN_MASK, (acparam->ECW&EDCF_ECWMAX_MASK)>>EDCF_ECWMAX_SHIFT, +- sizeof(acp))); ++ (int)sizeof(acp))); + acparam = &acp[AC_BE]; + CONFIG_TRACE(("%s: BE: aci %d aifsn %d ecwmin %d ecwmax %d size %d\n", __FUNCTION__, + acparam->ACI, acparam->ACI&EDCF_AIFSN_MASK, + acparam->ECW&EDCF_ECWMIN_MASK, (acparam->ECW&EDCF_ECWMAX_MASK)>>EDCF_ECWMAX_SHIFT, +- sizeof(acp))); ++ (int)sizeof(acp))); + acparam = &acp[AC_VI]; + CONFIG_TRACE(("%s: VI: aci %d aifsn %d ecwmin %d ecwmax %d size %d\n", __FUNCTION__, + acparam->ACI, acparam->ACI&EDCF_AIFSN_MASK, + acparam->ECW&EDCF_ECWMIN_MASK, (acparam->ECW&EDCF_ECWMAX_MASK)>>EDCF_ECWMAX_SHIFT, +- sizeof(acp))); ++ (int)sizeof(acp))); + acparam = &acp[AC_VO]; + CONFIG_TRACE(("%s: VO: aci %d aifsn %d ecwmin %d ecwmax %d size %d\n", __FUNCTION__, + acparam->ACI, acparam->ACI&EDCF_AIFSN_MASK, + acparam->ECW&EDCF_ECWMIN_MASK, (acparam->ECW&EDCF_ECWMAX_MASK)>>EDCF_ECWMAX_SHIFT, +- sizeof(acp))); ++ (int)sizeof(acp))); + + return; + } +@@ -701,10 +968,8 @@ + void + dhd_conf_update_wme(dhd_pub_t *dhd, edcf_acparam_t *acparam_cur, int aci) + { +- int bcmerror = -1; + int aifsn, ecwmin, ecwmax; + edcf_acparam_t *acp; +- char iovbuf[WLC_IOCTL_SMLEN]; + struct dhd_conf *conf = dhd->conf; + + /* Default value */ +@@ -729,19 +994,15 @@ + CONFIG_TRACE(("%s: mod aci %d aifsn %d ecwmin %d ecwmax %d size %d\n", __FUNCTION__, + acp->ACI, acp->ACI&EDCF_AIFSN_MASK, + acp->ECW&EDCF_ECWMIN_MASK, (acp->ECW&EDCF_ECWMAX_MASK)>>EDCF_ECWMAX_SHIFT, +- sizeof(edcf_acparam_t))); ++ (int)sizeof(edcf_acparam_t))); + + /* + * Now use buf as an output buffer. + * Put WME acparams after "wme_ac\0" in buf. + * NOTE: only one of the four ACs can be set at a time. + */ +- bcm_mkiovar("wme_ac_sta", (char*)acp, sizeof(edcf_acparam_t), iovbuf, +- sizeof(iovbuf)); +- if ((bcmerror = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), FALSE, 0)) < 0) { +- CONFIG_ERROR(("%s: wme_ac_sta setting failed %d\n", __FUNCTION__, bcmerror)); +- return; +- } ++ dhd_conf_set_fw_string_struct_cmd(dhd, "wme_ac_sta", (char *)acp, sizeof(edcf_acparam_t), FALSE); ++ + } + + void +@@ -773,58 +1034,35 @@ + return; + } + +-void +-dhd_conf_set_stbc(dhd_pub_t *dhd) +-{ +- int bcmerror = -1; +- char iovbuf[WL_EVENTING_MASK_LEN + 12]; /* Room for "event_msgs" + '\0' + bitvec */ +- uint stbc = 0; +- +- if (dhd->conf->chip == BCM4324_CHIP_ID && dhd->conf->stbc >= 0) { +- stbc = (uint)dhd->conf->stbc; +- printf("%s: set stbc_tx %d\n", __FUNCTION__, stbc); +- bcm_mkiovar("stbc_tx", (char *)&stbc, 4, iovbuf, sizeof(iovbuf)); +- if ((bcmerror = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) +- CONFIG_ERROR(("%s: stbc_tx setting failed %d\n", __FUNCTION__, bcmerror)); +- +- printf("%s: set stbc_rx %d\n", __FUNCTION__, stbc); +- bcm_mkiovar("stbc_rx", (char *)&stbc, 4, iovbuf, sizeof(iovbuf)); +- if ((bcmerror = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) +- CONFIG_ERROR(("%s: stbc_rx setting failed %d\n", __FUNCTION__, bcmerror)); +- } +-} +- +-void +-dhd_conf_set_phyoclscdenable(dhd_pub_t *dhd) +-{ +- int bcmerror = -1; +- char iovbuf[WL_EVENTING_MASK_LEN + 12]; /* Room for "event_msgs" + '\0' + bitvec */ +- uint phy_oclscdenable = 0; +- +- if (dhd->conf->chip == BCM4324_CHIP_ID && dhd->conf->phy_oclscdenable >= 0) { +- phy_oclscdenable = (uint)dhd->conf->phy_oclscdenable; +- printf("%s: set stbc_tx %d\n", __FUNCTION__, phy_oclscdenable); +- bcm_mkiovar("phy_oclscdenable", (char *)&phy_oclscdenable, 4, iovbuf, sizeof(iovbuf)); +- if ((bcmerror = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) +- CONFIG_ERROR(("%s: stbc_tx setting failed %d\n", __FUNCTION__, bcmerror)); +- } +-} +- + #ifdef PKT_FILTER_SUPPORT + void + dhd_conf_add_pkt_filter(dhd_pub_t *dhd) + { + int i; ++ char str[12]; ++#define MACS "%02x%02x%02x%02x%02x%02x" + + /* +- All pkt: pkt_filter_add=99 0 0 0 0x000000000000 0x000000000000 +- Netbios pkt: 120 0 0 12 0xFFFF000000000000000000FF000000000000000000000000FFFF 0x0800000000000000000000110000000000000000000000000089 +- */ +- for(i=0; iconf->pkt_filter_add.count; i++) { ++ * All pkt: pkt_filter_add=99 0 0 0 0x000000000000 0x000000000000 ++ * Netbios pkt: 120 0 0 12 0xFFFF000000000000000000FF000000000000000000000000FFFF 0x0800000000000000000000110000000000000000000000000089 ++ */ ++ for(i=0; iconf->pkt_filter_add.count; i++) { + dhd->pktfilter[i+dhd->pktfilter_count] = dhd->conf->pkt_filter_add.filter[i]; + printf("%s: %s\n", __FUNCTION__, dhd->pktfilter[i+dhd->pktfilter_count]); + } + dhd->pktfilter_count += i; ++ ++ if (dhd->conf->pkt_filter_magic) { ++ strcpy(&dhd->conf->pkt_filter_add.filter[dhd->conf->pkt_filter_add.count][0], "256 0 1 0 0x"); ++ for (i=0; i<16; i++) ++ strcat(&dhd->conf->pkt_filter_add.filter[dhd->conf->pkt_filter_add.count][0], "FFFFFFFFFFFF"); ++ strcat(&dhd->conf->pkt_filter_add.filter[dhd->conf->pkt_filter_add.count][0], " 0x"); ++ sprintf(str, MACS, MAC2STRDBG(dhd->mac.octet)); ++ for (i=0; i<16; i++) ++ strcat(&dhd->conf->pkt_filter_add.filter[dhd->conf->pkt_filter_add.count][0], str); ++ dhd->pktfilter[dhd->pktfilter_count] = dhd->conf->pkt_filter_add.filter[dhd->conf->pkt_filter_add.count]; ++ dhd->pktfilter_count += 1; ++ } + } + + bool +@@ -832,11 +1070,14 @@ + { + int i; + +- for(i=0; iconf->pkt_filter_del.count; i++) { +- if (id == dhd->conf->pkt_filter_del.id[i]) { +- printf("%s: %d\n", __FUNCTION__, dhd->conf->pkt_filter_del.id[i]); +- return true; ++ if (dhd && dhd->conf) { ++ for (i=0; i < dhd->conf->pkt_filter_del.count; i++) { ++ if (id == dhd->conf->pkt_filter_del.id[i]) { ++ printf("%s: %d\n", __FUNCTION__, dhd->conf->pkt_filter_del.id[i]); ++ return true; ++ } + } ++ return false; + } + return false; + } +@@ -869,85 +1110,28 @@ + #endif /* PKT_FILTER_SUPPORT */ + + void +-dhd_conf_set_srl(dhd_pub_t *dhd) +-{ +- int bcmerror = -1; +- uint srl = 0; +- +- if (dhd->conf->srl >= 0) { +- srl = (uint)dhd->conf->srl; +- printf("%s: set srl %d\n", __FUNCTION__, srl); +- if ((bcmerror = dhd_wl_ioctl_cmd(dhd, WLC_SET_SRL, &srl , sizeof(srl), true, 0)) < 0) +- CONFIG_ERROR(("%s: WLC_SET_SRL setting failed %d\n", __FUNCTION__, bcmerror)); +- } +-} +- +-void +-dhd_conf_set_lrl(dhd_pub_t *dhd) +-{ +- int bcmerror = -1; +- uint lrl = 0; +- +- if (dhd->conf->lrl >= 0) { +- lrl = (uint)dhd->conf->lrl; +- printf("%s: set lrl %d\n", __FUNCTION__, lrl); +- if ((bcmerror = dhd_wl_ioctl_cmd(dhd, WLC_SET_LRL, &lrl , sizeof(lrl), true, 0)) < 0) +- CONFIG_ERROR(("%s: WLC_SET_LRL setting failed %d\n", __FUNCTION__, bcmerror)); +- } +-} +- +-void +-dhd_conf_set_glom(dhd_pub_t *dhd) +-{ +- int bcmerror = -1; +- char iovbuf[WL_EVENTING_MASK_LEN + 12]; /* Room for "event_msgs" + '\0' + bitvec */ +- uint32 bus_txglom = 0; +- +- if (dhd->conf->bus_txglom) { +- bus_txglom = (uint)dhd->conf->bus_txglom; +- printf("%s: set bus:txglom %d\n", __FUNCTION__, bus_txglom); +- bcm_mkiovar("bus:txglom", (char *)&bus_txglom, 4, iovbuf, sizeof(iovbuf)); +- dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); +- if ((bcmerror = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) +- CONFIG_ERROR(("%s: bus:txglom setting failed %d\n", __FUNCTION__, bcmerror)); +- } +-} +- +-void +-dhd_conf_set_ampdu_ba_wsize(dhd_pub_t *dhd) ++dhd_conf_set_disable_proptx(dhd_pub_t *dhd) + { +- int bcmerror = -1; +- char iovbuf[WL_EVENTING_MASK_LEN + 12]; /* Room for "event_msgs" + '\0' + bitvec */ +- uint32 ampdu_ba_wsize = dhd->conf->ampdu_ba_wsize; +- +- /* Set ampdu ba wsize */ +- if (ampdu_ba_wsize > 0) { +- printf("%s: set ampdu_ba_wsize %d\n", __FUNCTION__, ampdu_ba_wsize); +- bcm_mkiovar("ampdu_ba_wsize", (char *)&du_ba_wsize, 4, iovbuf, sizeof(iovbuf)); +- if ((bcmerror = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, +- sizeof(iovbuf), TRUE, 0)) < 0) { +- DHD_ERROR(("%s Set ampdu_ba_wsize to %d failed %d\n", +- __FUNCTION__, ampdu_ba_wsize, bcmerror)); +- } +- } ++ printf("%s: set disable_proptx %d\n", __FUNCTION__, dhd->conf->disable_proptx); ++ disable_proptx = dhd->conf->disable_proptx; + } + +-void +-dhd_conf_set_spect(dhd_pub_t *dhd) ++int ++dhd_conf_get_pm(dhd_pub_t *dhd) + { +- int bcmerror = -1; +- uint spect = 0; +- +- if (dhd->conf->spect >= 0) { +- spect = (uint)dhd->conf->spect; +- if ((bcmerror = dhd_wl_ioctl_cmd(dhd, WLC_DOWN, NULL, 0, TRUE, 0)) < 0) +- CONFIG_ERROR(("%s: WLC_DOWN setting failed %d\n", __FUNCTION__, bcmerror)); +- printf("%s: set spect %d\n", __FUNCTION__, spect); +- if ((bcmerror = dhd_wl_ioctl_cmd(dhd, WLC_SET_SPECT_MANAGMENT, &spect , sizeof(spect), true, 0)) < 0) +- CONFIG_ERROR(("%s: WLC_SET_SPECT_MANAGMENT setting failed %d\n", __FUNCTION__, bcmerror)); +- } ++ if (dhd && dhd->conf) ++ return dhd->conf->pm; ++ return -1; + } + ++int ++dhd_conf_get_tcpack_sup_mode(dhd_pub_t *dhd) ++{ ++ if (dhd && dhd->conf) ++ return dhd->conf->tcpack_sup_mode; ++ return -1; ++} ++ + unsigned int + process_config_vars(char *varbuf, unsigned int len, char *pickbuf, char *param) + { +@@ -982,13 +1166,13 @@ + changenewline = FALSE; + continue; + } +- if (!memcmp(&varbuf[n], param, strlen(param)) && column==0) { ++ if (!memcmp(&varbuf[n], param, strlen(param)) && column == 0) { + pick = TRUE; + column = strlen(param); + n += column; + pick_column = 0; + } else { +- if (pick && column==0) ++ if (pick && column == 0) + pick = FALSE; + else + column++; +@@ -996,7 +1180,7 @@ + if (pick) { + if (varbuf[n] == 0x9) + continue; +- if (pick_column>0 && pickbuf[pick_column-1]==' ' && varbuf[n]==' ') ++ if (pick_column>0 && pickbuf[pick_column-1] == ' ' && varbuf[n] == ' ') + continue; + pickbuf[pick_column] = varbuf[n]; + pick_column++; +@@ -1010,7 +1194,14 @@ + dhd_conf_read_log_level(dhd_pub_t *dhd, char *bufp, uint len) + { + uint len_val; +- char pick[MAXSZ_BUF]; ++ char *pick; ++ ++ pick = MALLOC(dhd->osh, MAXSZ_BUF); ++ if (!pick) { ++ CONFIG_ERROR(("%s: Failed to allocate memory %d bytes\n", ++ __FUNCTION__, MAXSZ_BUF)); ++ return; ++ } + + /* Process dhd_msglevel */ + memset(pick, 0, MAXSZ_BUF); +@@ -1019,6 +1210,7 @@ + dhd_msg_level = (int)simple_strtol(pick, NULL, 0); + printf("%s: dhd_msg_level = 0x%X\n", __FUNCTION__, dhd_msg_level); + } ++#ifdef BCMSDIO + /* Process sd_msglevel */ + memset(pick, 0, MAXSZ_BUF); + len_val = process_config_vars(bufp, len, pick, "sd_msglevel="); +@@ -1026,6 +1218,7 @@ + sd_msglevel = (int)simple_strtol(pick, NULL, 0); + printf("%s: sd_msglevel = 0x%X\n", __FUNCTION__, sd_msglevel); + } ++#endif + /* Process android_msg_level */ + memset(pick, 0, MAXSZ_BUF); + len_val = process_config_vars(bufp, len, pick, "android_msg_level="); +@@ -1059,6 +1252,7 @@ + } + #endif + ++#if defined(DHD_DEBUG) + /* Process dhd_console_ms */ + memset(pick, 0, MAXSZ_BUF); + len_val = process_config_vars(bufp, len, pick, "dhd_console_ms="); +@@ -1066,14 +1260,25 @@ + dhd_console_ms = (int)simple_strtol(pick, NULL, 0); + printf("%s: dhd_console_ms = 0x%X\n", __FUNCTION__, dhd_console_ms); + } ++#endif ++ ++ if (pick) ++ MFREE(dhd->osh, pick, MAXSZ_BUF); + } + + void + dhd_conf_read_wme_ac_params(dhd_pub_t *dhd, char *bufp, uint len) + { + uint len_val; +- char pick[MAXSZ_BUF]; ++ char *pick; + struct dhd_conf *conf = dhd->conf; ++ ++ pick = MALLOC(dhd->osh, MAXSZ_BUF); ++ if (!pick) { ++ CONFIG_ERROR(("%s: Failed to allocate memory %d bytes\n", ++ __FUNCTION__, MAXSZ_BUF)); ++ return; ++ } + + /* Process WMM parameters */ + memset(pick, 0, MAXSZ_BUF); +@@ -1169,15 +1374,214 @@ + } + } + +-} +- +-void ++ if (pick) ++ MFREE(dhd->osh, pick, MAXSZ_BUF); ++ ++} ++ ++void ++dhd_conf_read_fw_by_mac(dhd_pub_t *dhd, char *bufp, uint len) ++{ ++ uint len_val; ++ int i, j; ++ char *pick; ++ char *pch, *pick_tmp; ++ wl_mac_list_t *mac_list; ++ wl_mac_range_t *mac_range; ++ struct dhd_conf *conf = dhd->conf; ++ ++ pick = MALLOC(dhd->osh, MAXSZ_BUF); ++ if (!pick) { ++ CONFIG_ERROR(("%s: Failed to allocate memory %d bytes\n", ++ __FUNCTION__, MAXSZ_BUF)); ++ return; ++ } ++ ++ /* Process fw_by_mac: ++ * fw_by_mac=[fw_mac_num] \ ++ * [fw_name1] [mac_num1] [oui1-1] [nic_start1-1] [nic_end1-1] \ ++ * [oui1-1] [nic_start1-1] [nic_end1-1]... \ ++ * [oui1-n] [nic_start1-n] [nic_end1-n] \ ++ * [fw_name2] [mac_num2] [oui2-1] [nic_start2-1] [nic_end2-1] \ ++ * [oui2-1] [nic_start2-1] [nic_end2-1]... \ ++ * [oui2-n] [nic_start2-n] [nic_end2-n] \ ++ * Ex: fw_by_mac=2 \ ++ * fw_bcmdhd1.bin 2 0x0022F4 0xE85408 0xE8549D 0x983B16 0x3557A9 0x35582A \ ++ * fw_bcmdhd2.bin 3 0x0022F4 0xE85408 0xE8549D 0x983B16 0x3557A9 0x35582A \ ++ * 0x983B16 0x916157 0x916487 ++ */ ++ memset(pick, 0, MAXSZ_BUF); ++ len_val = process_config_vars(bufp, len, pick, "fw_by_mac="); ++ if (len_val) { ++ pick_tmp = pick; ++ pch = bcmstrtok(&pick_tmp, " ", 0); ++ conf->fw_by_mac.count = (uint32)simple_strtol(pch, NULL, 0); ++ if (!(mac_list = kmalloc(sizeof(wl_mac_list_t)*conf->fw_by_mac.count, GFP_KERNEL))) { ++ conf->fw_by_mac.count = 0; ++ CONFIG_ERROR(("%s: kmalloc failed\n", __FUNCTION__)); ++ } ++ printf("%s: fw_count=%d\n", __FUNCTION__, conf->fw_by_mac.count); ++ conf->fw_by_mac.m_mac_list_head = mac_list; ++ for (i=0; ifw_by_mac.count; i++) { ++ pch = bcmstrtok(&pick_tmp, " ", 0); ++ strcpy(mac_list[i].name, pch); ++ pch = bcmstrtok(&pick_tmp, " ", 0); ++ mac_list[i].count = (uint32)simple_strtol(pch, NULL, 0); ++ printf("%s: name=%s, mac_count=%d\n", __FUNCTION__, ++ mac_list[i].name, mac_list[i].count); ++ if (!(mac_range = kmalloc(sizeof(wl_mac_range_t)*mac_list[i].count, GFP_KERNEL))) { ++ mac_list[i].count = 0; ++ CONFIG_ERROR(("%s: kmalloc failed\n", __FUNCTION__)); ++ break; ++ } ++ mac_list[i].mac = mac_range; ++ for (j=0; josh, pick, MAXSZ_BUF); ++} ++ ++void ++dhd_conf_read_nv_by_mac(dhd_pub_t *dhd, char *bufp, uint len) ++{ ++ uint len_val; ++ int i, j; ++ char *pick; ++ char *pch, *pick_tmp; ++ wl_mac_list_t *mac_list; ++ wl_mac_range_t *mac_range; ++ struct dhd_conf *conf = dhd->conf; ++ ++ pick = MALLOC(dhd->osh, MAXSZ_BUF); ++ if (!pick) { ++ CONFIG_ERROR(("%s: Failed to allocate memory %d bytes\n", ++ __FUNCTION__, MAXSZ_BUF)); ++ return; ++ } ++ ++ /* Process nv_by_mac: ++ * [nv_by_mac]: The same format as fw_by_mac ++ */ ++ memset(pick, 0, MAXSZ_BUF); ++ len_val = process_config_vars(bufp, len, pick, "nv_by_mac="); ++ if (len_val) { ++ pick_tmp = pick; ++ pch = bcmstrtok(&pick_tmp, " ", 0); ++ conf->nv_by_mac.count = (uint32)simple_strtol(pch, NULL, 0); ++ if (!(mac_list = kmalloc(sizeof(wl_mac_list_t)*conf->nv_by_mac.count, GFP_KERNEL))) { ++ conf->nv_by_mac.count = 0; ++ CONFIG_ERROR(("%s: kmalloc failed\n", __FUNCTION__)); ++ } ++ printf("%s: nv_count=%d\n", __FUNCTION__, conf->nv_by_mac.count); ++ conf->nv_by_mac.m_mac_list_head = mac_list; ++ for (i=0; inv_by_mac.count; i++) { ++ pch = bcmstrtok(&pick_tmp, " ", 0); ++ strcpy(mac_list[i].name, pch); ++ pch = bcmstrtok(&pick_tmp, " ", 0); ++ mac_list[i].count = (uint32)simple_strtol(pch, NULL, 0); ++ printf("%s: name=%s, mac_count=%d\n", __FUNCTION__, ++ mac_list[i].name, mac_list[i].count); ++ if (!(mac_range = kmalloc(sizeof(wl_mac_range_t)*mac_list[i].count, GFP_KERNEL))) { ++ mac_list[i].count = 0; ++ CONFIG_ERROR(("%s: kmalloc failed\n", __FUNCTION__)); ++ break; ++ } ++ mac_list[i].mac = mac_range; ++ for (j=0; josh, pick, MAXSZ_BUF); ++} ++ ++void ++dhd_conf_read_nv_by_chip(dhd_pub_t *dhd, char *bufp, uint len) ++{ ++ uint len_val; ++ int i; ++ char *pick; ++ char *pch, *pick_tmp; ++ wl_chip_nv_path_t *chip_nv_path; ++ struct dhd_conf *conf = dhd->conf; ++ ++ pick = MALLOC(dhd->osh, MAXSZ_BUF); ++ if (!pick) { ++ CONFIG_ERROR(("%s: Failed to allocate memory %d bytes\n", ++ __FUNCTION__, MAXSZ_BUF)); ++ return; ++ } ++ ++ /* Process nv_by_chip: ++ * nv_by_chip=[nv_chip_num] \ ++ * [chip1] [chiprev1] [nv_name1] [chip2] [chiprev2] [nv_name2] \ ++ * Ex: nv_by_chip=2 \ ++ * 43430 0 nvram_ap6212.txt 43430 1 nvram_ap6212a.txt \ ++ */ ++ memset(pick, 0, MAXSZ_BUF); ++ len_val = process_config_vars(bufp, len, pick, "nv_by_chip="); ++ if (len_val) { ++ pick_tmp = pick; ++ pch = bcmstrtok(&pick_tmp, " ", 0); ++ conf->nv_by_chip.count = (uint32)simple_strtol(pch, NULL, 0); ++ if (!(chip_nv_path = kmalloc(sizeof(wl_mac_list_t)*conf->nv_by_chip.count, GFP_KERNEL))) { ++ conf->nv_by_chip.count = 0; ++ CONFIG_ERROR(("%s: kmalloc failed\n", __FUNCTION__)); ++ } ++ printf("%s: nv_by_chip_count=%d\n", __FUNCTION__, conf->nv_by_chip.count); ++ conf->nv_by_chip.m_chip_nv_path_head = chip_nv_path; ++ for (i=0; inv_by_chip.count; i++) { ++ pch = bcmstrtok(&pick_tmp, " ", 0); ++ chip_nv_path[i].chip = (uint32)simple_strtol(pch, NULL, 0); ++ pch = bcmstrtok(&pick_tmp, " ", 0); ++ chip_nv_path[i].chiprev = (uint32)simple_strtol(pch, NULL, 0); ++ pch = bcmstrtok(&pick_tmp, " ", 0); ++ strcpy(chip_nv_path[i].name, pch); ++ printf("%s: chip=0x%x, chiprev=%d, name=%s\n", __FUNCTION__, ++ chip_nv_path[i].chip, chip_nv_path[i].chiprev, chip_nv_path[i].name); ++ } ++ } ++ ++ if (pick) ++ MFREE(dhd->osh, pick, MAXSZ_BUF); ++} ++ ++void + dhd_conf_read_roam_params(dhd_pub_t *dhd, char *bufp, uint len) + { + uint len_val; +- char pick[MAXSZ_BUF]; ++ char *pick; + struct dhd_conf *conf = dhd->conf; +- ++ ++ pick = MALLOC(dhd->osh, MAXSZ_BUF); ++ if (!pick) { ++ CONFIG_ERROR(("%s: Failed to allocate memory %d bytes\n", ++ __FUNCTION__, MAXSZ_BUF)); ++ return; ++ } ++ + /* Process roam */ + memset(pick, 0, MAXSZ_BUF); + len_val = process_config_vars(bufp, len, pick, "roam_off="); +@@ -1229,48 +1633,83 @@ + conf->fullroamperiod); + } + ++ if (pick) ++ MFREE(dhd->osh, pick, MAXSZ_BUF); ++ ++} ++ ++void ++dhd_conf_read_country_list(dhd_pub_t *dhd, char *bufp, uint len) ++{ ++ uint len_val; ++ int i; ++ char *pick, *pch, *pick_tmp; ++ struct dhd_conf *conf = dhd->conf; ++ ++ pick = MALLOC(dhd->osh, MAXSZ_BUF); ++ if (!pick) { ++ CONFIG_ERROR(("%s: Failed to allocate memory %d bytes\n", ++ __FUNCTION__, MAXSZ_BUF)); ++ return; ++ } ++ ++ /* Process country_list: ++ * country_list=[country1]:[ccode1]/[regrev1], ++ * [country2]:[ccode2]/[regrev2] \ ++ * Ex: country_list=US:US/0, TW:TW/1 ++ */ ++ memset(pick, 0, MAXSZ_BUF); ++ len_val = process_config_vars(bufp, len, pick, "country_list="); ++ if (len_val) { ++ pick_tmp = pick; ++ for (i=0; icountry_list.cspec[i].country_abbrev, pch); ++ pch = bcmstrtok(&pick_tmp, "/", 0); ++ if (!pch) ++ break; ++ memcpy(conf->country_list.cspec[i].ccode, pch, 2); ++ pch = bcmstrtok(&pick_tmp, ", ", 0); ++ if (!pch) ++ break; ++ conf->country_list.cspec[i].rev = (int32)simple_strtol(pch, NULL, 10); ++ conf->country_list.count ++; ++ CONFIG_TRACE(("%s: country_list abbrev=%s, ccode=%s, regrev=%d\n", __FUNCTION__, ++ conf->country_list.cspec[i].country_abbrev, ++ conf->country_list.cspec[i].ccode, ++ conf->country_list.cspec[i].rev)); ++ } ++ printf("%s: %d country in list\n", __FUNCTION__, conf->country_list.count); ++ } ++ ++ if (pick) ++ MFREE(dhd->osh, pick, MAXSZ_BUF); + } + +-/* +- * [fw_by_mac]: +- * fw_by_mac=[fw_mac_num] \ +- * [fw_name1] [mac_num1] [oui1-1] [nic_start1-1] [nic_end1-1] \ +- * [oui1-1] [nic_start1-1] [nic_end1-1]... \ +- * [oui1-n] [nic_start1-n] [nic_end1-n] \ +- * [fw_name2] [mac_num2] [oui2-1] [nic_start2-1] [nic_end2-1] \ +- * [oui2-1] [nic_start2-1] [nic_end2-1]... \ +- * [oui2-n] [nic_start2-n] [nic_end2-n] \ +- * Ex: fw_by_mac=2 \ +- * fw_bcmdhd1.bin 2 0x0022F4 0xE85408 0xE8549D 0x983B16 0x3557A9 0x35582A \ +- * fw_bcmdhd2.bin 3 0x0022F4 0xE85408 0xE8549D 0x983B16 0x3557A9 0x35582A \ +- * 0x983B16 0x916157 0x916487 +- * [nv_by_mac]: The same format as fw_by_mac +- * +-*/ + int +-dhd_conf_read_config(dhd_pub_t *dhd) ++dhd_conf_read_config(dhd_pub_t *dhd, char *conf_path) + { +- int bcmerror = -1, i, j; ++ int bcmerror = -1, i; + uint len, len_val; + void * image = NULL; + char * memblock = NULL; +- char *bufp, pick[MAXSZ_BUF], *pch, *pick_tmp; +- char *pconf_path; ++ char *bufp, *pick = NULL, *pch, *pick_tmp; + bool conf_file_exists; +- wl_mac_list_t *mac_list; +- wl_mac_range_t *mac_range; + struct dhd_conf *conf = dhd->conf; + +- pconf_path = dhd->conf_path; +- +- conf_file_exists = ((pconf_path != NULL) && (pconf_path[0] != '\0')); +- if (!conf_file_exists) +- return (0); ++ conf_file_exists = ((conf_path != NULL) && (conf_path[0] != '\0')); ++ if (!conf_file_exists) { ++ printf("%s: config path %s\n", __FUNCTION__, conf_path); ++ return (0); ++ } + + if (conf_file_exists) { +- image = dhd_os_open_image(pconf_path); ++ image = dhd_os_open_image(conf_path); + if (image == NULL) { +- printk("%s: Ignore config file %s\n", __FUNCTION__, pconf_path); ++ printf("%s: Ignore config file %s\n", __FUNCTION__, conf_path); + goto err; + } + } +@@ -1279,6 +1718,13 @@ + if (memblock == NULL) { + CONFIG_ERROR(("%s: Failed to allocate memory %d bytes\n", + __FUNCTION__, MAXSZ_CONFIG)); ++ goto err; ++ } ++ ++ pick = MALLOC(dhd->osh, MAXSZ_BUF); ++ if (!pick) { ++ CONFIG_ERROR(("%s: Failed to allocate memory %d bytes\n", ++ __FUNCTION__, MAXSZ_BUF)); + goto err; + } + +@@ -1294,104 +1740,14 @@ + dhd_conf_read_log_level(dhd, bufp, len); + dhd_conf_read_roam_params(dhd, bufp, len); + dhd_conf_read_wme_ac_params(dhd, bufp, len); +- +- /* Process fw_by_mac */ +- memset(pick, 0, MAXSZ_BUF); +- len_val = process_config_vars(bufp, len, pick, "fw_by_mac="); +- if (len_val) { +- pick_tmp = pick; +- pch = bcmstrtok(&pick_tmp, " ", 0); +- conf->fw_by_mac.count = (uint32)simple_strtol(pch, NULL, 0); +- if (!(mac_list = kmalloc(sizeof(wl_mac_list_t)*conf->fw_by_mac.count, GFP_KERNEL))) { +- conf->fw_by_mac.count = 0; +- CONFIG_ERROR(("%s: kmalloc failed\n", __FUNCTION__)); +- } +- printf("%s: fw_count=%d\n", __FUNCTION__, conf->fw_by_mac.count); +- conf->fw_by_mac.m_mac_list_head = mac_list; +- for (i=0; ifw_by_mac.count; i++) { +- pch = bcmstrtok(&pick_tmp, " ", 0); +- strcpy(mac_list[i].name, pch); +- pch = bcmstrtok(&pick_tmp, " ", 0); +- mac_list[i].count = (uint32)simple_strtol(pch, NULL, 0); +- printf("%s: name=%s, mac_count=%d\n", __FUNCTION__, +- mac_list[i].name, mac_list[i].count); +- if (!(mac_range = kmalloc(sizeof(wl_mac_range_t)*mac_list[i].count, GFP_KERNEL))) { +- mac_list[i].count = 0; +- CONFIG_ERROR(("%s: kmalloc failed\n", __FUNCTION__)); +- break; +- } +- mac_list[i].mac = mac_range; +- for (j=0; jnv_by_mac.count = (uint32)simple_strtol(pch, NULL, 0); +- if (!(mac_list = kmalloc(sizeof(wl_mac_list_t)*conf->nv_by_mac.count, GFP_KERNEL))) { +- conf->nv_by_mac.count = 0; +- CONFIG_ERROR(("%s: kmalloc failed\n", __FUNCTION__)); +- } +- printf("%s: nv_count=%d\n", __FUNCTION__, conf->nv_by_mac.count); +- conf->nv_by_mac.m_mac_list_head = mac_list; +- for (i=0; inv_by_mac.count; i++) { +- pch = bcmstrtok(&pick_tmp, " ", 0); +- strcpy(mac_list[i].name, pch); +- pch = bcmstrtok(&pick_tmp, " ", 0); +- mac_list[i].count = (uint32)simple_strtol(pch, NULL, 0); +- printf("%s: name=%s, mac_count=%d\n", __FUNCTION__, +- mac_list[i].name, mac_list[i].count); +- if (!(mac_range = kmalloc(sizeof(wl_mac_range_t)*mac_list[i].count, GFP_KERNEL))) { +- mac_list[i].count = 0; +- CONFIG_ERROR(("%s: kmalloc failed\n", __FUNCTION__)); +- break; +- } +- mac_list[i].mac = mac_range; +- for (j=0; jfw_path); +- } +- +- /* Process nvram path */ +- memset(pick, 0, MAXSZ_BUF); +- len_val = process_config_vars(bufp, len, pick, "nv_path="); +- if (len_val) { +- memcpy(conf->nv_path, pick, len_val); +- printf("%s: nv_path = %s\n", __FUNCTION__, conf->nv_path); +- } +- +- /* Process band */ ++ dhd_conf_read_fw_by_mac(dhd, bufp, len); ++ dhd_conf_read_nv_by_mac(dhd, bufp, len); ++ dhd_conf_read_nv_by_chip(dhd, bufp, len); ++ dhd_conf_read_country_list(dhd, bufp, len); ++ ++ /* Process band: ++ * band=a for 5GHz only and band=b for 2.4GHz only ++ */ + memset(pick, 0, MAXSZ_BUF); + len_val = process_config_vars(bufp, len, pick, "band="); + if (len_val) { +@@ -1404,7 +1760,7 @@ + printf("%s: band = %d\n", __FUNCTION__, conf->band); + } + +- /* Process bandwidth */ ++ /* Process mimo_bw_cap */ + memset(pick, 0, MAXSZ_BUF); + len_val = process_config_vars(bufp, len, pick, "mimo_bw_cap="); + if (len_val) { +@@ -1448,7 +1804,7 @@ + memset(pick, 0, MAXSZ_BUF); + len_val = process_config_vars(bufp, len, pick, "keep_alive_period="); + if (len_val) { +- conf->keep_alive_period = (int)simple_strtol(pick, NULL, 10); ++ conf->keep_alive_period = (uint)simple_strtol(pick, NULL, 10); + printf("%s: keep_alive_period = %d\n", __FUNCTION__, + conf->keep_alive_period); + } +@@ -1469,6 +1825,7 @@ + printf("%s: phy_oclscdenable = %d\n", __FUNCTION__, conf->phy_oclscdenable); + } + ++#ifdef BCMSDIO + /* Process dhd_doflow parameters */ + memset(pick, 0, MAXSZ_BUF); + len_val = process_config_vars(bufp, len, pick, "dhd_doflow="); +@@ -1478,7 +1835,19 @@ + else + dhd_doflow = TRUE; + printf("%s: dhd_doflow = %d\n", __FUNCTION__, dhd_doflow); ++ } ++ ++ /* Process dhd_slpauto parameters */ ++ memset(pick, 0, MAXSZ_BUF); ++ len_val = process_config_vars(bufp, len, pick, "dhd_slpauto="); ++ if (len_val) { ++ if (!strncmp(pick, "0", len_val)) ++ dhd_slpauto = FALSE; ++ else ++ dhd_slpauto = TRUE; ++ printf("%s: dhd_slpauto = %d\n", __FUNCTION__, dhd_slpauto); + } ++#endif + + /* Process dhd_master_mode parameters */ + memset(pick, 0, MAXSZ_BUF); +@@ -1491,7 +1860,10 @@ + printf("%s: dhd_master_mode = %d\n", __FUNCTION__, dhd_master_mode); + } + +- /* Process pkt_filter_add */ ++#ifdef PKT_FILTER_SUPPORT ++ /* Process pkt_filter_add: ++ * All pkt: pkt_filter_add=99 0 0 0 0x000000000000 0x000000000000 ++ */ + memset(pick, 0, MAXSZ_BUF); + len_val = process_config_vars(bufp, len, pick, "pkt_filter_add="); + pick_tmp = pick; +@@ -1525,6 +1897,7 @@ + printf("%d ", conf->pkt_filter_del.id[i]); + printf("\n"); + } ++#endif + + /* Process srl parameters */ + memset(pick, 0, MAXSZ_BUF); +@@ -1546,11 +1919,11 @@ + memset(pick, 0, MAXSZ_BUF); + len_val = process_config_vars(bufp, len, pick, "bcn_timeout="); + if (len_val) { +- conf->bcn_timeout= (int)simple_strtol(pick, NULL, 10); ++ conf->bcn_timeout= (uint)simple_strtol(pick, NULL, 10); + printf("%s: bcn_timeout = %d\n", __FUNCTION__, conf->bcn_timeout); + } + +- /* Process bus_txglom */ ++ /* Process bus:txglom */ + memset(pick, 0, MAXSZ_BUF); + len_val = process_config_vars(bufp, len, pick, "bus:txglom="); + if (len_val) { +@@ -1566,7 +1939,7 @@ + printf("%s: ampdu_ba_wsize = %d\n", __FUNCTION__, conf->ampdu_ba_wsize); + } + +- /* Process kso parameters */ ++ /* Process kso_enable parameters */ + memset(pick, 0, MAXSZ_BUF); + len_val = process_config_vars(bufp, len, pick, "kso_enable="); + if (len_val) { +@@ -1584,7 +1957,238 @@ + conf->spect = (int)simple_strtol(pick, NULL, 10); + printf("%s: spect = %d\n", __FUNCTION__, conf->spect); + } +- ++ ++ /* Process txbf parameters */ ++ memset(pick, 0, MAXSZ_BUF); ++ len_val = process_config_vars(bufp, len, pick, "txbf="); ++ if (len_val) { ++ conf->txbf = (int)simple_strtol(pick, NULL, 10); ++ printf("%s: txbf = %d\n", __FUNCTION__, conf->txbf); ++ } ++ ++ /* Process frameburst parameters */ ++ memset(pick, 0, MAXSZ_BUF); ++ len_val = process_config_vars(bufp, len, pick, "frameburst="); ++ if (len_val) { ++ conf->frameburst = (int)simple_strtol(pick, NULL, 10); ++ printf("%s: frameburst = %d\n", __FUNCTION__, conf->frameburst); ++ } ++ ++ /* Process lpc parameters */ ++ memset(pick, 0, MAXSZ_BUF); ++ len_val = process_config_vars(bufp, len, pick, "lpc="); ++ if (len_val) { ++ conf->lpc = (int)simple_strtol(pick, NULL, 10); ++ printf("%s: lpc = %d\n", __FUNCTION__, conf->lpc); ++ } ++ ++ /* Process use_rxchain parameters */ ++ memset(pick, 0, MAXSZ_BUF); ++ len_val = process_config_vars(bufp, len, pick, "use_rxchain="); ++ if (len_val) { ++ conf->use_rxchain = (int)simple_strtol(pick, NULL, 10); ++ printf("%s: use_rxchain = %d\n", __FUNCTION__, conf->use_rxchain); ++ } ++ ++#if defined(BCMSDIOH_TXGLOM) ++ /* Process txglomsize parameters */ ++ memset(pick, 0, MAXSZ_BUF); ++ len_val = process_config_vars(bufp, len, pick, "txglomsize="); ++ if (len_val) { ++ conf->txglomsize = (uint)simple_strtol(pick, NULL, 10); ++ if (conf->txglomsize > SDPCM_MAXGLOM_SIZE) ++ conf->txglomsize = SDPCM_MAXGLOM_SIZE; ++ printf("%s: txglomsize = %d\n", __FUNCTION__, conf->txglomsize); ++ } ++ ++ /* Process swtxglom parameters */ ++ memset(pick, 0, MAXSZ_BUF); ++ len_val = process_config_vars(bufp, len, pick, "swtxglom="); ++ if (len_val) { ++ if (!strncmp(pick, "0", len_val)) ++ conf->swtxglom = FALSE; ++ else ++ conf->swtxglom = TRUE; ++ printf("%s: swtxglom = %d\n", __FUNCTION__, conf->swtxglom); ++ } ++ ++ /* Process txglom_ext parameters */ ++ memset(pick, 0, MAXSZ_BUF); ++ len_val = process_config_vars(bufp, len, pick, "txglom_ext="); ++ if (len_val) { ++ if (!strncmp(pick, "0", len_val)) ++ conf->txglom_ext = FALSE; ++ else ++ conf->txglom_ext = TRUE; ++ printf("%s: txglom_ext = %d\n", __FUNCTION__, conf->txglom_ext); ++ if (conf->txglom_ext) { ++ if ((conf->chip == BCM43362_CHIP_ID) || (conf->chip == BCM4330_CHIP_ID)) ++ conf->txglom_bucket_size = 1680; ++ else if (conf->chip == BCM43340_CHIP_ID || conf->chip == BCM43341_CHIP_ID || ++ conf->chip == BCM4334_CHIP_ID || conf->chip == BCM4324_CHIP_ID) ++ conf->txglom_bucket_size = 1684; ++ } ++ printf("%s: txglom_bucket_size = %d\n", __FUNCTION__, conf->txglom_bucket_size); ++ } ++#endif ++ ++ /* Process disable_proptx parameters */ ++ memset(pick, 0, MAXSZ_BUF); ++ len_val = process_config_vars(bufp, len, pick, "disable_proptx="); ++ if (len_val) { ++ conf->disable_proptx = (int)simple_strtol(pick, NULL, 10); ++ printf("%s: disable_proptx = %d\n", __FUNCTION__, conf->disable_proptx); ++ } ++ ++ /* Process dpc_cpucore parameters */ ++ memset(pick, 0, MAXSZ_BUF); ++ len_val = process_config_vars(bufp, len, pick, "dpc_cpucore="); ++ if (len_val) { ++ conf->dpc_cpucore = (int)simple_strtol(pick, NULL, 10); ++ printf("%s: dpc_cpucore = %d\n", __FUNCTION__, conf->dpc_cpucore); ++ } ++ ++ /* Process bus:rxglom parameters */ ++ memset(pick, 0, MAXSZ_BUF); ++ len_val = process_config_vars(bufp, len, pick, "bus:rxglom="); ++ if (len_val) { ++ if (!strncmp(pick, "0", len_val)) ++ conf->bus_rxglom = FALSE; ++ else ++ conf->bus_rxglom = TRUE; ++ printf("%s: bus:rxglom = %d\n", __FUNCTION__, conf->bus_rxglom); ++ } ++ ++ /* Process deepsleep parameters */ ++ memset(pick, 0, MAXSZ_BUF); ++ len_val = process_config_vars(bufp, len, pick, "deepsleep="); ++ if (len_val) { ++ if (!strncmp(pick, "1", len_val)) ++ conf->deepsleep = TRUE; ++ else ++ conf->deepsleep = FALSE; ++ printf("%s: deepsleep = %d\n", __FUNCTION__, conf->deepsleep); ++ } ++ ++ /* Process PM parameters */ ++ memset(pick, 0, MAXSZ_BUF); ++ len_val = process_config_vars(bufp, len, pick, "PM="); ++ if (len_val) { ++ conf->pm = (int)simple_strtol(pick, NULL, 10); ++ printf("%s: PM = %d\n", __FUNCTION__, conf->pm); ++ } ++ ++ /* Process tcpack_sup_mode parameters */ ++ memset(pick, 0, MAXSZ_BUF); ++ len_val = process_config_vars(bufp, len, pick, "tcpack_sup_mode="); ++ if (len_val) { ++ conf->tcpack_sup_mode = (uint)simple_strtol(pick, NULL, 10); ++ printf("%s: tcpack_sup_mode = %d\n", __FUNCTION__, conf->tcpack_sup_mode); ++ } ++ ++ /* Process dhd_poll parameters */ ++ memset(pick, 0, MAXSZ_BUF); ++ len_val = process_config_vars(bufp, len, pick, "dhd_poll="); ++ if (len_val) { ++ if (!strncmp(pick, "0", len_val)) ++ conf->dhd_poll = 0; ++ else ++ conf->dhd_poll = 1; ++ printf("%s: dhd_poll = %d\n", __FUNCTION__, conf->dhd_poll); ++ } ++ ++ /* Process deferred_tx_len parameters */ ++ memset(pick, 0, MAXSZ_BUF); ++ len_val = process_config_vars(bufp, len, pick, "deferred_tx_len="); ++ if (len_val) { ++ conf->deferred_tx_len = (int)simple_strtol(pick, NULL, 10); ++ printf("%s: deferred_tx_len = %d\n", __FUNCTION__, conf->deferred_tx_len); ++ } ++ ++ /* Process pktprio8021x parameters */ ++ memset(pick, 0, MAXSZ_BUF); ++ len_val = process_config_vars(bufp, len, pick, "pktprio8021x="); ++ if (len_val) { ++ conf->pktprio8021x = (int)simple_strtol(pick, NULL, 10); ++ printf("%s: pktprio8021x = %d\n", __FUNCTION__, conf->pktprio8021x); ++ } ++ ++ /* Process txctl_tmo_fix parameters */ ++ memset(pick, 0, MAXSZ_BUF); ++ len_val = process_config_vars(bufp, len, pick, "txctl_tmo_fix="); ++ if (len_val) { ++ if (!strncmp(pick, "0", len_val)) ++ conf->txctl_tmo_fix = FALSE; ++ else ++ conf->txctl_tmo_fix = TRUE; ++ printf("%s: txctl_tmo_fix = %d\n", __FUNCTION__, conf->txctl_tmo_fix); ++ } ++ ++ /* Process tx_in_rx parameters */ ++ memset(pick, 0, MAXSZ_BUF); ++ len_val = process_config_vars(bufp, len, pick, "tx_in_rx="); ++ if (len_val) { ++ if (!strncmp(pick, "0", len_val)) ++ conf->tx_in_rx = FALSE; ++ else ++ conf->tx_in_rx = TRUE; ++ printf("%s: tx_in_rx = %d\n", __FUNCTION__, conf->tx_in_rx); ++ } ++ ++ /* Process dhd_txbound parameters */ ++ memset(pick, 0, MAXSZ_BUF); ++ len_val = process_config_vars(bufp, len, pick, "dhd_txbound="); ++ if (len_val) { ++ dhd_txbound = (uint)simple_strtol(pick, NULL, 10); ++ printf("%s: dhd_txbound = %d\n", __FUNCTION__, dhd_txbound); ++ } ++ ++ /* Process dhd_rxbound parameters */ ++ memset(pick, 0, MAXSZ_BUF); ++ len_val = process_config_vars(bufp, len, pick, "dhd_rxbound="); ++ if (len_val) { ++ dhd_rxbound = (uint)simple_strtol(pick, NULL, 10); ++ printf("%s: dhd_rxbound = %d\n", __FUNCTION__, dhd_rxbound); ++ } ++ ++ /* Process tx_max_offset parameters */ ++ memset(pick, 0, MAXSZ_BUF); ++ len_val = process_config_vars(bufp, len, pick, "tx_max_offset="); ++ if (len_val) { ++ conf->tx_max_offset = (int)simple_strtol(pick, NULL, 10); ++ printf("%s: tx_max_offset = %d\n", __FUNCTION__, conf->tx_max_offset); ++ } ++ ++ /* Process rsdb_mode parameters */ ++ memset(pick, 0, MAXSZ_BUF); ++ len_val = process_config_vars(bufp, len, pick, "rsdb_mode="); ++ if (len_val) { ++ conf->rsdb_mode = (int)simple_strtol(pick, NULL, 10); ++ printf("%s: rsdb_mode = %d\n", __FUNCTION__, conf->rsdb_mode); ++ } ++ ++ /* Process txglom_mode parameters */ ++ memset(pick, 0, MAXSZ_BUF); ++ len_val = process_config_vars(bufp, len, pick, "txglom_mode="); ++ if (len_val) { ++ if (!strncmp(pick, "0", len_val)) ++ conf->txglom_mode = FALSE; ++ else ++ conf->txglom_mode = TRUE; ++ printf("%s: txglom_mode = %d\n", __FUNCTION__, conf->txglom_mode); ++ } ++ ++ /* Process vhtmode parameters */ ++ memset(pick, 0, MAXSZ_BUF); ++ len_val = process_config_vars(bufp, len, pick, "vhtmode="); ++ if (len_val) { ++ if (!strncmp(pick, "0", len_val)) ++ conf->vhtmode = 0; ++ else ++ conf->vhtmode = 1; ++ printf("%s: vhtmode = %d\n", __FUNCTION__, conf->vhtmode); ++ } ++ + bcmerror = 0; + } else { + CONFIG_ERROR(("%s: error reading config file: %d\n", __FUNCTION__, len)); +@@ -1592,6 +2196,9 @@ + } + + err: ++ if (pick) ++ MFREE(dhd->osh, pick, MAXSZ_BUF); ++ + if (memblock) + MFREE(dhd->osh, memblock, MAXSZ_CONFIG); + +@@ -1604,6 +2211,7 @@ + int + dhd_conf_set_chiprev(dhd_pub_t *dhd, uint chip, uint chiprev) + { ++ printf("%s: chip=0x%x, chiprev=%d\n", __FUNCTION__, chip, chiprev); + dhd->conf->chip = chip; + dhd->conf->chiprev = chiprev; + return 0; +@@ -1629,6 +2237,44 @@ + return 0; + } + ++void ++dhd_conf_set_txglom_params(dhd_pub_t *dhd, bool enable) ++{ ++ struct dhd_conf *conf = dhd->conf; ++ ++ if (enable) { ++#if defined(SWTXGLOM) ++ if (conf->chip == BCM43362_CHIP_ID || conf->chip == BCM4330_CHIP_ID || ++ conf->chip == BCM43340_CHIP_ID || conf->chip == BCM43341_CHIP_ID || ++ conf->chip == BCM4334_CHIP_ID || conf->chip == BCM4324_CHIP_ID) { ++ // 43362/4330/4334/43340/43341/43241 must use 1.88.45.x swtxglom if txglom_ext is true, since 1.201.59 not support swtxglom ++ conf->swtxglom = TRUE; ++ conf->txglom_ext = TRUE; ++ } ++ if (conf->chip == BCM43362_CHIP_ID && conf->bus_txglom == 0) { ++ conf->bus_txglom = 1; // improve tcp tx tput. and cpu idle for 43362 only ++ } ++#endif ++ // other parameters set in preinit or config.txt ++ } else { ++ // clear txglom parameters, but don't change swtxglom since it's possible enabled in config.txt ++ conf->txglom_ext = FALSE; ++ conf->txglom_bucket_size = 0; ++ conf->tx_in_rx = TRUE; ++ conf->tx_max_offset = 0; ++ conf->txglomsize = 0; ++ conf->deferred_tx_len = 0; ++ } ++ printf("%s: swtxglom=%d, txglom_ext=%d\n", __FUNCTION__, ++ conf->swtxglom, conf->txglom_ext); ++ printf("%s: txglom_bucket_size=%d\n", __FUNCTION__, conf->txglom_bucket_size); ++ printf("%s: txglomsize=%d, deferred_tx_len=%d, bus_txglom=%d\n", __FUNCTION__, ++ conf->txglomsize, conf->deferred_tx_len, conf->bus_txglom); ++ printf("%s: tx_in_rx=%d, tx_max_offset=%d\n", __FUNCTION__, ++ conf->tx_in_rx, conf->tx_max_offset); ++ ++} ++ + int + dhd_conf_preinit(dhd_pub_t *dhd) + { +@@ -1636,8 +2282,12 @@ + + CONFIG_TRACE(("%s: Enter\n", __FUNCTION__)); + ++#ifdef BCMSDIO + dhd_conf_free_mac_list(&conf->fw_by_mac); + dhd_conf_free_mac_list(&conf->nv_by_mac); ++ dhd_conf_free_chip_nv_path_list(&conf->nv_by_chip); ++#endif ++ memset(&conf->country_list, 0, sizeof(conf_country_list_t)); + conf->band = WLC_BAND_AUTO; + conf->mimo_bw_cap = -1; + if (conf->chip == BCM43362_CHIP_ID || conf->chip == BCM4330_CHIP_ID) { +@@ -1645,7 +2295,9 @@ + strcpy(conf->cspec.ccode, "ALL"); + conf->cspec.rev = 0; + } else if (conf->chip == BCM4335_CHIP_ID || conf->chip == BCM4339_CHIP_ID || +- conf->chip == BCM4354_CHIP_ID) { ++ conf->chip == BCM4354_CHIP_ID || conf->chip == BCM4356_CHIP_ID || ++ conf->chip == BCM4345_CHIP_ID || conf->chip == BCM4371_CHIP_ID || ++ conf->chip == BCM4359_CHIP_ID) { + strcpy(conf->cspec.country_abbrev, "CN"); + strcpy(conf->cspec.ccode, "CN"); + conf->cspec.rev = 38; +@@ -1687,16 +2339,128 @@ + #ifdef PKT_FILTER_SUPPORT + memset(&conf->pkt_filter_add, 0, sizeof(conf_pkt_filter_add_t)); + memset(&conf->pkt_filter_del, 0, sizeof(conf_pkt_filter_del_t)); ++ conf->pkt_filter_magic = FALSE; + #endif + conf->srl = -1; + conf->lrl = -1; + conf->bcn_timeout = 15; +- if (conf->chip == BCM4339_CHIP_ID) { +- conf->bus_txglom = 8; +- conf->ampdu_ba_wsize = 40; +- } + conf->kso_enable = TRUE; + conf->spect = -1; ++ conf->txbf = -1; ++ conf->lpc = -1; ++ conf->disable_proptx = 0; ++ conf->bus_txglom = 0; ++ conf->use_rxchain = 0; ++ conf->bus_rxglom = TRUE; ++ conf->txglom_ext = FALSE; ++ conf->tx_max_offset = 0; ++ conf->deferred_tx_len = 0; ++ conf->txglomsize = SDPCM_DEFGLOM_SIZE; ++ conf->ampdu_ba_wsize = 0; ++ conf->dpc_cpucore = 0; ++ conf->frameburst = -1; ++ conf->deepsleep = FALSE; ++ conf->pm = -1; ++#ifdef DHDTCPACK_SUPPRESS ++ conf->tcpack_sup_mode = TCPACK_SUP_OFF; ++#endif ++ conf->dhd_poll = -1; ++ conf->pktprio8021x = -1; ++ conf->txctl_tmo_fix = FALSE; ++ conf->tx_in_rx = TRUE; ++ conf->rsdb_mode = -2; ++ conf->txglom_mode = SDPCM_TXGLOM_MDESC; ++ conf->vhtmode = -1; ++ if ((conf->chip == BCM43362_CHIP_ID) || (conf->chip == BCM4330_CHIP_ID)) { ++ conf->disable_proptx = 1; ++ } ++ if (conf->chip == BCM43430_CHIP_ID) { ++ conf->bus_rxglom = FALSE; ++ } ++ if (conf->chip == BCM4339_CHIP_ID) { ++ conf->txbf = 1; ++ } ++ if (conf->chip == BCM4345_CHIP_ID) { ++ conf->txbf = 1; ++ } ++ if (conf->chip == BCM4354_CHIP_ID) { ++ conf->txbf = 1; ++ } ++ if (conf->chip == BCM4356_CHIP_ID) { ++ conf->txbf = 1; ++ } ++ if (conf->chip == BCM4371_CHIP_ID) { ++ conf->txbf = 1; ++ } ++ if (conf->chip == BCM4359_CHIP_ID) { ++ conf->txbf = 1; ++ conf->rsdb_mode = 0; ++ } ++ ++#if defined(SWTXGLOM) ++ if (conf->chip == BCM43362_CHIP_ID || conf->chip == BCM4330_CHIP_ID || ++ conf->chip == BCM43340_CHIP_ID || conf->chip == BCM43341_CHIP_ID || ++ conf->chip == BCM4334_CHIP_ID || conf->chip == BCM4324_CHIP_ID) { ++ conf->swtxglom = FALSE; // disabled by default ++ conf->txglom_ext = TRUE; // enabled by default ++ conf->use_rxchain = 0; // use_rxchain have been disabled if swtxglom enabled ++ conf->txglomsize = 16; ++ } else { ++ conf->swtxglom = FALSE; // use 1.201.59.x txglom by default ++ conf->txglom_ext = FALSE; ++ } ++ ++ if (conf->chip == BCM43362_CHIP_ID) { ++ conf->txglom_bucket_size = 1680; // fixed value, don't change ++ conf->tx_in_rx = FALSE; ++ conf->tx_max_offset = 1; ++ } ++ if (conf->chip == BCM4330_CHIP_ID) { ++ conf->txglom_bucket_size = 1680; // fixed value, don't change ++ conf->tx_in_rx = FALSE; ++ conf->tx_max_offset = 0; ++ } ++ if (conf->chip == BCM4334_CHIP_ID) { ++ conf->txglom_bucket_size = 1684; // fixed value, don't change ++ conf->tx_in_rx = TRUE; // improve tcp tx tput. and cpu idle ++ conf->tx_max_offset = 0; // reduce udp tx: dhdsdio_readframes: got unlikely tx max 109 with tx_seq 110 ++ } ++ if (conf->chip == BCM43340_CHIP_ID || conf->chip == BCM43341_CHIP_ID) { ++ conf->txglom_bucket_size = 1684; // fixed value, don't change ++ conf->tx_in_rx = TRUE; // improve tcp tx tput. and cpu idle ++ conf->tx_max_offset = 1; ++ } ++ if (conf->chip == BCM4324_CHIP_ID) { ++ conf->txglom_bucket_size = 1684; // fixed value, don't change ++ conf->tx_in_rx = TRUE; // improve tcp tx tput. and cpu idle ++ conf->tx_max_offset = 0; ++ } ++#endif ++#if defined(BCMSDIOH_TXGLOM_EXT) ++ conf->txglom_mode = SDPCM_TXGLOM_CPY; ++ if (conf->chip == BCM43362_CHIP_ID || conf->chip == BCM4330_CHIP_ID || ++ conf->chip == BCM43340_CHIP_ID || conf->chip == BCM43341_CHIP_ID || ++ conf->chip == BCM4334_CHIP_ID || conf->chip == BCM4324_CHIP_ID) { ++ conf->txglom_ext = TRUE; ++ conf->use_rxchain = 0; ++ conf->tx_in_rx = TRUE; ++ conf->tx_max_offset = 1; ++ } else { ++ conf->txglom_ext = FALSE; ++ } ++ if (conf->chip == BCM43362_CHIP_ID || conf->chip == BCM4330_CHIP_ID) { ++ conf->txglom_bucket_size = 1680; // fixed value, don't change ++ conf->txglomsize = 6; ++ } ++ if (conf->chip == BCM4334_CHIP_ID || conf->chip == BCM43340_CHIP_ID || ++ conf->chip == BCM43341_CHIP_ID || conf->chip == BCM4324_CHIP_ID) { ++ conf->txglom_bucket_size = 1684; // fixed value, don't change ++ conf->txglomsize = 16; ++ } ++#endif ++ if (conf->txglomsize > SDPCM_MAXGLOM_SIZE) ++ conf->txglomsize = SDPCM_MAXGLOM_SIZE; ++ conf->deferred_tx_len = conf->txglomsize; + + return 0; + } +@@ -1704,8 +2468,11 @@ + int + dhd_conf_reset(dhd_pub_t *dhd) + { ++#ifdef BCMSDIO + dhd_conf_free_mac_list(&dhd->conf->fw_by_mac); + dhd_conf_free_mac_list(&dhd->conf->nv_by_mac); ++ dhd_conf_free_chip_nv_path_list(&dhd->conf->nv_by_chip); ++#endif + memset(dhd->conf, 0, sizeof(dhd_conf_t)); + return 0; + } +@@ -1744,8 +2511,11 @@ + CONFIG_TRACE(("%s: Enter\n", __FUNCTION__)); + + if (dhd->conf) { ++#ifdef BCMSDIO + dhd_conf_free_mac_list(&dhd->conf->fw_by_mac); + dhd_conf_free_mac_list(&dhd->conf->nv_by_mac); ++ dhd_conf_free_chip_nv_path_list(&dhd->conf->nv_by_chip); ++#endif + MFREE(dhd->osh, dhd->conf, sizeof(dhd_conf_t)); + } + dhd->conf = NULL; +diff -Nur a/drivers/net/wireless/bcmdhd/dhd_config.h c/drivers/net/wireless/bcmdhd/dhd_config.h +--- a/drivers/net/wireless/bcmdhd/dhd_config.h 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/dhd_config.h 2016-05-13 09:48:20.000000000 +0200 +@@ -1,145 +1,217 @@ +- +-#ifndef _dhd_config_ +-#define _dhd_config_ +- +-#include +-#include +-#include +-#include +-#include +- +-#define FW_PATH_AUTO_SELECT 1 +-extern char firmware_path[MOD_PARAM_PATHLEN]; +-extern int disable_proptx; +-extern uint dhd_doflow; +- +-/* mac range */ +-typedef struct wl_mac_range { +- uint32 oui; +- uint32 nic_start; +- uint32 nic_end; +-} wl_mac_range_t; +- +-/* mac list */ +-typedef struct wl_mac_list { +- int count; +- wl_mac_range_t *mac; +- char name[MOD_PARAM_PATHLEN]; /* path */ +-} wl_mac_list_t; +- +-/* mac list head */ +-typedef struct wl_mac_list_ctrl { +- int count; +- struct wl_mac_list *m_mac_list_head; +-} wl_mac_list_ctrl_t; +- +-/* channel list */ +-typedef struct wl_channel_list { +- /* in - # of channels, out - # of entries */ +- uint32 count; +- /* variable length channel list */ +- uint32 channel[WL_NUMCHANNELS]; +-} wl_channel_list_t; +- +-typedef struct wmes_param { +- int aifsn[AC_COUNT]; +- int cwmin[AC_COUNT]; +- int cwmax[AC_COUNT]; +-} wme_param_t; +- +-#ifdef PKT_FILTER_SUPPORT +-#define DHD_CONF_FILTER_MAX 8 +-/* filter list */ +-#define PKT_FILTER_LEN 150 +-typedef struct conf_pkt_filter_add { +- /* in - # of channels, out - # of entries */ +- uint32 count; +- /* variable length filter list */ +- char filter[DHD_CONF_FILTER_MAX][PKT_FILTER_LEN]; +-} conf_pkt_filter_add_t; +- +-/* pkt_filter_del list */ +-typedef struct conf_pkt_filter_del { +- /* in - # of channels, out - # of entries */ +- uint32 count; +- /* variable length filter list */ +- uint32 id[DHD_CONF_FILTER_MAX]; +-} conf_pkt_filter_del_t; +-#endif +- +-typedef struct dhd_conf { ++ ++#ifndef _dhd_config_ ++#define _dhd_config_ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#define FW_PATH_AUTO_SELECT 1 ++//#define CONFIG_PATH_AUTO_SELECT ++extern char firmware_path[MOD_PARAM_PATHLEN]; ++extern int disable_proptx; ++extern uint dhd_rxbound; ++extern uint dhd_txbound; ++#define TXGLOM_RECV_OFFSET 8 ++#ifdef BCMSDIO ++extern uint dhd_doflow; ++extern uint dhd_slpauto; ++ ++#define BCM43362A0_CHIP_REV 0 ++#define BCM43362A2_CHIP_REV 1 ++#define BCM43430A0_CHIP_REV 0 ++#define BCM43430A1_CHIP_REV 1 ++#define BCM4330B2_CHIP_REV 4 ++#define BCM4334B1_CHIP_REV 3 ++#define BCM43341B0_CHIP_REV 2 ++#define BCM43241B4_CHIP_REV 5 ++#define BCM4335A0_CHIP_REV 2 ++#define BCM4339A0_CHIP_REV 1 ++#define BCM43455C0_CHIP_REV 6 ++#define BCM4354A1_CHIP_REV 1 ++#define BCM4359B1_CHIP_REV 5 ++#endif ++#define BCM4356A2_CHIP_REV 2 ++ ++/* mac range */ ++typedef struct wl_mac_range { ++ uint32 oui; ++ uint32 nic_start; ++ uint32 nic_end; ++} wl_mac_range_t; ++ ++/* mac list */ ++typedef struct wl_mac_list { ++ int count; ++ wl_mac_range_t *mac; ++ char name[MOD_PARAM_PATHLEN]; /* path */ ++} wl_mac_list_t; ++ ++/* mac list head */ ++typedef struct wl_mac_list_ctrl { ++ int count; ++ struct wl_mac_list *m_mac_list_head; ++} wl_mac_list_ctrl_t; ++ ++/* chip_nv_path */ ++typedef struct wl_chip_nv_path { ++ uint chip; ++ uint chiprev; ++ char name[MOD_PARAM_PATHLEN]; /* path */ ++} wl_chip_nv_path_t; ++ ++/* chip_nv_path list head */ ++typedef struct wl_chip_nv_path_list_ctrl { ++ int count; ++ struct wl_chip_nv_path *m_chip_nv_path_head; ++} wl_chip_nv_path_list_ctrl_t; ++ ++/* channel list */ ++typedef struct wl_channel_list { ++ /* in - # of channels, out - # of entries */ ++ uint32 count; ++ /* variable length channel list */ ++ uint32 channel[WL_NUMCHANNELS]; ++} wl_channel_list_t; ++ ++typedef struct wmes_param { ++ int aifsn[AC_COUNT]; ++ int cwmin[AC_COUNT]; ++ int cwmax[AC_COUNT]; ++} wme_param_t; ++ ++#ifdef PKT_FILTER_SUPPORT ++#define DHD_CONF_FILTER_MAX 8 ++/* filter list */ ++#define PKT_FILTER_LEN 300 ++typedef struct conf_pkt_filter_add { ++ /* in - # of channels, out - # of entries */ ++ uint32 count; ++ /* variable length filter list */ ++ char filter[DHD_CONF_FILTER_MAX][PKT_FILTER_LEN]; ++} conf_pkt_filter_add_t; ++ ++/* pkt_filter_del list */ ++typedef struct conf_pkt_filter_del { ++ /* in - # of channels, out - # of entries */ ++ uint32 count; ++ /* variable length filter list */ ++ uint32 id[DHD_CONF_FILTER_MAX]; ++} conf_pkt_filter_del_t; ++#endif ++ ++#define CONFIG_COUNTRY_LIST_SIZE 100 ++/* country list */ ++typedef struct conf_country_list { ++ uint32 count; ++ wl_country_t cspec[CONFIG_COUNTRY_LIST_SIZE]; ++} conf_country_list_t; ++ ++typedef struct dhd_conf { + uint chip; /* chip number */ +- uint chiprev; /* chip revision */ +- wl_mac_list_ctrl_t fw_by_mac; /* Firmware auto selection by MAC */ +- wl_mac_list_ctrl_t nv_by_mac; /* NVRAM auto selection by MAC */ +- char fw_path[MOD_PARAM_PATHLEN]; /* Firmware path */ +- char nv_path[MOD_PARAM_PATHLEN]; /* NVRAM path */ +- uint band; /* Band, b:2.4G only, otherwise for auto */ +- int mimo_bw_cap; /* Bandwidth, 0:HT20ALL, 1: HT40ALL, 2:HT20IN2G_HT40PIN5G */ +- wl_country_t cspec; /* Country */ +- wl_channel_list_t channels; /* Support channels */ +- uint roam_off; /* Roaming, 0:enable, 1:disable */ +- uint roam_off_suspend; /* Roaming in suspend, 0:enable, 1:disable */ +- int roam_trigger[2]; /* The RSSI threshold to trigger roaming */ +- int roam_scan_period[2]; /* Roaming scan period */ +- int roam_delta[2]; /* Roaming candidate qualification delta */ +- int fullroamperiod; /* Full Roaming period */ +- uint keep_alive_period; /* The perioid in ms to send keep alive packet */ +- uint force_wme_ac; +- wme_param_t wme; /* WME parameters */ +- int stbc; /* STBC for Tx/Rx */ +- int phy_oclscdenable; /* phy_oclscdenable */ +-#ifdef PKT_FILTER_SUPPORT +- conf_pkt_filter_add_t pkt_filter_add; /* Packet filter add */ +- conf_pkt_filter_del_t pkt_filter_del; /* Packet filter add */ +-#endif +- int srl; /* short retry limit */ +- int lrl; /* long retry limit */ +- uint bcn_timeout; /* beacon timeout */ +- uint32 bus_txglom; /* bus:txglom */ +- uint32 ampdu_ba_wsize; +- bool kso_enable; +- int spect; +-} dhd_conf_t; +- +-int dhd_conf_get_mac(dhd_pub_t *dhd, bcmsdh_info_t *sdh, uint8 *mac); +-void dhd_conf_set_fw_name_by_mac(dhd_pub_t *dhd, bcmsdh_info_t *sdh, char *fw_path); +-void dhd_conf_set_nv_name_by_mac(dhd_pub_t *dhd, bcmsdh_info_t *sdh, char *nv_path); +-void dhd_conf_set_fw_name_by_chip(dhd_pub_t *dhd, char *fw_path); +-#if defined(HW_OOB) +-void dhd_conf_set_hw_oob_intr(bcmsdh_info_t *sdh, uint chip); +-#endif +-void dhd_conf_set_fw_path(dhd_pub_t *dhd, char *fw_path); +-void dhd_conf_set_nv_path(dhd_pub_t *dhd, char *nv_path); +-int dhd_conf_set_band(dhd_pub_t *dhd); +-uint dhd_conf_get_band(dhd_pub_t *dhd); +-int dhd_conf_set_country(dhd_pub_t *dhd); +-int dhd_conf_get_country(dhd_pub_t *dhd, wl_country_t *cspec); +-int dhd_conf_fix_country(dhd_pub_t *dhd); +-bool dhd_conf_match_channel(dhd_pub_t *dhd, uint32 channel); +-int dhd_conf_set_roam(dhd_pub_t *dhd); +-void dhd_conf_set_mimo_bw_cap(dhd_pub_t *dhd); +-void dhd_conf_force_wme(dhd_pub_t *dhd); +-void dhd_conf_get_wme(dhd_pub_t *dhd, edcf_acparam_t *acp); +-void dhd_conf_set_wme(dhd_pub_t *dhd); +-void dhd_conf_set_stbc(dhd_pub_t *dhd); +-void dhd_conf_set_phyoclscdenable(dhd_pub_t *dhd); +-void dhd_conf_add_pkt_filter(dhd_pub_t *dhd); +-bool dhd_conf_del_pkt_filter(dhd_pub_t *dhd, uint32 id); +-void dhd_conf_discard_pkt_filter(dhd_pub_t *dhd); +-void dhd_conf_set_srl(dhd_pub_t *dhd); +-void dhd_conf_set_lrl(dhd_pub_t *dhd); +-void dhd_conf_set_glom(dhd_pub_t *dhd); +-void dhd_conf_set_ampdu_ba_wsize(dhd_pub_t *dhd); +-void dhd_conf_set_spect(dhd_pub_t *dhd); +-int dhd_conf_read_config(dhd_pub_t *dhd); +-int dhd_conf_set_chiprev(dhd_pub_t *dhd, uint chip, uint chiprev); +-uint dhd_conf_get_chip(void *context); +-uint dhd_conf_get_chiprev(void *context); +-int dhd_conf_preinit(dhd_pub_t *dhd); +-int dhd_conf_reset(dhd_pub_t *dhd); +-int dhd_conf_attach(dhd_pub_t *dhd); +-void dhd_conf_detach(dhd_pub_t *dhd); +-void *dhd_get_pub(struct net_device *dev); +- +-#endif /* _dhd_config_ */ ++ uint chiprev; /* chip revision */ ++ wl_mac_list_ctrl_t fw_by_mac; /* Firmware auto selection by MAC */ ++ wl_mac_list_ctrl_t nv_by_mac; /* NVRAM auto selection by MAC */ ++ wl_chip_nv_path_list_ctrl_t nv_by_chip; /* NVRAM auto selection by chip */ ++ conf_country_list_t country_list; /* Country list */ ++ int band; /* Band, b:2.4G only, otherwise for auto */ ++ int mimo_bw_cap; /* Bandwidth, 0:HT20ALL, 1: HT40ALL, 2:HT20IN2G_HT40PIN5G */ ++ wl_country_t cspec; /* Country */ ++ wl_channel_list_t channels; /* Support channels */ ++ uint roam_off; /* Roaming, 0:enable, 1:disable */ ++ uint roam_off_suspend; /* Roaming in suspend, 0:enable, 1:disable */ ++ int roam_trigger[2]; /* The RSSI threshold to trigger roaming */ ++ int roam_scan_period[2]; /* Roaming scan period */ ++ int roam_delta[2]; /* Roaming candidate qualification delta */ ++ int fullroamperiod; /* Full Roaming period */ ++ uint keep_alive_period; /* The perioid in ms to send keep alive packet */ ++ int force_wme_ac; ++ wme_param_t wme; /* WME parameters */ ++ int stbc; /* STBC for Tx/Rx */ ++ int phy_oclscdenable; /* phy_oclscdenable */ ++#ifdef PKT_FILTER_SUPPORT ++ conf_pkt_filter_add_t pkt_filter_add; /* Packet filter add */ ++ conf_pkt_filter_del_t pkt_filter_del; /* Packet filter add */ ++ bool pkt_filter_magic; ++#endif ++ int srl; /* short retry limit */ ++ int lrl; /* long retry limit */ ++ uint bcn_timeout; /* beacon timeout */ ++ bool kso_enable; ++ int spect; ++ int txbf; ++ int lpc; ++ int disable_proptx; ++ int bus_txglom; /* bus:txglom */ ++ int use_rxchain; ++ bool bus_rxglom; /* bus:rxglom */ ++ uint txglomsize; ++ int ampdu_ba_wsize; ++ int dpc_cpucore; ++ int frameburst; ++ bool deepsleep; ++ int pm; ++ uint8 tcpack_sup_mode; ++ int dhd_poll; ++ uint deferred_tx_len; ++ int pktprio8021x; ++ bool txctl_tmo_fix; ++ bool swtxglom; /* SW TXGLOM */ ++ bool txglom_ext; /* Only for 43362/4330/43340/43341/43241 */ ++ /*txglom_bucket_size: ++ * 43362/4330: 1680 ++ * 43340/43341/43241: 1684 ++ */ ++ int txglom_bucket_size; ++ int tx_max_offset; ++ bool tx_in_rx; // Skip tx before rx, in order to get more glomed in tx ++ int rsdb_mode; ++ bool txglom_mode; ++ int vhtmode; ++} dhd_conf_t; ++ ++#ifdef BCMSDIO ++int dhd_conf_get_mac(dhd_pub_t *dhd, bcmsdh_info_t *sdh, uint8 *mac); ++void dhd_conf_set_fw_name_by_mac(dhd_pub_t *dhd, bcmsdh_info_t *sdh, char *fw_path); ++void dhd_conf_set_nv_name_by_mac(dhd_pub_t *dhd, bcmsdh_info_t *sdh, char *nv_path); ++#if defined(HW_OOB) || defined(FORCE_WOWLAN) ++void dhd_conf_set_hw_oob_intr(bcmsdh_info_t *sdh, uint chip); ++#endif ++#endif ++void dhd_conf_set_fw_name_by_chip(dhd_pub_t *dhd, char *fw_path); ++void dhd_conf_set_nv_name_by_chip(dhd_pub_t *dhd, char *nv_path); ++void dhd_conf_set_conf_path_by_nv_path(dhd_pub_t *dhd, char *conf_path, char *nv_path); ++#ifdef CONFIG_PATH_AUTO_SELECT ++void dhd_conf_set_conf_name_by_chip(dhd_pub_t *dhd, char *conf_path); ++#endif ++int dhd_conf_set_fw_int_cmd(dhd_pub_t *dhd, char *name, uint cmd, int val, int def, bool down); ++int dhd_conf_set_fw_string_cmd(dhd_pub_t *dhd, char *cmd, int val, int def, bool down); ++uint dhd_conf_get_band(dhd_pub_t *dhd); ++int dhd_conf_set_country(dhd_pub_t *dhd); ++int dhd_conf_get_country(dhd_pub_t *dhd, wl_country_t *cspec); ++int dhd_conf_get_country_from_config(dhd_pub_t *dhd, wl_country_t *cspec); ++int dhd_conf_fix_country(dhd_pub_t *dhd); ++bool dhd_conf_match_channel(dhd_pub_t *dhd, uint32 channel); ++int dhd_conf_set_roam(dhd_pub_t *dhd); ++void dhd_conf_get_wme(dhd_pub_t *dhd, edcf_acparam_t *acp); ++void dhd_conf_set_wme(dhd_pub_t *dhd); ++void dhd_conf_add_pkt_filter(dhd_pub_t *dhd); ++bool dhd_conf_del_pkt_filter(dhd_pub_t *dhd, uint32 id); ++void dhd_conf_discard_pkt_filter(dhd_pub_t *dhd); ++void dhd_conf_set_disable_proptx(dhd_pub_t *dhd); ++int dhd_conf_read_config(dhd_pub_t *dhd, char *conf_path); ++int dhd_conf_set_chiprev(dhd_pub_t *dhd, uint chip, uint chiprev); ++uint dhd_conf_get_chip(void *context); ++uint dhd_conf_get_chiprev(void *context); ++void dhd_conf_set_txglom_params(dhd_pub_t *dhd, bool enable); ++int dhd_conf_get_pm(dhd_pub_t *dhd); ++int dhd_conf_get_tcpack_sup_mode(dhd_pub_t *dhd); ++int dhd_conf_preinit(dhd_pub_t *dhd); ++int dhd_conf_reset(dhd_pub_t *dhd); ++int dhd_conf_attach(dhd_pub_t *dhd); ++void dhd_conf_detach(dhd_pub_t *dhd); ++void *dhd_get_pub(struct net_device *dev); ++ ++#endif /* _dhd_config_ */ +diff -Nur a/drivers/net/wireless/bcmdhd/dhd_custom_gpio.c c/drivers/net/wireless/bcmdhd/dhd_custom_gpio.c +--- a/drivers/net/wireless/bcmdhd/dhd_custom_gpio.c 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/dhd_custom_gpio.c 2016-05-13 09:48:20.000000000 +0200 +@@ -2,7 +2,7 @@ + * Customer code to add GPIO control during WLAN start/stop + * $Copyright Open Broadcom Corporation$ + * +-* $Id: dhd_custom_gpio.c 447105 2014-01-08 05:27:09Z $ ++* $Id: dhd_custom_gpio.c 493822 2014-07-29 13:20:26Z $ + */ + + #include +@@ -25,7 +25,7 @@ + int __attribute__ ((weak)) wifi_get_fw_nv_path(char *fw, char *nv) { return 0;}; + #endif + +-#endif ++#endif + + #if defined(OOB_INTR_ONLY) + +@@ -82,11 +82,11 @@ + host_oob_irq = gpio_to_irq(dhd_oob_gpio_num); + gpio_direction_input(dhd_oob_gpio_num); + #endif /* defined CUSTOMER_HW3 || defined(PLATFORM_MPS) */ +-#endif ++#endif + + return (host_oob_irq); + } +-#endif ++#endif + + /* Customer function to control hw specific wlan gpios */ + int +@@ -98,81 +98,16 @@ + } + + #ifdef GET_CUSTOM_MAC_ENABLE +-extern char *saved_command_line; +-#define MAC_KEY_VALUE "wifi_mac" +-s32 get_para_from_cmdline(const char *cmdline, const char *name, char *value) +-{ +- char *value_p = value; +- +- if(!cmdline || !name || !value) { +- return -1; +- } +- +- for(; *cmdline != 0;) { +- if(*cmdline++ == ' ') { +- if(0 == strncmp(cmdline, name, strlen(name))) { +- cmdline += strlen(name); +- if(*cmdline++ != '=') { +- continue; +- } +- while(*cmdline != 0 && *cmdline != ' ') { +- *value_p++ = *cmdline++; +- } +- return value_p - value; +- } +- } +- } +- +- return 0; +-} +-static u8 key_char2num(u8 ch) +-{ +- if((ch>='0')&&(ch<='9')) +- return ch - '0'; +- else if ((ch>='a')&&(ch<='f')) +- return ch - 'a' + 10; +- else if ((ch>='A')&&(ch<='F')) +- return ch - 'A' + 10; +- else +- return 0xff; +-} +-u8 key_2char2num(u8 hch, u8 lch) +-{ +- return ((key_char2num(hch) << 4) | key_char2num(lch)); +-} + /* Function to get custom MAC address */ + int +-dhd_custom_get_mac_address(unsigned char *buf) ++dhd_custom_get_mac_address(void *adapter, unsigned char *buf) + { + int ret = 0; +- char mac_str[18] = {0}; +- u8 mac[ETH_ALEN]; +- struct ether_addr mac_addr; +- int jj,kk; + +- printk("%s Enter\n", __FUNCTION__); + WL_TRACE(("%s Enter\n", __FUNCTION__)); + if (!buf) + return -EINVAL; + +- get_para_from_cmdline(saved_command_line, MAC_KEY_VALUE, mac_str); +- printk("%s wifi_mac=%s\n", __FUNCTION__, mac_str); +- if(mac_str != NULL) { +- //printk(KERN_ERR "mac_str=%s\n",mac_str); +- for( jj = 0, kk = 0; jj < ETH_ALEN; jj++, kk += 3 ) { +- mac[jj] = key_2char2num(mac_str[kk], mac_str[kk+ 1]); +- } +- if(is_valid_ether_addr(mac)) { +- memcpy(mac_addr.octet, mac, ETHER_ADDR_LEN); +- bcopy((char *)&mac_addr, buf, sizeof(struct ether_addr)); +- ret = 0; +- } else +- ret = -1; +- } else { +- printk("get mac from cmdline failed.\n"); +- ret = -1; +- } +- + /* Customer access to MAC address stored outside of DHD driver */ + #if (defined(CUSTOMER_HW2) || defined(CUSTOMER_HW10)) && (LINUX_VERSION_CODE >= \ + KERNEL_VERSION(2, 6, 35)) +@@ -238,7 +173,7 @@ + {"TR", "TR", 0}, + {"NO", "NO", 0}, + #endif /* EXMAPLE_TABLE */ +-#if defined(CUSTOMER_HW2) ++#if defined(CUSTOMER_HW2) && !defined(CUSTOMER_HW5) + #if defined(BCM4335_CHIP) + {"", "XZ", 11}, /* Universal if Country code is unknown or empty */ + #endif +@@ -299,7 +234,143 @@ + {"RU", "RU", 1}, + {"US", "US", 5} + #endif +-#endif /* CUSTOMER_HW2 */ ++ ++#elif defined(CUSTOMER_HW5) ++ {"", "XZ", 11}, ++ {"AE", "AE", 212}, ++ {"AG", "AG", 2}, ++ {"AI", "AI", 2}, ++ {"AL", "AL", 2}, ++ {"AN", "AN", 3}, ++ {"AR", "AR", 212}, ++ {"AS", "AS", 15}, ++ {"AT", "AT", 4}, ++ {"AU", "AU", 212}, ++ {"AW", "AW", 2}, ++ {"AZ", "AZ", 2}, ++ {"BA", "BA", 2}, ++ {"BD", "BD", 2}, ++ {"BE", "BE", 4}, ++ {"BG", "BG", 4}, ++ {"BH", "BH", 4}, ++ {"BM", "BM", 15}, ++ {"BN", "BN", 4}, ++ {"BR", "BR", 212}, ++ {"BS", "BS", 2}, ++ {"BY", "BY", 3}, ++ {"BW", "BW", 1}, ++ {"CA", "CA", 212}, ++ {"CH", "CH", 212}, ++ {"CL", "CL", 212}, ++ {"CN", "CN", 212}, ++ {"CO", "CO", 212}, ++ {"CR", "CR", 21}, ++ {"CY", "CY", 212}, ++ {"CZ", "CZ", 212}, ++ {"DE", "DE", 212}, ++ {"DK", "DK", 4}, ++ {"DZ", "DZ", 1}, ++ {"EC", "EC", 23}, ++ {"EE", "EE", 4}, ++ {"EG", "EG", 212}, ++ {"ES", "ES", 212}, ++ {"ET", "ET", 2}, ++ {"FI", "FI", 4}, ++ {"FR", "FR", 212}, ++ {"GB", "GB", 212}, ++ {"GD", "GD", 2}, ++ {"GF", "GF", 2}, ++ {"GP", "GP", 2}, ++ {"GR", "GR", 212}, ++ {"GT", "GT", 0}, ++ {"GU", "GU", 17}, ++ {"HK", "HK", 212}, ++ {"HR", "HR", 4}, ++ {"HU", "HU", 4}, ++ {"IN", "IN", 212}, ++ {"ID", "ID", 212}, ++ {"IE", "IE", 5}, ++ {"IL", "IL", 7}, ++ {"IN", "IN", 212}, ++ {"IS", "IS", 4}, ++ {"IT", "IT", 212}, ++ {"JO", "JO", 3}, ++ {"JP", "JP", 212}, ++ {"KH", "KH", 4}, ++ {"KI", "KI", 1}, ++ {"KR", "KR", 212}, ++ {"KW", "KW", 5}, ++ {"KY", "KY", 4}, ++ {"KZ", "KZ", 212}, ++ {"LA", "LA", 4}, ++ {"LB", "LB", 6}, ++ {"LI", "LI", 4}, ++ {"LK", "LK", 3}, ++ {"LS", "LS", 2}, ++ {"LT", "LT", 4}, ++ {"LR", "LR", 2}, ++ {"LU", "LU", 3}, ++ {"LV", "LV", 4}, ++ {"MA", "MA", 2}, ++ {"MC", "MC", 1}, ++ {"MD", "MD", 2}, ++ {"ME", "ME", 2}, ++ {"MK", "MK", 2}, ++ {"MN", "MN", 0}, ++ {"MO", "MO", 2}, ++ {"MR", "MR", 2}, ++ {"MT", "MT", 4}, ++ {"MQ", "MQ", 2}, ++ {"MU", "MU", 2}, ++ {"MV", "MV", 3}, ++ {"MX", "MX", 212}, ++ {"MY", "MY", 212}, ++ {"NI", "NI", 0}, ++ {"NL", "NL", 212}, ++ {"NO", "NO", 4}, ++ {"NP", "NP", 3}, ++ {"NZ", "NZ", 9}, ++ {"OM", "OM", 4}, ++ {"PA", "PA", 17}, ++ {"PE", "PE", 212}, ++ {"PG", "PG", 2}, ++ {"PH", "PH", 212}, ++ {"PL", "PL", 212}, ++ {"PR", "PR", 25}, ++ {"PT", "PT", 212}, ++ {"PY", "PY", 4}, ++ {"RE", "RE", 2}, ++ {"RO", "RO", 212}, ++ {"RS", "RS", 2}, ++ {"RU", "RU", 212}, ++ {"SA", "SA", 212}, ++ {"SE", "SE", 212}, ++ {"SG", "SG", 212}, ++ {"SI", "SI", 4}, ++ {"SK", "SK", 212}, ++ {"SN", "SN", 2}, ++ {"SV", "SV", 25}, ++ {"TH", "TH", 212}, ++ {"TR", "TR", 212}, ++ {"TT", "TT", 5}, ++ {"TW", "TW", 212}, ++ {"UA", "UA", 212}, ++ {"UG", "UG", 2}, ++ {"US", "US", 212}, ++ {"UY", "UY", 5}, ++ {"VA", "VA", 2}, ++ {"VE", "VE", 3}, ++ {"VG", "VG", 2}, ++ {"VI", "VI", 18}, ++ {"VN", "VN", 4}, ++ {"YT", "YT", 2}, ++ {"ZA", "ZA", 212}, ++ {"ZM", "ZM", 2}, ++ {"XT", "XT", 212}, ++ {"XZ", "XZ", 11}, ++ {"XV", "XV", 17}, ++ {"Q1", "Q1", 77}, ++#endif /* CUSTOMER_HW2 and CUSTOMER_HW5 */ + }; + + +@@ -309,7 +380,7 @@ + */ + void get_customized_country_code(void *adapter, char *country_iso_code, wl_country_t *cspec) + { +-#if defined(CUSTOMER_HW2) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) ++#if (defined(CUSTOMER_HW) || defined(CUSTOMER_HW2)) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) + + struct cntry_locales_custom *cloc_ptr; + +diff -Nur a/drivers/net/wireless/bcmdhd/dhd_dbg.h c/drivers/net/wireless/bcmdhd/dhd_dbg.h +--- a/drivers/net/wireless/bcmdhd/dhd_dbg.h 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/dhd_dbg.h 2016-05-13 09:48:20.000000000 +0200 +@@ -92,7 +92,7 @@ + #define DHD_NOCHECKDIED_ON() 0 + #define DHD_PNO_ON() 0 + +-#endif ++#endif + + #define DHD_LOG(args) + +diff -Nur a/drivers/net/wireless/bcmdhd/dhd_flowring.c c/drivers/net/wireless/bcmdhd/dhd_flowring.c +--- a/drivers/net/wireless/bcmdhd/dhd_flowring.c 1970-01-01 01:00:00.000000000 +0100 ++++ c/drivers/net/wireless/bcmdhd/dhd_flowring.c 2016-05-13 09:48:20.000000000 +0200 +@@ -0,0 +1,810 @@ ++/* ++ * Broadcom Dongle Host Driver (DHD), Flow ring specific code at top level ++ * $Copyright Open Broadcom Corporation$ ++ * ++ * $Id: dhd_flowrings.c jaganlv $ ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static INLINE uint16 dhd_flowid_alloc(dhd_pub_t *dhdp, uint8 ifindex, ++ uint8 prio, char *sa, char *da); ++ ++static INLINE int dhd_flowid_lookup(dhd_pub_t *dhdp, uint8 ifindex, ++ uint8 prio, char *sa, char *da, uint16 *flowid); ++int BCMFASTPATH dhd_flow_queue_overflow(flow_queue_t *queue, void *pkt); ++ ++#define FLOW_QUEUE_PKT_NEXT(p) PKTLINK(p) ++#define FLOW_QUEUE_PKT_SETNEXT(p, x) PKTSETLINK((p), (x)) ++ ++const uint8 prio2ac[8] = { 0, 1, 1, 0, 2, 2, 3, 3 }; ++const uint8 prio2tid[8] = { 0, 1, 2, 3, 4, 5, 6, 7 }; ++ ++int BCMFASTPATH ++dhd_flow_queue_overflow(flow_queue_t *queue, void *pkt) ++{ ++ return BCME_NORESOURCE; ++} ++ ++/* Flow ring's queue management functions */ ++ ++void /* Initialize a flow ring's queue */ ++dhd_flow_queue_init(dhd_pub_t *dhdp, flow_queue_t *queue, int max) ++{ ++ ASSERT((queue != NULL) && (max > 0)); ++ ++ dll_init(&queue->list); ++ queue->head = queue->tail = NULL; ++ queue->len = 0; ++ queue->max = max - 1; ++ queue->failures = 0U; ++ queue->cb = &dhd_flow_queue_overflow; ++} ++ ++void /* Register an enqueue overflow callback handler */ ++dhd_flow_queue_register(flow_queue_t *queue, flow_queue_cb_t cb) ++{ ++ ASSERT(queue != NULL); ++ queue->cb = cb; ++} ++ ++ ++int BCMFASTPATH /* Enqueue a packet in a flow ring's queue */ ++dhd_flow_queue_enqueue(dhd_pub_t *dhdp, flow_queue_t *queue, void *pkt) ++{ ++ int ret = BCME_OK; ++ ++ ASSERT(queue != NULL); ++ ++ if (queue->len >= queue->max) { ++ queue->failures++; ++ ret = (*queue->cb)(queue, pkt); ++ goto done; ++ } ++ ++ if (queue->head) { ++ FLOW_QUEUE_PKT_SETNEXT(queue->tail, pkt); ++ } else { ++ queue->head = pkt; ++ } ++ ++ FLOW_QUEUE_PKT_SETNEXT(pkt, NULL); ++ ++ queue->tail = pkt; /* at tail */ ++ ++ queue->len++; ++ ++done: ++ return ret; ++} ++ ++void * BCMFASTPATH /* Dequeue a packet from a flow ring's queue, from head */ ++dhd_flow_queue_dequeue(dhd_pub_t *dhdp, flow_queue_t *queue) ++{ ++ void * pkt; ++ ++ ASSERT(queue != NULL); ++ ++ pkt = queue->head; /* from head */ ++ ++ if (pkt == NULL) { ++ ASSERT((queue->len == 0) && (queue->tail == NULL)); ++ goto done; ++ } ++ ++ queue->head = FLOW_QUEUE_PKT_NEXT(pkt); ++ if (queue->head == NULL) ++ queue->tail = NULL; ++ ++ queue->len--; ++ ++ FLOW_QUEUE_PKT_SETNEXT(pkt, NULL); /* dettach packet from queue */ ++ ++done: ++ return pkt; ++} ++ ++void BCMFASTPATH /* Reinsert a dequeued packet back at the head */ ++dhd_flow_queue_reinsert(dhd_pub_t *dhdp, flow_queue_t *queue, void *pkt) ++{ ++ if (queue->head == NULL) { ++ queue->tail = pkt; ++ } ++ ++ FLOW_QUEUE_PKT_SETNEXT(pkt, queue->head); ++ queue->head = pkt; ++ queue->len++; ++} ++ ++ ++/* Init Flow Ring specific data structures */ ++int ++dhd_flow_rings_init(dhd_pub_t *dhdp, uint32 num_flow_rings) ++{ ++ uint32 idx; ++ uint32 flow_ring_table_sz; ++ uint32 if_flow_lkup_sz; ++ void * flowid_allocator; ++ flow_ring_table_t *flow_ring_table; ++ if_flow_lkup_t *if_flow_lkup = NULL; ++#ifdef PCIE_TX_DEFERRAL ++ uint32 count; ++#endif ++ void *lock = NULL; ++ unsigned long flags; ++ ++ DHD_INFO(("%s\n", __FUNCTION__)); ++ ++ /* Construct a 16bit flow1d allocator */ ++ flowid_allocator = id16_map_init(dhdp->osh, ++ num_flow_rings - FLOW_RING_COMMON, FLOWID_RESERVED); ++ if (flowid_allocator == NULL) { ++ DHD_ERROR(("%s: flowid allocator init failure\n", __FUNCTION__)); ++ return BCME_NOMEM; ++ } ++ ++ /* Allocate a flow ring table, comprising of requested number of rings */ ++ flow_ring_table_sz = (num_flow_rings * sizeof(flow_ring_node_t)); ++ flow_ring_table = (flow_ring_table_t *)MALLOC(dhdp->osh, flow_ring_table_sz); ++ if (flow_ring_table == NULL) { ++ DHD_ERROR(("%s: flow ring table alloc failure\n", __FUNCTION__)); ++ goto fail; ++ } ++ ++ /* Initialize flow ring table state */ ++ bzero((uchar *)flow_ring_table, flow_ring_table_sz); ++ for (idx = 0; idx < num_flow_rings; idx++) { ++ flow_ring_table[idx].status = FLOW_RING_STATUS_CLOSED; ++ flow_ring_table[idx].flowid = (uint16)idx; ++ flow_ring_table[idx].lock = dhd_os_spin_lock_init(dhdp->osh); ++ if (flow_ring_table[idx].lock == NULL) { ++ DHD_ERROR(("%s: Failed to init spinlock for queue!\n", __FUNCTION__)); ++ goto fail; ++ } ++ ++ dll_init(&flow_ring_table[idx].list); ++ ++ /* Initialize the per flow ring backup queue */ ++ dhd_flow_queue_init(dhdp, &flow_ring_table[idx].queue, ++ FLOW_RING_QUEUE_THRESHOLD); ++ } ++ ++ /* Allocate per interface hash table */ ++ if_flow_lkup_sz = sizeof(if_flow_lkup_t) * DHD_MAX_IFS; ++ if_flow_lkup = (if_flow_lkup_t *)DHD_OS_PREALLOC(dhdp, ++ DHD_PREALLOC_IF_FLOW_LKUP, if_flow_lkup_sz); ++ if (if_flow_lkup == NULL) { ++ DHD_ERROR(("%s: if flow lkup alloc failure\n", __FUNCTION__)); ++ goto fail; ++ } ++ ++ /* Initialize per interface hash table */ ++ bzero((uchar *)if_flow_lkup, if_flow_lkup_sz); ++ for (idx = 0; idx < DHD_MAX_IFS; idx++) { ++ int hash_ix; ++ if_flow_lkup[idx].status = 0; ++ if_flow_lkup[idx].role = 0; ++ for (hash_ix = 0; hash_ix < DHD_FLOWRING_HASH_SIZE; hash_ix++) ++ if_flow_lkup[idx].fl_hash[hash_ix] = NULL; ++ } ++ ++#ifdef PCIE_TX_DEFERRAL ++ count = BITS_TO_LONGS(num_flow_rings); ++ dhdp->bus->delete_flow_map = kzalloc(count, GFP_ATOMIC); ++ if (!dhdp->bus->delete_flow_map) { ++ DHD_ERROR(("%s: delete_flow_map alloc failure\n", __FUNCTION__)); ++ goto fail; ++ } ++#endif ++ ++ lock = dhd_os_spin_lock_init(dhdp->osh); ++ if (lock == NULL) ++ goto fail; ++ ++ dhdp->flow_prio_map_type = DHD_FLOW_PRIO_AC_MAP; ++ bcopy(prio2ac, dhdp->flow_prio_map, sizeof(uint8) * NUMPRIO); ++ ++ /* Now populate into dhd pub */ ++ DHD_FLOWID_LOCK(lock, flags); ++ dhdp->num_flow_rings = num_flow_rings; ++ dhdp->flowid_allocator = (void *)flowid_allocator; ++ dhdp->flow_ring_table = (void *)flow_ring_table; ++ dhdp->if_flow_lkup = (void *)if_flow_lkup; ++ dhdp->flowid_lock = lock; ++ DHD_FLOWID_UNLOCK(lock, flags); ++ ++ DHD_INFO(("%s done\n", __FUNCTION__)); ++ return BCME_OK; ++ ++fail: ++ ++#ifdef PCIE_TX_DEFERRAL ++ if (dhdp->bus->delete_flow_map) ++ kfree(dhdp->bus->delete_flow_map); ++#endif ++ /* Destruct the per interface flow lkup table */ ++ if (dhdp->if_flow_lkup != NULL) { ++ DHD_OS_PREFREE(dhdp, if_flow_lkup, if_flow_lkup_sz); ++ } ++ if (flow_ring_table != NULL) { ++ for (idx = 0; idx < num_flow_rings; idx++) { ++ if (flow_ring_table[idx].lock != NULL) ++ dhd_os_spin_lock_deinit(dhdp->osh, flow_ring_table[idx].lock); ++ } ++ MFREE(dhdp->osh, flow_ring_table, flow_ring_table_sz); ++ } ++ id16_map_fini(dhdp->osh, flowid_allocator); ++ ++ return BCME_NOMEM; ++} ++ ++/* Deinit Flow Ring specific data structures */ ++void dhd_flow_rings_deinit(dhd_pub_t *dhdp) ++{ ++ uint16 idx; ++ uint32 flow_ring_table_sz; ++ uint32 if_flow_lkup_sz; ++ flow_ring_table_t *flow_ring_table; ++ unsigned long flags; ++ void *lock; ++ ++ DHD_INFO(("dhd_flow_rings_deinit\n")); ++ ++ if (dhdp->flow_ring_table != NULL) { ++ ++ ASSERT(dhdp->num_flow_rings > 0); ++ ++ DHD_FLOWID_LOCK(dhdp->flowid_lock, flags); ++ flow_ring_table = (flow_ring_table_t *)dhdp->flow_ring_table; ++ dhdp->flow_ring_table = NULL; ++ DHD_FLOWID_UNLOCK(dhdp->flowid_lock, flags); ++ for (idx = 0; idx < dhdp->num_flow_rings; idx++) { ++ if (flow_ring_table[idx].active) { ++ dhd_bus_clean_flow_ring(dhdp->bus, &flow_ring_table[idx]); ++ } ++ ASSERT(flow_queue_empty(&flow_ring_table[idx].queue)); ++ ++ /* Deinit flow ring queue locks before destroying flow ring table */ ++ dhd_os_spin_lock_deinit(dhdp->osh, flow_ring_table[idx].lock); ++ flow_ring_table[idx].lock = NULL; ++ } ++ ++ /* Destruct the flow ring table */ ++ flow_ring_table_sz = dhdp->num_flow_rings * sizeof(flow_ring_table_t); ++ MFREE(dhdp->osh, flow_ring_table, flow_ring_table_sz); ++ } ++ ++ DHD_FLOWID_LOCK(dhdp->flowid_lock, flags); ++ ++ /* Destruct the per interface flow lkup table */ ++ if (dhdp->if_flow_lkup != NULL) { ++ if_flow_lkup_sz = sizeof(if_flow_lkup_t) * DHD_MAX_IFS; ++ bzero(dhdp->if_flow_lkup, sizeof(if_flow_lkup_sz)); ++ DHD_OS_PREFREE(dhdp, dhdp->if_flow_lkup, if_flow_lkup_sz); ++ dhdp->if_flow_lkup = NULL; ++ } ++ ++#ifdef PCIE_TX_DEFERRAL ++ if (dhdp->bus->delete_flow_map) ++ kfree(dhdp->bus->delete_flow_map); ++#endif ++ ++ /* Destruct the flowid allocator */ ++ if (dhdp->flowid_allocator != NULL) ++ dhdp->flowid_allocator = id16_map_fini(dhdp->osh, dhdp->flowid_allocator); ++ ++ dhdp->num_flow_rings = 0U; ++ lock = dhdp->flowid_lock; ++ dhdp->flowid_lock = NULL; ++ ++ DHD_FLOWID_UNLOCK(lock, flags); ++ dhd_os_spin_lock_deinit(dhdp->osh, lock); ++} ++ ++uint8 ++dhd_flow_rings_ifindex2role(dhd_pub_t *dhdp, uint8 ifindex) ++{ ++ if_flow_lkup_t *if_flow_lkup = (if_flow_lkup_t *)dhdp->if_flow_lkup; ++ ASSERT(if_flow_lkup); ++ return if_flow_lkup[ifindex].role; ++} ++ ++#ifdef WLTDLS ++bool is_tdls_destination(dhd_pub_t *dhdp, uint8 *da) ++{ ++ tdls_peer_node_t *cur = dhdp->peer_tbl.node; ++ while (cur != NULL) { ++ if (!memcmp(da, cur->addr, ETHER_ADDR_LEN)) { ++ return TRUE; ++ } ++ cur = cur->next; ++ } ++ return FALSE; ++} ++#endif /* WLTDLS */ ++ ++/* For a given interface, search the hash table for a matching flow */ ++uint16 ++dhd_flowid_find(dhd_pub_t *dhdp, uint8 ifindex, uint8 prio, char *sa, char *da) ++{ ++ int hash; ++ bool ismcast = FALSE; ++ flow_hash_info_t *cur; ++ if_flow_lkup_t *if_flow_lkup; ++ unsigned long flags; ++ ++ DHD_FLOWID_LOCK(dhdp->flowid_lock, flags); ++ if_flow_lkup = (if_flow_lkup_t *)dhdp->if_flow_lkup; ++ ++ if (DHD_IF_ROLE_STA(if_flow_lkup[ifindex].role)) { ++#ifdef WLTDLS ++ if (dhdp->peer_tbl.tdls_peer_count && !(ETHER_ISMULTI(da)) && ++ is_tdls_destination(dhdp, da)) { ++ hash = DHD_FLOWRING_HASHINDEX(da, prio); ++ cur = if_flow_lkup[ifindex].fl_hash[hash]; ++ while (cur != NULL) { ++ if (!memcmp(cur->flow_info.da, da, ETHER_ADDR_LEN)) { ++ DHD_FLOWID_UNLOCK(dhdp->flowid_lock, flags); ++ return cur->flowid; ++ } ++ cur = cur->next; ++ } ++ DHD_FLOWID_UNLOCK(dhdp->flowid_lock, flags); ++ return FLOWID_INVALID; ++ } ++#endif /* WLTDLS */ ++ cur = if_flow_lkup[ifindex].fl_hash[prio]; ++ if (cur) { ++ DHD_FLOWID_UNLOCK(dhdp->flowid_lock, flags); ++ return cur->flowid; ++ } ++ ++ } else { ++ ++ if (ETHER_ISMULTI(da)) { ++ ismcast = TRUE; ++ hash = 0; ++ } else { ++ hash = DHD_FLOWRING_HASHINDEX(da, prio); ++ } ++ ++ cur = if_flow_lkup[ifindex].fl_hash[hash]; ++ ++ while (cur) { ++ if ((ismcast && ETHER_ISMULTI(cur->flow_info.da)) || ++ (!memcmp(cur->flow_info.da, da, ETHER_ADDR_LEN) && ++ (cur->flow_info.tid == prio))) { ++ DHD_FLOWID_UNLOCK(dhdp->flowid_lock, flags); ++ return cur->flowid; ++ } ++ cur = cur->next; ++ } ++ } ++ DHD_FLOWID_UNLOCK(dhdp->flowid_lock, flags); ++ ++ return FLOWID_INVALID; ++} ++ ++/* Allocate Flow ID */ ++static INLINE uint16 ++dhd_flowid_alloc(dhd_pub_t *dhdp, uint8 ifindex, uint8 prio, char *sa, char *da) ++{ ++ flow_hash_info_t *fl_hash_node, *cur; ++ if_flow_lkup_t *if_flow_lkup; ++ int hash; ++ uint16 flowid; ++ unsigned long flags; ++ ++ fl_hash_node = (flow_hash_info_t *) MALLOC(dhdp->osh, sizeof(flow_hash_info_t)); ++ memcpy(fl_hash_node->flow_info.da, da, sizeof(fl_hash_node->flow_info.da)); ++ ++ DHD_FLOWID_LOCK(dhdp->flowid_lock, flags); ++ ASSERT(dhdp->flowid_allocator != NULL); ++ flowid = id16_map_alloc(dhdp->flowid_allocator); ++ DHD_FLOWID_UNLOCK(dhdp->flowid_lock, flags); ++ ++ if (flowid == FLOWID_INVALID) { ++ MFREE(dhdp->osh, fl_hash_node, sizeof(flow_hash_info_t)); ++ DHD_ERROR(("%s: cannot get free flowid \n", __FUNCTION__)); ++ return FLOWID_INVALID; ++ } ++ ++ fl_hash_node->flowid = flowid; ++ fl_hash_node->flow_info.tid = prio; ++ fl_hash_node->flow_info.ifindex = ifindex; ++ fl_hash_node->next = NULL; ++ ++ DHD_FLOWID_LOCK(dhdp->flowid_lock, flags); ++ if_flow_lkup = (if_flow_lkup_t *)dhdp->if_flow_lkup; ++ if (DHD_IF_ROLE_STA(if_flow_lkup[ifindex].role)) { ++ /* For STA non TDLS dest we allocate entry based on prio only */ ++#ifdef WLTDLS ++ if (dhdp->peer_tbl.tdls_peer_count && ++ (is_tdls_destination(dhdp, da))) { ++ hash = DHD_FLOWRING_HASHINDEX(da, prio); ++ cur = if_flow_lkup[ifindex].fl_hash[hash]; ++ if (cur) { ++ while (cur->next) { ++ cur = cur->next; ++ } ++ cur->next = fl_hash_node; ++ } else { ++ if_flow_lkup[ifindex].fl_hash[hash] = fl_hash_node; ++ } ++ } else ++#endif /* WLTDLS */ ++ if_flow_lkup[ifindex].fl_hash[prio] = fl_hash_node; ++ } else { ++ ++ /* For bcast/mcast assign first slot in in interface */ ++ hash = ETHER_ISMULTI(da) ? 0 : DHD_FLOWRING_HASHINDEX(da, prio); ++ cur = if_flow_lkup[ifindex].fl_hash[hash]; ++ if (cur) { ++ while (cur->next) { ++ cur = cur->next; ++ } ++ cur->next = fl_hash_node; ++ } else ++ if_flow_lkup[ifindex].fl_hash[hash] = fl_hash_node; ++ } ++ DHD_FLOWID_UNLOCK(dhdp->flowid_lock, flags); ++ ++ DHD_INFO(("%s: allocated flowid %d\n", __FUNCTION__, fl_hash_node->flowid)); ++ ++ return fl_hash_node->flowid; ++} ++ ++/* Get flow ring ID, if not present try to create one */ ++static INLINE int ++dhd_flowid_lookup(dhd_pub_t *dhdp, uint8 ifindex, ++ uint8 prio, char *sa, char *da, uint16 *flowid) ++{ ++ uint16 id; ++ flow_ring_node_t *flow_ring_node; ++ flow_ring_table_t *flow_ring_table; ++ unsigned long flags; ++ ++ DHD_INFO(("%s\n", __FUNCTION__)); ++ ++ if (!dhdp->flow_ring_table) ++ return BCME_ERROR; ++ ++ flow_ring_table = (flow_ring_table_t *)dhdp->flow_ring_table; ++ ++ id = dhd_flowid_find(dhdp, ifindex, prio, sa, da); ++ ++ if (id == FLOWID_INVALID) { ++ ++ if_flow_lkup_t *if_flow_lkup; ++ if_flow_lkup = (if_flow_lkup_t *)dhdp->if_flow_lkup; ++ ++ if (!if_flow_lkup[ifindex].status) ++ return BCME_ERROR; ++ ++ id = dhd_flowid_alloc(dhdp, ifindex, prio, sa, da); ++ if (id == FLOWID_INVALID) { ++ DHD_ERROR(("%s: alloc flowid ifindex %u status %u\n", ++ __FUNCTION__, ifindex, if_flow_lkup[ifindex].status)); ++ return BCME_ERROR; ++ } ++ ++ /* register this flowid in dhd_pub */ ++ dhd_add_flowid(dhdp, ifindex, prio, da, id); ++ } ++ ++ ASSERT(id < dhdp->num_flow_rings); ++ ++ flow_ring_node = (flow_ring_node_t *) &flow_ring_table[id]; ++ DHD_FLOWRING_LOCK(flow_ring_node->lock, flags); ++ if (flow_ring_node->active) { ++ DHD_FLOWRING_UNLOCK(flow_ring_node->lock, flags); ++ *flowid = id; ++ return BCME_OK; ++ } ++ /* Init Flow info */ ++ memcpy(flow_ring_node->flow_info.sa, sa, sizeof(flow_ring_node->flow_info.sa)); ++ memcpy(flow_ring_node->flow_info.da, da, sizeof(flow_ring_node->flow_info.da)); ++ flow_ring_node->flow_info.tid = prio; ++ flow_ring_node->flow_info.ifindex = ifindex; ++ flow_ring_node->active = TRUE; ++ flow_ring_node->status = FLOW_RING_STATUS_PENDING; ++ DHD_FLOWRING_UNLOCK(flow_ring_node->lock, flags); ++ DHD_FLOWID_LOCK(dhdp->flowid_lock, flags); ++ dll_prepend(&dhdp->bus->const_flowring, &flow_ring_node->list); ++ DHD_FLOWID_UNLOCK(dhdp->flowid_lock, flags); ++ ++ /* Create and inform device about the new flow */ ++ if (dhd_bus_flow_ring_create_request(dhdp->bus, (void *)flow_ring_node) ++ != BCME_OK) { ++ DHD_ERROR(("%s: create error %d\n", __FUNCTION__, id)); ++ return BCME_ERROR; ++ } ++ ++ *flowid = id; ++ return BCME_OK; ++} ++ ++/* Update flowid information on the packet */ ++int BCMFASTPATH ++dhd_flowid_update(dhd_pub_t *dhdp, uint8 ifindex, uint8 prio, void *pktbuf) ++{ ++ uint8 *pktdata = (uint8 *)PKTDATA(dhdp->osh, pktbuf); ++ struct ether_header *eh = (struct ether_header *)pktdata; ++ uint16 flowid; ++ ++ if (dhd_bus_is_txmode_push(dhdp->bus)) ++ return BCME_OK; ++ ++ ASSERT(ifindex < DHD_MAX_IFS); ++ if (ifindex >= DHD_MAX_IFS) { ++ return BCME_BADARG; ++ } ++ ++ if (!dhdp->flowid_allocator) { ++ DHD_ERROR(("%s: Flow ring not intited yet \n", __FUNCTION__)); ++ return BCME_ERROR; ++ } ++ if (dhd_flowid_lookup(dhdp, ifindex, prio, eh->ether_shost, eh->ether_dhost, ++ &flowid) != BCME_OK) { ++ return BCME_ERROR; ++ } ++ ++ DHD_INFO(("%s: prio %d flowid %d\n", __FUNCTION__, prio, flowid)); ++ ++ /* Tag the packet with flowid */ ++ DHD_PKTTAG_SET_FLOWID((dhd_pkttag_fr_t *)PKTTAG(pktbuf), flowid); ++ return BCME_OK; ++} ++ ++void ++dhd_flowid_free(dhd_pub_t *dhdp, uint8 ifindex, uint16 flowid) ++{ ++ int hashix; ++ bool found = FALSE; ++ flow_hash_info_t *cur, *prev; ++ if_flow_lkup_t *if_flow_lkup; ++ unsigned long flags; ++ ++ DHD_FLOWID_LOCK(dhdp->flowid_lock, flags); ++ if_flow_lkup = (if_flow_lkup_t *)dhdp->if_flow_lkup; ++ ++ for (hashix = 0; hashix < DHD_FLOWRING_HASH_SIZE; hashix++) { ++ ++ cur = if_flow_lkup[ifindex].fl_hash[hashix]; ++ ++ if (cur) { ++ if (cur->flowid == flowid) { ++ found = TRUE; ++ } ++ ++ prev = NULL; ++ while (!found && cur) { ++ if (cur->flowid == flowid) { ++ found = TRUE; ++ break; ++ } ++ prev = cur; ++ cur = cur->next; ++ } ++ if (found) { ++ if (!prev) { ++ if_flow_lkup[ifindex].fl_hash[hashix] = cur->next; ++ } else { ++ prev->next = cur->next; ++ } ++ ++ /* deregister flowid from dhd_pub. */ ++ dhd_del_flowid(dhdp, ifindex, flowid); ++ ++ id16_map_free(dhdp->flowid_allocator, flowid); ++ DHD_FLOWID_UNLOCK(dhdp->flowid_lock, flags); ++ MFREE(dhdp->osh, cur, sizeof(flow_hash_info_t)); ++ ++ return; ++ } ++ } ++ } ++ ++ DHD_FLOWID_UNLOCK(dhdp->flowid_lock, flags); ++ DHD_ERROR(("%s: could not free flow ring hash entry flowid %d\n", ++ __FUNCTION__, flowid)); ++} ++ ++ ++/* Delete all Flow rings assocaited with the given Interface */ ++void ++dhd_flow_rings_delete(dhd_pub_t *dhdp, uint8 ifindex) ++{ ++ uint32 id; ++ flow_ring_table_t *flow_ring_table; ++ ++ DHD_INFO(("%s: ifindex %u\n", __FUNCTION__, ifindex)); ++ ++ ASSERT(ifindex < DHD_MAX_IFS); ++ if (ifindex >= DHD_MAX_IFS) ++ return; ++ ++ if (!dhdp->flow_ring_table) ++ return; ++ ++ flow_ring_table = (flow_ring_table_t *)dhdp->flow_ring_table; ++ for (id = 0; id < dhdp->num_flow_rings; id++) { ++ if (flow_ring_table[id].active && ++ (flow_ring_table[id].flow_info.ifindex == ifindex) && ++ (flow_ring_table[id].status != FLOW_RING_STATUS_DELETE_PENDING)) { ++ DHD_INFO(("%s: deleting flowid %d\n", ++ __FUNCTION__, flow_ring_table[id].flowid)); ++ dhd_bus_flow_ring_delete_request(dhdp->bus, ++ (void *) &flow_ring_table[id]); ++ } ++ } ++} ++ ++/* Delete flow/s for given peer address */ ++void ++dhd_flow_rings_delete_for_peer(dhd_pub_t *dhdp, uint8 ifindex, char *addr) ++{ ++ uint32 id; ++ flow_ring_table_t *flow_ring_table; ++ ++ DHD_ERROR(("%s: ifindex %u\n", __FUNCTION__, ifindex)); ++ ++ ASSERT(ifindex < DHD_MAX_IFS); ++ if (ifindex >= DHD_MAX_IFS) ++ return; ++ ++ if (!dhdp->flow_ring_table) ++ return; ++ ++ flow_ring_table = (flow_ring_table_t *)dhdp->flow_ring_table; ++ for (id = 0; id < dhdp->num_flow_rings; id++) { ++ if (flow_ring_table[id].active && ++ (flow_ring_table[id].flow_info.ifindex == ifindex) && ++ (!memcmp(flow_ring_table[id].flow_info.da, addr, ETHER_ADDR_LEN)) && ++ (flow_ring_table[id].status != FLOW_RING_STATUS_DELETE_PENDING)) { ++ DHD_INFO(("%s: deleting flowid %d\n", ++ __FUNCTION__, flow_ring_table[id].flowid)); ++ dhd_bus_flow_ring_delete_request(dhdp->bus, ++ (void *) &flow_ring_table[id]); ++ } ++ } ++} ++ ++/* Handle Interface ADD, DEL operations */ ++void ++dhd_update_interface_flow_info(dhd_pub_t *dhdp, uint8 ifindex, ++ uint8 op, uint8 role) ++{ ++ if_flow_lkup_t *if_flow_lkup; ++ unsigned long flags; ++ ++ ASSERT(ifindex < DHD_MAX_IFS); ++ if (ifindex >= DHD_MAX_IFS) ++ return; ++ ++ DHD_INFO(("%s: ifindex %u op %u role is %u \n", ++ __FUNCTION__, ifindex, op, role)); ++ if (!dhdp->flowid_allocator) { ++ DHD_ERROR(("%s: Flow ring not intited yet \n", __FUNCTION__)); ++ return; ++ } ++ ++ DHD_FLOWID_LOCK(dhdp->flowid_lock, flags); ++ if_flow_lkup = (if_flow_lkup_t *)dhdp->if_flow_lkup; ++ ++ if (op == WLC_E_IF_ADD || op == WLC_E_IF_CHANGE) { ++ ++ if_flow_lkup[ifindex].role = role; ++ ++ if (!(DHD_IF_ROLE_STA(role))) { ++ if_flow_lkup[ifindex].status = TRUE; ++ DHD_INFO(("%s: Mcast Flow ring for ifindex %d role is %d \n", ++ __FUNCTION__, ifindex, role)); ++ /* Create Mcast Flow */ ++ } ++ } else if (op == WLC_E_IF_DEL) { ++ if_flow_lkup[ifindex].status = FALSE; ++ DHD_INFO(("%s: cleanup all Flow rings for ifindex %d role is %d \n", ++ __FUNCTION__, ifindex, role)); ++ } ++ DHD_FLOWID_UNLOCK(dhdp->flowid_lock, flags); ++} ++ ++/* Handle a STA interface link status update */ ++int ++dhd_update_interface_link_status(dhd_pub_t *dhdp, uint8 ifindex, uint8 status) ++{ ++ if_flow_lkup_t *if_flow_lkup; ++ unsigned long flags; ++ ++ ASSERT(ifindex < DHD_MAX_IFS); ++ if (ifindex >= DHD_MAX_IFS) ++ return BCME_BADARG; ++ ++ DHD_INFO(("%s: ifindex %d status %d\n", __FUNCTION__, ifindex, status)); ++ ++ DHD_FLOWID_LOCK(dhdp->flowid_lock, flags); ++ if_flow_lkup = (if_flow_lkup_t *)dhdp->if_flow_lkup; ++ ++ if (DHD_IF_ROLE_STA(if_flow_lkup[ifindex].role)) { ++ if (status) ++ if_flow_lkup[ifindex].status = TRUE; ++ else ++ if_flow_lkup[ifindex].status = FALSE; ++ } ++ DHD_FLOWID_UNLOCK(dhdp->flowid_lock, flags); ++ ++ return BCME_OK; ++} ++/* Update flow priority mapping */ ++int dhd_update_flow_prio_map(dhd_pub_t *dhdp, uint8 map) ++{ ++ uint16 flowid; ++ flow_ring_node_t *flow_ring_node; ++ ++ if (map > DHD_FLOW_PRIO_TID_MAP) ++ return BCME_BADOPTION; ++ ++ /* Check if we need to change prio map */ ++ if (map == dhdp->flow_prio_map_type) ++ return BCME_OK; ++ ++ /* If any ring is active we cannot change priority mapping for flow rings */ ++ for (flowid = 0; flowid < dhdp->num_flow_rings; flowid++) { ++ flow_ring_node = DHD_FLOW_RING(dhdp, flowid); ++ if (flow_ring_node->active) ++ return BCME_EPERM; ++ } ++ /* Infor firmware about new mapping type */ ++ if (BCME_OK != dhd_flow_prio_map(dhdp, &map, TRUE)) ++ return BCME_ERROR; ++ ++ /* update internal structures */ ++ dhdp->flow_prio_map_type = map; ++ if (dhdp->flow_prio_map_type == DHD_FLOW_PRIO_TID_MAP) ++ bcopy(prio2tid, dhdp->flow_prio_map, sizeof(uint8) * NUMPRIO); ++ else ++ bcopy(prio2ac, dhdp->flow_prio_map, sizeof(uint8) * NUMPRIO); ++ ++ return BCME_OK; ++} ++ ++/* Set/Get flwo ring priority map */ ++int dhd_flow_prio_map(dhd_pub_t *dhd, uint8 *map, bool set) ++{ ++ uint8 iovbuf[24]; ++ if (!set) { ++ bcm_mkiovar("bus:fl_prio_map", NULL, 0, (char*)iovbuf, sizeof(iovbuf)); ++ if (dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, iovbuf, sizeof(iovbuf), FALSE, 0) < 0) { ++ DHD_ERROR(("%s: failed to get fl_prio_map\n", __FUNCTION__)); ++ return BCME_ERROR; ++ } ++ *map = iovbuf[0]; ++ return BCME_OK; ++ } ++ bcm_mkiovar("bus:fl_prio_map", (char *)map, 4, (char*)iovbuf, sizeof(iovbuf)); ++ if (dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0) < 0) { ++ DHD_ERROR(("%s: failed to set fl_prio_map \n", ++ __FUNCTION__)); ++ return BCME_ERROR; ++ } ++ return BCME_OK; ++} +diff -Nur a/drivers/net/wireless/bcmdhd/dhd_flowring.h c/drivers/net/wireless/bcmdhd/dhd_flowring.h +--- a/drivers/net/wireless/bcmdhd/dhd_flowring.h 1970-01-01 01:00:00.000000000 +0100 ++++ c/drivers/net/wireless/bcmdhd/dhd_flowring.h 2016-05-13 09:48:20.000000000 +0200 +@@ -0,0 +1,159 @@ ++/* ++ * Header file describing the flow rings DHD interfaces. ++ * ++ * Provides type definitions and function prototypes used to create, delete and manage ++ * ++ * flow rings at high level ++ * ++ * $Copyright Open Broadcom Corporation$ ++ * ++ * $Id: dhd_flowrings.h jaganlv $ ++ */ ++ ++/**************** ++ * Common types * ++ */ ++ ++#ifndef _dhd_flowrings_h_ ++#define _dhd_flowrings_h_ ++ ++/* Max pkts held in a flow ring's backup queue */ ++#define FLOW_RING_QUEUE_THRESHOLD (2048) ++ ++/* Number of H2D common rings : PCIE Spec Rev? */ ++#define FLOW_RING_COMMON 2 ++ ++#define FLOWID_INVALID (ID16_INVALID) ++#define FLOWID_RESERVED (FLOW_RING_COMMON) ++ ++#define FLOW_RING_STATUS_OPEN 0 ++#define FLOW_RING_STATUS_PENDING 1 ++#define FLOW_RING_STATUS_CLOSED 2 ++#define FLOW_RING_STATUS_DELETE_PENDING 3 ++#define FLOW_RING_STATUS_FLUSH_PENDING 4 ++ ++#define DHD_FLOWRING_RX_BUFPOST_PKTSZ 2048 ++ ++#define DHD_FLOW_PRIO_AC_MAP 0 ++#define DHD_FLOW_PRIO_TID_MAP 1 ++ ++ ++/* Pkttag not compatible with PROP_TXSTATUS or WLFC */ ++typedef struct dhd_pkttag_fr { ++ uint16 flowid; ++ int dataoff; ++} dhd_pkttag_fr_t; ++ ++#define DHD_PKTTAG_SET_FLOWID(tag, flow) ((tag)->flowid = (uint16)(flow)) ++#define DHD_PKTTAG_SET_DATAOFF(tag, offset) ((tag)->dataoff = (int)(offset)) ++ ++#define DHD_PKTTAG_FLOWID(tag) ((tag)->flowid) ++#define DHD_PKTTAG_DATAOFF(tag) ((tag)->dataoff) ++ ++/* Hashing a MacAddress for lkup into a per interface flow hash table */ ++#define DHD_FLOWRING_HASH_SIZE 256 ++#define DHD_FLOWRING_HASHINDEX(ea, prio) \ ++ ((((uint8 *)(ea))[3] ^ ((uint8 *)(ea))[4] ^ ((uint8 *)(ea))[5] ^ ((uint8)(prio))) \ ++ % DHD_FLOWRING_HASH_SIZE) ++ ++#define DHD_IF_ROLE(pub, idx) (((if_flow_lkup_t *)(pub)->if_flow_lkup)[idx].role) ++#define DHD_IF_ROLE_AP(pub, idx) (DHD_IF_ROLE(pub, idx) == WLC_E_IF_ROLE_AP) ++#define DHD_IF_ROLE_P2PGO(pub, idx) (DHD_IF_ROLE(pub, idx) == WLC_E_IF_ROLE_P2P_GO) ++#define DHD_FLOW_RING(dhdp, flowid) \ ++ (flow_ring_node_t *)&(((flow_ring_node_t *)((dhdp)->flow_ring_table))[flowid]) ++ ++struct flow_queue; ++ ++/* Flow Ring Queue Enqueue overflow callback */ ++typedef int (*flow_queue_cb_t)(struct flow_queue * queue, void * pkt); ++ ++typedef struct flow_queue { ++ dll_t list; /* manage a flowring queue in a dll */ ++ void * head; /* first packet in the queue */ ++ void * tail; /* last packet in the queue */ ++ uint16 len; /* number of packets in the queue */ ++ uint16 max; /* maximum number of packets, queue may hold */ ++ uint32 failures; /* enqueue failures due to queue overflow */ ++ flow_queue_cb_t cb; /* callback invoked on threshold crossing */ ++} flow_queue_t; ++ ++#define flow_queue_len(queue) ((int)(queue)->len) ++#define flow_queue_max(queue) ((int)(queue)->max) ++#define flow_queue_avail(queue) ((int)((queue)->max - (queue)->len)) ++#define flow_queue_full(queue) ((queue)->len >= (queue)->max) ++#define flow_queue_empty(queue) ((queue)->len == 0) ++ ++typedef struct flow_info { ++ uint8 tid; ++ uint8 ifindex; ++ char sa[ETHER_ADDR_LEN]; ++ char da[ETHER_ADDR_LEN]; ++} flow_info_t; ++ ++typedef struct flow_ring_node { ++ dll_t list; /* manage a constructed flowring in a dll, must be at first place */ ++ flow_queue_t queue; ++ bool active; ++ uint8 status; ++ uint16 flowid; ++ flow_info_t flow_info; ++ void *prot_info; ++ void *lock; /* lock for flowring access protection */ ++} flow_ring_node_t; ++typedef flow_ring_node_t flow_ring_table_t; ++ ++typedef struct flow_hash_info { ++ uint16 flowid; ++ flow_info_t flow_info; ++ struct flow_hash_info *next; ++} flow_hash_info_t; ++ ++typedef struct if_flow_lkup { ++ bool status; ++ uint8 role; /* Interface role: STA/AP */ ++ flow_hash_info_t *fl_hash[DHD_FLOWRING_HASH_SIZE]; /* Lkup Hash table */ ++} if_flow_lkup_t; ++ ++static INLINE flow_ring_node_t * ++dhd_constlist_to_flowring(dll_t *item) ++{ ++ return ((flow_ring_node_t *)item); ++} ++ ++/* Exported API */ ++ ++/* Flow ring's queue management functions */ ++extern void dhd_flow_queue_init(dhd_pub_t *dhdp, flow_queue_t *queue, int max); ++extern void dhd_flow_queue_register(flow_queue_t *queue, flow_queue_cb_t cb); ++extern int dhd_flow_queue_enqueue(dhd_pub_t *dhdp, flow_queue_t *queue, void *pkt); ++extern void * dhd_flow_queue_dequeue(dhd_pub_t *dhdp, flow_queue_t *queue); ++extern void dhd_flow_queue_reinsert(dhd_pub_t *dhdp, flow_queue_t *queue, void *pkt); ++ ++extern int dhd_flow_rings_init(dhd_pub_t *dhdp, uint32 num_flow_rings); ++ ++extern void dhd_flow_rings_deinit(dhd_pub_t *dhdp); ++ ++extern uint16 dhd_flowid_find(dhd_pub_t *dhdp, uint8 ifindex, uint8 prio, char *sa, char *da); ++ ++extern int dhd_flowid_update(dhd_pub_t *dhdp, uint8 ifindex, uint8 prio, ++ void *pktbuf); ++ ++extern void dhd_flowid_free(dhd_pub_t *dhdp, uint8 ifindex, uint16 flowid); ++ ++extern void dhd_flow_rings_delete(dhd_pub_t *dhdp, uint8 ifindex); ++ ++extern void dhd_flow_rings_delete_for_peer(dhd_pub_t *dhdp, uint8 ifindex, ++ char *addr); ++ ++/* Handle Interface ADD, DEL operations */ ++extern void dhd_update_interface_flow_info(dhd_pub_t *dhdp, uint8 ifindex, ++ uint8 op, uint8 role); ++ ++/* Handle a STA interface link status update */ ++extern int dhd_update_interface_link_status(dhd_pub_t *dhdp, uint8 ifindex, ++ uint8 status); ++extern int dhd_flow_prio_map(dhd_pub_t *dhd, uint8 *map, bool set); ++extern int dhd_update_flow_prio_map(dhd_pub_t *dhdp, uint8 map); ++ ++extern uint8 dhd_flow_rings_ifindex2role(dhd_pub_t *dhdp, uint8 ifindex); ++#endif /* _dhd_flowrings_h_ */ +diff -Nur a/drivers/net/wireless/bcmdhd/dhd_gpio.c c/drivers/net/wireless/bcmdhd/dhd_gpio.c +--- a/drivers/net/wireless/bcmdhd/dhd_gpio.c 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/dhd_gpio.c 2016-09-30 00:48:52.927893893 +0200 +@@ -1,19 +1,15 @@ + + #include +- +-#ifdef CUSTOMER_HW ++#include + + #ifdef CONFIG_MACH_ODROID_4210 + #include + #include + #include +- + #include +-#include // modifed plat-samsung/dev-hsmmcX.c EXPORT_SYMBOL(s3c_device_hsmmcx) added +- ++#include + #define sdmmc_channel s3c_device_hsmmc0 + #endif +- + #ifdef CONFIG_ARCH_SUNXI + #include + #include +@@ -24,15 +20,6 @@ + extern void wifi_pm_power(int on); + #endif + +-struct wifi_platform_data { +- int (*set_power)(bool val); +- int (*set_carddetect)(bool val); +- void *(*mem_prealloc)(int section, unsigned long size); +- int (*get_mac_addr)(unsigned char *buf); +- void *(*get_country_code)(char *ccode); +-}; +- +-struct resource dhd_wlan_resources = {0}; + struct wifi_platform_data dhd_wlan_control = {0}; + + #ifdef CUSTOMER_OOB +@@ -41,7 +28,7 @@ + uint host_oob_irq = 0; + + #ifdef CONFIG_MACH_ODROID_4210 +- printk("GPIO(WL_HOST_WAKE) = EXYNOS4_GPX0(7) = %d\n", EXYNOS4_GPX0(7)); ++ printf("GPIO(WL_HOST_WAKE) = EXYNOS4_GPX0(7) = %d\n", EXYNOS4_GPX0(7)); + host_oob_irq = gpio_to_irq(EXYNOS4_GPX0(7)); + gpio_direction_input(EXYNOS4_GPX0(7)); + #endif +@@ -66,7 +53,7 @@ + } + printk("gpio [%d] map to virq [%d] ok\n",wl_host_wake, host_oob_irq); + #endif +- printk("host_oob_irq: %d \r\n", host_oob_irq); ++ printf("host_oob_irq: %d\n", host_oob_irq); + + return host_oob_irq; + } +@@ -75,28 +62,13 @@ + { + uint host_oob_irq_flags = 0; + +-#ifdef CONFIG_MACH_ODROID_4210 +- host_oob_irq_flags = (IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL | IORESOURCE_IRQ_SHAREABLE) & IRQF_TRIGGER_MASK; +-#endif +-#ifdef CONFIG_ARCH_SUNXI +- script_item_value_type_e type; +- script_item_u val; +- int host_wake_invert = 0; +- +- type = script_get_item("wifi_para", "wl_host_wake_invert", &val); +- if (SCIRPT_ITEM_VALUE_TYPE_INT != type) { +- printk(("has no wl_host_wake_invert\n")); +- } else { +- host_wake_invert = val.val; +- } +- +- if(!host_wake_invert) +- host_oob_irq_flags = (IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL | IORESOURCE_IRQ_SHAREABLE) & IRQF_TRIGGER_MASK; +- else +- host_oob_irq_flags = (IORESOURCE_IRQ | IORESOURCE_IRQ_LOWLEVEL | IORESOURCE_IRQ_SHAREABLE) & IRQF_TRIGGER_MASK; ++#ifdef HW_OOB ++ host_oob_irq_flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL | IORESOURCE_IRQ_SHAREABLE; ++#else ++ host_oob_irq_flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE | IORESOURCE_IRQ_SHAREABLE; + #endif + +- printk("host_oob_irq_flags=%d\n", host_oob_irq_flags); ++ printf("host_oob_irq_flags=0x%X\n", host_oob_irq_flags); + + return host_oob_irq_flags; + } +@@ -107,22 +79,25 @@ + int err = 0; + + if (on) { +- printk("======== PULL WL_REG_ON HIGH! ========\n"); ++ printf("======== PULL WL_REG_ON HIGH! ========\n"); + #ifdef CONFIG_MACH_ODROID_4210 + err = gpio_set_value(EXYNOS4_GPK1(0), 1); + #endif + #ifdef CONFIG_ARCH_SUNXI ++ wifi_pm_power(0); ++ mdelay(200); + wifi_pm_power(1); ++ mdelay(200); ++#endif + /* Lets customer power to get stable */ + mdelay(100); +-#endif + } else { +- printk("======== PULL WL_REG_ON LOW! ========\n"); ++ printf("======== PULL WL_REG_ON LOW! ========\n"); + #ifdef CONFIG_MACH_ODROID_4210 + err = gpio_set_value(EXYNOS4_GPK1(0), 0); + #endif + #ifdef CONFIG_ARCH_SUNXI +- wifi_pm_power(0); ++ //wifi_pm_power(0); + #endif + } + +@@ -134,7 +109,7 @@ + int err = 0; + + if (present) { +- printk("======== Card detection to detect SDIO card! ========\n"); ++ printf("======== Card detection to detect SDIO card! ========\n"); + #ifdef CONFIG_MACH_ODROID_4210 + err = sdhci_s3c_force_presence_change(&sdmmc_channel, 1); + #endif +@@ -142,7 +117,7 @@ + sunxi_mci_rescan_card(sdc_id, 1); + #endif + } else { +- printk("======== Card detection to remove SDIO card! ========\n"); ++ printf("======== Card detection to remove SDIO card! ========\n"); + #ifdef CONFIG_MACH_ODROID_4210 + err = sdhci_s3c_force_presence_change(&sdmmc_channel, 0); + #endif +@@ -154,6 +129,22 @@ + return err; + } + ++int bcm_wlan_get_mac_address(unsigned char *buf) ++{ ++ int err = 0; ++ ++ printf("======== %s ========\n", __FUNCTION__); ++#ifdef EXAMPLE_GET_MAC ++ /* EXAMPLE code */ ++ { ++ struct ether_addr ea_example = {{0x00, 0x11, 0x22, 0x33, 0x44, 0xFF}}; ++ bcopy((char *)&ea_example, buf, sizeof(struct ether_addr)); ++ } ++#endif /* EXAMPLE_GET_MAC */ ++ ++ return err; ++} ++ + #ifdef CONFIG_DHD_USE_STATIC_BUF + extern void *bcmdhd_mem_prealloc(int section, unsigned long size); + void* bcm_wlan_prealloc(int section, unsigned long size) +@@ -161,26 +152,54 @@ + void *alloc_ptr = NULL; + alloc_ptr = bcmdhd_mem_prealloc(section, size); + if (alloc_ptr) { +- printk("success alloc section %d, size %ld\n", section, size); ++ printf("success alloc section %d, size %ld\n", section, size); + if (size != 0L) + bzero(alloc_ptr, size); + return alloc_ptr; + } +- printk("can't alloc section %d\n", section); ++ printf("can't alloc section %d\n", section); + return NULL; + } + #endif + +-#ifdef GET_CUSTOM_MAC_ENABLE +-extern int dhd_custom_get_mac_address(unsigned char *buf); ++#if !defined(WL_WIRELESS_EXT) ++struct cntry_locales_custom { ++ char iso_abbrev[WLC_CNTRY_BUF_SZ]; /* ISO 3166-1 country abbreviation */ ++ char custom_locale[WLC_CNTRY_BUF_SZ]; /* Custom firmware locale */ ++ int32 custom_locale_rev; /* Custom local revisin default -1 */ ++}; + #endif + ++static struct cntry_locales_custom brcm_wlan_translate_custom_table[] = { ++ /* Table should be filled out based on custom platform regulatory requirement */ ++ {"", "XT", 49}, /* Universal if Country code is unknown or empty */ ++ {"US", "US", 0}, ++}; ++ ++static void *bcm_wlan_get_country_code(char *ccode) ++{ ++ struct cntry_locales_custom *locales; ++ int size; ++ int i; ++ ++ if (!ccode) ++ return NULL; ++ ++ locales = brcm_wlan_translate_custom_table; ++ size = ARRAY_SIZE(brcm_wlan_translate_custom_table); ++ ++ for (i = 0; i < size; i++) ++ if (strcmp(ccode, locales[i].iso_abbrev) == 0) ++ return &locales[i]; ++ return NULL; ++} ++ + int bcm_wlan_set_plat_data(void) { + #ifdef CONFIG_ARCH_SUNXI + script_item_value_type_e type; + script_item_u val; + #endif +- printk("======== %s ========\n", __FUNCTION__); ++ printf("======== %s ========\n", __FUNCTION__); + #ifdef CONFIG_ARCH_SUNXI + type = script_get_item("wifi_para", "wifi_sdc_id", &val); + if (SCIRPT_ITEM_VALUE_TYPE_INT != type) { +@@ -191,13 +210,11 @@ + #endif + dhd_wlan_control.set_power = bcm_wlan_set_power; + dhd_wlan_control.set_carddetect = bcm_wlan_set_carddetect; ++ dhd_wlan_control.get_mac_addr = bcm_wlan_get_mac_address; + #ifdef CONFIG_DHD_USE_STATIC_BUF + dhd_wlan_control.mem_prealloc = bcm_wlan_prealloc; + #endif +-#ifdef GET_CUSTOM_MAC_ENABLE +- dhd_wlan_control.get_mac_addr = dhd_custom_get_mac_address; +-#endif ++ dhd_wlan_control.get_country_code = bcm_wlan_get_country_code; + return 0; + } + +-#endif /* CUSTOMER_HW */ +diff -Nur a/drivers/net/wireless/bcmdhd/dhd.h c/drivers/net/wireless/bcmdhd/dhd.h +--- a/drivers/net/wireless/bcmdhd/dhd.h 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/dhd.h 2016-05-13 09:48:20.000000000 +0200 +@@ -6,7 +6,7 @@ + * + * $Copyright Open Broadcom Corporation$ + * +- * $Id: dhd.h 491170 2014-07-15 06:23:58Z $ ++ * $Id: dhd.h 504503 2014-09-24 11:28:56Z $ + */ + + /**************** +@@ -41,6 +41,7 @@ + + #include + #include ++#include + + #if defined(BCMWDF) + #include +@@ -70,7 +71,8 @@ + DHD_BUS_SUSPEND, /* Bus has been suspended */ + }; + +-#if defined(NDISVER) && (NDISVER >= 0x0600) ++#if defined(NDISVER) ++#if (NDISVER >= 0x0600) + /* Firmware requested operation mode */ + #define STA_MASK 0x0001 + #define HOSTAPD_MASK 0x0002 +@@ -80,6 +82,10 @@ + #define P2P_GC_ENABLED 0x0020 + #define CONCURENT_MASK 0x00F0 + #endif /* (NDISVER >= 0x0600) */ ++#endif /* #if defined(NDISVER) */ ++ ++#define DHD_IF_ROLE_STA(role) (role == WLC_E_IF_ROLE_STA ||\ ++ role == WLC_E_IF_ROLE_P2P_CLIENT) + + /* For supporting multiple interfaces */ + #define DHD_MAX_IFS 16 +@@ -145,13 +151,20 @@ + #if defined(STATIC_WL_PRIV_STRUCT) + DHD_PREALLOC_WIPHY_ESCAN0 = 5, + #endif /* STATIC_WL_PRIV_STRUCT */ +- DHD_PREALLOC_DHD_INFO = 7 ++ DHD_PREALLOC_DHD_INFO = 7, ++ DHD_PREALLOC_DHD_WLFC_INFO = 8, ++ DHD_PREALLOC_IF_FLOW_LKUP = 9, ++ DHD_PREALLOC_FLOWRING = 10 + }; + + /* Packet alignment for most efficient SDIO (can change based on platform) */ + #ifndef DHD_SDALIGN ++#ifdef CUSTOM_SDIO_F2_BLKSIZE ++#define DHD_SDALIGN CUSTOM_SDIO_F2_BLKSIZE ++#else + #define DHD_SDALIGN 32 + #endif ++#endif + + /* host reordering packts logic */ + /* followed the structure to hold the reorder buffers (void **p) */ +@@ -177,6 +190,7 @@ + * 2. TCPACKs that don't need to hurry delivered remains longer in TXQ so can be suppressed. + */ + TCPACK_SUP_DELAYTX, ++ TCPACK_SUP_HOLD, + TCPACK_SUP_LAST_MODE + }; + #endif /* DHDTCPACK_SUPPRESS */ +@@ -294,7 +308,7 @@ + #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1 + struct mutex wl_start_stop_lock; /* lock/unlock for Android start/stop */ + struct mutex wl_softap_lock; /* lock/unlock for any SoftAP/STA settings */ +-#endif ++#endif + + #ifdef WLBTAMP + uint16 maxdatablks; +@@ -351,6 +365,8 @@ + #ifdef DHDTCPACK_SUPPRESS + uint8 tcpack_sup_mode; /* TCPACK suppress mode */ + void *tcpack_sup_module; /* TCPACK suppress module */ ++ uint32 tcpack_sup_ratio; ++ uint32 tcpack_sup_delay; + #endif /* DHDTCPACK_SUPPRESS */ + #if defined(ARP_OFFLOAD_SUPPORT) + uint32 arp_version; +@@ -358,20 +374,30 @@ + #if defined(BCMSUP_4WAY_HANDSHAKE) && defined(WLAN_AKM_SUITE_FT_8021X) + bool fw_4way_handshake; /* Whether firmware will to do the 4way handshake. */ + #endif ++#if defined(CUSTOM_PLATFORM_NV_TEGRA) ++#ifdef PKT_FILTER_SUPPORT ++ uint pkt_filter_mode; ++ uint pkt_filter_ports_count; ++ uint16 pkt_filter_ports[WL_PKT_FILTER_PORTS_MAX]; ++#endif /* PKT_FILTER_SUPPORT */ ++#endif /* defined(CUSTOM_PLATFORM_NV_TEGRA) */ + #ifdef CUSTOM_SET_CPUCORE + struct task_struct * current_dpc; + struct task_struct * current_rxf; + int chan_isvht80; + #endif /* CUSTOM_SET_CPUCORE */ + +- + void *sta_pool; /* pre-allocated pool of sta objects */ + void *staid_allocator; /* allocator of sta indexes */ + + void *flowid_allocator; /* unique flowid allocator */ + void *flow_ring_table; /* flow ring table, include prot and bus info */ + void *if_flow_lkup; /* per interface flowid lkup hash table */ ++ void *flowid_lock; /* per os lock for flowid info protection */ + uint32 num_flow_rings; ++ ++ uint32 d2h_sync_mode; /* D2H DMA completion sync mode */ ++ + uint8 flow_prio_map[NUMPRIO]; + uint8 flow_prio_map_type; + char enable_log[MAX_EVENT]; +@@ -431,7 +457,7 @@ + #else + #define DHD_PM_RESUME_RETURN_ERROR(a) do { \ + if (dhd_mmc_suspend) return a; } while (0) +- #endif ++ #endif + #define DHD_PM_RESUME_RETURN do { if (dhd_mmc_suspend) return; } while (0) + + #define DHD_SPINWAIT_SLEEP_INIT(a) DECLARE_WAIT_QUEUE_HEAD(a); +@@ -483,6 +509,10 @@ + extern int dhd_os_wake_lock_ctrl_timeout_cancel(dhd_pub_t *pub); + extern int dhd_os_wd_wake_lock(dhd_pub_t *pub); + extern int dhd_os_wd_wake_unlock(dhd_pub_t *pub); ++#ifdef BCMPCIE_OOB_HOST_WAKE ++extern int dhd_os_oob_irq_wake_lock_timeout(dhd_pub_t *pub, int val); ++extern int dhd_os_oob_irq_wake_unlock(dhd_pub_t *pub); ++#endif /* BCMPCIE_OOB_HOST_WAKE */ + extern int dhd_os_wake_lock_waive(dhd_pub_t *pub); + extern int dhd_os_wake_lock_restore(dhd_pub_t *pub); + +@@ -521,6 +551,11 @@ + + #define DHD_OS_WD_WAKE_LOCK(pub) dhd_os_wd_wake_lock(pub) + #define DHD_OS_WD_WAKE_UNLOCK(pub) dhd_os_wd_wake_unlock(pub) ++#ifdef BCMPCIE_OOB_HOST_WAKE ++#define OOB_WAKE_LOCK_TIMEOUT 500 ++#define DHD_OS_OOB_IRQ_WAKE_LOCK_TIMEOUT(pub, val) dhd_os_oob_irq_wake_lock_timeout(pub, val) ++#define DHD_OS_OOB_IRQ_WAKE_UNLOCK(pub) dhd_os_oob_irq_wake_unlock(pub) ++#endif /* BCMPCIE_OOB_HOST_WAKE */ + #define DHD_PACKET_TIMEOUT_MS 500 + #define DHD_EVENT_TIMEOUT_MS 1500 + +@@ -532,7 +567,7 @@ + void dhd_net_if_unlock(struct net_device *dev); + + #if defined(MULTIPLE_SUPPLICANT) +-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1 && defined(BCMSDIO) ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1 + extern struct mutex _dhd_sdio_mutex_lock_; + #endif + #endif /* MULTIPLE_SUPPLICANT */ +@@ -577,6 +612,7 @@ + /* Indication from bus module regarding removal/absence of dongle */ + extern void dhd_detach(dhd_pub_t *dhdp); + extern void dhd_free(dhd_pub_t *dhdp); ++extern void dhd_clear(dhd_pub_t *dhdp); + + /* Indication from bus module to change flow-control state */ + extern void dhd_txflowcontrol(dhd_pub_t *dhdp, int ifidx, bool on); +@@ -605,11 +641,6 @@ + extern int dhd_os_ioctl_resp_wake(dhd_pub_t * pub); + extern unsigned int dhd_os_get_ioctl_resp_timeout(void); + extern void dhd_os_set_ioctl_resp_timeout(unsigned int timeout_msec); +-#if 0 && (NDISVER >= 0x0600) +-#define dhd_os_open_image(a) wl_os_open_image(a) +-#define dhd_os_close_image(a) wl_os_close_image(a) +-#define dhd_os_get_image_block(a, b, c) wl_os_get_image_block(a, b, c) +-#endif /* (NDISVER >= 0x0600) */ + + extern int dhd_os_get_image_block(char * buf, int len, void * image); + extern void * dhd_os_open_image(char * filename); +@@ -629,7 +660,7 @@ + + extern int dhd_customer_oob_irq_map(void *adapter, unsigned long *irq_flags_ptr); + extern int dhd_customer_gpio_wlan_ctrl(void *adapter, int onoff); +-extern int dhd_custom_get_mac_address(unsigned char *buf); ++extern int dhd_custom_get_mac_address(void *adapter, unsigned char *buf); + extern void get_customized_country_code(void *adapter, char *country_iso_code, wl_country_t *cspec); + extern void dhd_os_sdunlock_sndup_rxq(dhd_pub_t * pub); + extern void dhd_os_sdlock_eventq(dhd_pub_t * pub); +@@ -639,6 +670,7 @@ + extern void dhd_set_version_info(dhd_pub_t *pub, char *fw); + extern bool dhd_os_check_if_up(dhd_pub_t *pub); + extern int dhd_os_check_wakelock(dhd_pub_t *pub); ++extern int dhd_os_check_wakelock_all(dhd_pub_t *pub); + extern int dhd_get_instance(dhd_pub_t *pub); + #ifdef CUSTOM_SET_CPUCORE + extern void dhd_set_cpucore(dhd_pub_t *dhd, int set); +@@ -648,6 +680,10 @@ + extern int dhd_keep_alive_onoff(dhd_pub_t *dhd); + #endif /* KEEP_ALIVE */ + ++#ifdef SUPPORT_AP_POWERSAVE ++extern int dhd_set_ap_powersave(dhd_pub_t *dhdp, int ifidx, int enable); ++#endif ++ + + #ifdef PKT_FILTER_SUPPORT + #define DHD_UNICAST_FILTER_NUM 0 +@@ -656,10 +692,24 @@ + #define DHD_MULTICAST6_FILTER_NUM 3 + #define DHD_MDNS_FILTER_NUM 4 + #define DHD_ARP_FILTER_NUM 5 +-extern int dhd_os_enable_packet_filter(dhd_pub_t *dhdp, int val); ++ ++#if defined(CUSTOM_PLATFORM_NV_TEGRA) ++/* Port based packet filtering command actions */ ++#define PKT_FILTER_PORTS_CLEAR 0 ++#define PKT_FILTER_PORTS_ADD 1 ++#define PKT_FILTER_PORTS_DEL 2 ++#define PKT_FILTER_PORTS_LOOPBACK 3 ++#define PKT_FILTER_PORTS_MAX PKT_FILTER_PORTS_LOOPBACK ++#endif /* defined(CUSTOM_PLATFORM_NV_TEGRA) */ ++ ++extern int dhd_os_enable_packet_filter(dhd_pub_t *dhdp, int val); + extern void dhd_enable_packet_filter(int value, dhd_pub_t *dhd); + extern int net_os_enable_packet_filter(struct net_device *dev, int val); + extern int net_os_rxfilter_add_remove(struct net_device *dev, int val, int num); ++#if defined(CUSTOM_PLATFORM_NV_TEGRA) ++extern void dhd_set_packet_filter_mode(struct net_device *dev, char *command); ++extern int dhd_set_packet_filter_ports(struct net_device *dev, char *command); ++#endif /* defined(CUSTOM_PLATFORM_NV_TEGRA) */ + #endif /* PKT_FILTER_SUPPORT */ + + extern int dhd_get_suspend_bcn_li_dtim(dhd_pub_t *dhd); +@@ -776,6 +826,27 @@ + + /* Watchdog timer interval */ + extern uint dhd_watchdog_ms; ++extern bool dhd_os_wd_timer_enabled(void *bus); ++ ++#ifdef PKT_STATICS ++typedef struct pkt_statics { ++ uint16 event_count; ++ uint32 event_size; ++ uint16 ctrl_count; ++ uint32 ctrl_size; ++ uint32 data_count; ++ uint32 data_size; ++ uint16 glom_1_count; ++ uint16 glom_3_count; ++ uint16 glom_3_8_count; ++ uint16 glom_8_count; ++ uint16 glom_max; ++ uint16 glom_count; ++ uint32 glom_size; ++ uint16 test_count; ++ uint32 test_size; ++} pkt_statics_t; ++#endif + + #if defined(DHD_DEBUG) + /* Console output poll interval */ +@@ -897,7 +968,11 @@ + #define WIFI_TURNON_DELAY DEFAULT_WIFI_TURNON_DELAY + #endif /* WIFI_TURNON_DELAY */ + ++#ifdef BCMSDIO + #define DEFAULT_DHD_WATCHDOG_INTERVAL_MS 10 /* msec */ ++#else ++#define DEFAULT_DHD_WATCHDOG_INTERVAL_MS 0 /* msec */ ++#endif + #ifndef CUSTOM_DHD_WATCHDOG_MS + #define CUSTOM_DHD_WATCHDOG_MS DEFAULT_DHD_WATCHDOG_INTERVAL_MS + #endif /* DEFAULT_DHD_WATCHDOG_INTERVAL_MS */ +@@ -914,6 +989,10 @@ + #endif + #endif /* WLTDLS */ + ++#define DEFAULT_BCN_TIMEOUT 8 ++#ifndef CUSTOM_BCN_TIMEOUT ++#define CUSTOM_BCN_TIMEOUT DEFAULT_BCN_TIMEOUT ++#endif + + #define MAX_DTIM_SKIP_BEACON_INTERVAL 100 /* max allowed associated AP beacon for DTIM skip */ + #ifndef MAX_DTIM_ALLOWED_INTERVAL +@@ -1020,9 +1099,13 @@ + #define DHD_GENERAL_UNLOCK(dhdp, flags) \ + dhd_os_general_spin_unlock((dhdp), (flags)) + +-/* Enable DHD flowring queue spin lock/unlock */ +-#define DHD_QUEUE_LOCK(lock, flags) (flags) = dhd_os_spin_lock(lock) +-#define DHD_QUEUE_UNLOCK(lock, flags) dhd_os_spin_unlock((lock), (flags)) ++/* Enable DHD flowring spin lock/unlock */ ++#define DHD_FLOWRING_LOCK(lock, flags) (flags) = dhd_os_spin_lock(lock) ++#define DHD_FLOWRING_UNLOCK(lock, flags) dhd_os_spin_unlock((lock), (flags)) ++ ++/* Enable DHD common flowring info spin lock/unlock */ ++#define DHD_FLOWID_LOCK(lock, flags) (flags) = dhd_os_spin_lock(lock) ++#define DHD_FLOWID_UNLOCK(lock, flags) dhd_os_spin_unlock((lock), (flags)) + + + +@@ -1032,9 +1115,7 @@ + } wl_io_pport_t; + + extern void *dhd_pub_wlinfo(dhd_pub_t *dhd_pub); +-#ifdef EXYNOS5433_PCIE_WAR +-extern void exynos_pcie_set_l1_exit(void); +-extern void exynos_pcie_clear_l1_exit(void); +-extern int enum_wifi; +-#endif /* EXYNOS5433_PCIE_WAR */ ++#ifdef CONFIG_MACH_UNIVERSAL5433 ++extern int check_rev(void); ++#endif + #endif /* _dhd_h_ */ +diff -Nur a/drivers/net/wireless/bcmdhd/dhd_ip.c c/drivers/net/wireless/bcmdhd/dhd_ip.c +--- a/drivers/net/wireless/bcmdhd/dhd_ip.c 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/dhd_ip.c 2016-05-13 09:48:20.000000000 +0200 +@@ -3,7 +3,7 @@ + * + * $Copyright Open Broadcom Corporation$ + * +- * $Id: dhd_ip.c 468932 2014-04-09 06:58:15Z $ ++ * $Id: dhd_ip.c 502735 2014-09-16 00:53:02Z $ + */ + #include + #include +@@ -98,11 +98,79 @@ + } + } + ++bool pkt_is_dhcp(osl_t *osh, void *p) ++{ ++ uint8 *frame; ++ int length; ++ uint8 *pt; /* Pointer to type field */ ++ uint16 ethertype; ++ struct ipv4_hdr *iph; /* IP frame pointer */ ++ int ipl; /* IP frame length */ ++ uint16 src_port; ++ ++ ASSERT(osh && p); ++ ++ frame = PKTDATA(osh, p); ++ length = PKTLEN(osh, p); ++ ++ /* Process Ethernet II or SNAP-encapsulated 802.3 frames */ ++ if (length < ETHER_HDR_LEN) { ++ DHD_INFO(("%s: short eth frame (%d)\n", __FUNCTION__, length)); ++ return FALSE; ++ } else if (ntoh16(*(uint16 *)(frame + ETHER_TYPE_OFFSET)) >= ETHER_TYPE_MIN) { ++ /* Frame is Ethernet II */ ++ pt = frame + ETHER_TYPE_OFFSET; ++ } else if (length >= ETHER_HDR_LEN + SNAP_HDR_LEN + ETHER_TYPE_LEN && ++ !bcmp(llc_snap_hdr, frame + ETHER_HDR_LEN, SNAP_HDR_LEN)) { ++ pt = frame + ETHER_HDR_LEN + SNAP_HDR_LEN; ++ } else { ++ DHD_INFO(("%s: non-SNAP 802.3 frame\n", __FUNCTION__)); ++ return FALSE; ++ } ++ ++ ethertype = ntoh16(*(uint16 *)pt); ++ ++ /* Skip VLAN tag, if any */ ++ if (ethertype == ETHER_TYPE_8021Q) { ++ pt += VLAN_TAG_LEN; ++ ++ if (pt + ETHER_TYPE_LEN > frame + length) { ++ DHD_INFO(("%s: short VLAN frame (%d)\n", __FUNCTION__, length)); ++ return FALSE; ++ } ++ ++ ethertype = ntoh16(*(uint16 *)pt); ++ } ++ ++ if (ethertype != ETHER_TYPE_IP) { ++ DHD_INFO(("%s: non-IP frame (ethertype 0x%x, length %d)\n", ++ __FUNCTION__, ethertype, length)); ++ return FALSE; ++ } ++ ++ iph = (struct ipv4_hdr *)(pt + ETHER_TYPE_LEN); ++ ipl = (uint)(length - (pt + ETHER_TYPE_LEN - frame)); ++ ++ /* We support IPv4 only */ ++ if ((ipl < (IPV4_OPTIONS_OFFSET + 2)) || (IP_VER(iph) != IP_VER_4)) { ++ DHD_INFO(("%s: short frame (%d) or non-IPv4\n", __FUNCTION__, ipl)); ++ return FALSE; ++ } ++ ++ src_port = ntoh16(*(uint16 *)(pt + ETHER_TYPE_LEN + IPV4_OPTIONS_OFFSET)); ++ ++ return (src_port == 0x43 || src_port == 0x44); ++} ++ + #ifdef DHDTCPACK_SUPPRESS + + typedef struct { +- void *pkt_in_q; /* TCP ACK packet that is already in txq or DelayQ */ ++ void *pkt_in_q; /* TCP ACK packet that is already in txq or DelayQ */ + void *pkt_ether_hdr; /* Ethernet header pointer of pkt_in_q */ ++ int ifidx; ++ uint8 supp_cnt; ++ dhd_pub_t *dhdp; ++ struct timer_list timer; + } tcpack_info_t; + + typedef struct _tdata_psh_info_t { +@@ -258,6 +326,46 @@ + return; + } + ++static void dhd_tcpack_send(ulong data) ++{ ++ tcpack_sup_module_t *tcpack_sup_mod; ++ tcpack_info_t *cur_tbl = (tcpack_info_t *)data; ++ dhd_pub_t *dhdp; ++ int ifidx; ++ void* pkt; ++ ++ if (!cur_tbl) { ++ return; ++ } ++ ++ dhdp = cur_tbl->dhdp; ++ if (!dhdp) { ++ return; ++ } ++ ++ dhd_os_tcpacklock(dhdp); ++ ++ tcpack_sup_mod = dhdp->tcpack_sup_module; ++ pkt = cur_tbl->pkt_in_q; ++ ifidx = cur_tbl->ifidx; ++ if (!pkt) { ++ dhd_os_tcpackunlock(dhdp); ++ return; ++ } ++ cur_tbl->pkt_in_q = NULL; ++ cur_tbl->pkt_ether_hdr = NULL; ++ cur_tbl->ifidx = 0; ++ cur_tbl->supp_cnt = 0; ++ if (--tcpack_sup_mod->tcpack_info_cnt < 0) { ++ DHD_ERROR(("%s %d: ERROR!!! tcp_ack_info_cnt %d\n", ++ __FUNCTION__, __LINE__, tcpack_sup_mod->tcpack_info_cnt)); ++ } ++ ++ dhd_os_tcpackunlock(dhdp); ++ ++ dhd_sendpkt(dhdp, ifidx, pkt); ++} ++ + int dhd_tcpack_suppress_set(dhd_pub_t *dhdp, uint8 mode) + { + int ret = BCME_OK; +@@ -325,6 +433,22 @@ + dhd_bus_set_dotxinrx(dhdp->bus, FALSE); + } + ++ if (mode == TCPACK_SUP_HOLD) { ++ int i; ++ tcpack_sup_module_t *tcpack_sup_mod = ++ (tcpack_sup_module_t *)dhdp->tcpack_sup_module; ++ dhdp->tcpack_sup_ratio = TCPACK_SUPP_RATIO; ++ dhdp->tcpack_sup_delay = TCPACK_DELAY_TIME; ++ for (i = 0; i < TCPACK_INFO_MAXNUM; i++) ++ { ++ tcpack_sup_mod->tcpack_info_tbl[i].dhdp = dhdp; ++ init_timer(&tcpack_sup_mod->tcpack_info_tbl[i].timer); ++ tcpack_sup_mod->tcpack_info_tbl[i].timer.data = ++ (ulong)&tcpack_sup_mod->tcpack_info_tbl[i]; ++ tcpack_sup_mod->tcpack_info_tbl[i].timer.function = dhd_tcpack_send; ++ } ++ } ++ + exit: + dhd_os_tcpackunlock(dhdp); + return ret; +@@ -334,6 +458,7 @@ + dhd_tcpack_info_tbl_clean(dhd_pub_t *dhdp) + { + tcpack_sup_module_t *tcpack_sup_mod = dhdp->tcpack_sup_module; ++ int i; + + if (dhdp->tcpack_sup_mode == TCPACK_SUP_OFF) + goto exit; +@@ -347,10 +472,30 @@ + goto exit; + } + +- tcpack_sup_mod->tcpack_info_cnt = 0; +- bzero(tcpack_sup_mod->tcpack_info_tbl, sizeof(tcpack_info_t) * TCPACK_INFO_MAXNUM); ++ if (dhdp->tcpack_sup_mode == TCPACK_SUP_HOLD) { ++ for (i = 0; i < TCPACK_INFO_MAXNUM; i++) { ++ if (tcpack_sup_mod->tcpack_info_tbl[i].pkt_in_q) { ++ PKTFREE(dhdp->osh, tcpack_sup_mod->tcpack_info_tbl[i].pkt_in_q, ++ TRUE); ++ tcpack_sup_mod->tcpack_info_tbl[i].pkt_in_q = NULL; ++ tcpack_sup_mod->tcpack_info_tbl[i].pkt_ether_hdr = NULL; ++ tcpack_sup_mod->tcpack_info_tbl[i].ifidx = 0; ++ tcpack_sup_mod->tcpack_info_tbl[i].supp_cnt = 0; ++ } ++ } ++ } else { ++ tcpack_sup_mod->tcpack_info_cnt = 0; ++ bzero(tcpack_sup_mod->tcpack_info_tbl, sizeof(tcpack_info_t) * TCPACK_INFO_MAXNUM); ++ } ++ + dhd_os_tcpackunlock(dhdp); + ++ if (dhdp->tcpack_sup_mode == TCPACK_SUP_HOLD) { ++ for (i = 0; i < TCPACK_INFO_MAXNUM; i++) { ++ del_timer_sync(&tcpack_sup_mod->tcpack_info_tbl[i].timer); ++ } ++ } ++ + exit: + return; + } +@@ -854,7 +999,7 @@ + bcopy(last_tdata_info, tdata_info_tmp, sizeof(tcpdata_info_t)); + } + bzero(last_tdata_info, sizeof(tcpdata_info_t)); +- DHD_TRACE(("%s %d: tcpdata_info(idx %d) is aged out. ttl cnt is now %d\n", ++ DHD_ERROR(("%s %d: tcpdata_info(idx %d) is aged out. ttl cnt is now %d\n", + __FUNCTION__, __LINE__, i, tcpack_sup_mod->tcpdata_info_cnt)); + /* Don't increase "i" here, so that the prev last tcpdata_info is checked */ + } else +@@ -883,7 +1028,7 @@ + /* No TCP flow with the same IP addr and TCP port is found + * in tcp_data_info_tbl. So add this flow to the table. + */ +- DHD_TRACE(("%s %d: Add data info to tbl[%d]: IP addr "IPV4_ADDR_STR" "IPV4_ADDR_STR ++ DHD_ERROR(("%s %d: Add data info to tbl[%d]: IP addr "IPV4_ADDR_STR" "IPV4_ADDR_STR + " TCP port %d %d\n", + __FUNCTION__, __LINE__, tcpack_sup_mod->tcpdata_info_cnt, + IPV4_ADDR_TO_STR(ntoh32_ua(&ip_hdr[IPV4_SRC_IP_OFFSET])), +@@ -939,4 +1084,202 @@ + return ret; + } + ++bool ++dhd_tcpack_hold(dhd_pub_t *dhdp, void *pkt, int ifidx) ++{ ++ uint8 *new_ether_hdr; /* Ethernet header of the new packet */ ++ uint16 new_ether_type; /* Ethernet type of the new packet */ ++ uint8 *new_ip_hdr; /* IP header of the new packet */ ++ uint8 *new_tcp_hdr; /* TCP header of the new packet */ ++ uint32 new_ip_hdr_len; /* IP header length of the new packet */ ++ uint32 cur_framelen; ++ uint32 new_tcp_ack_num; /* TCP acknowledge number of the new packet */ ++ uint16 new_ip_total_len; /* Total length of IP packet for the new packet */ ++ uint32 new_tcp_hdr_len; /* TCP header length of the new packet */ ++ tcpack_sup_module_t *tcpack_sup_mod; ++ tcpack_info_t *tcpack_info_tbl; ++ int i, free_slot = TCPACK_INFO_MAXNUM; ++ bool hold = FALSE; ++ ++ if (dhdp->tcpack_sup_mode != TCPACK_SUP_HOLD) { ++ goto exit; ++ } ++ ++ if (dhdp->tcpack_sup_ratio == 1) { ++ goto exit; ++ } ++ ++ new_ether_hdr = PKTDATA(dhdp->osh, pkt); ++ cur_framelen = PKTLEN(dhdp->osh, pkt); ++ ++ if (cur_framelen < TCPACKSZMIN || cur_framelen > TCPACKSZMAX) { ++ DHD_TRACE(("%s %d: Too short or long length %d to be TCP ACK\n", ++ __FUNCTION__, __LINE__, cur_framelen)); ++ goto exit; ++ } ++ ++ new_ether_type = new_ether_hdr[12] << 8 | new_ether_hdr[13]; ++ ++ if (new_ether_type != ETHER_TYPE_IP) { ++ DHD_TRACE(("%s %d: Not a IP packet 0x%x\n", ++ __FUNCTION__, __LINE__, new_ether_type)); ++ goto exit; ++ } ++ ++ DHD_TRACE(("%s %d: IP pkt! 0x%x\n", __FUNCTION__, __LINE__, new_ether_type)); ++ ++ new_ip_hdr = new_ether_hdr + ETHER_HDR_LEN; ++ cur_framelen -= ETHER_HDR_LEN; ++ ++ ASSERT(cur_framelen >= IPV4_MIN_HEADER_LEN); ++ ++ new_ip_hdr_len = IPV4_HLEN(new_ip_hdr); ++ if (IP_VER(new_ip_hdr) != IP_VER_4 || IPV4_PROT(new_ip_hdr) != IP_PROT_TCP) { ++ DHD_TRACE(("%s %d: Not IPv4 nor TCP! ip ver %d, prot %d\n", ++ __FUNCTION__, __LINE__, IP_VER(new_ip_hdr), IPV4_PROT(new_ip_hdr))); ++ goto exit; ++ } ++ ++ new_tcp_hdr = new_ip_hdr + new_ip_hdr_len; ++ cur_framelen -= new_ip_hdr_len; ++ ++ ASSERT(cur_framelen >= TCP_MIN_HEADER_LEN); ++ ++ DHD_TRACE(("%s %d: TCP pkt!\n", __FUNCTION__, __LINE__)); ++ ++ /* is it an ack ? Allow only ACK flag, not to suppress others. */ ++ if (new_tcp_hdr[TCP_FLAGS_OFFSET] != TCP_FLAG_ACK) { ++ DHD_TRACE(("%s %d: Do not touch TCP flag 0x%x\n", ++ __FUNCTION__, __LINE__, new_tcp_hdr[TCP_FLAGS_OFFSET])); ++ goto exit; ++ } ++ ++ new_ip_total_len = ntoh16_ua(&new_ip_hdr[IPV4_PKTLEN_OFFSET]); ++ new_tcp_hdr_len = 4 * TCP_HDRLEN(new_tcp_hdr[TCP_HLEN_OFFSET]); ++ ++ /* This packet has TCP data, so just send */ ++ if (new_ip_total_len > new_ip_hdr_len + new_tcp_hdr_len) { ++ DHD_TRACE(("%s %d: Do nothing for TCP DATA\n", __FUNCTION__, __LINE__)); ++ goto exit; ++ } ++ ++ ASSERT(new_ip_total_len == new_ip_hdr_len + new_tcp_hdr_len); ++ ++ new_tcp_ack_num = ntoh32_ua(&new_tcp_hdr[TCP_ACK_NUM_OFFSET]); ++ ++ DHD_TRACE(("%s %d: TCP ACK with zero DATA length" ++ " IP addr "IPV4_ADDR_STR" "IPV4_ADDR_STR" TCP port %d %d\n", ++ __FUNCTION__, __LINE__, ++ IPV4_ADDR_TO_STR(ntoh32_ua(&new_ip_hdr[IPV4_SRC_IP_OFFSET])), ++ IPV4_ADDR_TO_STR(ntoh32_ua(&new_ip_hdr[IPV4_DEST_IP_OFFSET])), ++ ntoh16_ua(&new_tcp_hdr[TCP_SRC_PORT_OFFSET]), ++ ntoh16_ua(&new_tcp_hdr[TCP_DEST_PORT_OFFSET]))); ++ ++ /* Look for tcp_ack_info that has the same ip src/dst addrs and tcp src/dst ports */ ++ dhd_os_tcpacklock(dhdp); ++ ++ tcpack_sup_mod = dhdp->tcpack_sup_module; ++ tcpack_info_tbl = tcpack_sup_mod->tcpack_info_tbl; ++ ++ if (!tcpack_sup_mod) { ++ DHD_ERROR(("%s %d: tcpack suppress module NULL!!\n", __FUNCTION__, __LINE__)); ++ dhd_os_tcpackunlock(dhdp); ++ goto exit; ++ } ++ ++ hold = TRUE; ++ ++ for (i = 0; i < TCPACK_INFO_MAXNUM; i++) { ++ void *oldpkt; /* TCPACK packet that is already in txq or DelayQ */ ++ uint8 *old_ether_hdr, *old_ip_hdr, *old_tcp_hdr; ++ uint32 old_ip_hdr_len, old_tcp_hdr_len; ++ uint32 old_tcpack_num; /* TCP ACK number of old TCPACK packet in Q */ ++ ++ if ((oldpkt = tcpack_info_tbl[i].pkt_in_q) == NULL) { ++ if (free_slot == TCPACK_INFO_MAXNUM) { ++ free_slot = i; ++ } ++ continue; ++ } ++ ++ if (PKTDATA(dhdp->osh, oldpkt) == NULL) { ++ DHD_ERROR(("%s %d: oldpkt data NULL!! cur idx %d\n", ++ __FUNCTION__, __LINE__, i)); ++ hold = FALSE; ++ dhd_os_tcpackunlock(dhdp); ++ goto exit; ++ } ++ ++ old_ether_hdr = tcpack_info_tbl[i].pkt_ether_hdr; ++ old_ip_hdr = old_ether_hdr + ETHER_HDR_LEN; ++ old_ip_hdr_len = IPV4_HLEN(old_ip_hdr); ++ old_tcp_hdr = old_ip_hdr + old_ip_hdr_len; ++ old_tcp_hdr_len = 4 * TCP_HDRLEN(old_tcp_hdr[TCP_HLEN_OFFSET]); ++ ++ DHD_TRACE(("%s %d: oldpkt %p[%d], IP addr "IPV4_ADDR_STR" "IPV4_ADDR_STR ++ " TCP port %d %d\n", __FUNCTION__, __LINE__, oldpkt, i, ++ IPV4_ADDR_TO_STR(ntoh32_ua(&old_ip_hdr[IPV4_SRC_IP_OFFSET])), ++ IPV4_ADDR_TO_STR(ntoh32_ua(&old_ip_hdr[IPV4_DEST_IP_OFFSET])), ++ ntoh16_ua(&old_tcp_hdr[TCP_SRC_PORT_OFFSET]), ++ ntoh16_ua(&old_tcp_hdr[TCP_DEST_PORT_OFFSET]))); ++ ++ /* If either of IP address or TCP port number does not match, skip. */ ++ if (memcmp(&new_ip_hdr[IPV4_SRC_IP_OFFSET], ++ &old_ip_hdr[IPV4_SRC_IP_OFFSET], IPV4_ADDR_LEN * 2) || ++ memcmp(&new_tcp_hdr[TCP_SRC_PORT_OFFSET], ++ &old_tcp_hdr[TCP_SRC_PORT_OFFSET], TCP_PORT_LEN * 2)) { ++ continue; ++ } ++ ++ old_tcpack_num = ntoh32_ua(&old_tcp_hdr[TCP_ACK_NUM_OFFSET]); ++ ++ if (IS_TCPSEQ_GE(new_tcp_ack_num, old_tcpack_num)) { ++ tcpack_info_tbl[i].supp_cnt++; ++ if (tcpack_info_tbl[i].supp_cnt >= dhdp->tcpack_sup_ratio) { ++ tcpack_info_tbl[i].pkt_in_q = NULL; ++ tcpack_info_tbl[i].pkt_ether_hdr = NULL; ++ tcpack_info_tbl[i].ifidx = 0; ++ tcpack_info_tbl[i].supp_cnt = 0; ++ hold = FALSE; ++ } else { ++ tcpack_info_tbl[i].pkt_in_q = pkt; ++ tcpack_info_tbl[i].pkt_ether_hdr = new_ether_hdr; ++ tcpack_info_tbl[i].ifidx = ifidx; ++ } ++ PKTFREE(dhdp->osh, oldpkt, TRUE); ++ } else { ++ PKTFREE(dhdp->osh, pkt, TRUE); ++ } ++ dhd_os_tcpackunlock(dhdp); ++ ++ if (!hold) { ++ del_timer_sync(&tcpack_info_tbl[i].timer); ++ } ++ goto exit; ++ } ++ ++ if (free_slot < TCPACK_INFO_MAXNUM) { ++ /* No TCPACK packet with the same IP addr and TCP port is found ++ * in tcp_ack_info_tbl. So add this packet to the table. ++ */ ++ DHD_TRACE(("%s %d: Add pkt 0x%p(ether_hdr 0x%p) to tbl[%d]\n", ++ __FUNCTION__, __LINE__, pkt, new_ether_hdr, ++ free_slot)); ++ ++ tcpack_info_tbl[free_slot].pkt_in_q = pkt; ++ tcpack_info_tbl[free_slot].pkt_ether_hdr = new_ether_hdr; ++ tcpack_info_tbl[free_slot].ifidx = ifidx; ++ tcpack_info_tbl[free_slot].supp_cnt = 1; ++ mod_timer(&tcpack_sup_mod->tcpack_info_tbl[free_slot].timer, ++ jiffies + msecs_to_jiffies(dhdp->tcpack_sup_delay)); ++ tcpack_sup_mod->tcpack_info_cnt++; ++ } else { ++ DHD_TRACE(("%s %d: No empty tcp ack info tbl\n", ++ __FUNCTION__, __LINE__)); ++ } ++ dhd_os_tcpackunlock(dhdp); ++ ++exit: ++ return hold; ++} + #endif /* DHDTCPACK_SUPPRESS */ +diff -Nur a/drivers/net/wireless/bcmdhd/dhd_ip.h c/drivers/net/wireless/bcmdhd/dhd_ip.h +--- a/drivers/net/wireless/bcmdhd/dhd_ip.h 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/dhd_ip.h 2016-05-13 09:48:20.000000000 +0200 +@@ -5,7 +5,7 @@ + * + * $Copyright Open Broadcom Corporation$ + * +- * $Id: dhd_ip.h 458522 2014-02-27 02:26:15Z $ ++ * $Id: dhd_ip.h 502735 2014-09-16 00:53:02Z $ + */ + + #ifndef _dhd_ip_h_ +@@ -26,6 +26,7 @@ + } pkt_frag_t; + + extern pkt_frag_t pkt_frag_info(osl_t *osh, void *p); ++extern bool pkt_is_dhcp(osl_t *osh, void *p); + + #ifdef DHDTCPACK_SUPPRESS + #define TCPACKSZMIN (ETHER_HDR_LEN + IPV4_MIN_HEADER_LEN + TCP_MIN_HEADER_LEN) +@@ -39,12 +40,15 @@ + + #define TCPDATA_INFO_TIMEOUT 5000 /* Remove tcpdata_info if inactive for this time (in ms) */ + ++#define TCPACK_SUPP_RATIO 3 ++#define TCPACK_DELAY_TIME 10 /* ms */ ++ + extern int dhd_tcpack_suppress_set(dhd_pub_t *dhdp, uint8 on); + extern void dhd_tcpack_info_tbl_clean(dhd_pub_t *dhdp); + extern int dhd_tcpack_check_xmit(dhd_pub_t *dhdp, void *pkt); + extern bool dhd_tcpack_suppress(dhd_pub_t *dhdp, void *pkt); + extern bool dhd_tcpdata_info_get(dhd_pub_t *dhdp, void *pkt); +- ++extern bool dhd_tcpack_hold(dhd_pub_t *dhdp, void *pkt, int ifidx); + /* #define DHDTCPACK_SUP_DBG */ + #if defined(DEBUG_COUNTER) && defined(DHDTCPACK_SUP_DBG) + extern counter_tbl_t tack_tbl; +diff -Nur a/drivers/net/wireless/bcmdhd/dhd_linux.c c/drivers/net/wireless/bcmdhd/dhd_linux.c +--- a/drivers/net/wireless/bcmdhd/dhd_linux.c 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/dhd_linux.c 2016-09-30 00:05:56.001097833 +0200 +@@ -1,9302 +1,10391 @@ +-/* +- * Broadcom Dongle Host Driver (DHD), Linux-specific network interface +- * Basically selected code segments from usb-cdc.c and usb-rndis.c +- * +- * $Copyright Open Broadcom Corporation$ +- * +- * $Id: dhd_linux.c 491481 2014-07-16 14:08:43Z $ +- */ +- +-#include +-#include +-#include +-#ifdef SHOW_LOGTRACE +-#include +-#include +-#endif /* SHOW_LOGTRACE */ +- +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#ifdef ENABLE_ADAPTIVE_SCHED +-#include +-#endif /* ENABLE_ADAPTIVE_SCHED */ +- +-#include +-#include +- +-#include +-#include +-#include +-#include +- +-#include +-#include +-#include +-#include +-#include +-#ifdef DHD_L2_FILTER +-#include +-#endif +-#include +- +-#include +-#include +-#include +-#include +-#ifdef PCIE_FULL_DONGLE +-#include +-#endif +-#include +-#include +-#include +-#include +-#ifdef CONFIG_HAS_WAKELOCK +-#include +-#endif +-#ifdef WL_CFG80211 +-#include +-#endif +-#ifdef PNO_SUPPORT +-#include +-#endif +-#ifdef WLBTAMP +-#include +-#include +-#include +-#endif +- +-#ifdef CONFIG_COMPAT +-#include +-#endif +- +-#ifdef DHD_WMF +-#include +-#endif /* DHD_WMF */ +- +-#ifdef AMPDU_VO_ENABLE +-#include +-#endif /* AMPDU_VO_ENABLE */ +-#ifdef DHDTCPACK_SUPPRESS +-#include +-#endif /* DHDTCPACK_SUPPRESS */ +- +-#if defined(DHD_TCP_WINSIZE_ADJUST) +-#include +-#include +-#endif /* DHD_TCP_WINSIZE_ADJUST */ +- +-#ifdef WLMEDIA_HTSF +-#include +-#include +- +-#define HTSF_MINLEN 200 /* min. packet length to timestamp */ +-#define HTSF_BUS_DELAY 150 /* assume a fix propagation in us */ +-#define TSMAX 1000 /* max no. of timing record kept */ +-#define NUMBIN 34 +- +-static uint32 tsidx = 0; +-static uint32 htsf_seqnum = 0; +-uint32 tsfsync; +-struct timeval tsync; +-static uint32 tsport = 5010; +- +-typedef struct histo_ { +- uint32 bin[NUMBIN]; +-} histo_t; +- +-#if !ISPOWEROF2(DHD_SDALIGN) +-#error DHD_SDALIGN is not a power of 2! +-#endif +- +-static histo_t vi_d1, vi_d2, vi_d3, vi_d4; +-#endif /* WLMEDIA_HTSF */ +- +-#if defined(DHD_TCP_WINSIZE_ADJUST) +-#define MIN_TCP_WIN_SIZE 18000 +-#define WIN_SIZE_SCALE_FACTOR 2 +-#define MAX_TARGET_PORTS 5 +- +-static uint target_ports[MAX_TARGET_PORTS] = {20, 0, 0, 0, 0}; +-static uint dhd_use_tcp_window_size_adjust = FALSE; +-static void dhd_adjust_tcp_winsize(int op_mode, struct sk_buff *skb); +-#endif /* DHD_TCP_WINSIZE_ADJUST */ +- +- +-#if defined(SOFTAP) +-extern bool ap_cfg_running; +-extern bool ap_fw_loaded; +-#endif +- +- +-#ifdef ENABLE_ADAPTIVE_SCHED +-#define DEFAULT_CPUFREQ_THRESH 1000000 /* threshold frequency : 1000000 = 1GHz */ +-#ifndef CUSTOM_CPUFREQ_THRESH +-#define CUSTOM_CPUFREQ_THRESH DEFAULT_CPUFREQ_THRESH +-#endif /* CUSTOM_CPUFREQ_THRESH */ +-#endif /* ENABLE_ADAPTIVE_SCHED */ +- +-/* enable HOSTIP cache update from the host side when an eth0:N is up */ +-#define AOE_IP_ALIAS_SUPPORT 1 +- +-#ifdef BCM_FD_AGGR +-#include +-#include +-#endif +-#ifdef PROP_TXSTATUS +-#include +-#include +-#endif +- +-#include +- +-/* Maximum STA per radio */ +-#define DHD_MAX_STA 32 +- +- +-const uint8 wme_fifo2ac[] = { 0, 1, 2, 3, 1, 1 }; +-const uint8 prio2fifo[8] = { 1, 0, 0, 1, 2, 2, 3, 3 }; +-#define WME_PRIO2AC(prio) wme_fifo2ac[prio2fifo[(prio)]] +- +-#ifdef ARP_OFFLOAD_SUPPORT +-void aoe_update_host_ipv4_table(dhd_pub_t *dhd_pub, u32 ipa, bool add, int idx); +-static int dhd_inetaddr_notifier_call(struct notifier_block *this, +- unsigned long event, void *ptr); +-static struct notifier_block dhd_inetaddr_notifier = { +- .notifier_call = dhd_inetaddr_notifier_call +-}; +-/* to make sure we won't register the same notifier twice, otherwise a loop is likely to be +- * created in kernel notifier link list (with 'next' pointing to itself) +- */ +-static bool dhd_inetaddr_notifier_registered = FALSE; +-#endif /* ARP_OFFLOAD_SUPPORT */ +- +-#ifdef CONFIG_IPV6 +-static int dhd_inet6addr_notifier_call(struct notifier_block *this, +- unsigned long event, void *ptr); +-static struct notifier_block dhd_inet6addr_notifier = { +- .notifier_call = dhd_inet6addr_notifier_call +-}; +-/* to make sure we won't register the same notifier twice, otherwise a loop is likely to be +- * created in kernel notifier link list (with 'next' pointing to itself) +- */ +-static bool dhd_inet6addr_notifier_registered = FALSE; +-#endif +- +-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) +-#include +-volatile bool dhd_mmc_suspend = FALSE; +-DECLARE_WAIT_QUEUE_HEAD(dhd_dpc_wait); +-#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) */ +- +-#if defined(OOB_INTR_ONLY) +-extern void dhd_enable_oob_intr(struct dhd_bus *bus, bool enable); +-#endif +-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && (1) +-static void dhd_hang_process(void *dhd_info, void *event_data, u8 event); +-#endif +-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) +-MODULE_LICENSE("GPL v2"); +-#endif /* LinuxVer */ +- +-#include +- +-#ifdef BCM_FD_AGGR +-#define DBUS_RX_BUFFER_SIZE_DHD(net) (BCM_RPC_TP_DNGL_AGG_MAX_BYTE) +-#else +-#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 +-#endif /* BCM_FD_AGGR */ +- +-#ifdef PROP_TXSTATUS +-extern bool dhd_wlfc_skip_fc(void); +-extern void dhd_wlfc_plat_init(void *dhd); +-extern void dhd_wlfc_plat_deinit(void *dhd); +-#endif /* PROP_TXSTATUS */ +- +-#if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 15) +-const char * +-print_tainted() +-{ +- return ""; +-} +-#endif /* LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 15) */ +- +-/* Linux wireless extension support */ +-#if defined(WL_WIRELESS_EXT) +-#include +-extern wl_iw_extra_params_t g_wl_iw_params; +-#endif /* defined(WL_WIRELESS_EXT) */ +- +-#if defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND) +-#include +-#endif /* defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND) */ +- +-extern int dhd_get_suspend_bcn_li_dtim(dhd_pub_t *dhd); +- +-#ifdef PKT_FILTER_SUPPORT +-extern void dhd_pktfilter_offload_set(dhd_pub_t * dhd, char *arg); +-extern void dhd_pktfilter_offload_enable(dhd_pub_t * dhd, char *arg, int enable, int master_mode); +-extern void dhd_pktfilter_offload_delete(dhd_pub_t *dhd, int id); +-#endif +- +- +-#ifdef READ_MACADDR +-extern int dhd_read_macaddr(struct dhd_info *dhd); +-#else +-static inline int dhd_read_macaddr(struct dhd_info *dhd) { return 0; } +-#endif +-#ifdef WRITE_MACADDR +-extern int dhd_write_macaddr(struct ether_addr *mac); +-#else +-static inline int dhd_write_macaddr(struct ether_addr *mac) { return 0; } +-#endif +- +-#if defined(SOFTAP_TPUT_ENHANCE) +-extern void dhd_bus_setidletime(dhd_pub_t *dhdp, int idle_time); +-extern void dhd_bus_getidletime(dhd_pub_t *dhdp, int* idle_time); +-#endif /* SOFTAP_TPUT_ENHANCE */ +- +- +-static int dhd_reboot_callback(struct notifier_block *this, unsigned long code, void *unused); +-static struct notifier_block dhd_reboot_notifier = { +- .notifier_call = dhd_reboot_callback, +- .priority = 1, +-}; +- +- +-typedef struct dhd_if_event { +- struct list_head list; +- wl_event_data_if_t event; +- char name[IFNAMSIZ+1]; +- uint8 mac[ETHER_ADDR_LEN]; +-} dhd_if_event_t; +- +-/* Interface control information */ +-typedef struct dhd_if { +- struct dhd_info *info; /* back pointer to dhd_info */ +- /* OS/stack specifics */ +- struct net_device *net; +- int idx; /* iface idx in dongle */ +- uint subunit; /* subunit */ +- uint8 mac_addr[ETHER_ADDR_LEN]; /* assigned MAC address */ +- bool set_macaddress; +- bool set_multicast; +- uint8 bssidx; /* bsscfg index for the interface */ +- bool attached; /* Delayed attachment when unset */ +- bool txflowcontrol; /* Per interface flow control indicator */ +- char name[IFNAMSIZ+1]; /* linux interface name */ +- struct net_device_stats stats; +-#ifdef DHD_WMF +- dhd_wmf_t wmf; /* per bsscfg wmf setting */ +-#endif /* DHD_WMF */ +-#ifdef PCIE_FULL_DONGLE +- struct list_head sta_list; /* sll of associated stations */ +-#if !defined(BCM_GMAC3) +- spinlock_t sta_list_lock; /* lock for manipulating sll */ +-#endif /* ! BCM_GMAC3 */ +-#endif /* PCIE_FULL_DONGLE */ +- uint32 ap_isolate; /* ap-isolation settings */ +-} dhd_if_t; +- +-#ifdef WLMEDIA_HTSF +-typedef struct { +- uint32 low; +- uint32 high; +-} tsf_t; +- +-typedef struct { +- uint32 last_cycle; +- uint32 last_sec; +- uint32 last_tsf; +- uint32 coef; /* scaling factor */ +- uint32 coefdec1; /* first decimal */ +- uint32 coefdec2; /* second decimal */ +-} htsf_t; +- +-typedef struct { +- uint32 t1; +- uint32 t2; +- uint32 t3; +- uint32 t4; +-} tstamp_t; +- +-static tstamp_t ts[TSMAX]; +-static tstamp_t maxdelayts; +-static uint32 maxdelay = 0, tspktcnt = 0, maxdelaypktno = 0; +- +-#endif /* WLMEDIA_HTSF */ +- +-struct ipv6_work_info_t { +- uint8 if_idx; +- char ipv6_addr[16]; +- unsigned long event; +-}; +- +-/* When Perimeter locks are deployed, any blocking calls must be preceeded +- * with a PERIM UNLOCK and followed by a PERIM LOCK. +- * Examples of blocking calls are: schedule_timeout(), down_interruptible(), +- * wait_event_timeout(). +- */ +- +-/* Local private structure (extension of pub) */ +-typedef struct dhd_info { +-#if defined(WL_WIRELESS_EXT) +- wl_iw_t iw; /* wireless extensions state (must be first) */ +-#endif /* defined(WL_WIRELESS_EXT) */ +- dhd_pub_t pub; +- dhd_if_t *iflist[DHD_MAX_IFS]; /* for supporting multiple interfaces */ +- +- void *adapter; /* adapter information, interrupt, fw path etc. */ +- char fw_path[PATH_MAX]; /* path to firmware image */ +- char nv_path[PATH_MAX]; /* path to nvram vars file */ +- char conf_path[PATH_MAX]; /* path to config vars file */ +- +- struct semaphore proto_sem; +-#ifdef PROP_TXSTATUS +- spinlock_t wlfc_spinlock; +- +-#endif /* PROP_TXSTATUS */ +-#ifdef WLMEDIA_HTSF +- htsf_t htsf; +-#endif +- wait_queue_head_t ioctl_resp_wait; +- uint32 default_wd_interval; +- +- struct timer_list timer; +- bool wd_timer_valid; +- struct tasklet_struct tasklet; +- spinlock_t sdlock; +- spinlock_t txqlock; +- spinlock_t dhd_lock; +- +- struct semaphore sdsem; +- tsk_ctl_t thr_dpc_ctl; +- tsk_ctl_t thr_wdt_ctl; +- +- tsk_ctl_t thr_rxf_ctl; +- spinlock_t rxf_lock; +- bool rxthread_enabled; +- +- /* Wakelocks */ +-#if defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) +- struct wake_lock wl_wifi; /* Wifi wakelock */ +- struct wake_lock wl_rxwake; /* Wifi rx wakelock */ +- struct wake_lock wl_ctrlwake; /* Wifi ctrl wakelock */ +- struct wake_lock wl_wdwake; /* Wifi wd wakelock */ +-#endif +- +-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1 +- /* net_device interface lock, prevent race conditions among net_dev interface +- * calls and wifi_on or wifi_off +- */ +- struct mutex dhd_net_if_mutex; +- struct mutex dhd_suspend_mutex; +-#endif +- spinlock_t wakelock_spinlock; +- uint32 wakelock_counter; +- int wakelock_wd_counter; +- int wakelock_rx_timeout_enable; +- int wakelock_ctrl_timeout_enable; +- bool waive_wakelock; +- uint32 wakelock_before_waive; +- +- /* Thread to issue ioctl for multicast */ +- wait_queue_head_t ctrl_wait; +- atomic_t pend_8021x_cnt; +- dhd_attach_states_t dhd_state; +-#ifdef SHOW_LOGTRACE +- dhd_event_log_t event_data; +-#endif /* SHOW_LOGTRACE */ +- +-#if defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND) +- struct early_suspend early_suspend; +-#endif /* CONFIG_HAS_EARLYSUSPEND && DHD_USE_EARLYSUSPEND */ +- +-#ifdef ARP_OFFLOAD_SUPPORT +- u32 pend_ipaddr; +-#endif /* ARP_OFFLOAD_SUPPORT */ +-#ifdef BCM_FD_AGGR +- void *rpc_th; +- void *rpc_osh; +- struct timer_list rpcth_timer; +- bool rpcth_timer_active; +- bool fdaggr; +-#endif +-#ifdef DHDTCPACK_SUPPRESS +- spinlock_t tcpack_lock; +-#endif /* DHDTCPACK_SUPPRESS */ +- void *dhd_deferred_wq; +-#ifdef DEBUG_CPU_FREQ +- struct notifier_block freq_trans; +- int __percpu *new_freq; +-#endif +- unsigned int unit; +- struct notifier_block pm_notifier; +-} dhd_info_t; +- +-#define DHDIF_FWDER(dhdif) FALSE +- +-/* Flag to indicate if we should download firmware on driver load */ +-uint dhd_download_fw_on_driverload = TRUE; +- +-/* Definitions to provide path to the firmware and nvram +- * example nvram_path[MOD_PARAM_PATHLEN]="/projects/wlan/nvram.txt" +- */ +-char firmware_path[MOD_PARAM_PATHLEN]; +-char nvram_path[MOD_PARAM_PATHLEN]; +-char config_path[MOD_PARAM_PATHLEN]; +- +-/* backup buffer for firmware and nvram path */ +-char fw_bak_path[MOD_PARAM_PATHLEN]; +-char nv_bak_path[MOD_PARAM_PATHLEN]; +- +-/* information string to keep firmware, chio, cheip version info visiable from log */ +-char info_string[MOD_PARAM_INFOLEN]; +-module_param_string(info_string, info_string, MOD_PARAM_INFOLEN, 0444); +-int op_mode = 0; +-int disable_proptx = 0; +-module_param(op_mode, int, 0644); +-extern int wl_control_wl_start(struct net_device *dev); +-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(BCMLXSDMMC) +-struct semaphore dhd_registration_sem; +-#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ +- +-/* deferred handlers */ +-static void dhd_ifadd_event_handler(void *handle, void *event_info, u8 event); +-static void dhd_ifdel_event_handler(void *handle, void *event_info, u8 event); +-static void dhd_set_mac_addr_handler(void *handle, void *event_info, u8 event); +-static void dhd_set_mcast_list_handler(void *handle, void *event_info, u8 event); +-#ifdef CONFIG_IPV6 +-static void dhd_inet6_work_handler(void *dhd_info, void *event_data, u8 event); +-#endif +- +-#ifdef WL_CFG80211 +-extern void dhd_netdev_free(struct net_device *ndev); +-#endif /* WL_CFG80211 */ +- +-/* Error bits */ +-module_param(dhd_msg_level, int, 0); +-#if defined(WL_WIRELESS_EXT) +-module_param(iw_msg_level, int, 0); +-#endif +-#ifdef WL_CFG80211 +-module_param(wl_dbg_level, int, 0); +-#endif +-module_param(android_msg_level, int, 0); +-module_param(config_msg_level, int, 0); +- +-#ifdef ARP_OFFLOAD_SUPPORT +-/* ARP offload enable */ +-uint dhd_arp_enable = TRUE; +-module_param(dhd_arp_enable, uint, 0); +- +-/* ARP offload agent mode : Enable ARP Host Auto-Reply and ARP Peer Auto-Reply */ +- +-uint dhd_arp_mode = ARP_OL_AGENT | ARP_OL_PEER_AUTO_REPLY; +- +-module_param(dhd_arp_mode, uint, 0); +-#endif /* ARP_OFFLOAD_SUPPORT */ +- +-/* Disable Prop tx */ +-module_param(disable_proptx, int, 0644); +-/* load firmware and/or nvram values from the filesystem */ +-module_param_string(firmware_path, firmware_path, MOD_PARAM_PATHLEN, 0660); +-module_param_string(nvram_path, nvram_path, MOD_PARAM_PATHLEN, 0660); +-module_param_string(config_path, config_path, MOD_PARAM_PATHLEN, 0); +- +-/* Watchdog interval */ +- +-/* extend watchdog expiration to 2 seconds when DPC is running */ +-#define WATCHDOG_EXTEND_INTERVAL (2000) +- +-uint dhd_watchdog_ms = CUSTOM_DHD_WATCHDOG_MS; +-module_param(dhd_watchdog_ms, uint, 0); +- +-#if defined(DHD_DEBUG) +-/* Console poll interval */ +-uint dhd_console_ms = 0; +-module_param(dhd_console_ms, uint, 0644); +-#endif /* defined(DHD_DEBUG) */ +- +- +-uint dhd_slpauto = TRUE; +-module_param(dhd_slpauto, uint, 0); +- +-#ifdef PKT_FILTER_SUPPORT +-/* Global Pkt filter enable control */ +-uint dhd_pkt_filter_enable = TRUE; +-module_param(dhd_pkt_filter_enable, uint, 0); +-#endif +- +-/* Pkt filter init setup */ +-uint dhd_pkt_filter_init = 0; +-module_param(dhd_pkt_filter_init, uint, 0); +- +-/* Pkt filter mode control */ +-uint dhd_master_mode = FALSE; +-module_param(dhd_master_mode, uint, 0); +- +-int dhd_watchdog_prio = 0; +-module_param(dhd_watchdog_prio, int, 0); +- +-/* DPC thread priority */ +-int dhd_dpc_prio = CUSTOM_DPC_PRIO_SETTING; +-module_param(dhd_dpc_prio, int, 0); +- +-/* RX frame thread priority */ +-int dhd_rxf_prio = CUSTOM_RXF_PRIO_SETTING; +-module_param(dhd_rxf_prio, int, 0); +- +-#if !defined(BCMDHDUSB) +-extern int dhd_dongle_ramsize; +-module_param(dhd_dongle_ramsize, int, 0); +-#endif /* BCMDHDUSB */ +- +-/* Keep track of number of instances */ +-static int dhd_found = 0; +-static int instance_base = 0; /* Starting instance number */ +-module_param(instance_base, int, 0644); +- +- +-/* DHD Perimiter lock only used in router with bypass forwarding. */ +-#define DHD_PERIM_RADIO_INIT() do { /* noop */ } while (0) +-#define DHD_PERIM_LOCK_TRY(unit, flag) do { /* noop */ } while (0) +-#define DHD_PERIM_UNLOCK_TRY(unit, flag) do { /* noop */ } while (0) +-#define DHD_PERIM_LOCK_ALL() do { /* noop */ } while (0) +-#define DHD_PERIM_UNLOCK_ALL() do { /* noop */ } while (0) +- +-#ifdef PCIE_FULL_DONGLE +-#if defined(BCM_GMAC3) +-#define DHD_IF_STA_LIST_LOCK_INIT(ifp) do { /* noop */ } while (0) +-#define DHD_IF_STA_LIST_LOCK(ifp, flags) ({ BCM_REFERENCE(flags); }) +-#define DHD_IF_STA_LIST_UNLOCK(ifp, flags) ({ BCM_REFERENCE(flags); }) +-#else /* ! BCM_GMAC3 */ +-#define DHD_IF_STA_LIST_LOCK_INIT(ifp) spin_lock_init(&(ifp)->sta_list_lock) +-#define DHD_IF_STA_LIST_LOCK(ifp, flags) \ +- spin_lock_irqsave(&(ifp)->sta_list_lock, (flags)) +-#define DHD_IF_STA_LIST_UNLOCK(ifp, flags) \ +- spin_unlock_irqrestore(&(ifp)->sta_list_lock, (flags)) +-#endif /* ! BCM_GMAC3 */ +-#endif /* PCIE_FULL_DONGLE */ +- +-/* Control fw roaming */ +-#ifdef BCMCCX +-uint dhd_roam_disable = 0; +-#else +-uint dhd_roam_disable = 0; +-#endif /* BCMCCX */ +- +-/* Control radio state */ +-uint dhd_radio_up = 1; +- +-/* Network inteface name */ +-char iface_name[IFNAMSIZ] = {'\0'}; +-module_param_string(iface_name, iface_name, IFNAMSIZ, 0); +- +-/* The following are specific to the SDIO dongle */ +- +-/* IOCTL response timeout */ +-int dhd_ioctl_timeout_msec = IOCTL_RESP_TIMEOUT; +- +-/* Idle timeout for backplane clock */ +-int dhd_idletime = DHD_IDLETIME_TICKS; +-module_param(dhd_idletime, int, 0); +- +-/* Use polling */ +-uint dhd_poll = FALSE; +-module_param(dhd_poll, uint, 0); +- +-/* Use interrupts */ +-uint dhd_intr = TRUE; +-module_param(dhd_intr, uint, 0); +- +-/* SDIO Drive Strength (in milliamps) */ +-uint dhd_sdiod_drive_strength = 6; +-module_param(dhd_sdiod_drive_strength, uint, 0); +- +-#ifdef BCMSDIO +-/* Tx/Rx bounds */ +-extern uint dhd_txbound; +-extern uint dhd_rxbound; +-module_param(dhd_txbound, uint, 0); +-module_param(dhd_rxbound, uint, 0); +- +-/* Deferred transmits */ +-extern uint dhd_deferred_tx; +-module_param(dhd_deferred_tx, uint, 0); +- +-#ifdef BCMDBGFS +-extern void dhd_dbg_init(dhd_pub_t *dhdp); +-extern void dhd_dbg_remove(void); +-#endif /* BCMDBGFS */ +- +-#endif /* BCMSDIO */ +- +- +-#ifdef SDTEST +-/* Echo packet generator (pkts/s) */ +-uint dhd_pktgen = 0; +-module_param(dhd_pktgen, uint, 0); +- +-/* Echo packet len (0 => sawtooth, max 2040) */ +-uint dhd_pktgen_len = 0; +-module_param(dhd_pktgen_len, uint, 0); +-#endif /* SDTEST */ +- +-#if defined(BCMSUP_4WAY_HANDSHAKE) +-/* Use in dongle supplicant for 4-way handshake */ +-uint dhd_use_idsup = 0; +-module_param(dhd_use_idsup, uint, 0); +-#endif /* BCMSUP_4WAY_HANDSHAKE */ +- +-extern char dhd_version[]; +- +-int dhd_net_bus_devreset(struct net_device *dev, uint8 flag); +-static void dhd_net_if_lock_local(dhd_info_t *dhd); +-static void dhd_net_if_unlock_local(dhd_info_t *dhd); +-static void dhd_suspend_lock(dhd_pub_t *dhdp); +-static void dhd_suspend_unlock(dhd_pub_t *dhdp); +- +-#ifdef WLMEDIA_HTSF +-void htsf_update(dhd_info_t *dhd, void *data); +-tsf_t prev_tsf, cur_tsf; +- +-uint32 dhd_get_htsf(dhd_info_t *dhd, int ifidx); +-static int dhd_ioctl_htsf_get(dhd_info_t *dhd, int ifidx); +-static void dhd_dump_latency(void); +-static void dhd_htsf_addtxts(dhd_pub_t *dhdp, void *pktbuf); +-static void dhd_htsf_addrxts(dhd_pub_t *dhdp, void *pktbuf); +-static void dhd_dump_htsfhisto(histo_t *his, char *s); +-#endif /* WLMEDIA_HTSF */ +- +-/* Monitor interface */ +-int dhd_monitor_init(void *dhd_pub); +-int dhd_monitor_uninit(void); +- +- +-#if defined(WL_WIRELESS_EXT) +-struct iw_statistics *dhd_get_wireless_stats(struct net_device *dev); +-#endif /* defined(WL_WIRELESS_EXT) */ +- +-static void dhd_dpc(ulong data); +-/* forward decl */ +-extern int dhd_wait_pend8021x(struct net_device *dev); +-void dhd_os_wd_timer_extend(void *bus, bool extend); +- +-#ifdef TOE +-#ifndef BDC +-#error TOE requires BDC +-#endif /* !BDC */ +-static int dhd_toe_get(dhd_info_t *dhd, int idx, uint32 *toe_ol); +-static int dhd_toe_set(dhd_info_t *dhd, int idx, uint32 toe_ol); +-#endif /* TOE */ +- +-static int dhd_wl_host_event(dhd_info_t *dhd, int *ifidx, void *pktdata, +- wl_event_msg_t *event_ptr, void **data_ptr); +-#ifdef DHD_UNICAST_DHCP +-static const uint8 llc_snap_hdr[SNAP_HDR_LEN] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00}; +-static int dhd_get_pkt_ip_type(dhd_pub_t *dhd, void *skb, uint8 **data_ptr, +- int *len_ptr, uint8 *prot_ptr); +-static int dhd_get_pkt_ether_type(dhd_pub_t *dhd, void *skb, uint8 **data_ptr, +- int *len_ptr, uint16 *et_ptr, bool *snap_ptr); +- +-static int dhd_convert_dhcp_broadcast_ack_to_unicast(dhd_pub_t *pub, void *pktbuf, int ifidx); +-#endif /* DHD_UNICAST_DHCP */ +-#ifdef DHD_L2_FILTER +-static int dhd_l2_filter_block_ping(dhd_pub_t *pub, void *pktbuf, int ifidx); +-#endif +-#if defined(CONFIG_PM_SLEEP) +-static int dhd_pm_callback(struct notifier_block *nfb, unsigned long action, void *ignored) +-{ +- int ret = NOTIFY_DONE; +- bool suspend = FALSE; +- dhd_info_t *dhdinfo = (dhd_info_t*)container_of(nfb, struct dhd_info, pm_notifier); +- +- BCM_REFERENCE(dhdinfo); +- switch (action) { +- case PM_HIBERNATION_PREPARE: +- case PM_SUSPEND_PREPARE: +- suspend = TRUE; +- break; +- case PM_POST_HIBERNATION: +- case PM_POST_SUSPEND: +- suspend = FALSE; +- break; +- } +- +-#if defined(SUPPORT_P2P_GO_PS) +-#ifdef PROP_TXSTATUS +- if (suspend) { +- DHD_OS_WAKE_LOCK_WAIVE(&dhdinfo->pub); +- dhd_wlfc_suspend(&dhdinfo->pub); +- DHD_OS_WAKE_LOCK_RESTORE(&dhdinfo->pub); +- } else +- dhd_wlfc_resume(&dhdinfo->pub); +-#endif +-#endif /* defined(SUPPORT_P2P_GO_PS) */ +- +-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && (LINUX_VERSION_CODE <= \ +- KERNEL_VERSION(2, 6, 39)) +- dhd_mmc_suspend = suspend; +- smp_mb(); +-#endif +- +- return ret; +-} +- +-static struct notifier_block dhd_pm_notifier = { +- .notifier_call = dhd_pm_callback, +- .priority = 10 +-}; +-/* to make sure we won't register the same notifier twice, otherwise a loop is likely to be +- * created in kernel notifier link list (with 'next' pointing to itself) +- */ +-static bool dhd_pm_notifier_registered = FALSE; +- +-extern int register_pm_notifier(struct notifier_block *nb); +-extern int unregister_pm_notifier(struct notifier_block *nb); +-#endif /* CONFIG_PM_SLEEP */ +- +-/* Request scheduling of the bus rx frame */ +-static void dhd_sched_rxf(dhd_pub_t *dhdp, void *skb); +-static void dhd_os_rxflock(dhd_pub_t *pub); +-static void dhd_os_rxfunlock(dhd_pub_t *pub); +- +-/** priv_link is the link between netdev and the dhdif and dhd_info structs. */ +-typedef struct dhd_dev_priv { +- dhd_info_t * dhd; /* cached pointer to dhd_info in netdevice priv */ +- dhd_if_t * ifp; /* cached pointer to dhd_if in netdevice priv */ +- int ifidx; /* interface index */ +-} dhd_dev_priv_t; +- +-#define DHD_DEV_PRIV_SIZE (sizeof(dhd_dev_priv_t)) +-#define DHD_DEV_PRIV(dev) ((dhd_dev_priv_t *)DEV_PRIV(dev)) +-#define DHD_DEV_INFO(dev) (((dhd_dev_priv_t *)DEV_PRIV(dev))->dhd) +-#define DHD_DEV_IFP(dev) (((dhd_dev_priv_t *)DEV_PRIV(dev))->ifp) +-#define DHD_DEV_IFIDX(dev) (((dhd_dev_priv_t *)DEV_PRIV(dev))->ifidx) +- +-/** Clear the dhd net_device's private structure. */ +-static inline void +-dhd_dev_priv_clear(struct net_device * dev) +-{ +- dhd_dev_priv_t * dev_priv; +- ASSERT(dev != (struct net_device *)NULL); +- dev_priv = DHD_DEV_PRIV(dev); +- dev_priv->dhd = (dhd_info_t *)NULL; +- dev_priv->ifp = (dhd_if_t *)NULL; +- dev_priv->ifidx = DHD_BAD_IF; +-} +- +-/** Setup the dhd net_device's private structure. */ +-static inline void +-dhd_dev_priv_save(struct net_device * dev, dhd_info_t * dhd, dhd_if_t * ifp, +- int ifidx) +-{ +- dhd_dev_priv_t * dev_priv; +- ASSERT(dev != (struct net_device *)NULL); +- dev_priv = DHD_DEV_PRIV(dev); +- dev_priv->dhd = dhd; +- dev_priv->ifp = ifp; +- dev_priv->ifidx = ifidx; +-} +- +-#ifdef PCIE_FULL_DONGLE +- +-/** Dummy objects are defined with state representing bad|down. +- * Performance gains from reducing branch conditionals, instruction parallelism, +- * dual issue, reducing load shadows, avail of larger pipelines. +- * Use DHD_XXX_NULL instead of (dhd_xxx_t *)NULL, whenever an object pointer +- * is accessed via the dhd_sta_t. +- */ +- +-/* Dummy dhd_info object */ +-dhd_info_t dhd_info_null = { +-#if defined(BCM_GMAC3) +- .fwdh = FWDER_NULL, +-#endif +- .pub = { +- .info = &dhd_info_null, +-#ifdef DHDTCPACK_SUPPRESS +- .tcpack_sup_mode = TCPACK_SUP_REPLACE, +-#endif /* DHDTCPACK_SUPPRESS */ +- .up = FALSE, .busstate = DHD_BUS_DOWN +- } +-}; +-#define DHD_INFO_NULL (&dhd_info_null) +-#define DHD_PUB_NULL (&dhd_info_null.pub) +- +-/* Dummy netdevice object */ +-struct net_device dhd_net_dev_null = { +- .reg_state = NETREG_UNREGISTERED +-}; +-#define DHD_NET_DEV_NULL (&dhd_net_dev_null) +- +-/* Dummy dhd_if object */ +-dhd_if_t dhd_if_null = { +-#if defined(BCM_GMAC3) +- .fwdh = FWDER_NULL, +-#endif +-#ifdef WMF +- .wmf = { .wmf_enable = TRUE }, +-#endif +- .info = DHD_INFO_NULL, +- .net = DHD_NET_DEV_NULL, +- .idx = DHD_BAD_IF +-}; +-#define DHD_IF_NULL (&dhd_if_null) +- +-#define DHD_STA_NULL ((dhd_sta_t *)NULL) +- +-/** Interface STA list management. */ +- +-/** Fetch the dhd_if object, given the interface index in the dhd. */ +-static inline dhd_if_t *dhd_get_ifp(dhd_pub_t *dhdp, uint32 ifidx); +- +-/** Alloc/Free a dhd_sta object from the dhd instances' sta_pool. */ +-static void dhd_sta_free(dhd_pub_t *pub, dhd_sta_t *sta); +-static dhd_sta_t * dhd_sta_alloc(dhd_pub_t * dhdp); +- +-/* Delete a dhd_sta or flush all dhd_sta in an interface's sta_list. */ +-static void dhd_if_del_sta_list(dhd_if_t * ifp); +-static void dhd_if_flush_sta(dhd_if_t * ifp); +- +-/* Construct/Destruct a sta pool. */ +-static int dhd_sta_pool_init(dhd_pub_t *dhdp, int max_sta); +-static void dhd_sta_pool_fini(dhd_pub_t *dhdp, int max_sta); +- +- +-/* Return interface pointer */ +-static inline dhd_if_t *dhd_get_ifp(dhd_pub_t *dhdp, uint32 ifidx) +-{ +- ASSERT(ifidx < DHD_MAX_IFS); +- return dhdp->info->iflist[ifidx]; +-} +- +-/** Reset a dhd_sta object and free into the dhd pool. */ +-static void +-dhd_sta_free(dhd_pub_t * dhdp, dhd_sta_t * sta) +-{ +- int prio; +- +- ASSERT((sta != DHD_STA_NULL) && (sta->idx != ID16_INVALID)); +- +- ASSERT((dhdp->staid_allocator != NULL) && (dhdp->sta_pool != NULL)); +- id16_map_free(dhdp->staid_allocator, sta->idx); +- for (prio = 0; prio < (int)NUMPRIO; prio++) +- sta->flowid[prio] = FLOWID_INVALID; +- sta->ifp = DHD_IF_NULL; /* dummy dhd_if object */ +- sta->ifidx = DHD_BAD_IF; +- bzero(sta->ea.octet, ETHER_ADDR_LEN); +- INIT_LIST_HEAD(&sta->list); +- sta->idx = ID16_INVALID; /* implying free */ +-} +- +-/** Allocate a dhd_sta object from the dhd pool. */ +-static dhd_sta_t * +-dhd_sta_alloc(dhd_pub_t * dhdp) +-{ +- uint16 idx; +- dhd_sta_t * sta; +- dhd_sta_pool_t * sta_pool; +- +- ASSERT((dhdp->staid_allocator != NULL) && (dhdp->sta_pool != NULL)); +- +- idx = id16_map_alloc(dhdp->staid_allocator); +- if (idx == ID16_INVALID) { +- DHD_ERROR(("%s: cannot get free staid\n", __FUNCTION__)); +- return DHD_STA_NULL; +- } +- +- sta_pool = (dhd_sta_pool_t *)(dhdp->sta_pool); +- sta = &sta_pool[idx]; +- +- ASSERT((sta->idx == ID16_INVALID) && +- (sta->ifp == DHD_IF_NULL) && (sta->ifidx == DHD_BAD_IF)); +- sta->idx = idx; /* implying allocated */ +- +- return sta; +-} +- +-/** Delete all STAs in an interface's STA list. */ +-static void +-dhd_if_del_sta_list(dhd_if_t *ifp) +-{ +- dhd_sta_t *sta, *next; +- unsigned long flags; +- +- DHD_IF_STA_LIST_LOCK(ifp, flags); +- +- list_for_each_entry_safe(sta, next, &ifp->sta_list, list) { +-#if defined(BCM_GMAC3) +- if (ifp->fwdh) { +- /* Remove sta from WOFA forwarder. */ +- fwder_deassoc(ifp->fwdh, (uint16 *)(sta->ea.octet), (wofa_t)sta); +- } +-#endif /* BCM_GMAC3 */ +- list_del(&sta->list); +- dhd_sta_free(&ifp->info->pub, sta); +- } +- +- DHD_IF_STA_LIST_UNLOCK(ifp, flags); +- +- return; +-} +- +-/** Router/GMAC3: Flush all station entries in the forwarder's WOFA database. */ +-static void +-dhd_if_flush_sta(dhd_if_t * ifp) +-{ +-#if defined(BCM_GMAC3) +- +- if (ifp && (ifp->fwdh != FWDER_NULL)) { +- dhd_sta_t *sta, *next; +- unsigned long flags; +- +- DHD_IF_STA_LIST_LOCK(ifp, flags); +- +- list_for_each_entry_safe(sta, next, &ifp->sta_list, list) { +- /* Remove any sta entry from WOFA forwarder. */ +- fwder_flush(ifp->fwdh, (wofa_t)sta); +- } +- +- DHD_IF_STA_LIST_UNLOCK(ifp, flags); +- } +-#endif /* BCM_GMAC3 */ +-} +- +-/** Construct a pool of dhd_sta_t objects to be used by interfaces. */ +-static int +-dhd_sta_pool_init(dhd_pub_t *dhdp, int max_sta) +-{ +- int idx, sta_pool_memsz; +- dhd_sta_t * sta; +- dhd_sta_pool_t * sta_pool; +- void * staid_allocator; +- +- ASSERT(dhdp != (dhd_pub_t *)NULL); +- ASSERT((dhdp->staid_allocator == NULL) && (dhdp->sta_pool == NULL)); +- +- /* dhd_sta objects per radio are managed in a table. id#0 reserved. */ +- staid_allocator = id16_map_init(dhdp->osh, max_sta, 1); +- if (staid_allocator == NULL) { +- DHD_ERROR(("%s: sta id allocator init failure\n", __FUNCTION__)); +- return BCME_ERROR; +- } +- +- /* Pre allocate a pool of dhd_sta objects (one extra). */ +- sta_pool_memsz = ((max_sta + 1) * sizeof(dhd_sta_t)); /* skip idx 0 */ +- sta_pool = (dhd_sta_pool_t *)MALLOC(dhdp->osh, sta_pool_memsz); +- if (sta_pool == NULL) { +- DHD_ERROR(("%s: sta table alloc failure\n", __FUNCTION__)); +- id16_map_fini(dhdp->osh, staid_allocator); +- return BCME_ERROR; +- } +- +- dhdp->sta_pool = sta_pool; +- dhdp->staid_allocator = staid_allocator; +- +- /* Initialize all sta(s) for the pre-allocated free pool. */ +- bzero((uchar *)sta_pool, sta_pool_memsz); +- for (idx = max_sta; idx >= 1; idx--) { /* skip sta_pool[0] */ +- sta = &sta_pool[idx]; +- sta->idx = id16_map_alloc(staid_allocator); +- ASSERT(sta->idx <= max_sta); +- } +- /* Now place them into the pre-allocated free pool. */ +- for (idx = 1; idx <= max_sta; idx++) { +- sta = &sta_pool[idx]; +- dhd_sta_free(dhdp, sta); +- } +- +- return BCME_OK; +-} +- +-/** Destruct the pool of dhd_sta_t objects. +- * Caller must ensure that no STA objects are currently associated with an if. +- */ +-static void +-dhd_sta_pool_fini(dhd_pub_t *dhdp, int max_sta) +-{ +- dhd_sta_pool_t * sta_pool = (dhd_sta_pool_t *)dhdp->sta_pool; +- +- if (sta_pool) { +- int idx; +- int sta_pool_memsz = ((max_sta + 1) * sizeof(dhd_sta_t)); +- for (idx = 1; idx <= max_sta; idx++) { +- ASSERT(sta_pool[idx].ifp == DHD_IF_NULL); +- ASSERT(sta_pool[idx].idx == ID16_INVALID); +- } +- MFREE(dhdp->osh, dhdp->sta_pool, sta_pool_memsz); +- dhdp->sta_pool = NULL; +- } +- +- id16_map_fini(dhdp->osh, dhdp->staid_allocator); +- dhdp->staid_allocator = NULL; +-} +- +-/** Find STA with MAC address ea in an interface's STA list. */ +-dhd_sta_t * +-dhd_find_sta(void *pub, int ifidx, void *ea) +-{ +- dhd_sta_t *sta; +- dhd_if_t *ifp; +- unsigned long flags; +- +- ASSERT(ea != NULL); +- ifp = dhd_get_ifp((dhd_pub_t *)pub, ifidx); +- +- DHD_IF_STA_LIST_LOCK(ifp, flags); +- +- list_for_each_entry(sta, &ifp->sta_list, list) { +- if (!memcmp(sta->ea.octet, ea, ETHER_ADDR_LEN)) { +- DHD_IF_STA_LIST_UNLOCK(ifp, flags); +- return sta; +- } +- } +- +- DHD_IF_STA_LIST_UNLOCK(ifp, flags); +- +- return DHD_STA_NULL; +-} +- +-/** Add STA into the interface's STA list. */ +-dhd_sta_t * +-dhd_add_sta(void *pub, int ifidx, void *ea) +-{ +- dhd_sta_t *sta; +- dhd_if_t *ifp; +- unsigned long flags; +- +- ASSERT(ea != NULL); +- ifp = dhd_get_ifp((dhd_pub_t *)pub, ifidx); +- +- sta = dhd_sta_alloc((dhd_pub_t *)pub); +- if (sta == DHD_STA_NULL) { +- DHD_ERROR(("%s: Alloc failed\n", __FUNCTION__)); +- return DHD_STA_NULL; +- } +- +- memcpy(sta->ea.octet, ea, ETHER_ADDR_LEN); +- +- /* link the sta and the dhd interface */ +- sta->ifp = ifp; +- sta->ifidx = ifidx; +- INIT_LIST_HEAD(&sta->list); +- +- DHD_IF_STA_LIST_LOCK(ifp, flags); +- +- list_add_tail(&sta->list, &ifp->sta_list); +- +-#if defined(BCM_GMAC3) +- if (ifp->fwdh) { +- ASSERT(ISALIGNED(ea, 2)); +- /* Add sta to WOFA forwarder. */ +- fwder_reassoc(ifp->fwdh, (uint16 *)ea, (wofa_t)sta); +- } +-#endif /* BCM_GMAC3 */ +- +- DHD_IF_STA_LIST_UNLOCK(ifp, flags); +- +- return sta; +-} +- +-/** Delete STA from the interface's STA list. */ +-void +-dhd_del_sta(void *pub, int ifidx, void *ea) +-{ +- dhd_sta_t *sta, *next; +- dhd_if_t *ifp; +- unsigned long flags; +- +- ASSERT(ea != NULL); +- ifp = dhd_get_ifp((dhd_pub_t *)pub, ifidx); +- +- DHD_IF_STA_LIST_LOCK(ifp, flags); +- +- list_for_each_entry_safe(sta, next, &ifp->sta_list, list) { +- if (!memcmp(sta->ea.octet, ea, ETHER_ADDR_LEN)) { +-#if defined(BCM_GMAC3) +- if (ifp->fwdh) { /* Found a sta, remove from WOFA forwarder. */ +- ASSERT(ISALIGNED(ea, 2)); +- fwder_deassoc(ifp->fwdh, (uint16 *)ea, (wofa_t)sta); +- } +-#endif /* BCM_GMAC3 */ +- list_del(&sta->list); +- dhd_sta_free(&ifp->info->pub, sta); +- } +- } +- +- DHD_IF_STA_LIST_UNLOCK(ifp, flags); +- +- return; +-} +- +-/** Add STA if it doesn't exist. Not reentrant. */ +-dhd_sta_t* +-dhd_findadd_sta(void *pub, int ifidx, void *ea) +-{ +- dhd_sta_t *sta; +- +- sta = dhd_find_sta(pub, ifidx, ea); +- +- if (!sta) { +- /* Add entry */ +- sta = dhd_add_sta(pub, ifidx, ea); +- } +- +- return sta; +-} +-#else +-static inline void dhd_if_flush_sta(dhd_if_t * ifp) { } +-static inline void dhd_if_del_sta_list(dhd_if_t *ifp) {} +-static inline int dhd_sta_pool_init(dhd_pub_t *dhdp, int max_sta) { return BCME_OK; } +-static inline void dhd_sta_pool_fini(dhd_pub_t *dhdp, int max_sta) {} +-dhd_sta_t *dhd_findadd_sta(void *pub, int ifidx, void *ea) { return NULL; } +-void dhd_del_sta(void *pub, int ifidx, void *ea) {} +-#endif /* PCIE_FULL_DONGLE */ +- +- +-/* Returns dhd iflist index correspondig the the bssidx provided by apps */ +-int dhd_bssidx2idx(dhd_pub_t *dhdp, uint32 bssidx) +-{ +- dhd_if_t *ifp; +- dhd_info_t *dhd = dhdp->info; +- int i; +- +- ASSERT(bssidx < DHD_MAX_IFS); +- ASSERT(dhdp); +- +- for (i = 0; i < DHD_MAX_IFS; i++) { +- ifp = dhd->iflist[i]; +- if (ifp && (ifp->bssidx == bssidx)) { +- DHD_TRACE(("Index manipulated for %s from %d to %d\n", +- ifp->name, bssidx, i)); +- break; +- } +- } +- return i; +-} +- +-static inline int dhd_rxf_enqueue(dhd_pub_t *dhdp, void* skb) +-{ +- uint32 store_idx; +- uint32 sent_idx; +- +- if (!skb) { +- DHD_ERROR(("dhd_rxf_enqueue: NULL skb!!!\n")); +- return BCME_ERROR; +- } +- +- dhd_os_rxflock(dhdp); +- store_idx = dhdp->store_idx; +- sent_idx = dhdp->sent_idx; +- if (dhdp->skbbuf[store_idx] != NULL) { +- /* Make sure the previous packets are processed */ +- dhd_os_rxfunlock(dhdp); +-#ifdef RXF_DEQUEUE_ON_BUSY +- DHD_TRACE(("dhd_rxf_enqueue: pktbuf not consumed %p, store idx %d sent idx %d\n", +- skb, store_idx, sent_idx)); +- return BCME_BUSY; +-#else /* RXF_DEQUEUE_ON_BUSY */ +- DHD_ERROR(("dhd_rxf_enqueue: pktbuf not consumed %p, store idx %d sent idx %d\n", +- skb, store_idx, sent_idx)); +- /* removed msleep here, should use wait_event_timeout if we +- * want to give rx frame thread a chance to run +- */ +-#if defined(WAIT_DEQUEUE) +- OSL_SLEEP(1); +-#endif +- return BCME_ERROR; +-#endif /* RXF_DEQUEUE_ON_BUSY */ +- } +- DHD_TRACE(("dhd_rxf_enqueue: Store SKB %p. idx %d -> %d\n", +- skb, store_idx, (store_idx + 1) & (MAXSKBPEND - 1))); +- dhdp->skbbuf[store_idx] = skb; +- dhdp->store_idx = (store_idx + 1) & (MAXSKBPEND - 1); +- dhd_os_rxfunlock(dhdp); +- +- return BCME_OK; +-} +- +-static inline void* dhd_rxf_dequeue(dhd_pub_t *dhdp) +-{ +- uint32 store_idx; +- uint32 sent_idx; +- void *skb; +- +- dhd_os_rxflock(dhdp); +- +- store_idx = dhdp->store_idx; +- sent_idx = dhdp->sent_idx; +- skb = dhdp->skbbuf[sent_idx]; +- +- if (skb == NULL) { +- dhd_os_rxfunlock(dhdp); +- DHD_ERROR(("dhd_rxf_dequeue: Dequeued packet is NULL, store idx %d sent idx %d\n", +- store_idx, sent_idx)); +- return NULL; +- } +- +- dhdp->skbbuf[sent_idx] = NULL; +- dhdp->sent_idx = (sent_idx + 1) & (MAXSKBPEND - 1); +- +- DHD_TRACE(("dhd_rxf_dequeue: netif_rx_ni(%p), sent idx %d\n", +- skb, sent_idx)); +- +- dhd_os_rxfunlock(dhdp); +- +- return skb; +-} +- +-int dhd_process_cid_mac(dhd_pub_t *dhdp, bool prepost) +-{ +-#ifndef CUSTOMER_HW10 +- dhd_info_t *dhd = (dhd_info_t *)dhdp->info; +-#endif /* !CUSTOMER_HW10 */ +- +- if (prepost) { /* pre process */ +- dhd_read_macaddr(dhd); +- } else { /* post process */ +- dhd_write_macaddr(&dhd->pub.mac); +- } +- +- return 0; +-} +- +-#if defined(PKT_FILTER_SUPPORT) && !defined(GAN_LITE_NAT_KEEPALIVE_FILTER) +-static bool +-_turn_on_arp_filter(dhd_pub_t *dhd, int op_mode) +-{ +- bool _apply = FALSE; +- /* In case of IBSS mode, apply arp pkt filter */ +- if (op_mode & DHD_FLAG_IBSS_MODE) { +- _apply = TRUE; +- goto exit; +- } +- /* In case of P2P GO or GC, apply pkt filter to pass arp pkt to host */ +- if ((dhd->arp_version == 1) && +- (op_mode & (DHD_FLAG_P2P_GC_MODE | DHD_FLAG_P2P_GO_MODE))) { +- _apply = TRUE; +- goto exit; +- } +- +-exit: +- return _apply; +-} +-#endif /* PKT_FILTER_SUPPORT && !GAN_LITE_NAT_KEEPALIVE_FILTER */ +- +-void dhd_set_packet_filter(dhd_pub_t *dhd) +-{ +-#ifdef PKT_FILTER_SUPPORT +- int i; +- +- DHD_TRACE(("%s: enter\n", __FUNCTION__)); +- if (dhd_pkt_filter_enable) { +- for (i = 0; i < dhd->pktfilter_count; i++) { +- dhd_pktfilter_offload_set(dhd, dhd->pktfilter[i]); +- } +- } +-#endif /* PKT_FILTER_SUPPORT */ +-} +- +-void dhd_enable_packet_filter(int value, dhd_pub_t *dhd) +-{ +-#ifdef PKT_FILTER_SUPPORT +- int i; +- +- DHD_TRACE(("%s: enter, value = %d\n", __FUNCTION__, value)); +- /* 1 - Enable packet filter, only allow unicast packet to send up */ +- /* 0 - Disable packet filter */ +- if (dhd_pkt_filter_enable && (!value || +- (dhd_support_sta_mode(dhd) && !dhd->dhcp_in_progress))) +- { +- for (i = 0; i < dhd->pktfilter_count; i++) { +-#ifndef GAN_LITE_NAT_KEEPALIVE_FILTER +- if (value && (i == DHD_ARP_FILTER_NUM) && +- !_turn_on_arp_filter(dhd, dhd->op_mode)) { +- DHD_TRACE(("Do not turn on ARP white list pkt filter:" +- "val %d, cnt %d, op_mode 0x%x\n", +- value, i, dhd->op_mode)); +- continue; +- } +-#endif /* !GAN_LITE_NAT_KEEPALIVE_FILTER */ +- dhd_pktfilter_offload_enable(dhd, dhd->pktfilter[i], +- value, dhd_master_mode); +- } +- } +-#endif /* PKT_FILTER_SUPPORT */ +-} +- +-static int dhd_set_suspend(int value, dhd_pub_t *dhd) +-{ +-#ifndef SUPPORT_PM2_ONLY +- int power_mode = PM_MAX; +-#endif /* SUPPORT_PM2_ONLY */ +- /* wl_pkt_filter_enable_t enable_parm; */ +- char iovbuf[32]; +- int bcn_li_dtim = 0; /* Default bcn_li_dtim in resume mode is 0 */ +- uint roamvar = dhd->conf->roam_off_suspend; +- uint nd_ra_filter = 0; +- int ret = 0; +- +- if (!dhd) +- return -ENODEV; +- +- DHD_TRACE(("%s: enter, value = %d in_suspend=%d\n", +- __FUNCTION__, value, dhd->in_suspend)); +- +- dhd_suspend_lock(dhd); +- +-#ifdef CUSTOM_SET_CPUCORE +- DHD_TRACE(("%s set cpucore(suspend%d)\n", __FUNCTION__, value)); +- /* set specific cpucore */ +- dhd_set_cpucore(dhd, TRUE); +-#endif /* CUSTOM_SET_CPUCORE */ +- if (dhd->up) { +- if (value && dhd->in_suspend) { +-#ifdef PKT_FILTER_SUPPORT +- dhd->early_suspended = 1; +-#endif +- /* Kernel suspended */ +- DHD_ERROR(("%s: force extra Suspend setting\n", __FUNCTION__)); +- +-#ifndef SUPPORT_PM2_ONLY +- dhd_wl_ioctl_cmd(dhd, WLC_SET_PM, (char *)&power_mode, +- sizeof(power_mode), TRUE, 0); +-#endif /* SUPPORT_PM2_ONLY */ +- +- /* Enable packet filter, only allow unicast packet to send up */ +- dhd_enable_packet_filter(1, dhd); +- +- /* If DTIM skip is set up as default, force it to wake +- * each third DTIM for better power savings. Note that +- * one side effect is a chance to miss BC/MC packet. +- */ +- bcn_li_dtim = dhd_get_suspend_bcn_li_dtim(dhd); +- bcm_mkiovar("bcn_li_dtim", (char *)&bcn_li_dtim, +- 4, iovbuf, sizeof(iovbuf)); +- if (dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), +- TRUE, 0) < 0) +- DHD_ERROR(("%s: set dtim failed\n", __FUNCTION__)); +- +- /* Disable firmware roaming during suspend */ +- bcm_mkiovar("roam_off", (char *)&roamvar, 4, iovbuf, sizeof(iovbuf)); +- dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); +- if (FW_SUPPORTED(dhd, ndoe)) { +- /* enable IPv6 RA filter in firmware during suspend */ +- nd_ra_filter = 1; +- bcm_mkiovar("nd_ra_filter_enable", (char *)&nd_ra_filter, 4, +- iovbuf, sizeof(iovbuf)); +- if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, +- sizeof(iovbuf), TRUE, 0)) < 0) +- DHD_ERROR(("failed to set nd_ra_filter (%d)\n", +- ret)); +- } +- } else { +-#ifdef PKT_FILTER_SUPPORT +- dhd->early_suspended = 0; +-#endif +- /* Kernel resumed */ +- DHD_ERROR(("%s: Remove extra suspend setting\n", __FUNCTION__)); +- +-#ifndef SUPPORT_PM2_ONLY +- power_mode = PM_FAST; +- dhd_wl_ioctl_cmd(dhd, WLC_SET_PM, (char *)&power_mode, +- sizeof(power_mode), TRUE, 0); +-#endif /* SUPPORT_PM2_ONLY */ +-#ifdef PKT_FILTER_SUPPORT +- /* disable pkt filter */ +- dhd_enable_packet_filter(0, dhd); +-#endif /* PKT_FILTER_SUPPORT */ +- +- /* restore pre-suspend setting for dtim_skip */ +- bcm_mkiovar("bcn_li_dtim", (char *)&bcn_li_dtim, +- 4, iovbuf, sizeof(iovbuf)); +- +- dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); +- roamvar = dhd_roam_disable; +- bcm_mkiovar("roam_off", (char *)&roamvar, 4, iovbuf, sizeof(iovbuf)); +- dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); +- if (FW_SUPPORTED(dhd, ndoe)) { +- /* disable IPv6 RA filter in firmware during suspend */ +- nd_ra_filter = 0; +- bcm_mkiovar("nd_ra_filter_enable", (char *)&nd_ra_filter, 4, +- iovbuf, sizeof(iovbuf)); +- if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, +- sizeof(iovbuf), TRUE, 0)) < 0) +- DHD_ERROR(("failed to set nd_ra_filter (%d)\n", +- ret)); +- } +- } +- } +- dhd_suspend_unlock(dhd); +- +- return 0; +-} +- +-static int dhd_suspend_resume_helper(struct dhd_info *dhd, int val, int force) +-{ +- dhd_pub_t *dhdp = &dhd->pub; +- int ret = 0; +- +- DHD_OS_WAKE_LOCK(dhdp); +- DHD_PERIM_LOCK(dhdp); +- +- /* Set flag when early suspend was called */ +- dhdp->in_suspend = val; +- if ((force || !dhdp->suspend_disable_flag) && +- dhd_support_sta_mode(dhdp)) +- { +- ret = dhd_set_suspend(val, dhdp); +- } +- +- DHD_PERIM_UNLOCK(dhdp); +- DHD_OS_WAKE_UNLOCK(dhdp); +- return ret; +-} +- +-#if defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND) +-static void dhd_early_suspend(struct early_suspend *h) +-{ +- struct dhd_info *dhd = container_of(h, struct dhd_info, early_suspend); +- DHD_TRACE_HW4(("%s: enter\n", __FUNCTION__)); +- +- if (dhd) +- dhd_suspend_resume_helper(dhd, 1, 0); +-} +- +-static void dhd_late_resume(struct early_suspend *h) +-{ +- struct dhd_info *dhd = container_of(h, struct dhd_info, early_suspend); +- DHD_TRACE_HW4(("%s: enter\n", __FUNCTION__)); +- +- if (dhd) +- dhd_suspend_resume_helper(dhd, 0, 0); +-} +-#endif /* CONFIG_HAS_EARLYSUSPEND && DHD_USE_EARLYSUSPEND */ +- +-/* +- * Generalized timeout mechanism. Uses spin sleep with exponential back-off until +- * the sleep time reaches one jiffy, then switches over to task delay. Usage: +- * +- * dhd_timeout_start(&tmo, usec); +- * while (!dhd_timeout_expired(&tmo)) +- * if (poll_something()) +- * break; +- * if (dhd_timeout_expired(&tmo)) +- * fatal(); +- */ +- +-void +-dhd_timeout_start(dhd_timeout_t *tmo, uint usec) +-{ +- tmo->limit = usec; +- tmo->increment = 0; +- tmo->elapsed = 0; +- tmo->tick = jiffies_to_usecs(1); +-} +- +-int +-dhd_timeout_expired(dhd_timeout_t *tmo) +-{ +- /* Does nothing the first call */ +- if (tmo->increment == 0) { +- tmo->increment = 1; +- return 0; +- } +- +- if (tmo->elapsed >= tmo->limit) +- return 1; +- +- /* Add the delay that's about to take place */ +- tmo->elapsed += tmo->increment; +- +- if ((!CAN_SLEEP()) || tmo->increment < tmo->tick) { +- OSL_DELAY(tmo->increment); +- tmo->increment *= 2; +- if (tmo->increment > tmo->tick) +- tmo->increment = tmo->tick; +- } else { +- wait_queue_head_t delay_wait; +- DECLARE_WAITQUEUE(wait, current); +- init_waitqueue_head(&delay_wait); +- add_wait_queue(&delay_wait, &wait); +- set_current_state(TASK_INTERRUPTIBLE); +- (void)schedule_timeout(1); +- remove_wait_queue(&delay_wait, &wait); +- set_current_state(TASK_RUNNING); +- } +- +- return 0; +-} +- +-int +-dhd_net2idx(dhd_info_t *dhd, struct net_device *net) +-{ +- int i = 0; +- +- ASSERT(dhd); +- while (i < DHD_MAX_IFS) { +- if (dhd->iflist[i] && dhd->iflist[i]->net && (dhd->iflist[i]->net == net)) +- return i; +- i++; +- } +- +- return DHD_BAD_IF; +-} +- +-struct net_device * dhd_idx2net(void *pub, int ifidx) +-{ +- struct dhd_pub *dhd_pub = (struct dhd_pub *)pub; +- struct dhd_info *dhd_info; +- +- if (!dhd_pub || ifidx < 0 || ifidx >= DHD_MAX_IFS) +- return NULL; +- dhd_info = dhd_pub->info; +- if (dhd_info && dhd_info->iflist[ifidx]) +- return dhd_info->iflist[ifidx]->net; +- return NULL; +-} +- +-int +-dhd_ifname2idx(dhd_info_t *dhd, char *name) +-{ +- int i = DHD_MAX_IFS; +- +- ASSERT(dhd); +- +- if (name == NULL || *name == '\0') +- return 0; +- +- while (--i > 0) +- if (dhd->iflist[i] && !strncmp(dhd->iflist[i]->name, name, IFNAMSIZ)) +- break; +- +- DHD_TRACE(("%s: return idx %d for \"%s\"\n", __FUNCTION__, i, name)); +- +- return i; /* default - the primary interface */ +-} +- +-int +-dhd_ifidx2hostidx(dhd_info_t *dhd, int ifidx) +-{ +- int i = DHD_MAX_IFS; +- +- ASSERT(dhd); +- +- while (--i > 0) +- if (dhd->iflist[i] && (dhd->iflist[i]->idx == ifidx)) +- break; +- +- DHD_TRACE(("%s: return hostidx %d for ifidx %d\n", __FUNCTION__, i, ifidx)); +- +- return i; /* default - the primary interface */ +-} +- +-char * +-dhd_ifname(dhd_pub_t *dhdp, int ifidx) +-{ +- dhd_info_t *dhd = (dhd_info_t *)dhdp->info; +- +- ASSERT(dhd); +- +- if (ifidx < 0 || ifidx >= DHD_MAX_IFS) { +- DHD_ERROR(("%s: ifidx %d out of range\n", __FUNCTION__, ifidx)); +- return ""; +- } +- +- if (dhd->iflist[ifidx] == NULL) { +- DHD_ERROR(("%s: null i/f %d\n", __FUNCTION__, ifidx)); +- return ""; +- } +- +- if (dhd->iflist[ifidx]->net) +- return dhd->iflist[ifidx]->net->name; +- +- return ""; +-} +- +-uint8 * +-dhd_bssidx2bssid(dhd_pub_t *dhdp, int idx) +-{ +- int i; +- dhd_info_t *dhd = (dhd_info_t *)dhdp; +- +- ASSERT(dhd); +- for (i = 0; i < DHD_MAX_IFS; i++) +- if (dhd->iflist[i] && dhd->iflist[i]->bssidx == idx) +- return dhd->iflist[i]->mac_addr; +- +- return NULL; +-} +- +- +-static void +-_dhd_set_multicast_list(dhd_info_t *dhd, int ifidx) +-{ +- struct net_device *dev; +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35) +- struct netdev_hw_addr *ha; +-#else +- struct dev_mc_list *mclist; +-#endif +- uint32 allmulti, cnt; +- +- wl_ioctl_t ioc; +- char *buf, *bufp; +- uint buflen; +- int ret; +- +- ASSERT(dhd && dhd->iflist[ifidx]); +- dev = dhd->iflist[ifidx]->net; +- if (!dev) +- return; +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) +- netif_addr_lock_bh(dev); +-#endif +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35) +- cnt = netdev_mc_count(dev); +-#else +- cnt = dev->mc_count; +-#endif /* LINUX_VERSION_CODE */ +- +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) +- netif_addr_unlock_bh(dev); +-#endif +- +- /* Determine initial value of allmulti flag */ +- allmulti = (dev->flags & IFF_ALLMULTI) ? TRUE : FALSE; +- +- /* Send down the multicast list first. */ +- +- +- buflen = sizeof("mcast_list") + sizeof(cnt) + (cnt * ETHER_ADDR_LEN); +- if (!(bufp = buf = MALLOC(dhd->pub.osh, buflen))) { +- DHD_ERROR(("%s: out of memory for mcast_list, cnt %d\n", +- dhd_ifname(&dhd->pub, ifidx), cnt)); +- return; +- } +- +- strncpy(bufp, "mcast_list", buflen - 1); +- bufp[buflen - 1] = '\0'; +- bufp += strlen("mcast_list") + 1; +- +- cnt = htol32(cnt); +- memcpy(bufp, &cnt, sizeof(cnt)); +- bufp += sizeof(cnt); +- +- +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) +- netif_addr_lock_bh(dev); +-#endif +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35) +- netdev_for_each_mc_addr(ha, dev) { +- if (!cnt) +- break; +- memcpy(bufp, ha->addr, ETHER_ADDR_LEN); +- bufp += ETHER_ADDR_LEN; +- cnt--; +- } +-#else +- for (mclist = dev->mc_list; (mclist && (cnt > 0)); +- cnt--, mclist = mclist->next) { +- memcpy(bufp, (void *)mclist->dmi_addr, ETHER_ADDR_LEN); +- bufp += ETHER_ADDR_LEN; +- } +-#endif /* LINUX_VERSION_CODE */ +- +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) +- netif_addr_unlock_bh(dev); +-#endif +- +- memset(&ioc, 0, sizeof(ioc)); +- ioc.cmd = WLC_SET_VAR; +- ioc.buf = buf; +- ioc.len = buflen; +- ioc.set = TRUE; +- +- ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len); +- if (ret < 0) { +- DHD_ERROR(("%s: set mcast_list failed, cnt %d\n", +- dhd_ifname(&dhd->pub, ifidx), cnt)); +- allmulti = cnt ? TRUE : allmulti; +- } +- +- MFREE(dhd->pub.osh, buf, buflen); +- +- /* Now send the allmulti setting. This is based on the setting in the +- * net_device flags, but might be modified above to be turned on if we +- * were trying to set some addresses and dongle rejected it... +- */ +- +- buflen = sizeof("allmulti") + sizeof(allmulti); +- if (!(buf = MALLOC(dhd->pub.osh, buflen))) { +- DHD_ERROR(("%s: out of memory for allmulti\n", dhd_ifname(&dhd->pub, ifidx))); +- return; +- } +- allmulti = htol32(allmulti); +- +- if (!bcm_mkiovar("allmulti", (void*)&allmulti, sizeof(allmulti), buf, buflen)) { +- DHD_ERROR(("%s: mkiovar failed for allmulti, datalen %d buflen %u\n", +- dhd_ifname(&dhd->pub, ifidx), (int)sizeof(allmulti), buflen)); +- MFREE(dhd->pub.osh, buf, buflen); +- return; +- } +- +- +- memset(&ioc, 0, sizeof(ioc)); +- ioc.cmd = WLC_SET_VAR; +- ioc.buf = buf; +- ioc.len = buflen; +- ioc.set = TRUE; +- +- ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len); +- if (ret < 0) { +- DHD_ERROR(("%s: set allmulti %d failed\n", +- dhd_ifname(&dhd->pub, ifidx), ltoh32(allmulti))); +- } +- +- MFREE(dhd->pub.osh, buf, buflen); +- +- /* Finally, pick up the PROMISC flag as well, like the NIC driver does */ +- +- allmulti = (dev->flags & IFF_PROMISC) ? TRUE : FALSE; +- +- allmulti = htol32(allmulti); +- +- memset(&ioc, 0, sizeof(ioc)); +- ioc.cmd = WLC_SET_PROMISC; +- ioc.buf = &allmulti; +- ioc.len = sizeof(allmulti); +- ioc.set = TRUE; +- +- ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len); +- if (ret < 0) { +- DHD_ERROR(("%s: set promisc %d failed\n", +- dhd_ifname(&dhd->pub, ifidx), ltoh32(allmulti))); +- } +-} +- +-int +-_dhd_set_mac_address(dhd_info_t *dhd, int ifidx, uint8 *addr) +-{ +- char buf[32]; +- wl_ioctl_t ioc; +- int ret; +- +- if (!bcm_mkiovar("cur_etheraddr", (char*)addr, ETHER_ADDR_LEN, buf, 32)) { +- DHD_ERROR(("%s: mkiovar failed for cur_etheraddr\n", dhd_ifname(&dhd->pub, ifidx))); +- return -1; +- } +- memset(&ioc, 0, sizeof(ioc)); +- ioc.cmd = WLC_SET_VAR; +- ioc.buf = buf; +- ioc.len = 32; +- ioc.set = TRUE; +- +- ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len); +- if (ret < 0) { +- DHD_ERROR(("%s: set cur_etheraddr failed\n", dhd_ifname(&dhd->pub, ifidx))); +- } else { +- memcpy(dhd->iflist[ifidx]->net->dev_addr, addr, ETHER_ADDR_LEN); +- if (ifidx == 0) +- memcpy(dhd->pub.mac.octet, addr, ETHER_ADDR_LEN); +- } +- +- return ret; +-} +- +-#ifdef SOFTAP +-extern struct net_device *ap_net_dev; +-extern tsk_ctl_t ap_eth_ctl; /* ap netdev heper thread ctl */ +-#endif +- +-static void +-dhd_ifadd_event_handler(void *handle, void *event_info, u8 event) +-{ +- dhd_info_t *dhd = handle; +- dhd_if_event_t *if_event = event_info; +- struct net_device *ndev; +- int ifidx, bssidx; +- int ret; +-#if 1 && (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)) +- struct wireless_dev *vwdev, *primary_wdev; +- struct net_device *primary_ndev; +-#endif /* OEM_ANDROID && (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)) */ +- +- if (event != DHD_WQ_WORK_IF_ADD) { +- DHD_ERROR(("%s: unexpected event \n", __FUNCTION__)); +- return; +- } +- +- if (!dhd) { +- DHD_ERROR(("%s: dhd info not available \n", __FUNCTION__)); +- return; +- } +- +- if (!if_event) { +- DHD_ERROR(("%s: event data is null \n", __FUNCTION__)); +- return; +- } +- +- dhd_net_if_lock_local(dhd); +- DHD_OS_WAKE_LOCK(&dhd->pub); +- DHD_PERIM_LOCK(&dhd->pub); +- +- ifidx = if_event->event.ifidx; +- bssidx = if_event->event.bssidx; +- DHD_TRACE(("%s: registering if with ifidx %d\n", __FUNCTION__, ifidx)); +- +- ndev = dhd_allocate_if(&dhd->pub, ifidx, if_event->name, +- if_event->mac, bssidx, TRUE); +- if (!ndev) { +- DHD_ERROR(("%s: net device alloc failed \n", __FUNCTION__)); +- goto done; +- } +- +-#if 1 && (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)) +- vwdev = kzalloc(sizeof(*vwdev), GFP_KERNEL); +- if (unlikely(!vwdev)) { +- WL_ERR(("Could not allocate wireless device\n")); +- goto done; +- } +- primary_ndev = dhd->pub.info->iflist[0]->net; +- primary_wdev = ndev_to_wdev(primary_ndev); +- vwdev->wiphy = primary_wdev->wiphy; +- vwdev->iftype = if_event->event.role; +- vwdev->netdev = ndev; +- ndev->ieee80211_ptr = vwdev; +- SET_NETDEV_DEV(ndev, wiphy_dev(vwdev->wiphy)); +- DHD_ERROR(("virtual interface(%s) is created\n", if_event->name)); +-#endif /* OEM_ANDROID && (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)) */ +- +- DHD_PERIM_UNLOCK(&dhd->pub); +- ret = dhd_register_if(&dhd->pub, ifidx, TRUE); +- DHD_PERIM_LOCK(&dhd->pub); +- if (ret != BCME_OK) { +- DHD_ERROR(("%s: dhd_register_if failed\n", __FUNCTION__)); +- dhd_remove_if(&dhd->pub, ifidx, TRUE); +- } +-#ifdef PCIE_FULL_DONGLE +- /* Turn on AP isolation in the firmware for interfaces operating in AP mode */ +- if (FW_SUPPORTED((&dhd->pub), ap) && (if_event->event.role != WLC_E_IF_ROLE_STA)) { +- char iovbuf[WLC_IOCTL_SMLEN]; +- uint32 var_int = 1; +- +- memset(iovbuf, 0, sizeof(iovbuf)); +- bcm_mkiovar("ap_isolate", (char *)&var_int, 4, iovbuf, sizeof(iovbuf)); +- dhd_wl_ioctl_cmd(&dhd->pub, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, ifidx); +- } +-#endif /* PCIE_FULL_DONGLE */ +-done: +- MFREE(dhd->pub.osh, if_event, sizeof(dhd_if_event_t)); +- +- DHD_PERIM_UNLOCK(&dhd->pub); +- DHD_OS_WAKE_UNLOCK(&dhd->pub); +- dhd_net_if_unlock_local(dhd); +-} +- +-static void +-dhd_ifdel_event_handler(void *handle, void *event_info, u8 event) +-{ +- dhd_info_t *dhd = handle; +- int ifidx; +- dhd_if_event_t *if_event = event_info; +- +- +- if (event != DHD_WQ_WORK_IF_DEL) { +- DHD_ERROR(("%s: unexpected event \n", __FUNCTION__)); +- return; +- } +- +- if (!dhd) { +- DHD_ERROR(("%s: dhd info not available \n", __FUNCTION__)); +- return; +- } +- +- if (!if_event) { +- DHD_ERROR(("%s: event data is null \n", __FUNCTION__)); +- return; +- } +- +- dhd_net_if_lock_local(dhd); +- DHD_OS_WAKE_LOCK(&dhd->pub); +- DHD_PERIM_LOCK(&dhd->pub); +- +- ifidx = if_event->event.ifidx; +- DHD_TRACE(("Removing interface with idx %d\n", ifidx)); +- +- dhd_remove_if(&dhd->pub, ifidx, TRUE); +- +- MFREE(dhd->pub.osh, if_event, sizeof(dhd_if_event_t)); +- +- DHD_PERIM_UNLOCK(&dhd->pub); +- DHD_OS_WAKE_UNLOCK(&dhd->pub); +- dhd_net_if_unlock_local(dhd); +-} +- +-static void +-dhd_set_mac_addr_handler(void *handle, void *event_info, u8 event) +-{ +- dhd_info_t *dhd = handle; +- dhd_if_t *ifp = event_info; +- +- if (event != DHD_WQ_WORK_SET_MAC) { +- DHD_ERROR(("%s: unexpected event \n", __FUNCTION__)); +- } +- +- if (!dhd) { +- DHD_ERROR(("%s: dhd info not available \n", __FUNCTION__)); +- return; +- } +- +- dhd_net_if_lock_local(dhd); +- DHD_OS_WAKE_LOCK(&dhd->pub); +- DHD_PERIM_LOCK(&dhd->pub); +- +-#ifdef SOFTAP +- { +- unsigned long flags; +- bool in_ap = FALSE; +- DHD_GENERAL_LOCK(&dhd->pub, flags); +- in_ap = (ap_net_dev != NULL); +- DHD_GENERAL_UNLOCK(&dhd->pub, flags); +- +- if (in_ap) { +- DHD_ERROR(("attempt to set MAC for %s in AP Mode, blocked. \n", +- ifp->net->name)); +- goto done; +- } +- } +-#endif /* SOFTAP */ +- +- if (ifp == NULL || !dhd->pub.up) { +- DHD_ERROR(("%s: interface info not available/down \n", __FUNCTION__)); +- goto done; +- } +- +- DHD_ERROR(("%s: MACID is overwritten\n", __FUNCTION__)); +- ifp->set_macaddress = FALSE; +- if (_dhd_set_mac_address(dhd, ifp->idx, ifp->mac_addr) == 0) +- DHD_INFO(("%s: MACID is overwritten\n", __FUNCTION__)); +- else +- DHD_ERROR(("%s: _dhd_set_mac_address() failed\n", __FUNCTION__)); +- +-done: +- DHD_PERIM_UNLOCK(&dhd->pub); +- DHD_OS_WAKE_UNLOCK(&dhd->pub); +- dhd_net_if_unlock_local(dhd); +-} +- +-static void +-dhd_set_mcast_list_handler(void *handle, void *event_info, u8 event) +-{ +- dhd_info_t *dhd = handle; +- dhd_if_t *ifp = event_info; +- int ifidx; +- +- if (event != DHD_WQ_WORK_SET_MCAST_LIST) { +- DHD_ERROR(("%s: unexpected event \n", __FUNCTION__)); +- return; +- } +- +- if (!dhd) { +- DHD_ERROR(("%s: dhd info not available \n", __FUNCTION__)); +- return; +- } +- +- dhd_net_if_lock_local(dhd); +- DHD_OS_WAKE_LOCK(&dhd->pub); +- DHD_PERIM_LOCK(&dhd->pub); +- +-#ifdef SOFTAP +- { +- bool in_ap = FALSE; +- unsigned long flags; +- DHD_GENERAL_LOCK(&dhd->pub, flags); +- in_ap = (ap_net_dev != NULL); +- DHD_GENERAL_UNLOCK(&dhd->pub, flags); +- +- if (in_ap) { +- DHD_ERROR(("set MULTICAST list for %s in AP Mode, blocked. \n", +- ifp->net->name)); +- ifp->set_multicast = FALSE; +- goto done; +- } +- } +-#endif /* SOFTAP */ +- +- if (ifp == NULL || !dhd->pub.up) { +- DHD_ERROR(("%s: interface info not available/down \n", __FUNCTION__)); +- goto done; +- } +- +- ifidx = ifp->idx; +- +- +- _dhd_set_multicast_list(dhd, ifidx); +- DHD_INFO(("%s: set multicast list for if %d\n", __FUNCTION__, ifidx)); +- +-done: +- DHD_PERIM_UNLOCK(&dhd->pub); +- DHD_OS_WAKE_UNLOCK(&dhd->pub); +- dhd_net_if_unlock_local(dhd); +-} +- +-static int +-dhd_set_mac_address(struct net_device *dev, void *addr) +-{ +- int ret = 0; +- +- dhd_info_t *dhd = DHD_DEV_INFO(dev); +- struct sockaddr *sa = (struct sockaddr *)addr; +- int ifidx; +- dhd_if_t *dhdif; +- +- ifidx = dhd_net2idx(dhd, dev); +- if (ifidx == DHD_BAD_IF) +- return -1; +- +- dhdif = dhd->iflist[ifidx]; +- +- dhd_net_if_lock_local(dhd); +- memcpy(dhdif->mac_addr, sa->sa_data, ETHER_ADDR_LEN); +- dhdif->set_macaddress = TRUE; +- dhd_net_if_unlock_local(dhd); +- dhd_deferred_schedule_work(dhd->dhd_deferred_wq, (void *)dhdif, DHD_WQ_WORK_SET_MAC, +- dhd_set_mac_addr_handler, DHD_WORK_PRIORITY_LOW); +- return ret; +-} +- +-static void +-dhd_set_multicast_list(struct net_device *dev) +-{ +- dhd_info_t *dhd = DHD_DEV_INFO(dev); +- int ifidx; +- +- ifidx = dhd_net2idx(dhd, dev); +- if (ifidx == DHD_BAD_IF) +- return; +- +- dhd->iflist[ifidx]->set_multicast = TRUE; +- dhd_deferred_schedule_work(dhd->dhd_deferred_wq, (void *)dhd->iflist[ifidx], +- DHD_WQ_WORK_SET_MCAST_LIST, dhd_set_mcast_list_handler, DHD_WORK_PRIORITY_LOW); +-} +- +-#ifdef PROP_TXSTATUS +-int +-dhd_os_wlfc_block(dhd_pub_t *pub) +-{ +- dhd_info_t *di = (dhd_info_t *)(pub->info); +- ASSERT(di != NULL); +- spin_lock_bh(&di->wlfc_spinlock); +- return 1; +-} +- +-int +-dhd_os_wlfc_unblock(dhd_pub_t *pub) +-{ +- dhd_info_t *di = (dhd_info_t *)(pub->info); +- +- ASSERT(di != NULL); +- spin_unlock_bh(&di->wlfc_spinlock); +- return 1; +-} +- +-#endif /* PROP_TXSTATUS */ +- +-int BCMFASTPATH +-dhd_sendpkt(dhd_pub_t *dhdp, int ifidx, void *pktbuf) +-{ +- int ret = BCME_OK; +- dhd_info_t *dhd = (dhd_info_t *)(dhdp->info); +- struct ether_header *eh = NULL; +- +- /* Reject if down */ +- if (!dhdp->up || (dhdp->busstate == DHD_BUS_DOWN)) { +- /* free the packet here since the caller won't */ +- PKTFREE(dhdp->osh, pktbuf, TRUE); +- return -ENODEV; +- } +- +-#ifdef PCIE_FULL_DONGLE +- if (dhdp->busstate == DHD_BUS_SUSPEND) { +- DHD_ERROR(("%s : pcie is still in suspend state!!\n", __FUNCTION__)); +- PKTFREE(dhdp->osh, pktbuf, TRUE); +- return -EBUSY; +- } +-#endif /* PCIE_FULL_DONGLE */ +- +-#ifdef DHD_UNICAST_DHCP +- /* if dhcp_unicast is enabled, we need to convert the */ +- /* broadcast DHCP ACK/REPLY packets to Unicast. */ +- if (dhdp->dhcp_unicast) { +- dhd_convert_dhcp_broadcast_ack_to_unicast(dhdp, pktbuf, ifidx); +- } +-#endif /* DHD_UNICAST_DHCP */ +- /* Update multicast statistic */ +- if (PKTLEN(dhdp->osh, pktbuf) >= ETHER_HDR_LEN) { +- uint8 *pktdata = (uint8 *)PKTDATA(dhdp->osh, pktbuf); +- eh = (struct ether_header *)pktdata; +- +- if (ETHER_ISMULTI(eh->ether_dhost)) +- dhdp->tx_multicast++; +- if (ntoh16(eh->ether_type) == ETHER_TYPE_802_1X) +- atomic_inc(&dhd->pend_8021x_cnt); +- } else { +- PKTFREE(dhd->pub.osh, pktbuf, TRUE); +- return BCME_ERROR; +- } +- +-#ifdef DHDTCPACK_SUPPRESS +- /* If this packet has replaced another packet and got freed, just return */ +- if (dhd_tcpack_suppress(dhdp, pktbuf)) +- return ret; +-#endif /* DHDTCPACK_SUPPRESS */ +- +- /* Look into the packet and update the packet priority */ +-#ifndef PKTPRIO_OVERRIDE +- if (PKTPRIO(pktbuf) == 0) +-#endif +- pktsetprio(pktbuf, FALSE); +- +- +-#ifdef PCIE_FULL_DONGLE +- /* +- * Lkup the per interface hash table, for a matching flowring. If one is not +- * available, allocate a unique flowid and add a flowring entry. +- * The found or newly created flowid is placed into the pktbuf's tag. +- */ +- ret = dhd_flowid_update(dhdp, ifidx, dhdp->flow_prio_map[(PKTPRIO(pktbuf))], pktbuf); +- if (ret != BCME_OK) { +- PKTCFREE(dhd->pub.osh, pktbuf, TRUE); +- return ret; +- } +-#endif +- +-#ifdef PROP_TXSTATUS +- if (dhd_wlfc_is_supported(dhdp)) { +- /* store the interface ID */ +- DHD_PKTTAG_SETIF(PKTTAG(pktbuf), ifidx); +- +- /* store destination MAC in the tag as well */ +- DHD_PKTTAG_SETDSTN(PKTTAG(pktbuf), eh->ether_dhost); +- +- /* decide which FIFO this packet belongs to */ +- if (ETHER_ISMULTI(eh->ether_dhost)) +- /* one additional queue index (highest AC + 1) is used for bc/mc queue */ +- DHD_PKTTAG_SETFIFO(PKTTAG(pktbuf), AC_COUNT); +- else +- DHD_PKTTAG_SETFIFO(PKTTAG(pktbuf), WME_PRIO2AC(PKTPRIO(pktbuf))); +- } else +-#endif /* PROP_TXSTATUS */ +- /* If the protocol uses a data header, apply it */ +- dhd_prot_hdrpush(dhdp, ifidx, pktbuf); +- +- /* Use bus module to send data frame */ +-#ifdef WLMEDIA_HTSF +- dhd_htsf_addtxts(dhdp, pktbuf); +-#endif +-#ifdef PROP_TXSTATUS +- { +- if (dhd_wlfc_commit_packets(dhdp, (f_commitpkt_t)dhd_bus_txdata, +- dhdp->bus, pktbuf, TRUE) == WLFC_UNSUPPORTED) { +- /* non-proptxstatus way */ +-#ifdef BCMPCIE +- ret = dhd_bus_txdata(dhdp->bus, pktbuf, (uint8)ifidx); +-#else +- ret = dhd_bus_txdata(dhdp->bus, pktbuf); +-#endif /* BCMPCIE */ +- } +- } +-#else +-#ifdef BCMPCIE +- ret = dhd_bus_txdata(dhdp->bus, pktbuf, (uint8)ifidx); +-#else +- ret = dhd_bus_txdata(dhdp->bus, pktbuf); +-#endif /* BCMPCIE */ +-#endif /* PROP_TXSTATUS */ +- +- return ret; +-} +- +-int BCMFASTPATH +-dhd_start_xmit(struct sk_buff *skb, struct net_device *net) +-{ +- int ret; +- uint datalen; +- void *pktbuf; +- dhd_info_t *dhd = DHD_DEV_INFO(net); +- dhd_if_t *ifp = NULL; +- int ifidx; +-#ifdef WLMEDIA_HTSF +- uint8 htsfdlystat_sz = dhd->pub.htsfdlystat_sz; +-#else +- uint8 htsfdlystat_sz = 0; +-#endif +-#ifdef DHD_WMF +- struct ether_header *eh; +- uint8 *iph; +-#endif /* DHD_WMF */ +- +- DHD_TRACE(("%s: Enter\n", __FUNCTION__)); +- +- DHD_OS_WAKE_LOCK(&dhd->pub); +- DHD_PERIM_LOCK_TRY(DHD_FWDER_UNIT(dhd), TRUE); +- +- /* Reject if down */ +- if (dhd->pub.busstate == DHD_BUS_DOWN || dhd->pub.hang_was_sent) { +- DHD_ERROR(("%s: xmit rejected pub.up=%d busstate=%d \n", +- __FUNCTION__, dhd->pub.up, dhd->pub.busstate)); +- netif_stop_queue(net); +- /* Send Event when bus down detected during data session */ +- if (dhd->pub.up) { +- DHD_ERROR(("%s: Event HANG sent up\n", __FUNCTION__)); +- net_os_send_hang_message(net); +- } +- DHD_PERIM_UNLOCK_TRY(DHD_FWDER_UNIT(dhd), TRUE); +- DHD_OS_WAKE_UNLOCK(&dhd->pub); +-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)) +- return -ENODEV; +-#else +- return NETDEV_TX_BUSY; +-#endif +- } +- +- ifp = DHD_DEV_IFP(net); +- ifidx = DHD_DEV_IFIDX(net); +- +- ASSERT(ifidx == dhd_net2idx(dhd, net)); +- ASSERT((ifp != NULL) && (ifp == dhd->iflist[ifidx])); +- +- if (ifidx == DHD_BAD_IF) { +- DHD_ERROR(("%s: bad ifidx %d\n", __FUNCTION__, ifidx)); +- netif_stop_queue(net); +- DHD_PERIM_UNLOCK_TRY(DHD_FWDER_UNIT(dhd), TRUE); +- DHD_OS_WAKE_UNLOCK(&dhd->pub); +-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)) +- return -ENODEV; +-#else +- return NETDEV_TX_BUSY; +-#endif +- } +- +- /* re-align socket buffer if "skb->data" is odd address */ +- if (((unsigned long)(skb->data)) & 0x1) { +- unsigned char *data = skb->data; +- uint32 length = skb->len; +- PKTPUSH(dhd->pub.osh, skb, 1); +- memmove(skb->data, data, length); +- PKTSETLEN(dhd->pub.osh, skb, length); +- } +- +- datalen = PKTLEN(dhd->pub.osh, skb); +- +- /* Make sure there's enough room for any header */ +- +- if (skb_headroom(skb) < dhd->pub.hdrlen + htsfdlystat_sz) { +- struct sk_buff *skb2; +- +- DHD_INFO(("%s: insufficient headroom\n", +- dhd_ifname(&dhd->pub, ifidx))); +- dhd->pub.tx_realloc++; +- +- skb2 = skb_realloc_headroom(skb, dhd->pub.hdrlen + htsfdlystat_sz); +- +- dev_kfree_skb(skb); +- if ((skb = skb2) == NULL) { +- DHD_ERROR(("%s: skb_realloc_headroom failed\n", +- dhd_ifname(&dhd->pub, ifidx))); +- ret = -ENOMEM; +- goto done; +- } +- } +- +- /* Convert to packet */ +- if (!(pktbuf = PKTFRMNATIVE(dhd->pub.osh, skb))) { +- DHD_ERROR(("%s: PKTFRMNATIVE failed\n", +- dhd_ifname(&dhd->pub, ifidx))); +- dev_kfree_skb_any(skb); +- ret = -ENOMEM; +- goto done; +- } +-#ifdef WLMEDIA_HTSF +- if (htsfdlystat_sz && PKTLEN(dhd->pub.osh, pktbuf) >= ETHER_ADDR_LEN) { +- uint8 *pktdata = (uint8 *)PKTDATA(dhd->pub.osh, pktbuf); +- struct ether_header *eh = (struct ether_header *)pktdata; +- +- if (!ETHER_ISMULTI(eh->ether_dhost) && +- (ntoh16(eh->ether_type) == ETHER_TYPE_IP)) { +- eh->ether_type = hton16(ETHER_TYPE_BRCM_PKTDLYSTATS); +- } +- } +-#endif +-#ifdef DHD_WMF +- eh = (struct ether_header *)PKTDATA(dhd->pub.osh, pktbuf); +- iph = (uint8 *)eh + ETHER_HDR_LEN; +- +- /* WMF processing for multicast packets +- * Only IPv4 packets are handled +- */ +- if (ifp->wmf.wmf_enable && (ntoh16(eh->ether_type) == ETHER_TYPE_IP) && +- (IP_VER(iph) == IP_VER_4) && (ETHER_ISMULTI(eh->ether_dhost) || +- ((IPV4_PROT(iph) == IP_PROT_IGMP) && dhd->pub.wmf_ucast_igmp))) { +-#if defined(DHD_IGMP_UCQUERY) || defined(DHD_UCAST_UPNP) +- void *sdu_clone; +- bool ucast_convert = FALSE; +-#ifdef DHD_UCAST_UPNP +- uint32 dest_ip; +- +- dest_ip = ntoh32(*((uint32 *)(iph + IPV4_DEST_IP_OFFSET))); +- ucast_convert = dhd->pub.wmf_ucast_upnp && MCAST_ADDR_UPNP_SSDP(dest_ip); +-#endif /* DHD_UCAST_UPNP */ +-#ifdef DHD_IGMP_UCQUERY +- ucast_convert |= dhd->pub.wmf_ucast_igmp_query && +- (IPV4_PROT(iph) == IP_PROT_IGMP) && +- (*(iph + IPV4_HLEN(iph)) == IGMPV2_HOST_MEMBERSHIP_QUERY); +-#endif /* DHD_IGMP_UCQUERY */ +- if (ucast_convert) { +- dhd_sta_t *sta; +- unsigned long flags; +- +- DHD_IF_STA_LIST_LOCK(ifp, flags); +- +- /* Convert upnp/igmp query to unicast for each assoc STA */ +- list_for_each_entry(sta, &ifp->sta_list, list) { +- if ((sdu_clone = PKTDUP(dhd->pub.osh, pktbuf)) == NULL) { +- DHD_IF_STA_LIST_UNLOCK(ifp, flags); +- DHD_PERIM_UNLOCK_TRY(DHD_FWDER_UNIT(dhd), TRUE); +- DHD_OS_WAKE_UNLOCK(&dhd->pub); +- return (WMF_NOP); +- } +- dhd_wmf_forward(ifp->wmf.wmfh, sdu_clone, 0, sta, 1); +- } +- +- DHD_IF_STA_LIST_UNLOCK(ifp, flags); +- DHD_PERIM_UNLOCK_TRY(DHD_FWDER_UNIT(dhd), TRUE); +- DHD_OS_WAKE_UNLOCK(&dhd->pub); +- +- PKTFREE(dhd->pub.osh, pktbuf, TRUE); +- return NETDEV_TX_OK; +- } else +-#endif /* defined(DHD_IGMP_UCQUERY) || defined(DHD_UCAST_UPNP) */ +- { +- /* There will be no STA info if the packet is coming from LAN host +- * Pass as NULL +- */ +- ret = dhd_wmf_packets_handle(&dhd->pub, pktbuf, NULL, ifidx, 0); +- switch (ret) { +- case WMF_TAKEN: +- case WMF_DROP: +- /* Either taken by WMF or we should drop it. +- * Exiting send path +- */ +- DHD_PERIM_UNLOCK_TRY(DHD_FWDER_UNIT(dhd), TRUE); +- DHD_OS_WAKE_UNLOCK(&dhd->pub); +- return NETDEV_TX_OK; +- default: +- /* Continue the transmit path */ +- break; +- } +- } +- } +-#endif /* DHD_WMF */ +- +- ret = dhd_sendpkt(&dhd->pub, ifidx, pktbuf); +- +-done: +- if (ret) { +- ifp->stats.tx_dropped++; +- dhd->pub.tx_dropped++; +- } +- else { +- dhd->pub.tx_packets++; +- ifp->stats.tx_packets++; +- ifp->stats.tx_bytes += datalen; +- } +- +- DHD_PERIM_UNLOCK_TRY(DHD_FWDER_UNIT(dhd), TRUE); +- DHD_OS_WAKE_UNLOCK(&dhd->pub); +- +- /* Return ok: we always eat the packet */ +-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)) +- return 0; +-#else +- return NETDEV_TX_OK; +-#endif +-} +- +- +-void +-dhd_txflowcontrol(dhd_pub_t *dhdp, int ifidx, bool state) +-{ +- struct net_device *net; +- dhd_info_t *dhd = dhdp->info; +- int i; +- +- DHD_TRACE(("%s: Enter\n", __FUNCTION__)); +- +- ASSERT(dhd); +- +- if (ifidx == ALL_INTERFACES) { +- /* Flow control on all active interfaces */ +- dhdp->txoff = state; +- for (i = 0; i < DHD_MAX_IFS; i++) { +- if (dhd->iflist[i]) { +- net = dhd->iflist[i]->net; +- if (state == ON) +- netif_stop_queue(net); +- else +- netif_wake_queue(net); +- } +- } +- } +- else { +- if (dhd->iflist[ifidx]) { +- net = dhd->iflist[ifidx]->net; +- if (state == ON) +- netif_stop_queue(net); +- else +- netif_wake_queue(net); +- } +- } +-} +- +-#ifdef DHD_RX_DUMP +-typedef struct { +- uint16 type; +- const char *str; +-} PKTTYPE_INFO; +- +-static const PKTTYPE_INFO packet_type_info[] = +-{ +- { ETHER_TYPE_IP, "IP" }, +- { ETHER_TYPE_ARP, "ARP" }, +- { ETHER_TYPE_BRCM, "BRCM" }, +- { ETHER_TYPE_802_1X, "802.1X" }, +- { ETHER_TYPE_WAI, "WAPI" }, +- { 0, ""} +-}; +- +-static const char *_get_packet_type_str(uint16 type) +-{ +- int i; +- int n = sizeof(packet_type_info)/sizeof(packet_type_info[1]) - 1; +- +- for (i = 0; i < n; i++) { +- if (packet_type_info[i].type == type) +- return packet_type_info[i].str; +- } +- +- return packet_type_info[n].str; +-} +-#endif /* DHD_RX_DUMP */ +- +- +-#ifdef DHD_WMF +-bool +-dhd_is_rxthread_enabled(dhd_pub_t *dhdp) +-{ +- dhd_info_t *dhd = dhdp->info; +- +- return dhd->rxthread_enabled; +-} +-#endif /* DHD_WMF */ +- +-void +-dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt, uint8 chan) +-{ +- dhd_info_t *dhd = (dhd_info_t *)dhdp->info; +- struct sk_buff *skb; +- uchar *eth; +- uint len; +- void *data, *pnext = NULL; +- int i; +- dhd_if_t *ifp; +- wl_event_msg_t event; +- int tout_rx = 0; +- int tout_ctrl = 0; +- void *skbhead = NULL; +- void *skbprev = NULL; +-#if defined(DHD_RX_DUMP) || defined(DHD_8021X_DUMP) +- char *dump_data; +- uint16 protocol; +-#endif /* DHD_RX_DUMP || DHD_8021X_DUMP */ +- +- DHD_TRACE(("%s: Enter\n", __FUNCTION__)); +- +- for (i = 0; pktbuf && i < numpkt; i++, pktbuf = pnext) { +- struct ether_header *eh; +-#ifdef WLBTAMP +- struct dot11_llc_snap_header *lsh; +-#endif +- +- pnext = PKTNEXT(dhdp->osh, pktbuf); +- PKTSETNEXT(dhdp->osh, pktbuf, NULL); +- +- ifp = dhd->iflist[ifidx]; +- if (ifp == NULL) { +- DHD_ERROR(("%s: ifp is NULL. drop packet\n", +- __FUNCTION__)); +- PKTCFREE(dhdp->osh, pktbuf, FALSE); +- continue; +- } +- +- eh = (struct ether_header *)PKTDATA(dhdp->osh, pktbuf); +- +- /* Dropping only data packets before registering net device to avoid kernel panic */ +-#ifndef PROP_TXSTATUS_VSDB +- if ((!ifp->net || ifp->net->reg_state != NETREG_REGISTERED) && +- (ntoh16(eh->ether_type) != ETHER_TYPE_BRCM)) +-#else +- if ((!ifp->net || ifp->net->reg_state != NETREG_REGISTERED || !dhd->pub.up) && +- (ntoh16(eh->ether_type) != ETHER_TYPE_BRCM)) +-#endif /* PROP_TXSTATUS_VSDB */ +- { +- DHD_ERROR(("%s: net device is NOT registered yet. drop packet\n", +- __FUNCTION__)); +- PKTCFREE(dhdp->osh, pktbuf, FALSE); +- continue; +- } +- +-#ifdef WLBTAMP +- lsh = (struct dot11_llc_snap_header *)&eh[1]; +- +- if ((ntoh16(eh->ether_type) < ETHER_TYPE_MIN) && +- (PKTLEN(dhdp->osh, pktbuf) >= RFC1042_HDR_LEN) && +- bcmp(lsh, BT_SIG_SNAP_MPROT, DOT11_LLC_SNAP_HDR_LEN - 2) == 0 && +- lsh->type == HTON16(BTA_PROT_L2CAP)) { +- amp_hci_ACL_data_t *ACL_data = (amp_hci_ACL_data_t *) +- ((uint8 *)eh + RFC1042_HDR_LEN); +- ACL_data = NULL; +- } +-#endif /* WLBTAMP */ +- +-#ifdef PROP_TXSTATUS +- if (dhd_wlfc_is_header_only_pkt(dhdp, pktbuf)) { +- /* WLFC may send header only packet when +- there is an urgent message but no packet to +- piggy-back on +- */ +- PKTCFREE(dhdp->osh, pktbuf, FALSE); +- continue; +- } +-#endif +-#ifdef DHD_L2_FILTER +- /* If block_ping is enabled drop the ping packet */ +- if (dhdp->block_ping) { +- if (dhd_l2_filter_block_ping(dhdp, pktbuf, ifidx) == BCME_OK) { +- PKTFREE(dhdp->osh, pktbuf, FALSE); +- continue; +- } +- } +-#endif +-#ifdef DHD_WMF +- /* WMF processing for multicast packets */ +- if (ifp->wmf.wmf_enable && (ETHER_ISMULTI(eh->ether_dhost))) { +- dhd_sta_t *sta; +- int ret; +- +- sta = dhd_find_sta(dhdp, ifidx, (void *)eh->ether_shost); +- ret = dhd_wmf_packets_handle(dhdp, pktbuf, sta, ifidx, 1); +- switch (ret) { +- case WMF_TAKEN: +- /* The packet is taken by WMF. Continue to next iteration */ +- continue; +- case WMF_DROP: +- /* Packet DROP decision by WMF. Toss it */ +- DHD_ERROR(("%s: WMF decides to drop packet\n", +- __FUNCTION__)); +- PKTCFREE(dhdp->osh, pktbuf, FALSE); +- continue; +- default: +- /* Continue the transmit path */ +- break; +- } +- } +-#endif /* DHD_WMF */ +-#ifdef DHDTCPACK_SUPPRESS +- dhd_tcpdata_info_get(dhdp, pktbuf); +-#endif +- skb = PKTTONATIVE(dhdp->osh, pktbuf); +- +- ifp = dhd->iflist[ifidx]; +- if (ifp == NULL) +- ifp = dhd->iflist[0]; +- +- ASSERT(ifp); +- skb->dev = ifp->net; +- +-#ifdef PCIE_FULL_DONGLE +- if ((DHD_IF_ROLE_AP(dhdp, ifidx) || DHD_IF_ROLE_P2PGO(dhdp, ifidx)) && +- (!ifp->ap_isolate)) { +- eh = (struct ether_header *)PKTDATA(dhdp->osh, pktbuf); +- if (ETHER_ISUCAST(eh->ether_dhost)) { +- if (dhd_find_sta(dhdp, ifidx, (void *)eh->ether_dhost)) { +- dhd_sendpkt(dhdp, ifidx, pktbuf); +- continue; +- } +- } else { +- void *npktbuf = PKTDUP(dhdp->osh, pktbuf); +- dhd_sendpkt(dhdp, ifidx, npktbuf); +- } +- } +-#endif /* PCIE_FULL_DONGLE */ +- +- /* Get the protocol, maintain skb around eth_type_trans() +- * The main reason for this hack is for the limitation of +- * Linux 2.4 where 'eth_type_trans' uses the 'net->hard_header_len' +- * to perform skb_pull inside vs ETH_HLEN. Since to avoid +- * coping of the packet coming from the network stack to add +- * BDC, Hardware header etc, during network interface registration +- * we set the 'net->hard_header_len' to ETH_HLEN + extra space required +- * for BDC, Hardware header etc. and not just the ETH_HLEN +- */ +- eth = skb->data; +- len = skb->len; +- +-#if defined(DHD_RX_DUMP) || defined(DHD_8021X_DUMP) +- dump_data = skb->data; +- protocol = (dump_data[12] << 8) | dump_data[13]; +- +- if (protocol == ETHER_TYPE_802_1X) { +- DHD_ERROR(("ETHER_TYPE_802_1X: " +- "ver %d, type %d, replay %d\n", +- dump_data[14], dump_data[15], +- dump_data[30])); +- } +-#endif /* DHD_RX_DUMP || DHD_8021X_DUMP */ +-#if defined(DHD_RX_DUMP) +- DHD_ERROR(("RX DUMP - %s\n", _get_packet_type_str(protocol))); +- if (protocol != ETHER_TYPE_BRCM) { +- if (dump_data[0] == 0xFF) { +- DHD_ERROR(("%s: BROADCAST\n", __FUNCTION__)); +- +- if ((dump_data[12] == 8) && +- (dump_data[13] == 6)) { +- DHD_ERROR(("%s: ARP %d\n", +- __FUNCTION__, dump_data[0x15])); +- } +- } else if (dump_data[0] & 1) { +- DHD_ERROR(("%s: MULTICAST: " MACDBG "\n", +- __FUNCTION__, MAC2STRDBG(dump_data))); +- } +-#ifdef DHD_RX_FULL_DUMP +- { +- int k; +- for (k = 0; k < skb->len; k++) { +- DHD_ERROR(("%02X ", dump_data[k])); +- if ((k & 15) == 15) +- DHD_ERROR(("\n")); +- } +- DHD_ERROR(("\n")); +- } +-#endif /* DHD_RX_FULL_DUMP */ +- } +-#endif /* DHD_RX_DUMP */ +- +- skb->protocol = eth_type_trans(skb, skb->dev); +- +- if (skb->pkt_type == PACKET_MULTICAST) { +- dhd->pub.rx_multicast++; +- ifp->stats.multicast++; +- } +- +- skb->data = eth; +- skb->len = len; +- +-#ifdef WLMEDIA_HTSF +- dhd_htsf_addrxts(dhdp, pktbuf); +-#endif +- /* Strip header, count, deliver upward */ +- skb_pull(skb, ETH_HLEN); +- +- /* Process special event packets and then discard them */ +- memset(&event, 0, sizeof(event)); +- if (ntoh16(skb->protocol) == ETHER_TYPE_BRCM) { +- dhd_wl_host_event(dhd, &ifidx, +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) +- skb_mac_header(skb), +-#else +- skb->mac.raw, +-#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) */ +- &event, +- &data); +- +- wl_event_to_host_order(&event); +- if (!tout_ctrl) +- tout_ctrl = DHD_PACKET_TIMEOUT_MS; +-#ifdef WLBTAMP +- if (event.event_type == WLC_E_BTA_HCI_EVENT) { +- dhd_bta_doevt(dhdp, data, event.datalen); +- } +-#endif /* WLBTAMP */ +- +-#if defined(PNO_SUPPORT) +- if (event.event_type == WLC_E_PFN_NET_FOUND) { +- /* enforce custom wake lock to garantee that Kernel not suspended */ +- tout_ctrl = CUSTOM_PNO_EVENT_LOCK_xTIME * DHD_PACKET_TIMEOUT_MS; +- } +-#endif /* PNO_SUPPORT */ +- +-#ifdef DHD_DONOT_FORWARD_BCMEVENT_AS_NETWORK_PKT +- PKTFREE(dhdp->osh, pktbuf, FALSE); +- continue; +-#endif /* DHD_DONOT_FORWARD_BCMEVENT_AS_NETWORK_PKT */ +- } else { +- tout_rx = DHD_PACKET_TIMEOUT_MS; +- +-#ifdef PROP_TXSTATUS +- dhd_wlfc_save_rxpath_ac_time(dhdp, (uint8)PKTPRIO(skb)); +-#endif /* PROP_TXSTATUS */ +- } +- +- ASSERT(ifidx < DHD_MAX_IFS && dhd->iflist[ifidx]); +- ifp = dhd->iflist[ifidx]; +- +- if (ifp->net) +- ifp->net->last_rx = jiffies; +- +- if (ntoh16(skb->protocol) != ETHER_TYPE_BRCM) { +- dhdp->dstats.rx_bytes += skb->len; +- dhdp->rx_packets++; /* Local count */ +- ifp->stats.rx_bytes += skb->len; +- ifp->stats.rx_packets++; +- } +-#if defined(DHD_TCP_WINSIZE_ADJUST) +- if (dhd_use_tcp_window_size_adjust) { +- if (ifidx == 0 && ntoh16(skb->protocol) == ETHER_TYPE_IP) { +- dhd_adjust_tcp_winsize(dhdp->op_mode, skb); +- } +- } +-#endif /* DHD_TCP_WINSIZE_ADJUST */ +- +- if (in_interrupt()) { +- netif_rx(skb); +- } else { +- if (dhd->rxthread_enabled) { +- if (!skbhead) +- skbhead = skb; +- else +- PKTSETNEXT(dhdp->osh, skbprev, skb); +- skbprev = skb; +- } else { +- +- /* If the receive is not processed inside an ISR, +- * the softirqd must be woken explicitly to service +- * the NET_RX_SOFTIRQ. In 2.6 kernels, this is handled +- * by netif_rx_ni(), but in earlier kernels, we need +- * to do it manually. +- */ +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) +- netif_rx_ni(skb); +-#else +- ulong flags; +- netif_rx(skb); +- local_irq_save(flags); +- RAISE_RX_SOFTIRQ(); +- local_irq_restore(flags); +-#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) */ +- } +- } +- } +- +- if (dhd->rxthread_enabled && skbhead) +- dhd_sched_rxf(dhdp, skbhead); +- +- DHD_OS_WAKE_LOCK_RX_TIMEOUT_ENABLE(dhdp, tout_rx); +- DHD_OS_WAKE_LOCK_CTRL_TIMEOUT_ENABLE(dhdp, tout_ctrl); +-} +- +-void +-dhd_event(struct dhd_info *dhd, char *evpkt, int evlen, int ifidx) +-{ +- /* Linux version has nothing to do */ +- return; +-} +- +-void +-dhd_txcomplete(dhd_pub_t *dhdp, void *txp, bool success) +-{ +- dhd_info_t *dhd = (dhd_info_t *)(dhdp->info); +- struct ether_header *eh; +- uint16 type; +-#ifdef WLBTAMP +- uint len; +-#endif +- +- dhd_prot_hdrpull(dhdp, NULL, txp, NULL, NULL); +- +- eh = (struct ether_header *)PKTDATA(dhdp->osh, txp); +- type = ntoh16(eh->ether_type); +- +- if (type == ETHER_TYPE_802_1X) +- atomic_dec(&dhd->pend_8021x_cnt); +- +-#ifdef WLBTAMP +- /* Crack open the packet and check to see if it is BT HCI ACL data packet. +- * If yes generate packet completion event. +- */ +- len = PKTLEN(dhdp->osh, txp); +- +- /* Generate ACL data tx completion event locally to avoid SDIO bus transaction */ +- if ((type < ETHER_TYPE_MIN) && (len >= RFC1042_HDR_LEN)) { +- struct dot11_llc_snap_header *lsh = (struct dot11_llc_snap_header *)&eh[1]; +- +- if (bcmp(lsh, BT_SIG_SNAP_MPROT, DOT11_LLC_SNAP_HDR_LEN - 2) == 0 && +- ntoh16(lsh->type) == BTA_PROT_L2CAP) { +- +- dhd_bta_tx_hcidata_complete(dhdp, txp, success); +- } +- } +-#endif /* WLBTAMP */ +-} +- +-static struct net_device_stats * +-dhd_get_stats(struct net_device *net) +-{ +- dhd_info_t *dhd = DHD_DEV_INFO(net); +- dhd_if_t *ifp; +- int ifidx; +- +- DHD_TRACE(("%s: Enter\n", __FUNCTION__)); +- +- ifidx = dhd_net2idx(dhd, net); +- if (ifidx == DHD_BAD_IF) { +- DHD_ERROR(("%s: BAD_IF\n", __FUNCTION__)); +- +- memset(&net->stats, 0, sizeof(net->stats)); +- return &net->stats; +- } +- +- ifp = dhd->iflist[ifidx]; +- ASSERT(dhd && ifp); +- +- if (dhd->pub.up) { +- /* Use the protocol to get dongle stats */ +- dhd_prot_dstats(&dhd->pub); +- } +- return &ifp->stats; +-} +- +-static int +-dhd_watchdog_thread(void *data) +-{ +- tsk_ctl_t *tsk = (tsk_ctl_t *)data; +- dhd_info_t *dhd = (dhd_info_t *)tsk->parent; +- /* This thread doesn't need any user-level access, +- * so get rid of all our resources +- */ +- if (dhd_watchdog_prio > 0) { +- struct sched_param param; +- param.sched_priority = (dhd_watchdog_prio < MAX_RT_PRIO)? +- dhd_watchdog_prio:(MAX_RT_PRIO-1); +- setScheduler(current, SCHED_FIFO, ¶m); +- } +- +- while (1) +- if (down_interruptible (&tsk->sema) == 0) { +- unsigned long flags; +- unsigned long jiffies_at_start = jiffies; +- unsigned long time_lapse; +- +- SMP_RD_BARRIER_DEPENDS(); +- if (tsk->terminated) { +- break; +- } +- +- if (dhd->pub.dongle_reset == FALSE) { +- DHD_TIMER(("%s:\n", __FUNCTION__)); +- +- /* Call the bus module watchdog */ +- dhd_bus_watchdog(&dhd->pub); +- +- +- DHD_GENERAL_LOCK(&dhd->pub, flags); +- /* Count the tick for reference */ +- dhd->pub.tickcnt++; +- time_lapse = jiffies - jiffies_at_start; +- +- /* Reschedule the watchdog */ +- if (dhd->wd_timer_valid) +- mod_timer(&dhd->timer, +- jiffies + +- msecs_to_jiffies(dhd_watchdog_ms) - +- min(msecs_to_jiffies(dhd_watchdog_ms), time_lapse)); +- DHD_GENERAL_UNLOCK(&dhd->pub, flags); +- } +- } else { +- break; +- } +- +- complete_and_exit(&tsk->completed, 0); +-} +- +-static void dhd_watchdog(ulong data) +-{ +- dhd_info_t *dhd = (dhd_info_t *)data; +- unsigned long flags; +- +- if (dhd->pub.dongle_reset) { +- return; +- } +- +- if (dhd->thr_wdt_ctl.thr_pid >= 0) { +- up(&dhd->thr_wdt_ctl.sema); +- return; +- } +- +- /* Call the bus module watchdog */ +- dhd_bus_watchdog(&dhd->pub); +- +- DHD_GENERAL_LOCK(&dhd->pub, flags); +- /* Count the tick for reference */ +- dhd->pub.tickcnt++; +- +- /* Reschedule the watchdog */ +- if (dhd->wd_timer_valid) +- mod_timer(&dhd->timer, jiffies + msecs_to_jiffies(dhd_watchdog_ms)); +- DHD_GENERAL_UNLOCK(&dhd->pub, flags); +- +-} +- +-#ifdef ENABLE_ADAPTIVE_SCHED +-static void +-dhd_sched_policy(int prio) +-{ +- struct sched_param param; +- if (cpufreq_quick_get(0) <= CUSTOM_CPUFREQ_THRESH) { +- param.sched_priority = 0; +- setScheduler(current, SCHED_NORMAL, ¶m); +- } else { +- if (get_scheduler_policy(current) != SCHED_FIFO) { +- param.sched_priority = (prio < MAX_RT_PRIO)? prio : (MAX_RT_PRIO-1); +- setScheduler(current, SCHED_FIFO, ¶m); +- } +- } +-} +-#endif /* ENABLE_ADAPTIVE_SCHED */ +-#ifdef DEBUG_CPU_FREQ +-static int dhd_cpufreq_notifier(struct notifier_block *nb, unsigned long val, void *data) +-{ +- dhd_info_t *dhd = container_of(nb, struct dhd_info, freq_trans); +- struct cpufreq_freqs *freq = data; +- if (dhd) { +- if (!dhd->new_freq) +- goto exit; +- if (val == CPUFREQ_POSTCHANGE) { +- DHD_ERROR(("cpu freq is changed to %u kHZ on CPU %d\n", +- freq->new, freq->cpu)); +- *per_cpu_ptr(dhd->new_freq, freq->cpu) = freq->new; +- } +- } +-exit: +- return 0; +-} +-#endif /* DEBUG_CPU_FREQ */ +-static int +-dhd_dpc_thread(void *data) +-{ +- tsk_ctl_t *tsk = (tsk_ctl_t *)data; +- dhd_info_t *dhd = (dhd_info_t *)tsk->parent; +- +- /* This thread doesn't need any user-level access, +- * so get rid of all our resources +- */ +- if (dhd_dpc_prio > 0) +- { +- struct sched_param param; +- param.sched_priority = (dhd_dpc_prio < MAX_RT_PRIO)?dhd_dpc_prio:(MAX_RT_PRIO-1); +- setScheduler(current, SCHED_FIFO, ¶m); +- } +- +-#ifdef CUSTOM_DPC_CPUCORE +- set_cpus_allowed_ptr(current, cpumask_of(CUSTOM_DPC_CPUCORE)); +-#endif +-#ifdef CUSTOM_SET_CPUCORE +- dhd->pub.current_dpc = current; +-#endif /* CUSTOM_SET_CPUCORE */ +- +- /* Run until signal received */ +- while (1) { +- if (!binary_sema_down(tsk)) { +-#ifdef ENABLE_ADAPTIVE_SCHED +- dhd_sched_policy(dhd_dpc_prio); +-#endif /* ENABLE_ADAPTIVE_SCHED */ +- SMP_RD_BARRIER_DEPENDS(); +- if (tsk->terminated) { +- break; +- } +- +- /* Call bus dpc unless it indicated down (then clean stop) */ +- if (dhd->pub.busstate != DHD_BUS_DOWN) { +- dhd_os_wd_timer_extend(&dhd->pub, TRUE); +- while (dhd_bus_dpc(dhd->pub.bus)) { +- /* process all data */ +- } +- dhd_os_wd_timer_extend(&dhd->pub, FALSE); +- DHD_OS_WAKE_UNLOCK(&dhd->pub); +- +- } else { +- if (dhd->pub.up) +- dhd_bus_stop(dhd->pub.bus, TRUE); +- DHD_OS_WAKE_UNLOCK(&dhd->pub); +- } +- } +- else +- break; +- } +- +- complete_and_exit(&tsk->completed, 0); +-} +- +-static int +-dhd_rxf_thread(void *data) +-{ +- tsk_ctl_t *tsk = (tsk_ctl_t *)data; +- dhd_info_t *dhd = (dhd_info_t *)tsk->parent; +-#if defined(WAIT_DEQUEUE) +-#define RXF_WATCHDOG_TIME 250 /* BARK_TIME(1000) / */ +- ulong watchdogTime = OSL_SYSUPTIME(); /* msec */ +-#endif +- dhd_pub_t *pub = &dhd->pub; +- +- /* This thread doesn't need any user-level access, +- * so get rid of all our resources +- */ +- if (dhd_rxf_prio > 0) +- { +- struct sched_param param; +- param.sched_priority = (dhd_rxf_prio < MAX_RT_PRIO)?dhd_rxf_prio:(MAX_RT_PRIO-1); +- setScheduler(current, SCHED_FIFO, ¶m); +- } +- +- DAEMONIZE("dhd_rxf"); +- /* DHD_OS_WAKE_LOCK is called in dhd_sched_dpc[dhd_linux.c] down below */ +- +- /* signal: thread has started */ +- complete(&tsk->completed); +-#ifdef CUSTOM_SET_CPUCORE +- dhd->pub.current_rxf = current; +-#endif /* CUSTOM_SET_CPUCORE */ +- +- /* Run until signal received */ +- while (1) { +- if (down_interruptible(&tsk->sema) == 0) { +- void *skb; +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0) +- ulong flags; +-#endif +-#ifdef ENABLE_ADAPTIVE_SCHED +- dhd_sched_policy(dhd_rxf_prio); +-#endif /* ENABLE_ADAPTIVE_SCHED */ +- +- SMP_RD_BARRIER_DEPENDS(); +- +- if (tsk->terminated) { +- break; +- } +- skb = dhd_rxf_dequeue(pub); +- +- if (skb == NULL) { +- continue; +- } +- while (skb) { +- void *skbnext = PKTNEXT(pub->osh, skb); +- PKTSETNEXT(pub->osh, skb, NULL); +- +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) +- netif_rx_ni(skb); +-#else +- netif_rx(skb); +- local_irq_save(flags); +- RAISE_RX_SOFTIRQ(); +- local_irq_restore(flags); +- +-#endif +- skb = skbnext; +- } +-#if defined(WAIT_DEQUEUE) +- if (OSL_SYSUPTIME() - watchdogTime > RXF_WATCHDOG_TIME) { +- OSL_SLEEP(1); +- watchdogTime = OSL_SYSUPTIME(); +- } +-#endif +- +- DHD_OS_WAKE_UNLOCK(pub); +- } +- else +- break; +- } +- +- complete_and_exit(&tsk->completed, 0); +-} +- +-#ifdef BCMPCIE +-void dhd_dpc_kill(dhd_pub_t *dhdp) +-{ +- dhd_info_t *dhd; +- +- if (!dhdp) +- return; +- +- dhd = dhdp->info; +- +- if (!dhd) +- return; +- +- tasklet_kill(&dhd->tasklet); +- DHD_ERROR(("%s: tasklet disabled\n", __FUNCTION__)); +-} +-#endif /* BCMPCIE */ +- +-static void +-dhd_dpc(ulong data) +-{ +- dhd_info_t *dhd; +- +- dhd = (dhd_info_t *)data; +- +- /* this (tasklet) can be scheduled in dhd_sched_dpc[dhd_linux.c] +- * down below , wake lock is set, +- * the tasklet is initialized in dhd_attach() +- */ +- /* Call bus dpc unless it indicated down (then clean stop) */ +- if (dhd->pub.busstate != DHD_BUS_DOWN) { +- if (dhd_bus_dpc(dhd->pub.bus)) +- tasklet_schedule(&dhd->tasklet); +- else +- DHD_OS_WAKE_UNLOCK(&dhd->pub); +- } else { +- dhd_bus_stop(dhd->pub.bus, TRUE); +- DHD_OS_WAKE_UNLOCK(&dhd->pub); +- } +-} +- +-void +-dhd_sched_dpc(dhd_pub_t *dhdp) +-{ +- dhd_info_t *dhd = (dhd_info_t *)dhdp->info; +- +- DHD_OS_WAKE_LOCK(dhdp); +- if (dhd->thr_dpc_ctl.thr_pid >= 0) { +- /* If the semaphore does not get up, +- * wake unlock should be done here +- */ +- if (!binary_sema_up(&dhd->thr_dpc_ctl)) +- DHD_OS_WAKE_UNLOCK(dhdp); +- return; +- } else { +- tasklet_schedule(&dhd->tasklet); +- } +-} +- +-static void +-dhd_sched_rxf(dhd_pub_t *dhdp, void *skb) +-{ +- dhd_info_t *dhd = (dhd_info_t *)dhdp->info; +-#ifdef RXF_DEQUEUE_ON_BUSY +- int ret = BCME_OK; +- int retry = 2; +-#endif /* RXF_DEQUEUE_ON_BUSY */ +- +- DHD_OS_WAKE_LOCK(dhdp); +- +- DHD_TRACE(("dhd_sched_rxf: Enter\n")); +-#ifdef RXF_DEQUEUE_ON_BUSY +- do { +- ret = dhd_rxf_enqueue(dhdp, skb); +- if (ret == BCME_OK || ret == BCME_ERROR) +- break; +- else +- OSL_SLEEP(50); /* waiting for dequeueing */ +- } while (retry-- > 0); +- +- if (retry <= 0 && ret == BCME_BUSY) { +- void *skbp = skb; +- +- while (skbp) { +- void *skbnext = PKTNEXT(dhdp->osh, skbp); +- PKTSETNEXT(dhdp->osh, skbp, NULL); +- netif_rx_ni(skbp); +- skbp = skbnext; +- } +- DHD_ERROR(("send skb to kernel backlog without rxf_thread\n")); +- } +- else { +- if (dhd->thr_rxf_ctl.thr_pid >= 0) { +- up(&dhd->thr_rxf_ctl.sema); +- } +- } +-#else /* RXF_DEQUEUE_ON_BUSY */ +- do { +- if (dhd_rxf_enqueue(dhdp, skb) == BCME_OK) +- break; +- } while (1); +- if (dhd->thr_rxf_ctl.thr_pid >= 0) { +- up(&dhd->thr_rxf_ctl.sema); +- } +- return; +-#endif /* RXF_DEQUEUE_ON_BUSY */ +-} +- +-#ifdef TOE +-/* Retrieve current toe component enables, which are kept as a bitmap in toe_ol iovar */ +-static int +-dhd_toe_get(dhd_info_t *dhd, int ifidx, uint32 *toe_ol) +-{ +- wl_ioctl_t ioc; +- char buf[32]; +- int ret; +- +- memset(&ioc, 0, sizeof(ioc)); +- +- ioc.cmd = WLC_GET_VAR; +- ioc.buf = buf; +- ioc.len = (uint)sizeof(buf); +- ioc.set = FALSE; +- +- strncpy(buf, "toe_ol", sizeof(buf) - 1); +- buf[sizeof(buf) - 1] = '\0'; +- if ((ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len)) < 0) { +- /* Check for older dongle image that doesn't support toe_ol */ +- if (ret == -EIO) { +- DHD_ERROR(("%s: toe not supported by device\n", +- dhd_ifname(&dhd->pub, ifidx))); +- return -EOPNOTSUPP; +- } +- +- DHD_INFO(("%s: could not get toe_ol: ret=%d\n", dhd_ifname(&dhd->pub, ifidx), ret)); +- return ret; +- } +- +- memcpy(toe_ol, buf, sizeof(uint32)); +- return 0; +-} +- +-/* Set current toe component enables in toe_ol iovar, and set toe global enable iovar */ +-static int +-dhd_toe_set(dhd_info_t *dhd, int ifidx, uint32 toe_ol) +-{ +- wl_ioctl_t ioc; +- char buf[32]; +- int toe, ret; +- +- memset(&ioc, 0, sizeof(ioc)); +- +- ioc.cmd = WLC_SET_VAR; +- ioc.buf = buf; +- ioc.len = (uint)sizeof(buf); +- ioc.set = TRUE; +- +- /* Set toe_ol as requested */ +- +- strncpy(buf, "toe_ol", sizeof(buf) - 1); +- buf[sizeof(buf) - 1] = '\0'; +- memcpy(&buf[sizeof("toe_ol")], &toe_ol, sizeof(uint32)); +- +- if ((ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len)) < 0) { +- DHD_ERROR(("%s: could not set toe_ol: ret=%d\n", +- dhd_ifname(&dhd->pub, ifidx), ret)); +- return ret; +- } +- +- /* Enable toe globally only if any components are enabled. */ +- +- toe = (toe_ol != 0); +- +- strcpy(buf, "toe"); +- memcpy(&buf[sizeof("toe")], &toe, sizeof(uint32)); +- +- if ((ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len)) < 0) { +- DHD_ERROR(("%s: could not set toe: ret=%d\n", dhd_ifname(&dhd->pub, ifidx), ret)); +- return ret; +- } +- +- return 0; +-} +-#endif /* TOE */ +- +-#if defined(WL_CFG80211) +-void dhd_set_scb_probe(dhd_pub_t *dhd) +-{ +-#define NUM_SCB_MAX_PROBE 3 +- int ret = 0; +- wl_scb_probe_t scb_probe; +- char iovbuf[WL_EVENTING_MASK_LEN + 12]; +- +- memset(&scb_probe, 0, sizeof(wl_scb_probe_t)); +- +- if (dhd->op_mode & DHD_FLAG_HOSTAP_MODE) +- return; +- +- bcm_mkiovar("scb_probe", NULL, 0, iovbuf, sizeof(iovbuf)); +- +- if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) +- DHD_ERROR(("%s: GET max_scb_probe failed\n", __FUNCTION__)); +- +- memcpy(&scb_probe, iovbuf, sizeof(wl_scb_probe_t)); +- +- scb_probe.scb_max_probe = NUM_SCB_MAX_PROBE; +- +- bcm_mkiovar("scb_probe", (char *)&scb_probe, +- sizeof(wl_scb_probe_t), iovbuf, sizeof(iovbuf)); +- if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) +- DHD_ERROR(("%s: max_scb_probe setting failed\n", __FUNCTION__)); +-#undef NUM_SCB_MAX_PROBE +- return; +-} +-#endif /* WL_CFG80211 */ +- +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) +-static void +-dhd_ethtool_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info) +-{ +- dhd_info_t *dhd = DHD_DEV_INFO(net); +- +- snprintf(info->driver, sizeof(info->driver), "wl"); +- snprintf(info->version, sizeof(info->version), "%lu", dhd->pub.drv_version); +-} +- +-struct ethtool_ops dhd_ethtool_ops = { +- .get_drvinfo = dhd_ethtool_get_drvinfo +-}; +-#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) */ +- +- +-#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 2) +-static int +-dhd_ethtool(dhd_info_t *dhd, void *uaddr) +-{ +- struct ethtool_drvinfo info; +- char drvname[sizeof(info.driver)]; +- uint32 cmd; +-#ifdef TOE +- struct ethtool_value edata; +- uint32 toe_cmpnt, csum_dir; +- int ret; +-#endif +- +- DHD_TRACE(("%s: Enter\n", __FUNCTION__)); +- +- /* all ethtool calls start with a cmd word */ +- if (copy_from_user(&cmd, uaddr, sizeof (uint32))) +- return -EFAULT; +- +- switch (cmd) { +- case ETHTOOL_GDRVINFO: +- /* Copy out any request driver name */ +- if (copy_from_user(&info, uaddr, sizeof(info))) +- return -EFAULT; +- strncpy(drvname, info.driver, sizeof(info.driver)); +- drvname[sizeof(info.driver)-1] = '\0'; +- +- /* clear struct for return */ +- memset(&info, 0, sizeof(info)); +- info.cmd = cmd; +- +- /* if dhd requested, identify ourselves */ +- if (strcmp(drvname, "?dhd") == 0) { +- snprintf(info.driver, sizeof(info.driver), "dhd"); +- strncpy(info.version, EPI_VERSION_STR, sizeof(info.version) - 1); +- info.version[sizeof(info.version) - 1] = '\0'; +- } +- +- /* otherwise, require dongle to be up */ +- else if (!dhd->pub.up) { +- DHD_ERROR(("%s: dongle is not up\n", __FUNCTION__)); +- return -ENODEV; +- } +- +- /* finally, report dongle driver type */ +- else if (dhd->pub.iswl) +- snprintf(info.driver, sizeof(info.driver), "wl"); +- else +- snprintf(info.driver, sizeof(info.driver), "xx"); +- +- snprintf(info.version, sizeof(info.version), "%lu", dhd->pub.drv_version); +- if (copy_to_user(uaddr, &info, sizeof(info))) +- return -EFAULT; +- DHD_CTL(("%s: given %*s, returning %s\n", __FUNCTION__, +- (int)sizeof(drvname), drvname, info.driver)); +- break; +- +-#ifdef TOE +- /* Get toe offload components from dongle */ +- case ETHTOOL_GRXCSUM: +- case ETHTOOL_GTXCSUM: +- if ((ret = dhd_toe_get(dhd, 0, &toe_cmpnt)) < 0) +- return ret; +- +- csum_dir = (cmd == ETHTOOL_GTXCSUM) ? TOE_TX_CSUM_OL : TOE_RX_CSUM_OL; +- +- edata.cmd = cmd; +- edata.data = (toe_cmpnt & csum_dir) ? 1 : 0; +- +- if (copy_to_user(uaddr, &edata, sizeof(edata))) +- return -EFAULT; +- break; +- +- /* Set toe offload components in dongle */ +- case ETHTOOL_SRXCSUM: +- case ETHTOOL_STXCSUM: +- if (copy_from_user(&edata, uaddr, sizeof(edata))) +- return -EFAULT; +- +- /* Read the current settings, update and write back */ +- if ((ret = dhd_toe_get(dhd, 0, &toe_cmpnt)) < 0) +- return ret; +- +- csum_dir = (cmd == ETHTOOL_STXCSUM) ? TOE_TX_CSUM_OL : TOE_RX_CSUM_OL; +- +- if (edata.data != 0) +- toe_cmpnt |= csum_dir; +- else +- toe_cmpnt &= ~csum_dir; +- +- if ((ret = dhd_toe_set(dhd, 0, toe_cmpnt)) < 0) +- return ret; +- +- /* If setting TX checksum mode, tell Linux the new mode */ +- if (cmd == ETHTOOL_STXCSUM) { +- if (edata.data) +- dhd->iflist[0]->net->features |= NETIF_F_IP_CSUM; +- else +- dhd->iflist[0]->net->features &= ~NETIF_F_IP_CSUM; +- } +- +- break; +-#endif /* TOE */ +- +- default: +- return -EOPNOTSUPP; +- } +- +- return 0; +-} +-#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 2) */ +- +-static bool dhd_check_hang(struct net_device *net, dhd_pub_t *dhdp, int error) +-{ +- dhd_info_t *dhd; +- +- if (!dhdp) { +- DHD_ERROR(("%s: dhdp is NULL\n", __FUNCTION__)); +- return FALSE; +- } +- +- if (!dhdp->up) +- return FALSE; +- +- dhd = (dhd_info_t *)dhdp->info; +-#if !defined(BCMPCIE) +- if (dhd->thr_dpc_ctl.thr_pid < 0) { +- DHD_ERROR(("%s : skipped due to negative pid - unloading?\n", __FUNCTION__)); +- return FALSE; +- } +-#endif +- +- if ((error == -ETIMEDOUT) || (error == -EREMOTEIO) || +- ((dhdp->busstate == DHD_BUS_DOWN) && (!dhdp->dongle_reset))) { +- DHD_ERROR(("%s: Event HANG send up due to re=%d te=%d e=%d s=%d\n", __FUNCTION__, +- dhdp->rxcnt_timeout, dhdp->txcnt_timeout, error, dhdp->busstate)); +- net_os_send_hang_message(net); +- return TRUE; +- } +- return FALSE; +-} +- +-int dhd_ioctl_process(dhd_pub_t *pub, int ifidx, dhd_ioctl_t *ioc, void *data_buf) +-{ +- int bcmerror = BCME_OK; +- int buflen = 0; +- struct net_device *net; +- +- net = dhd_idx2net(pub, ifidx); +- if (!net) { +- bcmerror = BCME_BADARG; +- goto done; +- } +- +- if (data_buf) +- buflen = MIN(ioc->len, DHD_IOCTL_MAXLEN); +- +- /* check for local dhd ioctl and handle it */ +- if (ioc->driver == DHD_IOCTL_MAGIC) { +- bcmerror = dhd_ioctl((void *)pub, ioc, data_buf, buflen); +- if (bcmerror) +- pub->bcmerror = bcmerror; +- goto done; +- } +- +- /* send to dongle (must be up, and wl). */ +- if (pub->busstate != DHD_BUS_DATA) { +- bcmerror = BCME_DONGLE_DOWN; +- goto done; +- } +- +- if (!pub->iswl) { +- bcmerror = BCME_DONGLE_DOWN; +- goto done; +- } +- +- /* +- * Flush the TX queue if required for proper message serialization: +- * Intercept WLC_SET_KEY IOCTL - serialize M4 send and set key IOCTL to +- * prevent M4 encryption and +- * intercept WLC_DISASSOC IOCTL - serialize WPS-DONE and WLC_DISASSOC IOCTL to +- * prevent disassoc frame being sent before WPS-DONE frame. +- */ +- if (ioc->cmd == WLC_SET_KEY || +- (ioc->cmd == WLC_SET_VAR && data_buf != NULL && +- strncmp("wsec_key", data_buf, 9) == 0) || +- (ioc->cmd == WLC_SET_VAR && data_buf != NULL && +- strncmp("bsscfg:wsec_key", data_buf, 15) == 0) || +- ioc->cmd == WLC_DISASSOC) +- dhd_wait_pend8021x(net); +- +-#ifdef WLMEDIA_HTSF +- if (data_buf) { +- /* short cut wl ioctl calls here */ +- if (strcmp("htsf", data_buf) == 0) { +- dhd_ioctl_htsf_get(dhd, 0); +- return BCME_OK; +- } +- +- if (strcmp("htsflate", data_buf) == 0) { +- if (ioc->set) { +- memset(ts, 0, sizeof(tstamp_t)*TSMAX); +- memset(&maxdelayts, 0, sizeof(tstamp_t)); +- maxdelay = 0; +- tspktcnt = 0; +- maxdelaypktno = 0; +- memset(&vi_d1.bin, 0, sizeof(uint32)*NUMBIN); +- memset(&vi_d2.bin, 0, sizeof(uint32)*NUMBIN); +- memset(&vi_d3.bin, 0, sizeof(uint32)*NUMBIN); +- memset(&vi_d4.bin, 0, sizeof(uint32)*NUMBIN); +- } else { +- dhd_dump_latency(); +- } +- return BCME_OK; +- } +- if (strcmp("htsfclear", data_buf) == 0) { +- memset(&vi_d1.bin, 0, sizeof(uint32)*NUMBIN); +- memset(&vi_d2.bin, 0, sizeof(uint32)*NUMBIN); +- memset(&vi_d3.bin, 0, sizeof(uint32)*NUMBIN); +- memset(&vi_d4.bin, 0, sizeof(uint32)*NUMBIN); +- htsf_seqnum = 0; +- return BCME_OK; +- } +- if (strcmp("htsfhis", data_buf) == 0) { +- dhd_dump_htsfhisto(&vi_d1, "H to D"); +- dhd_dump_htsfhisto(&vi_d2, "D to D"); +- dhd_dump_htsfhisto(&vi_d3, "D to H"); +- dhd_dump_htsfhisto(&vi_d4, "H to H"); +- return BCME_OK; +- } +- if (strcmp("tsport", data_buf) == 0) { +- if (ioc->set) { +- memcpy(&tsport, data_buf + 7, 4); +- } else { +- DHD_ERROR(("current timestamp port: %d \n", tsport)); +- } +- return BCME_OK; +- } +- } +-#endif /* WLMEDIA_HTSF */ +- +- if ((ioc->cmd == WLC_SET_VAR || ioc->cmd == WLC_GET_VAR) && +- data_buf != NULL && strncmp("rpc_", data_buf, 4) == 0) { +-#ifdef BCM_FD_AGGR +- bcmerror = dhd_fdaggr_ioctl(pub, ifidx, (wl_ioctl_t *)ioc, data_buf, buflen); +-#else +- bcmerror = BCME_UNSUPPORTED; +-#endif +- goto done; +- } +- bcmerror = dhd_wl_ioctl(pub, ifidx, (wl_ioctl_t *)ioc, data_buf, buflen); +- +-done: +- dhd_check_hang(net, pub, bcmerror); +- +- return bcmerror; +-} +- +-static int +-dhd_ioctl_entry(struct net_device *net, struct ifreq *ifr, int cmd) +-{ +- dhd_info_t *dhd = DHD_DEV_INFO(net); +- dhd_ioctl_t ioc; +- int bcmerror = 0; +- int ifidx; +- int ret; +- void *local_buf = NULL; +- u16 buflen = 0; +- +- DHD_OS_WAKE_LOCK(&dhd->pub); +- DHD_PERIM_LOCK(&dhd->pub); +- +- /* Interface up check for built-in type */ +- if (!dhd_download_fw_on_driverload && dhd->pub.up == 0) { +- DHD_ERROR(("%s: Interface is down \n", __FUNCTION__)); +- DHD_PERIM_UNLOCK(&dhd->pub); +- DHD_OS_WAKE_UNLOCK(&dhd->pub); +- return BCME_NOTUP; +- } +- +- /* 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\n", __FUNCTION__)); +- DHD_OS_WAKE_LOCK_CTRL_TIMEOUT_ENABLE(&dhd->pub, DHD_EVENT_TIMEOUT_MS); +- DHD_OS_WAKE_UNLOCK(&dhd->pub); +- return OSL_ERROR(BCME_DONGLE_DOWN); +- } +- +- ifidx = dhd_net2idx(dhd, net); +- DHD_TRACE(("%s: ifidx %d, cmd 0x%04x\n", __FUNCTION__, ifidx, cmd)); +- +- if (ifidx == DHD_BAD_IF) { +- DHD_ERROR(("%s: BAD IF\n", __FUNCTION__)); +- DHD_PERIM_UNLOCK(&dhd->pub); +- DHD_OS_WAKE_UNLOCK(&dhd->pub); +- return -1; +- } +- +-#if defined(WL_WIRELESS_EXT) +- /* linux wireless extensions */ +- if ((cmd >= SIOCIWFIRST) && (cmd <= SIOCIWLAST)) { +- /* may recurse, do NOT lock */ +- ret = wl_iw_ioctl(net, ifr, cmd); +- DHD_PERIM_UNLOCK(&dhd->pub); +- DHD_OS_WAKE_UNLOCK(&dhd->pub); +- return ret; +- } +-#endif /* defined(WL_WIRELESS_EXT) */ +- +-#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 2) +- if (cmd == SIOCETHTOOL) { +- ret = dhd_ethtool(dhd, (void*)ifr->ifr_data); +- DHD_PERIM_UNLOCK(&dhd->pub); +- DHD_OS_WAKE_UNLOCK(&dhd->pub); +- return ret; +- } +-#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 2) */ +- +- if (cmd == SIOCDEVPRIVATE+1) { +- ret = wl_android_priv_cmd(net, ifr, cmd); +- dhd_check_hang(net, &dhd->pub, ret); +- DHD_OS_WAKE_UNLOCK(&dhd->pub); +- return ret; +- } +- +- if (cmd != SIOCDEVPRIVATE) { +- DHD_PERIM_UNLOCK(&dhd->pub); +- DHD_OS_WAKE_UNLOCK(&dhd->pub); +- return -EOPNOTSUPP; +- } +- +- memset(&ioc, 0, sizeof(ioc)); +- +-#ifdef CONFIG_COMPAT +- if (is_compat_task()) { +- compat_wl_ioctl_t compat_ioc; +- if (copy_from_user(&compat_ioc, ifr->ifr_data, sizeof(compat_wl_ioctl_t))) { +- bcmerror = BCME_BADADDR; +- goto done; +- } +- ioc.cmd = compat_ioc.cmd; +- ioc.buf = compat_ptr(compat_ioc.buf); +- ioc.len = compat_ioc.len; +- ioc.set = compat_ioc.set; +- ioc.used = compat_ioc.used; +- ioc.needed = compat_ioc.needed; +- /* To differentiate between wl and dhd read 4 more byes */ +- if ((copy_from_user(&ioc.driver, (char *)ifr->ifr_data + sizeof(compat_wl_ioctl_t), +- sizeof(uint)) != 0)) { +- bcmerror = BCME_BADADDR; +- goto done; +- } +- } else +-#endif /* CONFIG_COMPAT */ +- { +- /* Copy the ioc control structure part of ioctl request */ +- if (copy_from_user(&ioc, ifr->ifr_data, sizeof(wl_ioctl_t))) { +- bcmerror = BCME_BADADDR; +- goto done; +- } +- +- /* To differentiate between wl and dhd read 4 more byes */ +- if ((copy_from_user(&ioc.driver, (char *)ifr->ifr_data + sizeof(wl_ioctl_t), +- sizeof(uint)) != 0)) { +- bcmerror = BCME_BADADDR; +- goto done; +- } +- } +- +- if (!capable(CAP_NET_ADMIN)) { +- bcmerror = BCME_EPERM; +- goto done; +- } +- +- if (ioc.len > 0) { +- buflen = MIN(ioc.len, DHD_IOCTL_MAXLEN); +- if (!(local_buf = MALLOC(dhd->pub.osh, buflen+1))) { +- bcmerror = BCME_NOMEM; +- goto done; +- } +- +- DHD_PERIM_UNLOCK(&dhd->pub); +- if (copy_from_user(local_buf, ioc.buf, buflen)) { +- DHD_PERIM_LOCK(&dhd->pub); +- bcmerror = BCME_BADADDR; +- goto done; +- } +- DHD_PERIM_LOCK(&dhd->pub); +- +- *(char *)(local_buf + buflen) = '\0'; +- } +- +- bcmerror = dhd_ioctl_process(&dhd->pub, ifidx, &ioc, local_buf); +- +- if (!bcmerror && buflen && local_buf && ioc.buf) { +- DHD_PERIM_UNLOCK(&dhd->pub); +- if (copy_to_user(ioc.buf, local_buf, buflen)) +- bcmerror = -EFAULT; +- DHD_PERIM_LOCK(&dhd->pub); +- } +- +-done: +- if (local_buf) +- MFREE(dhd->pub.osh, local_buf, buflen+1); +- +- DHD_PERIM_UNLOCK(&dhd->pub); +- DHD_OS_WAKE_UNLOCK(&dhd->pub); +- +- return OSL_ERROR(bcmerror); +-} +- +- +- +-static int +-dhd_stop(struct net_device *net) +-{ +- int ifidx = 0; +- dhd_info_t *dhd = DHD_DEV_INFO(net); +- DHD_OS_WAKE_LOCK(&dhd->pub); +- DHD_PERIM_LOCK(&dhd->pub); +- printk("%s: Enter %p\n", __FUNCTION__, net); +- if (dhd->pub.up == 0) { +- goto exit; +- } +- +- dhd_if_flush_sta(DHD_DEV_IFP(net)); +- +- +- ifidx = dhd_net2idx(dhd, net); +- BCM_REFERENCE(ifidx); +- +- /* Set state and stop OS transmissions */ +- netif_stop_queue(net); +- dhd->pub.up = 0; +- +-#ifdef WL_CFG80211 +- if (ifidx == 0) { +- wl_cfg80211_down(NULL); +- +- /* +- * For CFG80211: Clean up all the left over virtual interfaces +- * when the primary Interface is brought down. [ifconfig wlan0 down] +- */ +- if (!dhd_download_fw_on_driverload) { +- if ((dhd->dhd_state & DHD_ATTACH_STATE_ADD_IF) && +- (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211)) { +- int i; +- +- dhd_net_if_lock_local(dhd); +- for (i = 1; i < DHD_MAX_IFS; i++) +- dhd_remove_if(&dhd->pub, i, FALSE); +- dhd_net_if_unlock_local(dhd); +- } +- } +- } +-#endif /* WL_CFG80211 */ +- +-#ifdef PROP_TXSTATUS +- dhd_wlfc_cleanup(&dhd->pub, NULL, 0); +-#endif +- /* Stop the protocol module */ +- dhd_prot_stop(&dhd->pub); +- +- OLD_MOD_DEC_USE_COUNT; +-exit: +- if (ifidx == 0 && !dhd_download_fw_on_driverload) +- wl_android_wifi_off(net); +- dhd->pub.rxcnt_timeout = 0; +- dhd->pub.txcnt_timeout = 0; +- +- dhd->pub.hang_was_sent = 0; +- +- /* Clear country spec for for built-in type driver */ +- if (!dhd_download_fw_on_driverload) { +- dhd->pub.dhd_cspec.country_abbrev[0] = 0x00; +- dhd->pub.dhd_cspec.rev = 0; +- dhd->pub.dhd_cspec.ccode[0] = 0x00; +- } +- +- printk("%s: Exit\n", __FUNCTION__); +- DHD_PERIM_UNLOCK(&dhd->pub); +- DHD_OS_WAKE_UNLOCK(&dhd->pub); +- return 0; +-} +- +-#if defined(WL_CFG80211) && defined(USE_INITIAL_SHORT_DWELL_TIME) +-extern bool g_first_broadcast_scan; +-#endif +- +-#ifdef WL11U +-static int dhd_interworking_enable(dhd_pub_t *dhd) +-{ +- char iovbuf[WLC_IOCTL_SMLEN]; +- uint32 enable = true; +- int ret = BCME_OK; +- +- bcm_mkiovar("interworking", (char *)&enable, sizeof(enable), iovbuf, sizeof(iovbuf)); +- if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) { +- DHD_ERROR(("%s: enableing interworking failed, ret=%d\n", __FUNCTION__, ret)); +- } +- +- if (ret == BCME_OK) { +- /* basic capabilities for HS20 REL2 */ +- uint32 cap = WL_WNM_BSSTRANS | WL_WNM_NOTIF; +- bcm_mkiovar("wnm", (char *)&cap, sizeof(cap), iovbuf, sizeof(iovbuf)); +- if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, +- iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) { +- DHD_ERROR(("%s: failed to set WNM info, ret=%d\n", __FUNCTION__, ret)); +- } +- } +- +- return ret; +-} +-#endif /* WL11u */ +- +-static int +-dhd_open(struct net_device *net) +-{ +- dhd_info_t *dhd = DHD_DEV_INFO(net); +-#ifdef TOE +- uint32 toe_ol; +-#endif +- int ifidx; +- int32 ret = 0; +- +- printk("%s: Enter %p\n", __FUNCTION__, net); +-#if defined(MULTIPLE_SUPPLICANT) +-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1 && defined(BCMSDIO) +- if (mutex_is_locked(&_dhd_sdio_mutex_lock_) != 0) { +- DHD_ERROR(("%s : dhd_open: call dev open before insmod complete!\n", __FUNCTION__)); +- } +- mutex_lock(&_dhd_sdio_mutex_lock_); +-#endif +-#endif /* MULTIPLE_SUPPLICANT */ +- +- DHD_OS_WAKE_LOCK(&dhd->pub); +- DHD_PERIM_LOCK(&dhd->pub); +- dhd->pub.dongle_trap_occured = 0; +- dhd->pub.hang_was_sent = 0; +- +-#if 0 +- /* +- * Force start if ifconfig_up gets called before START command +- * We keep WEXT's wl_control_wl_start to provide backward compatibility +- * This should be removed in the future +- */ +- ret = wl_control_wl_start(net); +- if (ret != 0) { +- DHD_ERROR(("%s: failed with code %d\n", __FUNCTION__, ret)); +- ret = -1; +- goto exit; +- } +-#endif +- +- ifidx = dhd_net2idx(dhd, net); +- DHD_TRACE(("%s: ifidx %d\n", __FUNCTION__, ifidx)); +- +- if (ifidx < 0) { +- DHD_ERROR(("%s: Error: called with invalid IF\n", __FUNCTION__)); +- ret = -1; +- goto exit; +- } +- +- if (!dhd->iflist[ifidx]) { +- DHD_ERROR(("%s: Error: called when IF already deleted\n", __FUNCTION__)); +- ret = -1; +- goto exit; +- } +- +- if (ifidx == 0) { +- atomic_set(&dhd->pend_8021x_cnt, 0); +- if (!dhd_download_fw_on_driverload) { +- DHD_ERROR(("\n%s\n", dhd_version)); +-#if defined(USE_INITIAL_SHORT_DWELL_TIME) +- g_first_broadcast_scan = TRUE; +-#endif +- ret = wl_android_wifi_on(net); +- if (ret != 0) { +- DHD_ERROR(("%s : wl_android_wifi_on failed (%d)\n", +- __FUNCTION__, ret)); +- ret = -1; +- goto exit; +- } +- } +- +- if (dhd->pub.busstate != DHD_BUS_DATA) { +- +- /* try to bring up bus */ +- DHD_PERIM_UNLOCK(&dhd->pub); +- ret = dhd_bus_start(&dhd->pub); +- DHD_PERIM_LOCK(&dhd->pub); +- if (ret) { +- DHD_ERROR(("%s: failed with code %d\n", __FUNCTION__, ret)); +- ret = -1; +- goto exit; +- } +- +- } +- +- /* dhd_sync_with_dongle has been called in dhd_bus_start or wl_android_wifi_on */ +- memcpy(net->dev_addr, dhd->pub.mac.octet, ETHER_ADDR_LEN); +- +-#ifdef TOE +- /* Get current TOE mode from dongle */ +- if (dhd_toe_get(dhd, ifidx, &toe_ol) >= 0 && (toe_ol & TOE_TX_CSUM_OL) != 0) +- dhd->iflist[ifidx]->net->features |= NETIF_F_IP_CSUM; +- else +- dhd->iflist[ifidx]->net->features &= ~NETIF_F_IP_CSUM; +-#endif /* TOE */ +- +-#if defined(WL_CFG80211) +- if (unlikely(wl_cfg80211_up(NULL))) { +- DHD_ERROR(("%s: failed to bring up cfg80211\n", __FUNCTION__)); +- ret = -1; +- goto exit; +- } +- dhd_set_scb_probe(&dhd->pub); +-#endif /* WL_CFG80211 */ +- } +- +- /* Allow transmit calls */ +- netif_start_queue(net); +- dhd->pub.up = 1; +- +-#ifdef BCMDBGFS +- dhd_dbg_init(&dhd->pub); +-#endif +- +- OLD_MOD_INC_USE_COUNT; +-exit: +- if (ret) +- dhd_stop(net); +- +- DHD_PERIM_UNLOCK(&dhd->pub); +- DHD_OS_WAKE_UNLOCK(&dhd->pub); +- +-#if defined(MULTIPLE_SUPPLICANT) +-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1 && defined(BCMSDIO) +- mutex_unlock(&_dhd_sdio_mutex_lock_); +-#endif +-#endif /* MULTIPLE_SUPPLICANT */ +- +- printk("%s: Exit ret=%d\n", __FUNCTION__, ret); +- return ret; +-} +- +-int dhd_do_driver_init(struct net_device *net) +-{ +- dhd_info_t *dhd = NULL; +- +- if (!net) { +- DHD_ERROR(("Primary Interface not initialized \n")); +- return -EINVAL; +- } +- +-#ifdef MULTIPLE_SUPPLICANT +-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1 && defined(BCMSDIO) +- if (mutex_is_locked(&_dhd_sdio_mutex_lock_) != 0) { +- DHD_ERROR(("%s : dhdsdio_probe is already running!\n", __FUNCTION__)); +- return 0; +- } +-#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) */ +-#endif /* MULTIPLE_SUPPLICANT */ +- +- /* && defined(OEM_ANDROID) && defined(BCMSDIO) */ +- dhd = DHD_DEV_INFO(net); +- +- /* If driver is already initialized, do nothing +- */ +- if (dhd->pub.busstate == DHD_BUS_DATA) { +- DHD_TRACE(("Driver already Inititalized. Nothing to do")); +- return 0; +- } +- +- if (dhd_open(net) < 0) { +- DHD_ERROR(("Driver Init Failed \n")); +- return -1; +- } +- +- return 0; +-} +- +-int +-dhd_event_ifadd(dhd_info_t *dhdinfo, wl_event_data_if_t *ifevent, char *name, uint8 *mac) +-{ +- +-#ifdef WL_CFG80211 +- if (wl_cfg80211_notify_ifadd(ifevent->ifidx, name, mac, ifevent->bssidx) == BCME_OK) +- return BCME_OK; +-#endif +- +- /* handle IF event caused by wl commands, SoftAP, WEXT and +- * anything else. This has to be done asynchronously otherwise +- * DPC will be blocked (and iovars will timeout as DPC has no chance +- * to read the response back) +- */ +- if (ifevent->ifidx > 0) { +- dhd_if_event_t *if_event = MALLOC(dhdinfo->pub.osh, sizeof(dhd_if_event_t)); +- +- memcpy(&if_event->event, ifevent, sizeof(if_event->event)); +- memcpy(if_event->mac, mac, ETHER_ADDR_LEN); +- strncpy(if_event->name, name, IFNAMSIZ); +- if_event->name[IFNAMSIZ - 1] = '\0'; +- dhd_deferred_schedule_work(dhdinfo->dhd_deferred_wq, (void *)if_event, +- DHD_WQ_WORK_IF_ADD, dhd_ifadd_event_handler, DHD_WORK_PRIORITY_LOW); +- } +- +- return BCME_OK; +-} +- +-int +-dhd_event_ifdel(dhd_info_t *dhdinfo, wl_event_data_if_t *ifevent, char *name, uint8 *mac) +-{ +- dhd_if_event_t *if_event; +- +-#ifdef WL_CFG80211 +- if (wl_cfg80211_notify_ifdel(ifevent->ifidx, name, mac, ifevent->bssidx) == BCME_OK) +- return BCME_OK; +-#endif /* WL_CFG80211 */ +- +- /* handle IF event caused by wl commands, SoftAP, WEXT and +- * anything else +- */ +- if_event = MALLOC(dhdinfo->pub.osh, sizeof(dhd_if_event_t)); +- memcpy(&if_event->event, ifevent, sizeof(if_event->event)); +- memcpy(if_event->mac, mac, ETHER_ADDR_LEN); +- strncpy(if_event->name, name, IFNAMSIZ); +- if_event->name[IFNAMSIZ - 1] = '\0'; +- dhd_deferred_schedule_work(dhdinfo->dhd_deferred_wq, (void *)if_event, DHD_WQ_WORK_IF_DEL, +- dhd_ifdel_event_handler, DHD_WORK_PRIORITY_LOW); +- +- return BCME_OK; +-} +- +-/* unregister and free the existing net_device interface (if any) in iflist and +- * allocate a new one. the slot is reused. this function does NOT register the +- * new interface to linux kernel. dhd_register_if does the job +- */ +-struct net_device* +-dhd_allocate_if(dhd_pub_t *dhdpub, int ifidx, char *name, +- uint8 *mac, uint8 bssidx, bool need_rtnl_lock) +-{ +- dhd_info_t *dhdinfo = (dhd_info_t *)dhdpub->info; +- dhd_if_t *ifp; +- +- ASSERT(dhdinfo && (ifidx < DHD_MAX_IFS)); +- ifp = dhdinfo->iflist[ifidx]; +- +- if (ifp != NULL) { +- if (ifp->net != NULL) { +- DHD_ERROR(("%s: free existing IF %s\n", __FUNCTION__, ifp->net->name)); +- +- dhd_dev_priv_clear(ifp->net); /* clear net_device private */ +- +- /* in unregister_netdev case, the interface gets freed by net->destructor +- * (which is set to free_netdev) +- */ +- if (ifp->net->reg_state == NETREG_UNINITIALIZED) { +- free_netdev(ifp->net); +- } else { +- netif_stop_queue(ifp->net); +- if (need_rtnl_lock) +- unregister_netdev(ifp->net); +- else +- unregister_netdevice(ifp->net); +- } +- ifp->net = NULL; +- } +- } else { +- ifp = MALLOC(dhdinfo->pub.osh, sizeof(dhd_if_t)); +- if (ifp == NULL) { +- DHD_ERROR(("%s: OOM - dhd_if_t(%zu)\n", __FUNCTION__, sizeof(dhd_if_t))); +- return NULL; +- } +- } +- +- memset(ifp, 0, sizeof(dhd_if_t)); +- ifp->info = dhdinfo; +- ifp->idx = ifidx; +- ifp->bssidx = bssidx; +- if (mac != NULL) +- memcpy(&ifp->mac_addr, mac, ETHER_ADDR_LEN); +- +- /* Allocate etherdev, including space for private structure */ +- ifp->net = alloc_etherdev(DHD_DEV_PRIV_SIZE); +- if (ifp->net == NULL) { +- DHD_ERROR(("%s: OOM - alloc_etherdev(%zu)\n", __FUNCTION__, sizeof(dhdinfo))); +- goto fail; +- } +- +- /* Setup the dhd interface's netdevice private structure. */ +- dhd_dev_priv_save(ifp->net, dhdinfo, ifp, ifidx); +- +- if (name && name[0]) { +- strncpy(ifp->net->name, name, IFNAMSIZ); +- ifp->net->name[IFNAMSIZ - 1] = '\0'; +- } +-#ifdef WL_CFG80211 +- if (ifidx == 0) +- ifp->net->destructor = free_netdev; +- else +- ifp->net->destructor = dhd_netdev_free; +-#else +- ifp->net->destructor = free_netdev; +-#endif /* WL_CFG80211 */ +- strncpy(ifp->name, ifp->net->name, IFNAMSIZ); +- ifp->name[IFNAMSIZ - 1] = '\0'; +- dhdinfo->iflist[ifidx] = ifp; +- +-#ifdef PCIE_FULL_DONGLE +- /* Initialize STA info list */ +- INIT_LIST_HEAD(&ifp->sta_list); +- DHD_IF_STA_LIST_LOCK_INIT(ifp); +-#endif /* PCIE_FULL_DONGLE */ +- +- return ifp->net; +- +-fail: +- if (ifp != NULL) { +- if (ifp->net != NULL) { +- dhd_dev_priv_clear(ifp->net); +- free_netdev(ifp->net); +- ifp->net = NULL; +- } +- MFREE(dhdinfo->pub.osh, ifp, sizeof(*ifp)); +- ifp = NULL; +- } +- dhdinfo->iflist[ifidx] = NULL; +- return NULL; +-} +- +-/* unregister and free the the net_device interface associated with the indexed +- * slot, also free the slot memory and set the slot pointer to NULL +- */ +-int +-dhd_remove_if(dhd_pub_t *dhdpub, int ifidx, bool need_rtnl_lock) +-{ +- dhd_info_t *dhdinfo = (dhd_info_t *)dhdpub->info; +- dhd_if_t *ifp; +- +- ifp = dhdinfo->iflist[ifidx]; +- if (ifp != NULL) { +- if (ifp->net != NULL) { +- DHD_ERROR(("deleting interface '%s' idx %d\n", ifp->net->name, ifp->idx)); +- +- /* in unregister_netdev case, the interface gets freed by net->destructor +- * (which is set to free_netdev) +- */ +- if (ifp->net->reg_state == NETREG_UNINITIALIZED) { +- free_netdev(ifp->net); +- } else { +- netif_stop_queue(ifp->net); +- +- +- +- if (need_rtnl_lock) +- unregister_netdev(ifp->net); +- else +- unregister_netdevice(ifp->net); +- } +- ifp->net = NULL; +- } +-#ifdef DHD_WMF +- dhd_wmf_cleanup(dhdpub, ifidx); +-#endif /* DHD_WMF */ +- +- dhd_if_del_sta_list(ifp); +- +- dhdinfo->iflist[ifidx] = NULL; +- MFREE(dhdinfo->pub.osh, ifp, sizeof(*ifp)); +- +- } +- +- return BCME_OK; +-} +- +-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)) +-static struct net_device_ops dhd_ops_pri = { +- .ndo_open = dhd_open, +- .ndo_stop = dhd_stop, +- .ndo_get_stats = dhd_get_stats, +- .ndo_do_ioctl = dhd_ioctl_entry, +- .ndo_start_xmit = dhd_start_xmit, +- .ndo_set_mac_address = dhd_set_mac_address, +-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)) +- .ndo_set_rx_mode = dhd_set_multicast_list, +-#else +- .ndo_set_multicast_list = dhd_set_multicast_list, +-#endif +-}; +- +-static struct net_device_ops dhd_ops_virt = { +- .ndo_get_stats = dhd_get_stats, +- .ndo_do_ioctl = dhd_ioctl_entry, +- .ndo_start_xmit = dhd_start_xmit, +- .ndo_set_mac_address = dhd_set_mac_address, +-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)) +- .ndo_set_rx_mode = dhd_set_multicast_list, +-#else +- .ndo_set_multicast_list = dhd_set_multicast_list, +-#endif +-}; +-#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)) */ +- +-#ifdef DEBUGGER +-extern void debugger_init(void *bus_handle); +-#endif +- +- +-#ifdef SHOW_LOGTRACE +-static char *logstrs_path = "/root/logstrs.bin"; +-module_param(logstrs_path, charp, S_IRUGO); +- +-int +-dhd_init_logstrs_array(dhd_event_log_t *temp) +-{ +- struct file *filep = NULL; +- struct kstat stat; +- mm_segment_t fs; +- char *raw_fmts = NULL; +- int logstrs_size = 0; +- +- logstr_header_t *hdr = NULL; +- uint32 *lognums = NULL; +- char *logstrs = NULL; +- int ram_index = 0; +- char **fmts; +- int num_fmts = 0; +- uint32 i = 0; +- int error = 0; +- set_fs(KERNEL_DS); +- fs = get_fs(); +- filep = filp_open(logstrs_path, O_RDONLY, 0); +- if (IS_ERR(filep)) { +- DHD_ERROR(("Failed to open the file logstrs.bin in %s", __FUNCTION__)); +- goto fail; +- } +- error = vfs_stat(logstrs_path, &stat); +- if (error) { +- DHD_ERROR(("Failed in %s to find file stat", __FUNCTION__)); +- goto fail; +- } +- logstrs_size = (int) stat.size; +- +- raw_fmts = kmalloc(logstrs_size, GFP_KERNEL); +- if (raw_fmts == NULL) { +- DHD_ERROR(("Failed to allocate raw_fmts memory")); +- goto fail; +- } +- if (vfs_read(filep, raw_fmts, logstrs_size, &filep->f_pos) != logstrs_size) { +- DHD_ERROR(("Error: Log strings file read failed")); +- goto fail; +- } +- +- /* Remember header from the logstrs.bin file */ +- hdr = (logstr_header_t *) (raw_fmts + logstrs_size - +- sizeof(logstr_header_t)); +- +- if (hdr->log_magic == LOGSTRS_MAGIC) { +- /* +- * logstrs.bin start with header. +- */ +- num_fmts = hdr->rom_logstrs_offset / sizeof(uint32); +- ram_index = (hdr->ram_lognums_offset - +- hdr->rom_lognums_offset) / sizeof(uint32); +- lognums = (uint32 *) &raw_fmts[hdr->rom_lognums_offset]; +- logstrs = (char *) &raw_fmts[hdr->rom_logstrs_offset]; +- } else { +- /* +- * Legacy logstrs.bin format without header. +- */ +- num_fmts = *((uint32 *) (raw_fmts)) / sizeof(uint32); +- if (num_fmts == 0) { +- /* Legacy ROM/RAM logstrs.bin format: +- * - ROM 'lognums' section +- * - RAM 'lognums' section +- * - ROM 'logstrs' section. +- * - RAM 'logstrs' section. +- * +- * 'lognums' is an array of indexes for the strings in the +- * 'logstrs' section. The first uint32 is 0 (index of first +- * string in ROM 'logstrs' section). +- * +- * The 4324b5 is the only ROM that uses this legacy format. Use the +- * fixed number of ROM fmtnums to find the start of the RAM +- * 'lognums' section. Use the fixed first ROM string ("Con\n") to +- * find the ROM 'logstrs' section. +- */ +- #define NUM_4324B5_ROM_FMTS 186 +- #define FIRST_4324B5_ROM_LOGSTR "Con\n" +- ram_index = NUM_4324B5_ROM_FMTS; +- lognums = (uint32 *) raw_fmts; +- num_fmts = ram_index; +- logstrs = (char *) &raw_fmts[num_fmts << 2]; +- while (strncmp(FIRST_4324B5_ROM_LOGSTR, logstrs, 4)) { +- num_fmts++; +- logstrs = (char *) &raw_fmts[num_fmts << 2]; +- } +- } else { +- /* Legacy RAM-only logstrs.bin format: +- * - RAM 'lognums' section +- * - RAM 'logstrs' section. +- * +- * 'lognums' is an array of indexes for the strings in the +- * 'logstrs' section. The first uint32 is an index to the +- * start of 'logstrs'. Therefore, if this index is divided +- * by 'sizeof(uint32)' it provides the number of logstr +- * entries. +- */ +- ram_index = 0; +- lognums = (uint32 *) raw_fmts; +- logstrs = (char *) &raw_fmts[num_fmts << 2]; +- } +- } +- fmts = kmalloc(num_fmts * sizeof(char *), GFP_KERNEL); +- if (fmts == NULL) { +- DHD_ERROR(("Failed to allocate fmts memory")); +- goto fail; +- } +- +- for (i = 0; i < num_fmts; i++) { +- /* ROM lognums index into logstrs using 'rom_logstrs_offset' as a base +- * (they are 0-indexed relative to 'rom_logstrs_offset'). +- * +- * RAM lognums are already indexed to point to the correct RAM logstrs (they +- * are 0-indexed relative to the start of the logstrs.bin file). +- */ +- if (i == ram_index) { +- logstrs = raw_fmts; +- } +- fmts[i] = &logstrs[lognums[i]]; +- } +- temp->fmts = fmts; +- temp->raw_fmts = raw_fmts; +- temp->num_fmts = num_fmts; +- filp_close(filep, NULL); +- set_fs(fs); +- return 0; +-fail: +- if (raw_fmts) { +- kfree(raw_fmts); +- raw_fmts = NULL; +- } +- if (!IS_ERR(filep)) +- filp_close(filep, NULL); +- set_fs(fs); +- temp->fmts = NULL; +- return -1; +-} +-#endif /* SHOW_LOGTRACE */ +- +- +-dhd_pub_t * +-dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) +-{ +- dhd_info_t *dhd = NULL; +- struct net_device *net = NULL; +- char if_name[IFNAMSIZ] = {'\0'}; +- uint32 bus_type = -1; +- uint32 bus_num = -1; +- uint32 slot_num = -1; +- wifi_adapter_info_t *adapter = NULL; +- +- dhd_attach_states_t dhd_state = DHD_ATTACH_STATE_INIT; +- DHD_TRACE(("%s: Enter\n", __FUNCTION__)); +- +- /* will implement get_ids for DBUS later */ +-#if defined(BCMSDIO) +- dhd_bus_get_ids(bus, &bus_type, &bus_num, &slot_num); +-#endif +- adapter = dhd_wifi_platform_get_adapter(bus_type, bus_num, slot_num); +- +- /* Allocate primary dhd_info */ +- dhd = wifi_platform_prealloc(adapter, DHD_PREALLOC_DHD_INFO, sizeof(dhd_info_t)); +- if (dhd == NULL) { +- dhd = MALLOC(osh, sizeof(dhd_info_t)); +- if (dhd == NULL) { +- DHD_ERROR(("%s: OOM - alloc dhd_info\n", __FUNCTION__)); +- goto fail; +- } +- } +- memset(dhd, 0, sizeof(dhd_info_t)); +- dhd_state |= DHD_ATTACH_STATE_DHD_ALLOC; +- +- dhd->unit = dhd_found + instance_base; /* do not increment dhd_found, yet */ +- +- dhd->pub.osh = osh; +- dhd->adapter = adapter; +- +-#ifdef GET_CUSTOM_MAC_ENABLE +- wifi_platform_get_mac_addr(dhd->adapter, dhd->pub.mac.octet); +-#endif /* GET_CUSTOM_MAC_ENABLE */ +- dhd->thr_dpc_ctl.thr_pid = DHD_PID_KT_TL_INVALID; +- dhd->thr_wdt_ctl.thr_pid = DHD_PID_KT_INVALID; +- +- /* Initialize thread based operation and lock */ +- sema_init(&dhd->sdsem, 1); +- +- /* Link to info module */ +- dhd->pub.info = dhd; +- +- +- /* Link to bus module */ +- dhd->pub.bus = bus; +- dhd->pub.hdrlen = bus_hdrlen; +- +- /* dhd_conf must be attached after linking dhd to dhd->pub.info, +- * because dhd_detech will check .info is NULL or not. +- */ +- if (dhd_conf_attach(&dhd->pub) != 0) { +- DHD_ERROR(("dhd_conf_attach failed\n")); +- goto fail; +- } +- dhd_conf_reset(&dhd->pub); +- dhd_conf_set_chiprev(&dhd->pub, dhd_bus_chip(bus), dhd_bus_chiprev(bus)); +- +- /* Some DHD modules (e.g. cfg80211) configures operation mode based on firmware name. +- * This is indeed a hack but we have to make it work properly before we have a better +- * solution +- */ +- dhd_update_fw_nv_path(dhd); +- +- /* Set network interface name if it was provided as module parameter */ +- if (iface_name[0]) { +- int len; +- char ch; +- strncpy(if_name, iface_name, IFNAMSIZ); +- if_name[IFNAMSIZ - 1] = 0; +- len = strlen(if_name); +- ch = if_name[len - 1]; +- if ((ch > '9' || ch < '0') && (len < IFNAMSIZ - 2)) +- strcat(if_name, "%d"); +- } +- net = dhd_allocate_if(&dhd->pub, 0, if_name, NULL, 0, TRUE); +- if (net == NULL) +- goto fail; +- dhd_state |= DHD_ATTACH_STATE_ADD_IF; +- +-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)) +- net->open = NULL; +-#else +- net->netdev_ops = NULL; +-#endif +- +- sema_init(&dhd->proto_sem, 1); +- +-#ifdef PROP_TXSTATUS +- spin_lock_init(&dhd->wlfc_spinlock); +- +- dhd->pub.skip_fc = dhd_wlfc_skip_fc; +- dhd->pub.plat_init = dhd_wlfc_plat_init; +- dhd->pub.plat_deinit = dhd_wlfc_plat_deinit; +-#endif /* PROP_TXSTATUS */ +- +- /* Initialize other structure content */ +- init_waitqueue_head(&dhd->ioctl_resp_wait); +- init_waitqueue_head(&dhd->ctrl_wait); +- +- /* Initialize the spinlocks */ +- spin_lock_init(&dhd->sdlock); +- spin_lock_init(&dhd->txqlock); +- spin_lock_init(&dhd->dhd_lock); +- spin_lock_init(&dhd->rxf_lock); +-#if defined(RXFRAME_THREAD) +- dhd->rxthread_enabled = TRUE; +-#endif /* defined(RXFRAME_THREAD) */ +- +-#ifdef DHDTCPACK_SUPPRESS +- spin_lock_init(&dhd->tcpack_lock); +-#endif /* DHDTCPACK_SUPPRESS */ +- +- /* Initialize Wakelock stuff */ +- spin_lock_init(&dhd->wakelock_spinlock); +- dhd->wakelock_counter = 0; +- dhd->wakelock_wd_counter = 0; +- dhd->wakelock_rx_timeout_enable = 0; +- dhd->wakelock_ctrl_timeout_enable = 0; +-#ifdef CONFIG_HAS_WAKELOCK +- wake_lock_init(&dhd->wl_wifi, WAKE_LOCK_SUSPEND, "wlan_wake"); +- wake_lock_init(&dhd->wl_rxwake, WAKE_LOCK_SUSPEND, "wlan_rx_wake"); +- wake_lock_init(&dhd->wl_ctrlwake, WAKE_LOCK_SUSPEND, "wlan_ctrl_wake"); +- wake_lock_init(&dhd->wl_wdwake, WAKE_LOCK_SUSPEND, "wlan_wd_wake"); +-#endif /* CONFIG_HAS_WAKELOCK */ +-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1 +- mutex_init(&dhd->dhd_net_if_mutex); +- mutex_init(&dhd->dhd_suspend_mutex); +-#endif +- dhd_state |= DHD_ATTACH_STATE_WAKELOCKS_INIT; +- +- /* Attach and link in the protocol */ +- if (dhd_prot_attach(&dhd->pub) != 0) { +- DHD_ERROR(("dhd_prot_attach failed\n")); +- goto fail; +- } +- dhd_state |= DHD_ATTACH_STATE_PROT_ATTACH; +- +-#ifdef WL_CFG80211 +- /* Attach and link in the cfg80211 */ +- if (unlikely(wl_cfg80211_attach(net, &dhd->pub))) { +- DHD_ERROR(("wl_cfg80211_attach failed\n")); +- goto fail; +- } +- +- dhd_monitor_init(&dhd->pub); +- dhd_state |= DHD_ATTACH_STATE_CFG80211; +-#endif +-#if defined(WL_WIRELESS_EXT) +- /* Attach and link in the iw */ +- if (!(dhd_state & DHD_ATTACH_STATE_CFG80211)) { +- if (wl_iw_attach(net, (void *)&dhd->pub) != 0) { +- DHD_ERROR(("wl_iw_attach failed\n")); +- goto fail; +- } +- dhd_state |= DHD_ATTACH_STATE_WL_ATTACH; +- } +-#endif /* defined(WL_WIRELESS_EXT) */ +- +-#ifdef SHOW_LOGTRACE +- dhd_init_logstrs_array(&dhd->event_data); +-#endif /* SHOW_LOGTRACE */ +- +- if (dhd_sta_pool_init(&dhd->pub, DHD_MAX_STA) != BCME_OK) { +- DHD_ERROR(("%s: Initializing %u sta\n", __FUNCTION__, DHD_MAX_STA)); +- goto fail; +- } +- +- +- /* Set up the watchdog timer */ +- init_timer(&dhd->timer); +- dhd->timer.data = (ulong)dhd; +- dhd->timer.function = dhd_watchdog; +- dhd->default_wd_interval = dhd_watchdog_ms; +- +- if (dhd_watchdog_prio >= 0) { +- /* Initialize watchdog thread */ +- PROC_START(dhd_watchdog_thread, dhd, &dhd->thr_wdt_ctl, 0, "dhd_watchdog_thread"); +- +- } else { +- dhd->thr_wdt_ctl.thr_pid = -1; +- } +- +-#ifdef DEBUGGER +- debugger_init((void *) bus); +-#endif +- +- /* Set up the bottom half handler */ +- if (dhd_dpc_prio >= 0) { +- /* Initialize DPC thread */ +- PROC_START(dhd_dpc_thread, dhd, &dhd->thr_dpc_ctl, 0, "dhd_dpc"); +- } else { +- /* use tasklet for dpc */ +- tasklet_init(&dhd->tasklet, dhd_dpc, (ulong)dhd); +- dhd->thr_dpc_ctl.thr_pid = -1; +- } +- +- if (dhd->rxthread_enabled) { +- bzero(&dhd->pub.skbbuf[0], sizeof(void *) * MAXSKBPEND); +- /* Initialize RXF thread */ +- PROC_START(dhd_rxf_thread, dhd, &dhd->thr_rxf_ctl, 0, "dhd_rxf"); +- } +- +- dhd_state |= DHD_ATTACH_STATE_THREADS_CREATED; +- +-#if defined(CONFIG_PM_SLEEP) +- if (!dhd_pm_notifier_registered) { +- dhd_pm_notifier_registered = TRUE; +- register_pm_notifier(&dhd_pm_notifier); +- } +-#endif /* CONFIG_PM_SLEEP */ +- +-#if defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND) +- dhd->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 20; +- dhd->early_suspend.suspend = dhd_early_suspend; +- dhd->early_suspend.resume = dhd_late_resume; +- register_early_suspend(&dhd->early_suspend); +- dhd_state |= DHD_ATTACH_STATE_EARLYSUSPEND_DONE; +-#endif /* CONFIG_HAS_EARLYSUSPEND && DHD_USE_EARLYSUSPEND */ +- +-#ifdef ARP_OFFLOAD_SUPPORT +- dhd->pend_ipaddr = 0; +- if (!dhd_inetaddr_notifier_registered) { +- dhd_inetaddr_notifier_registered = TRUE; +- register_inetaddr_notifier(&dhd_inetaddr_notifier); +- } +-#endif /* ARP_OFFLOAD_SUPPORT */ +-#ifdef CONFIG_IPV6 +- if (!dhd_inet6addr_notifier_registered) { +- dhd_inet6addr_notifier_registered = TRUE; +- register_inet6addr_notifier(&dhd_inet6addr_notifier); +- } +-#endif +- dhd->dhd_deferred_wq = dhd_deferred_work_init((void *)dhd); +-#ifdef DEBUG_CPU_FREQ +- dhd->new_freq = alloc_percpu(int); +- dhd->freq_trans.notifier_call = dhd_cpufreq_notifier; +- cpufreq_register_notifier(&dhd->freq_trans, CPUFREQ_TRANSITION_NOTIFIER); +-#endif +-#ifdef DHDTCPACK_SUPPRESS +-#ifdef BCMSDIO +- dhd_tcpack_suppress_set(&dhd->pub, TCPACK_SUP_DELAYTX); +-#elif defined(BCMPCIE) +- dhd_tcpack_suppress_set(&dhd->pub, TCPACK_SUP_REPLACE); +-#else +- dhd_tcpack_suppress_set(&dhd->pub, TCPACK_SUP_OFF); +-#endif /* BCMSDIO */ +-#endif /* DHDTCPACK_SUPPRESS */ +- +- dhd_state |= DHD_ATTACH_STATE_DONE; +- dhd->dhd_state = dhd_state; +- +- dhd_found++; +- return &dhd->pub; +- +-fail: +- if (dhd_state >= DHD_ATTACH_STATE_DHD_ALLOC) { +- DHD_TRACE(("%s: Calling dhd_detach dhd_state 0x%x &dhd->pub %p\n", +- __FUNCTION__, dhd_state, &dhd->pub)); +- dhd->dhd_state = dhd_state; +- dhd_detach(&dhd->pub); +- dhd_free(&dhd->pub); +- } +- +- return NULL; +-} +- +-int dhd_get_fw_mode(dhd_info_t *dhdinfo) +-{ +- if (strstr(dhdinfo->fw_path, "_apsta") != NULL) +- return DHD_FLAG_HOSTAP_MODE; +- if (strstr(dhdinfo->fw_path, "_p2p") != NULL) +- return DHD_FLAG_P2P_MODE; +- if (strstr(dhdinfo->fw_path, "_ibss") != NULL) +- return DHD_FLAG_IBSS_MODE; +- if (strstr(dhdinfo->fw_path, "_mfg") != NULL) +- return DHD_FLAG_MFG_MODE; +- +- return DHD_FLAG_STA_MODE; +-} +- +-bool dhd_update_fw_nv_path(dhd_info_t *dhdinfo) +-{ +- int fw_len; +- int nv_len; +- int conf_len; +- const char *fw = NULL; +- const char *nv = NULL; +- const char *conf = NULL; +- wifi_adapter_info_t *adapter = dhdinfo->adapter; +- +- +- /* Update firmware and nvram path. The path may be from adapter info or module parameter +- * The path from adapter info is used for initialization only (as it won't change). +- * +- * The firmware_path/nvram_path module parameter may be changed by the system at run +- * time. When it changes we need to copy it to dhdinfo->fw_path. Also Android private +- * command may change dhdinfo->fw_path. As such we need to clear the path info in +- * module parameter after it is copied. We won't update the path until the module parameter +- * is changed again (first character is not '\0') +- */ +- +- /* set default firmware and nvram path for built-in type driver */ +-// if (!dhd_download_fw_on_driverload) { +-#ifdef CONFIG_BCMDHD_FW_PATH +- fw = CONFIG_BCMDHD_FW_PATH; +-#endif /* CONFIG_BCMDHD_FW_PATH */ +-#ifdef CONFIG_BCMDHD_NVRAM_PATH +- nv = CONFIG_BCMDHD_NVRAM_PATH; +-#endif /* CONFIG_BCMDHD_NVRAM_PATH */ +-#ifdef CONFIG_BCMDHD_CONFIG_PATH +- conf = CONFIG_BCMDHD_CONFIG_PATH; +-#endif /* CONFIG_BCMDHD_CONFIG_PATH */ +-// } +- +- /* check if we need to initialize the path */ +- if (dhdinfo->fw_path[0] == '\0') { +- if (adapter && adapter->fw_path && adapter->fw_path[0] != '\0') +- fw = adapter->fw_path; +- +- } +- if (dhdinfo->nv_path[0] == '\0') { +- if (adapter && adapter->nv_path && adapter->nv_path[0] != '\0') +- nv = adapter->nv_path; +- } +- if (dhdinfo->conf_path[0] == '\0') { +- if (adapter && adapter->conf_path && adapter->conf_path[0] != '\0') +- conf = adapter->conf_path; +- } +- +- /* Use module parameter if it is valid, EVEN IF the path has not been initialized +- * +- * TODO: need a solution for multi-chip, can't use the same firmware for all chips +- */ +- if (firmware_path[0] != '\0') +- fw = firmware_path; +- if (nvram_path[0] != '\0') +- nv = nvram_path; +- if (config_path[0] != '\0') +- conf = config_path; +- +- if (fw && fw[0] != '\0') { +- fw_len = strlen(fw); +- if (fw_len >= sizeof(dhdinfo->fw_path)) { +- DHD_ERROR(("fw path len exceeds max len of dhdinfo->fw_path\n")); +- return FALSE; +- } +- strncpy(dhdinfo->fw_path, fw, sizeof(dhdinfo->fw_path)); +- if (dhdinfo->fw_path[fw_len-1] == '\n') +- dhdinfo->fw_path[fw_len-1] = '\0'; +- } +- if (nv && nv[0] != '\0') { +- nv_len = strlen(nv); +- if (nv_len >= sizeof(dhdinfo->nv_path)) { +- DHD_ERROR(("nvram path len exceeds max len of dhdinfo->nv_path\n")); +- return FALSE; +- } +- strncpy(dhdinfo->nv_path, nv, sizeof(dhdinfo->nv_path)); +- if (dhdinfo->nv_path[nv_len-1] == '\n') +- dhdinfo->nv_path[nv_len-1] = '\0'; +- } +- if (conf && conf[0] != '\0') { +- conf_len = strlen(conf); +- if (conf_len >= sizeof(dhdinfo->conf_path)) { +- DHD_ERROR(("config path len exceeds max len of dhdinfo->conf_path\n")); +- return FALSE; +- } +- strncpy(dhdinfo->conf_path, conf, sizeof(dhdinfo->conf_path)); +- if (dhdinfo->conf_path[conf_len-1] == '\n') +- dhdinfo->conf_path[conf_len-1] = '\0'; +- } +- +-#if 0 +- /* clear the path in module parameter */ +- firmware_path[0] = '\0'; +- nvram_path[0] = '\0'; +- config_path[0] = '\0'; +-#endif +- +-#ifndef BCMEMBEDIMAGE +- /* fw_path and nv_path are not mandatory for BCMEMBEDIMAGE */ +- if (dhdinfo->fw_path[0] == '\0') { +- DHD_ERROR(("firmware path not found\n")); +- return FALSE; +- } +- if (dhdinfo->nv_path[0] == '\0') { +- DHD_ERROR(("nvram path not found\n")); +- return FALSE; +- } +- if (dhdinfo->conf_path[0] == '\0') { +- DHD_ERROR(("config path not found\n")); +- return FALSE; +- } +-#endif /* BCMEMBEDIMAGE */ +- +- return TRUE; +-} +- +- +-#ifdef EXYNOS5433_PCIE_WAR +-extern int enum_wifi; +-#endif /* EXYNOS5433_PCIE_WAR */ +-int +-dhd_bus_start(dhd_pub_t *dhdp) +-{ +- int ret = -1; +- dhd_info_t *dhd = (dhd_info_t*)dhdp->info; +- unsigned long flags; +- +- ASSERT(dhd); +- +- DHD_TRACE(("Enter %s:\n", __FUNCTION__)); +- +- DHD_PERIM_LOCK(dhdp); +- +- /* try to download image and nvram to the dongle */ +- if (dhd->pub.busstate == DHD_BUS_DOWN && dhd_update_fw_nv_path(dhd)) { +- DHD_INFO(("%s download fw %s, nv %s, conf %s\n", +- __FUNCTION__, dhd->fw_path, dhd->nv_path, dhd->conf_path)); +- ret = dhd_bus_download_firmware(dhd->pub.bus, dhd->pub.osh, +- dhd->fw_path, dhd->nv_path, dhd->conf_path); +- if (ret < 0) { +-#ifdef EXYNOS5433_PCIE_WAR +- enum_wifi = 0; +-#endif /* EXYNOS5433_PCIE_WAR */ +- DHD_ERROR(("%s: failed to download firmware %s\n", +- __FUNCTION__, dhd->fw_path)); +- DHD_PERIM_UNLOCK(dhdp); +- return ret; +- } +-#ifdef EXYNOS5433_PCIE_WAR +- enum_wifi = 1; +-#endif /* EXYNOS5433_PCIE_WAR */ +- } +- if (dhd->pub.busstate != DHD_BUS_LOAD) { +- DHD_PERIM_UNLOCK(dhdp); +- return -ENETDOWN; +- } +- +- dhd_os_sdlock(dhdp); +- +- /* Start the watchdog timer */ +- dhd->pub.tickcnt = 0; +- dhd_os_wd_timer(&dhd->pub, dhd_watchdog_ms); +- +- /* Bring up the bus */ +- if ((ret = dhd_bus_init(&dhd->pub, FALSE)) != 0) { +- +- DHD_ERROR(("%s, dhd_bus_init failed %d\n", __FUNCTION__, ret)); +- dhd_os_sdunlock(dhdp); +- DHD_PERIM_UNLOCK(dhdp); +- return ret; +- } +-#if defined(OOB_INTR_ONLY) +- /* Host registration for OOB interrupt */ +- if (dhd_bus_oob_intr_register(dhdp)) { +- /* deactivate timer and wait for the handler to finish */ +- +- DHD_GENERAL_LOCK(&dhd->pub, flags); +- dhd->wd_timer_valid = FALSE; +- DHD_GENERAL_UNLOCK(&dhd->pub, flags); +- del_timer_sync(&dhd->timer); +- +- DHD_ERROR(("%s Host failed to register for OOB\n", __FUNCTION__)); +- dhd_os_sdunlock(dhdp); +- DHD_PERIM_UNLOCK(dhdp); +- DHD_OS_WD_WAKE_UNLOCK(&dhd->pub); +- return -ENODEV; +- } +- +- /* Enable oob at firmware */ +- dhd_enable_oob_intr(dhd->pub.bus, TRUE); +-#endif +-#ifdef PCIE_FULL_DONGLE +- { +- uint8 txpush = 0; +- uint32 num_flowrings; /* includes H2D common rings */ +- num_flowrings = dhd_bus_max_h2d_queues(dhd->pub.bus, &txpush); +- DHD_ERROR(("%s: Initializing %u flowrings\n", __FUNCTION__, +- num_flowrings)); +- if ((ret = dhd_flow_rings_init(&dhd->pub, num_flowrings)) != BCME_OK) { +- dhd_os_sdunlock(dhdp); +- DHD_PERIM_UNLOCK(dhdp); +- return ret; +- } +- } +-#endif /* PCIE_FULL_DONGLE */ +- +- /* Do protocol initialization necessary for IOCTL/IOVAR */ +- dhd_prot_init(&dhd->pub); +- +- /* If bus is not ready, can't come up */ +- if (dhd->pub.busstate != DHD_BUS_DATA) { +- DHD_GENERAL_LOCK(&dhd->pub, flags); +- dhd->wd_timer_valid = FALSE; +- DHD_GENERAL_UNLOCK(&dhd->pub, flags); +- del_timer_sync(&dhd->timer); +- DHD_ERROR(("%s failed bus is not ready\n", __FUNCTION__)); +- dhd_os_sdunlock(dhdp); +- DHD_PERIM_UNLOCK(dhdp); +- DHD_OS_WD_WAKE_UNLOCK(&dhd->pub); +- return -ENODEV; +- } +- +- dhd_os_sdunlock(dhdp); +- +- /* Bus is ready, query any dongle information */ +- if ((ret = dhd_sync_with_dongle(&dhd->pub)) < 0) { +- DHD_PERIM_UNLOCK(dhdp); +- return ret; +- } +- +-#ifdef ARP_OFFLOAD_SUPPORT +- if (dhd->pend_ipaddr) { +-#ifdef AOE_IP_ALIAS_SUPPORT +- aoe_update_host_ipv4_table(&dhd->pub, dhd->pend_ipaddr, TRUE, 0); +-#endif /* AOE_IP_ALIAS_SUPPORT */ +- dhd->pend_ipaddr = 0; +- } +-#endif /* ARP_OFFLOAD_SUPPORT */ +- +- DHD_PERIM_UNLOCK(dhdp); +- return 0; +-} +- +-#ifdef WLTDLS +-int _dhd_tdls_enable(dhd_pub_t *dhd, bool tdls_on, bool auto_on, struct ether_addr *mac) +-{ +- char iovbuf[WLC_IOCTL_SMLEN]; +- uint32 tdls = tdls_on; +- int ret = 0; +- uint32 tdls_auto_op = 0; +- uint32 tdls_idle_time = CUSTOM_TDLS_IDLE_MODE_SETTING; +- int32 tdls_rssi_high = CUSTOM_TDLS_RSSI_THRESHOLD_HIGH; +- int32 tdls_rssi_low = CUSTOM_TDLS_RSSI_THRESHOLD_LOW; +- BCM_REFERENCE(mac); +- if (!FW_SUPPORTED(dhd, tdls)) +- return BCME_ERROR; +- +- if (dhd->tdls_enable == tdls_on) +- goto auto_mode; +- bcm_mkiovar("tdls_enable", (char *)&tdls, sizeof(tdls), iovbuf, sizeof(iovbuf)); +- if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) { +- DHD_ERROR(("%s: tdls %d failed %d\n", __FUNCTION__, tdls, ret)); +- goto exit; +- } +- dhd->tdls_enable = tdls_on; +-auto_mode: +- +- tdls_auto_op = auto_on; +- bcm_mkiovar("tdls_auto_op", (char *)&tdls_auto_op, sizeof(tdls_auto_op), +- iovbuf, sizeof(iovbuf)); +- if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, +- sizeof(iovbuf), TRUE, 0)) < 0) { +- DHD_ERROR(("%s: tdls_auto_op failed %d\n", __FUNCTION__, ret)); +- goto exit; +- } +- +- if (tdls_auto_op) { +- bcm_mkiovar("tdls_idle_time", (char *)&tdls_idle_time, +- sizeof(tdls_idle_time), iovbuf, sizeof(iovbuf)); +- if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, +- sizeof(iovbuf), TRUE, 0)) < 0) { +- DHD_ERROR(("%s: tdls_idle_time failed %d\n", __FUNCTION__, ret)); +- goto exit; +- } +- bcm_mkiovar("tdls_rssi_high", (char *)&tdls_rssi_high, 4, iovbuf, sizeof(iovbuf)); +- if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, +- sizeof(iovbuf), TRUE, 0)) < 0) { +- DHD_ERROR(("%s: tdls_rssi_high failed %d\n", __FUNCTION__, ret)); +- goto exit; +- } +- bcm_mkiovar("tdls_rssi_low", (char *)&tdls_rssi_low, 4, iovbuf, sizeof(iovbuf)); +- if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, +- sizeof(iovbuf), TRUE, 0)) < 0) { +- DHD_ERROR(("%s: tdls_rssi_low failed %d\n", __FUNCTION__, ret)); +- goto exit; +- } +- } +- +-exit: +- return ret; +-} +- +-int dhd_tdls_enable(struct net_device *dev, bool tdls_on, bool auto_on, struct ether_addr *mac) +-{ +- dhd_info_t *dhd = DHD_DEV_INFO(dev); +- int ret = 0; +- if (dhd) +- ret = _dhd_tdls_enable(&dhd->pub, tdls_on, auto_on, mac); +- else +- ret = BCME_ERROR; +- return ret; +-} +-#ifdef PCIE_FULL_DONGLE +-void dhd_tdls_update_peer_info(struct net_device *dev, bool connect, uint8 *da) +-{ +- dhd_info_t *dhd = DHD_DEV_INFO(dev); +- dhd_pub_t *dhdp = (dhd_pub_t *)&dhd->pub; +- tdls_peer_node_t *cur = dhdp->peer_tbl.node; +- tdls_peer_node_t *new = NULL, *prev = NULL; +- dhd_if_t *dhdif; +- uint8 sa[ETHER_ADDR_LEN]; +- int ifidx = dhd_net2idx(dhd, dev); +- +- if (ifidx == DHD_BAD_IF) +- return; +- +- dhdif = dhd->iflist[ifidx]; +- memcpy(sa, dhdif->mac_addr, ETHER_ADDR_LEN); +- +- if (connect) { +- while (cur != NULL) { +- if (!memcmp(da, cur->addr, ETHER_ADDR_LEN)) { +- DHD_ERROR(("%s: TDLS Peer exist already %d\n", +- __FUNCTION__, __LINE__)); +- return; +- } +- cur = cur->next; +- } +- +- new = MALLOC(dhdp->osh, sizeof(tdls_peer_node_t)); +- if (new == NULL) { +- DHD_ERROR(("%s: Failed to allocate memory\n", __FUNCTION__)); +- return; +- } +- memcpy(new->addr, da, ETHER_ADDR_LEN); +- new->next = dhdp->peer_tbl.node; +- dhdp->peer_tbl.node = new; +- dhdp->peer_tbl.tdls_peer_count++; +- +- } else { +- while (cur != NULL) { +- if (!memcmp(da, cur->addr, ETHER_ADDR_LEN)) { +- dhd_flow_rings_delete_for_peer(dhdp, ifidx, da); +- if (prev) +- prev->next = cur->next; +- else +- dhdp->peer_tbl.node = cur->next; +- MFREE(dhdp->osh, cur, sizeof(tdls_peer_node_t)); +- dhdp->peer_tbl.tdls_peer_count--; +- return; +- } +- prev = cur; +- cur = cur->next; +- } +- DHD_ERROR(("%s: TDLS Peer Entry Not found\n", __FUNCTION__)); +- } +-} +-#endif /* PCIE_FULL_DONGLE */ +-#endif +- +-bool dhd_is_concurrent_mode(dhd_pub_t *dhd) +-{ +- if (!dhd) +- return FALSE; +- +- if (dhd->op_mode & DHD_FLAG_CONCURR_MULTI_CHAN_MODE) +- return TRUE; +- else if ((dhd->op_mode & DHD_FLAG_CONCURR_SINGLE_CHAN_MODE) == +- DHD_FLAG_CONCURR_SINGLE_CHAN_MODE) +- return TRUE; +- else +- return FALSE; +-} +-#if !defined(AP) && defined(WLP2P) +-/* From Android JerryBean release, the concurrent mode is enabled by default and the firmware +- * name would be fw_bcmdhd.bin. So we need to determine whether P2P is enabled in the STA +- * firmware and accordingly enable concurrent mode (Apply P2P settings). SoftAP firmware +- * would still be named as fw_bcmdhd_apsta. +- */ +-uint32 +-dhd_get_concurrent_capabilites(dhd_pub_t *dhd) +-{ +- int32 ret = 0; +- char buf[WLC_IOCTL_SMLEN]; +- bool mchan_supported = FALSE; +- /* if dhd->op_mode is already set for HOSTAP and Manufacturing +- * test mode, that means we only will use the mode as it is +- */ +- if (dhd->op_mode & (DHD_FLAG_HOSTAP_MODE | DHD_FLAG_MFG_MODE)) +- return 0; +- if (FW_SUPPORTED(dhd, vsdb)) { +- mchan_supported = TRUE; +- } +- if (!FW_SUPPORTED(dhd, p2p)) { +- DHD_TRACE(("Chip does not support p2p\n")); +- return 0; +- } +- else { +- /* Chip supports p2p but ensure that p2p is really implemented in firmware or not */ +- memset(buf, 0, sizeof(buf)); +- bcm_mkiovar("p2p", 0, 0, buf, sizeof(buf)); +- if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, buf, sizeof(buf), +- FALSE, 0)) < 0) { +- DHD_ERROR(("%s: Get P2P failed (error=%d)\n", __FUNCTION__, ret)); +- return 0; +- } +- else { +- if (buf[0] == 1) { +- /* By default, chip supports single chan concurrency, +- * now lets check for mchan +- */ +- ret = DHD_FLAG_CONCURR_SINGLE_CHAN_MODE; +- if (mchan_supported) +- ret |= DHD_FLAG_CONCURR_MULTI_CHAN_MODE; +-#if defined(WL_ENABLE_P2P_IF) || defined(WL_CFG80211_P2P_DEV_IF) +- /* For customer_hw4, although ICS, +- * we still support concurrent mode +- */ +- return ret; +-#else +- return 0; +-#endif +- } +- } +- } +- return 0; +-} +-#endif +-#if defined(READ_CONFIG_FROM_FILE) +-#include +-#include +- +-#define strtoul(nptr, endptr, base) bcm_strtoul((nptr), (endptr), (base)) +-bool PM_control = TRUE; +- +-static int dhd_preinit_proc(dhd_pub_t *dhd, int ifidx, char *name, char *value) +-{ +- int var_int; +- wl_country_t cspec = {{0}, -1, {0}}; +- char *revstr; +- char *endptr = NULL; +- int iolen; +- char smbuf[WLC_IOCTL_SMLEN*2]; +- +- if (!strcmp(name, "country")) { +- revstr = strchr(value, '/'); +- if (revstr) { +- cspec.rev = strtoul(revstr + 1, &endptr, 10); +- memcpy(cspec.country_abbrev, value, WLC_CNTRY_BUF_SZ); +- cspec.country_abbrev[2] = '\0'; +- memcpy(cspec.ccode, cspec.country_abbrev, WLC_CNTRY_BUF_SZ); +- } else { +- cspec.rev = -1; +- memcpy(cspec.country_abbrev, value, WLC_CNTRY_BUF_SZ); +- memcpy(cspec.ccode, value, WLC_CNTRY_BUF_SZ); +- get_customized_country_code(dhd->info->adapter, +- (char *)&cspec.country_abbrev, &cspec); +- } +- memset(smbuf, 0, sizeof(smbuf)); +- DHD_ERROR(("config country code is country : %s, rev : %d !!\n", +- cspec.country_abbrev, cspec.rev)); +- iolen = bcm_mkiovar("country", (char*)&cspec, sizeof(cspec), +- smbuf, sizeof(smbuf)); +- return dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, +- smbuf, iolen, TRUE, 0); +- } else if (!strcmp(name, "roam_scan_period")) { +- var_int = (int)simple_strtol(value, NULL, 0); +- return dhd_wl_ioctl_cmd(dhd, WLC_SET_ROAM_SCAN_PERIOD, +- &var_int, sizeof(var_int), TRUE, 0); +- } else if (!strcmp(name, "roam_delta")) { +- struct { +- int val; +- int band; +- } x; +- x.val = (int)simple_strtol(value, NULL, 0); +- /* x.band = WLC_BAND_AUTO; */ +- x.band = WLC_BAND_ALL; +- return dhd_wl_ioctl_cmd(dhd, WLC_SET_ROAM_DELTA, &x, sizeof(x), TRUE, 0); +- } else if (!strcmp(name, "roam_trigger")) { +- int ret = 0; +- +- roam_trigger[0] = (int)simple_strtol(value, NULL, 0); +- roam_trigger[1] = WLC_BAND_ALL; +- ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_ROAM_TRIGGER, &roam_trigger, +- sizeof(roam_trigger), TRUE, 0); +- +- return ret; +- } else if (!strcmp(name, "PM")) { +- int ret = 0; +- var_int = (int)simple_strtol(value, NULL, 0); +- +- ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_PM, +- &var_int, sizeof(var_int), TRUE, 0); +- +-#if defined(CONFIG_PM_LOCK) +- if (var_int == 0) { +- g_pm_control = TRUE; +- printk("%s var_int=%d don't control PM\n", __func__, var_int); +- } else { +- g_pm_control = FALSE; +- printk("%s var_int=%d do control PM\n", __func__, var_int); +- } +-#endif +- +- return ret; +- } +-#ifdef WLBTAMP +- else if (!strcmp(name, "btamp_chan")) { +- int btamp_chan; +- int iov_len = 0; +- char iovbuf[128]; +- int ret; +- +- btamp_chan = (int)simple_strtol(value, NULL, 0); +- iov_len = bcm_mkiovar("btamp_chan", (char *)&btamp_chan, 4, iovbuf, sizeof(iovbuf)); +- if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, iov_len, TRUE, 0) < 0)) +- DHD_ERROR(("%s btamp_chan=%d set failed code %d\n", +- __FUNCTION__, btamp_chan, ret)); +- else +- DHD_ERROR(("%s btamp_chan %d set success\n", +- __FUNCTION__, btamp_chan)); +- } +-#endif /* WLBTAMP */ +- else if (!strcmp(name, "band")) { +- int ret; +- if (!strcmp(value, "auto")) +- var_int = WLC_BAND_AUTO; +- else if (!strcmp(value, "a")) +- var_int = WLC_BAND_5G; +- else if (!strcmp(value, "b")) +- var_int = WLC_BAND_2G; +- else if (!strcmp(value, "all")) +- var_int = WLC_BAND_ALL; +- else { +- printk(" set band value should be one of the a or b or all\n"); +- var_int = WLC_BAND_AUTO; +- } +- if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_BAND, &var_int, +- sizeof(var_int), TRUE, 0)) < 0) +- printk(" set band err=%d\n", ret); +- return ret; +- } else if (!strcmp(name, "cur_etheraddr")) { +- struct ether_addr ea; +- char buf[32]; +- uint iovlen; +- int ret; +- +- bcm_ether_atoe(value, &ea); +- +- ret = memcmp(&ea.octet, dhd->mac.octet, ETHER_ADDR_LEN); +- if (ret == 0) { +- DHD_ERROR(("%s: Same Macaddr\n", __FUNCTION__)); +- return 0; +- } +- +- DHD_ERROR(("%s: Change Macaddr = %02X:%02X:%02X:%02X:%02X:%02X\n", __FUNCTION__, +- ea.octet[0], ea.octet[1], ea.octet[2], +- ea.octet[3], ea.octet[4], ea.octet[5])); +- +- iovlen = bcm_mkiovar("cur_etheraddr", (char*)&ea, ETHER_ADDR_LEN, buf, 32); +- +- ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, buf, iovlen, TRUE, 0); +- if (ret < 0) { +- DHD_ERROR(("%s: can't set MAC address , error=%d\n", __FUNCTION__, ret)); +- return ret; +- } +- else { +- memcpy(dhd->mac.octet, (void *)&ea, ETHER_ADDR_LEN); +- return ret; +- } +- } else { +- uint iovlen; +- char iovbuf[WLC_IOCTL_SMLEN]; +- +- /* wlu_iovar_setint */ +- var_int = (int)simple_strtol(value, NULL, 0); +- +- /* Setup timeout bcn_timeout from dhd driver 4.217.48 */ +- if (!strcmp(name, "roam_off")) { +- /* Setup timeout if Beacons are lost to report link down */ +- if (var_int) { +- uint bcn_timeout = 2; +- bcm_mkiovar("bcn_timeout", (char *)&bcn_timeout, 4, +- iovbuf, sizeof(iovbuf)); +- dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); +- } +- } +- /* Setup timeout bcm_timeout from dhd driver 4.217.48 */ +- +- DHD_INFO(("%s:[%s]=[%d]\n", __FUNCTION__, name, var_int)); +- +- iovlen = bcm_mkiovar(name, (char *)&var_int, sizeof(var_int), +- iovbuf, sizeof(iovbuf)); +- return dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, +- iovbuf, iovlen, TRUE, 0); +- } +- +- return 0; +-} +- +-static int dhd_preinit_config(dhd_pub_t *dhd, int ifidx) +-{ +- mm_segment_t old_fs; +- struct kstat stat; +- struct file *fp = NULL; +- unsigned int len; +- char *buf = NULL, *p, *name, *value; +- int ret = 0; +- char *config_path; +- +- config_path = CONFIG_BCMDHD_CONFIG_PATH; +- +- if (!config_path) +- { +- printk(KERN_ERR "config_path can't read. \n"); +- return 0; +- } +- +- old_fs = get_fs(); +- set_fs(get_ds()); +- if ((ret = vfs_stat(config_path, &stat))) { +- set_fs(old_fs); +- printk(KERN_ERR "%s: Failed to get information (%d)\n", +- config_path, ret); +- return ret; +- } +- set_fs(old_fs); +- +- if (!(buf = MALLOC(dhd->osh, stat.size + 1))) { +- printk(KERN_ERR "Failed to allocate memory %llu bytes\n", stat.size); +- return -ENOMEM; +- } +- +- printk("dhd_preinit_config : config path : %s \n", config_path); +- +- if (!(fp = dhd_os_open_image(config_path)) || +- (len = dhd_os_get_image_block(buf, stat.size, fp)) < 0) +- goto err; +- +- buf[stat.size] = '\0'; +- for (p = buf; *p; p++) { +- if (isspace(*p)) +- continue; +- for (name = p++; *p && !isspace(*p); p++) { +- if (*p == '=') { +- *p = '\0'; +- p++; +- for (value = p; *p && !isspace(*p); p++); +- *p = '\0'; +- if ((ret = dhd_preinit_proc(dhd, ifidx, name, value)) < 0) { +- printk(KERN_ERR "%s: %s=%s\n", +- bcmerrorstr(ret), name, value); +- } +- break; +- } +- } +- } +- ret = 0; +- +-out: +- if (fp) +- dhd_os_close_image(fp); +- if (buf) +- MFREE(dhd->osh, buf, stat.size+1); +- return ret; +- +-err: +- ret = -1; +- goto out; +-} +-#endif /* READ_CONFIG_FROM_FILE */ +- +-int +-dhd_preinit_ioctls(dhd_pub_t *dhd) +-{ +- int ret = 0; +- char eventmask[WL_EVENTING_MASK_LEN]; +- char iovbuf[WL_EVENTING_MASK_LEN + 12]; /* Room for "event_msgs" + '\0' + bitvec */ +- uint32 buf_key_b4_m4 = 1; +-#ifndef WL_CFG80211 +- u32 up = 0; +-#endif +- uint8 msglen; +- eventmsgs_ext_t *eventmask_msg; +- char iov_buf[WLC_IOCTL_SMLEN]; +- int ret2 = 0; +-#ifdef WLAIBSS +- aibss_bcn_force_config_t bcn_config; +- uint32 aibss; +-#ifdef WLAIBSS_PS +- uint32 aibss_ps; +-#endif /* WLAIBSS_PS */ +-#endif /* WLAIBSS */ +-#if defined(BCMSUP_4WAY_HANDSHAKE) && defined(WLAN_AKM_SUITE_FT_8021X) +- uint32 sup_wpa = 0; +-#endif +-#if defined(CUSTOM_AMPDU_BA_WSIZE) || (defined(WLAIBSS) && \ +- defined(CUSTOM_IBSS_AMPDU_BA_WSIZE)) +- uint32 ampdu_ba_wsize = 0; +-#endif /* CUSTOM_AMPDU_BA_WSIZE ||(WLAIBSS && CUSTOM_IBSS_AMPDU_BA_WSIZE) */ +-#if defined(CUSTOM_AMPDU_MPDU) +- int32 ampdu_mpdu = 0; +-#endif +-#if defined(CUSTOM_AMPDU_RELEASE) +- int32 ampdu_release = 0; +-#endif +- +-#if defined(BCMSDIO) +-#ifdef PROP_TXSTATUS +- int wlfc_enable = TRUE; +-#ifndef DISABLE_11N +- uint32 hostreorder = 1; +- uint wl_down = 1; +-#endif /* DISABLE_11N */ +-#endif /* PROP_TXSTATUS */ +-#endif +-#ifdef PCIE_FULL_DONGLE +- uint32 wl_ap_isolate; +-#endif /* PCIE_FULL_DONGLE */ +- +-#ifdef DHD_ENABLE_LPC +- uint32 lpc = 1; +-#endif /* DHD_ENABLE_LPC */ +- uint power_mode = PM_FAST; +- uint32 dongle_align = DHD_SDALIGN; +-#if defined(BCMSDIO) +- uint32 glom = CUSTOM_GLOM_SETTING; +-#endif /* defined(BCMSDIO) */ +-#if defined(CUSTOMER_HW2) && defined(USE_WL_CREDALL) +- uint32 credall = 1; +-#endif +- uint bcn_timeout = dhd->conf->bcn_timeout; +- uint retry_max = 3; +-#if defined(ARP_OFFLOAD_SUPPORT) +- int arpoe = 1; +-#endif +- int scan_assoc_time = DHD_SCAN_ASSOC_ACTIVE_TIME; +- int scan_unassoc_time = DHD_SCAN_UNASSOC_ACTIVE_TIME; +- int scan_passive_time = DHD_SCAN_PASSIVE_TIME; +- char buf[WLC_IOCTL_SMLEN]; +- char *ptr; +- uint32 listen_interval = CUSTOM_LISTEN_INTERVAL; /* Default Listen Interval in Beacons */ +-#ifdef ROAM_ENABLE +- uint roamvar = 0; +- int roam_trigger[2] = {CUSTOM_ROAM_TRIGGER_SETTING, WLC_BAND_ALL}; +- int roam_scan_period[2] = {10, WLC_BAND_ALL}; +- int roam_delta[2] = {CUSTOM_ROAM_DELTA_SETTING, WLC_BAND_ALL}; +-#ifdef FULL_ROAMING_SCAN_PERIOD_60_SEC +- int roam_fullscan_period = 60; +-#else /* FULL_ROAMING_SCAN_PERIOD_60_SEC */ +- int roam_fullscan_period = 120; +-#endif /* FULL_ROAMING_SCAN_PERIOD_60_SEC */ +-#else +-#ifdef DISABLE_BUILTIN_ROAM +- uint roamvar = 1; +-#endif /* DISABLE_BUILTIN_ROAM */ +-#endif /* ROAM_ENABLE */ +- +-#if defined(SOFTAP) +- uint dtim = 1; +-#endif +-#if (defined(AP) && !defined(WLP2P)) || (!defined(AP) && defined(WL_CFG80211)) +- uint32 mpc = 0; /* Turn MPC off for AP/APSTA mode */ +- struct ether_addr p2p_ea; +-#endif +-#ifdef BCMCCX +- uint32 ccx = 1; +-#endif +- +-#if (defined(AP) || defined(WLP2P)) && !defined(SOFTAP_AND_GC) +- uint32 apsta = 1; /* Enable APSTA mode */ +-#elif defined(SOFTAP_AND_GC) +- uint32 apsta = 0; +- int ap_mode = 1; +-#endif /* (defined(AP) || defined(WLP2P)) && !defined(SOFTAP_AND_GC) */ +-#ifdef GET_CUSTOM_MAC_ENABLE +- struct ether_addr ea_addr; +-#endif /* GET_CUSTOM_MAC_ENABLE */ +- +-#ifdef DISABLE_11N +- uint32 nmode = 0; +-#endif /* DISABLE_11N */ +- +-#if defined(DISABLE_11AC) +- uint32 vhtmode = 0; +-#endif /* DISABLE_11AC */ +-#ifdef USE_WL_TXBF +- uint32 txbf = 1; +-#endif /* USE_WL_TXBF */ +-#ifdef AMPDU_VO_ENABLE +- struct ampdu_tid_control tid; +-#endif +-#ifdef USE_WL_FRAMEBURST +- uint32 frameburst = 1; +-#endif /* USE_WL_FRAMEBURST */ +-#ifdef DHD_SET_FW_HIGHSPEED +- uint32 ack_ratio = 250; +- uint32 ack_ratio_depth = 64; +-#endif /* DHD_SET_FW_HIGHSPEED */ +-#ifdef SUPPORT_2G_VHT +- uint32 vht_features = 0x3; /* 2G enable | rates all */ +-#endif /* SUPPORT_2G_VHT */ +-#ifdef CUSTOM_PSPRETEND_THR +- uint32 pspretend_thr = CUSTOM_PSPRETEND_THR; +-#endif +-#ifdef PKT_FILTER_SUPPORT +- dhd_pkt_filter_enable = TRUE; +-#endif /* PKT_FILTER_SUPPORT */ +-#ifdef WLTDLS +- dhd->tdls_enable = FALSE; +-#endif /* WLTDLS */ +- dhd->suspend_bcn_li_dtim = CUSTOM_SUSPEND_BCN_LI_DTIM; +- DHD_TRACE(("Enter %s\n", __FUNCTION__)); +- +- dhd_conf_set_band(dhd); +- +- dhd->op_mode = 0; +- if ((!op_mode && dhd_get_fw_mode(dhd->info) == DHD_FLAG_MFG_MODE) || +- (op_mode == DHD_FLAG_MFG_MODE)) { +- /* Check and adjust IOCTL response timeout for Manufactring firmware */ +- dhd_os_set_ioctl_resp_timeout(MFG_IOCTL_RESP_TIMEOUT); +- DHD_ERROR(("%s : Set IOCTL response time for Manufactring Firmware\n", +- __FUNCTION__)); +- } +- else { +- dhd_os_set_ioctl_resp_timeout(IOCTL_RESP_TIMEOUT); +- DHD_INFO(("%s : Set IOCTL response time.\n", __FUNCTION__)); +- } +-#ifdef GET_CUSTOM_MAC_ENABLE +- ret = wifi_platform_get_mac_addr(dhd->info->adapter, ea_addr.octet); +- if (!ret) { +- memset(buf, 0, sizeof(buf)); +- bcm_mkiovar("cur_etheraddr", (void *)&ea_addr, ETHER_ADDR_LEN, buf, sizeof(buf)); +- ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, buf, sizeof(buf), TRUE, 0); +- if (ret < 0) { +- DHD_ERROR(("%s: can't set MAC address , error=%d\n", __FUNCTION__, ret)); +- return BCME_NOTUP; +- } +- memcpy(dhd->mac.octet, ea_addr.octet, ETHER_ADDR_LEN); +- } else { +-#endif /* GET_CUSTOM_MAC_ENABLE */ +- /* Get the default device MAC address directly from firmware */ +- memset(buf, 0, sizeof(buf)); +- bcm_mkiovar("cur_etheraddr", 0, 0, buf, sizeof(buf)); +- if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, buf, sizeof(buf), +- FALSE, 0)) < 0) { +- DHD_ERROR(("%s: can't get MAC address , error=%d\n", __FUNCTION__, ret)); +- return BCME_NOTUP; +- } +- /* Update public MAC address after reading from Firmware */ +- memcpy(dhd->mac.octet, buf, ETHER_ADDR_LEN); +- +-#ifdef GET_CUSTOM_MAC_ENABLE +- } +-#endif /* GET_CUSTOM_MAC_ENABLE */ +- +- /* get a capabilities from firmware */ +- memset(dhd->fw_capabilities, 0, sizeof(dhd->fw_capabilities)); +- bcm_mkiovar("cap", 0, 0, dhd->fw_capabilities, sizeof(dhd->fw_capabilities)); +- if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, dhd->fw_capabilities, +- sizeof(dhd->fw_capabilities), FALSE, 0)) < 0) { +- DHD_ERROR(("%s: Get Capability failed (error=%d)\n", +- __FUNCTION__, ret)); +- return 0; +- } +- if ((!op_mode && dhd_get_fw_mode(dhd->info) == DHD_FLAG_HOSTAP_MODE) || +- (op_mode == DHD_FLAG_HOSTAP_MODE)) { +-#ifdef SET_RANDOM_MAC_SOFTAP +- uint rand_mac; +-#endif +- dhd->op_mode = DHD_FLAG_HOSTAP_MODE; +-#if defined(ARP_OFFLOAD_SUPPORT) +- arpoe = 0; +-#endif +-#ifdef PKT_FILTER_SUPPORT +- dhd_pkt_filter_enable = FALSE; +-#endif +-#ifdef SET_RANDOM_MAC_SOFTAP +- SRANDOM32((uint)jiffies); +- rand_mac = RANDOM32(); +- iovbuf[0] = 0x02; /* locally administered bit */ +- iovbuf[1] = 0x1A; +- iovbuf[2] = 0x11; +- iovbuf[3] = (unsigned char)(rand_mac & 0x0F) | 0xF0; +- iovbuf[4] = (unsigned char)(rand_mac >> 8); +- iovbuf[5] = (unsigned char)(rand_mac >> 16); +- +- bcm_mkiovar("cur_etheraddr", (void *)iovbuf, ETHER_ADDR_LEN, buf, sizeof(buf)); +- ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, buf, sizeof(buf), TRUE, 0); +- if (ret < 0) { +- DHD_ERROR(("%s: can't set MAC address , error=%d\n", __FUNCTION__, ret)); +- } else +- memcpy(dhd->mac.octet, iovbuf, ETHER_ADDR_LEN); +-#endif /* SET_RANDOM_MAC_SOFTAP */ +-#if !defined(AP) && defined(WL_CFG80211) +- /* Turn off MPC in AP mode */ +- bcm_mkiovar("mpc", (char *)&mpc, 4, iovbuf, sizeof(iovbuf)); +- if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, +- sizeof(iovbuf), TRUE, 0)) < 0) { +- DHD_ERROR(("%s mpc for HostAPD failed %d\n", __FUNCTION__, ret)); +- } +-#endif +- } else if ((!op_mode && dhd_get_fw_mode(dhd->info) == DHD_FLAG_MFG_MODE) || +- (op_mode == DHD_FLAG_MFG_MODE)) { +-#if defined(ARP_OFFLOAD_SUPPORT) +- arpoe = 0; +-#endif /* ARP_OFFLOAD_SUPPORT */ +-#ifdef PKT_FILTER_SUPPORT +- dhd_pkt_filter_enable = FALSE; +-#endif /* PKT_FILTER_SUPPORT */ +- dhd->op_mode = DHD_FLAG_MFG_MODE; +- } else { +- uint32 concurrent_mode = 0; +- if ((!op_mode && dhd_get_fw_mode(dhd->info) == DHD_FLAG_P2P_MODE) || +- (op_mode == DHD_FLAG_P2P_MODE)) { +-#if defined(ARP_OFFLOAD_SUPPORT) +- arpoe = 0; +-#endif +-#ifdef PKT_FILTER_SUPPORT +- dhd_pkt_filter_enable = FALSE; +-#endif +- dhd->op_mode = DHD_FLAG_P2P_MODE; +- } else if ((!op_mode && dhd_get_fw_mode(dhd->info) == DHD_FLAG_IBSS_MODE) || +- (op_mode == DHD_FLAG_IBSS_MODE)) { +- dhd->op_mode = DHD_FLAG_IBSS_MODE; +- } else +- dhd->op_mode = DHD_FLAG_STA_MODE; +-#if !defined(AP) && defined(WLP2P) +- if (dhd->op_mode != DHD_FLAG_IBSS_MODE && +- (concurrent_mode = dhd_get_concurrent_capabilites(dhd))) { +-#if defined(ARP_OFFLOAD_SUPPORT) +- arpoe = 1; +-#endif +- dhd->op_mode |= concurrent_mode; +- } +- +- /* Check if we are enabling p2p */ +- if (dhd->op_mode & DHD_FLAG_P2P_MODE) { +- bcm_mkiovar("apsta", (char *)&apsta, 4, iovbuf, sizeof(iovbuf)); +- if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, +- iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) { +- DHD_ERROR(("%s APSTA for P2P failed ret= %d\n", __FUNCTION__, ret)); +- } +- +-#if defined(SOFTAP_AND_GC) +- if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_AP, +- (char *)&ap_mode, sizeof(ap_mode), TRUE, 0)) < 0) { +- DHD_ERROR(("%s WLC_SET_AP failed %d\n", __FUNCTION__, ret)); +- } +-#endif +- memcpy(&p2p_ea, &dhd->mac, ETHER_ADDR_LEN); +- ETHER_SET_LOCALADDR(&p2p_ea); +- bcm_mkiovar("p2p_da_override", (char *)&p2p_ea, +- ETHER_ADDR_LEN, iovbuf, sizeof(iovbuf)); +- if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, +- iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) { +- DHD_ERROR(("%s p2p_da_override ret= %d\n", __FUNCTION__, ret)); +- } else { +- DHD_INFO(("dhd_preinit_ioctls: p2p_da_override succeeded\n")); +- } +- } +-#else +- (void)concurrent_mode; +-#endif +- } +- +- DHD_ERROR(("Firmware up: op_mode=0x%04x, MAC="MACDBG"\n", +- dhd->op_mode, MAC2STRDBG(dhd->mac.octet))); +- /* Set Country code */ +- if (dhd->dhd_cspec.ccode[0] != 0) { +- printf("Set country %s, revision %d\n", dhd->dhd_cspec.ccode, dhd->dhd_cspec.rev); +- bcm_mkiovar("country", (char *)&dhd->dhd_cspec, +- sizeof(wl_country_t), iovbuf, sizeof(iovbuf)); +- if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) +- printf("%s: country code setting failed %d\n", __FUNCTION__, ret); +- } else { +- dhd_conf_set_country(dhd); +- dhd_conf_fix_country(dhd); +- } +- dhd_conf_get_country(dhd, &dhd->dhd_cspec); +- +-#if defined(DISABLE_11AC) +- bcm_mkiovar("vhtmode", (char *)&vhtmode, 4, iovbuf, sizeof(iovbuf)); +- if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) +- DHD_ERROR(("%s wl vhtmode 0 failed %d\n", __FUNCTION__, ret)); +-#endif /* DISABLE_11AC */ +- +- /* Set Listen Interval */ +- bcm_mkiovar("assoc_listen", (char *)&listen_interval, 4, iovbuf, sizeof(iovbuf)); +- if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) +- DHD_ERROR(("%s assoc_listen failed %d\n", __FUNCTION__, ret)); +- +-#if defined(ROAM_ENABLE) || defined(DISABLE_BUILTIN_ROAM) +- /* Disable built-in roaming to allowed ext supplicant to take care of roaming */ +- bcm_mkiovar("roam_off", (char *)&roamvar, 4, iovbuf, sizeof(iovbuf)); +- dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); +-#endif /* ROAM_ENABLE || DISABLE_BUILTIN_ROAM */ +-#if defined(ROAM_ENABLE) +- if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_ROAM_TRIGGER, roam_trigger, +- sizeof(roam_trigger), TRUE, 0)) < 0) +- DHD_ERROR(("%s: roam trigger set failed %d\n", __FUNCTION__, ret)); +- if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_ROAM_SCAN_PERIOD, roam_scan_period, +- sizeof(roam_scan_period), TRUE, 0)) < 0) +- DHD_ERROR(("%s: roam scan period set failed %d\n", __FUNCTION__, ret)); +- if ((dhd_wl_ioctl_cmd(dhd, WLC_SET_ROAM_DELTA, roam_delta, +- sizeof(roam_delta), TRUE, 0)) < 0) +- DHD_ERROR(("%s: roam delta set failed %d\n", __FUNCTION__, ret)); +- bcm_mkiovar("fullroamperiod", (char *)&roam_fullscan_period, 4, iovbuf, sizeof(iovbuf)); +- if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) +- DHD_ERROR(("%s: roam fullscan period set failed %d\n", __FUNCTION__, ret)); +-#endif /* ROAM_ENABLE */ +- dhd_conf_set_roam(dhd); +- +-#ifdef BCMCCX +- bcm_mkiovar("ccx_enable", (char *)&ccx, 4, iovbuf, sizeof(iovbuf)); +- dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); +-#endif /* BCMCCX */ +-#ifdef WLTDLS +- /* by default TDLS on and auto mode off */ +- _dhd_tdls_enable(dhd, true, false, NULL); +-#endif /* WLTDLS */ +- +-#ifdef DHD_ENABLE_LPC +- /* Set lpc 1 */ +- bcm_mkiovar("lpc", (char *)&lpc, 4, iovbuf, sizeof(iovbuf)); +- if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, +- sizeof(iovbuf), TRUE, 0)) < 0) { +- DHD_ERROR(("%s Set lpc failed %d\n", __FUNCTION__, ret)); +- } +-#endif /* DHD_ENABLE_LPC */ +- +- /* Set PowerSave mode */ +- dhd_wl_ioctl_cmd(dhd, WLC_SET_PM, (char *)&power_mode, sizeof(power_mode), TRUE, 0); +- +- /* Match Host and Dongle rx alignment */ +- bcm_mkiovar("bus:txglomalign", (char *)&dongle_align, 4, iovbuf, sizeof(iovbuf)); +- dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); +- +-#if defined(CUSTOMER_HW2) && defined(USE_WL_CREDALL) +- /* enable credall to reduce the chance of no bus credit happened. */ +- bcm_mkiovar("bus:credall", (char *)&credall, 4, iovbuf, sizeof(iovbuf)); +- dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); +-#endif +- +-#if defined(BCMSDIO) +- if (glom != DEFAULT_GLOM_VALUE) { +- DHD_INFO(("%s set glom=0x%X\n", __FUNCTION__, glom)); +- bcm_mkiovar("bus:txglom", (char *)&glom, 4, iovbuf, sizeof(iovbuf)); +- dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); +- } +-#endif /* defined(BCMSDIO) */ +- dhd_conf_set_glom(dhd); +- +- /* Setup timeout if Beacons are lost and roam is off to report link down */ +- bcm_mkiovar("bcn_timeout", (char *)&bcn_timeout, 4, iovbuf, sizeof(iovbuf)); +- dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); +- /* Setup assoc_retry_max count to reconnect target AP in dongle */ +- bcm_mkiovar("assoc_retry_max", (char *)&retry_max, 4, iovbuf, sizeof(iovbuf)); +- dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); +-#if defined(AP) && !defined(WLP2P) +- /* Turn off MPC in AP mode */ +- bcm_mkiovar("mpc", (char *)&mpc, 4, iovbuf, sizeof(iovbuf)); +- dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); +- bcm_mkiovar("apsta", (char *)&apsta, 4, iovbuf, sizeof(iovbuf)); +- dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); +-#endif /* defined(AP) && !defined(WLP2P) */ +- dhd_conf_set_mimo_bw_cap(dhd); +- dhd_conf_force_wme(dhd); +- dhd_conf_set_stbc(dhd); +- dhd_conf_set_srl(dhd); +- dhd_conf_set_lrl(dhd); +- dhd_conf_set_spect(dhd); +- +-#if defined(SOFTAP) +- if (ap_fw_loaded == TRUE) { +- dhd_wl_ioctl_cmd(dhd, WLC_SET_DTIMPRD, (char *)&dtim, sizeof(dtim), TRUE, 0); +- } +-#endif +- +-#if defined(KEEP_ALIVE) +- { +- /* Set Keep Alive : be sure to use FW with -keepalive */ +- int res; +- +-#if defined(SOFTAP) +- if (ap_fw_loaded == FALSE) +-#endif +- if (!(dhd->op_mode & +- (DHD_FLAG_HOSTAP_MODE | DHD_FLAG_MFG_MODE))) { +- if ((res = dhd_keep_alive_onoff(dhd)) < 0) +- DHD_ERROR(("%s set keeplive failed %d\n", +- __FUNCTION__, res)); +- } +- } +-#endif /* defined(KEEP_ALIVE) */ +- +-#ifdef USE_WL_TXBF +- bcm_mkiovar("txbf", (char *)&txbf, 4, iovbuf, sizeof(iovbuf)); +- if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, +- sizeof(iovbuf), TRUE, 0)) < 0) { +- DHD_ERROR(("%s Set txbf failed %d\n", __FUNCTION__, ret)); +- } +-#endif /* USE_WL_TXBF */ +-#ifdef USE_WL_FRAMEBURST +- /* Set frameburst to value */ +- if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_FAKEFRAG, (char *)&frameburst, +- sizeof(frameburst), TRUE, 0)) < 0) { +- DHD_ERROR(("%s Set frameburst failed %d\n", __FUNCTION__, ret)); +- } +-#endif /* USE_WL_FRAMEBURST */ +-#ifdef DHD_SET_FW_HIGHSPEED +- /* Set ack_ratio */ +- bcm_mkiovar("ack_ratio", (char *)&ack_ratio, 4, iovbuf, sizeof(iovbuf)); +- if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, +- sizeof(iovbuf), TRUE, 0)) < 0) { +- DHD_ERROR(("%s Set ack_ratio failed %d\n", __FUNCTION__, ret)); +- } +- +- /* Set ack_ratio_depth */ +- bcm_mkiovar("ack_ratio_depth", (char *)&ack_ratio_depth, 4, iovbuf, sizeof(iovbuf)); +- if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, +- sizeof(iovbuf), TRUE, 0)) < 0) { +- DHD_ERROR(("%s Set ack_ratio_depth failed %d\n", __FUNCTION__, ret)); +- } +-#endif /* DHD_SET_FW_HIGHSPEED */ +-#if defined(CUSTOM_AMPDU_BA_WSIZE) || (defined(WLAIBSS) && \ +- defined(CUSTOM_IBSS_AMPDU_BA_WSIZE)) +- /* Set ampdu ba wsize to 64 or 16 */ +-#ifdef CUSTOM_AMPDU_BA_WSIZE +- ampdu_ba_wsize = CUSTOM_AMPDU_BA_WSIZE; +-#endif +-#if defined(WLAIBSS) && defined(CUSTOM_IBSS_AMPDU_BA_WSIZE) +- if (dhd->op_mode == DHD_FLAG_IBSS_MODE) +- ampdu_ba_wsize = CUSTOM_IBSS_AMPDU_BA_WSIZE; +-#endif /* WLAIBSS && CUSTOM_IBSS_AMPDU_BA_WSIZE */ +- if (ampdu_ba_wsize != 0) { +- bcm_mkiovar("ampdu_ba_wsize", (char *)&du_ba_wsize, 4, iovbuf, sizeof(iovbuf)); +- if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, +- sizeof(iovbuf), TRUE, 0)) < 0) { +- DHD_ERROR(("%s Set ampdu_ba_wsize to %d failed %d\n", +- __FUNCTION__, ampdu_ba_wsize, ret)); +- } +- } +-#endif /* CUSTOM_AMPDU_BA_WSIZE || (WLAIBSS && CUSTOM_IBSS_AMPDU_BA_WSIZE) */ +- +-#ifdef WLAIBSS +- /* Configure custom IBSS beacon transmission */ +- if (dhd->op_mode & DHD_FLAG_IBSS_MODE) +- { +- aibss = 1; +- bcm_mkiovar("aibss", (char *)&aibss, 4, iovbuf, sizeof(iovbuf)); +- if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, +- sizeof(iovbuf), TRUE, 0)) < 0) { +- DHD_ERROR(("%s Set aibss to %d failed %d\n", +- __FUNCTION__, aibss, ret)); +- } +-#ifdef WLAIBSS_PS +- aibss_ps = 1; +- bcm_mkiovar("aibss_ps", (char *)&aibss_ps, 4, iovbuf, sizeof(iovbuf)); +- if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, +- sizeof(iovbuf), TRUE, 0)) < 0) { +- DHD_ERROR(("%s Set aibss PS to %d failed %d\n", +- __FUNCTION__, aibss, ret)); +- } +-#endif /* WLAIBSS_PS */ +- } +- memset(&bcn_config, 0, sizeof(bcn_config)); +- bcn_config.initial_min_bcn_dur = AIBSS_INITIAL_MIN_BCN_DUR; +- bcn_config.min_bcn_dur = AIBSS_MIN_BCN_DUR; +- bcn_config.bcn_flood_dur = AIBSS_BCN_FLOOD_DUR; +- bcn_config.version = AIBSS_BCN_FORCE_CONFIG_VER_0; +- bcn_config.len = sizeof(bcn_config); +- +- bcm_mkiovar("aibss_bcn_force_config", (char *)&bcn_config, +- sizeof(aibss_bcn_force_config_t), iov_buf, sizeof(iov_buf)); +- if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iov_buf, +- sizeof(iov_buf), TRUE, 0)) < 0) { +- DHD_ERROR(("%s Set aibss_bcn_force_config to %d, %d, %d failed %d\n", +- __FUNCTION__, AIBSS_INITIAL_MIN_BCN_DUR, AIBSS_MIN_BCN_DUR, +- AIBSS_BCN_FLOOD_DUR, ret)); +- } +-#endif /* WLAIBSS */ +- +-#if defined(CUSTOM_AMPDU_MPDU) +- ampdu_mpdu = CUSTOM_AMPDU_MPDU; +- if (ampdu_mpdu != 0 && (ampdu_mpdu <= ampdu_ba_wsize)) { +- bcm_mkiovar("ampdu_mpdu", (char *)&du_mpdu, 4, iovbuf, sizeof(iovbuf)); +- if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, +- sizeof(iovbuf), TRUE, 0)) < 0) { +- DHD_ERROR(("%s Set ampdu_mpdu to %d failed %d\n", +- __FUNCTION__, CUSTOM_AMPDU_MPDU, ret)); +- } +- } +-#endif /* CUSTOM_AMPDU_MPDU */ +- dhd_conf_set_ampdu_ba_wsize(dhd); +- +-#if defined(CUSTOM_AMPDU_RELEASE) +- ampdu_release = CUSTOM_AMPDU_RELEASE; +- if (ampdu_release != 0 && (ampdu_release <= ampdu_ba_wsize)) { +- bcm_mkiovar("ampdu_release", (char *)&du_release, 4, iovbuf, sizeof(iovbuf)); +- if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, +- sizeof(iovbuf), TRUE, 0)) < 0) { +- DHD_ERROR(("%s Set ampdu_release to %d failed %d\n", +- __FUNCTION__, CUSTOM_AMPDU_RELEASE, ret)); +- } +- } +-#endif /* CUSTOM_AMPDU_RELEASE */ +- +-#if defined(BCMSUP_4WAY_HANDSHAKE) && defined(WLAN_AKM_SUITE_FT_8021X) +- /* Read 4-way handshake requirements */ +- if (dhd_use_idsup == 1) { +- bcm_mkiovar("sup_wpa", (char *)&sup_wpa, 4, iovbuf, sizeof(iovbuf)); +- ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, iovbuf, sizeof(iovbuf), FALSE, 0); +- /* sup_wpa iovar returns NOTREADY status on some platforms using modularized +- * in-dongle supplicant. +- */ +- if (ret >= 0 || ret == BCME_NOTREADY) +- dhd->fw_4way_handshake = TRUE; +- DHD_TRACE(("4-way handshake mode is: %d\n", dhd->fw_4way_handshake)); +- } +-#endif /* BCMSUP_4WAY_HANDSHAKE && WLAN_AKM_SUITE_FT_8021X */ +-#ifdef SUPPORT_2G_VHT +- bcm_mkiovar("vht_features", (char *)&vht_features, 4, iovbuf, sizeof(iovbuf)); +- if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) { +- DHD_ERROR(("%s vht_features set failed %d\n", __FUNCTION__, ret)); +- } +-#endif /* SUPPORT_2G_VHT */ +-#ifdef CUSTOM_PSPRETEND_THR +- /* Turn off MPC in AP mode */ +- bcm_mkiovar("pspretend_threshold", (char *)&pspretend_thr, 4, +- iovbuf, sizeof(iovbuf)); +- if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, +- sizeof(iovbuf), TRUE, 0)) < 0) { +- DHD_ERROR(("%s pspretend_threshold for HostAPD failed %d\n", +- __FUNCTION__, ret)); +- } +-#endif +- +- bcm_mkiovar("buf_key_b4_m4", (char *)&buf_key_b4_m4, 4, iovbuf, sizeof(iovbuf)); +- if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, +- sizeof(iovbuf), TRUE, 0)) < 0) { +- DHD_ERROR(("%s buf_key_b4_m4 set failed %d\n", __FUNCTION__, ret)); +- } +- +- /* Read event_msgs mask */ +- bcm_mkiovar("event_msgs", eventmask, WL_EVENTING_MASK_LEN, iovbuf, sizeof(iovbuf)); +- if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, iovbuf, sizeof(iovbuf), FALSE, 0)) < 0) { +- DHD_ERROR(("%s read Event mask failed %d\n", __FUNCTION__, ret)); +- goto done; +- } +- bcopy(iovbuf, eventmask, WL_EVENTING_MASK_LEN); +- +- /* Setup event_msgs */ +- setbit(eventmask, WLC_E_SET_SSID); +- setbit(eventmask, WLC_E_PRUNE); +- setbit(eventmask, WLC_E_AUTH); +- setbit(eventmask, WLC_E_AUTH_IND); +- setbit(eventmask, WLC_E_ASSOC); +- setbit(eventmask, WLC_E_REASSOC); +- setbit(eventmask, WLC_E_REASSOC_IND); +- setbit(eventmask, WLC_E_DEAUTH); +- setbit(eventmask, WLC_E_DEAUTH_IND); +- setbit(eventmask, WLC_E_DISASSOC_IND); +- setbit(eventmask, WLC_E_DISASSOC); +- setbit(eventmask, WLC_E_JOIN); +- setbit(eventmask, WLC_E_START); +- setbit(eventmask, WLC_E_ASSOC_IND); +- setbit(eventmask, WLC_E_PSK_SUP); +- setbit(eventmask, WLC_E_LINK); +- setbit(eventmask, WLC_E_NDIS_LINK); +- setbit(eventmask, WLC_E_MIC_ERROR); +- setbit(eventmask, WLC_E_ASSOC_REQ_IE); +- setbit(eventmask, WLC_E_ASSOC_RESP_IE); +-#ifndef WL_CFG80211 +- setbit(eventmask, WLC_E_PMKID_CACHE); +- setbit(eventmask, WLC_E_TXFAIL); +-#endif +- setbit(eventmask, WLC_E_JOIN_START); +- setbit(eventmask, WLC_E_SCAN_COMPLETE); +-#ifdef WLMEDIA_HTSF +- setbit(eventmask, WLC_E_HTSFSYNC); +-#endif /* WLMEDIA_HTSF */ +-#ifdef PNO_SUPPORT +- setbit(eventmask, WLC_E_PFN_NET_FOUND); +- setbit(eventmask, WLC_E_PFN_BEST_BATCHING); +- setbit(eventmask, WLC_E_PFN_BSSID_NET_FOUND); +- setbit(eventmask, WLC_E_PFN_BSSID_NET_LOST); +-#endif /* PNO_SUPPORT */ +- /* enable dongle roaming event */ +- setbit(eventmask, WLC_E_ROAM); +- setbit(eventmask, WLC_E_BSSID); +-#ifdef BCMCCX +- setbit(eventmask, WLC_E_ADDTS_IND); +- setbit(eventmask, WLC_E_DELTS_IND); +-#endif /* BCMCCX */ +-#ifdef WLTDLS +- setbit(eventmask, WLC_E_TDLS_PEER_EVENT); +-#endif /* WLTDLS */ +-#ifdef WL_CFG80211 +- setbit(eventmask, WLC_E_ESCAN_RESULT); +- if (dhd->op_mode & DHD_FLAG_P2P_MODE) { +- setbit(eventmask, WLC_E_ACTION_FRAME_RX); +- setbit(eventmask, WLC_E_P2P_DISC_LISTEN_COMPLETE); +- } +-#endif /* WL_CFG80211 */ +-#ifdef WLAIBSS +- setbit(eventmask, WLC_E_AIBSS_TXFAIL); +-#endif /* WLAIBSS */ +- setbit(eventmask, WLC_E_TRACE); +- +- /* Write updated Event mask */ +- bcm_mkiovar("event_msgs", eventmask, WL_EVENTING_MASK_LEN, iovbuf, sizeof(iovbuf)); +- if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) { +- DHD_ERROR(("%s Set Event mask failed %d\n", __FUNCTION__, ret)); +- goto done; +- } +- +- /* make up event mask ext message iovar for event larger than 128 */ +- msglen = ROUNDUP(WLC_E_LAST, NBBY)/NBBY + EVENTMSGS_EXT_STRUCT_SIZE; +- eventmask_msg = (eventmsgs_ext_t*)kmalloc(msglen, GFP_KERNEL); +- if (eventmask_msg == NULL) { +- DHD_ERROR(("failed to allocate %d bytes for event_msg_ext\n", msglen)); +- return BCME_NOMEM; +- } +- bzero(eventmask_msg, msglen); +- eventmask_msg->ver = EVENTMSGS_VER; +- eventmask_msg->len = ROUNDUP(WLC_E_LAST, NBBY)/NBBY; +- +- /* Read event_msgs_ext mask */ +- bcm_mkiovar("event_msgs_ext", (char *)eventmask_msg, msglen, iov_buf, sizeof(iov_buf)); +- ret2 = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, iov_buf, sizeof(iov_buf), FALSE, 0); +- if (ret2 != BCME_UNSUPPORTED) +- ret = ret2; +- if (ret2 == 0) { /* event_msgs_ext must be supported */ +- bcopy(iov_buf, eventmask_msg, msglen); +- +-#ifdef BT_WIFI_HANDOVER +- setbit(eventmask_msg->mask, WLC_E_BT_WIFI_HANDOVER_REQ); +-#endif /* BT_WIFI_HANDOVER */ +- +- /* Write updated Event mask */ +- eventmask_msg->ver = EVENTMSGS_VER; +- eventmask_msg->command = EVENTMSGS_SET_MASK; +- eventmask_msg->len = ROUNDUP(WLC_E_LAST, NBBY)/NBBY; +- bcm_mkiovar("event_msgs_ext", (char *)eventmask_msg, +- msglen, iov_buf, sizeof(iov_buf)); +- if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, +- iov_buf, sizeof(iov_buf), TRUE, 0)) < 0) { +- DHD_ERROR(("%s write event mask ext failed %d\n", __FUNCTION__, ret)); +- kfree(eventmask_msg); +- goto done; +- } +- } else if (ret2 < 0 && ret2 != BCME_UNSUPPORTED) { +- DHD_ERROR(("%s read event mask ext failed %d\n", __FUNCTION__, ret2)); +- kfree(eventmask_msg); +- goto done; +- } /* unsupported is ok */ +- kfree(eventmask_msg); +- +- dhd_wl_ioctl_cmd(dhd, WLC_SET_SCAN_CHANNEL_TIME, (char *)&scan_assoc_time, +- 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 defined(SOFTAP) +- if (arpoe && !ap_fw_loaded) +-#else +- if (arpoe) +-#endif +- { +- dhd_arp_offload_enable(dhd, TRUE); +- dhd_arp_offload_set(dhd, dhd_arp_mode); +- } else { +- dhd_arp_offload_enable(dhd, FALSE); +- dhd_arp_offload_set(dhd, 0); +- } +- dhd_arp_enable = arpoe; +-#endif /* ARP_OFFLOAD_SUPPORT */ +- +-#ifdef PKT_FILTER_SUPPORT +- /* Setup default defintions for pktfilter , enable in suspend */ +- dhd->pktfilter_count = 6; +- /* Setup filter to allow only unicast */ +- if (dhd_master_mode) { +- dhd->pktfilter[DHD_UNICAST_FILTER_NUM] = "100 0 0 0 0x01 0x00"; +- dhd->pktfilter[DHD_BROADCAST_FILTER_NUM] = NULL; +- dhd->pktfilter[DHD_MULTICAST4_FILTER_NUM] = NULL; +- dhd->pktfilter[DHD_MULTICAST6_FILTER_NUM] = NULL; +- /* Add filter to pass multicastDNS packet and NOT filter out as Broadcast */ +- dhd->pktfilter[DHD_MDNS_FILTER_NUM] = "104 0 0 0 0xFFFFFFFFFFFF 0x01005E0000FB"; +- /* apply APP pktfilter */ +- dhd->pktfilter[DHD_ARP_FILTER_NUM] = "105 0 0 12 0xFFFF 0x0806"; +- } else +- dhd_conf_discard_pkt_filter(dhd); +- dhd_conf_add_pkt_filter(dhd); +- +-#if defined(SOFTAP) +- if (ap_fw_loaded) { +- dhd_enable_packet_filter(0, dhd); +- } +-#endif /* defined(SOFTAP) */ +- dhd_set_packet_filter(dhd); +-#endif /* PKT_FILTER_SUPPORT */ +-#ifdef DISABLE_11N +- bcm_mkiovar("nmode", (char *)&nmode, 4, iovbuf, sizeof(iovbuf)); +- if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) +- DHD_ERROR(("%s wl nmode 0 failed %d\n", __FUNCTION__, ret)); +-#endif /* DISABLE_11N */ +- +-#ifdef AMPDU_VO_ENABLE +- tid.tid = PRIO_8021D_VO; /* Enable TID(6) for voice */ +- tid.enable = TRUE; +- bcm_mkiovar("ampdu_tid", (char *)&tid, sizeof(tid), iovbuf, sizeof(iovbuf)); +- dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); +- +- tid.tid = PRIO_8021D_NC; /* Enable TID(7) for voice */ +- tid.enable = TRUE; +- bcm_mkiovar("ampdu_tid", (char *)&tid, sizeof(tid), iovbuf, sizeof(iovbuf)); +- dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); +-#endif +-#if defined(SOFTAP_TPUT_ENHANCE) +- if (dhd->op_mode & DHD_FLAG_HOSTAP_MODE) { +- dhd_bus_setidletime(dhd, (int)100); +-#ifdef DHDTCPACK_SUPPRESS +- dhd->tcpack_sup_enabled = FALSE; +-#endif +-#if defined(DHD_TCP_WINSIZE_ADJUST) +- dhd_use_tcp_window_size_adjust = TRUE; +-#endif +- +- memset(buf, 0, sizeof(buf)); +- bcm_mkiovar("bus:txglom_auto_control", 0, 0, buf, sizeof(buf)); +- if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, buf, sizeof(buf), FALSE, 0)) < 0) { +- glom = 0; +- bcm_mkiovar("bus:txglom", (char *)&glom, 4, iovbuf, sizeof(iovbuf)); +- dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); +- } +- else { +- if (buf[0] == 0) { +- glom = 1; +- bcm_mkiovar("bus:txglom_auto_control", (char *)&glom, 4, iovbuf, +- sizeof(iovbuf)); +- dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); +- } +- } +- } +-#endif /* SOFTAP_TPUT_ENHANCE */ +- +- /* query for 'ver' to get version info from firmware */ +- memset(buf, 0, sizeof(buf)); +- ptr = buf; +- bcm_mkiovar("ver", (char *)&buf, 4, buf, sizeof(buf)); +- if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, buf, sizeof(buf), FALSE, 0)) < 0) +- DHD_ERROR(("%s failed %d\n", __FUNCTION__, ret)); +- else { +- bcmstrtok(&ptr, "\n", 0); +- /* Print fw version info */ +- DHD_ERROR(("Firmware version = %s\n", buf)); +-#if defined(BCMSDIO) +- dhd_set_version_info(dhd, buf); +-#endif /* defined(BCMSDIO) */ +- } +- +-#if defined(BCMSDIO) +- dhd_txglom_enable(dhd, TRUE); +-#endif /* defined(BCMSDIO) */ +- +-#if defined(BCMSDIO) +-#ifdef PROP_TXSTATUS +- if (disable_proptx || +-#ifdef PROP_TXSTATUS_VSDB +- /* enable WLFC only if the firmware is VSDB when it is in STA mode */ +- (dhd->op_mode != DHD_FLAG_HOSTAP_MODE && +- dhd->op_mode != DHD_FLAG_IBSS_MODE) || +-#endif /* PROP_TXSTATUS_VSDB */ +- FALSE) { +- wlfc_enable = FALSE; +- } +- +-#ifndef DISABLE_11N +- ret = dhd_wl_ioctl_cmd(dhd, WLC_DOWN, (char *)&wl_down, sizeof(wl_down), TRUE, 0); +- bcm_mkiovar("ampdu_hostreorder", (char *)&hostreorder, 4, iovbuf, sizeof(iovbuf)); +- if ((ret2 = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) { +- DHD_ERROR(("%s wl ampdu_hostreorder failed %d\n", __FUNCTION__, ret2)); +- if (ret2 != BCME_UNSUPPORTED) +- ret = ret2; +- if (ret2 != BCME_OK) +- hostreorder = 0; +- } +-#endif /* DISABLE_11N */ +- +-#ifdef READ_CONFIG_FROM_FILE +- dhd_preinit_config(dhd, 0); +-#endif /* READ_CONFIG_FROM_FILE */ +- +- if (wlfc_enable) +- dhd_wlfc_init(dhd); +-#ifndef DISABLE_11N +- else if (hostreorder) +- dhd_wlfc_hostreorder_init(dhd); +-#endif /* DISABLE_11N */ +- +-#endif /* PROP_TXSTATUS */ +-#endif /* BCMSDIO || BCMBUS */ +-#ifdef PCIE_FULL_DONGLE +- /* For FD we need all the packets at DHD to handle intra-BSS forwarding */ +- if (FW_SUPPORTED(dhd, ap)) { +- wl_ap_isolate = AP_ISOLATE_SENDUP_ALL; +- bcm_mkiovar("ap_isolate", (char *)&wl_ap_isolate, 4, iovbuf, sizeof(iovbuf)); +- if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) +- DHD_ERROR(("%s failed %d\n", __FUNCTION__, ret)); +- } +-#endif /* PCIE_FULL_DONGLE */ +-#ifdef PNO_SUPPORT +- if (!dhd->pno_state) { +- dhd_pno_init(dhd); +- } +-#endif +-#ifdef WL11U +- dhd_interworking_enable(dhd); +-#endif /* WL11U */ +-#ifndef WL_CFG80211 +- dhd_wl_ioctl_cmd(dhd, WLC_UP, (char *)&up, sizeof(up), TRUE, 0); +-#endif +- +-done: +- return ret; +-} +- +- +-int +-dhd_iovar(dhd_pub_t *pub, int ifidx, char *name, char *cmd_buf, uint cmd_len, int set) +-{ +- char buf[strlen(name) + 1 + cmd_len]; +- int len = sizeof(buf); +- wl_ioctl_t ioc; +- int ret; +- +- len = bcm_mkiovar(name, cmd_buf, cmd_len, buf, len); +- +- memset(&ioc, 0, sizeof(ioc)); +- +- ioc.cmd = set? WLC_SET_VAR : WLC_GET_VAR; +- ioc.buf = buf; +- ioc.len = len; +- ioc.set = set; +- +- ret = dhd_wl_ioctl(pub, ifidx, &ioc, ioc.buf, ioc.len); +- if (!set && ret >= 0) +- memcpy(cmd_buf, buf, cmd_len); +- +- return ret; +-} +- +-int dhd_change_mtu(dhd_pub_t *dhdp, int new_mtu, int ifidx) +-{ +- struct dhd_info *dhd = dhdp->info; +- struct net_device *dev = NULL; +- +- ASSERT(dhd && dhd->iflist[ifidx]); +- dev = dhd->iflist[ifidx]->net; +- ASSERT(dev); +- +- if (netif_running(dev)) { +- DHD_ERROR(("%s: Must be down to change its MTU", dev->name)); +- return BCME_NOTDOWN; +- } +- +-#define DHD_MIN_MTU 1500 +-#define DHD_MAX_MTU 1752 +- +- if ((new_mtu < DHD_MIN_MTU) || (new_mtu > DHD_MAX_MTU)) { +- DHD_ERROR(("%s: MTU size %d is invalid.\n", __FUNCTION__, new_mtu)); +- return BCME_BADARG; +- } +- +- dev->mtu = new_mtu; +- return 0; +-} +- +-#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, int idx) +-{ +- u32 ipv4_buf[MAX_IPV4_ENTRIES]; /* temp save for AOE host_ip table */ +- int i; +- int ret; +- +- bzero(ipv4_buf, sizeof(ipv4_buf)); +- +- /* display what we've got */ +- ret = dhd_arp_get_arp_hostip_table(dhd_pub, ipv4_buf, sizeof(ipv4_buf), idx); +- DHD_ARPOE(("%s: hostip table read from Dongle:\n", __FUNCTION__)); +-#ifdef AOE_DBG +- dhd_print_buf(ipv4_buf, 32, 4); /* max 8 IPs 4b each */ +-#endif +- /* now we saved hoste_ip table, clr it in the dongle AOE */ +- dhd_aoe_hostip_clr(dhd_pub, idx); +- +- if (ret) { +- DHD_ERROR(("%s failed\n", __FUNCTION__)); +- return; +- } +- +- for (i = 0; i < MAX_IPV4_ENTRIES; i++) { +- if (add && (ipv4_buf[i] == 0)) { +- 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", +- __FUNCTION__, ipa, i)); +- } +- +- if (ipv4_buf[i] != 0) { +- /* add back host_ip entries from our local cache */ +- dhd_arp_offload_add_ip(dhd_pub, ipv4_buf[i], idx); +- DHD_ARPOE(("%s: added IP:%x to dongle arp_hostip[%d]\n\n", +- __FUNCTION__, ipv4_buf[i], i)); +- } +- } +-#ifdef AOE_DBG +- /* see the resulting hostip table */ +- dhd_arp_get_arp_hostip_table(dhd_pub, ipv4_buf, sizeof(ipv4_buf), idx); +- DHD_ARPOE(("%s: read back arp_hostip table:\n", __FUNCTION__)); +- dhd_print_buf(ipv4_buf, 32, 4); /* max 8 IPs 4b each */ +-#endif +-} +- +-/* +- * Notification mechanism from kernel to our driver. This function is called by the Linux kernel +- * whenever there is an event related to an IP address. +- * ptr : kernel provided pointer to IP address that has changed +- */ +-static int dhd_inetaddr_notifier_call(struct notifier_block *this, +- unsigned long event, +- void *ptr) +-{ +- struct in_ifaddr *ifa = (struct in_ifaddr *)ptr; +- +- dhd_info_t *dhd; +- dhd_pub_t *dhd_pub; +- int idx; +- +- if (!dhd_arp_enable) +- return NOTIFY_DONE; +- if (!ifa || !(ifa->ifa_dev->dev)) +- return NOTIFY_DONE; +- +-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)) +- /* Filter notifications meant for non Broadcom devices */ +- if ((ifa->ifa_dev->dev->netdev_ops != &dhd_ops_pri) && +- (ifa->ifa_dev->dev->netdev_ops != &dhd_ops_virt)) { +-#if defined(WL_ENABLE_P2P_IF) +- if (!wl_cfgp2p_is_ifops(ifa->ifa_dev->dev->netdev_ops)) +-#endif /* WL_ENABLE_P2P_IF */ +- return NOTIFY_DONE; +- } +-#endif /* LINUX_VERSION_CODE */ +- +- dhd = DHD_DEV_INFO(ifa->ifa_dev->dev); +- if (!dhd) +- return NOTIFY_DONE; +- +- dhd_pub = &dhd->pub; +- +- if (dhd_pub->arp_version == 1) { +- idx = 0; +- } +- else { +- for (idx = 0; idx < DHD_MAX_IFS; idx++) { +- if (dhd->iflist[idx] && dhd->iflist[idx]->net == ifa->ifa_dev->dev) +- break; +- } +- if (idx < DHD_MAX_IFS) +- DHD_TRACE(("ifidx : %p %s %d\n", dhd->iflist[idx]->net, +- dhd->iflist[idx]->name, dhd->iflist[idx]->idx)); +- else { +- DHD_ERROR(("Cannot find ifidx for(%s) set to 0\n", ifa->ifa_label)); +- idx = 0; +- } +- } +- +- switch (event) { +- case NETDEV_UP: +- DHD_ARPOE(("%s: [%s] Up IP: 0x%x\n", +- __FUNCTION__, ifa->ifa_label, ifa->ifa_address)); +- +- if (dhd->pub.busstate != DHD_BUS_DATA) { +- DHD_ERROR(("%s: bus not ready, exit\n", __FUNCTION__)); +- if (dhd->pend_ipaddr) { +- DHD_ERROR(("%s: overwrite pending ipaddr: 0x%x\n", +- __FUNCTION__, dhd->pend_ipaddr)); +- } +- dhd->pend_ipaddr = ifa->ifa_address; +- break; +- } +- +-#ifdef AOE_IP_ALIAS_SUPPORT +- DHD_ARPOE(("%s:add aliased IP to AOE hostip cache\n", +- __FUNCTION__)); +- aoe_update_host_ipv4_table(dhd_pub, ifa->ifa_address, TRUE, idx); +-#endif /* AOE_IP_ALIAS_SUPPORT */ +- break; +- +- case NETDEV_DOWN: +- DHD_ARPOE(("%s: [%s] Down IP: 0x%x\n", +- __FUNCTION__, ifa->ifa_label, ifa->ifa_address)); +- dhd->pend_ipaddr = 0; +-#ifdef AOE_IP_ALIAS_SUPPORT +- DHD_ARPOE(("%s:interface is down, AOE clr all for this if\n", +- __FUNCTION__)); +- aoe_update_host_ipv4_table(dhd_pub, ifa->ifa_address, FALSE, idx); +-#else +- dhd_aoe_hostip_clr(&dhd->pub, idx); +- dhd_aoe_arp_clr(&dhd->pub, idx); +-#endif /* AOE_IP_ALIAS_SUPPORT */ +- break; +- +- default: +- DHD_ARPOE(("%s: do noting for [%s] Event: %lu\n", +- __func__, ifa->ifa_label, event)); +- break; +- } +- return NOTIFY_DONE; +-} +-#endif /* ARP_OFFLOAD_SUPPORT */ +- +-#ifdef CONFIG_IPV6 +-/* Neighbor Discovery Offload: defered handler */ +-static void +-dhd_inet6_work_handler(void *dhd_info, void *event_data, u8 event) +-{ +- struct ipv6_work_info_t *ndo_work = (struct ipv6_work_info_t *)event_data; +- dhd_pub_t *pub = &((dhd_info_t *)dhd_info)->pub; +- int ret; +- +- if (event != DHD_WQ_WORK_IPV6_NDO) { +- DHD_ERROR(("%s: unexpected event \n", __FUNCTION__)); +- return; +- } +- +- if (!ndo_work) { +- DHD_ERROR(("%s: ipv6 work info is not initialized \n", __FUNCTION__)); +- return; +- } +- +- if (!pub) { +- DHD_ERROR(("%s: dhd pub is not initialized \n", __FUNCTION__)); +- return; +- } +- +- if (ndo_work->if_idx) { +- DHD_ERROR(("%s: idx %d \n", __FUNCTION__, ndo_work->if_idx)); +- return; +- } +- +- switch (ndo_work->event) { +- case NETDEV_UP: +- DHD_TRACE(("%s: Enable NDO and add ipv6 into table \n ", __FUNCTION__)); +- ret = dhd_ndo_enable(pub, TRUE); +- if (ret < 0) { +- DHD_ERROR(("%s: Enabling NDO Failed %d\n", __FUNCTION__, ret)); +- } +- +- ret = dhd_ndo_add_ip(pub, &ndo_work->ipv6_addr[0], ndo_work->if_idx); +- if (ret < 0) { +- DHD_ERROR(("%s: Adding host ip for NDO failed %d\n", +- __FUNCTION__, ret)); +- } +- break; +- case NETDEV_DOWN: +- DHD_TRACE(("%s: clear ipv6 table \n", __FUNCTION__)); +- ret = dhd_ndo_remove_ip(pub, ndo_work->if_idx); +- if (ret < 0) { +- DHD_ERROR(("%s: Removing host ip for NDO failed %d\n", +- __FUNCTION__, ret)); +- goto done; +- } +- +- ret = dhd_ndo_enable(pub, FALSE); +- if (ret < 0) { +- DHD_ERROR(("%s: disabling NDO Failed %d\n", __FUNCTION__, ret)); +- goto done; +- } +- break; +- default: +- DHD_ERROR(("%s: unknown notifier event \n", __FUNCTION__)); +- break; +- } +-done: +- /* free ndo_work. alloced while scheduling the work */ +- kfree(ndo_work); +- +- return; +-} +- +-/* +- * Neighbor Discovery Offload: Called when an interface +- * is assigned with ipv6 address. +- * Handles only primary interface +- */ +-static int dhd_inet6addr_notifier_call(struct notifier_block *this, +- unsigned long event, +- void *ptr) +-{ +- dhd_info_t *dhd; +- dhd_pub_t *dhd_pub; +- struct inet6_ifaddr *inet6_ifa = ptr; +- struct in6_addr *ipv6_addr = &inet6_ifa->addr; +- struct ipv6_work_info_t *ndo_info; +- int idx = 0; /* REVISIT */ +- +-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)) +- /* Filter notifications meant for non Broadcom devices */ +- if (inet6_ifa->idev->dev->netdev_ops != &dhd_ops_pri) { +- return NOTIFY_DONE; +- } +-#endif /* LINUX_VERSION_CODE */ +- +- dhd = DHD_DEV_INFO(inet6_ifa->idev->dev); +- if (!dhd) +- return NOTIFY_DONE; +- +- if (dhd->iflist[idx] && dhd->iflist[idx]->net != inet6_ifa->idev->dev) +- return NOTIFY_DONE; +- dhd_pub = &dhd->pub; +- if (!FW_SUPPORTED(dhd_pub, ndoe)) +- return NOTIFY_DONE; +- +- ndo_info = (struct ipv6_work_info_t *)kzalloc(sizeof(struct ipv6_work_info_t), GFP_ATOMIC); +- if (!ndo_info) { +- DHD_ERROR(("%s: ipv6 work alloc failed\n", __FUNCTION__)); +- return NOTIFY_DONE; +- } +- +- ndo_info->event = event; +- ndo_info->if_idx = idx; +- memcpy(&ndo_info->ipv6_addr[0], ipv6_addr, IPV6_ADDR_LEN); +- +- /* defer the work to thread as it may block kernel */ +- dhd_deferred_schedule_work(dhd->dhd_deferred_wq, (void *)ndo_info, DHD_WQ_WORK_IPV6_NDO, +- dhd_inet6_work_handler, DHD_WORK_PRIORITY_LOW); +- return NOTIFY_DONE; +-} +-#endif /* #ifdef CONFIG_IPV6 */ +- +-int +-dhd_register_if(dhd_pub_t *dhdp, int ifidx, bool need_rtnl_lock) +-{ +- dhd_info_t *dhd = (dhd_info_t *)dhdp->info; +- dhd_if_t *ifp; +- struct net_device *net = NULL; +- int err = 0; +- uint8 temp_addr[ETHER_ADDR_LEN] = { 0x00, 0x90, 0x4c, 0x11, 0x22, 0x33 }; +- +- DHD_TRACE(("%s: ifidx %d\n", __FUNCTION__, ifidx)); +- +- ASSERT(dhd && dhd->iflist[ifidx]); +- ifp = dhd->iflist[ifidx]; +- net = ifp->net; +- ASSERT(net && (ifp->idx == ifidx)); +- +-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)) +- ASSERT(!net->open); +- net->get_stats = dhd_get_stats; +- net->do_ioctl = dhd_ioctl_entry; +- net->hard_start_xmit = dhd_start_xmit; +- net->set_mac_address = dhd_set_mac_address; +- net->set_multicast_list = dhd_set_multicast_list; +- net->open = net->stop = NULL; +-#else +- ASSERT(!net->netdev_ops); +- net->netdev_ops = &dhd_ops_virt; +-#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) */ +- +- /* Ok, link into the network layer... */ +- if (ifidx == 0) { +- /* +- * device functions for the primary interface only +- */ +-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)) +- net->open = dhd_open; +- net->stop = dhd_stop; +-#else +- net->netdev_ops = &dhd_ops_pri; +-#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) */ +- if (!ETHER_ISNULLADDR(dhd->pub.mac.octet)) +- memcpy(temp_addr, dhd->pub.mac.octet, ETHER_ADDR_LEN); +- } else { +- /* +- * We have to use the primary MAC for virtual interfaces +- */ +- memcpy(temp_addr, ifp->mac_addr, ETHER_ADDR_LEN); +- /* +- * Android sets the locally administered bit to indicate that this is a +- * portable hotspot. This will not work in simultaneous AP/STA mode, +- * nor with P2P. Need to set the Donlge's MAC address, and then use that. +- */ +- if (!memcmp(temp_addr, dhd->iflist[0]->mac_addr, +- ETHER_ADDR_LEN)) { +- DHD_ERROR(("%s interface [%s]: set locally administered bit in MAC\n", +- __func__, net->name)); +- temp_addr[0] |= 0x02; +- } +- } +- +- net->hard_header_len = ETH_HLEN + dhd->pub.hdrlen; +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) +- net->ethtool_ops = &dhd_ethtool_ops; +-#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) */ +- +-#if defined(WL_WIRELESS_EXT) +-#if WIRELESS_EXT < 19 +- net->get_wireless_stats = dhd_get_wireless_stats; +-#endif /* WIRELESS_EXT < 19 */ +-#if WIRELESS_EXT > 12 +- net->wireless_handlers = (struct iw_handler_def *)&wl_iw_handler_def; +-#endif /* WIRELESS_EXT > 12 */ +-#endif /* defined(WL_WIRELESS_EXT) */ +- +- dhd->pub.rxsz = DBUS_RX_BUFFER_SIZE_DHD(net); +- +- memcpy(net->dev_addr, temp_addr, ETHER_ADDR_LEN); +- +- if (ifidx == 0) +- printf("%s\n", dhd_version); +- +- if (need_rtnl_lock) +- err = register_netdev(net); +- else +- err = register_netdevice(net); +- +- if (err != 0) { +- DHD_ERROR(("couldn't register the net device [%s], err %d\n", net->name, err)); +- goto fail; +- } +- +- +- +- printf("Register interface [%s] MAC: "MACDBG"\n\n", net->name, +- MAC2STRDBG(net->dev_addr)); +- +-#if defined(SOFTAP) && defined(WL_WIRELESS_EXT) && !defined(WL_CFG80211) +-// wl_iw_iscan_set_scan_broadcast_prep(net, 1); +-#endif +- +-#if 1 && (defined(BCMPCIE) || (defined(BCMLXSDMMC) && (LINUX_VERSION_CODE >= \ +- KERNEL_VERSION(2, 6, 27)))) +- if (ifidx == 0) { +-#ifdef BCMLXSDMMC +- up(&dhd_registration_sem); +-#endif +- if (!dhd_download_fw_on_driverload) { +- dhd_net_bus_devreset(net, TRUE); +-#ifdef BCMLXSDMMC +- dhd_net_bus_suspend(net); +-#endif /* BCMLXSDMMC */ +- wifi_platform_set_power(dhdp->info->adapter, FALSE, WIFI_TURNOFF_DELAY); +- } +- } +-#endif /* OEM_ANDROID && (BCMPCIE || (BCMLXSDMMC && KERNEL_VERSION >= 2.6.27)) */ +- return 0; +- +-fail: +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) +- net->open = NULL; +-#else +- net->netdev_ops = NULL; +-#endif +- return err; +-} +- +-void +-dhd_bus_detach(dhd_pub_t *dhdp) +-{ +- dhd_info_t *dhd; +- +- DHD_TRACE(("%s: Enter\n", __FUNCTION__)); +- +- if (dhdp) { +- dhd = (dhd_info_t *)dhdp->info; +- if (dhd) { +- +- /* +- * In case of Android cfg80211 driver, the bus is down in dhd_stop, +- * calling stop again will cuase SD read/write errors. +- */ +- if (dhd->pub.busstate != DHD_BUS_DOWN) { +- /* Stop the protocol module */ +- dhd_prot_stop(&dhd->pub); +- +- /* Stop the bus module */ +- dhd_bus_stop(dhd->pub.bus, TRUE); +- } +- +-#if defined(OOB_INTR_ONLY) +- dhd_bus_oob_intr_unregister(dhdp); +-#endif +- } +- } +-} +- +- +-void dhd_detach(dhd_pub_t *dhdp) +-{ +- dhd_info_t *dhd; +- unsigned long flags; +- int timer_valid = FALSE; +- +- if (!dhdp) +- return; +- +- dhd = (dhd_info_t *)dhdp->info; +- if (!dhd) +- return; +- +- DHD_TRACE(("%s: Enter state 0x%x\n", __FUNCTION__, dhd->dhd_state)); +- +- dhd->pub.up = 0; +- if (!(dhd->dhd_state & DHD_ATTACH_STATE_DONE)) { +- /* Give sufficient time for threads to start running in case +- * dhd_attach() has failed +- */ +- OSL_SLEEP(100); +- } +- +- if (dhd->dhd_state & DHD_ATTACH_STATE_PROT_ATTACH) { +-#ifdef PCIE_FULL_DONGLE +- dhd_flow_rings_deinit(dhdp); +-#endif +- dhd_bus_detach(dhdp); +- +- if (dhdp->prot) +- dhd_prot_detach(dhdp); +- } +- +-#ifdef ARP_OFFLOAD_SUPPORT +- if (dhd_inetaddr_notifier_registered) { +- dhd_inetaddr_notifier_registered = FALSE; +- unregister_inetaddr_notifier(&dhd_inetaddr_notifier); +- } +-#endif /* ARP_OFFLOAD_SUPPORT */ +-#ifdef CONFIG_IPV6 +- if (dhd_inet6addr_notifier_registered) { +- dhd_inet6addr_notifier_registered = FALSE; +- unregister_inet6addr_notifier(&dhd_inet6addr_notifier); +- } +-#endif +- +-#if defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND) +- if (dhd->dhd_state & DHD_ATTACH_STATE_EARLYSUSPEND_DONE) { +- if (dhd->early_suspend.suspend) +- unregister_early_suspend(&dhd->early_suspend); +- } +-#endif /* CONFIG_HAS_EARLYSUSPEND && DHD_USE_EARLYSUSPEND */ +- +-#if defined(WL_WIRELESS_EXT) +- if (dhd->dhd_state & DHD_ATTACH_STATE_WL_ATTACH) { +- /* Detatch and unlink in the iw */ +- wl_iw_detach(); +- } +-#endif /* defined(WL_WIRELESS_EXT) */ +- +- /* delete all interfaces, start with virtual */ +- if (dhd->dhd_state & DHD_ATTACH_STATE_ADD_IF) { +- int i = 1; +- dhd_if_t *ifp; +- +- /* Cleanup virtual interfaces */ +- dhd_net_if_lock_local(dhd); +- for (i = 1; i < DHD_MAX_IFS; i++) { +- if (dhd->iflist[i]) +- dhd_remove_if(&dhd->pub, i, TRUE); +- } +- dhd_net_if_unlock_local(dhd); +- +- /* delete primary interface 0 */ +- ifp = dhd->iflist[0]; +- ASSERT(ifp); +- ASSERT(ifp->net); +- if (ifp && ifp->net) { +- +- +- +- /* in unregister_netdev case, the interface gets freed by net->destructor +- * (which is set to free_netdev) +- */ +- if (ifp->net->reg_state == NETREG_UNINITIALIZED) +- free_netdev(ifp->net); +- else +- unregister_netdev(ifp->net); +- ifp->net = NULL; +-#ifdef DHD_WMF +- dhd_wmf_cleanup(dhdp, 0); +-#endif /* DHD_WMF */ +- +- dhd_if_del_sta_list(ifp); +- +- MFREE(dhd->pub.osh, ifp, sizeof(*ifp)); +- dhd->iflist[0] = NULL; +- } +- } +- +- /* Clear the watchdog timer */ +- DHD_GENERAL_LOCK(&dhd->pub, flags); +- timer_valid = dhd->wd_timer_valid; +- dhd->wd_timer_valid = FALSE; +- DHD_GENERAL_UNLOCK(&dhd->pub, flags); +- if (timer_valid) +- del_timer_sync(&dhd->timer); +- +- if (dhd->dhd_state & DHD_ATTACH_STATE_THREADS_CREATED) { +- if (dhd->thr_wdt_ctl.thr_pid >= 0) { +- PROC_STOP(&dhd->thr_wdt_ctl); +- } +- +- if (dhd->rxthread_enabled && dhd->thr_rxf_ctl.thr_pid >= 0) { +- PROC_STOP(&dhd->thr_rxf_ctl); +- } +- +- if (dhd->thr_dpc_ctl.thr_pid >= 0) { +- PROC_STOP(&dhd->thr_dpc_ctl); +- } else +- tasklet_kill(&dhd->tasklet); +- } +-#ifdef WL_CFG80211 +- if (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211) { +- wl_cfg80211_detach(NULL); +- dhd_monitor_uninit(); +- } +-#endif +- /* free deferred work queue */ +- dhd_deferred_work_deinit(dhd->dhd_deferred_wq); +- dhd->dhd_deferred_wq = NULL; +- +-#ifdef SHOW_LOGTRACE +- if (dhd->event_data.fmts) +- kfree(dhd->event_data.fmts); +- if (dhd->event_data.raw_fmts) +- kfree(dhd->event_data.raw_fmts); +-#endif /* SHOW_LOGTRACE */ +- +-#ifdef PNO_SUPPORT +- if (dhdp->pno_state) +- dhd_pno_deinit(dhdp); +-#endif +-#if defined(CONFIG_PM_SLEEP) +- if (dhd_pm_notifier_registered) { +- unregister_pm_notifier(&dhd_pm_notifier); +- dhd_pm_notifier_registered = FALSE; +- } +-#endif /* CONFIG_PM_SLEEP */ +-#ifdef DEBUG_CPU_FREQ +- if (dhd->new_freq) +- free_percpu(dhd->new_freq); +- dhd->new_freq = NULL; +- cpufreq_unregister_notifier(&dhd->freq_trans, CPUFREQ_TRANSITION_NOTIFIER); +-#endif +- if (dhd->dhd_state & DHD_ATTACH_STATE_WAKELOCKS_INIT) { +- DHD_TRACE(("wd wakelock count:%d\n", dhd->wakelock_wd_counter)); +-#ifdef CONFIG_HAS_WAKELOCK +- dhd->wakelock_counter = 0; +- dhd->wakelock_wd_counter = 0; +- dhd->wakelock_rx_timeout_enable = 0; +- dhd->wakelock_ctrl_timeout_enable = 0; +- wake_lock_destroy(&dhd->wl_wifi); +- wake_lock_destroy(&dhd->wl_rxwake); +- wake_lock_destroy(&dhd->wl_ctrlwake); +- wake_lock_destroy(&dhd->wl_wdwake); +-#endif /* CONFIG_HAS_WAKELOCK */ +- } +- +- +- +-#ifdef DHDTCPACK_SUPPRESS +- /* This will free all MEM allocated for TCPACK SUPPRESS */ +- dhd_tcpack_suppress_set(&dhd->pub, TCPACK_SUP_OFF); +-#endif /* DHDTCPACK_SUPPRESS */ +- dhd_conf_detach(dhdp); +-} +- +- +-void +-dhd_free(dhd_pub_t *dhdp) +-{ +- dhd_info_t *dhd; +- DHD_TRACE(("%s: Enter\n", __FUNCTION__)); +- +- if (dhdp) { +- int i; +- for (i = 0; i < ARRAYSIZE(dhdp->reorder_bufs); i++) { +- if (dhdp->reorder_bufs[i]) { +- reorder_info_t *ptr; +- uint32 buf_size = sizeof(struct reorder_info); +- +- ptr = dhdp->reorder_bufs[i]; +- +- buf_size += ((ptr->max_idx + 1) * sizeof(void*)); +- DHD_REORDER(("free flow id buf %d, maxidx is %d, buf_size %d\n", +- i, ptr->max_idx, buf_size)); +- +- MFREE(dhdp->osh, dhdp->reorder_bufs[i], buf_size); +- dhdp->reorder_bufs[i] = NULL; +- } +- } +- +- dhd_sta_pool_fini(dhdp, DHD_MAX_STA); +- +- dhd = (dhd_info_t *)dhdp->info; +- /* If pointer is allocated by dhd_os_prealloc then avoid MFREE */ +- if (dhd && +- dhd != (dhd_info_t *)dhd_os_prealloc(dhdp, DHD_PREALLOC_DHD_INFO, 0, FALSE)) +- MFREE(dhd->pub.osh, dhd, sizeof(*dhd)); +- dhd = NULL; +- } +-} +- +-static void +-dhd_module_cleanup(void) +-{ +- printk("%s: Enter\n", __FUNCTION__); +- +- dhd_bus_unregister(); +- +- wl_android_exit(); +- +- dhd_wifi_platform_unregister_drv(); +- printk("%s: Exit\n", __FUNCTION__); +-} +- +-static void __exit +-dhd_module_exit(void) +-{ +- dhd_module_cleanup(); +- unregister_reboot_notifier(&dhd_reboot_notifier); +-} +- +-static int __init +-dhd_module_init(void) +-{ +- int err; +- int retry = POWERUP_MAX_RETRY; +- +- printk("%s: in\n", __FUNCTION__); +- +- DHD_PERIM_RADIO_INIT(); +- +- if (firmware_path[0] != '\0') { +- strncpy(fw_bak_path, firmware_path, MOD_PARAM_PATHLEN); +- fw_bak_path[MOD_PARAM_PATHLEN-1] = '\0'; +- } +- +- if (nvram_path[0] != '\0') { +- strncpy(nv_bak_path, nvram_path, MOD_PARAM_PATHLEN); +- nv_bak_path[MOD_PARAM_PATHLEN-1] = '\0'; +- } +- +- do { +- err = dhd_wifi_platform_register_drv(); +- if (!err) { +- register_reboot_notifier(&dhd_reboot_notifier); +- break; +- } +- else { +- DHD_ERROR(("%s: Failed to load the driver, try cnt %d\n", +- __FUNCTION__, retry)); +- strncpy(firmware_path, fw_bak_path, MOD_PARAM_PATHLEN); +- firmware_path[MOD_PARAM_PATHLEN-1] = '\0'; +- strncpy(nvram_path, nv_bak_path, MOD_PARAM_PATHLEN); +- nvram_path[MOD_PARAM_PATHLEN-1] = '\0'; +- } +- } while (retry--); +- +- if (err) +- DHD_ERROR(("%s: Failed to load driver max retry reached**\n", __FUNCTION__)); +- +- printk("%s: Exit err=%d\n", __FUNCTION__, err); +- return err; +-} +- +-static int +-dhd_reboot_callback(struct notifier_block *this, unsigned long code, void *unused) +-{ +- DHD_TRACE(("%s: code = %ld\n", __FUNCTION__, code)); +- if (code == SYS_RESTART) { +- } +- +- return NOTIFY_DONE; +-} +- +- +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) +-#if defined(CONFIG_DEFERRED_INITCALLS) +-deferred_module_init(dhd_module_init); +-#elif defined(USE_LATE_INITCALL_SYNC) +-late_initcall_sync(dhd_module_init); +-#else +-late_initcall(dhd_module_init); +-#endif /* USE_LATE_INITCALL_SYNC */ +-#else +-module_init(dhd_module_init); +-#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) */ +- +-module_exit(dhd_module_exit); +- +-/* +- * OS specific functions required to implement DHD driver in OS independent way +- */ +-int +-dhd_os_proto_block(dhd_pub_t *pub) +-{ +- dhd_info_t * dhd = (dhd_info_t *)(pub->info); +- +- if (dhd) { +- DHD_PERIM_UNLOCK(pub); +- +- down(&dhd->proto_sem); +- +- DHD_PERIM_LOCK(pub); +- return 1; +- } +- +- return 0; +-} +- +-int +-dhd_os_proto_unblock(dhd_pub_t *pub) +-{ +- dhd_info_t * dhd = (dhd_info_t *)(pub->info); +- +- if (dhd) { +- up(&dhd->proto_sem); +- return 1; +- } +- +- return 0; +-} +- +-unsigned int +-dhd_os_get_ioctl_resp_timeout(void) +-{ +- return ((unsigned int)dhd_ioctl_timeout_msec); +-} +- +-void +-dhd_os_set_ioctl_resp_timeout(unsigned int timeout_msec) +-{ +- dhd_ioctl_timeout_msec = (int)timeout_msec; +-} +- +-int +-dhd_os_ioctl_resp_wait(dhd_pub_t *pub, uint *condition, bool *pending) +-{ +- dhd_info_t * dhd = (dhd_info_t *)(pub->info); +- int timeout; +- +- /* Convert timeout in millsecond to jiffies */ +-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) +- timeout = msecs_to_jiffies(dhd_ioctl_timeout_msec); +-#else +- timeout = dhd_ioctl_timeout_msec * HZ / 1000; +-#endif +- +- DHD_PERIM_UNLOCK(pub); +- +- timeout = wait_event_timeout(dhd->ioctl_resp_wait, (*condition), timeout); +- +- DHD_PERIM_LOCK(pub); +- +- return timeout; +-} +- +-int +-dhd_os_ioctl_resp_wake(dhd_pub_t *pub) +-{ +- dhd_info_t *dhd = (dhd_info_t *)(pub->info); +- +- wake_up(&dhd->ioctl_resp_wait); +- return 0; +-} +- +-void +-dhd_os_wd_timer_extend(void *bus, bool extend) +-{ +- dhd_pub_t *pub = bus; +- dhd_info_t *dhd = (dhd_info_t *)pub->info; +- +- if (extend) +- dhd_os_wd_timer(bus, WATCHDOG_EXTEND_INTERVAL); +- else +- dhd_os_wd_timer(bus, dhd->default_wd_interval); +-} +- +- +-void +-dhd_os_wd_timer(void *bus, uint wdtick) +-{ +- dhd_pub_t *pub = bus; +- dhd_info_t *dhd = (dhd_info_t *)pub->info; +- unsigned long flags; +- +- DHD_TRACE(("%s: Enter\n", __FUNCTION__)); +- +- if (!dhd) { +- DHD_ERROR(("%s: dhd NULL\n", __FUNCTION__)); +- return; +- } +- +- DHD_GENERAL_LOCK(pub, flags); +- +- /* don't start the wd until fw is loaded */ +- if (pub->busstate == DHD_BUS_DOWN) { +- DHD_GENERAL_UNLOCK(pub, flags); +- if (!wdtick) +- DHD_OS_WD_WAKE_UNLOCK(pub); +- return; +- } +- +- /* Totally stop the timer */ +- if (!wdtick && dhd->wd_timer_valid == TRUE) { +- dhd->wd_timer_valid = FALSE; +- DHD_GENERAL_UNLOCK(pub, flags); +- del_timer_sync(&dhd->timer); +- DHD_OS_WD_WAKE_UNLOCK(pub); +- return; +- } +- +- if (wdtick) { +- DHD_OS_WD_WAKE_LOCK(pub); +- dhd_watchdog_ms = (uint)wdtick; +- /* Re arm the timer, at last watchdog period */ +- mod_timer(&dhd->timer, jiffies + msecs_to_jiffies(dhd_watchdog_ms)); +- dhd->wd_timer_valid = TRUE; +- } +- DHD_GENERAL_UNLOCK(pub, flags); +-} +- +-void * +-dhd_os_open_image(char *filename) +-{ +- struct file *fp; +- +- fp = filp_open(filename, O_RDONLY, 0); +- /* +- * 2.6.11 (FC4) supports filp_open() but later revs don't? +- * Alternative: +- * fp = open_namei(AT_FDCWD, filename, O_RD, 0); +- * ??? +- */ +- if (IS_ERR(fp)) +- fp = NULL; +- +- return fp; +-} +- +-int +-dhd_os_get_image_block(char *buf, int len, void *image) +-{ +- struct file *fp = (struct file *)image; +- int rdlen; +- +- if (!image) +- return 0; +- +- rdlen = kernel_read(fp, fp->f_pos, buf, len); +- if (rdlen > 0) +- fp->f_pos += rdlen; +- +- return rdlen; +-} +- +-void +-dhd_os_close_image(void *image) +-{ +- if (image) +- filp_close((struct file *)image, NULL); +-} +- +-void +-dhd_os_sdlock(dhd_pub_t *pub) +-{ +- dhd_info_t *dhd; +- +- dhd = (dhd_info_t *)(pub->info); +- +- if (dhd_dpc_prio >= 0) +- down(&dhd->sdsem); +- else +- spin_lock_bh(&dhd->sdlock); +-} +- +-void +-dhd_os_sdunlock(dhd_pub_t *pub) +-{ +- dhd_info_t *dhd; +- +- dhd = (dhd_info_t *)(pub->info); +- +- if (dhd_dpc_prio >= 0) +- up(&dhd->sdsem); +- else +- spin_unlock_bh(&dhd->sdlock); +-} +- +-void +-dhd_os_sdlock_txq(dhd_pub_t *pub) +-{ +- dhd_info_t *dhd; +- +- dhd = (dhd_info_t *)(pub->info); +- spin_lock_bh(&dhd->txqlock); +-} +- +-void +-dhd_os_sdunlock_txq(dhd_pub_t *pub) +-{ +- dhd_info_t *dhd; +- +- dhd = (dhd_info_t *)(pub->info); +- spin_unlock_bh(&dhd->txqlock); +-} +- +-void +-dhd_os_sdlock_rxq(dhd_pub_t *pub) +-{ +-} +- +-void +-dhd_os_sdunlock_rxq(dhd_pub_t *pub) +-{ +-} +- +-static void +-dhd_os_rxflock(dhd_pub_t *pub) +-{ +- dhd_info_t *dhd; +- +- dhd = (dhd_info_t *)(pub->info); +- spin_lock_bh(&dhd->rxf_lock); +- +-} +- +-static void +-dhd_os_rxfunlock(dhd_pub_t *pub) +-{ +- dhd_info_t *dhd; +- +- dhd = (dhd_info_t *)(pub->info); +- spin_unlock_bh(&dhd->rxf_lock); +-} +- +-#ifdef DHDTCPACK_SUPPRESS +-void +-dhd_os_tcpacklock(dhd_pub_t *pub) +-{ +- dhd_info_t *dhd; +- +- dhd = (dhd_info_t *)(pub->info); +- spin_lock_bh(&dhd->tcpack_lock); +- +-} +- +-void +-dhd_os_tcpackunlock(dhd_pub_t *pub) +-{ +- dhd_info_t *dhd; +- +- dhd = (dhd_info_t *)(pub->info); +- spin_unlock_bh(&dhd->tcpack_lock); +-} +-#endif /* DHDTCPACK_SUPPRESS */ +- +-uint8* dhd_os_prealloc(dhd_pub_t *dhdpub, int section, uint size, bool kmalloc_if_fail) +-{ +- uint8* buf; +- gfp_t flags = CAN_SLEEP() ? GFP_KERNEL: GFP_ATOMIC; +- +- buf = (uint8*)wifi_platform_prealloc(dhdpub->info->adapter, section, size); +- if (buf == NULL && kmalloc_if_fail) +- buf = kmalloc(size, flags); +- +- return buf; +-} +- +-void dhd_os_prefree(dhd_pub_t *dhdpub, void *addr, uint size) +-{ +-} +- +-#if defined(WL_WIRELESS_EXT) +-struct iw_statistics * +-dhd_get_wireless_stats(struct net_device *dev) +-{ +- int res = 0; +- dhd_info_t *dhd = DHD_DEV_INFO(dev); +- +- if (!dhd->pub.up) { +- return NULL; +- } +- +- res = wl_iw_get_wireless_stats(dev, &dhd->iw.wstats); +- +- if (res == 0) +- return &dhd->iw.wstats; +- else +- return NULL; +-} +-#endif /* defined(WL_WIRELESS_EXT) */ +- +-static int +-dhd_wl_host_event(dhd_info_t *dhd, int *ifidx, void *pktdata, +- wl_event_msg_t *event, void **data) +-{ +- int bcmerror = 0; +- ASSERT(dhd != NULL); +- +-#ifdef SHOW_LOGTRACE +- bcmerror = wl_host_event(&dhd->pub, ifidx, pktdata, event, data, &dhd->event_data); +-#else +- bcmerror = wl_host_event(&dhd->pub, ifidx, pktdata, event, data, NULL); +-#endif /* SHOW_LOGTRACE */ +- +- if (bcmerror != BCME_OK) +- return (bcmerror); +- +-#if defined(WL_WIRELESS_EXT) +- if (event->bsscfgidx == 0) { +- /* +- * Wireless ext is on primary interface only +- */ +- +- ASSERT(dhd->iflist[*ifidx] != NULL); +- ASSERT(dhd->iflist[*ifidx]->net != NULL); +- +- if (dhd->iflist[*ifidx]->net) { +- wl_iw_event(dhd->iflist[*ifidx]->net, event, *data); +- } +- } +-#endif /* defined(WL_WIRELESS_EXT) */ +- +-#ifdef WL_CFG80211 +- ASSERT(dhd->iflist[*ifidx] != NULL); +- ASSERT(dhd->iflist[*ifidx]->net != NULL); +- if (dhd->iflist[*ifidx]->net) +- wl_cfg80211_event(dhd->iflist[*ifidx]->net, event, *data); +-#endif /* defined(WL_CFG80211) */ +- +- return (bcmerror); +-} +- +-/* send up locally generated event */ +-void +-dhd_sendup_event(dhd_pub_t *dhdp, wl_event_msg_t *event, void *data) +-{ +- switch (ntoh32(event->event_type)) { +-#ifdef WLBTAMP +- /* Send up locally generated AMP HCI Events */ +- case WLC_E_BTA_HCI_EVENT: { +- struct sk_buff *p, *skb; +- bcm_event_t *msg; +- wl_event_msg_t *p_bcm_event; +- char *ptr; +- uint32 len; +- uint32 pktlen; +- dhd_if_t *ifp; +- dhd_info_t *dhd; +- uchar *eth; +- int ifidx; +- +- len = ntoh32(event->datalen); +- pktlen = sizeof(bcm_event_t) + len + 2; +- dhd = dhdp->info; +- ifidx = dhd_ifname2idx(dhd, event->ifname); +- +- if ((p = PKTGET(dhdp->osh, pktlen, FALSE))) { +- ASSERT(ISALIGNED((uintptr)PKTDATA(dhdp->osh, p), sizeof(uint32))); +- +- msg = (bcm_event_t *) PKTDATA(dhdp->osh, p); +- +- bcopy(&dhdp->mac, &msg->eth.ether_dhost, ETHER_ADDR_LEN); +- bcopy(&dhdp->mac, &msg->eth.ether_shost, ETHER_ADDR_LEN); +- ETHER_TOGGLE_LOCALADDR(&msg->eth.ether_shost); +- +- msg->eth.ether_type = hton16(ETHER_TYPE_BRCM); +- +- /* BCM Vendor specific header... */ +- msg->bcm_hdr.subtype = hton16(BCMILCP_SUBTYPE_VENDOR_LONG); +- msg->bcm_hdr.version = BCMILCP_BCM_SUBTYPEHDR_VERSION; +- bcopy(BRCM_OUI, &msg->bcm_hdr.oui[0], DOT11_OUI_LEN); +- +- /* vendor spec header length + pvt data length (private indication +- * hdr + actual message itself) +- */ +- msg->bcm_hdr.length = hton16(BCMILCP_BCM_SUBTYPEHDR_MINLENGTH + +- BCM_MSG_LEN + sizeof(wl_event_msg_t) + (uint16)len); +- msg->bcm_hdr.usr_subtype = hton16(BCMILCP_BCM_SUBTYPE_EVENT); +- +- PKTSETLEN(dhdp->osh, p, (sizeof(bcm_event_t) + len + 2)); +- +- /* copy wl_event_msg_t into sk_buf */ +- +- /* pointer to wl_event_msg_t in sk_buf */ +- p_bcm_event = &msg->event; +- bcopy(event, p_bcm_event, sizeof(wl_event_msg_t)); +- +- /* copy hci event into sk_buf */ +- bcopy(data, (p_bcm_event + 1), len); +- +- msg->bcm_hdr.length = hton16(sizeof(wl_event_msg_t) + +- ntoh16(msg->bcm_hdr.length)); +- PKTSETLEN(dhdp->osh, p, (sizeof(bcm_event_t) + len + 2)); +- +- ptr = (char *)(msg + 1); +- /* Last 2 bytes of the message are 0x00 0x00 to signal that there +- * are no ethertypes which are following this +- */ +- ptr[len+0] = 0x00; +- ptr[len+1] = 0x00; +- +- skb = PKTTONATIVE(dhdp->osh, p); +- eth = skb->data; +- len = skb->len; +- +- ifp = dhd->iflist[ifidx]; +- if (ifp == NULL) +- ifp = dhd->iflist[0]; +- +- ASSERT(ifp); +- skb->dev = ifp->net; +- skb->protocol = eth_type_trans(skb, skb->dev); +- +- skb->data = eth; +- skb->len = len; +- +- /* Strip header, count, deliver upward */ +- skb_pull(skb, ETH_HLEN); +- +- /* Send the packet */ +- if (in_interrupt()) { +- netif_rx(skb); +- } else { +- netif_rx_ni(skb); +- } +- } +- else { +- /* Could not allocate a sk_buf */ +- DHD_ERROR(("%s: unable to alloc sk_buf", __FUNCTION__)); +- } +- break; +- } /* case WLC_E_BTA_HCI_EVENT */ +-#endif /* WLBTAMP */ +- +- default: +- break; +- } +-} +- +-#ifdef LOG_INTO_TCPDUMP +-void +-dhd_sendup_log(dhd_pub_t *dhdp, void *data, int data_len) +-{ +- struct sk_buff *p, *skb; +- uint32 pktlen; +- int len; +- dhd_if_t *ifp; +- dhd_info_t *dhd; +- uchar *skb_data; +- int ifidx = 0; +- struct ether_header eth; +- +- pktlen = sizeof(eth) + data_len; +- dhd = dhdp->info; +- +- if ((p = PKTGET(dhdp->osh, pktlen, FALSE))) { +- ASSERT(ISALIGNED((uintptr)PKTDATA(dhdp->osh, p), sizeof(uint32))); +- +- bcopy(&dhdp->mac, ð.ether_dhost, ETHER_ADDR_LEN); +- bcopy(&dhdp->mac, ð.ether_shost, ETHER_ADDR_LEN); +- ETHER_TOGGLE_LOCALADDR(ð.ether_shost); +- eth.ether_type = hton16(ETHER_TYPE_BRCM); +- +- bcopy((void *)ð, PKTDATA(dhdp->osh, p), sizeof(eth)); +- bcopy(data, PKTDATA(dhdp->osh, p) + sizeof(eth), data_len); +- skb = PKTTONATIVE(dhdp->osh, p); +- skb_data = skb->data; +- len = skb->len; +- +- ifidx = dhd_ifname2idx(dhd, "wlan0"); +- ifp = dhd->iflist[ifidx]; +- if (ifp == NULL) +- ifp = dhd->iflist[0]; +- +- ASSERT(ifp); +- skb->dev = ifp->net; +- skb->protocol = eth_type_trans(skb, skb->dev); +- skb->data = skb_data; +- skb->len = len; +- +- /* Strip header, count, deliver upward */ +- skb_pull(skb, ETH_HLEN); +- +- /* Send the packet */ +- if (in_interrupt()) { +- netif_rx(skb); +- } else { +- netif_rx_ni(skb); +- } +- } +- else { +- /* Could not allocate a sk_buf */ +- DHD_ERROR(("%s: unable to alloc sk_buf", __FUNCTION__)); +- } +-} +-#endif /* LOG_INTO_TCPDUMP */ +- +-void dhd_wait_for_event(dhd_pub_t *dhd, bool *lockvar) +-{ +-#if defined(BCMSDIO) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) +- struct dhd_info *dhdinfo = dhd->info; +- +-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) +- int timeout = msecs_to_jiffies(IOCTL_RESP_TIMEOUT); +-#else +- int timeout = (IOCTL_RESP_TIMEOUT / 1000) * HZ; +-#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ +- +- dhd_os_sdunlock(dhd); +- wait_event_timeout(dhdinfo->ctrl_wait, (*lockvar == FALSE), timeout); +- dhd_os_sdlock(dhd); +-#endif /* defined(BCMSDIO) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) */ +- return; +-} +- +-void dhd_wait_event_wakeup(dhd_pub_t *dhd) +-{ +-#if defined(BCMSDIO) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) +- struct dhd_info *dhdinfo = dhd->info; +- if (waitqueue_active(&dhdinfo->ctrl_wait)) +- wake_up(&dhdinfo->ctrl_wait); +-#endif +- return; +-} +- +-#if defined(BCMSDIO) || defined(BCMPCIE) +-int +-dhd_net_bus_devreset(struct net_device *dev, uint8 flag) +-{ +- int ret = 0; +- dhd_info_t *dhd = DHD_DEV_INFO(dev); +- +- if (flag == TRUE) { +- /* Issue wl down command before resetting the chip */ +- if (dhd_wl_ioctl_cmd(&dhd->pub, WLC_DOWN, NULL, 0, TRUE, 0) < 0) { +- DHD_TRACE(("%s: wl down failed\n", __FUNCTION__)); +- } +-#ifdef PROP_TXSTATUS +- if (dhd->pub.wlfc_enabled) +- dhd_wlfc_deinit(&dhd->pub); +-#endif /* PROP_TXSTATUS */ +-#ifdef PNO_SUPPORT +- if (dhd->pub.pno_state) +- dhd_pno_deinit(&dhd->pub); +-#endif +- } +- +-#ifdef BCMSDIO +- if (!flag) { +- dhd_update_fw_nv_path(dhd); +- /* update firmware and nvram path to sdio bus */ +- dhd_bus_update_fw_nv_path(dhd->pub.bus, +- dhd->fw_path, dhd->nv_path, dhd->conf_path); +- } +-#endif /* BCMSDIO */ +- +- ret = dhd_bus_devreset(&dhd->pub, flag); +- if (ret) { +- DHD_ERROR(("%s: dhd_bus_devreset: %d\n", __FUNCTION__, ret)); +- return ret; +- } +- +- return ret; +-} +- +-#ifdef BCMSDIO +-int +-dhd_net_bus_suspend(struct net_device *dev) +-{ +- dhd_info_t *dhd = DHD_DEV_INFO(dev); +- return dhd_bus_suspend(&dhd->pub); +-} +- +-int +-dhd_net_bus_resume(struct net_device *dev, uint8 stage) +-{ +- dhd_info_t *dhd = DHD_DEV_INFO(dev); +- return dhd_bus_resume(&dhd->pub, stage); +-} +- +-#endif /* BCMSDIO */ +-#endif /* BCMSDIO || BCMPCIE */ +- +-int net_os_set_suspend_disable(struct net_device *dev, int val) +-{ +- dhd_info_t *dhd = DHD_DEV_INFO(dev); +- int ret = 0; +- +- if (dhd) { +- ret = dhd->pub.suspend_disable_flag; +- dhd->pub.suspend_disable_flag = val; +- } +- return ret; +-} +- +-int net_os_set_suspend(struct net_device *dev, int val, int force) +-{ +- int ret = 0; +- dhd_info_t *dhd = DHD_DEV_INFO(dev); +- +- if (dhd) { +-#if defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND) +- ret = dhd_set_suspend(val, &dhd->pub); +-#else +- ret = dhd_suspend_resume_helper(dhd, val, force); +-#endif +-#ifdef WL_CFG80211 +- wl_cfg80211_update_power_mode(dev); +-#endif +- } +- return ret; +-} +- +-int net_os_set_suspend_bcn_li_dtim(struct net_device *dev, int val) +-{ +- dhd_info_t *dhd = DHD_DEV_INFO(dev); +- +- if (dhd) +- dhd->pub.suspend_bcn_li_dtim = val; +- +- return 0; +-} +- +-#ifdef PKT_FILTER_SUPPORT +-int net_os_rxfilter_add_remove(struct net_device *dev, int add_remove, int num) +-{ +- dhd_info_t *dhd = DHD_DEV_INFO(dev); +- char *filterp = NULL; +- int filter_id = 0; +- int ret = 0; +- +- if (!dhd_master_mode) +- add_remove = !add_remove; +- +- if (!dhd || (num == DHD_UNICAST_FILTER_NUM) || +- (num == DHD_MDNS_FILTER_NUM)) +- return ret; +- if (num >= dhd->pub.pktfilter_count) +- return -EINVAL; +- switch (num) { +- case DHD_BROADCAST_FILTER_NUM: +- filterp = "101 0 0 0 0xFFFFFFFFFFFF 0xFFFFFFFFFFFF"; +- filter_id = 101; +- break; +- case DHD_MULTICAST4_FILTER_NUM: +- filterp = "102 0 0 0 0xFFFFFF 0x01005E"; +- filter_id = 102; +- break; +- case DHD_MULTICAST6_FILTER_NUM: +- filterp = "103 0 0 0 0xFFFF 0x3333"; +- filter_id = 103; +- break; +- default: +- return -EINVAL; +- } +- +- /* Add filter */ +- if (add_remove) { +- dhd->pub.pktfilter[num] = filterp; +- dhd_pktfilter_offload_set(&dhd->pub, dhd->pub.pktfilter[num]); +- } else { /* Delete filter */ +- if (dhd->pub.pktfilter[num] != NULL) { +- dhd_pktfilter_offload_delete(&dhd->pub, filter_id); +- dhd->pub.pktfilter[num] = NULL; +- } +- } +- return ret; +-} +- +-int dhd_os_enable_packet_filter(dhd_pub_t *dhdp, int val) +- +-{ +- int ret = 0; +- +- /* Packet filtering is set only if we still in early-suspend and +- * we need either to turn it ON or turn it OFF +- * We can always turn it OFF in case of early-suspend, but we turn it +- * back ON only if suspend_disable_flag was not set +- */ +- if (dhdp && dhdp->up) { +- if (dhdp->in_suspend) { +- if (!val || (val && !dhdp->suspend_disable_flag)) +- dhd_enable_packet_filter(val, dhdp); +- } +- } +- return ret; +-} +- +-/* function to enable/disable packet for Network device */ +-int net_os_enable_packet_filter(struct net_device *dev, int val) +-{ +- dhd_info_t *dhd = DHD_DEV_INFO(dev); +- +- return dhd_os_enable_packet_filter(&dhd->pub, val); +-} +-#endif /* PKT_FILTER_SUPPORT */ +- +-int +-dhd_dev_init_ioctl(struct net_device *dev) +-{ +- dhd_info_t *dhd = DHD_DEV_INFO(dev); +- int ret; +- +- if ((ret = dhd_sync_with_dongle(&dhd->pub)) < 0) +- goto done; +- +-done: +- return ret; +-} +- +-#ifdef PNO_SUPPORT +-/* Linux wrapper to call common dhd_pno_stop_for_ssid */ +-int +-dhd_dev_pno_stop_for_ssid(struct net_device *dev) +-{ +- dhd_info_t *dhd = DHD_DEV_INFO(dev); +- +- return (dhd_pno_stop_for_ssid(&dhd->pub)); +-} +-/* Linux wrapper to call common dhd_pno_set_for_ssid */ +-int +-dhd_dev_pno_set_for_ssid(struct net_device *dev, wlc_ssid_t* ssids_local, int nssid, +- uint16 scan_fr, int pno_repeat, int pno_freq_expo_max, uint16 *channel_list, int nchan) +-{ +- dhd_info_t *dhd = DHD_DEV_INFO(dev); +- +- return (dhd_pno_set_for_ssid(&dhd->pub, ssids_local, nssid, scan_fr, +- pno_repeat, pno_freq_expo_max, channel_list, nchan)); +-} +- +-/* Linux wrapper to call common dhd_pno_enable */ +-int +-dhd_dev_pno_enable(struct net_device *dev, int enable) +-{ +- dhd_info_t *dhd = DHD_DEV_INFO(dev); +- +- return (dhd_pno_enable(&dhd->pub, enable)); +-} +- +-/* Linux wrapper to call common dhd_pno_set_for_hotlist */ +-int +-dhd_dev_pno_set_for_hotlist(struct net_device *dev, wl_pfn_bssid_t *p_pfn_bssid, +- struct dhd_pno_hotlist_params *hotlist_params) +-{ +- dhd_info_t *dhd = DHD_DEV_INFO(dev); +- return (dhd_pno_set_for_hotlist(&dhd->pub, p_pfn_bssid, hotlist_params)); +-} +-/* Linux wrapper to call common dhd_dev_pno_stop_for_batch */ +-int +-dhd_dev_pno_stop_for_batch(struct net_device *dev) +-{ +- dhd_info_t *dhd = DHD_DEV_INFO(dev); +- return (dhd_pno_stop_for_batch(&dhd->pub)); +-} +-/* Linux wrapper to call common dhd_dev_pno_set_for_batch */ +-int +-dhd_dev_pno_set_for_batch(struct net_device *dev, struct dhd_pno_batch_params *batch_params) +-{ +- dhd_info_t *dhd = DHD_DEV_INFO(dev); +- return (dhd_pno_set_for_batch(&dhd->pub, batch_params)); +-} +-/* Linux wrapper to call common dhd_dev_pno_get_for_batch */ +-int +-dhd_dev_pno_get_for_batch(struct net_device *dev, char *buf, int bufsize) +-{ +- dhd_info_t *dhd = DHD_DEV_INFO(dev); +- return (dhd_pno_get_for_batch(&dhd->pub, buf, bufsize, PNO_STATUS_NORMAL)); +-} +-#endif /* PNO_SUPPORT */ +- +-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && (1) +-static void dhd_hang_process(void *dhd_info, void *event_info, u8 event) +-{ +- dhd_info_t *dhd; +- struct net_device *dev; +- +- dhd = (dhd_info_t *)dhd_info; +- dev = dhd->iflist[0]->net; +- +- if (dev) { +- rtnl_lock(); +- dev_close(dev); +- rtnl_unlock(); +-#if defined(WL_WIRELESS_EXT) +- wl_iw_send_priv_event(dev, "HANG"); +-#endif +-#if defined(WL_CFG80211) +- wl_cfg80211_hang(dev, WLAN_REASON_UNSPECIFIED); +-#endif +- } +-} +- +-int dhd_os_send_hang_message(dhd_pub_t *dhdp) +-{ +- int ret = 0; +- if (dhdp) { +- if (!dhdp->hang_was_sent) { +- dhdp->hang_was_sent = 1; +- dhd_deferred_schedule_work(dhdp->info->dhd_deferred_wq, (void *)dhdp, +- DHD_WQ_WORK_HANG_MSG, dhd_hang_process, DHD_WORK_PRIORITY_HIGH); +- } +- } +- return ret; +-} +- +-int net_os_send_hang_message(struct net_device *dev) +-{ +- dhd_info_t *dhd = DHD_DEV_INFO(dev); +- int ret = 0; +- +- if (dhd) { +- /* Report FW problem when enabled */ +- if (dhd->pub.hang_report) { +-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) +- ret = dhd_os_send_hang_message(&dhd->pub); +-#else +- ret = wl_cfg80211_hang(dev, WLAN_REASON_UNSPECIFIED); +-#endif +- } else { +- DHD_ERROR(("%s: FW HANG ignored (for testing purpose) and not sent up\n", +- __FUNCTION__)); +- /* Enforce bus down to stop any future traffic */ +- dhd->pub.busstate = DHD_BUS_DOWN; +- } +- } +- return ret; +-} +-#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) && OEM_ANDROID */ +- +- +-int dhd_net_wifi_platform_set_power(struct net_device *dev, bool on, unsigned long delay_msec) +-{ +- dhd_info_t *dhd = DHD_DEV_INFO(dev); +- return wifi_platform_set_power(dhd->adapter, on, delay_msec); +-} +- +-void dhd_get_customized_country_code(struct net_device *dev, char *country_iso_code, +- wl_country_t *cspec) +-{ +- dhd_info_t *dhd = DHD_DEV_INFO(dev); +- get_customized_country_code(dhd->adapter, country_iso_code, cspec); +-} +-void dhd_bus_country_set(struct net_device *dev, wl_country_t *cspec, bool notify) +-{ +- dhd_info_t *dhd = DHD_DEV_INFO(dev); +- if (dhd && dhd->pub.up) { +- memcpy(&dhd->pub.dhd_cspec, cspec, sizeof(wl_country_t)); +-#ifdef WL_CFG80211 +- wl_update_wiphybands(NULL, notify); +-#endif +- } +-} +- +-void dhd_bus_band_set(struct net_device *dev, uint band) +-{ +- dhd_info_t *dhd = DHD_DEV_INFO(dev); +- if (dhd && dhd->pub.up) { +-#ifdef WL_CFG80211 +- wl_update_wiphybands(NULL, true); +-#endif +- } +-} +- +-int dhd_net_set_fw_path(struct net_device *dev, char *fw) +-{ +- dhd_info_t *dhd = DHD_DEV_INFO(dev); +- +- if (!fw || fw[0] == '\0') +- return -EINVAL; +- +- strncpy(dhd->fw_path, fw, sizeof(dhd->fw_path) - 1); +- dhd->fw_path[sizeof(dhd->fw_path)-1] = '\0'; +- +-#if defined(SOFTAP) +- if (strstr(fw, "apsta") != NULL) { +- DHD_INFO(("GOT APSTA FIRMWARE\n")); +- ap_fw_loaded = TRUE; +- } else { +- DHD_INFO(("GOT STA FIRMWARE\n")); +- ap_fw_loaded = FALSE; +- } +-#endif +- return 0; +-} +- +-void dhd_net_if_lock(struct net_device *dev) +-{ +- dhd_info_t *dhd = DHD_DEV_INFO(dev); +- dhd_net_if_lock_local(dhd); +-} +- +-void dhd_net_if_unlock(struct net_device *dev) +-{ +- dhd_info_t *dhd = DHD_DEV_INFO(dev); +- dhd_net_if_unlock_local(dhd); +-} +- +-static void dhd_net_if_lock_local(dhd_info_t *dhd) +-{ +-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1 +- if (dhd) +- mutex_lock(&dhd->dhd_net_if_mutex); +-#endif +-} +- +-static void dhd_net_if_unlock_local(dhd_info_t *dhd) +-{ +-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1 +- if (dhd) +- mutex_unlock(&dhd->dhd_net_if_mutex); +-#endif +-} +- +-static void dhd_suspend_lock(dhd_pub_t *pub) +-{ +-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1 +- dhd_info_t *dhd = (dhd_info_t *)(pub->info); +- if (dhd) +- mutex_lock(&dhd->dhd_suspend_mutex); +-#endif +-} +- +-static void dhd_suspend_unlock(dhd_pub_t *pub) +-{ +-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1 +- dhd_info_t *dhd = (dhd_info_t *)(pub->info); +- if (dhd) +- mutex_unlock(&dhd->dhd_suspend_mutex); +-#endif +-} +- +-unsigned long dhd_os_general_spin_lock(dhd_pub_t *pub) +-{ +- dhd_info_t *dhd = (dhd_info_t *)(pub->info); +- unsigned long flags = 0; +- +- if (dhd) +- spin_lock_irqsave(&dhd->dhd_lock, flags); +- +- return flags; +-} +- +-void dhd_os_general_spin_unlock(dhd_pub_t *pub, unsigned long flags) +-{ +- dhd_info_t *dhd = (dhd_info_t *)(pub->info); +- +- if (dhd) +- spin_unlock_irqrestore(&dhd->dhd_lock, flags); +-} +- +-/* Linux specific multipurpose spinlock API */ +-void * +-dhd_os_spin_lock_init(osl_t *osh) +-{ +- /* Adding 4 bytes since the sizeof(spinlock_t) could be 0 */ +- /* if CONFIG_SMP and CONFIG_DEBUG_SPINLOCK are not defined */ +- /* and this results in kernel asserts in internal builds */ +- spinlock_t * lock = MALLOC(osh, sizeof(spinlock_t) + 4); +- if (lock) +- spin_lock_init(lock); +- return ((void *)lock); +-} +-void +-dhd_os_spin_lock_deinit(osl_t *osh, void *lock) +-{ +- MFREE(osh, lock, sizeof(spinlock_t) + 4); +-} +-unsigned long +-dhd_os_spin_lock(void *lock) +-{ +- unsigned long flags = 0; +- +- if (lock) +- spin_lock_irqsave((spinlock_t *)lock, flags); +- +- return flags; +-} +-void +-dhd_os_spin_unlock(void *lock, unsigned long flags) +-{ +- if (lock) +- spin_unlock_irqrestore((spinlock_t *)lock, flags); +-} +- +-static int +-dhd_get_pend_8021x_cnt(dhd_info_t *dhd) +-{ +- return (atomic_read(&dhd->pend_8021x_cnt)); +-} +- +-#define MAX_WAIT_FOR_8021X_TX 100 +- +-int +-dhd_wait_pend8021x(struct net_device *dev) +-{ +- dhd_info_t *dhd = DHD_DEV_INFO(dev); +- int timeout = msecs_to_jiffies(10); +- int ntimes = MAX_WAIT_FOR_8021X_TX; +- int pend = dhd_get_pend_8021x_cnt(dhd); +- +- while (ntimes && pend) { +- if (pend) { +- set_current_state(TASK_INTERRUPTIBLE); +- DHD_PERIM_UNLOCK(&dhd->pub); +- schedule_timeout(timeout); +- DHD_PERIM_LOCK(&dhd->pub); +- set_current_state(TASK_RUNNING); +- ntimes--; +- } +- pend = dhd_get_pend_8021x_cnt(dhd); +- } +- if (ntimes == 0) +- { +- atomic_set(&dhd->pend_8021x_cnt, 0); +- DHD_ERROR(("%s: TIMEOUT\n", __FUNCTION__)); +- } +- return pend; +-} +- +-#ifdef DHD_DEBUG +-int +-write_to_file(dhd_pub_t *dhd, uint8 *buf, int size) +-{ +- int ret = 0; +- struct file *fp; +- mm_segment_t old_fs; +- loff_t pos = 0; +- +- /* change to KERNEL_DS address limit */ +- old_fs = get_fs(); +- set_fs(KERNEL_DS); +- +- /* open file to write */ +- fp = filp_open("/tmp/mem_dump", O_WRONLY|O_CREAT, 0640); +- if (!fp) { +- printf("%s: open file error\n", __FUNCTION__); +- ret = -1; +- goto exit; +- } +- +- /* Write buf to file */ +- fp->f_op->write(fp, buf, size, &pos); +- +-exit: +- /* free buf before return */ +- MFREE(dhd->osh, buf, size); +- /* close file before return */ +- if (fp) +- filp_close(fp, current->files); +- /* restore previous address limit */ +- set_fs(old_fs); +- +- return ret; +-} +-#endif /* DHD_DEBUG */ +- +-int dhd_os_wake_lock_timeout(dhd_pub_t *pub) +-{ +- dhd_info_t *dhd = (dhd_info_t *)(pub->info); +- unsigned long flags; +- int ret = 0; +- +- if (dhd) { +- spin_lock_irqsave(&dhd->wakelock_spinlock, flags); +- ret = dhd->wakelock_rx_timeout_enable > dhd->wakelock_ctrl_timeout_enable ? +- dhd->wakelock_rx_timeout_enable : dhd->wakelock_ctrl_timeout_enable; +-#ifdef CONFIG_HAS_WAKELOCK +- if (dhd->wakelock_rx_timeout_enable) +- wake_lock_timeout(&dhd->wl_rxwake, +- msecs_to_jiffies(dhd->wakelock_rx_timeout_enable)); +- if (dhd->wakelock_ctrl_timeout_enable) +- wake_lock_timeout(&dhd->wl_ctrlwake, +- msecs_to_jiffies(dhd->wakelock_ctrl_timeout_enable)); +-#endif +- dhd->wakelock_rx_timeout_enable = 0; +- dhd->wakelock_ctrl_timeout_enable = 0; +- spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags); +- } +- return ret; +-} +- +-int net_os_wake_lock_timeout(struct net_device *dev) +-{ +- dhd_info_t *dhd = DHD_DEV_INFO(dev); +- int ret = 0; +- +- if (dhd) +- ret = dhd_os_wake_lock_timeout(&dhd->pub); +- return ret; +-} +- +-int dhd_os_wake_lock_rx_timeout_enable(dhd_pub_t *pub, int val) +-{ +- dhd_info_t *dhd = (dhd_info_t *)(pub->info); +- unsigned long flags; +- +- if (dhd) { +- spin_lock_irqsave(&dhd->wakelock_spinlock, flags); +- if (val > dhd->wakelock_rx_timeout_enable) +- dhd->wakelock_rx_timeout_enable = val; +- spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags); +- } +- return 0; +-} +- +-int dhd_os_wake_lock_ctrl_timeout_enable(dhd_pub_t *pub, int val) +-{ +- dhd_info_t *dhd = (dhd_info_t *)(pub->info); +- unsigned long flags; +- +- if (dhd) { +- spin_lock_irqsave(&dhd->wakelock_spinlock, flags); +- if (val > dhd->wakelock_ctrl_timeout_enable) +- dhd->wakelock_ctrl_timeout_enable = val; +- spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags); +- } +- return 0; +-} +- +-int dhd_os_wake_lock_ctrl_timeout_cancel(dhd_pub_t *pub) +-{ +- dhd_info_t *dhd = (dhd_info_t *)(pub->info); +- unsigned long flags; +- +- if (dhd) { +- spin_lock_irqsave(&dhd->wakelock_spinlock, flags); +- dhd->wakelock_ctrl_timeout_enable = 0; +-#ifdef CONFIG_HAS_WAKELOCK +- if (wake_lock_active(&dhd->wl_ctrlwake)) +- wake_unlock(&dhd->wl_ctrlwake); +-#endif +- spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags); +- } +- return 0; +-} +- +-int net_os_wake_lock_rx_timeout_enable(struct net_device *dev, int val) +-{ +- dhd_info_t *dhd = DHD_DEV_INFO(dev); +- int ret = 0; +- +- if (dhd) +- ret = dhd_os_wake_lock_rx_timeout_enable(&dhd->pub, val); +- return ret; +-} +- +-int net_os_wake_lock_ctrl_timeout_enable(struct net_device *dev, int val) +-{ +- dhd_info_t *dhd = DHD_DEV_INFO(dev); +- int ret = 0; +- +- if (dhd) +- ret = dhd_os_wake_lock_ctrl_timeout_enable(&dhd->pub, val); +- return ret; +-} +- +-int dhd_os_wake_lock(dhd_pub_t *pub) +-{ +- dhd_info_t *dhd = (dhd_info_t *)(pub->info); +- unsigned long flags; +- int ret = 0; +- +- if (dhd) { +- spin_lock_irqsave(&dhd->wakelock_spinlock, flags); +- +- if (dhd->wakelock_counter == 0 && !dhd->waive_wakelock) { +-#ifdef CONFIG_HAS_WAKELOCK +- wake_lock(&dhd->wl_wifi); +-#elif defined(BCMSDIO) && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36)) +- dhd_bus_dev_pm_stay_awake(pub); +-#endif +- } +- dhd->wakelock_counter++; +- ret = dhd->wakelock_counter; +- spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags); +- } +- return ret; +-} +- +-int net_os_wake_lock(struct net_device *dev) +-{ +- dhd_info_t *dhd = DHD_DEV_INFO(dev); +- int ret = 0; +- +- if (dhd) +- ret = dhd_os_wake_lock(&dhd->pub); +- return ret; +-} +- +-int dhd_os_wake_unlock(dhd_pub_t *pub) +-{ +- dhd_info_t *dhd = (dhd_info_t *)(pub->info); +- unsigned long flags; +- int ret = 0; +- +- dhd_os_wake_lock_timeout(pub); +- if (dhd) { +- spin_lock_irqsave(&dhd->wakelock_spinlock, flags); +- if (dhd->wakelock_counter > 0) { +- dhd->wakelock_counter--; +- if (dhd->wakelock_counter == 0 && !dhd->waive_wakelock) { +-#ifdef CONFIG_HAS_WAKELOCK +- wake_unlock(&dhd->wl_wifi); +-#elif defined(BCMSDIO) && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36)) +- dhd_bus_dev_pm_relax(pub); +-#endif +- } +- ret = dhd->wakelock_counter; +- } +- spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags); +- } +- return ret; +-} +- +-int dhd_os_check_wakelock(dhd_pub_t *pub) +-{ +-#if defined(CONFIG_HAS_WAKELOCK) || (defined(BCMSDIO) && (LINUX_VERSION_CODE > \ +- KERNEL_VERSION(2, 6, 36))) +- dhd_info_t *dhd; +- +- if (!pub) +- return 0; +- dhd = (dhd_info_t *)(pub->info); +-#endif /* CONFIG_HAS_WAKELOCK || BCMSDIO */ +- +-#ifdef CONFIG_HAS_WAKELOCK +- /* Indicate to the SD Host to avoid going to suspend if internal locks are up */ +- if (dhd && (wake_lock_active(&dhd->wl_wifi) || +- (wake_lock_active(&dhd->wl_wdwake)))) +- return 1; +-#elif defined(BCMSDIO) && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36)) +- if (dhd && (dhd->wakelock_counter > 0) && dhd_bus_dev_pm_enabled(pub)) +- return 1; +-#endif +- return 0; +-} +-int net_os_wake_unlock(struct net_device *dev) +-{ +- dhd_info_t *dhd = DHD_DEV_INFO(dev); +- int ret = 0; +- +- if (dhd) +- ret = dhd_os_wake_unlock(&dhd->pub); +- return ret; +-} +- +-int dhd_os_wd_wake_lock(dhd_pub_t *pub) +-{ +- dhd_info_t *dhd = (dhd_info_t *)(pub->info); +- unsigned long flags; +- int ret = 0; +- +- if (dhd) { +- spin_lock_irqsave(&dhd->wakelock_spinlock, flags); +-#ifdef CONFIG_HAS_WAKELOCK +- /* if wakelock_wd_counter was never used : lock it at once */ +- if (!dhd->wakelock_wd_counter) +- wake_lock(&dhd->wl_wdwake); +-#endif +- dhd->wakelock_wd_counter++; +- ret = dhd->wakelock_wd_counter; +- spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags); +- } +- return ret; +-} +- +-int dhd_os_wd_wake_unlock(dhd_pub_t *pub) +-{ +- dhd_info_t *dhd = (dhd_info_t *)(pub->info); +- unsigned long flags; +- int ret = 0; +- +- if (dhd) { +- spin_lock_irqsave(&dhd->wakelock_spinlock, flags); +- if (dhd->wakelock_wd_counter) { +- dhd->wakelock_wd_counter = 0; +-#ifdef CONFIG_HAS_WAKELOCK +- wake_unlock(&dhd->wl_wdwake); +-#endif +- } +- spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags); +- } +- return ret; +-} +- +-/* waive wakelocks for operations such as IOVARs in suspend function, must be closed +- * by a paired function call to dhd_wakelock_restore. returns current wakelock counter +- */ +-int dhd_os_wake_lock_waive(dhd_pub_t *pub) +-{ +- dhd_info_t *dhd = (dhd_info_t *)(pub->info); +- unsigned long flags; +- int ret = 0; +- +- if (dhd) { +- spin_lock_irqsave(&dhd->wakelock_spinlock, flags); +- /* dhd_wakelock_waive/dhd_wakelock_restore must be paired */ +- if (dhd->waive_wakelock == FALSE) { +- /* record current lock status */ +- dhd->wakelock_before_waive = dhd->wakelock_counter; +- dhd->waive_wakelock = TRUE; +- } +- ret = dhd->wakelock_wd_counter; +- spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags); +- } +- return ret; +-} +- +-int dhd_os_wake_lock_restore(dhd_pub_t *pub) +-{ +- dhd_info_t *dhd = (dhd_info_t *)(pub->info); +- unsigned long flags; +- int ret = 0; +- +- if (!dhd) +- return 0; +- +- spin_lock_irqsave(&dhd->wakelock_spinlock, flags); +- /* dhd_wakelock_waive/dhd_wakelock_restore must be paired */ +- if (!dhd->waive_wakelock) +- goto exit; +- +- dhd->waive_wakelock = FALSE; +- /* if somebody else acquires wakelock between dhd_wakelock_waive/dhd_wakelock_restore, +- * we need to make it up by calling wake_lock or pm_stay_awake. or if somebody releases +- * the lock in between, do the same by calling wake_unlock or pm_relax +- */ +- if (dhd->wakelock_before_waive == 0 && dhd->wakelock_counter > 0) { +-#ifdef CONFIG_HAS_WAKELOCK +- wake_lock(&dhd->wl_wifi); +-#elif defined(BCMSDIO) && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36)) +- dhd_bus_dev_pm_stay_awake(&dhd->pub); +-#endif +- } else if (dhd->wakelock_before_waive > 0 && dhd->wakelock_counter == 0) { +-#ifdef CONFIG_HAS_WAKELOCK +- wake_unlock(&dhd->wl_wifi); +-#elif defined(BCMSDIO) && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36)) +- dhd_bus_dev_pm_relax(&dhd->pub); +-#endif +- } +- dhd->wakelock_before_waive = 0; +-exit: +- ret = dhd->wakelock_wd_counter; +- spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags); +- return ret; +-} +- +-bool dhd_os_check_if_up(dhd_pub_t *pub) +-{ +- if (!pub) +- return FALSE; +- return pub->up; +-} +- +-#if defined(BCMSDIO) +-/* function to collect firmware, chip id and chip version info */ +-void dhd_set_version_info(dhd_pub_t *dhdp, char *fw) +-{ +- int i; +- +- i = snprintf(info_string, sizeof(info_string), +- " Driver: %s\n Firmware: %s ", EPI_VERSION_STR, fw); +- printf("%s\n", info_string); +- +- if (!dhdp) +- return; +- +- i = snprintf(&info_string[i], sizeof(info_string) - i, +- "\n Chip: %x Rev %x Pkg %x", dhd_bus_chip_id(dhdp), +- dhd_bus_chiprev_id(dhdp), dhd_bus_chippkg_id(dhdp)); +-} +-#endif /* defined(BCMSDIO) */ +-int dhd_ioctl_entry_local(struct net_device *net, wl_ioctl_t *ioc, int cmd) +-{ +- int ifidx; +- int ret = 0; +- dhd_info_t *dhd = NULL; +- +- if (!net || !DEV_PRIV(net)) { +- DHD_ERROR(("%s invalid parameter\n", __FUNCTION__)); +- return -EINVAL; +- } +- +- dhd = DHD_DEV_INFO(net); +- if (!dhd) +- return -EINVAL; +- +- ifidx = dhd_net2idx(dhd, net); +- if (ifidx == DHD_BAD_IF) { +- DHD_ERROR(("%s bad ifidx\n", __FUNCTION__)); +- return -ENODEV; +- } +- +- DHD_OS_WAKE_LOCK(&dhd->pub); +- DHD_PERIM_LOCK(&dhd->pub); +- +- ret = dhd_wl_ioctl(&dhd->pub, ifidx, ioc, ioc->buf, ioc->len); +- dhd_check_hang(net, &dhd->pub, ret); +- +- DHD_PERIM_UNLOCK(&dhd->pub); +- DHD_OS_WAKE_UNLOCK(&dhd->pub); +- +- return ret; +-} +- +-bool dhd_os_check_hang(dhd_pub_t *dhdp, int ifidx, int ret) +-{ +- struct net_device *net; +- +- net = dhd_idx2net(dhdp, ifidx); +- if (!net) { +- DHD_ERROR(("%s : Invalid index : %d\n", __FUNCTION__, ifidx)); +- return -EINVAL; +- } +- +- return dhd_check_hang(net, dhdp, ret); +-} +- +-/* Return instance */ +-int dhd_get_instance(dhd_pub_t *dhdp) +-{ +- return dhdp->info->unit; +-} +- +- +-#ifdef PROP_TXSTATUS +- +-void dhd_wlfc_plat_init(void *dhd) +-{ +- return; +-} +- +-void dhd_wlfc_plat_deinit(void *dhd) +-{ +- return; +-} +- +-bool dhd_wlfc_skip_fc(void) +-{ +- return FALSE; +-} +-#endif /* PROP_TXSTATUS */ +- +-#ifdef BCMDBGFS +- +-#include +- +-extern uint32 dhd_readregl(void *bp, uint32 addr); +-extern uint32 dhd_writeregl(void *bp, uint32 addr, uint32 data); +- +-typedef struct dhd_dbgfs { +- struct dentry *debugfs_dir; +- struct dentry *debugfs_mem; +- dhd_pub_t *dhdp; +- uint32 size; +-} dhd_dbgfs_t; +- +-dhd_dbgfs_t g_dbgfs; +- +-static int +-dhd_dbg_state_open(struct inode *inode, struct file *file) +-{ +- file->private_data = inode->i_private; +- return 0; +-} +- +-static ssize_t +-dhd_dbg_state_read(struct file *file, char __user *ubuf, +- size_t count, loff_t *ppos) +-{ +- ssize_t rval; +- uint32 tmp; +- loff_t pos = *ppos; +- size_t ret; +- +- if (pos < 0) +- return -EINVAL; +- if (pos >= g_dbgfs.size || !count) +- return 0; +- if (count > g_dbgfs.size - pos) +- count = g_dbgfs.size - pos; +- +- /* Basically enforce aligned 4 byte reads. It's up to the user to work out the details */ +- tmp = dhd_readregl(g_dbgfs.dhdp->bus, file->f_pos & (~3)); +- +- ret = copy_to_user(ubuf, &tmp, 4); +- if (ret == count) +- return -EFAULT; +- +- count -= ret; +- *ppos = pos + count; +- rval = count; +- +- return rval; +-} +- +- +-static ssize_t +-dhd_debugfs_write(struct file *file, const char __user *ubuf, size_t count, loff_t *ppos) +-{ +- loff_t pos = *ppos; +- size_t ret; +- uint32 buf; +- +- if (pos < 0) +- return -EINVAL; +- if (pos >= g_dbgfs.size || !count) +- return 0; +- if (count > g_dbgfs.size - pos) +- count = g_dbgfs.size - pos; +- +- ret = copy_from_user(&buf, ubuf, sizeof(uint32)); +- if (ret == count) +- return -EFAULT; +- +- /* Basically enforce aligned 4 byte writes. It's up to the user to work out the details */ +- dhd_writeregl(g_dbgfs.dhdp->bus, file->f_pos & (~3), buf); +- +- return count; +-} +- +- +-loff_t +-dhd_debugfs_lseek(struct file *file, loff_t off, int whence) +-{ +- loff_t pos = -1; +- +- switch (whence) { +- case 0: +- pos = off; +- break; +- case 1: +- pos = file->f_pos + off; +- break; +- case 2: +- pos = g_dbgfs.size - off; +- } +- return (pos < 0 || pos > g_dbgfs.size) ? -EINVAL : (file->f_pos = pos); +-} +- +-static const struct file_operations dhd_dbg_state_ops = { +- .read = dhd_dbg_state_read, +- .write = dhd_debugfs_write, +- .open = dhd_dbg_state_open, +- .llseek = dhd_debugfs_lseek +-}; +- +-static void dhd_dbg_create(void) +-{ +- if (g_dbgfs.debugfs_dir) { +- g_dbgfs.debugfs_mem = debugfs_create_file("mem", 0644, g_dbgfs.debugfs_dir, +- NULL, &dhd_dbg_state_ops); +- } +-} +- +-void dhd_dbg_init(dhd_pub_t *dhdp) +-{ +- int err; +- +- g_dbgfs.dhdp = dhdp; +- g_dbgfs.size = 0x20000000; /* Allow access to various cores regs */ +- +- g_dbgfs.debugfs_dir = debugfs_create_dir("dhd", 0); +- if (IS_ERR(g_dbgfs.debugfs_dir)) { +- err = PTR_ERR(g_dbgfs.debugfs_dir); +- g_dbgfs.debugfs_dir = NULL; +- return; +- } +- +- dhd_dbg_create(); +- +- return; +-} +- +-void dhd_dbg_remove(void) +-{ +- debugfs_remove(g_dbgfs.debugfs_mem); +- debugfs_remove(g_dbgfs.debugfs_dir); +- +- bzero((unsigned char *) &g_dbgfs, sizeof(g_dbgfs)); +- +-} +-#endif /* ifdef BCMDBGFS */ +- +-#ifdef WLMEDIA_HTSF +- +-static +-void dhd_htsf_addtxts(dhd_pub_t *dhdp, void *pktbuf) +-{ +- dhd_info_t *dhd = (dhd_info_t *)(dhdp->info); +- struct sk_buff *skb; +- uint32 htsf = 0; +- uint16 dport = 0, oldmagic = 0xACAC; +- char *p1; +- htsfts_t ts; +- +- /* timestamp packet */ +- +- p1 = (char*) PKTDATA(dhdp->osh, pktbuf); +- +- if (PKTLEN(dhdp->osh, pktbuf) > HTSF_MINLEN) { +-/* memcpy(&proto, p1+26, 4); */ +- memcpy(&dport, p1+40, 2); +-/* proto = ((ntoh32(proto))>> 16) & 0xFF; */ +- dport = ntoh16(dport); +- } +- +- /* timestamp only if icmp or udb iperf with port 5555 */ +-/* if (proto == 17 && dport == tsport) { */ +- if (dport >= tsport && dport <= tsport + 20) { +- +- skb = (struct sk_buff *) pktbuf; +- +- htsf = dhd_get_htsf(dhd, 0); +- memset(skb->data + 44, 0, 2); /* clear checksum */ +- memcpy(skb->data+82, &oldmagic, 2); +- memcpy(skb->data+84, &htsf, 4); +- +- memset(&ts, 0, sizeof(htsfts_t)); +- ts.magic = HTSFMAGIC; +- ts.prio = PKTPRIO(pktbuf); +- ts.seqnum = htsf_seqnum++; +- ts.c10 = get_cycles(); +- ts.t10 = htsf; +- ts.endmagic = HTSFENDMAGIC; +- +- memcpy(skb->data + HTSF_HOSTOFFSET, &ts, sizeof(ts)); +- } +-} +- +-static void dhd_dump_htsfhisto(histo_t *his, char *s) +-{ +- int pktcnt = 0, curval = 0, i; +- for (i = 0; i < (NUMBIN-2); i++) { +- curval += 500; +- printf("%d ", his->bin[i]); +- pktcnt += his->bin[i]; +- } +- printf(" max: %d TotPkt: %d neg: %d [%s]\n", his->bin[NUMBIN-2], pktcnt, +- his->bin[NUMBIN-1], s); +-} +- +-static +-void sorttobin(int value, histo_t *histo) +-{ +- int i, binval = 0; +- +- if (value < 0) { +- histo->bin[NUMBIN-1]++; +- return; +- } +- if (value > histo->bin[NUMBIN-2]) /* store the max value */ +- histo->bin[NUMBIN-2] = value; +- +- for (i = 0; i < (NUMBIN-2); i++) { +- binval += 500; /* 500m s bins */ +- if (value <= binval) { +- histo->bin[i]++; +- return; +- } +- } +- histo->bin[NUMBIN-3]++; +-} +- +-static +-void dhd_htsf_addrxts(dhd_pub_t *dhdp, void *pktbuf) +-{ +- dhd_info_t *dhd = (dhd_info_t *)dhdp->info; +- struct sk_buff *skb; +- char *p1; +- uint16 old_magic; +- int d1, d2, d3, end2end; +- htsfts_t *htsf_ts; +- uint32 htsf; +- +- skb = PKTTONATIVE(dhdp->osh, pktbuf); +- p1 = (char*)PKTDATA(dhdp->osh, pktbuf); +- +- if (PKTLEN(osh, pktbuf) > HTSF_MINLEN) { +- memcpy(&old_magic, p1+78, 2); +- htsf_ts = (htsfts_t*) (p1 + HTSF_HOSTOFFSET - 4); +- } +- else +- return; +- +- if (htsf_ts->magic == HTSFMAGIC) { +- htsf_ts->tE0 = dhd_get_htsf(dhd, 0); +- htsf_ts->cE0 = get_cycles(); +- } +- +- if (old_magic == 0xACAC) { +- +- tspktcnt++; +- htsf = dhd_get_htsf(dhd, 0); +- memcpy(skb->data+92, &htsf, sizeof(uint32)); +- +- memcpy(&ts[tsidx].t1, skb->data+80, 16); +- +- d1 = ts[tsidx].t2 - ts[tsidx].t1; +- d2 = ts[tsidx].t3 - ts[tsidx].t2; +- d3 = ts[tsidx].t4 - ts[tsidx].t3; +- end2end = ts[tsidx].t4 - ts[tsidx].t1; +- +- sorttobin(d1, &vi_d1); +- sorttobin(d2, &vi_d2); +- sorttobin(d3, &vi_d3); +- sorttobin(end2end, &vi_d4); +- +- if (end2end > 0 && end2end > maxdelay) { +- maxdelay = end2end; +- maxdelaypktno = tspktcnt; +- memcpy(&maxdelayts, &ts[tsidx], 16); +- } +- if (++tsidx >= TSMAX) +- tsidx = 0; +- } +-} +- +-uint32 dhd_get_htsf(dhd_info_t *dhd, int ifidx) +-{ +- uint32 htsf = 0, cur_cycle, delta, delta_us; +- uint32 factor, baseval, baseval2; +- cycles_t t; +- +- t = get_cycles(); +- cur_cycle = t; +- +- if (cur_cycle > dhd->htsf.last_cycle) +- delta = cur_cycle - dhd->htsf.last_cycle; +- else { +- delta = cur_cycle + (0xFFFFFFFF - dhd->htsf.last_cycle); +- } +- +- delta = delta >> 4; +- +- if (dhd->htsf.coef) { +- /* times ten to get the first digit */ +- factor = (dhd->htsf.coef*10 + dhd->htsf.coefdec1); +- baseval = (delta*10)/factor; +- baseval2 = (delta*10)/(factor+1); +- delta_us = (baseval - (((baseval - baseval2) * dhd->htsf.coefdec2)) / 10); +- htsf = (delta_us << 4) + dhd->htsf.last_tsf + HTSF_BUS_DELAY; +- } +- else { +- DHD_ERROR(("-------dhd->htsf.coef = 0 -------\n")); +- } +- +- return htsf; +-} +- +-static void dhd_dump_latency(void) +-{ +- int i, max = 0; +- int d1, d2, d3, d4, d5; +- +- printf("T1 T2 T3 T4 d1 d2 t4-t1 i \n"); +- for (i = 0; i < TSMAX; i++) { +- d1 = ts[i].t2 - ts[i].t1; +- d2 = ts[i].t3 - ts[i].t2; +- d3 = ts[i].t4 - ts[i].t3; +- d4 = ts[i].t4 - ts[i].t1; +- d5 = ts[max].t4-ts[max].t1; +- if (d4 > d5 && d4 > 0) { +- max = i; +- } +- printf("%08X %08X %08X %08X \t%d %d %d %d i=%d\n", +- ts[i].t1, ts[i].t2, ts[i].t3, ts[i].t4, +- d1, d2, d3, d4, i); +- } +- +- printf("current idx = %d \n", tsidx); +- +- printf("Highest latency %d pkt no.%d total=%d\n", maxdelay, maxdelaypktno, tspktcnt); +- printf("%08X %08X %08X %08X \t%d %d %d %d\n", +- maxdelayts.t1, maxdelayts.t2, maxdelayts.t3, maxdelayts.t4, +- maxdelayts.t2 - maxdelayts.t1, +- maxdelayts.t3 - maxdelayts.t2, +- maxdelayts.t4 - maxdelayts.t3, +- maxdelayts.t4 - maxdelayts.t1); +-} +- +- +-static int +-dhd_ioctl_htsf_get(dhd_info_t *dhd, int ifidx) +-{ +- wl_ioctl_t ioc; +- char buf[32]; +- int ret; +- uint32 s1, s2; +- +- struct tsf { +- uint32 low; +- uint32 high; +- } tsf_buf; +- +- memset(&ioc, 0, sizeof(ioc)); +- memset(&tsf_buf, 0, sizeof(tsf_buf)); +- +- ioc.cmd = WLC_GET_VAR; +- ioc.buf = buf; +- ioc.len = (uint)sizeof(buf); +- ioc.set = FALSE; +- +- strncpy(buf, "tsf", sizeof(buf) - 1); +- buf[sizeof(buf) - 1] = '\0'; +- s1 = dhd_get_htsf(dhd, 0); +- if ((ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len)) < 0) { +- if (ret == -EIO) { +- DHD_ERROR(("%s: tsf is not supported by device\n", +- dhd_ifname(&dhd->pub, ifidx))); +- return -EOPNOTSUPP; +- } +- return ret; +- } +- s2 = dhd_get_htsf(dhd, 0); +- +- memcpy(&tsf_buf, buf, sizeof(tsf_buf)); +- printf(" TSF_h=%04X lo=%08X Calc:htsf=%08X, coef=%d.%d%d delta=%d ", +- tsf_buf.high, tsf_buf.low, s2, dhd->htsf.coef, dhd->htsf.coefdec1, +- dhd->htsf.coefdec2, s2-tsf_buf.low); +- printf("lasttsf=%08X lastcycle=%08X\n", dhd->htsf.last_tsf, dhd->htsf.last_cycle); +- return 0; +-} +- +-void htsf_update(dhd_info_t *dhd, void *data) +-{ +- static ulong cur_cycle = 0, prev_cycle = 0; +- uint32 htsf, tsf_delta = 0; +- uint32 hfactor = 0, cyc_delta, dec1 = 0, dec2, dec3, tmp; +- ulong b, a; +- cycles_t t; +- +- /* cycles_t in inlcude/mips/timex.h */ +- +- t = get_cycles(); +- +- prev_cycle = cur_cycle; +- cur_cycle = t; +- +- if (cur_cycle > prev_cycle) +- cyc_delta = cur_cycle - prev_cycle; +- else { +- b = cur_cycle; +- a = prev_cycle; +- cyc_delta = cur_cycle + (0xFFFFFFFF - prev_cycle); +- } +- +- if (data == NULL) +- printf(" tsf update ata point er is null \n"); +- +- memcpy(&prev_tsf, &cur_tsf, sizeof(tsf_t)); +- memcpy(&cur_tsf, data, sizeof(tsf_t)); +- +- if (cur_tsf.low == 0) { +- DHD_INFO((" ---- 0 TSF, do not update, return\n")); +- return; +- } +- +- if (cur_tsf.low > prev_tsf.low) +- tsf_delta = (cur_tsf.low - prev_tsf.low); +- else { +- DHD_INFO((" ---- tsf low is smaller cur_tsf= %08X, prev_tsf=%08X, \n", +- cur_tsf.low, prev_tsf.low)); +- if (cur_tsf.high > prev_tsf.high) { +- tsf_delta = cur_tsf.low + (0xFFFFFFFF - prev_tsf.low); +- DHD_INFO((" ---- Wrap around tsf coutner adjusted TSF=%08X\n", tsf_delta)); +- } +- else +- return; /* do not update */ +- } +- +- if (tsf_delta) { +- hfactor = cyc_delta / tsf_delta; +- tmp = (cyc_delta - (hfactor * tsf_delta))*10; +- dec1 = tmp/tsf_delta; +- dec2 = ((tmp - dec1*tsf_delta)*10) / tsf_delta; +- tmp = (tmp - (dec1*tsf_delta))*10; +- dec3 = ((tmp - dec2*tsf_delta)*10) / tsf_delta; +- +- if (dec3 > 4) { +- if (dec2 == 9) { +- dec2 = 0; +- if (dec1 == 9) { +- dec1 = 0; +- hfactor++; +- } +- else { +- dec1++; +- } +- } +- else +- dec2++; +- } +- } +- +- if (hfactor) { +- htsf = ((cyc_delta * 10) / (hfactor*10+dec1)) + prev_tsf.low; +- dhd->htsf.coef = hfactor; +- dhd->htsf.last_cycle = cur_cycle; +- dhd->htsf.last_tsf = cur_tsf.low; +- dhd->htsf.coefdec1 = dec1; +- dhd->htsf.coefdec2 = dec2; +- } +- else { +- htsf = prev_tsf.low; +- } +-} +- +-#endif /* WLMEDIA_HTSF */ +- +-#ifdef CUSTOM_SET_CPUCORE +-void dhd_set_cpucore(dhd_pub_t *dhd, int set) +-{ +- int e_dpc = 0, e_rxf = 0, retry_set = 0; +- +- if (!(dhd->chan_isvht80)) { +- DHD_ERROR(("%s: chan_status(%d) cpucore!!!\n", __FUNCTION__, dhd->chan_isvht80)); +- return; +- } +- +- if (DPC_CPUCORE) { +- do { +- if (set == TRUE) { +- e_dpc = set_cpus_allowed_ptr(dhd->current_dpc, +- cpumask_of(DPC_CPUCORE)); +- } else { +- e_dpc = set_cpus_allowed_ptr(dhd->current_dpc, +- cpumask_of(PRIMARY_CPUCORE)); +- } +- if (retry_set++ > MAX_RETRY_SET_CPUCORE) { +- DHD_ERROR(("%s: dpc(%d) invalid cpu!\n", __FUNCTION__, e_dpc)); +- return; +- } +- if (e_dpc < 0) +- OSL_SLEEP(1); +- } while (e_dpc < 0); +- } +- if (RXF_CPUCORE) { +- do { +- if (set == TRUE) { +- e_rxf = set_cpus_allowed_ptr(dhd->current_rxf, +- cpumask_of(RXF_CPUCORE)); +- } else { +- e_rxf = set_cpus_allowed_ptr(dhd->current_rxf, +- cpumask_of(PRIMARY_CPUCORE)); +- } +- if (retry_set++ > MAX_RETRY_SET_CPUCORE) { +- DHD_ERROR(("%s: rxf(%d) invalid cpu!\n", __FUNCTION__, e_rxf)); +- return; +- } +- if (e_rxf < 0) +- OSL_SLEEP(1); +- } while (e_rxf < 0); +- } +-#ifdef DHD_OF_SUPPORT +- interrupt_set_cpucore(set); +-#endif /* DHD_OF_SUPPORT */ +- DHD_TRACE(("%s: set(%d) cpucore success!\n", __FUNCTION__, set)); +- +- return; +-} +-#endif /* CUSTOM_SET_CPUCORE */ +-#if defined(DHD_TCP_WINSIZE_ADJUST) +-static int dhd_port_list_match(int port) +-{ +- int i; +- for (i = 0; i < MAX_TARGET_PORTS; i++) { +- if (target_ports[i] == port) +- return 1; +- } +- return 0; +-} +-static void dhd_adjust_tcp_winsize(int op_mode, struct sk_buff *skb) +-{ +- struct iphdr *ipheader; +- struct tcphdr *tcpheader; +- uint16 win_size; +- int32 incremental_checksum; +- +- if (!(op_mode & DHD_FLAG_HOSTAP_MODE)) +- return; +- if (skb == NULL || skb->data == NULL) +- return; +- +- ipheader = (struct iphdr*)(skb->data); +- +- if (ipheader->protocol == IPPROTO_TCP) { +- tcpheader = (struct tcphdr*) skb_pull(skb, (ipheader->ihl)<<2); +- if (tcpheader) { +- win_size = ntoh16(tcpheader->window); +- if (win_size < MIN_TCP_WIN_SIZE && +- dhd_port_list_match(ntoh16(tcpheader->dest))) { +- incremental_checksum = ntoh16(tcpheader->check); +- incremental_checksum += win_size - win_size*WIN_SIZE_SCALE_FACTOR; +- if (incremental_checksum < 0) +- --incremental_checksum; +- tcpheader->window = hton16(win_size*WIN_SIZE_SCALE_FACTOR); +- tcpheader->check = hton16((unsigned short)incremental_checksum); +- } +- } +- skb_push(skb, (ipheader->ihl)<<2); +- } +-} +-#endif /* DHD_TCP_WINSIZE_ADJUST */ +- +-/* Get interface specific ap_isolate configuration */ +-int dhd_get_ap_isolate(dhd_pub_t *dhdp, uint32 idx) +-{ +- dhd_info_t *dhd = dhdp->info; +- dhd_if_t *ifp; +- +- ASSERT(idx < DHD_MAX_IFS); +- +- ifp = dhd->iflist[idx]; +- +- return ifp->ap_isolate; +-} +- +-/* Set interface specific ap_isolate configuration */ +-int dhd_set_ap_isolate(dhd_pub_t *dhdp, uint32 idx, int val) +-{ +- dhd_info_t *dhd = dhdp->info; +- dhd_if_t *ifp; +- +- ASSERT(idx < DHD_MAX_IFS); +- +- ifp = dhd->iflist[idx]; +- +- ifp->ap_isolate = val; +- +- return 0; +-} +- +-#ifdef DHD_WMF +-/* Returns interface specific WMF configuration */ +-dhd_wmf_t* dhd_wmf_conf(dhd_pub_t *dhdp, uint32 idx) +-{ +- dhd_info_t *dhd = dhdp->info; +- dhd_if_t *ifp; +- +- ASSERT(idx < DHD_MAX_IFS); +- +- ifp = dhd->iflist[idx]; +- return &ifp->wmf; +-} +-#endif /* DHD_WMF */ +- +- +-#ifdef DHD_UNICAST_DHCP +-static int +-dhd_get_pkt_ether_type(dhd_pub_t *pub, void *pktbuf, +- uint8 **data_ptr, int *len_ptr, uint16 *et_ptr, bool *snap_ptr) +-{ +- uint8 *frame = PKTDATA(pub->osh, pktbuf); +- int length = PKTLEN(pub->osh, pktbuf); +- uint8 *pt; /* Pointer to type field */ +- uint16 ethertype; +- bool snap = FALSE; +- /* Process Ethernet II or SNAP-encapsulated 802.3 frames */ +- if (length < ETHER_HDR_LEN) { +- DHD_ERROR(("dhd: %s: short eth frame (%d)\n", +- __FUNCTION__, length)); +- return BCME_ERROR; +- } else if (ntoh16_ua(frame + ETHER_TYPE_OFFSET) >= ETHER_TYPE_MIN) { +- /* Frame is Ethernet II */ +- pt = frame + ETHER_TYPE_OFFSET; +- } else if (length >= ETHER_HDR_LEN + SNAP_HDR_LEN + ETHER_TYPE_LEN && +- !bcmp(llc_snap_hdr, frame + ETHER_HDR_LEN, SNAP_HDR_LEN)) { +- pt = frame + ETHER_HDR_LEN + SNAP_HDR_LEN; +- snap = TRUE; +- } else { +- DHD_INFO(("DHD: %s: non-SNAP 802.3 frame\n", +- __FUNCTION__)); +- return BCME_ERROR; +- } +- +- ethertype = ntoh16_ua(pt); +- +- /* Skip VLAN tag, if any */ +- if (ethertype == ETHER_TYPE_8021Q) { +- pt += VLAN_TAG_LEN; +- +- if ((pt + ETHER_TYPE_LEN) > (frame + length)) { +- DHD_ERROR(("dhd: %s: short VLAN frame (%d)\n", +- __FUNCTION__, length)); +- return BCME_ERROR; +- } +- +- ethertype = ntoh16_ua(pt); +- } +- +- *data_ptr = pt + ETHER_TYPE_LEN; +- *len_ptr = length - (pt + ETHER_TYPE_LEN - frame); +- *et_ptr = ethertype; +- *snap_ptr = snap; +- return BCME_OK; +-} +- +-static int +-dhd_get_pkt_ip_type(dhd_pub_t *pub, void *pktbuf, +- uint8 **data_ptr, int *len_ptr, uint8 *prot_ptr) +-{ +- struct ipv4_hdr *iph; /* IP frame pointer */ +- int iplen; /* IP frame length */ +- uint16 ethertype, iphdrlen, ippktlen; +- uint16 iph_frag; +- uint8 prot; +- bool snap; +- +- if (dhd_get_pkt_ether_type(pub, pktbuf, (uint8 **)&iph, +- &iplen, ðertype, &snap) != 0) +- return BCME_ERROR; +- +- if (ethertype != ETHER_TYPE_IP) { +- return BCME_ERROR; +- } +- +- /* We support IPv4 only */ +- if (iplen < IPV4_OPTIONS_OFFSET || (IP_VER(iph) != IP_VER_4)) { +- return BCME_ERROR; +- } +- +- /* Header length sanity */ +- iphdrlen = IPV4_HLEN(iph); +- +- /* +- * Packet length sanity; sometimes we receive eth-frame size bigger +- * than the IP content, which results in a bad tcp chksum +- */ +- ippktlen = ntoh16(iph->tot_len); +- if (ippktlen < iplen) { +- +- DHD_INFO(("%s: extra frame length ignored\n", +- __FUNCTION__)); +- iplen = ippktlen; +- } else if (ippktlen > iplen) { +- DHD_ERROR(("dhd: %s: truncated IP packet (%d)\n", +- __FUNCTION__, ippktlen - iplen)); +- return BCME_ERROR; +- } +- +- if (iphdrlen < IPV4_OPTIONS_OFFSET || iphdrlen > iplen) { +- DHD_ERROR(("DHD: %s: IP-header-len (%d) out of range (%d-%d)\n", +- __FUNCTION__, iphdrlen, IPV4_OPTIONS_OFFSET, iplen)); +- return BCME_ERROR; +- } +- +- /* +- * We don't handle fragmented IP packets. A first frag is indicated by the MF +- * (more frag) bit and a subsequent frag is indicated by a non-zero frag offset. +- */ +- iph_frag = ntoh16(iph->frag); +- +- if ((iph_frag & IPV4_FRAG_MORE) || (iph_frag & IPV4_FRAG_OFFSET_MASK) != 0) { +- DHD_INFO(("DHD:%s: IP fragment not handled\n", +- __FUNCTION__)); +- return BCME_ERROR; +- } +- +- prot = IPV4_PROT(iph); +- +- *data_ptr = (((uint8 *)iph) + iphdrlen); +- *len_ptr = iplen - iphdrlen; +- *prot_ptr = prot; +- return BCME_OK; +-} +- +-/** check the packet type, if it is DHCP ACK/REPLY, convert into unicast packet */ +-static +-int dhd_convert_dhcp_broadcast_ack_to_unicast(dhd_pub_t *pub, void *pktbuf, int ifidx) +-{ +- dhd_sta_t* stainfo; +- uint8 *eh = PKTDATA(pub->osh, pktbuf); +- uint8 *udph; +- uint8 *dhcp; +- uint8 *chaddr; +- int udpl; +- int dhcpl; +- uint16 port; +- uint8 prot; +- +- if (!ETHER_ISMULTI(eh + ETHER_DEST_OFFSET)) +- return BCME_ERROR; +- if (dhd_get_pkt_ip_type(pub, pktbuf, &udph, &udpl, &prot) != 0) +- return BCME_ERROR; +- if (prot != IP_PROT_UDP) +- return BCME_ERROR; +- /* check frame length, at least UDP_HDR_LEN */ +- if (udpl < UDP_HDR_LEN) { +- DHD_ERROR(("DHD: %s: short UDP frame, ignored\n", +- __FUNCTION__)); +- return BCME_ERROR; +- } +- port = ntoh16_ua(udph + UDP_DEST_PORT_OFFSET); +- /* only process DHCP packets from server to client */ +- if (port != DHCP_PORT_CLIENT) +- return BCME_ERROR; +- +- dhcp = udph + UDP_HDR_LEN; +- dhcpl = udpl - UDP_HDR_LEN; +- +- if (dhcpl < DHCP_CHADDR_OFFSET + ETHER_ADDR_LEN) { +- DHD_ERROR(("DHD: %s: short DHCP frame, ignored\n", +- __FUNCTION__)); +- return BCME_ERROR; +- } +- /* only process DHCP reply(offer/ack) packets */ +- if (*(dhcp + DHCP_TYPE_OFFSET) != DHCP_TYPE_REPLY) +- return BCME_ERROR; +- chaddr = dhcp + DHCP_CHADDR_OFFSET; +- stainfo = dhd_find_sta(pub, ifidx, chaddr); +- if (stainfo) { +- bcopy(chaddr, eh + ETHER_DEST_OFFSET, ETHER_ADDR_LEN); +- return BCME_OK; +- } +- return BCME_ERROR; +-} +-#endif /* DHD_UNICAST_DHD */ +-#ifdef DHD_L2_FILTER +-/* Check if packet type is ICMP ECHO */ +-static +-int dhd_l2_filter_block_ping(dhd_pub_t *pub, void *pktbuf, int ifidx) +-{ +- struct bcmicmp_hdr *icmph; +- int udpl; +- uint8 prot; +- +- if (dhd_get_pkt_ip_type(pub, pktbuf, (uint8 **)&icmph, &udpl, &prot) != 0) +- return BCME_ERROR; +- if (prot == IP_PROT_ICMP) { +- if (icmph->type == ICMP_TYPE_ECHO_REQUEST) +- return BCME_OK; +- } +- return BCME_ERROR; +-} +-#endif /* DHD_L2_FILTER */ +- +-void *dhd_get_pub(struct net_device *dev) +-{ +- dhd_info_t *dhdinfo = *(dhd_info_t **)netdev_priv(dev); +- return (void *)&dhdinfo->pub; +-} ++/* ++ * Broadcom Dongle Host Driver (DHD), Linux-specific network interface ++ * Basically selected code segments from usb-cdc.c and usb-rndis.c ++ * ++ * $Copyright Open Broadcom Corporation$ ++ * ++ * $Id: dhd_linux.c 505753 2014-10-01 01:40:15Z $ ++ */ ++ ++#include ++#include ++#include ++#ifdef SHOW_LOGTRACE ++#include ++#include ++#endif /* SHOW_LOGTRACE */ ++ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#ifdef ENABLE_ADAPTIVE_SCHED ++#include ++#endif /* ENABLE_ADAPTIVE_SCHED */ ++ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#ifdef DHD_L2_FILTER ++#include ++#endif ++#include ++ ++#include ++#include ++#include ++#include ++#ifdef PCIE_FULL_DONGLE ++#include ++#endif ++#include ++#include ++#include ++#include ++#ifdef CONFIG_HAS_WAKELOCK ++#include ++#endif ++#ifdef WL_CFG80211 ++#include ++#endif ++#ifdef P2PONEINT ++#include ++#endif ++#ifdef PNO_SUPPORT ++#include ++#endif ++#ifdef WLBTAMP ++#include ++#include ++#include ++#endif ++ ++#ifdef CONFIG_COMPAT ++#include ++#endif ++ ++#ifdef DHD_WMF ++#include ++#endif /* DHD_WMF */ ++ ++#ifdef AMPDU_VO_ENABLE ++#include ++#endif /* AMPDU_VO_ENABLE */ ++#ifdef DHDTCPACK_SUPPRESS ++#include ++#endif /* DHDTCPACK_SUPPRESS */ ++ ++#if defined(DHD_TCP_WINSIZE_ADJUST) ++#include ++#include ++#endif /* DHD_TCP_WINSIZE_ADJUST */ ++ ++#ifdef WLMEDIA_HTSF ++#include ++#include ++ ++#define HTSF_MINLEN 200 /* min. packet length to timestamp */ ++#define HTSF_BUS_DELAY 150 /* assume a fix propagation in us */ ++#define TSMAX 1000 /* max no. of timing record kept */ ++#define NUMBIN 34 ++ ++static uint32 tsidx = 0; ++static uint32 htsf_seqnum = 0; ++uint32 tsfsync; ++struct timeval tsync; ++static uint32 tsport = 5010; ++ ++typedef struct histo_ { ++ uint32 bin[NUMBIN]; ++} histo_t; ++ ++#if !ISPOWEROF2(DHD_SDALIGN) ++#error DHD_SDALIGN is not a power of 2! ++#endif ++ ++static histo_t vi_d1, vi_d2, vi_d3, vi_d4; ++#endif /* WLMEDIA_HTSF */ ++ ++#if defined(DHD_TCP_WINSIZE_ADJUST) ++#define MIN_TCP_WIN_SIZE 18000 ++#define WIN_SIZE_SCALE_FACTOR 2 ++#define MAX_TARGET_PORTS 5 ++ ++static uint target_ports[MAX_TARGET_PORTS] = {20, 0, 0, 0, 0}; ++static uint dhd_use_tcp_window_size_adjust = FALSE; ++static void dhd_adjust_tcp_winsize(int op_mode, struct sk_buff *skb); ++#endif /* DHD_TCP_WINSIZE_ADJUST */ ++ ++ ++#if defined(SOFTAP) ++extern bool ap_cfg_running; ++extern bool ap_fw_loaded; ++#endif ++ ++ ++#ifdef ENABLE_ADAPTIVE_SCHED ++#define DEFAULT_CPUFREQ_THRESH 1000000 /* threshold frequency : 1000000 = 1GHz */ ++#ifndef CUSTOM_CPUFREQ_THRESH ++#define CUSTOM_CPUFREQ_THRESH DEFAULT_CPUFREQ_THRESH ++#endif /* CUSTOM_CPUFREQ_THRESH */ ++#endif /* ENABLE_ADAPTIVE_SCHED */ ++ ++/* enable HOSTIP cache update from the host side when an eth0:N is up */ ++#define AOE_IP_ALIAS_SUPPORT 1 ++ ++#ifdef BCM_FD_AGGR ++#include ++#include ++#endif ++#ifdef PROP_TXSTATUS ++#include ++#include ++#endif ++ ++#include ++ ++#if defined(CUSTOMER_HW20) && defined(WLANAUDIO) ++#include ++#endif /* CUSTOMER_HW20 && WLANAUDIO */ ++ ++/* Maximum STA per radio */ ++#define DHD_MAX_STA 32 ++ ++ ++const uint8 wme_fifo2ac[] = { 0, 1, 2, 3, 1, 1 }; ++const uint8 prio2fifo[8] = { 1, 0, 0, 1, 2, 2, 3, 3 }; ++#define WME_PRIO2AC(prio) wme_fifo2ac[prio2fifo[(prio)]] ++ ++#ifdef ARP_OFFLOAD_SUPPORT ++void aoe_update_host_ipv4_table(dhd_pub_t *dhd_pub, u32 ipa, bool add, int idx); ++static int dhd_inetaddr_notifier_call(struct notifier_block *this, ++ unsigned long event, void *ptr); ++static struct notifier_block dhd_inetaddr_notifier = { ++ .notifier_call = dhd_inetaddr_notifier_call ++}; ++/* to make sure we won't register the same notifier twice, otherwise a loop is likely to be ++ * created in kernel notifier link list (with 'next' pointing to itself) ++ */ ++static bool dhd_inetaddr_notifier_registered = FALSE; ++#endif /* ARP_OFFLOAD_SUPPORT */ ++ ++#ifdef CONFIG_IPV6 ++static int dhd_inet6addr_notifier_call(struct notifier_block *this, ++ unsigned long event, void *ptr); ++static struct notifier_block dhd_inet6addr_notifier = { ++ .notifier_call = dhd_inet6addr_notifier_call ++}; ++/* to make sure we won't register the same notifier twice, otherwise a loop is likely to be ++ * created in kernel notifier link list (with 'next' pointing to itself) ++ */ ++static bool dhd_inet6addr_notifier_registered = FALSE; ++#endif ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) ++#include ++volatile bool dhd_mmc_suspend = FALSE; ++DECLARE_WAIT_QUEUE_HEAD(dhd_dpc_wait); ++#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) */ ++ ++#if defined(OOB_INTR_ONLY) || defined(FORCE_WOWLAN) ++extern void dhd_enable_oob_intr(struct dhd_bus *bus, bool enable); ++#endif ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && (1) ++static void dhd_hang_process(void *dhd_info, void *event_data, u8 event); ++#endif ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) ++MODULE_LICENSE("GPL v2"); ++#endif /* LinuxVer */ ++ ++#include ++ ++#ifdef BCM_FD_AGGR ++#define DBUS_RX_BUFFER_SIZE_DHD(net) (BCM_RPC_TP_DNGL_AGG_MAX_BYTE) ++#else ++#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 ++#endif /* BCM_FD_AGGR */ ++ ++#ifdef PROP_TXSTATUS ++extern bool dhd_wlfc_skip_fc(void); ++extern void dhd_wlfc_plat_init(void *dhd); ++extern void dhd_wlfc_plat_deinit(void *dhd); ++#endif /* PROP_TXSTATUS */ ++ ++#if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 15) ++const char * ++print_tainted() ++{ ++ return ""; ++} ++#endif /* LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 15) */ ++ ++/* Linux wireless extension support */ ++#if defined(WL_WIRELESS_EXT) ++#include ++extern wl_iw_extra_params_t g_wl_iw_params; ++#endif /* defined(WL_WIRELESS_EXT) */ ++ ++#if defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND) ++#include ++#endif /* defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND) */ ++ ++extern int dhd_get_suspend_bcn_li_dtim(dhd_pub_t *dhd); ++ ++#ifdef PKT_FILTER_SUPPORT ++extern void dhd_pktfilter_offload_set(dhd_pub_t * dhd, char *arg); ++extern void dhd_pktfilter_offload_enable(dhd_pub_t * dhd, char *arg, int enable, int master_mode); ++extern void dhd_pktfilter_offload_delete(dhd_pub_t *dhd, int id); ++#endif ++ ++ ++#ifdef READ_MACADDR ++extern int dhd_read_macaddr(struct dhd_info *dhd); ++#else ++static inline int dhd_read_macaddr(struct dhd_info *dhd) { return 0; } ++#endif ++#ifdef WRITE_MACADDR ++extern int dhd_write_macaddr(struct ether_addr *mac); ++#else ++static inline int dhd_write_macaddr(struct ether_addr *mac) { return 0; } ++#endif ++ ++ ++#if defined(SOFTAP_TPUT_ENHANCE) ++extern void dhd_bus_setidletime(dhd_pub_t *dhdp, int idle_time); ++extern void dhd_bus_getidletime(dhd_pub_t *dhdp, int* idle_time); ++#endif /* SOFTAP_TPUT_ENHANCE */ ++ ++ ++#ifdef SET_RPS_CPUS ++int custom_rps_map_set(struct netdev_rx_queue *queue, char *buf, size_t len); ++void custom_rps_map_clear(struct netdev_rx_queue *queue); ++#ifdef CONFIG_MACH_UNIVERSAL5433 ++#define RPS_CPUS_MASK "10" ++#else ++#define RPS_CPUS_MASK "6" ++#endif /* CONFIG_MACH_UNIVERSAL5433 */ ++#endif /* SET_RPS_CPUS */ ++ ++static int dhd_reboot_callback(struct notifier_block *this, unsigned long code, void *unused); ++static struct notifier_block dhd_reboot_notifier = { ++ .notifier_call = dhd_reboot_callback, ++ .priority = 1, ++}; ++ ++ ++typedef struct dhd_if_event { ++ struct list_head list; ++ wl_event_data_if_t event; ++ char name[IFNAMSIZ+1]; ++ uint8 mac[ETHER_ADDR_LEN]; ++} dhd_if_event_t; ++ ++/* Interface control information */ ++typedef struct dhd_if { ++ struct dhd_info *info; /* back pointer to dhd_info */ ++ /* OS/stack specifics */ ++ struct net_device *net; ++ int idx; /* iface idx in dongle */ ++ uint subunit; /* subunit */ ++ uint8 mac_addr[ETHER_ADDR_LEN]; /* assigned MAC address */ ++ bool set_macaddress; ++ bool set_multicast; ++ uint8 bssidx; /* bsscfg index for the interface */ ++ bool attached; /* Delayed attachment when unset */ ++ bool txflowcontrol; /* Per interface flow control indicator */ ++ char name[IFNAMSIZ+1]; /* linux interface name */ ++ struct net_device_stats stats; ++#ifdef DHD_WMF ++ dhd_wmf_t wmf; /* per bsscfg wmf setting */ ++#endif /* DHD_WMF */ ++#ifdef PCIE_FULL_DONGLE ++ struct list_head sta_list; /* sll of associated stations */ ++#if !defined(BCM_GMAC3) ++ spinlock_t sta_list_lock; /* lock for manipulating sll */ ++#endif /* ! BCM_GMAC3 */ ++#endif /* PCIE_FULL_DONGLE */ ++ uint32 ap_isolate; /* ap-isolation settings */ ++} dhd_if_t; ++ ++#ifdef WLMEDIA_HTSF ++typedef struct { ++ uint32 low; ++ uint32 high; ++} tsf_t; ++ ++typedef struct { ++ uint32 last_cycle; ++ uint32 last_sec; ++ uint32 last_tsf; ++ uint32 coef; /* scaling factor */ ++ uint32 coefdec1; /* first decimal */ ++ uint32 coefdec2; /* second decimal */ ++} htsf_t; ++ ++typedef struct { ++ uint32 t1; ++ uint32 t2; ++ uint32 t3; ++ uint32 t4; ++} tstamp_t; ++ ++static tstamp_t ts[TSMAX]; ++static tstamp_t maxdelayts; ++static uint32 maxdelay = 0, tspktcnt = 0, maxdelaypktno = 0; ++ ++#endif /* WLMEDIA_HTSF */ ++ ++struct ipv6_work_info_t { ++ uint8 if_idx; ++ char ipv6_addr[16]; ++ unsigned long event; ++}; ++ ++#if defined(CUSTOMER_HW20) && defined(WLANAUDIO) ++#define MAX_WLANAUDIO_BLACKLIST 4 ++ ++struct wlanaudio_blacklist { ++ bool is_blacklist; ++ uint32 cnt; ++ ulong txfail_jiffies; ++ struct ether_addr blacklist_addr; ++}; ++#endif /* CUSTOMER_HW20 && WLANAUDIO */ ++ ++/* When Perimeter locks are deployed, any blocking calls must be preceeded ++ * with a PERIM UNLOCK and followed by a PERIM LOCK. ++ * Examples of blocking calls are: schedule_timeout(), down_interruptible(), ++ * wait_event_timeout(). ++ */ ++ ++/* Local private structure (extension of pub) */ ++typedef struct dhd_info { ++#if defined(WL_WIRELESS_EXT) ++ wl_iw_t iw; /* wireless extensions state (must be first) */ ++#endif /* defined(WL_WIRELESS_EXT) */ ++ dhd_pub_t pub; ++ dhd_if_t *iflist[DHD_MAX_IFS]; /* for supporting multiple interfaces */ ++ ++ void *adapter; /* adapter information, interrupt, fw path etc. */ ++ char fw_path[PATH_MAX]; /* path to firmware image */ ++ char nv_path[PATH_MAX]; /* path to nvram vars file */ ++ char conf_path[PATH_MAX]; /* path to config vars file */ ++ ++ struct semaphore proto_sem; ++#ifdef PROP_TXSTATUS ++ spinlock_t wlfc_spinlock; ++ ++#endif /* PROP_TXSTATUS */ ++#ifdef WLMEDIA_HTSF ++ htsf_t htsf; ++#endif ++ wait_queue_head_t ioctl_resp_wait; ++ uint32 default_wd_interval; ++ ++ struct timer_list timer; ++ bool wd_timer_valid; ++ struct tasklet_struct tasklet; ++ spinlock_t sdlock; ++ spinlock_t txqlock; ++ spinlock_t dhd_lock; ++ ++ struct semaphore sdsem; ++ tsk_ctl_t thr_dpc_ctl; ++ tsk_ctl_t thr_wdt_ctl; ++ ++ tsk_ctl_t thr_rxf_ctl; ++ spinlock_t rxf_lock; ++ bool rxthread_enabled; ++ ++ /* Wakelocks */ ++#if defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) ++ struct wake_lock wl_wifi; /* Wifi wakelock */ ++ struct wake_lock wl_rxwake; /* Wifi rx wakelock */ ++ struct wake_lock wl_ctrlwake; /* Wifi ctrl wakelock */ ++ struct wake_lock wl_wdwake; /* Wifi wd wakelock */ ++#ifdef BCMPCIE_OOB_HOST_WAKE ++ struct wake_lock wl_intrwake; /* Host wakeup wakelock */ ++#endif /* BCMPCIE_OOB_HOST_WAKE */ ++#endif /* CONFIG_HAS_WAKELOCK && LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) */ ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1 ++ /* net_device interface lock, prevent race conditions among net_dev interface ++ * calls and wifi_on or wifi_off ++ */ ++ struct mutex dhd_net_if_mutex; ++ struct mutex dhd_suspend_mutex; ++#endif ++ spinlock_t wakelock_spinlock; ++ uint32 wakelock_counter; ++ int wakelock_wd_counter; ++ int wakelock_rx_timeout_enable; ++ int wakelock_ctrl_timeout_enable; ++ bool waive_wakelock; ++ uint32 wakelock_before_waive; ++ ++ /* Thread to issue ioctl for multicast */ ++ wait_queue_head_t ctrl_wait; ++ atomic_t pend_8021x_cnt; ++ dhd_attach_states_t dhd_state; ++#ifdef SHOW_LOGTRACE ++ dhd_event_log_t event_data; ++#endif /* SHOW_LOGTRACE */ ++ ++#if defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND) ++ struct early_suspend early_suspend; ++#endif /* CONFIG_HAS_EARLYSUSPEND && DHD_USE_EARLYSUSPEND */ ++ ++#ifdef ARP_OFFLOAD_SUPPORT ++ u32 pend_ipaddr; ++#endif /* ARP_OFFLOAD_SUPPORT */ ++#ifdef BCM_FD_AGGR ++ void *rpc_th; ++ void *rpc_osh; ++ struct timer_list rpcth_timer; ++ bool rpcth_timer_active; ++ bool fdaggr; ++#endif ++#ifdef DHDTCPACK_SUPPRESS ++ spinlock_t tcpack_lock; ++#endif /* DHDTCPACK_SUPPRESS */ ++ void *dhd_deferred_wq; ++#ifdef DEBUG_CPU_FREQ ++ struct notifier_block freq_trans; ++ int __percpu *new_freq; ++#endif ++ unsigned int unit; ++ struct notifier_block pm_notifier; ++#if defined(CUSTOMER_HW20) && defined(WLANAUDIO) ++ struct wlanaudio_blacklist wlanaudio_blist[MAX_WLANAUDIO_BLACKLIST]; ++ bool is_wlanaudio_blist; ++#endif /* CUSTOMER_HW20 && WLANAUDIO */ ++} dhd_info_t; ++ ++#define DHDIF_FWDER(dhdif) FALSE ++ ++/* Flag to indicate if we should download firmware on driver load */ ++uint dhd_download_fw_on_driverload = TRUE; ++ ++/* Definitions to provide path to the firmware and nvram ++ * example nvram_path[MOD_PARAM_PATHLEN]="/projects/wlan/nvram.txt" ++ */ ++char firmware_path[MOD_PARAM_PATHLEN]; ++char nvram_path[MOD_PARAM_PATHLEN]; ++char config_path[MOD_PARAM_PATHLEN]; ++ ++/* backup buffer for firmware and nvram path */ ++char fw_bak_path[MOD_PARAM_PATHLEN]; ++char nv_bak_path[MOD_PARAM_PATHLEN]; ++ ++/* information string to keep firmware, chio, cheip version info visiable from log */ ++char info_string[MOD_PARAM_INFOLEN]; ++module_param_string(info_string, info_string, MOD_PARAM_INFOLEN, 0444); ++int op_mode = 0; ++int disable_proptx = 0; ++module_param(op_mode, int, 0644); ++extern int wl_control_wl_start(struct net_device *dev); ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(BCMLXSDMMC) ++struct semaphore dhd_registration_sem; ++#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ ++ ++/* deferred handlers */ ++static void dhd_ifadd_event_handler(void *handle, void *event_info, u8 event); ++static void dhd_ifdel_event_handler(void *handle, void *event_info, u8 event); ++static void dhd_set_mac_addr_handler(void *handle, void *event_info, u8 event); ++static void dhd_set_mcast_list_handler(void *handle, void *event_info, u8 event); ++#ifdef CONFIG_IPV6 ++static void dhd_inet6_work_handler(void *dhd_info, void *event_data, u8 event); ++#endif ++ ++#ifdef WL_CFG80211 ++extern void dhd_netdev_free(struct net_device *ndev); ++#endif /* WL_CFG80211 */ ++ ++/* Error bits */ ++module_param(dhd_msg_level, int, 0); ++#if defined(WL_WIRELESS_EXT) ++module_param(iw_msg_level, int, 0); ++#endif ++#ifdef WL_CFG80211 ++module_param(wl_dbg_level, int, 0); ++#endif ++module_param(android_msg_level, int, 0); ++module_param(config_msg_level, int, 0); ++ ++#ifdef ARP_OFFLOAD_SUPPORT ++/* ARP offload enable */ ++uint dhd_arp_enable = TRUE; ++module_param(dhd_arp_enable, uint, 0); ++ ++/* ARP offload agent mode : Enable ARP Host Auto-Reply and ARP Peer Auto-Reply */ ++ ++uint dhd_arp_mode = ARP_OL_AGENT | ARP_OL_PEER_AUTO_REPLY; ++ ++module_param(dhd_arp_mode, uint, 0); ++#endif /* ARP_OFFLOAD_SUPPORT */ ++ ++/* Disable Prop tx */ ++module_param(disable_proptx, int, 0644); ++/* load firmware and/or nvram values from the filesystem */ ++module_param_string(firmware_path, firmware_path, MOD_PARAM_PATHLEN, 0660); ++module_param_string(nvram_path, nvram_path, MOD_PARAM_PATHLEN, 0660); ++module_param_string(config_path, config_path, MOD_PARAM_PATHLEN, 0); ++ ++/* Watchdog interval */ ++ ++/* extend watchdog expiration to 2 seconds when DPC is running */ ++#define WATCHDOG_EXTEND_INTERVAL (2000) ++ ++uint dhd_watchdog_ms = CUSTOM_DHD_WATCHDOG_MS; ++module_param(dhd_watchdog_ms, uint, 0); ++ ++#if defined(DHD_DEBUG) ++/* Console poll interval */ ++uint dhd_console_ms = 0; ++module_param(dhd_console_ms, uint, 0644); ++#endif /* defined(DHD_DEBUG) */ ++ ++ ++uint dhd_slpauto = TRUE; ++module_param(dhd_slpauto, uint, 0); ++ ++#ifdef PKT_FILTER_SUPPORT ++/* Global Pkt filter enable control */ ++uint dhd_pkt_filter_enable = TRUE; ++module_param(dhd_pkt_filter_enable, uint, 0); ++#endif ++ ++/* Pkt filter init setup */ ++uint dhd_pkt_filter_init = 0; ++module_param(dhd_pkt_filter_init, uint, 0); ++ ++/* Pkt filter mode control */ ++uint dhd_master_mode = FALSE; ++module_param(dhd_master_mode, uint, 0); ++ ++int dhd_watchdog_prio = 0; ++module_param(dhd_watchdog_prio, int, 0); ++ ++/* DPC thread priority */ ++int dhd_dpc_prio = CUSTOM_DPC_PRIO_SETTING; ++module_param(dhd_dpc_prio, int, 0); ++ ++/* RX frame thread priority */ ++int dhd_rxf_prio = CUSTOM_RXF_PRIO_SETTING; ++module_param(dhd_rxf_prio, int, 0); ++ ++int passive_channel_skip = 0; ++module_param(passive_channel_skip, int, (S_IRUSR|S_IWUSR)); ++ ++#if !defined(BCMDHDUSB) ++extern int dhd_dongle_ramsize; ++module_param(dhd_dongle_ramsize, int, 0); ++#endif /* BCMDHDUSB */ ++ ++/* Keep track of number of instances */ ++static int dhd_found = 0; ++static int instance_base = 0; /* Starting instance number */ ++module_param(instance_base, int, 0644); ++ ++#if defined(CUSTOMER_HW20) && defined(WLANAUDIO) ++dhd_info_t *dhd_global = NULL; ++#endif /* CUSTOMER_HW20 && WLANAUDIO */ ++ ++ ++ ++/* DHD Perimiter lock only used in router with bypass forwarding. */ ++#define DHD_PERIM_RADIO_INIT() do { /* noop */ } while (0) ++#define DHD_PERIM_LOCK_TRY(unit, flag) do { /* noop */ } while (0) ++#define DHD_PERIM_UNLOCK_TRY(unit, flag) do { /* noop */ } while (0) ++#define DHD_PERIM_LOCK_ALL() do { /* noop */ } while (0) ++#define DHD_PERIM_UNLOCK_ALL() do { /* noop */ } while (0) ++ ++#ifdef PCIE_FULL_DONGLE ++#if defined(BCM_GMAC3) ++#define DHD_IF_STA_LIST_LOCK_INIT(ifp) do { /* noop */ } while (0) ++#define DHD_IF_STA_LIST_LOCK(ifp, flags) ({ BCM_REFERENCE(flags); }) ++#define DHD_IF_STA_LIST_UNLOCK(ifp, flags) ({ BCM_REFERENCE(flags); }) ++#else /* ! BCM_GMAC3 */ ++#define DHD_IF_STA_LIST_LOCK_INIT(ifp) spin_lock_init(&(ifp)->sta_list_lock) ++#define DHD_IF_STA_LIST_LOCK(ifp, flags) \ ++ spin_lock_irqsave(&(ifp)->sta_list_lock, (flags)) ++#define DHD_IF_STA_LIST_UNLOCK(ifp, flags) \ ++ spin_unlock_irqrestore(&(ifp)->sta_list_lock, (flags)) ++#endif /* ! BCM_GMAC3 */ ++#endif /* PCIE_FULL_DONGLE */ ++ ++/* Control fw roaming */ ++#ifdef BCMCCX ++uint dhd_roam_disable = 0; ++#else ++uint dhd_roam_disable = 0; ++#endif /* BCMCCX */ ++ ++/* Control radio state */ ++uint dhd_radio_up = 1; ++ ++/* Network inteface name */ ++char iface_name[IFNAMSIZ] = {'\0'}; ++module_param_string(iface_name, iface_name, IFNAMSIZ, 0); ++ ++/* The following are specific to the SDIO dongle */ ++ ++/* IOCTL response timeout */ ++int dhd_ioctl_timeout_msec = IOCTL_RESP_TIMEOUT; ++ ++/* Idle timeout for backplane clock */ ++int dhd_idletime = DHD_IDLETIME_TICKS; ++module_param(dhd_idletime, int, 0); ++ ++/* Use polling */ ++uint dhd_poll = FALSE; ++module_param(dhd_poll, uint, 0); ++ ++/* Use interrupts */ ++uint dhd_intr = TRUE; ++module_param(dhd_intr, uint, 0); ++ ++/* SDIO Drive Strength (in milliamps) */ ++uint dhd_sdiod_drive_strength = 6; ++module_param(dhd_sdiod_drive_strength, uint, 0); ++ ++#ifdef BCMSDIO ++/* Tx/Rx bounds */ ++extern uint dhd_txbound; ++extern uint dhd_rxbound; ++module_param(dhd_txbound, uint, 0); ++module_param(dhd_rxbound, uint, 0); ++ ++/* Deferred transmits */ ++extern uint dhd_deferred_tx; ++module_param(dhd_deferred_tx, uint, 0); ++ ++#ifdef BCMDBGFS ++extern void dhd_dbg_init(dhd_pub_t *dhdp); ++extern void dhd_dbg_remove(void); ++#endif /* BCMDBGFS */ ++ ++#endif /* BCMSDIO */ ++ ++ ++#ifdef SDTEST ++/* Echo packet generator (pkts/s) */ ++uint dhd_pktgen = 0; ++module_param(dhd_pktgen, uint, 0); ++ ++/* Echo packet len (0 => sawtooth, max 2040) */ ++uint dhd_pktgen_len = 0; ++module_param(dhd_pktgen_len, uint, 0); ++#endif /* SDTEST */ ++ ++#if defined(BCMSUP_4WAY_HANDSHAKE) ++/* Use in dongle supplicant for 4-way handshake */ ++uint dhd_use_idsup = 0; ++module_param(dhd_use_idsup, uint, 0); ++#endif /* BCMSUP_4WAY_HANDSHAKE */ ++ ++extern char dhd_version[]; ++ ++int dhd_net_bus_devreset(struct net_device *dev, uint8 flag); ++static void dhd_net_if_lock_local(dhd_info_t *dhd); ++static void dhd_net_if_unlock_local(dhd_info_t *dhd); ++static void dhd_suspend_lock(dhd_pub_t *dhdp); ++static void dhd_suspend_unlock(dhd_pub_t *dhdp); ++ ++#ifdef WLMEDIA_HTSF ++void htsf_update(dhd_info_t *dhd, void *data); ++tsf_t prev_tsf, cur_tsf; ++ ++uint32 dhd_get_htsf(dhd_info_t *dhd, int ifidx); ++static int dhd_ioctl_htsf_get(dhd_info_t *dhd, int ifidx); ++static void dhd_dump_latency(void); ++static void dhd_htsf_addtxts(dhd_pub_t *dhdp, void *pktbuf); ++static void dhd_htsf_addrxts(dhd_pub_t *dhdp, void *pktbuf); ++static void dhd_dump_htsfhisto(histo_t *his, char *s); ++#endif /* WLMEDIA_HTSF */ ++ ++/* Monitor interface */ ++int dhd_monitor_init(void *dhd_pub); ++int dhd_monitor_uninit(void); ++ ++ ++#if defined(WL_WIRELESS_EXT) ++struct iw_statistics *dhd_get_wireless_stats(struct net_device *dev); ++#endif /* defined(WL_WIRELESS_EXT) */ ++ ++static void dhd_dpc(ulong data); ++/* forward decl */ ++extern int dhd_wait_pend8021x(struct net_device *dev); ++void dhd_os_wd_timer_extend(void *bus, bool extend); ++ ++#ifdef TOE ++#ifndef BDC ++#error TOE requires BDC ++#endif /* !BDC */ ++static int dhd_toe_get(dhd_info_t *dhd, int idx, uint32 *toe_ol); ++static int dhd_toe_set(dhd_info_t *dhd, int idx, uint32 toe_ol); ++#endif /* TOE */ ++ ++static int dhd_wl_host_event(dhd_info_t *dhd, int *ifidx, void *pktdata, ++ wl_event_msg_t *event_ptr, void **data_ptr); ++#ifdef DHD_UNICAST_DHCP ++static const uint8 llc_snap_hdr[SNAP_HDR_LEN] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00}; ++static int dhd_get_pkt_ip_type(dhd_pub_t *dhd, void *skb, uint8 **data_ptr, ++ int *len_ptr, uint8 *prot_ptr); ++static int dhd_get_pkt_ether_type(dhd_pub_t *dhd, void *skb, uint8 **data_ptr, ++ int *len_ptr, uint16 *et_ptr, bool *snap_ptr); ++ ++static int dhd_convert_dhcp_broadcast_ack_to_unicast(dhd_pub_t *pub, void *pktbuf, int ifidx); ++#endif /* DHD_UNICAST_DHCP */ ++#ifdef DHD_L2_FILTER ++static int dhd_l2_filter_block_ping(dhd_pub_t *pub, void *pktbuf, int ifidx); ++#endif ++#if defined(CONFIG_PM_SLEEP) ++static int dhd_pm_callback(struct notifier_block *nfb, unsigned long action, void *ignored) ++{ ++ int ret = NOTIFY_DONE; ++ bool suspend = FALSE; ++ dhd_info_t *dhdinfo = (dhd_info_t*)container_of(nfb, struct dhd_info, pm_notifier); ++ ++ BCM_REFERENCE(dhdinfo); ++ switch (action) { ++ case PM_HIBERNATION_PREPARE: ++ case PM_SUSPEND_PREPARE: ++ suspend = TRUE; ++ break; ++ case PM_POST_HIBERNATION: ++ case PM_POST_SUSPEND: ++ suspend = FALSE; ++ break; ++ } ++ ++#if defined(SUPPORT_P2P_GO_PS) ++#ifdef PROP_TXSTATUS ++ if (suspend) { ++ DHD_OS_WAKE_LOCK_WAIVE(&dhdinfo->pub); ++ dhd_wlfc_suspend(&dhdinfo->pub); ++ DHD_OS_WAKE_LOCK_RESTORE(&dhdinfo->pub); ++ } else ++ dhd_wlfc_resume(&dhdinfo->pub); ++#endif ++#endif /* defined(SUPPORT_P2P_GO_PS) */ ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && (LINUX_VERSION_CODE <= \ ++ KERNEL_VERSION(2, 6, 39)) ++ dhd_mmc_suspend = suspend; ++ smp_mb(); ++#endif ++ ++ return ret; ++} ++ ++static struct notifier_block dhd_pm_notifier = { ++ .notifier_call = dhd_pm_callback, ++ .priority = 10 ++}; ++/* to make sure we won't register the same notifier twice, otherwise a loop is likely to be ++ * created in kernel notifier link list (with 'next' pointing to itself) ++ */ ++static bool dhd_pm_notifier_registered = FALSE; ++ ++extern int register_pm_notifier(struct notifier_block *nb); ++extern int unregister_pm_notifier(struct notifier_block *nb); ++#endif /* CONFIG_PM_SLEEP */ ++ ++/* Request scheduling of the bus rx frame */ ++static void dhd_sched_rxf(dhd_pub_t *dhdp, void *skb); ++static void dhd_os_rxflock(dhd_pub_t *pub); ++static void dhd_os_rxfunlock(dhd_pub_t *pub); ++ ++/** priv_link is the link between netdev and the dhdif and dhd_info structs. */ ++typedef struct dhd_dev_priv { ++ dhd_info_t * dhd; /* cached pointer to dhd_info in netdevice priv */ ++ dhd_if_t * ifp; /* cached pointer to dhd_if in netdevice priv */ ++ int ifidx; /* interface index */ ++} dhd_dev_priv_t; ++ ++#define DHD_DEV_PRIV_SIZE (sizeof(dhd_dev_priv_t)) ++#define DHD_DEV_PRIV(dev) ((dhd_dev_priv_t *)DEV_PRIV(dev)) ++#define DHD_DEV_INFO(dev) (((dhd_dev_priv_t *)DEV_PRIV(dev))->dhd) ++#define DHD_DEV_IFP(dev) (((dhd_dev_priv_t *)DEV_PRIV(dev))->ifp) ++#define DHD_DEV_IFIDX(dev) (((dhd_dev_priv_t *)DEV_PRIV(dev))->ifidx) ++ ++/** Clear the dhd net_device's private structure. */ ++static inline void ++dhd_dev_priv_clear(struct net_device * dev) ++{ ++ dhd_dev_priv_t * dev_priv; ++ ASSERT(dev != (struct net_device *)NULL); ++ dev_priv = DHD_DEV_PRIV(dev); ++ dev_priv->dhd = (dhd_info_t *)NULL; ++ dev_priv->ifp = (dhd_if_t *)NULL; ++ dev_priv->ifidx = DHD_BAD_IF; ++} ++ ++/** Setup the dhd net_device's private structure. */ ++static inline void ++dhd_dev_priv_save(struct net_device * dev, dhd_info_t * dhd, dhd_if_t * ifp, ++ int ifidx) ++{ ++ dhd_dev_priv_t * dev_priv; ++ ASSERT(dev != (struct net_device *)NULL); ++ dev_priv = DHD_DEV_PRIV(dev); ++ dev_priv->dhd = dhd; ++ dev_priv->ifp = ifp; ++ dev_priv->ifidx = ifidx; ++} ++ ++#ifdef PCIE_FULL_DONGLE ++ ++/** Dummy objects are defined with state representing bad|down. ++ * Performance gains from reducing branch conditionals, instruction parallelism, ++ * dual issue, reducing load shadows, avail of larger pipelines. ++ * Use DHD_XXX_NULL instead of (dhd_xxx_t *)NULL, whenever an object pointer ++ * is accessed via the dhd_sta_t. ++ */ ++ ++/* Dummy dhd_info object */ ++dhd_info_t dhd_info_null = { ++#if defined(BCM_GMAC3) ++ .fwdh = FWDER_NULL, ++#endif ++ .pub = { ++ .info = &dhd_info_null, ++#ifdef DHDTCPACK_SUPPRESS ++ .tcpack_sup_mode = TCPACK_SUP_REPLACE, ++#endif /* DHDTCPACK_SUPPRESS */ ++ .up = FALSE, .busstate = DHD_BUS_DOWN ++ } ++}; ++#define DHD_INFO_NULL (&dhd_info_null) ++#define DHD_PUB_NULL (&dhd_info_null.pub) ++ ++/* Dummy netdevice object */ ++struct net_device dhd_net_dev_null = { ++ .reg_state = NETREG_UNREGISTERED ++}; ++#define DHD_NET_DEV_NULL (&dhd_net_dev_null) ++ ++/* Dummy dhd_if object */ ++dhd_if_t dhd_if_null = { ++#if defined(BCM_GMAC3) ++ .fwdh = FWDER_NULL, ++#endif ++#ifdef WMF ++ .wmf = { .wmf_enable = TRUE }, ++#endif ++ .info = DHD_INFO_NULL, ++ .net = DHD_NET_DEV_NULL, ++ .idx = DHD_BAD_IF ++}; ++#define DHD_IF_NULL (&dhd_if_null) ++ ++#define DHD_STA_NULL ((dhd_sta_t *)NULL) ++ ++/** Interface STA list management. */ ++ ++/** Fetch the dhd_if object, given the interface index in the dhd. */ ++static inline dhd_if_t *dhd_get_ifp(dhd_pub_t *dhdp, uint32 ifidx); ++ ++/** Alloc/Free a dhd_sta object from the dhd instances' sta_pool. */ ++static void dhd_sta_free(dhd_pub_t *pub, dhd_sta_t *sta); ++static dhd_sta_t * dhd_sta_alloc(dhd_pub_t * dhdp); ++ ++/* Delete a dhd_sta or flush all dhd_sta in an interface's sta_list. */ ++static void dhd_if_del_sta_list(dhd_if_t * ifp); ++static void dhd_if_flush_sta(dhd_if_t * ifp); ++ ++/* Construct/Destruct a sta pool. */ ++static int dhd_sta_pool_init(dhd_pub_t *dhdp, int max_sta); ++static void dhd_sta_pool_fini(dhd_pub_t *dhdp, int max_sta); ++static void dhd_sta_pool_clear(dhd_pub_t *dhdp, int max_sta); ++ ++ ++/* Return interface pointer */ ++static inline dhd_if_t *dhd_get_ifp(dhd_pub_t *dhdp, uint32 ifidx) ++{ ++ ASSERT(ifidx < DHD_MAX_IFS); ++ ++ if (ifidx >= DHD_MAX_IFS) ++ return NULL; ++ ++ return dhdp->info->iflist[ifidx]; ++} ++ ++/** Reset a dhd_sta object and free into the dhd pool. */ ++static void ++dhd_sta_free(dhd_pub_t * dhdp, dhd_sta_t * sta) ++{ ++ int prio; ++ ++ ASSERT((sta != DHD_STA_NULL) && (sta->idx != ID16_INVALID)); ++ ++ ASSERT((dhdp->staid_allocator != NULL) && (dhdp->sta_pool != NULL)); ++ id16_map_free(dhdp->staid_allocator, sta->idx); ++ for (prio = 0; prio < (int)NUMPRIO; prio++) ++ sta->flowid[prio] = FLOWID_INVALID; ++ sta->ifp = DHD_IF_NULL; /* dummy dhd_if object */ ++ sta->ifidx = DHD_BAD_IF; ++ bzero(sta->ea.octet, ETHER_ADDR_LEN); ++ INIT_LIST_HEAD(&sta->list); ++ sta->idx = ID16_INVALID; /* implying free */ ++} ++ ++/** Allocate a dhd_sta object from the dhd pool. */ ++static dhd_sta_t * ++dhd_sta_alloc(dhd_pub_t * dhdp) ++{ ++ uint16 idx; ++ dhd_sta_t * sta; ++ dhd_sta_pool_t * sta_pool; ++ ++ ASSERT((dhdp->staid_allocator != NULL) && (dhdp->sta_pool != NULL)); ++ ++ idx = id16_map_alloc(dhdp->staid_allocator); ++ if (idx == ID16_INVALID) { ++ DHD_ERROR(("%s: cannot get free staid\n", __FUNCTION__)); ++ return DHD_STA_NULL; ++ } ++ ++ sta_pool = (dhd_sta_pool_t *)(dhdp->sta_pool); ++ sta = &sta_pool[idx]; ++ ++ ASSERT((sta->idx == ID16_INVALID) && ++ (sta->ifp == DHD_IF_NULL) && (sta->ifidx == DHD_BAD_IF)); ++ sta->idx = idx; /* implying allocated */ ++ ++ return sta; ++} ++ ++/** Delete all STAs in an interface's STA list. */ ++static void ++dhd_if_del_sta_list(dhd_if_t *ifp) ++{ ++ dhd_sta_t *sta, *next; ++ unsigned long flags; ++ ++ DHD_IF_STA_LIST_LOCK(ifp, flags); ++ ++ list_for_each_entry_safe(sta, next, &ifp->sta_list, list) { ++#if defined(BCM_GMAC3) ++ if (ifp->fwdh) { ++ /* Remove sta from WOFA forwarder. */ ++ fwder_deassoc(ifp->fwdh, (uint16 *)(sta->ea.octet), (wofa_t)sta); ++ } ++#endif /* BCM_GMAC3 */ ++ list_del(&sta->list); ++ dhd_sta_free(&ifp->info->pub, sta); ++ } ++ ++ DHD_IF_STA_LIST_UNLOCK(ifp, flags); ++ ++ return; ++} ++ ++/** Router/GMAC3: Flush all station entries in the forwarder's WOFA database. */ ++static void ++dhd_if_flush_sta(dhd_if_t * ifp) ++{ ++#if defined(BCM_GMAC3) ++ ++ if (ifp && (ifp->fwdh != FWDER_NULL)) { ++ dhd_sta_t *sta, *next; ++ unsigned long flags; ++ ++ DHD_IF_STA_LIST_LOCK(ifp, flags); ++ ++ list_for_each_entry_safe(sta, next, &ifp->sta_list, list) { ++ /* Remove any sta entry from WOFA forwarder. */ ++ fwder_flush(ifp->fwdh, (wofa_t)sta); ++ } ++ ++ DHD_IF_STA_LIST_UNLOCK(ifp, flags); ++ } ++#endif /* BCM_GMAC3 */ ++} ++ ++/** Construct a pool of dhd_sta_t objects to be used by interfaces. */ ++static int ++dhd_sta_pool_init(dhd_pub_t *dhdp, int max_sta) ++{ ++ int idx, sta_pool_memsz; ++ dhd_sta_t * sta; ++ dhd_sta_pool_t * sta_pool; ++ void * staid_allocator; ++ ++ ASSERT(dhdp != (dhd_pub_t *)NULL); ++ ASSERT((dhdp->staid_allocator == NULL) && (dhdp->sta_pool == NULL)); ++ ++ /* dhd_sta objects per radio are managed in a table. id#0 reserved. */ ++ staid_allocator = id16_map_init(dhdp->osh, max_sta, 1); ++ if (staid_allocator == NULL) { ++ DHD_ERROR(("%s: sta id allocator init failure\n", __FUNCTION__)); ++ return BCME_ERROR; ++ } ++ ++ /* Pre allocate a pool of dhd_sta objects (one extra). */ ++ sta_pool_memsz = ((max_sta + 1) * sizeof(dhd_sta_t)); /* skip idx 0 */ ++ sta_pool = (dhd_sta_pool_t *)MALLOC(dhdp->osh, sta_pool_memsz); ++ if (sta_pool == NULL) { ++ DHD_ERROR(("%s: sta table alloc failure\n", __FUNCTION__)); ++ id16_map_fini(dhdp->osh, staid_allocator); ++ return BCME_ERROR; ++ } ++ ++ dhdp->sta_pool = sta_pool; ++ dhdp->staid_allocator = staid_allocator; ++ ++ /* Initialize all sta(s) for the pre-allocated free pool. */ ++ bzero((uchar *)sta_pool, sta_pool_memsz); ++ for (idx = max_sta; idx >= 1; idx--) { /* skip sta_pool[0] */ ++ sta = &sta_pool[idx]; ++ sta->idx = id16_map_alloc(staid_allocator); ++ ASSERT(sta->idx <= max_sta); ++ } ++ /* Now place them into the pre-allocated free pool. */ ++ for (idx = 1; idx <= max_sta; idx++) { ++ sta = &sta_pool[idx]; ++ dhd_sta_free(dhdp, sta); ++ } ++ ++ return BCME_OK; ++} ++ ++/** Destruct the pool of dhd_sta_t objects. ++ * Caller must ensure that no STA objects are currently associated with an if. ++ */ ++static void ++dhd_sta_pool_fini(dhd_pub_t *dhdp, int max_sta) ++{ ++ dhd_sta_pool_t * sta_pool = (dhd_sta_pool_t *)dhdp->sta_pool; ++ ++ if (sta_pool) { ++ int idx; ++ int sta_pool_memsz = ((max_sta + 1) * sizeof(dhd_sta_t)); ++ for (idx = 1; idx <= max_sta; idx++) { ++ ASSERT(sta_pool[idx].ifp == DHD_IF_NULL); ++ ASSERT(sta_pool[idx].idx == ID16_INVALID); ++ } ++ MFREE(dhdp->osh, dhdp->sta_pool, sta_pool_memsz); ++ dhdp->sta_pool = NULL; ++ } ++ ++ id16_map_fini(dhdp->osh, dhdp->staid_allocator); ++ dhdp->staid_allocator = NULL; ++} ++ ++/* Clear the pool of dhd_sta_t objects for built-in type driver */ ++static void ++dhd_sta_pool_clear(dhd_pub_t *dhdp, int max_sta) ++{ ++ int idx, sta_pool_memsz; ++ dhd_sta_t * sta; ++ dhd_sta_pool_t * sta_pool; ++ void *staid_allocator; ++ ++ if (!dhdp) { ++ DHD_ERROR(("%s: dhdp is NULL\n", __FUNCTION__)); ++ return; ++ } ++ ++ sta_pool = (dhd_sta_pool_t *)dhdp->sta_pool; ++ staid_allocator = dhdp->staid_allocator; ++ ++ if (!sta_pool) { ++ DHD_ERROR(("%s: sta_pool is NULL\n", __FUNCTION__)); ++ return; ++ } ++ ++ if (!staid_allocator) { ++ DHD_ERROR(("%s: staid_allocator is NULL\n", __FUNCTION__)); ++ return; ++ } ++ ++ /* clear free pool */ ++ sta_pool_memsz = ((max_sta + 1) * sizeof(dhd_sta_t)); ++ bzero((uchar *)sta_pool, sta_pool_memsz); ++ ++ /* dhd_sta objects per radio are managed in a table. id#0 reserved. */ ++ id16_map_clear(staid_allocator, max_sta, 1); ++ ++ /* Initialize all sta(s) for the pre-allocated free pool. */ ++ for (idx = max_sta; idx >= 1; idx--) { /* skip sta_pool[0] */ ++ sta = &sta_pool[idx]; ++ sta->idx = id16_map_alloc(staid_allocator); ++ ASSERT(sta->idx <= max_sta); ++ } ++ /* Now place them into the pre-allocated free pool. */ ++ for (idx = 1; idx <= max_sta; idx++) { ++ sta = &sta_pool[idx]; ++ dhd_sta_free(dhdp, sta); ++ } ++} ++ ++/** Find STA with MAC address ea in an interface's STA list. */ ++dhd_sta_t * ++dhd_find_sta(void *pub, int ifidx, void *ea) ++{ ++ dhd_sta_t *sta; ++ dhd_if_t *ifp; ++ unsigned long flags; ++ ++ ASSERT(ea != NULL); ++ ifp = dhd_get_ifp((dhd_pub_t *)pub, ifidx); ++ if (ifp == NULL) ++ return DHD_STA_NULL; ++ ++ DHD_IF_STA_LIST_LOCK(ifp, flags); ++ ++ list_for_each_entry(sta, &ifp->sta_list, list) { ++ if (!memcmp(sta->ea.octet, ea, ETHER_ADDR_LEN)) { ++ DHD_IF_STA_LIST_UNLOCK(ifp, flags); ++ return sta; ++ } ++ } ++ ++ DHD_IF_STA_LIST_UNLOCK(ifp, flags); ++ ++ return DHD_STA_NULL; ++} ++ ++/** Add STA into the interface's STA list. */ ++dhd_sta_t * ++dhd_add_sta(void *pub, int ifidx, void *ea) ++{ ++ dhd_sta_t *sta; ++ dhd_if_t *ifp; ++ unsigned long flags; ++ ++ ASSERT(ea != NULL); ++ ifp = dhd_get_ifp((dhd_pub_t *)pub, ifidx); ++ if (ifp == NULL) ++ return DHD_STA_NULL; ++ ++ sta = dhd_sta_alloc((dhd_pub_t *)pub); ++ if (sta == DHD_STA_NULL) { ++ DHD_ERROR(("%s: Alloc failed\n", __FUNCTION__)); ++ return DHD_STA_NULL; ++ } ++ ++ memcpy(sta->ea.octet, ea, ETHER_ADDR_LEN); ++ ++ /* link the sta and the dhd interface */ ++ sta->ifp = ifp; ++ sta->ifidx = ifidx; ++ INIT_LIST_HEAD(&sta->list); ++ ++ DHD_IF_STA_LIST_LOCK(ifp, flags); ++ ++ list_add_tail(&sta->list, &ifp->sta_list); ++ ++#if defined(BCM_GMAC3) ++ if (ifp->fwdh) { ++ ASSERT(ISALIGNED(ea, 2)); ++ /* Add sta to WOFA forwarder. */ ++ fwder_reassoc(ifp->fwdh, (uint16 *)ea, (wofa_t)sta); ++ } ++#endif /* BCM_GMAC3 */ ++ ++ DHD_IF_STA_LIST_UNLOCK(ifp, flags); ++ ++ return sta; ++} ++ ++/** Delete STA from the interface's STA list. */ ++void ++dhd_del_sta(void *pub, int ifidx, void *ea) ++{ ++ dhd_sta_t *sta, *next; ++ dhd_if_t *ifp; ++ unsigned long flags; ++ ++ ASSERT(ea != NULL); ++ ifp = dhd_get_ifp((dhd_pub_t *)pub, ifidx); ++ if (ifp == NULL) ++ return; ++ ++ DHD_IF_STA_LIST_LOCK(ifp, flags); ++ ++ list_for_each_entry_safe(sta, next, &ifp->sta_list, list) { ++ if (!memcmp(sta->ea.octet, ea, ETHER_ADDR_LEN)) { ++#if defined(BCM_GMAC3) ++ if (ifp->fwdh) { /* Found a sta, remove from WOFA forwarder. */ ++ ASSERT(ISALIGNED(ea, 2)); ++ fwder_deassoc(ifp->fwdh, (uint16 *)ea, (wofa_t)sta); ++ } ++#endif /* BCM_GMAC3 */ ++ list_del(&sta->list); ++ dhd_sta_free(&ifp->info->pub, sta); ++ } ++ } ++ ++ DHD_IF_STA_LIST_UNLOCK(ifp, flags); ++ ++ return; ++} ++ ++/** Add STA if it doesn't exist. Not reentrant. */ ++dhd_sta_t* ++dhd_findadd_sta(void *pub, int ifidx, void *ea) ++{ ++ dhd_sta_t *sta; ++ ++ sta = dhd_find_sta(pub, ifidx, ea); ++ ++ if (!sta) { ++ /* Add entry */ ++ sta = dhd_add_sta(pub, ifidx, ea); ++ } ++ ++ return sta; ++} ++#else ++static inline void dhd_if_flush_sta(dhd_if_t * ifp) { } ++static inline void dhd_if_del_sta_list(dhd_if_t *ifp) {} ++static inline int dhd_sta_pool_init(dhd_pub_t *dhdp, int max_sta) { return BCME_OK; } ++static inline void dhd_sta_pool_fini(dhd_pub_t *dhdp, int max_sta) {} ++static inline void dhd_sta_pool_clear(dhd_pub_t *dhdp, int max_sta) {} ++dhd_sta_t *dhd_findadd_sta(void *pub, int ifidx, void *ea) { return NULL; } ++void dhd_del_sta(void *pub, int ifidx, void *ea) {} ++#endif /* PCIE_FULL_DONGLE */ ++ ++ ++/* Returns dhd iflist index correspondig the the bssidx provided by apps */ ++int dhd_bssidx2idx(dhd_pub_t *dhdp, uint32 bssidx) ++{ ++ dhd_if_t *ifp; ++ dhd_info_t *dhd = dhdp->info; ++ int i; ++ ++ ASSERT(bssidx < DHD_MAX_IFS); ++ ASSERT(dhdp); ++ ++ for (i = 0; i < DHD_MAX_IFS; i++) { ++ ifp = dhd->iflist[i]; ++ if (ifp && (ifp->bssidx == bssidx)) { ++ DHD_TRACE(("Index manipulated for %s from %d to %d\n", ++ ifp->name, bssidx, i)); ++ break; ++ } ++ } ++ return i; ++} ++ ++static inline int dhd_rxf_enqueue(dhd_pub_t *dhdp, void* skb) ++{ ++ uint32 store_idx; ++ uint32 sent_idx; ++ ++ if (!skb) { ++ DHD_ERROR(("dhd_rxf_enqueue: NULL skb!!!\n")); ++ return BCME_ERROR; ++ } ++ ++ dhd_os_rxflock(dhdp); ++ store_idx = dhdp->store_idx; ++ sent_idx = dhdp->sent_idx; ++ if (dhdp->skbbuf[store_idx] != NULL) { ++ /* Make sure the previous packets are processed */ ++ dhd_os_rxfunlock(dhdp); ++#ifdef RXF_DEQUEUE_ON_BUSY ++ DHD_TRACE(("dhd_rxf_enqueue: pktbuf not consumed %p, store idx %d sent idx %d\n", ++ skb, store_idx, sent_idx)); ++ return BCME_BUSY; ++#else /* RXF_DEQUEUE_ON_BUSY */ ++ DHD_ERROR(("dhd_rxf_enqueue: pktbuf not consumed %p, store idx %d sent idx %d\n", ++ skb, store_idx, sent_idx)); ++ /* removed msleep here, should use wait_event_timeout if we ++ * want to give rx frame thread a chance to run ++ */ ++#if defined(WAIT_DEQUEUE) ++ OSL_SLEEP(1); ++#endif ++ return BCME_ERROR; ++#endif /* RXF_DEQUEUE_ON_BUSY */ ++ } ++ DHD_TRACE(("dhd_rxf_enqueue: Store SKB %p. idx %d -> %d\n", ++ skb, store_idx, (store_idx + 1) & (MAXSKBPEND - 1))); ++ dhdp->skbbuf[store_idx] = skb; ++ dhdp->store_idx = (store_idx + 1) & (MAXSKBPEND - 1); ++ dhd_os_rxfunlock(dhdp); ++ ++ return BCME_OK; ++} ++ ++static inline void* dhd_rxf_dequeue(dhd_pub_t *dhdp) ++{ ++ uint32 store_idx; ++ uint32 sent_idx; ++ void *skb; ++ ++ dhd_os_rxflock(dhdp); ++ ++ store_idx = dhdp->store_idx; ++ sent_idx = dhdp->sent_idx; ++ skb = dhdp->skbbuf[sent_idx]; ++ ++ if (skb == NULL) { ++ dhd_os_rxfunlock(dhdp); ++ DHD_ERROR(("dhd_rxf_dequeue: Dequeued packet is NULL, store idx %d sent idx %d\n", ++ store_idx, sent_idx)); ++ return NULL; ++ } ++ ++ dhdp->skbbuf[sent_idx] = NULL; ++ dhdp->sent_idx = (sent_idx + 1) & (MAXSKBPEND - 1); ++ ++ DHD_TRACE(("dhd_rxf_dequeue: netif_rx_ni(%p), sent idx %d\n", ++ skb, sent_idx)); ++ ++ dhd_os_rxfunlock(dhdp); ++ ++ return skb; ++} ++ ++int dhd_process_cid_mac(dhd_pub_t *dhdp, bool prepost) ++{ ++#ifndef CUSTOMER_HW10 ++ dhd_info_t *dhd = (dhd_info_t *)dhdp->info; ++#endif /* !CUSTOMER_HW10 */ ++ ++ if (prepost) { /* pre process */ ++ dhd_read_macaddr(dhd); ++ } else { /* post process */ ++ dhd_write_macaddr(&dhd->pub.mac); ++ } ++ ++ return 0; ++} ++ ++#if defined(PKT_FILTER_SUPPORT) && !defined(GAN_LITE_NAT_KEEPALIVE_FILTER) ++static bool ++_turn_on_arp_filter(dhd_pub_t *dhd, int op_mode) ++{ ++ bool _apply = FALSE; ++ /* In case of IBSS mode, apply arp pkt filter */ ++ if (op_mode & DHD_FLAG_IBSS_MODE) { ++ _apply = TRUE; ++ goto exit; ++ } ++ /* In case of P2P GO or GC, apply pkt filter to pass arp pkt to host */ ++ if ((dhd->arp_version == 1) && ++ (op_mode & (DHD_FLAG_P2P_GC_MODE | DHD_FLAG_P2P_GO_MODE))) { ++ _apply = TRUE; ++ goto exit; ++ } ++ ++exit: ++ return _apply; ++} ++#endif /* PKT_FILTER_SUPPORT && !GAN_LITE_NAT_KEEPALIVE_FILTER */ ++ ++#if defined(CUSTOM_PLATFORM_NV_TEGRA) ++#ifdef PKT_FILTER_SUPPORT ++void ++dhd_set_packet_filter_mode(struct net_device *dev, char *command) ++{ ++ dhd_info_t *dhdi = *(dhd_info_t **)netdev_priv(dev); ++ ++ dhdi->pub.pkt_filter_mode = bcm_strtoul(command, &command, 0); ++} ++ ++int ++dhd_set_packet_filter_ports(struct net_device *dev, char *command) ++{ ++ int i = 0, error = BCME_OK, count = 0, get_count = 0, action = 0; ++ uint16 portnum = 0, *ports = NULL, get_ports[WL_PKT_FILTER_PORTS_MAX]; ++ dhd_info_t *dhdi = *(dhd_info_t **)netdev_priv(dev); ++ dhd_pub_t *dhdp = &dhdi->pub; ++ char iovbuf[WLC_IOCTL_SMLEN]; ++ ++ /* get action */ ++ action = bcm_strtoul(command, &command, 0); ++ if (action > PKT_FILTER_PORTS_MAX) ++ return BCME_BADARG; ++ ++ if (action == PKT_FILTER_PORTS_LOOPBACK) { ++ /* echo the loopback value if port filter is supported else error */ ++ bcm_mkiovar("cap", NULL, 0, iovbuf, sizeof(iovbuf)); ++ error = dhd_wl_ioctl_cmd(dhdp, WLC_GET_VAR, iovbuf, sizeof(iovbuf), FALSE, 0); ++ if (error < 0) { ++ DHD_ERROR(("%s: Get Capability failed (error=%d)\n", __FUNCTION__, error)); ++ return error; ++ } ++ ++ if (strstr(iovbuf, "pktfltr2")) ++ return bcm_strtoul(command, &command, 0); ++ else { ++ DHD_ERROR(("%s: pktfltr2 is not supported\n", __FUNCTION__)); ++ return BCME_UNSUPPORTED; ++ } ++ } ++ ++ if (action == PKT_FILTER_PORTS_CLEAR) { ++ /* action 0 is clear all ports */ ++ dhdp->pkt_filter_ports_count = 0; ++ bzero(dhdp->pkt_filter_ports, sizeof(dhdp->pkt_filter_ports)); ++ } ++ else { ++ portnum = bcm_strtoul(command, &command, 0); ++ if (portnum == 0) { ++ /* no ports to add or remove */ ++ return BCME_BADARG; ++ } ++ ++ /* get configured ports */ ++ count = dhdp->pkt_filter_ports_count; ++ ports = dhdp->pkt_filter_ports; ++ ++ if (action == PKT_FILTER_PORTS_ADD) { ++ /* action 1 is add ports */ ++ ++ /* copy new ports */ ++ while ((portnum != 0) && (count < WL_PKT_FILTER_PORTS_MAX)) { ++ for (i = 0; i < count; i++) { ++ /* duplicate port */ ++ if (portnum == ports[i]) ++ break; ++ } ++ if (portnum != ports[i]) ++ ports[count++] = portnum; ++ portnum = bcm_strtoul(command, &command, 0); ++ } ++ } else if ((action == PKT_FILTER_PORTS_DEL) && (count > 0)) { ++ /* action 2 is remove ports */ ++ bcopy(dhdp->pkt_filter_ports, get_ports, count * sizeof(uint16)); ++ get_count = count; ++ ++ while (portnum != 0) { ++ count = 0; ++ for (i = 0; i < get_count; i++) { ++ if (portnum != get_ports[i]) ++ ports[count++] = get_ports[i]; ++ } ++ get_count = count; ++ bcopy(ports, get_ports, count * sizeof(uint16)); ++ portnum = bcm_strtoul(command, &command, 0); ++ } ++ } ++ dhdp->pkt_filter_ports_count = count; ++ } ++ return error; ++} ++ ++static void ++dhd_enable_packet_filter_ports(dhd_pub_t *dhd, bool enable) ++{ ++ int error = 0; ++ wl_pkt_filter_ports_t *portlist = NULL; ++ const uint pkt_filter_ports_buf_len = sizeof("pkt_filter_ports") ++ + WL_PKT_FILTER_PORTS_FIXED_LEN + (WL_PKT_FILTER_PORTS_MAX * sizeof(uint16)); ++ char pkt_filter_ports_buf[pkt_filter_ports_buf_len]; ++ char iovbuf[pkt_filter_ports_buf_len]; ++ ++ DHD_TRACE(("%s: enable %d, in_suspend %d, mode %d, port count %d\n", __FUNCTION__, ++ enable, dhd->in_suspend, dhd->pkt_filter_mode, ++ dhd->pkt_filter_ports_count)); ++ ++ bzero(pkt_filter_ports_buf, sizeof(pkt_filter_ports_buf)); ++ portlist = (wl_pkt_filter_ports_t*)pkt_filter_ports_buf; ++ portlist->version = WL_PKT_FILTER_PORTS_VERSION; ++ portlist->reserved = 0; ++ ++ if (enable) { ++ if (!(dhd->pkt_filter_mode & PKT_FILTER_MODE_PORTS_ONLY)) ++ return; ++ ++ /* enable port filter */ ++ dhd_master_mode |= PKT_FILTER_MODE_PORTS_ONLY; ++ if (dhd->pkt_filter_mode & PKT_FILTER_MODE_FORWARD_ON_MATCH) ++ /* whitelist mode: FORWARD_ON_MATCH */ ++ dhd_master_mode |= PKT_FILTER_MODE_FORWARD_ON_MATCH; ++ else ++ /* blacklist mode: DISCARD_ON_MATCH */ ++ dhd_master_mode &= ~PKT_FILTER_MODE_FORWARD_ON_MATCH; ++ ++ portlist->count = dhd->pkt_filter_ports_count; ++ bcopy(dhd->pkt_filter_ports, portlist->ports, ++ dhd->pkt_filter_ports_count * sizeof(uint16)); ++ } else { ++ /* disable port filter */ ++ portlist->count = 0; ++ dhd_master_mode &= ~PKT_FILTER_MODE_PORTS_ONLY; ++ dhd_master_mode |= PKT_FILTER_MODE_FORWARD_ON_MATCH; ++ } ++ ++ DHD_INFO(("%s: update: mode %d, port count %d\n", __FUNCTION__, dhd_master_mode, ++ portlist->count)); ++ ++ /* update ports */ ++ bcm_mkiovar("pkt_filter_ports", ++ (char*)portlist, ++ (WL_PKT_FILTER_PORTS_FIXED_LEN + (portlist->count * sizeof(uint16))), ++ iovbuf, sizeof(iovbuf)); ++ error = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); ++ if (error < 0) ++ DHD_ERROR(("%s: set pkt_filter_ports failed %d\n", __FUNCTION__, error)); ++ ++ /* update mode */ ++ bcm_mkiovar("pkt_filter_mode", (char*)&dhd_master_mode, ++ sizeof(dhd_master_mode), iovbuf, sizeof(iovbuf)); ++ error = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); ++ if (error < 0) ++ DHD_ERROR(("%s: set pkt_filter_mode failed %d\n", __FUNCTION__, error)); ++ ++ return; ++} ++#endif /* PKT_FILTER_SUPPORT */ ++#endif /* defined(CUSTOM_PLATFORM_NV_TEGRA) */ ++ ++void dhd_set_packet_filter(dhd_pub_t *dhd) ++{ ++#ifdef PKT_FILTER_SUPPORT ++ int i; ++ ++ DHD_TRACE(("%s: enter\n", __FUNCTION__)); ++ if (dhd_pkt_filter_enable) { ++ for (i = 0; i < dhd->pktfilter_count; i++) { ++ dhd_pktfilter_offload_set(dhd, dhd->pktfilter[i]); ++ } ++ } ++#endif /* PKT_FILTER_SUPPORT */ ++} ++ ++void dhd_enable_packet_filter(int value, dhd_pub_t *dhd) ++{ ++#ifdef PKT_FILTER_SUPPORT ++ int i; ++ ++ DHD_TRACE(("%s: enter, value = %d\n", __FUNCTION__, value)); ++ ++#if defined(CUSTOM_PLATFORM_NV_TEGRA) ++ dhd_enable_packet_filter_ports(dhd, value); ++#endif /* defined(CUSTOM_PLATFORM_NV_TEGRA) */ ++ ++ /* 1 - Enable packet filter, only allow unicast packet to send up */ ++ /* 0 - Disable packet filter */ ++ if (dhd_pkt_filter_enable && (!value || ++ (dhd_support_sta_mode(dhd) && !dhd->dhcp_in_progress))) ++ { ++ for (i = 0; i < dhd->pktfilter_count; i++) { ++#ifndef GAN_LITE_NAT_KEEPALIVE_FILTER ++ if (value && (i == DHD_ARP_FILTER_NUM) && ++ !_turn_on_arp_filter(dhd, dhd->op_mode)) { ++ DHD_TRACE(("Do not turn on ARP white list pkt filter:" ++ "val %d, cnt %d, op_mode 0x%x\n", ++ value, i, dhd->op_mode)); ++ continue; ++ } ++#endif /* !GAN_LITE_NAT_KEEPALIVE_FILTER */ ++ dhd_pktfilter_offload_enable(dhd, dhd->pktfilter[i], ++ value, dhd_master_mode); ++ } ++ } ++#endif /* PKT_FILTER_SUPPORT */ ++} ++ ++static int dhd_set_suspend(int value, dhd_pub_t *dhd) ++{ ++#ifndef SUPPORT_PM2_ONLY ++ int power_mode = PM_MAX; ++#endif /* SUPPORT_PM2_ONLY */ ++ /* wl_pkt_filter_enable_t enable_parm; */ ++ char iovbuf[32]; ++ int bcn_li_dtim = 0; /* Default bcn_li_dtim in resume mode is 0 */ ++ uint roamvar = dhd->conf->roam_off_suspend; ++ uint nd_ra_filter = 0; ++ int ret = 0; ++ ++ if (!dhd) ++ return -ENODEV; ++ ++ DHD_TRACE(("%s: enter, value = %d in_suspend=%d\n", ++ __FUNCTION__, value, dhd->in_suspend)); ++ ++ dhd_suspend_lock(dhd); ++ ++#ifdef CUSTOM_SET_CPUCORE ++ DHD_TRACE(("%s set cpucore(suspend%d)\n", __FUNCTION__, value)); ++ /* set specific cpucore */ ++ dhd_set_cpucore(dhd, TRUE); ++#endif /* CUSTOM_SET_CPUCORE */ ++#ifndef SUPPORT_PM2_ONLY ++ if (dhd->conf->pm >= 0) ++ power_mode = dhd->conf->pm; ++#endif /* SUPPORT_PM2_ONLY */ ++ if (dhd->up) { ++ if (value && dhd->in_suspend) { ++#ifdef PKT_FILTER_SUPPORT ++ dhd->early_suspended = 1; ++#endif ++ /* Kernel suspended */ ++ DHD_ERROR(("%s: force extra Suspend setting\n", __FUNCTION__)); ++ ++#ifndef SUPPORT_PM2_ONLY ++ dhd_wl_ioctl_cmd(dhd, WLC_SET_PM, (char *)&power_mode, ++ sizeof(power_mode), TRUE, 0); ++#endif /* SUPPORT_PM2_ONLY */ ++ ++ /* Enable packet filter, only allow unicast packet to send up */ ++ dhd_enable_packet_filter(1, dhd); ++ ++ /* If DTIM skip is set up as default, force it to wake ++ * each third DTIM for better power savings. Note that ++ * one side effect is a chance to miss BC/MC packet. ++ */ ++ bcn_li_dtim = dhd_get_suspend_bcn_li_dtim(dhd); ++ bcm_mkiovar("bcn_li_dtim", (char *)&bcn_li_dtim, ++ 4, iovbuf, sizeof(iovbuf)); ++ if (dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), ++ TRUE, 0) < 0) ++ DHD_ERROR(("%s: set dtim failed\n", __FUNCTION__)); ++ ++ /* Disable firmware roaming during suspend */ ++ bcm_mkiovar("roam_off", (char *)&roamvar, 4, iovbuf, sizeof(iovbuf)); ++ dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); ++ if (FW_SUPPORTED(dhd, ndoe)) { ++ /* enable IPv6 RA filter in firmware during suspend */ ++ nd_ra_filter = 1; ++ bcm_mkiovar("nd_ra_filter_enable", (char *)&nd_ra_filter, 4, ++ iovbuf, sizeof(iovbuf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, ++ sizeof(iovbuf), TRUE, 0)) < 0) ++ DHD_ERROR(("failed to set nd_ra_filter (%d)\n", ++ ret)); ++ } ++ } else { ++#ifdef PKT_FILTER_SUPPORT ++ dhd->early_suspended = 0; ++#endif ++ /* Kernel resumed */ ++ DHD_ERROR(("%s: Remove extra suspend setting\n", __FUNCTION__)); ++ ++#ifndef SUPPORT_PM2_ONLY ++ power_mode = PM_FAST; ++ dhd_wl_ioctl_cmd(dhd, WLC_SET_PM, (char *)&power_mode, ++ sizeof(power_mode), TRUE, 0); ++#endif /* SUPPORT_PM2_ONLY */ ++#ifdef PKT_FILTER_SUPPORT ++ /* disable pkt filter */ ++ dhd_enable_packet_filter(0, dhd); ++#endif /* PKT_FILTER_SUPPORT */ ++ ++ /* restore pre-suspend setting for dtim_skip */ ++ bcm_mkiovar("bcn_li_dtim", (char *)&bcn_li_dtim, ++ 4, iovbuf, sizeof(iovbuf)); ++ ++ dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); ++ roamvar = dhd_roam_disable; ++ bcm_mkiovar("roam_off", (char *)&roamvar, 4, iovbuf, sizeof(iovbuf)); ++ dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); ++ if (FW_SUPPORTED(dhd, ndoe)) { ++ /* disable IPv6 RA filter in firmware during suspend */ ++ nd_ra_filter = 0; ++ bcm_mkiovar("nd_ra_filter_enable", (char *)&nd_ra_filter, 4, ++ iovbuf, sizeof(iovbuf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, ++ sizeof(iovbuf), TRUE, 0)) < 0) ++ DHD_ERROR(("failed to set nd_ra_filter (%d)\n", ++ ret)); ++ } ++ } ++ } ++ dhd_suspend_unlock(dhd); ++ ++ return 0; ++} ++ ++static int dhd_suspend_resume_helper(struct dhd_info *dhd, int val, int force) ++{ ++ dhd_pub_t *dhdp = &dhd->pub; ++ int ret = 0; ++ ++ DHD_OS_WAKE_LOCK(dhdp); ++ DHD_PERIM_LOCK(dhdp); ++ ++ /* Set flag when early suspend was called */ ++ dhdp->in_suspend = val; ++ if ((force || !dhdp->suspend_disable_flag) && ++ dhd_support_sta_mode(dhdp)) ++ { ++ ret = dhd_set_suspend(val, dhdp); ++ } ++ ++ DHD_PERIM_UNLOCK(dhdp); ++ DHD_OS_WAKE_UNLOCK(dhdp); ++ return ret; ++} ++ ++#if defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND) ++static void dhd_early_suspend(struct early_suspend *h) ++{ ++ struct dhd_info *dhd = container_of(h, struct dhd_info, early_suspend); ++ DHD_TRACE_HW4(("%s: enter\n", __FUNCTION__)); ++ ++ if (dhd) ++ dhd_suspend_resume_helper(dhd, 1, 0); ++} ++ ++static void dhd_late_resume(struct early_suspend *h) ++{ ++ struct dhd_info *dhd = container_of(h, struct dhd_info, early_suspend); ++ DHD_TRACE_HW4(("%s: enter\n", __FUNCTION__)); ++ ++ if (dhd) ++ dhd_suspend_resume_helper(dhd, 0, 0); ++} ++#endif /* CONFIG_HAS_EARLYSUSPEND && DHD_USE_EARLYSUSPEND */ ++ ++/* ++ * Generalized timeout mechanism. Uses spin sleep with exponential back-off until ++ * the sleep time reaches one jiffy, then switches over to task delay. Usage: ++ * ++ * dhd_timeout_start(&tmo, usec); ++ * while (!dhd_timeout_expired(&tmo)) ++ * if (poll_something()) ++ * break; ++ * if (dhd_timeout_expired(&tmo)) ++ * fatal(); ++ */ ++ ++void ++dhd_timeout_start(dhd_timeout_t *tmo, uint usec) ++{ ++ tmo->limit = usec; ++ tmo->increment = 0; ++ tmo->elapsed = 0; ++ tmo->tick = jiffies_to_usecs(1); ++} ++ ++int ++dhd_timeout_expired(dhd_timeout_t *tmo) ++{ ++ /* Does nothing the first call */ ++ if (tmo->increment == 0) { ++ tmo->increment = 1; ++ return 0; ++ } ++ ++ if (tmo->elapsed >= tmo->limit) ++ return 1; ++ ++ /* Add the delay that's about to take place */ ++ tmo->elapsed += tmo->increment; ++ ++ if ((!CAN_SLEEP()) || tmo->increment < tmo->tick) { ++ OSL_DELAY(tmo->increment); ++ tmo->increment *= 2; ++ if (tmo->increment > tmo->tick) ++ tmo->increment = tmo->tick; ++ } else { ++ wait_queue_head_t delay_wait; ++ DECLARE_WAITQUEUE(wait, current); ++ init_waitqueue_head(&delay_wait); ++ add_wait_queue(&delay_wait, &wait); ++ set_current_state(TASK_INTERRUPTIBLE); ++ (void)schedule_timeout(1); ++ remove_wait_queue(&delay_wait, &wait); ++ set_current_state(TASK_RUNNING); ++ } ++ ++ return 0; ++} ++ ++int ++dhd_net2idx(dhd_info_t *dhd, struct net_device *net) ++{ ++ int i = 0; ++ ++ if (!dhd) { ++ DHD_ERROR(("%s : DHD_BAD_IF return\n", __FUNCTION__)); ++ return DHD_BAD_IF; ++ } ++ while (i < DHD_MAX_IFS) { ++ if (dhd->iflist[i] && dhd->iflist[i]->net && (dhd->iflist[i]->net == net)) ++ return i; ++ i++; ++ } ++ ++ return DHD_BAD_IF; ++} ++ ++struct net_device * dhd_idx2net(void *pub, int ifidx) ++{ ++ struct dhd_pub *dhd_pub = (struct dhd_pub *)pub; ++ struct dhd_info *dhd_info; ++ ++ if (!dhd_pub || ifidx < 0 || ifidx >= DHD_MAX_IFS) ++ return NULL; ++ dhd_info = dhd_pub->info; ++ if (dhd_info && dhd_info->iflist[ifidx]) ++ return dhd_info->iflist[ifidx]->net; ++ return NULL; ++} ++ ++int ++dhd_ifname2idx(dhd_info_t *dhd, char *name) ++{ ++ int i = DHD_MAX_IFS; ++ ++ ASSERT(dhd); ++ ++ if (name == NULL || *name == '\0') ++ return 0; ++ ++ while (--i > 0) ++ if (dhd->iflist[i] && !strncmp(dhd->iflist[i]->name, name, IFNAMSIZ)) ++ break; ++ ++ DHD_TRACE(("%s: return idx %d for \"%s\"\n", __FUNCTION__, i, name)); ++ ++ return i; /* default - the primary interface */ ++} ++ ++int ++dhd_ifidx2hostidx(dhd_info_t *dhd, int ifidx) ++{ ++ int i = DHD_MAX_IFS; ++ ++ ASSERT(dhd); ++ ++ while (--i > 0) ++ if (dhd->iflist[i] && (dhd->iflist[i]->idx == ifidx)) ++ break; ++ ++ DHD_TRACE(("%s: return hostidx %d for ifidx %d\n", __FUNCTION__, i, ifidx)); ++ ++ return i; /* default - the primary interface */ ++} ++ ++char * ++dhd_ifname(dhd_pub_t *dhdp, int ifidx) ++{ ++ dhd_info_t *dhd = (dhd_info_t *)dhdp->info; ++ ++ ASSERT(dhd); ++ ++ if (ifidx < 0 || ifidx >= DHD_MAX_IFS) { ++ DHD_ERROR(("%s: ifidx %d out of range\n", __FUNCTION__, ifidx)); ++ return ""; ++ } ++ ++ if (dhd->iflist[ifidx] == NULL) { ++ DHD_ERROR(("%s: null i/f %d\n", __FUNCTION__, ifidx)); ++ return ""; ++ } ++ ++ if (dhd->iflist[ifidx]->net) ++ return dhd->iflist[ifidx]->net->name; ++ ++ return ""; ++} ++ ++uint8 * ++dhd_bssidx2bssid(dhd_pub_t *dhdp, int idx) ++{ ++ int i; ++ dhd_info_t *dhd = (dhd_info_t *)dhdp; ++ ++ ASSERT(dhd); ++ for (i = 0; i < DHD_MAX_IFS; i++) ++ if (dhd->iflist[i] && dhd->iflist[i]->bssidx == idx) ++ return dhd->iflist[i]->mac_addr; ++ ++ return NULL; ++} ++ ++ ++static void ++_dhd_set_multicast_list(dhd_info_t *dhd, int ifidx) ++{ ++ struct net_device *dev; ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35) ++ struct netdev_hw_addr *ha; ++#else ++ struct dev_mc_list *mclist; ++#endif ++ uint32 allmulti, cnt; ++ ++ wl_ioctl_t ioc; ++ char *buf, *bufp; ++ uint buflen; ++ int ret; ++ ++ ASSERT(dhd && dhd->iflist[ifidx]); ++ dev = dhd->iflist[ifidx]->net; ++ if (!dev) ++ return; ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) ++ netif_addr_lock_bh(dev); ++#endif ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35) ++ cnt = netdev_mc_count(dev); ++#else ++ cnt = dev->mc_count; ++#endif /* LINUX_VERSION_CODE */ ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) ++ netif_addr_unlock_bh(dev); ++#endif ++ ++ /* Determine initial value of allmulti flag */ ++ allmulti = (dev->flags & IFF_ALLMULTI) ? TRUE : FALSE; ++ ++ /* Send down the multicast list first. */ ++ ++ ++ buflen = sizeof("mcast_list") + sizeof(cnt) + (cnt * ETHER_ADDR_LEN); ++ if (!(bufp = buf = MALLOC(dhd->pub.osh, buflen))) { ++ DHD_ERROR(("%s: out of memory for mcast_list, cnt %d\n", ++ dhd_ifname(&dhd->pub, ifidx), cnt)); ++ return; ++ } ++ ++ strncpy(bufp, "mcast_list", buflen - 1); ++ bufp[buflen - 1] = '\0'; ++ bufp += strlen("mcast_list") + 1; ++ ++ cnt = htol32(cnt); ++ memcpy(bufp, &cnt, sizeof(cnt)); ++ bufp += sizeof(cnt); ++ ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) ++ netif_addr_lock_bh(dev); ++#endif ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35) ++ netdev_for_each_mc_addr(ha, dev) { ++ if (!cnt) ++ break; ++ memcpy(bufp, ha->addr, ETHER_ADDR_LEN); ++ bufp += ETHER_ADDR_LEN; ++ cnt--; ++ } ++#else ++ for (mclist = dev->mc_list; (mclist && (cnt > 0)); ++ cnt--, mclist = mclist->next) { ++ memcpy(bufp, (void *)mclist->dmi_addr, ETHER_ADDR_LEN); ++ bufp += ETHER_ADDR_LEN; ++ } ++#endif /* LINUX_VERSION_CODE */ ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) ++ netif_addr_unlock_bh(dev); ++#endif ++ ++ memset(&ioc, 0, sizeof(ioc)); ++ ioc.cmd = WLC_SET_VAR; ++ ioc.buf = buf; ++ ioc.len = buflen; ++ ioc.set = TRUE; ++ ++ ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len); ++ if (ret < 0) { ++ DHD_ERROR(("%s: set mcast_list failed, cnt %d\n", ++ dhd_ifname(&dhd->pub, ifidx), cnt)); ++ allmulti = cnt ? TRUE : allmulti; ++ } ++ ++ MFREE(dhd->pub.osh, buf, buflen); ++ ++ /* Now send the allmulti setting. This is based on the setting in the ++ * net_device flags, but might be modified above to be turned on if we ++ * were trying to set some addresses and dongle rejected it... ++ */ ++ ++ buflen = sizeof("allmulti") + sizeof(allmulti); ++ if (!(buf = MALLOC(dhd->pub.osh, buflen))) { ++ DHD_ERROR(("%s: out of memory for allmulti\n", dhd_ifname(&dhd->pub, ifidx))); ++ return; ++ } ++ allmulti = htol32(allmulti); ++ ++ if (!bcm_mkiovar("allmulti", (void*)&allmulti, sizeof(allmulti), buf, buflen)) { ++ DHD_ERROR(("%s: mkiovar failed for allmulti, datalen %d buflen %u\n", ++ dhd_ifname(&dhd->pub, ifidx), (int)sizeof(allmulti), buflen)); ++ MFREE(dhd->pub.osh, buf, buflen); ++ return; ++ } ++ ++ ++ memset(&ioc, 0, sizeof(ioc)); ++ ioc.cmd = WLC_SET_VAR; ++ ioc.buf = buf; ++ ioc.len = buflen; ++ ioc.set = TRUE; ++ ++ ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len); ++ if (ret < 0) { ++ DHD_ERROR(("%s: set allmulti %d failed\n", ++ dhd_ifname(&dhd->pub, ifidx), ltoh32(allmulti))); ++ } ++ ++ MFREE(dhd->pub.osh, buf, buflen); ++ ++ /* Finally, pick up the PROMISC flag as well, like the NIC driver does */ ++ ++ allmulti = (dev->flags & IFF_PROMISC) ? TRUE : FALSE; ++ ++ allmulti = htol32(allmulti); ++ ++ memset(&ioc, 0, sizeof(ioc)); ++ ioc.cmd = WLC_SET_PROMISC; ++ ioc.buf = &allmulti; ++ ioc.len = sizeof(allmulti); ++ ioc.set = TRUE; ++ ++ ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len); ++ if (ret < 0) { ++ DHD_ERROR(("%s: set promisc %d failed\n", ++ dhd_ifname(&dhd->pub, ifidx), ltoh32(allmulti))); ++ } ++} ++ ++int ++_dhd_set_mac_address(dhd_info_t *dhd, int ifidx, uint8 *addr) ++{ ++ char buf[32]; ++ wl_ioctl_t ioc; ++ int ret; ++ ++ if (!bcm_mkiovar("cur_etheraddr", (char*)addr, ETHER_ADDR_LEN, buf, 32)) { ++ DHD_ERROR(("%s: mkiovar failed for cur_etheraddr\n", dhd_ifname(&dhd->pub, ifidx))); ++ return -1; ++ } ++ memset(&ioc, 0, sizeof(ioc)); ++ ioc.cmd = WLC_SET_VAR; ++ ioc.buf = buf; ++ ioc.len = 32; ++ ioc.set = TRUE; ++ ++ ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len); ++ if (ret < 0) { ++ DHD_ERROR(("%s: set cur_etheraddr failed\n", dhd_ifname(&dhd->pub, ifidx))); ++ } else { ++ memcpy(dhd->iflist[ifidx]->net->dev_addr, addr, ETHER_ADDR_LEN); ++ if (ifidx == 0) ++ memcpy(dhd->pub.mac.octet, addr, ETHER_ADDR_LEN); ++ } ++ ++ return ret; ++} ++ ++#ifdef SOFTAP ++extern struct net_device *ap_net_dev; ++extern tsk_ctl_t ap_eth_ctl; /* ap netdev heper thread ctl */ ++#endif ++ ++static void ++dhd_ifadd_event_handler(void *handle, void *event_info, u8 event) ++{ ++ dhd_info_t *dhd = handle; ++ dhd_if_event_t *if_event = event_info; ++ struct net_device *ndev; ++ int ifidx, bssidx; ++ int ret; ++#if 1 && (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)) ++ struct wireless_dev *vwdev, *primary_wdev; ++ struct net_device *primary_ndev; ++#endif /* OEM_ANDROID && (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)) */ ++ ++ if (event != DHD_WQ_WORK_IF_ADD) { ++ DHD_ERROR(("%s: unexpected event \n", __FUNCTION__)); ++ return; ++ } ++ ++ if (!dhd) { ++ DHD_ERROR(("%s: dhd info not available \n", __FUNCTION__)); ++ return; ++ } ++ ++ if (!if_event) { ++ DHD_ERROR(("%s: event data is null \n", __FUNCTION__)); ++ return; ++ } ++ ++ dhd_net_if_lock_local(dhd); ++ DHD_OS_WAKE_LOCK(&dhd->pub); ++ DHD_PERIM_LOCK(&dhd->pub); ++ ++ ifidx = if_event->event.ifidx; ++ bssidx = if_event->event.bssidx; ++ DHD_TRACE(("%s: registering if with ifidx %d\n", __FUNCTION__, ifidx)); ++ ++ ndev = dhd_allocate_if(&dhd->pub, ifidx, if_event->name, ++ if_event->mac, bssidx, TRUE); ++ if (!ndev) { ++ DHD_ERROR(("%s: net device alloc failed \n", __FUNCTION__)); ++ goto done; ++ } ++ ++#if 1 && (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)) ++ vwdev = kzalloc(sizeof(*vwdev), GFP_KERNEL); ++ if (unlikely(!vwdev)) { ++ WL_ERR(("Could not allocate wireless device\n")); ++ goto done; ++ } ++ primary_ndev = dhd->pub.info->iflist[0]->net; ++ primary_wdev = ndev_to_wdev(primary_ndev); ++ vwdev->wiphy = primary_wdev->wiphy; ++ vwdev->iftype = if_event->event.role; ++ vwdev->netdev = ndev; ++ ndev->ieee80211_ptr = vwdev; ++ SET_NETDEV_DEV(ndev, wiphy_dev(vwdev->wiphy)); ++ DHD_ERROR(("virtual interface(%s) is created\n", if_event->name)); ++#endif /* OEM_ANDROID && (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)) */ ++ ++ DHD_PERIM_UNLOCK(&dhd->pub); ++ ret = dhd_register_if(&dhd->pub, ifidx, TRUE); ++ DHD_PERIM_LOCK(&dhd->pub); ++ if (ret != BCME_OK) { ++ DHD_ERROR(("%s: dhd_register_if failed\n", __FUNCTION__)); ++ dhd_remove_if(&dhd->pub, ifidx, TRUE); ++ goto done; ++ } ++#ifdef PCIE_FULL_DONGLE ++ /* Turn on AP isolation in the firmware for interfaces operating in AP mode */ ++ if (FW_SUPPORTED((&dhd->pub), ap) && !(DHD_IF_ROLE_STA(if_event->event.role))) { ++ char iovbuf[WLC_IOCTL_SMLEN]; ++ uint32 var_int = 1; ++ ++ memset(iovbuf, 0, sizeof(iovbuf)); ++ bcm_mkiovar("ap_isolate", (char *)&var_int, 4, iovbuf, sizeof(iovbuf)); ++ ret = dhd_wl_ioctl_cmd(&dhd->pub, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, ifidx); ++ ++ if (ret != BCME_OK) { ++ DHD_ERROR(("%s: Failed to set ap_isolate to dongle\n", __FUNCTION__)); ++ dhd_remove_if(&dhd->pub, ifidx, TRUE); ++ } ++ } ++#endif /* PCIE_FULL_DONGLE */ ++done: ++ MFREE(dhd->pub.osh, if_event, sizeof(dhd_if_event_t)); ++ ++ DHD_PERIM_UNLOCK(&dhd->pub); ++ DHD_OS_WAKE_UNLOCK(&dhd->pub); ++ dhd_net_if_unlock_local(dhd); ++} ++ ++static void ++dhd_ifdel_event_handler(void *handle, void *event_info, u8 event) ++{ ++ dhd_info_t *dhd = handle; ++ int ifidx; ++ dhd_if_event_t *if_event = event_info; ++ ++ ++ if (event != DHD_WQ_WORK_IF_DEL) { ++ DHD_ERROR(("%s: unexpected event \n", __FUNCTION__)); ++ return; ++ } ++ ++ if (!dhd) { ++ DHD_ERROR(("%s: dhd info not available \n", __FUNCTION__)); ++ return; ++ } ++ ++ if (!if_event) { ++ DHD_ERROR(("%s: event data is null \n", __FUNCTION__)); ++ return; ++ } ++ ++ dhd_net_if_lock_local(dhd); ++ DHD_OS_WAKE_LOCK(&dhd->pub); ++ DHD_PERIM_LOCK(&dhd->pub); ++ ++ ifidx = if_event->event.ifidx; ++ DHD_TRACE(("Removing interface with idx %d\n", ifidx)); ++ ++ dhd_remove_if(&dhd->pub, ifidx, TRUE); ++ ++ MFREE(dhd->pub.osh, if_event, sizeof(dhd_if_event_t)); ++ ++ DHD_PERIM_UNLOCK(&dhd->pub); ++ DHD_OS_WAKE_UNLOCK(&dhd->pub); ++ dhd_net_if_unlock_local(dhd); ++} ++ ++static void ++dhd_set_mac_addr_handler(void *handle, void *event_info, u8 event) ++{ ++ dhd_info_t *dhd = handle; ++ dhd_if_t *ifp = event_info; ++ ++ if (event != DHD_WQ_WORK_SET_MAC) { ++ DHD_ERROR(("%s: unexpected event \n", __FUNCTION__)); ++ } ++ ++ if (!dhd) { ++ DHD_ERROR(("%s: dhd info not available \n", __FUNCTION__)); ++ return; ++ } ++ ++ dhd_net_if_lock_local(dhd); ++ DHD_OS_WAKE_LOCK(&dhd->pub); ++ DHD_PERIM_LOCK(&dhd->pub); ++ ++#ifdef SOFTAP ++ { ++ unsigned long flags; ++ bool in_ap = FALSE; ++ DHD_GENERAL_LOCK(&dhd->pub, flags); ++ in_ap = (ap_net_dev != NULL); ++ DHD_GENERAL_UNLOCK(&dhd->pub, flags); ++ ++ if (in_ap) { ++ DHD_ERROR(("attempt to set MAC for %s in AP Mode, blocked. \n", ++ ifp->net->name)); ++ goto done; ++ } ++ } ++#endif /* SOFTAP */ ++ ++ if (ifp == NULL || !dhd->pub.up) { ++ DHD_ERROR(("%s: interface info not available/down \n", __FUNCTION__)); ++ goto done; ++ } ++ ++ DHD_ERROR(("%s: MACID is overwritten\n", __FUNCTION__)); ++ ifp->set_macaddress = FALSE; ++ if (_dhd_set_mac_address(dhd, ifp->idx, ifp->mac_addr) == 0) ++ DHD_INFO(("%s: MACID is overwritten\n", __FUNCTION__)); ++ else ++ DHD_ERROR(("%s: _dhd_set_mac_address() failed\n", __FUNCTION__)); ++ ++done: ++ DHD_PERIM_UNLOCK(&dhd->pub); ++ DHD_OS_WAKE_UNLOCK(&dhd->pub); ++ dhd_net_if_unlock_local(dhd); ++} ++ ++static void ++dhd_set_mcast_list_handler(void *handle, void *event_info, u8 event) ++{ ++ dhd_info_t *dhd = handle; ++ dhd_if_t *ifp = event_info; ++ int ifidx; ++ ++ if (event != DHD_WQ_WORK_SET_MCAST_LIST) { ++ DHD_ERROR(("%s: unexpected event \n", __FUNCTION__)); ++ return; ++ } ++ ++ if (!dhd) { ++ DHD_ERROR(("%s: dhd info not available \n", __FUNCTION__)); ++ return; ++ } ++ ++ dhd_net_if_lock_local(dhd); ++ DHD_OS_WAKE_LOCK(&dhd->pub); ++ DHD_PERIM_LOCK(&dhd->pub); ++ ++#ifdef SOFTAP ++ { ++ bool in_ap = FALSE; ++ unsigned long flags; ++ DHD_GENERAL_LOCK(&dhd->pub, flags); ++ in_ap = (ap_net_dev != NULL); ++ DHD_GENERAL_UNLOCK(&dhd->pub, flags); ++ ++ if (in_ap) { ++ DHD_ERROR(("set MULTICAST list for %s in AP Mode, blocked. \n", ++ ifp->net->name)); ++ ifp->set_multicast = FALSE; ++ goto done; ++ } ++ } ++#endif /* SOFTAP */ ++ ++ if (ifp == NULL || !dhd->pub.up) { ++ DHD_ERROR(("%s: interface info not available/down \n", __FUNCTION__)); ++ goto done; ++ } ++ ++ ifidx = ifp->idx; ++ ++ ++ _dhd_set_multicast_list(dhd, ifidx); ++ DHD_INFO(("%s: set multicast list for if %d\n", __FUNCTION__, ifidx)); ++ ++done: ++ DHD_PERIM_UNLOCK(&dhd->pub); ++ DHD_OS_WAKE_UNLOCK(&dhd->pub); ++ dhd_net_if_unlock_local(dhd); ++} ++ ++static int ++dhd_set_mac_address(struct net_device *dev, void *addr) ++{ ++ int ret = 0; ++ ++ dhd_info_t *dhd = DHD_DEV_INFO(dev); ++ struct sockaddr *sa = (struct sockaddr *)addr; ++ int ifidx; ++ dhd_if_t *dhdif; ++ ++ ifidx = dhd_net2idx(dhd, dev); ++ if (ifidx == DHD_BAD_IF) ++ return -1; ++ ++ dhdif = dhd->iflist[ifidx]; ++ ++ dhd_net_if_lock_local(dhd); ++ memcpy(dhdif->mac_addr, sa->sa_data, ETHER_ADDR_LEN); ++ dhdif->set_macaddress = TRUE; ++ dhd_net_if_unlock_local(dhd); ++ dhd_deferred_schedule_work(dhd->dhd_deferred_wq, (void *)dhdif, DHD_WQ_WORK_SET_MAC, ++ dhd_set_mac_addr_handler, DHD_WORK_PRIORITY_LOW); ++ return ret; ++} ++ ++static void ++dhd_set_multicast_list(struct net_device *dev) ++{ ++ dhd_info_t *dhd = DHD_DEV_INFO(dev); ++ int ifidx; ++ ++ ifidx = dhd_net2idx(dhd, dev); ++ if (ifidx == DHD_BAD_IF) ++ return; ++ ++ dhd->iflist[ifidx]->set_multicast = TRUE; ++ dhd_deferred_schedule_work(dhd->dhd_deferred_wq, (void *)dhd->iflist[ifidx], ++ DHD_WQ_WORK_SET_MCAST_LIST, dhd_set_mcast_list_handler, DHD_WORK_PRIORITY_LOW); ++} ++ ++#ifdef PROP_TXSTATUS ++int ++dhd_os_wlfc_block(dhd_pub_t *pub) ++{ ++ dhd_info_t *di = (dhd_info_t *)(pub->info); ++ ASSERT(di != NULL); ++ spin_lock_bh(&di->wlfc_spinlock); ++ return 1; ++} ++ ++int ++dhd_os_wlfc_unblock(dhd_pub_t *pub) ++{ ++ dhd_info_t *di = (dhd_info_t *)(pub->info); ++ ++ ASSERT(di != NULL); ++ spin_unlock_bh(&di->wlfc_spinlock); ++ return 1; ++} ++ ++#endif /* PROP_TXSTATUS */ ++ ++#if defined(DHD_RX_DUMP) || defined(DHD_TX_DUMP) ++typedef struct { ++ uint16 type; ++ const char *str; ++} PKTTYPE_INFO; ++ ++static const PKTTYPE_INFO packet_type_info[] = ++{ ++ { ETHER_TYPE_IP, "IP" }, ++ { ETHER_TYPE_ARP, "ARP" }, ++ { ETHER_TYPE_BRCM, "BRCM" }, ++ { ETHER_TYPE_802_1X, "802.1X" }, ++ { ETHER_TYPE_WAI, "WAPI" }, ++ { 0, ""} ++}; ++ ++static const char *_get_packet_type_str(uint16 type) ++{ ++ int i; ++ int n = sizeof(packet_type_info)/sizeof(packet_type_info[1]) - 1; ++ ++ for (i = 0; i < n; i++) { ++ if (packet_type_info[i].type == type) ++ return packet_type_info[i].str; ++ } ++ ++ return packet_type_info[n].str; ++} ++#endif /* DHD_RX_DUMP || DHD_TX_DUMP */ ++ ++#if defined(DHD_TX_DUMP) ++void ++dhd_tx_dump(osl_t *osh, void *pkt) ++{ ++ uint8 *dump_data; ++ uint16 protocol; ++ struct ether_header *eh; ++ ++ dump_data = PKTDATA(osh, pkt); ++ eh = (struct ether_header *) dump_data; ++ protocol = ntoh16(eh->ether_type); ++ ++ DHD_ERROR(("TX DUMP - %s\n", _get_packet_type_str(protocol))); ++ ++ if (protocol == ETHER_TYPE_802_1X) { ++ DHD_ERROR(("ETHER_TYPE_802_1X [TX]: ver %d, type %d, replay %d\n", ++ dump_data[14], dump_data[15], dump_data[30])); ++ } ++ ++#if defined(DHD_TX_FULL_DUMP) ++ { ++ int i; ++ uint datalen; ++ datalen = PKTLEN(osh, pkt); ++ ++ for (i = 0; i < datalen; i++) { ++ DHD_ERROR(("%02X ", dump_data[i])); ++ if ((i & 15) == 15) ++ printk("\n"); ++ } ++ DHD_ERROR(("\n")); ++ } ++#endif /* DHD_TX_FULL_DUMP */ ++} ++#endif /* DHD_TX_DUMP */ ++ ++int BCMFASTPATH ++dhd_sendpkt(dhd_pub_t *dhdp, int ifidx, void *pktbuf) ++{ ++ int ret = BCME_OK; ++ dhd_info_t *dhd = (dhd_info_t *)(dhdp->info); ++ struct ether_header *eh = NULL; ++ ++ /* Reject if down */ ++ if (!dhdp->up || (dhdp->busstate == DHD_BUS_DOWN)) { ++ /* free the packet here since the caller won't */ ++ PKTFREE(dhdp->osh, pktbuf, TRUE); ++ return -ENODEV; ++ } ++ ++#ifdef PCIE_FULL_DONGLE ++ if (dhdp->busstate == DHD_BUS_SUSPEND) { ++ DHD_ERROR(("%s : pcie is still in suspend state!!\n", __FUNCTION__)); ++ PKTFREE(dhdp->osh, pktbuf, TRUE); ++ return -EBUSY; ++ } ++#endif /* PCIE_FULL_DONGLE */ ++ ++#ifdef DHD_UNICAST_DHCP ++ /* if dhcp_unicast is enabled, we need to convert the */ ++ /* broadcast DHCP ACK/REPLY packets to Unicast. */ ++ if (dhdp->dhcp_unicast) { ++ dhd_convert_dhcp_broadcast_ack_to_unicast(dhdp, pktbuf, ifidx); ++ } ++#endif /* DHD_UNICAST_DHCP */ ++ /* Update multicast statistic */ ++ if (PKTLEN(dhdp->osh, pktbuf) >= ETHER_HDR_LEN) { ++ uint8 *pktdata = (uint8 *)PKTDATA(dhdp->osh, pktbuf); ++ eh = (struct ether_header *)pktdata; ++ ++ if (ETHER_ISMULTI(eh->ether_dhost)) ++ dhdp->tx_multicast++; ++ if (ntoh16(eh->ether_type) == ETHER_TYPE_802_1X) ++ atomic_inc(&dhd->pend_8021x_cnt); ++ } else { ++ PKTFREE(dhd->pub.osh, pktbuf, TRUE); ++ return BCME_ERROR; ++ } ++ ++ /* Look into the packet and update the packet priority */ ++#ifndef PKTPRIO_OVERRIDE ++ if (PKTPRIO(pktbuf) == 0) ++#endif ++ pktsetprio(pktbuf, FALSE); ++ ++ ++#if defined(PCIE_FULL_DONGLE) && !defined(PCIE_TX_DEFERRAL) ++ /* ++ * Lkup the per interface hash table, for a matching flowring. If one is not ++ * available, allocate a unique flowid and add a flowring entry. ++ * The found or newly created flowid is placed into the pktbuf's tag. ++ */ ++ ret = dhd_flowid_update(dhdp, ifidx, dhdp->flow_prio_map[(PKTPRIO(pktbuf))], pktbuf); ++ if (ret != BCME_OK) { ++ PKTCFREE(dhd->pub.osh, pktbuf, TRUE); ++ return ret; ++ } ++#endif ++#if defined(DHD_TX_DUMP) ++ dhd_tx_dump(dhdp->osh, pktbuf); ++#endif ++ ++ /* terence 20150901: Micky add to ajust the 802.1X priority */ ++ /* Set the 802.1X packet with the highest priority 7 */ ++ if (dhdp->conf->pktprio8021x >= 0) ++ pktset8021xprio(pktbuf, dhdp->conf->pktprio8021x); ++ ++#ifdef PROP_TXSTATUS ++ if (dhd_wlfc_is_supported(dhdp)) { ++ /* store the interface ID */ ++ DHD_PKTTAG_SETIF(PKTTAG(pktbuf), ifidx); ++ ++ /* store destination MAC in the tag as well */ ++ DHD_PKTTAG_SETDSTN(PKTTAG(pktbuf), eh->ether_dhost); ++ ++ /* decide which FIFO this packet belongs to */ ++ if (ETHER_ISMULTI(eh->ether_dhost)) ++ /* one additional queue index (highest AC + 1) is used for bc/mc queue */ ++ DHD_PKTTAG_SETFIFO(PKTTAG(pktbuf), AC_COUNT); ++ else ++ DHD_PKTTAG_SETFIFO(PKTTAG(pktbuf), WME_PRIO2AC(PKTPRIO(pktbuf))); ++ } else ++#endif /* PROP_TXSTATUS */ ++ /* If the protocol uses a data header, apply it */ ++ dhd_prot_hdrpush(dhdp, ifidx, pktbuf); ++ ++ /* Use bus module to send data frame */ ++#ifdef WLMEDIA_HTSF ++ dhd_htsf_addtxts(dhdp, pktbuf); ++#endif ++ ++#ifdef PROP_TXSTATUS ++ { ++ if (dhd_wlfc_commit_packets(dhdp, (f_commitpkt_t)dhd_bus_txdata, ++ dhdp->bus, pktbuf, TRUE) == WLFC_UNSUPPORTED) { ++ /* non-proptxstatus way */ ++#ifdef BCMPCIE ++ ret = dhd_bus_txdata(dhdp->bus, pktbuf, (uint8)ifidx); ++#else ++ ret = dhd_bus_txdata(dhdp->bus, pktbuf); ++#endif /* BCMPCIE */ ++ } ++ } ++#else ++#ifdef BCMPCIE ++ ret = dhd_bus_txdata(dhdp->bus, pktbuf, (uint8)ifidx); ++#else ++ ret = dhd_bus_txdata(dhdp->bus, pktbuf); ++#endif /* BCMPCIE */ ++#endif /* PROP_TXSTATUS */ ++ ++ return ret; ++} ++ ++int BCMFASTPATH ++dhd_start_xmit(struct sk_buff *skb, struct net_device *net) ++{ ++ int ret; ++ uint datalen; ++ void *pktbuf; ++ dhd_info_t *dhd = DHD_DEV_INFO(net); ++ dhd_if_t *ifp = NULL; ++ int ifidx; ++#ifdef WLMEDIA_HTSF ++ uint8 htsfdlystat_sz = dhd->pub.htsfdlystat_sz; ++#else ++ uint8 htsfdlystat_sz = 0; ++#endif ++#ifdef DHD_WMF ++ struct ether_header *eh; ++ uint8 *iph; ++#endif /* DHD_WMF */ ++ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++ DHD_OS_WAKE_LOCK(&dhd->pub); ++ DHD_PERIM_LOCK_TRY(DHD_FWDER_UNIT(dhd), TRUE); ++ ++ /* Reject if down */ ++ if (dhd->pub.busstate == DHD_BUS_DOWN || dhd->pub.hang_was_sent) { ++ DHD_ERROR(("%s: xmit rejected pub.up=%d busstate=%d \n", ++ __FUNCTION__, dhd->pub.up, dhd->pub.busstate)); ++ netif_stop_queue(net); ++ /* Send Event when bus down detected during data session */ ++ if (dhd->pub.up) { ++ DHD_ERROR(("%s: Event HANG sent up\n", __FUNCTION__)); ++ net_os_send_hang_message(net); ++ } ++ DHD_PERIM_UNLOCK_TRY(DHD_FWDER_UNIT(dhd), TRUE); ++ DHD_OS_WAKE_UNLOCK(&dhd->pub); ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)) ++ return -ENODEV; ++#else ++ return NETDEV_TX_BUSY; ++#endif ++ } ++ ++ ifp = DHD_DEV_IFP(net); ++ ifidx = DHD_DEV_IFIDX(net); ++ ++ ASSERT(ifidx == dhd_net2idx(dhd, net)); ++ ASSERT((ifp != NULL) && (ifp == dhd->iflist[ifidx])); ++ ++ if (ifidx == DHD_BAD_IF) { ++ DHD_ERROR(("%s: bad ifidx %d\n", __FUNCTION__, ifidx)); ++ netif_stop_queue(net); ++ DHD_PERIM_UNLOCK_TRY(DHD_FWDER_UNIT(dhd), TRUE); ++ DHD_OS_WAKE_UNLOCK(&dhd->pub); ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)) ++ return -ENODEV; ++#else ++ return NETDEV_TX_BUSY; ++#endif ++ } ++ ++ /* re-align socket buffer if "skb->data" is odd address */ ++ if (((unsigned long)(skb->data)) & 0x1) { ++ unsigned char *data = skb->data; ++ uint32 length = skb->len; ++ PKTPUSH(dhd->pub.osh, skb, 1); ++ memmove(skb->data, data, length); ++ PKTSETLEN(dhd->pub.osh, skb, length); ++ } ++ ++ datalen = PKTLEN(dhd->pub.osh, skb); ++ ++ /* Make sure there's enough room for any header */ ++ ++ if (skb_headroom(skb) < dhd->pub.hdrlen + htsfdlystat_sz) { ++ struct sk_buff *skb2; ++ ++ DHD_INFO(("%s: insufficient headroom\n", ++ dhd_ifname(&dhd->pub, ifidx))); ++ dhd->pub.tx_realloc++; ++ ++ skb2 = skb_realloc_headroom(skb, dhd->pub.hdrlen + htsfdlystat_sz); ++ ++ dev_kfree_skb(skb); ++ if ((skb = skb2) == NULL) { ++ DHD_ERROR(("%s: skb_realloc_headroom failed\n", ++ dhd_ifname(&dhd->pub, ifidx))); ++ ret = -ENOMEM; ++ goto done; ++ } ++ } ++ ++ /* Convert to packet */ ++ if (!(pktbuf = PKTFRMNATIVE(dhd->pub.osh, skb))) { ++ DHD_ERROR(("%s: PKTFRMNATIVE failed\n", ++ dhd_ifname(&dhd->pub, ifidx))); ++ dev_kfree_skb_any(skb); ++ ret = -ENOMEM; ++ goto done; ++ } ++#ifdef WLMEDIA_HTSF ++ if (htsfdlystat_sz && PKTLEN(dhd->pub.osh, pktbuf) >= ETHER_ADDR_LEN) { ++ uint8 *pktdata = (uint8 *)PKTDATA(dhd->pub.osh, pktbuf); ++ struct ether_header *eh = (struct ether_header *)pktdata; ++ ++ if (!ETHER_ISMULTI(eh->ether_dhost) && ++ (ntoh16(eh->ether_type) == ETHER_TYPE_IP)) { ++ eh->ether_type = hton16(ETHER_TYPE_BRCM_PKTDLYSTATS); ++ } ++ } ++#endif ++#ifdef DHD_WMF ++ eh = (struct ether_header *)PKTDATA(dhd->pub.osh, pktbuf); ++ iph = (uint8 *)eh + ETHER_HDR_LEN; ++ ++ /* WMF processing for multicast packets ++ * Only IPv4 packets are handled ++ */ ++ if (ifp->wmf.wmf_enable && (ntoh16(eh->ether_type) == ETHER_TYPE_IP) && ++ (IP_VER(iph) == IP_VER_4) && (ETHER_ISMULTI(eh->ether_dhost) || ++ ((IPV4_PROT(iph) == IP_PROT_IGMP) && dhd->pub.wmf_ucast_igmp))) { ++#if defined(DHD_IGMP_UCQUERY) || defined(DHD_UCAST_UPNP) ++ void *sdu_clone; ++ bool ucast_convert = FALSE; ++#ifdef DHD_UCAST_UPNP ++ uint32 dest_ip; ++ ++ dest_ip = ntoh32(*((uint32 *)(iph + IPV4_DEST_IP_OFFSET))); ++ ucast_convert = dhd->pub.wmf_ucast_upnp && MCAST_ADDR_UPNP_SSDP(dest_ip); ++#endif /* DHD_UCAST_UPNP */ ++#ifdef DHD_IGMP_UCQUERY ++ ucast_convert |= dhd->pub.wmf_ucast_igmp_query && ++ (IPV4_PROT(iph) == IP_PROT_IGMP) && ++ (*(iph + IPV4_HLEN(iph)) == IGMPV2_HOST_MEMBERSHIP_QUERY); ++#endif /* DHD_IGMP_UCQUERY */ ++ if (ucast_convert) { ++ dhd_sta_t *sta; ++ unsigned long flags; ++ ++ DHD_IF_STA_LIST_LOCK(ifp, flags); ++ ++ /* Convert upnp/igmp query to unicast for each assoc STA */ ++ list_for_each_entry(sta, &ifp->sta_list, list) { ++ if ((sdu_clone = PKTDUP(dhd->pub.osh, pktbuf)) == NULL) { ++ DHD_IF_STA_LIST_UNLOCK(ifp, flags); ++ DHD_PERIM_UNLOCK_TRY(DHD_FWDER_UNIT(dhd), TRUE); ++ DHD_OS_WAKE_UNLOCK(&dhd->pub); ++ return (WMF_NOP); ++ } ++ dhd_wmf_forward(ifp->wmf.wmfh, sdu_clone, 0, sta, 1); ++ } ++ ++ DHD_IF_STA_LIST_UNLOCK(ifp, flags); ++ DHD_PERIM_UNLOCK_TRY(DHD_FWDER_UNIT(dhd), TRUE); ++ DHD_OS_WAKE_UNLOCK(&dhd->pub); ++ ++ PKTFREE(dhd->pub.osh, pktbuf, TRUE); ++ return NETDEV_TX_OK; ++ } else ++#endif /* defined(DHD_IGMP_UCQUERY) || defined(DHD_UCAST_UPNP) */ ++ { ++ /* There will be no STA info if the packet is coming from LAN host ++ * Pass as NULL ++ */ ++ ret = dhd_wmf_packets_handle(&dhd->pub, pktbuf, NULL, ifidx, 0); ++ switch (ret) { ++ case WMF_TAKEN: ++ case WMF_DROP: ++ /* Either taken by WMF or we should drop it. ++ * Exiting send path ++ */ ++ DHD_PERIM_UNLOCK_TRY(DHD_FWDER_UNIT(dhd), TRUE); ++ DHD_OS_WAKE_UNLOCK(&dhd->pub); ++ return NETDEV_TX_OK; ++ default: ++ /* Continue the transmit path */ ++ break; ++ } ++ } ++ } ++#endif /* DHD_WMF */ ++ ++#ifdef DHDTCPACK_SUPPRESS ++ if (dhd->pub.tcpack_sup_mode == TCPACK_SUP_HOLD) { ++ /* If this packet has been hold or got freed, just return */ ++ if (dhd_tcpack_hold(&dhd->pub, pktbuf, ifidx)) ++ return 0; ++ } else { ++ /* If this packet has replaced another packet and got freed, just return */ ++ if (dhd_tcpack_suppress(&dhd->pub, pktbuf)) ++ return 0; ++ } ++#endif /* DHDTCPACK_SUPPRESS */ ++ ++ ret = dhd_sendpkt(&dhd->pub, ifidx, pktbuf); ++ ++done: ++ if (ret) { ++ ifp->stats.tx_dropped++; ++ dhd->pub.tx_dropped++; ++ } ++ else { ++ ++#ifdef PROP_TXSTATUS ++ /* tx_packets counter can counted only when wlfc is disabled */ ++ if (!dhd_wlfc_is_supported(&dhd->pub)) ++#endif ++ { ++ dhd->pub.tx_packets++; ++ ifp->stats.tx_packets++; ++ ifp->stats.tx_bytes += datalen; ++ } ++ } ++ ++ DHD_PERIM_UNLOCK_TRY(DHD_FWDER_UNIT(dhd), TRUE); ++ DHD_OS_WAKE_UNLOCK(&dhd->pub); ++ ++ /* Return ok: we always eat the packet */ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)) ++ return 0; ++#else ++ return NETDEV_TX_OK; ++#endif ++} ++ ++ ++void ++dhd_txflowcontrol(dhd_pub_t *dhdp, int ifidx, bool state) ++{ ++ struct net_device *net; ++ dhd_info_t *dhd = dhdp->info; ++ int i; ++ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++ ASSERT(dhd); ++ ++ if (ifidx == ALL_INTERFACES) { ++ /* Flow control on all active interfaces */ ++ dhdp->txoff = state; ++ for (i = 0; i < DHD_MAX_IFS; i++) { ++ if (dhd->iflist[i]) { ++ net = dhd->iflist[i]->net; ++ if (state == ON) ++ netif_stop_queue(net); ++ else ++ netif_wake_queue(net); ++ } ++ } ++ } ++ else { ++ if (dhd->iflist[ifidx]) { ++ net = dhd->iflist[ifidx]->net; ++ if (state == ON) ++ netif_stop_queue(net); ++ else ++ netif_wake_queue(net); ++ } ++ } ++} ++ ++ ++#ifdef DHD_WMF ++bool ++dhd_is_rxthread_enabled(dhd_pub_t *dhdp) ++{ ++ dhd_info_t *dhd = dhdp->info; ++ ++ return dhd->rxthread_enabled; ++} ++#endif /* DHD_WMF */ ++ ++void ++dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt, uint8 chan) ++{ ++ dhd_info_t *dhd = (dhd_info_t *)dhdp->info; ++ struct sk_buff *skb; ++ uchar *eth; ++ uint len; ++ void *data, *pnext = NULL; ++ int i; ++ dhd_if_t *ifp; ++ wl_event_msg_t event; ++ int tout_rx = 0; ++ int tout_ctrl = 0; ++ void *skbhead = NULL; ++ void *skbprev = NULL; ++#if defined(DHD_RX_DUMP) || defined(DHD_8021X_DUMP) ++ char *dump_data; ++ uint16 protocol; ++#endif /* DHD_RX_DUMP || DHD_8021X_DUMP */ ++ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++ for (i = 0; pktbuf && i < numpkt; i++, pktbuf = pnext) { ++ struct ether_header *eh; ++#ifdef WLBTAMP ++ struct dot11_llc_snap_header *lsh; ++#endif ++ ++ pnext = PKTNEXT(dhdp->osh, pktbuf); ++ PKTSETNEXT(dhdp->osh, pktbuf, NULL); ++ ++ ifp = dhd->iflist[ifidx]; ++ if (ifp == NULL) { ++ DHD_ERROR(("%s: ifp is NULL. drop packet\n", ++ __FUNCTION__)); ++ PKTCFREE(dhdp->osh, pktbuf, FALSE); ++ continue; ++ } ++ ++ eh = (struct ether_header *)PKTDATA(dhdp->osh, pktbuf); ++ ++ /* Dropping only data packets before registering net device to avoid kernel panic */ ++#ifndef PROP_TXSTATUS_VSDB ++ if ((!ifp->net || ifp->net->reg_state != NETREG_REGISTERED) && ++ (ntoh16(eh->ether_type) != ETHER_TYPE_BRCM)) ++#else ++ if ((!ifp->net || ifp->net->reg_state != NETREG_REGISTERED || !dhd->pub.up) && ++ (ntoh16(eh->ether_type) != ETHER_TYPE_BRCM)) ++#endif /* PROP_TXSTATUS_VSDB */ ++ { ++ DHD_ERROR(("%s: net device is NOT registered yet. drop packet\n", ++ __FUNCTION__)); ++ PKTCFREE(dhdp->osh, pktbuf, FALSE); ++ continue; ++ } ++ ++#ifdef WLBTAMP ++ lsh = (struct dot11_llc_snap_header *)&eh[1]; ++ ++ if ((ntoh16(eh->ether_type) < ETHER_TYPE_MIN) && ++ (PKTLEN(dhdp->osh, pktbuf) >= RFC1042_HDR_LEN) && ++ bcmp(lsh, BT_SIG_SNAP_MPROT, DOT11_LLC_SNAP_HDR_LEN - 2) == 0 && ++ lsh->type == HTON16(BTA_PROT_L2CAP)) { ++ amp_hci_ACL_data_t *ACL_data = (amp_hci_ACL_data_t *) ++ ((uint8 *)eh + RFC1042_HDR_LEN); ++ ACL_data = NULL; ++ } ++#endif /* WLBTAMP */ ++ ++#ifdef PROP_TXSTATUS ++ if (dhd_wlfc_is_header_only_pkt(dhdp, pktbuf)) { ++ /* WLFC may send header only packet when ++ there is an urgent message but no packet to ++ piggy-back on ++ */ ++ PKTCFREE(dhdp->osh, pktbuf, FALSE); ++ continue; ++ } ++#endif ++#ifdef DHD_L2_FILTER ++ /* If block_ping is enabled drop the ping packet */ ++ if (dhdp->block_ping) { ++ if (dhd_l2_filter_block_ping(dhdp, pktbuf, ifidx) == BCME_OK) { ++ PKTFREE(dhdp->osh, pktbuf, FALSE); ++ continue; ++ } ++ } ++#endif ++#ifdef DHD_WMF ++ /* WMF processing for multicast packets */ ++ if (ifp->wmf.wmf_enable && (ETHER_ISMULTI(eh->ether_dhost))) { ++ dhd_sta_t *sta; ++ int ret; ++ ++ sta = dhd_find_sta(dhdp, ifidx, (void *)eh->ether_shost); ++ ret = dhd_wmf_packets_handle(dhdp, pktbuf, sta, ifidx, 1); ++ switch (ret) { ++ case WMF_TAKEN: ++ /* The packet is taken by WMF. Continue to next iteration */ ++ continue; ++ case WMF_DROP: ++ /* Packet DROP decision by WMF. Toss it */ ++ DHD_ERROR(("%s: WMF decides to drop packet\n", ++ __FUNCTION__)); ++ PKTCFREE(dhdp->osh, pktbuf, FALSE); ++ continue; ++ default: ++ /* Continue the transmit path */ ++ break; ++ } ++ } ++#endif /* DHD_WMF */ ++#ifdef DHDTCPACK_SUPPRESS ++ dhd_tcpdata_info_get(dhdp, pktbuf); ++#endif ++ skb = PKTTONATIVE(dhdp->osh, pktbuf); ++ ++ ifp = dhd->iflist[ifidx]; ++ if (ifp == NULL) ++ ifp = dhd->iflist[0]; ++ ++ ASSERT(ifp); ++ skb->dev = ifp->net; ++ ++#ifdef PCIE_FULL_DONGLE ++ if ((DHD_IF_ROLE_AP(dhdp, ifidx) || DHD_IF_ROLE_P2PGO(dhdp, ifidx)) && ++ (!ifp->ap_isolate)) { ++ eh = (struct ether_header *)PKTDATA(dhdp->osh, pktbuf); ++ if (ETHER_ISUCAST(eh->ether_dhost)) { ++ if (dhd_find_sta(dhdp, ifidx, (void *)eh->ether_dhost)) { ++ dhd_sendpkt(dhdp, ifidx, pktbuf); ++ continue; ++ } ++ } else { ++ void *npktbuf = PKTDUP(dhdp->osh, pktbuf); ++ dhd_sendpkt(dhdp, ifidx, npktbuf); ++ } ++ } ++#endif /* PCIE_FULL_DONGLE */ ++ ++ /* Get the protocol, maintain skb around eth_type_trans() ++ * The main reason for this hack is for the limitation of ++ * Linux 2.4 where 'eth_type_trans' uses the 'net->hard_header_len' ++ * to perform skb_pull inside vs ETH_HLEN. Since to avoid ++ * coping of the packet coming from the network stack to add ++ * BDC, Hardware header etc, during network interface registration ++ * we set the 'net->hard_header_len' to ETH_HLEN + extra space required ++ * for BDC, Hardware header etc. and not just the ETH_HLEN ++ */ ++ eth = skb->data; ++ len = skb->len; ++ ++#if defined(DHD_RX_DUMP) || defined(DHD_8021X_DUMP) ++ dump_data = skb->data; ++ protocol = (dump_data[12] << 8) | dump_data[13]; ++ ++ if (protocol == ETHER_TYPE_802_1X) { ++ DHD_ERROR(("ETHER_TYPE_802_1X [RX]: " ++ "ver %d, type %d, replay %d\n", ++ dump_data[14], dump_data[15], ++ dump_data[30])); ++ } ++#endif /* DHD_RX_DUMP || DHD_8021X_DUMP */ ++#if defined(DHD_RX_DUMP) ++ DHD_ERROR(("RX DUMP - %s\n", _get_packet_type_str(protocol))); ++ if (protocol != ETHER_TYPE_BRCM) { ++ if (dump_data[0] == 0xFF) { ++ DHD_ERROR(("%s: BROADCAST\n", __FUNCTION__)); ++ ++ if ((dump_data[12] == 8) && ++ (dump_data[13] == 6)) { ++ DHD_ERROR(("%s: ARP %d\n", ++ __FUNCTION__, dump_data[0x15])); ++ } ++ } else if (dump_data[0] & 1) { ++ DHD_ERROR(("%s: MULTICAST: " MACDBG "\n", ++ __FUNCTION__, MAC2STRDBG(dump_data))); ++ } ++#ifdef DHD_RX_FULL_DUMP ++ { ++ int k; ++ for (k = 0; k < skb->len; k++) { ++ DHD_ERROR(("%02X ", dump_data[k])); ++ if ((k & 15) == 15) ++ DHD_ERROR(("\n")); ++ } ++ DHD_ERROR(("\n")); ++ } ++#endif /* DHD_RX_FULL_DUMP */ ++ } ++#endif /* DHD_RX_DUMP */ ++ ++ skb->protocol = eth_type_trans(skb, skb->dev); ++ ++ if (skb->pkt_type == PACKET_MULTICAST) { ++ dhd->pub.rx_multicast++; ++ ifp->stats.multicast++; ++ } ++ ++ skb->data = eth; ++ skb->len = len; ++ ++#ifdef WLMEDIA_HTSF ++ dhd_htsf_addrxts(dhdp, pktbuf); ++#endif ++ /* Strip header, count, deliver upward */ ++ skb_pull(skb, ETH_HLEN); ++ ++ /* Process special event packets and then discard them */ ++ memset(&event, 0, sizeof(event)); ++ if (ntoh16(skb->protocol) == ETHER_TYPE_BRCM) { ++ dhd_wl_host_event(dhd, &ifidx, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) ++ skb_mac_header(skb), ++#else ++ skb->mac.raw, ++#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) */ ++ &event, ++ &data); ++ ++ wl_event_to_host_order(&event); ++ if (!tout_ctrl) ++ tout_ctrl = DHD_PACKET_TIMEOUT_MS; ++#ifdef WLBTAMP ++ if (event.event_type == WLC_E_BTA_HCI_EVENT) { ++ dhd_bta_doevt(dhdp, data, event.datalen); ++ } ++#endif /* WLBTAMP */ ++ ++#if defined(PNO_SUPPORT) ++ if (event.event_type == WLC_E_PFN_NET_FOUND) { ++ /* enforce custom wake lock to garantee that Kernel not suspended */ ++ tout_ctrl = CUSTOM_PNO_EVENT_LOCK_xTIME * DHD_PACKET_TIMEOUT_MS; ++ } ++#endif /* PNO_SUPPORT */ ++ ++#ifdef DHD_DONOT_FORWARD_BCMEVENT_AS_NETWORK_PKT ++ PKTFREE(dhdp->osh, pktbuf, FALSE); ++ continue; ++#endif /* DHD_DONOT_FORWARD_BCMEVENT_AS_NETWORK_PKT */ ++ } else { ++ tout_rx = DHD_PACKET_TIMEOUT_MS; ++ ++#ifdef PROP_TXSTATUS ++ dhd_wlfc_save_rxpath_ac_time(dhdp, (uint8)PKTPRIO(skb)); ++#endif /* PROP_TXSTATUS */ ++ } ++ ++ ASSERT(ifidx < DHD_MAX_IFS && dhd->iflist[ifidx]); ++ ifp = dhd->iflist[ifidx]; ++ ++ if (ifp->net) ++ ifp->net->last_rx = jiffies; ++ ++ if (ntoh16(skb->protocol) != ETHER_TYPE_BRCM) { ++ dhdp->dstats.rx_bytes += skb->len; ++ dhdp->rx_packets++; /* Local count */ ++ ifp->stats.rx_bytes += skb->len; ++ ifp->stats.rx_packets++; ++ } ++#if defined(DHD_TCP_WINSIZE_ADJUST) ++ if (dhd_use_tcp_window_size_adjust) { ++ if (ifidx == 0 && ntoh16(skb->protocol) == ETHER_TYPE_IP) { ++ dhd_adjust_tcp_winsize(dhdp->op_mode, skb); ++ } ++ } ++#endif /* DHD_TCP_WINSIZE_ADJUST */ ++ ++ if (in_interrupt()) { ++ netif_rx(skb); ++ } else { ++ if (dhd->rxthread_enabled) { ++ if (!skbhead) ++ skbhead = skb; ++ else ++ PKTSETNEXT(dhdp->osh, skbprev, skb); ++ skbprev = skb; ++ } else { ++ ++ /* If the receive is not processed inside an ISR, ++ * the softirqd must be woken explicitly to service ++ * the NET_RX_SOFTIRQ. In 2.6 kernels, this is handled ++ * by netif_rx_ni(), but in earlier kernels, we need ++ * to do it manually. ++ */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) ++ netif_rx_ni(skb); ++#else ++ ulong flags; ++ netif_rx(skb); ++ local_irq_save(flags); ++ RAISE_RX_SOFTIRQ(); ++ local_irq_restore(flags); ++#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) */ ++ } ++ } ++ } ++ ++ if (dhd->rxthread_enabled && skbhead) ++ dhd_sched_rxf(dhdp, skbhead); ++ ++ DHD_OS_WAKE_LOCK_RX_TIMEOUT_ENABLE(dhdp, tout_rx); ++ DHD_OS_WAKE_LOCK_CTRL_TIMEOUT_ENABLE(dhdp, tout_ctrl); ++} ++ ++void ++dhd_event(struct dhd_info *dhd, char *evpkt, int evlen, int ifidx) ++{ ++ /* Linux version has nothing to do */ ++ return; ++} ++ ++void ++dhd_txcomplete(dhd_pub_t *dhdp, void *txp, bool success) ++{ ++ dhd_info_t *dhd = (dhd_info_t *)(dhdp->info); ++ struct ether_header *eh; ++ uint16 type; ++#ifdef WLBTAMP ++ uint len; ++#endif ++ ++ dhd_prot_hdrpull(dhdp, NULL, txp, NULL, NULL); ++ ++ eh = (struct ether_header *)PKTDATA(dhdp->osh, txp); ++ type = ntoh16(eh->ether_type); ++ ++ if (type == ETHER_TYPE_802_1X) ++ atomic_dec(&dhd->pend_8021x_cnt); ++ ++#ifdef WLBTAMP ++ /* Crack open the packet and check to see if it is BT HCI ACL data packet. ++ * If yes generate packet completion event. ++ */ ++ len = PKTLEN(dhdp->osh, txp); ++ ++ /* Generate ACL data tx completion event locally to avoid SDIO bus transaction */ ++ if ((type < ETHER_TYPE_MIN) && (len >= RFC1042_HDR_LEN)) { ++ struct dot11_llc_snap_header *lsh = (struct dot11_llc_snap_header *)&eh[1]; ++ ++ if (bcmp(lsh, BT_SIG_SNAP_MPROT, DOT11_LLC_SNAP_HDR_LEN - 2) == 0 && ++ ntoh16(lsh->type) == BTA_PROT_L2CAP) { ++ ++ dhd_bta_tx_hcidata_complete(dhdp, txp, success); ++ } ++ } ++#endif /* WLBTAMP */ ++#ifdef PROP_TXSTATUS ++ if (dhdp->wlfc_state && (dhdp->proptxstatus_mode != WLFC_FCMODE_NONE)) { ++ dhd_if_t *ifp = dhd->iflist[DHD_PKTTAG_IF(PKTTAG(txp))]; ++ uint datalen = PKTLEN(dhd->pub.osh, txp); ++ ++ if (success) { ++ dhd->pub.tx_packets++; ++ ifp->stats.tx_packets++; ++ ifp->stats.tx_bytes += datalen; ++ } else { ++ ifp->stats.tx_dropped++; ++ } ++ } ++#endif ++} ++ ++static struct net_device_stats * ++dhd_get_stats(struct net_device *net) ++{ ++ dhd_info_t *dhd = DHD_DEV_INFO(net); ++ dhd_if_t *ifp; ++ int ifidx; ++ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++ ifidx = dhd_net2idx(dhd, net); ++ if (ifidx == DHD_BAD_IF) { ++ DHD_ERROR(("%s: BAD_IF\n", __FUNCTION__)); ++ ++ memset(&net->stats, 0, sizeof(net->stats)); ++ return &net->stats; ++ } ++ ++ ifp = dhd->iflist[ifidx]; ++ ASSERT(dhd && ifp); ++ ++ if (dhd->pub.up) { ++ /* Use the protocol to get dongle stats */ ++ dhd_prot_dstats(&dhd->pub); ++ } ++ return &ifp->stats; ++} ++ ++static int ++dhd_watchdog_thread(void *data) ++{ ++ tsk_ctl_t *tsk = (tsk_ctl_t *)data; ++ dhd_info_t *dhd = (dhd_info_t *)tsk->parent; ++ /* This thread doesn't need any user-level access, ++ * so get rid of all our resources ++ */ ++ if (dhd_watchdog_prio > 0) { ++ struct sched_param param; ++ param.sched_priority = (dhd_watchdog_prio < MAX_RT_PRIO)? ++ dhd_watchdog_prio:(MAX_RT_PRIO-1); ++ setScheduler(current, SCHED_FIFO, ¶m); ++ } ++ ++ while (1) ++ if (down_interruptible (&tsk->sema) == 0) { ++ unsigned long flags; ++ unsigned long jiffies_at_start = jiffies; ++ unsigned long time_lapse; ++ ++ SMP_RD_BARRIER_DEPENDS(); ++ if (tsk->terminated) { ++ break; ++ } ++ ++ if (dhd->pub.dongle_reset == FALSE) { ++ DHD_TIMER(("%s:\n", __FUNCTION__)); ++ ++ /* Call the bus module watchdog */ ++ dhd_bus_watchdog(&dhd->pub); ++ ++ ++ DHD_GENERAL_LOCK(&dhd->pub, flags); ++ /* Count the tick for reference */ ++ dhd->pub.tickcnt++; ++ time_lapse = jiffies - jiffies_at_start; ++ ++ /* Reschedule the watchdog */ ++ if (dhd->wd_timer_valid) ++ mod_timer(&dhd->timer, ++ jiffies + ++ msecs_to_jiffies(dhd_watchdog_ms) - ++ min(msecs_to_jiffies(dhd_watchdog_ms), time_lapse)); ++ DHD_GENERAL_UNLOCK(&dhd->pub, flags); ++ } ++ } else { ++ break; ++ } ++ ++ complete_and_exit(&tsk->completed, 0); ++} ++ ++static void dhd_watchdog(ulong data) ++{ ++ dhd_info_t *dhd = (dhd_info_t *)data; ++ unsigned long flags; ++ ++ if (dhd->pub.dongle_reset) { ++ return; ++ } ++ ++ if (dhd->thr_wdt_ctl.thr_pid >= 0) { ++ up(&dhd->thr_wdt_ctl.sema); ++ return; ++ } ++ ++ /* Call the bus module watchdog */ ++ dhd_bus_watchdog(&dhd->pub); ++ ++ DHD_GENERAL_LOCK(&dhd->pub, flags); ++ /* Count the tick for reference */ ++ dhd->pub.tickcnt++; ++ ++ /* Reschedule the watchdog */ ++ if (dhd->wd_timer_valid) ++ mod_timer(&dhd->timer, jiffies + msecs_to_jiffies(dhd_watchdog_ms)); ++ DHD_GENERAL_UNLOCK(&dhd->pub, flags); ++ ++} ++ ++#ifdef ENABLE_ADAPTIVE_SCHED ++static void ++dhd_sched_policy(int prio) ++{ ++ struct sched_param param; ++ if (cpufreq_quick_get(0) <= CUSTOM_CPUFREQ_THRESH) { ++ param.sched_priority = 0; ++ setScheduler(current, SCHED_NORMAL, ¶m); ++ } else { ++ if (get_scheduler_policy(current) != SCHED_FIFO) { ++ param.sched_priority = (prio < MAX_RT_PRIO)? prio : (MAX_RT_PRIO-1); ++ setScheduler(current, SCHED_FIFO, ¶m); ++ } ++ } ++} ++#endif /* ENABLE_ADAPTIVE_SCHED */ ++#ifdef DEBUG_CPU_FREQ ++static int dhd_cpufreq_notifier(struct notifier_block *nb, unsigned long val, void *data) ++{ ++ dhd_info_t *dhd = container_of(nb, struct dhd_info, freq_trans); ++ struct cpufreq_freqs *freq = data; ++ if (dhd) { ++ if (!dhd->new_freq) ++ goto exit; ++ if (val == CPUFREQ_POSTCHANGE) { ++ DHD_ERROR(("cpu freq is changed to %u kHZ on CPU %d\n", ++ freq->new, freq->cpu)); ++ *per_cpu_ptr(dhd->new_freq, freq->cpu) = freq->new; ++ } ++ } ++exit: ++ return 0; ++} ++#endif /* DEBUG_CPU_FREQ */ ++static int ++dhd_dpc_thread(void *data) ++{ ++ tsk_ctl_t *tsk = (tsk_ctl_t *)data; ++ dhd_info_t *dhd = (dhd_info_t *)tsk->parent; ++ ++ /* This thread doesn't need any user-level access, ++ * so get rid of all our resources ++ */ ++ if (dhd_dpc_prio > 0) ++ { ++ struct sched_param param; ++ param.sched_priority = (dhd_dpc_prio < MAX_RT_PRIO)?dhd_dpc_prio:(MAX_RT_PRIO-1); ++ setScheduler(current, SCHED_FIFO, ¶m); ++ } ++ ++#ifdef CUSTOM_DPC_CPUCORE ++ set_cpus_allowed_ptr(current, cpumask_of(CUSTOM_DPC_CPUCORE)); ++#else ++ if (dhd->pub.conf->dpc_cpucore >= 0) { ++ printf("%s: set dpc_cpucore %d from config.txt\n", __FUNCTION__, dhd->pub.conf->dpc_cpucore); ++ set_cpus_allowed_ptr(current, cpumask_of(dhd->pub.conf->dpc_cpucore)); ++ } ++#endif ++#ifdef CUSTOM_SET_CPUCORE ++ dhd->pub.current_dpc = current; ++#endif /* CUSTOM_SET_CPUCORE */ ++ /* Run until signal received */ ++ while (1) { ++ if (!binary_sema_down(tsk)) { ++#ifdef ENABLE_ADAPTIVE_SCHED ++ dhd_sched_policy(dhd_dpc_prio); ++#endif /* ENABLE_ADAPTIVE_SCHED */ ++ SMP_RD_BARRIER_DEPENDS(); ++ if (tsk->terminated) { ++ break; ++ } ++ ++ /* Call bus dpc unless it indicated down (then clean stop) */ ++ if (dhd->pub.busstate != DHD_BUS_DOWN) { ++ dhd_os_wd_timer_extend(&dhd->pub, TRUE); ++ while (dhd_bus_dpc(dhd->pub.bus)) { ++ /* process all data */ ++ } ++ dhd_os_wd_timer_extend(&dhd->pub, FALSE); ++ DHD_OS_WAKE_UNLOCK(&dhd->pub); ++ ++ } else { ++ if (dhd->pub.up) ++ dhd_bus_stop(dhd->pub.bus, TRUE); ++ DHD_OS_WAKE_UNLOCK(&dhd->pub); ++ } ++ } ++ else ++ break; ++ } ++ complete_and_exit(&tsk->completed, 0); ++} ++ ++static int ++dhd_rxf_thread(void *data) ++{ ++ tsk_ctl_t *tsk = (tsk_ctl_t *)data; ++ dhd_info_t *dhd = (dhd_info_t *)tsk->parent; ++#if defined(WAIT_DEQUEUE) ++#define RXF_WATCHDOG_TIME 250 /* BARK_TIME(1000) / */ ++ ulong watchdogTime = OSL_SYSUPTIME(); /* msec */ ++#endif ++ dhd_pub_t *pub = &dhd->pub; ++ ++ /* This thread doesn't need any user-level access, ++ * so get rid of all our resources ++ */ ++ if (dhd_rxf_prio > 0) ++ { ++ struct sched_param param; ++ param.sched_priority = (dhd_rxf_prio < MAX_RT_PRIO)?dhd_rxf_prio:(MAX_RT_PRIO-1); ++ setScheduler(current, SCHED_FIFO, ¶m); ++ } ++ ++ DAEMONIZE("dhd_rxf"); ++ /* DHD_OS_WAKE_LOCK is called in dhd_sched_dpc[dhd_linux.c] down below */ ++ ++ /* signal: thread has started */ ++ complete(&tsk->completed); ++#ifdef CUSTOM_SET_CPUCORE ++ dhd->pub.current_rxf = current; ++#endif /* CUSTOM_SET_CPUCORE */ ++ /* Run until signal received */ ++ while (1) { ++ if (down_interruptible(&tsk->sema) == 0) { ++ void *skb; ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0) ++ ulong flags; ++#endif ++#ifdef ENABLE_ADAPTIVE_SCHED ++ dhd_sched_policy(dhd_rxf_prio); ++#endif /* ENABLE_ADAPTIVE_SCHED */ ++ ++ SMP_RD_BARRIER_DEPENDS(); ++ ++ if (tsk->terminated) { ++ break; ++ } ++ skb = dhd_rxf_dequeue(pub); ++ ++ if (skb == NULL) { ++ continue; ++ } ++ while (skb) { ++ void *skbnext = PKTNEXT(pub->osh, skb); ++ PKTSETNEXT(pub->osh, skb, NULL); ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) ++ netif_rx_ni(skb); ++#else ++ netif_rx(skb); ++ local_irq_save(flags); ++ RAISE_RX_SOFTIRQ(); ++ local_irq_restore(flags); ++ ++#endif ++ skb = skbnext; ++ } ++#if defined(WAIT_DEQUEUE) ++ if (OSL_SYSUPTIME() - watchdogTime > RXF_WATCHDOG_TIME) { ++ OSL_SLEEP(1); ++ watchdogTime = OSL_SYSUPTIME(); ++ } ++#endif ++ ++ DHD_OS_WAKE_UNLOCK(pub); ++ } ++ else ++ break; ++ } ++ complete_and_exit(&tsk->completed, 0); ++} ++ ++#ifdef BCMPCIE ++void dhd_dpc_kill(dhd_pub_t *dhdp) ++{ ++ dhd_info_t *dhd; ++ ++ if (!dhdp) ++ return; ++ ++ dhd = dhdp->info; ++ ++ if (!dhd) ++ return; ++ ++ tasklet_kill(&dhd->tasklet); ++ DHD_ERROR(("%s: tasklet disabled\n", __FUNCTION__)); ++} ++#endif /* BCMPCIE */ ++ ++static void ++dhd_dpc(ulong data) ++{ ++ dhd_info_t *dhd; ++ ++ dhd = (dhd_info_t *)data; ++ ++ /* this (tasklet) can be scheduled in dhd_sched_dpc[dhd_linux.c] ++ * down below , wake lock is set, ++ * the tasklet is initialized in dhd_attach() ++ */ ++ /* Call bus dpc unless it indicated down (then clean stop) */ ++ if (dhd->pub.busstate != DHD_BUS_DOWN) { ++ if (dhd_bus_dpc(dhd->pub.bus)) ++ tasklet_schedule(&dhd->tasklet); ++ else ++ DHD_OS_WAKE_UNLOCK(&dhd->pub); ++ } else { ++ dhd_bus_stop(dhd->pub.bus, TRUE); ++ DHD_OS_WAKE_UNLOCK(&dhd->pub); ++ } ++} ++ ++void ++dhd_sched_dpc(dhd_pub_t *dhdp) ++{ ++ dhd_info_t *dhd = (dhd_info_t *)dhdp->info; ++ ++ DHD_OS_WAKE_LOCK(dhdp); ++ if (dhd->thr_dpc_ctl.thr_pid >= 0) { ++ /* If the semaphore does not get up, ++ * wake unlock should be done here ++ */ ++ if (!binary_sema_up(&dhd->thr_dpc_ctl)) ++ DHD_OS_WAKE_UNLOCK(dhdp); ++ return; ++ } else { ++ tasklet_schedule(&dhd->tasklet); ++ } ++} ++ ++static void ++dhd_sched_rxf(dhd_pub_t *dhdp, void *skb) ++{ ++ dhd_info_t *dhd = (dhd_info_t *)dhdp->info; ++#ifdef RXF_DEQUEUE_ON_BUSY ++ int ret = BCME_OK; ++ int retry = 2; ++#endif /* RXF_DEQUEUE_ON_BUSY */ ++ ++ DHD_OS_WAKE_LOCK(dhdp); ++ ++ DHD_TRACE(("dhd_sched_rxf: Enter\n")); ++#ifdef RXF_DEQUEUE_ON_BUSY ++ do { ++ ret = dhd_rxf_enqueue(dhdp, skb); ++ if (ret == BCME_OK || ret == BCME_ERROR) ++ break; ++ else ++ OSL_SLEEP(50); /* waiting for dequeueing */ ++ } while (retry-- > 0); ++ ++ if (retry <= 0 && ret == BCME_BUSY) { ++ void *skbp = skb; ++ ++ while (skbp) { ++ void *skbnext = PKTNEXT(dhdp->osh, skbp); ++ PKTSETNEXT(dhdp->osh, skbp, NULL); ++ netif_rx_ni(skbp); ++ skbp = skbnext; ++ } ++ DHD_ERROR(("send skb to kernel backlog without rxf_thread\n")); ++ } ++ else { ++ if (dhd->thr_rxf_ctl.thr_pid >= 0) { ++ up(&dhd->thr_rxf_ctl.sema); ++ } ++ } ++#else /* RXF_DEQUEUE_ON_BUSY */ ++ do { ++ if (dhd_rxf_enqueue(dhdp, skb) == BCME_OK) ++ break; ++ } while (1); ++ if (dhd->thr_rxf_ctl.thr_pid >= 0) { ++ up(&dhd->thr_rxf_ctl.sema); ++ } ++ return; ++#endif /* RXF_DEQUEUE_ON_BUSY */ ++} ++ ++#ifdef TOE ++/* Retrieve current toe component enables, which are kept as a bitmap in toe_ol iovar */ ++static int ++dhd_toe_get(dhd_info_t *dhd, int ifidx, uint32 *toe_ol) ++{ ++ wl_ioctl_t ioc; ++ char buf[32]; ++ int ret; ++ ++ memset(&ioc, 0, sizeof(ioc)); ++ ++ ioc.cmd = WLC_GET_VAR; ++ ioc.buf = buf; ++ ioc.len = (uint)sizeof(buf); ++ ioc.set = FALSE; ++ ++ strncpy(buf, "toe_ol", sizeof(buf) - 1); ++ buf[sizeof(buf) - 1] = '\0'; ++ if ((ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len)) < 0) { ++ /* Check for older dongle image that doesn't support toe_ol */ ++ if (ret == -EIO) { ++ DHD_ERROR(("%s: toe not supported by device\n", ++ dhd_ifname(&dhd->pub, ifidx))); ++ return -EOPNOTSUPP; ++ } ++ ++ DHD_INFO(("%s: could not get toe_ol: ret=%d\n", dhd_ifname(&dhd->pub, ifidx), ret)); ++ return ret; ++ } ++ ++ memcpy(toe_ol, buf, sizeof(uint32)); ++ return 0; ++} ++ ++/* Set current toe component enables in toe_ol iovar, and set toe global enable iovar */ ++static int ++dhd_toe_set(dhd_info_t *dhd, int ifidx, uint32 toe_ol) ++{ ++ wl_ioctl_t ioc; ++ char buf[32]; ++ int toe, ret; ++ ++ memset(&ioc, 0, sizeof(ioc)); ++ ++ ioc.cmd = WLC_SET_VAR; ++ ioc.buf = buf; ++ ioc.len = (uint)sizeof(buf); ++ ioc.set = TRUE; ++ ++ /* Set toe_ol as requested */ ++ ++ strncpy(buf, "toe_ol", sizeof(buf) - 1); ++ buf[sizeof(buf) - 1] = '\0'; ++ memcpy(&buf[sizeof("toe_ol")], &toe_ol, sizeof(uint32)); ++ ++ if ((ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len)) < 0) { ++ DHD_ERROR(("%s: could not set toe_ol: ret=%d\n", ++ dhd_ifname(&dhd->pub, ifidx), ret)); ++ return ret; ++ } ++ ++ /* Enable toe globally only if any components are enabled. */ ++ ++ toe = (toe_ol != 0); ++ ++ strcpy(buf, "toe"); ++ memcpy(&buf[sizeof("toe")], &toe, sizeof(uint32)); ++ ++ if ((ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len)) < 0) { ++ DHD_ERROR(("%s: could not set toe: ret=%d\n", dhd_ifname(&dhd->pub, ifidx), ret)); ++ return ret; ++ } ++ ++ return 0; ++} ++#endif /* TOE */ ++ ++#if defined(WL_CFG80211) ++void dhd_set_scb_probe(dhd_pub_t *dhd) ++{ ++#define NUM_SCB_MAX_PROBE 3 ++ int ret = 0; ++ wl_scb_probe_t scb_probe; ++ char iovbuf[WL_EVENTING_MASK_LEN + 12]; ++ ++ memset(&scb_probe, 0, sizeof(wl_scb_probe_t)); ++ ++ if (dhd->op_mode & DHD_FLAG_HOSTAP_MODE) ++ return; ++ ++ bcm_mkiovar("scb_probe", NULL, 0, iovbuf, sizeof(iovbuf)); ++ ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) ++ DHD_ERROR(("%s: GET max_scb_probe failed\n", __FUNCTION__)); ++ ++ memcpy(&scb_probe, iovbuf, sizeof(wl_scb_probe_t)); ++ ++ scb_probe.scb_max_probe = NUM_SCB_MAX_PROBE; ++ ++ bcm_mkiovar("scb_probe", (char *)&scb_probe, ++ sizeof(wl_scb_probe_t), iovbuf, sizeof(iovbuf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) ++ DHD_ERROR(("%s: max_scb_probe setting failed\n", __FUNCTION__)); ++#undef NUM_SCB_MAX_PROBE ++ return; ++} ++#endif /* WL_CFG80211 */ ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) ++static void ++dhd_ethtool_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info) ++{ ++ dhd_info_t *dhd = DHD_DEV_INFO(net); ++ ++ snprintf(info->driver, sizeof(info->driver), "wl"); ++ snprintf(info->version, sizeof(info->version), "%lu", dhd->pub.drv_version); ++} ++ ++struct ethtool_ops dhd_ethtool_ops = { ++ .get_drvinfo = dhd_ethtool_get_drvinfo ++}; ++#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) */ ++ ++ ++#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 2) ++static int ++dhd_ethtool(dhd_info_t *dhd, void *uaddr) ++{ ++ struct ethtool_drvinfo info; ++ char drvname[sizeof(info.driver)]; ++ uint32 cmd; ++#ifdef TOE ++ struct ethtool_value edata; ++ uint32 toe_cmpnt, csum_dir; ++ int ret; ++#endif ++ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++ /* all ethtool calls start with a cmd word */ ++ if (copy_from_user(&cmd, uaddr, sizeof (uint32))) ++ return -EFAULT; ++ ++ switch (cmd) { ++ case ETHTOOL_GDRVINFO: ++ /* Copy out any request driver name */ ++ if (copy_from_user(&info, uaddr, sizeof(info))) ++ return -EFAULT; ++ strncpy(drvname, info.driver, sizeof(info.driver)); ++ drvname[sizeof(info.driver)-1] = '\0'; ++ ++ /* clear struct for return */ ++ memset(&info, 0, sizeof(info)); ++ info.cmd = cmd; ++ ++ /* if dhd requested, identify ourselves */ ++ if (strcmp(drvname, "?dhd") == 0) { ++ snprintf(info.driver, sizeof(info.driver), "dhd"); ++ strncpy(info.version, EPI_VERSION_STR, sizeof(info.version) - 1); ++ info.version[sizeof(info.version) - 1] = '\0'; ++ } ++ ++ /* otherwise, require dongle to be up */ ++ else if (!dhd->pub.up) { ++ DHD_ERROR(("%s: dongle is not up\n", __FUNCTION__)); ++ return -ENODEV; ++ } ++ ++ /* finally, report dongle driver type */ ++ else if (dhd->pub.iswl) ++ snprintf(info.driver, sizeof(info.driver), "wl"); ++ else ++ snprintf(info.driver, sizeof(info.driver), "xx"); ++ ++ snprintf(info.version, sizeof(info.version), "%lu", dhd->pub.drv_version); ++ if (copy_to_user(uaddr, &info, sizeof(info))) ++ return -EFAULT; ++ DHD_CTL(("%s: given %*s, returning %s\n", __FUNCTION__, ++ (int)sizeof(drvname), drvname, info.driver)); ++ break; ++ ++#ifdef TOE ++ /* Get toe offload components from dongle */ ++ case ETHTOOL_GRXCSUM: ++ case ETHTOOL_GTXCSUM: ++ if ((ret = dhd_toe_get(dhd, 0, &toe_cmpnt)) < 0) ++ return ret; ++ ++ csum_dir = (cmd == ETHTOOL_GTXCSUM) ? TOE_TX_CSUM_OL : TOE_RX_CSUM_OL; ++ ++ edata.cmd = cmd; ++ edata.data = (toe_cmpnt & csum_dir) ? 1 : 0; ++ ++ if (copy_to_user(uaddr, &edata, sizeof(edata))) ++ return -EFAULT; ++ break; ++ ++ /* Set toe offload components in dongle */ ++ case ETHTOOL_SRXCSUM: ++ case ETHTOOL_STXCSUM: ++ if (copy_from_user(&edata, uaddr, sizeof(edata))) ++ return -EFAULT; ++ ++ /* Read the current settings, update and write back */ ++ if ((ret = dhd_toe_get(dhd, 0, &toe_cmpnt)) < 0) ++ return ret; ++ ++ csum_dir = (cmd == ETHTOOL_STXCSUM) ? TOE_TX_CSUM_OL : TOE_RX_CSUM_OL; ++ ++ if (edata.data != 0) ++ toe_cmpnt |= csum_dir; ++ else ++ toe_cmpnt &= ~csum_dir; ++ ++ if ((ret = dhd_toe_set(dhd, 0, toe_cmpnt)) < 0) ++ return ret; ++ ++ /* If setting TX checksum mode, tell Linux the new mode */ ++ if (cmd == ETHTOOL_STXCSUM) { ++ if (edata.data) ++ dhd->iflist[0]->net->features |= NETIF_F_IP_CSUM; ++ else ++ dhd->iflist[0]->net->features &= ~NETIF_F_IP_CSUM; ++ } ++ ++ break; ++#endif /* TOE */ ++ ++ default: ++ return -EOPNOTSUPP; ++ } ++ ++ return 0; ++} ++#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 2) */ ++ ++static bool dhd_check_hang(struct net_device *net, dhd_pub_t *dhdp, int error) ++{ ++ dhd_info_t *dhd; ++ ++ if (!dhdp) { ++ DHD_ERROR(("%s: dhdp is NULL\n", __FUNCTION__)); ++ return FALSE; ++ } ++ ++ if (!dhdp->up) ++ return FALSE; ++ ++ dhd = (dhd_info_t *)dhdp->info; ++#if !defined(BCMPCIE) ++ if (dhd->thr_dpc_ctl.thr_pid < 0) { ++ DHD_ERROR(("%s : skipped due to negative pid - unloading?\n", __FUNCTION__)); ++ return FALSE; ++ } ++#endif ++ ++#ifdef CONFIG_MACH_UNIVERSAL5433 ++ /* old revision does not send hang message */ ++ if ((check_rev() && (error == -ETIMEDOUT)) || (error == -EREMOTEIO) || ++#else ++ if ((error == -ETIMEDOUT) || (error == -EREMOTEIO) || ++#endif /* CONFIG_MACH_UNIVERSAL5433 */ ++ ((dhdp->busstate == DHD_BUS_DOWN) && (!dhdp->dongle_reset))) { ++ DHD_ERROR(("%s: Event HANG send up due to re=%d te=%d e=%d s=%d\n", __FUNCTION__, ++ dhdp->rxcnt_timeout, dhdp->txcnt_timeout, error, dhdp->busstate)); ++ net_os_send_hang_message(net); ++ return TRUE; ++ } ++ return FALSE; ++} ++ ++int dhd_ioctl_process(dhd_pub_t *pub, int ifidx, dhd_ioctl_t *ioc, void *data_buf) ++{ ++ int bcmerror = BCME_OK; ++ int buflen = 0; ++ struct net_device *net; ++ ++ net = dhd_idx2net(pub, ifidx); ++ if (!net) { ++ bcmerror = BCME_BADARG; ++ goto done; ++ } ++ ++ if (data_buf) ++ buflen = MIN(ioc->len, DHD_IOCTL_MAXLEN); ++ ++ /* check for local dhd ioctl and handle it */ ++ if (ioc->driver == DHD_IOCTL_MAGIC) { ++ bcmerror = dhd_ioctl((void *)pub, ioc, data_buf, buflen); ++ if (bcmerror) ++ pub->bcmerror = bcmerror; ++ goto done; ++ } ++ ++ /* send to dongle (must be up, and wl). */ ++ if (pub->busstate != DHD_BUS_DATA) { ++ bcmerror = BCME_DONGLE_DOWN; ++ goto done; ++ } ++ ++ if (!pub->iswl) { ++ bcmerror = BCME_DONGLE_DOWN; ++ goto done; ++ } ++ ++ /* ++ * Flush the TX queue if required for proper message serialization: ++ * Intercept WLC_SET_KEY IOCTL - serialize M4 send and set key IOCTL to ++ * prevent M4 encryption and ++ * intercept WLC_DISASSOC IOCTL - serialize WPS-DONE and WLC_DISASSOC IOCTL to ++ * prevent disassoc frame being sent before WPS-DONE frame. ++ */ ++ if (ioc->cmd == WLC_SET_KEY || ++ (ioc->cmd == WLC_SET_VAR && data_buf != NULL && ++ strncmp("wsec_key", data_buf, 9) == 0) || ++ (ioc->cmd == WLC_SET_VAR && data_buf != NULL && ++ strncmp("bsscfg:wsec_key", data_buf, 15) == 0) || ++ ioc->cmd == WLC_DISASSOC) ++ dhd_wait_pend8021x(net); ++ ++#ifdef WLMEDIA_HTSF ++ if (data_buf) { ++ /* short cut wl ioctl calls here */ ++ if (strcmp("htsf", data_buf) == 0) { ++ dhd_ioctl_htsf_get(dhd, 0); ++ return BCME_OK; ++ } ++ ++ if (strcmp("htsflate", data_buf) == 0) { ++ if (ioc->set) { ++ memset(ts, 0, sizeof(tstamp_t)*TSMAX); ++ memset(&maxdelayts, 0, sizeof(tstamp_t)); ++ maxdelay = 0; ++ tspktcnt = 0; ++ maxdelaypktno = 0; ++ memset(&vi_d1.bin, 0, sizeof(uint32)*NUMBIN); ++ memset(&vi_d2.bin, 0, sizeof(uint32)*NUMBIN); ++ memset(&vi_d3.bin, 0, sizeof(uint32)*NUMBIN); ++ memset(&vi_d4.bin, 0, sizeof(uint32)*NUMBIN); ++ } else { ++ dhd_dump_latency(); ++ } ++ return BCME_OK; ++ } ++ if (strcmp("htsfclear", data_buf) == 0) { ++ memset(&vi_d1.bin, 0, sizeof(uint32)*NUMBIN); ++ memset(&vi_d2.bin, 0, sizeof(uint32)*NUMBIN); ++ memset(&vi_d3.bin, 0, sizeof(uint32)*NUMBIN); ++ memset(&vi_d4.bin, 0, sizeof(uint32)*NUMBIN); ++ htsf_seqnum = 0; ++ return BCME_OK; ++ } ++ if (strcmp("htsfhis", data_buf) == 0) { ++ dhd_dump_htsfhisto(&vi_d1, "H to D"); ++ dhd_dump_htsfhisto(&vi_d2, "D to D"); ++ dhd_dump_htsfhisto(&vi_d3, "D to H"); ++ dhd_dump_htsfhisto(&vi_d4, "H to H"); ++ return BCME_OK; ++ } ++ if (strcmp("tsport", data_buf) == 0) { ++ if (ioc->set) { ++ memcpy(&tsport, data_buf + 7, 4); ++ } else { ++ DHD_ERROR(("current timestamp port: %d \n", tsport)); ++ } ++ return BCME_OK; ++ } ++ } ++#endif /* WLMEDIA_HTSF */ ++ ++ if ((ioc->cmd == WLC_SET_VAR || ioc->cmd == WLC_GET_VAR) && ++ data_buf != NULL && strncmp("rpc_", data_buf, 4) == 0) { ++#ifdef BCM_FD_AGGR ++ bcmerror = dhd_fdaggr_ioctl(pub, ifidx, (wl_ioctl_t *)ioc, data_buf, buflen); ++#else ++ bcmerror = BCME_UNSUPPORTED; ++#endif ++ goto done; ++ } ++ bcmerror = dhd_wl_ioctl(pub, ifidx, (wl_ioctl_t *)ioc, data_buf, buflen); ++ ++done: ++ dhd_check_hang(net, pub, bcmerror); ++ ++ return bcmerror; ++} ++ ++static int ++dhd_ioctl_entry(struct net_device *net, struct ifreq *ifr, int cmd) ++{ ++ dhd_info_t *dhd = DHD_DEV_INFO(net); ++ dhd_ioctl_t ioc; ++ int bcmerror = 0; ++ int ifidx; ++ int ret; ++ void *local_buf = NULL; ++ u16 buflen = 0; ++ ++ DHD_OS_WAKE_LOCK(&dhd->pub); ++ DHD_PERIM_LOCK(&dhd->pub); ++ ++ /* Interface up check for built-in type */ ++ if (!dhd_download_fw_on_driverload && dhd->pub.up == 0) { ++ DHD_ERROR(("%s: Interface is down \n", __FUNCTION__)); ++ DHD_PERIM_UNLOCK(&dhd->pub); ++ DHD_OS_WAKE_UNLOCK(&dhd->pub); ++ return BCME_NOTUP; ++ } ++ ++ /* 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\n", __FUNCTION__)); ++ DHD_OS_WAKE_LOCK_CTRL_TIMEOUT_ENABLE(&dhd->pub, DHD_EVENT_TIMEOUT_MS); ++ DHD_OS_WAKE_UNLOCK(&dhd->pub); ++ return OSL_ERROR(BCME_DONGLE_DOWN); ++ } ++ ++ ifidx = dhd_net2idx(dhd, net); ++ DHD_TRACE(("%s: ifidx %d, cmd 0x%04x\n", __FUNCTION__, ifidx, cmd)); ++ ++ if (ifidx == DHD_BAD_IF) { ++ DHD_ERROR(("%s: BAD IF\n", __FUNCTION__)); ++ DHD_PERIM_UNLOCK(&dhd->pub); ++ DHD_OS_WAKE_UNLOCK(&dhd->pub); ++ return -1; ++ } ++ ++#if defined(WL_WIRELESS_EXT) ++ /* linux wireless extensions */ ++ if ((cmd >= SIOCIWFIRST) && (cmd <= SIOCIWLAST)) { ++ /* may recurse, do NOT lock */ ++ ret = wl_iw_ioctl(net, ifr, cmd); ++ DHD_PERIM_UNLOCK(&dhd->pub); ++ DHD_OS_WAKE_UNLOCK(&dhd->pub); ++ return ret; ++ } ++#endif /* defined(WL_WIRELESS_EXT) */ ++ ++#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 2) ++ if (cmd == SIOCETHTOOL) { ++ ret = dhd_ethtool(dhd, (void*)ifr->ifr_data); ++ DHD_PERIM_UNLOCK(&dhd->pub); ++ DHD_OS_WAKE_UNLOCK(&dhd->pub); ++ return ret; ++ } ++#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 2) */ ++ ++ if (cmd == SIOCDEVPRIVATE+1) { ++ ret = wl_android_priv_cmd(net, ifr, cmd); ++ dhd_check_hang(net, &dhd->pub, ret); ++ DHD_OS_WAKE_UNLOCK(&dhd->pub); ++ return ret; ++ } ++ ++ if (cmd != SIOCDEVPRIVATE) { ++ DHD_PERIM_UNLOCK(&dhd->pub); ++ DHD_OS_WAKE_UNLOCK(&dhd->pub); ++ return -EOPNOTSUPP; ++ } ++ ++ memset(&ioc, 0, sizeof(ioc)); ++ ++#ifdef CONFIG_COMPAT ++ if (is_compat_task()) { ++ compat_wl_ioctl_t compat_ioc; ++ if (copy_from_user(&compat_ioc, ifr->ifr_data, sizeof(compat_wl_ioctl_t))) { ++ bcmerror = BCME_BADADDR; ++ goto done; ++ } ++ ioc.cmd = compat_ioc.cmd; ++ ioc.buf = compat_ptr(compat_ioc.buf); ++ ioc.len = compat_ioc.len; ++ ioc.set = compat_ioc.set; ++ ioc.used = compat_ioc.used; ++ ioc.needed = compat_ioc.needed; ++ /* To differentiate between wl and dhd read 4 more byes */ ++ if ((copy_from_user(&ioc.driver, (char *)ifr->ifr_data + sizeof(compat_wl_ioctl_t), ++ sizeof(uint)) != 0)) { ++ bcmerror = BCME_BADADDR; ++ goto done; ++ } ++ } else ++#endif /* CONFIG_COMPAT */ ++ { ++ /* Copy the ioc control structure part of ioctl request */ ++ if (copy_from_user(&ioc, ifr->ifr_data, sizeof(wl_ioctl_t))) { ++ bcmerror = BCME_BADADDR; ++ goto done; ++ } ++ ++ /* To differentiate between wl and dhd read 4 more byes */ ++ if ((copy_from_user(&ioc.driver, (char *)ifr->ifr_data + sizeof(wl_ioctl_t), ++ sizeof(uint)) != 0)) { ++ bcmerror = BCME_BADADDR; ++ goto done; ++ } ++ } ++ ++ if (!capable(CAP_NET_ADMIN)) { ++ bcmerror = BCME_EPERM; ++ goto done; ++ } ++ ++ if (ioc.len > 0) { ++ buflen = MIN(ioc.len, DHD_IOCTL_MAXLEN); ++ if (!(local_buf = MALLOC(dhd->pub.osh, buflen+1))) { ++ bcmerror = BCME_NOMEM; ++ goto done; ++ } ++ ++ DHD_PERIM_UNLOCK(&dhd->pub); ++ if (copy_from_user(local_buf, ioc.buf, buflen)) { ++ DHD_PERIM_LOCK(&dhd->pub); ++ bcmerror = BCME_BADADDR; ++ goto done; ++ } ++ DHD_PERIM_LOCK(&dhd->pub); ++ ++ *(char *)(local_buf + buflen) = '\0'; ++ } ++ ++ bcmerror = dhd_ioctl_process(&dhd->pub, ifidx, &ioc, local_buf); ++ ++ if (!bcmerror && buflen && local_buf && ioc.buf) { ++ DHD_PERIM_UNLOCK(&dhd->pub); ++ if (copy_to_user(ioc.buf, local_buf, buflen)) ++ bcmerror = -EFAULT; ++ DHD_PERIM_LOCK(&dhd->pub); ++ } ++ ++done: ++ if (local_buf) ++ MFREE(dhd->pub.osh, local_buf, buflen+1); ++ ++ DHD_PERIM_UNLOCK(&dhd->pub); ++ DHD_OS_WAKE_UNLOCK(&dhd->pub); ++ ++ return OSL_ERROR(bcmerror); ++} ++ ++#define MAX_TRY_CNT 5 /* Number of tries to disable deepsleep */ ++int dhd_deepsleep(dhd_info_t *dhd, int flag) ++{ ++ char iovbuf[20]; ++ uint powervar = 0; ++ dhd_pub_t *dhdp; ++ int cnt = 0; ++ int ret = 0; ++ ++ dhdp = &dhd->pub; ++ ++ switch (flag) { ++ case 1 : /* Deepsleep on */ ++ DHD_ERROR(("dhd_deepsleep: ON\n")); ++ /* give some time to sysioc_work before deepsleep */ ++ OSL_SLEEP(200); ++#ifdef PKT_FILTER_SUPPORT ++ /* disable pkt filter */ ++ dhd_enable_packet_filter(0, dhdp); ++#endif /* PKT_FILTER_SUPPORT */ ++ /* Disable MPC */ ++ powervar = 0; ++ memset(iovbuf, 0, sizeof(iovbuf)); ++ bcm_mkiovar("mpc", (char *)&powervar, 4, iovbuf, sizeof(iovbuf)); ++ dhd_wl_ioctl_cmd(dhdp, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); ++ ++ /* Enable Deepsleep */ ++ powervar = 1; ++ memset(iovbuf, 0, sizeof(iovbuf)); ++ bcm_mkiovar("deepsleep", (char *)&powervar, 4, iovbuf, sizeof(iovbuf)); ++ dhd_wl_ioctl_cmd(dhdp, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); ++ break; ++ ++ case 0: /* Deepsleep Off */ ++ DHD_ERROR(("dhd_deepsleep: OFF\n")); ++ ++ /* Disable Deepsleep */ ++ for (cnt = 0; cnt < MAX_TRY_CNT; cnt++) { ++ powervar = 0; ++ memset(iovbuf, 0, sizeof(iovbuf)); ++ bcm_mkiovar("deepsleep", (char *)&powervar, 4, ++ iovbuf, sizeof(iovbuf)); ++ dhd_wl_ioctl_cmd(dhdp, WLC_SET_VAR, iovbuf, ++ sizeof(iovbuf), TRUE, 0); ++ ++ memset(iovbuf, 0, sizeof(iovbuf)); ++ bcm_mkiovar("deepsleep", (char *)&powervar, 4, ++ iovbuf, sizeof(iovbuf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhdp, WLC_GET_VAR, iovbuf, ++ sizeof(iovbuf), FALSE, 0)) < 0) { ++ DHD_ERROR(("the error of dhd deepsleep status" ++ " ret value :%d\n", ret)); ++ } else { ++ if (!(*(int *)iovbuf)) { ++ DHD_ERROR(("deepsleep mode is 0," ++ " count: %d\n", cnt)); ++ break; ++ } ++ } ++ } ++ ++ /* Enable MPC */ ++ powervar = 1; ++ memset(iovbuf, 0, sizeof(iovbuf)); ++ bcm_mkiovar("mpc", (char *)&powervar, 4, iovbuf, sizeof(iovbuf)); ++ dhd_wl_ioctl_cmd(dhdp, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); ++ break; ++ } ++ ++ return 0; ++} ++ ++static int ++dhd_stop(struct net_device *net) ++{ ++ int ifidx = 0; ++ dhd_info_t *dhd = DHD_DEV_INFO(net); ++ DHD_OS_WAKE_LOCK(&dhd->pub); ++ DHD_PERIM_LOCK(&dhd->pub); ++ printf("%s: Enter %p\n", __FUNCTION__, net); ++ if (dhd->pub.up == 0) { ++ goto exit; ++ } ++ ++ dhd_if_flush_sta(DHD_DEV_IFP(net)); ++ ++ ++ ifidx = dhd_net2idx(dhd, net); ++ BCM_REFERENCE(ifidx); ++ ++ /* Set state and stop OS transmissions */ ++ netif_stop_queue(net); ++ dhd->pub.up = 0; ++ ++#ifdef WL_CFG80211 ++ if (ifidx == 0) { ++ wl_cfg80211_down(NULL); ++ ++ /* ++ * For CFG80211: Clean up all the left over virtual interfaces ++ * when the primary Interface is brought down. [ifconfig wlan0 down] ++ */ ++ if (!dhd_download_fw_on_driverload) { ++ if ((dhd->dhd_state & DHD_ATTACH_STATE_ADD_IF) && ++ (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211)) { ++ int i; ++ ++ dhd_net_if_lock_local(dhd); ++ for (i = 1; i < DHD_MAX_IFS; i++) ++ dhd_remove_if(&dhd->pub, i, FALSE); ++ dhd_net_if_unlock_local(dhd); ++ } ++ } ++ } ++#endif /* WL_CFG80211 */ ++ ++#ifdef PROP_TXSTATUS ++ dhd_wlfc_cleanup(&dhd->pub, NULL, 0); ++#endif ++ /* Stop the protocol module */ ++ dhd_prot_stop(&dhd->pub); ++ ++ OLD_MOD_DEC_USE_COUNT; ++exit: ++ if (ifidx == 0 && !dhd_download_fw_on_driverload) ++ wl_android_wifi_off(net); ++ else { ++ if (dhd->pub.conf->deepsleep) ++ dhd_deepsleep(dhd, 1); ++ } ++ dhd->pub.rxcnt_timeout = 0; ++ dhd->pub.txcnt_timeout = 0; ++ ++ dhd->pub.hang_was_sent = 0; ++ ++ /* Clear country spec for for built-in type driver */ ++ if (!dhd_download_fw_on_driverload) { ++ dhd->pub.dhd_cspec.country_abbrev[0] = 0x00; ++ dhd->pub.dhd_cspec.rev = 0; ++ dhd->pub.dhd_cspec.ccode[0] = 0x00; ++ } ++ ++ printf("%s: Exit\n", __FUNCTION__); ++ DHD_PERIM_UNLOCK(&dhd->pub); ++ DHD_OS_WAKE_UNLOCK(&dhd->pub); ++ return 0; ++} ++ ++#if defined(WL_CFG80211) && defined(USE_INITIAL_SHORT_DWELL_TIME) ++extern bool g_first_broadcast_scan; ++#endif ++ ++#ifdef WL11U ++static int dhd_interworking_enable(dhd_pub_t *dhd) ++{ ++ char iovbuf[WLC_IOCTL_SMLEN]; ++ uint32 enable = true; ++ int ret = BCME_OK; ++ ++ bcm_mkiovar("interworking", (char *)&enable, sizeof(enable), iovbuf, sizeof(iovbuf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) { ++ DHD_ERROR(("%s: enableing interworking failed, ret=%d\n", __FUNCTION__, ret)); ++ } ++ ++ if (ret == BCME_OK) { ++ /* basic capabilities for HS20 REL2 */ ++ uint32 cap = WL_WNM_BSSTRANS | WL_WNM_NOTIF; ++ bcm_mkiovar("wnm", (char *)&cap, sizeof(cap), iovbuf, sizeof(iovbuf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, ++ iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) { ++ DHD_ERROR(("%s: failed to set WNM info, ret=%d\n", __FUNCTION__, ret)); ++ } ++ } ++ ++ return ret; ++} ++#endif /* WL11u */ ++ ++static int ++dhd_open(struct net_device *net) ++{ ++ dhd_info_t *dhd = DHD_DEV_INFO(net); ++#ifdef TOE ++ uint32 toe_ol; ++#endif ++ int ifidx; ++ int32 ret = 0; ++ ++ printf("%s: Enter %p\n", __FUNCTION__, net); ++#if defined(MULTIPLE_SUPPLICANT) ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1 ++ if (mutex_is_locked(&_dhd_sdio_mutex_lock_) != 0) { ++ DHD_ERROR(("%s : dhd_open: call dev open before insmod complete!\n", __FUNCTION__)); ++ } ++ mutex_lock(&_dhd_sdio_mutex_lock_); ++#endif ++#endif /* MULTIPLE_SUPPLICANT */ ++ ++ DHD_OS_WAKE_LOCK(&dhd->pub); ++ DHD_PERIM_LOCK(&dhd->pub); ++ dhd->pub.dongle_trap_occured = 0; ++ dhd->pub.hang_was_sent = 0; ++ ++#if 0 ++ /* ++ * Force start if ifconfig_up gets called before START command ++ * We keep WEXT's wl_control_wl_start to provide backward compatibility ++ * This should be removed in the future ++ */ ++ ret = wl_control_wl_start(net); ++ if (ret != 0) { ++ DHD_ERROR(("%s: failed with code %d\n", __FUNCTION__, ret)); ++ ret = -1; ++ goto exit; ++ } ++#endif ++ ++ ifidx = dhd_net2idx(dhd, net); ++ DHD_TRACE(("%s: ifidx %d\n", __FUNCTION__, ifidx)); ++ ++ if (ifidx < 0) { ++ DHD_ERROR(("%s: Error: called with invalid IF\n", __FUNCTION__)); ++ ret = -1; ++ goto exit; ++ } ++ ++ if (!dhd->iflist[ifidx]) { ++ DHD_ERROR(("%s: Error: called when IF already deleted\n", __FUNCTION__)); ++ ret = -1; ++ goto exit; ++ } ++ ++ if (ifidx == 0) { ++ atomic_set(&dhd->pend_8021x_cnt, 0); ++ if (!dhd_download_fw_on_driverload) { ++ DHD_ERROR(("\n%s\n", dhd_version)); ++#if defined(USE_INITIAL_SHORT_DWELL_TIME) ++ g_first_broadcast_scan = TRUE; ++#endif ++ ret = wl_android_wifi_on(net); ++ if (ret != 0) { ++ DHD_ERROR(("%s : wl_android_wifi_on failed (%d)\n", ++ __FUNCTION__, ret)); ++ ret = -1; ++ goto exit; ++ } ++ } ++ ++ if (dhd->pub.busstate != DHD_BUS_DATA) { ++ ++ /* try to bring up bus */ ++ DHD_PERIM_UNLOCK(&dhd->pub); ++ ret = dhd_bus_start(&dhd->pub); ++ DHD_PERIM_LOCK(&dhd->pub); ++ if (ret) { ++ DHD_ERROR(("%s: failed with code %d\n", __FUNCTION__, ret)); ++ ret = -1; ++ goto exit; ++ } ++ ++ } ++ if (dhd_download_fw_on_driverload) { ++ if (dhd->pub.conf->deepsleep) ++ dhd_deepsleep(dhd, 0); ++ } ++ ++ /* dhd_sync_with_dongle has been called in dhd_bus_start or wl_android_wifi_on */ ++ memcpy(net->dev_addr, dhd->pub.mac.octet, ETHER_ADDR_LEN); ++ ++#ifdef TOE ++ /* Get current TOE mode from dongle */ ++ if (dhd_toe_get(dhd, ifidx, &toe_ol) >= 0 && (toe_ol & TOE_TX_CSUM_OL) != 0) ++ dhd->iflist[ifidx]->net->features |= NETIF_F_IP_CSUM; ++ else ++ dhd->iflist[ifidx]->net->features &= ~NETIF_F_IP_CSUM; ++#endif /* TOE */ ++ ++#if defined(WL_CFG80211) ++ if (unlikely(wl_cfg80211_up(NULL))) { ++ DHD_ERROR(("%s: failed to bring up cfg80211\n", __FUNCTION__)); ++ ret = -1; ++ goto exit; ++ } ++ dhd_set_scb_probe(&dhd->pub); ++#endif /* WL_CFG80211 */ ++ } ++ ++ /* Allow transmit calls */ ++ netif_start_queue(net); ++ dhd->pub.up = 1; ++ ++#ifdef BCMDBGFS ++ dhd_dbg_init(&dhd->pub); ++#endif ++ ++ OLD_MOD_INC_USE_COUNT; ++exit: ++ if (ret) ++ dhd_stop(net); ++ ++ DHD_PERIM_UNLOCK(&dhd->pub); ++ DHD_OS_WAKE_UNLOCK(&dhd->pub); ++ ++#if defined(MULTIPLE_SUPPLICANT) ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1 ++ mutex_unlock(&_dhd_sdio_mutex_lock_); ++#endif ++#endif /* MULTIPLE_SUPPLICANT */ ++ ++ printf("%s: Exit ret=%d\n", __FUNCTION__, ret); ++ return ret; ++} ++ ++int dhd_do_driver_init(struct net_device *net) ++{ ++ dhd_info_t *dhd = NULL; ++ ++ if (!net) { ++ DHD_ERROR(("Primary Interface not initialized \n")); ++ return -EINVAL; ++ } ++ ++#ifdef MULTIPLE_SUPPLICANT ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1 && defined(BCMSDIO) ++ if (mutex_is_locked(&_dhd_sdio_mutex_lock_) != 0) { ++ DHD_ERROR(("%s : dhdsdio_probe is already running!\n", __FUNCTION__)); ++ return 0; ++ } ++#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) */ ++#endif /* MULTIPLE_SUPPLICANT */ ++ ++ /* && defined(OEM_ANDROID) && defined(BCMSDIO) */ ++ dhd = DHD_DEV_INFO(net); ++ ++ /* If driver is already initialized, do nothing ++ */ ++ if (dhd->pub.busstate == DHD_BUS_DATA) { ++ DHD_TRACE(("Driver already Inititalized. Nothing to do")); ++ return 0; ++ } ++ ++ if (dhd_open(net) < 0) { ++ DHD_ERROR(("Driver Init Failed \n")); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++int ++dhd_event_ifadd(dhd_info_t *dhdinfo, wl_event_data_if_t *ifevent, char *name, uint8 *mac) ++{ ++ ++#ifdef WL_CFG80211 ++ if (wl_cfg80211_notify_ifadd(ifevent->ifidx, name, mac, ifevent->bssidx) == BCME_OK) ++ return BCME_OK; ++#endif ++ ++ /* handle IF event caused by wl commands, SoftAP, WEXT and ++ * anything else. This has to be done asynchronously otherwise ++ * DPC will be blocked (and iovars will timeout as DPC has no chance ++ * to read the response back) ++ */ ++ if (ifevent->ifidx > 0) { ++ dhd_if_event_t *if_event = MALLOC(dhdinfo->pub.osh, sizeof(dhd_if_event_t)); ++ ++ memcpy(&if_event->event, ifevent, sizeof(if_event->event)); ++ memcpy(if_event->mac, mac, ETHER_ADDR_LEN); ++ strncpy(if_event->name, name, IFNAMSIZ); ++ if_event->name[IFNAMSIZ - 1] = '\0'; ++ dhd_deferred_schedule_work(dhdinfo->dhd_deferred_wq, (void *)if_event, ++ DHD_WQ_WORK_IF_ADD, dhd_ifadd_event_handler, DHD_WORK_PRIORITY_LOW); ++ } ++ ++ return BCME_OK; ++} ++ ++int ++dhd_event_ifdel(dhd_info_t *dhdinfo, wl_event_data_if_t *ifevent, char *name, uint8 *mac) ++{ ++ dhd_if_event_t *if_event; ++ ++#if defined(WL_CFG80211) && !defined(P2PONEINT) ++ if (wl_cfg80211_notify_ifdel(ifevent->ifidx, name, mac, ifevent->bssidx) == BCME_OK) ++ return BCME_OK; ++#endif /* WL_CFG80211 */ ++ ++ /* handle IF event caused by wl commands, SoftAP, WEXT and ++ * anything else ++ */ ++ if_event = MALLOC(dhdinfo->pub.osh, sizeof(dhd_if_event_t)); ++ memcpy(&if_event->event, ifevent, sizeof(if_event->event)); ++ memcpy(if_event->mac, mac, ETHER_ADDR_LEN); ++ strncpy(if_event->name, name, IFNAMSIZ); ++ if_event->name[IFNAMSIZ - 1] = '\0'; ++ dhd_deferred_schedule_work(dhdinfo->dhd_deferred_wq, (void *)if_event, DHD_WQ_WORK_IF_DEL, ++ dhd_ifdel_event_handler, DHD_WORK_PRIORITY_LOW); ++ ++ return BCME_OK; ++} ++ ++/* unregister and free the existing net_device interface (if any) in iflist and ++ * allocate a new one. the slot is reused. this function does NOT register the ++ * new interface to linux kernel. dhd_register_if does the job ++ */ ++struct net_device* ++dhd_allocate_if(dhd_pub_t *dhdpub, int ifidx, char *name, ++ uint8 *mac, uint8 bssidx, bool need_rtnl_lock) ++{ ++ dhd_info_t *dhdinfo = (dhd_info_t *)dhdpub->info; ++ dhd_if_t *ifp; ++ ++ ASSERT(dhdinfo && (ifidx < DHD_MAX_IFS)); ++ ifp = dhdinfo->iflist[ifidx]; ++ ++ if (ifp != NULL) { ++ if (ifp->net != NULL) { ++ DHD_ERROR(("%s: free existing IF %s\n", __FUNCTION__, ifp->net->name)); ++ ++ dhd_dev_priv_clear(ifp->net); /* clear net_device private */ ++ ++ /* in unregister_netdev case, the interface gets freed by net->destructor ++ * (which is set to free_netdev) ++ */ ++ if (ifp->net->reg_state == NETREG_UNINITIALIZED) { ++ free_netdev(ifp->net); ++ } else { ++ netif_stop_queue(ifp->net); ++ if (need_rtnl_lock) ++ unregister_netdev(ifp->net); ++ else ++ unregister_netdevice(ifp->net); ++ } ++ ifp->net = NULL; ++ } ++ } else { ++ ifp = MALLOC(dhdinfo->pub.osh, sizeof(dhd_if_t)); ++ if (ifp == NULL) { ++ DHD_ERROR(("%s: OOM - dhd_if_t(%zu)\n", __FUNCTION__, sizeof(dhd_if_t))); ++ return NULL; ++ } ++ } ++ ++ memset(ifp, 0, sizeof(dhd_if_t)); ++ ifp->info = dhdinfo; ++ ifp->idx = ifidx; ++ ifp->bssidx = bssidx; ++ if (mac != NULL) ++ memcpy(&ifp->mac_addr, mac, ETHER_ADDR_LEN); ++ ++ /* Allocate etherdev, including space for private structure */ ++ ifp->net = alloc_etherdev(DHD_DEV_PRIV_SIZE); ++ if (ifp->net == NULL) { ++ DHD_ERROR(("%s: OOM - alloc_etherdev(%zu)\n", __FUNCTION__, sizeof(dhdinfo))); ++ goto fail; ++ } ++ ++ /* Setup the dhd interface's netdevice private structure. */ ++ dhd_dev_priv_save(ifp->net, dhdinfo, ifp, ifidx); ++ ++ if (name && name[0]) { ++ strncpy(ifp->net->name, name, IFNAMSIZ); ++ ifp->net->name[IFNAMSIZ - 1] = '\0'; ++ } ++#ifdef WL_CFG80211 ++ if (ifidx == 0) ++ ifp->net->destructor = free_netdev; ++ else ++ ifp->net->destructor = dhd_netdev_free; ++#else ++ ifp->net->destructor = free_netdev; ++#endif /* WL_CFG80211 */ ++ strncpy(ifp->name, ifp->net->name, IFNAMSIZ); ++ ifp->name[IFNAMSIZ - 1] = '\0'; ++ dhdinfo->iflist[ifidx] = ifp; ++ ++#ifdef PCIE_FULL_DONGLE ++ /* Initialize STA info list */ ++ INIT_LIST_HEAD(&ifp->sta_list); ++ DHD_IF_STA_LIST_LOCK_INIT(ifp); ++#endif /* PCIE_FULL_DONGLE */ ++ ++ return ifp->net; ++ ++fail: ++ if (ifp != NULL) { ++ if (ifp->net != NULL) { ++ dhd_dev_priv_clear(ifp->net); ++ free_netdev(ifp->net); ++ ifp->net = NULL; ++ } ++ MFREE(dhdinfo->pub.osh, ifp, sizeof(*ifp)); ++ ifp = NULL; ++ } ++ dhdinfo->iflist[ifidx] = NULL; ++ return NULL; ++} ++ ++/* unregister and free the the net_device interface associated with the indexed ++ * slot, also free the slot memory and set the slot pointer to NULL ++ */ ++int ++dhd_remove_if(dhd_pub_t *dhdpub, int ifidx, bool need_rtnl_lock) ++{ ++ dhd_info_t *dhdinfo = (dhd_info_t *)dhdpub->info; ++ dhd_if_t *ifp; ++ ++ ifp = dhdinfo->iflist[ifidx]; ++ if (ifp != NULL) { ++ if (ifp->net != NULL) { ++ DHD_ERROR(("deleting interface '%s' idx %d\n", ifp->net->name, ifp->idx)); ++ ++ /* in unregister_netdev case, the interface gets freed by net->destructor ++ * (which is set to free_netdev) ++ */ ++ if (ifp->net->reg_state == NETREG_UNINITIALIZED) { ++ free_netdev(ifp->net); ++ } else { ++ netif_stop_queue(ifp->net); ++ ++ ++ ++#ifdef SET_RPS_CPUS ++ custom_rps_map_clear(ifp->net->_rx); ++#endif /* SET_RPS_CPUS */ ++ if (need_rtnl_lock) ++ unregister_netdev(ifp->net); ++ else ++ unregister_netdevice(ifp->net); ++ } ++ ifp->net = NULL; ++ } ++#ifdef DHD_WMF ++ dhd_wmf_cleanup(dhdpub, ifidx); ++#endif /* DHD_WMF */ ++ ++ dhd_if_del_sta_list(ifp); ++ ++ dhdinfo->iflist[ifidx] = NULL; ++ MFREE(dhdinfo->pub.osh, ifp, sizeof(*ifp)); ++ ++ } ++ ++ return BCME_OK; ++} ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)) ++static struct net_device_ops dhd_ops_pri = { ++ .ndo_open = dhd_open, ++ .ndo_stop = dhd_stop, ++ .ndo_get_stats = dhd_get_stats, ++ .ndo_do_ioctl = dhd_ioctl_entry, ++ .ndo_start_xmit = dhd_start_xmit, ++ .ndo_set_mac_address = dhd_set_mac_address, ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)) ++ .ndo_set_rx_mode = dhd_set_multicast_list, ++#else ++ .ndo_set_multicast_list = dhd_set_multicast_list, ++#endif ++}; ++ ++static struct net_device_ops dhd_ops_virt = { ++ .ndo_get_stats = dhd_get_stats, ++ .ndo_do_ioctl = dhd_ioctl_entry, ++ .ndo_start_xmit = dhd_start_xmit, ++ .ndo_set_mac_address = dhd_set_mac_address, ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)) ++ .ndo_set_rx_mode = dhd_set_multicast_list, ++#else ++ .ndo_set_multicast_list = dhd_set_multicast_list, ++#endif ++}; ++ ++#ifdef P2PONEINT ++extern int wl_cfgp2p_if_open(struct net_device *net); ++extern int wl_cfgp2p_if_stop(struct net_device *net); ++ ++static struct net_device_ops dhd_cfgp2p_ops_virt = { ++ .ndo_open = wl_cfgp2p_if_open, ++ .ndo_stop = wl_cfgp2p_if_stop, ++ .ndo_get_stats = dhd_get_stats, ++ .ndo_do_ioctl = dhd_ioctl_entry, ++ .ndo_start_xmit = dhd_start_xmit, ++ .ndo_set_mac_address = dhd_set_mac_address, ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)) ++ .ndo_set_rx_mode = dhd_set_multicast_list, ++#else ++ .ndo_set_multicast_list = dhd_set_multicast_list, ++#endif ++}; ++#endif /* P2PONEINT */ ++#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)) */ ++ ++#ifdef DEBUGGER ++extern void debugger_init(void *bus_handle); ++#endif ++ ++ ++#ifdef SHOW_LOGTRACE ++static char *logstrs_path = "/root/logstrs.bin"; ++module_param(logstrs_path, charp, S_IRUGO); ++ ++int ++dhd_init_logstrs_array(dhd_event_log_t *temp) ++{ ++ struct file *filep = NULL; ++ struct kstat stat; ++ mm_segment_t fs; ++ char *raw_fmts = NULL; ++ int logstrs_size = 0; ++ ++ logstr_header_t *hdr = NULL; ++ uint32 *lognums = NULL; ++ char *logstrs = NULL; ++ int ram_index = 0; ++ char **fmts; ++ int num_fmts = 0; ++ uint32 i = 0; ++ int error = 0; ++ set_fs(KERNEL_DS); ++ fs = get_fs(); ++ filep = filp_open(logstrs_path, O_RDONLY, 0); ++ if (IS_ERR(filep)) { ++ DHD_ERROR(("Failed to open the file logstrs.bin in %s\n", __FUNCTION__)); ++ goto fail; ++ } ++ error = vfs_stat(logstrs_path, &stat); ++ if (error) { ++ DHD_ERROR(("Failed in %s to find file stat\n", __FUNCTION__)); ++ goto fail; ++ } ++ logstrs_size = (int) stat.size; ++ ++ raw_fmts = kmalloc(logstrs_size, GFP_KERNEL); ++ if (raw_fmts == NULL) { ++ DHD_ERROR(("Failed to allocate raw_fmts memory\n")); ++ goto fail; ++ } ++ if (vfs_read(filep, raw_fmts, logstrs_size, &filep->f_pos) != logstrs_size) { ++ DHD_ERROR(("Error: Log strings file read failed\n")); ++ goto fail; ++ } ++ ++ /* Remember header from the logstrs.bin file */ ++ hdr = (logstr_header_t *) (raw_fmts + logstrs_size - ++ sizeof(logstr_header_t)); ++ ++ if (hdr->log_magic == LOGSTRS_MAGIC) { ++ /* ++ * logstrs.bin start with header. ++ */ ++ num_fmts = hdr->rom_logstrs_offset / sizeof(uint32); ++ ram_index = (hdr->ram_lognums_offset - ++ hdr->rom_lognums_offset) / sizeof(uint32); ++ lognums = (uint32 *) &raw_fmts[hdr->rom_lognums_offset]; ++ logstrs = (char *) &raw_fmts[hdr->rom_logstrs_offset]; ++ } else { ++ /* ++ * Legacy logstrs.bin format without header. ++ */ ++ num_fmts = *((uint32 *) (raw_fmts)) / sizeof(uint32); ++ if (num_fmts == 0) { ++ /* Legacy ROM/RAM logstrs.bin format: ++ * - ROM 'lognums' section ++ * - RAM 'lognums' section ++ * - ROM 'logstrs' section. ++ * - RAM 'logstrs' section. ++ * ++ * 'lognums' is an array of indexes for the strings in the ++ * 'logstrs' section. The first uint32 is 0 (index of first ++ * string in ROM 'logstrs' section). ++ * ++ * The 4324b5 is the only ROM that uses this legacy format. Use the ++ * fixed number of ROM fmtnums to find the start of the RAM ++ * 'lognums' section. Use the fixed first ROM string ("Con\n") to ++ * find the ROM 'logstrs' section. ++ */ ++ #define NUM_4324B5_ROM_FMTS 186 ++ #define FIRST_4324B5_ROM_LOGSTR "Con\n" ++ ram_index = NUM_4324B5_ROM_FMTS; ++ lognums = (uint32 *) raw_fmts; ++ num_fmts = ram_index; ++ logstrs = (char *) &raw_fmts[num_fmts << 2]; ++ while (strncmp(FIRST_4324B5_ROM_LOGSTR, logstrs, 4)) { ++ num_fmts++; ++ logstrs = (char *) &raw_fmts[num_fmts << 2]; ++ } ++ } else { ++ /* Legacy RAM-only logstrs.bin format: ++ * - RAM 'lognums' section ++ * - RAM 'logstrs' section. ++ * ++ * 'lognums' is an array of indexes for the strings in the ++ * 'logstrs' section. The first uint32 is an index to the ++ * start of 'logstrs'. Therefore, if this index is divided ++ * by 'sizeof(uint32)' it provides the number of logstr ++ * entries. ++ */ ++ ram_index = 0; ++ lognums = (uint32 *) raw_fmts; ++ logstrs = (char *) &raw_fmts[num_fmts << 2]; ++ } ++ } ++ fmts = kmalloc(num_fmts * sizeof(char *), GFP_KERNEL); ++ if (fmts == NULL) { ++ DHD_ERROR(("Failed to allocate fmts memory\n")); ++ goto fail; ++ } ++ ++ for (i = 0; i < num_fmts; i++) { ++ /* ROM lognums index into logstrs using 'rom_logstrs_offset' as a base ++ * (they are 0-indexed relative to 'rom_logstrs_offset'). ++ * ++ * RAM lognums are already indexed to point to the correct RAM logstrs (they ++ * are 0-indexed relative to the start of the logstrs.bin file). ++ */ ++ if (i == ram_index) { ++ logstrs = raw_fmts; ++ } ++ fmts[i] = &logstrs[lognums[i]]; ++ } ++ temp->fmts = fmts; ++ temp->raw_fmts = raw_fmts; ++ temp->num_fmts = num_fmts; ++ filp_close(filep, NULL); ++ set_fs(fs); ++ return 0; ++fail: ++ if (raw_fmts) { ++ kfree(raw_fmts); ++ raw_fmts = NULL; ++ } ++ if (!IS_ERR(filep)) ++ filp_close(filep, NULL); ++ set_fs(fs); ++ temp->fmts = NULL; ++ return -1; ++} ++#endif /* SHOW_LOGTRACE */ ++ ++ ++dhd_pub_t * ++dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) ++{ ++ dhd_info_t *dhd = NULL; ++ struct net_device *net = NULL; ++ char if_name[IFNAMSIZ] = {'\0'}; ++ uint32 bus_type = -1; ++ uint32 bus_num = -1; ++ uint32 slot_num = -1; ++ wifi_adapter_info_t *adapter = NULL; ++ ++ dhd_attach_states_t dhd_state = DHD_ATTACH_STATE_INIT; ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++ /* will implement get_ids for DBUS later */ ++#if defined(BCMSDIO) ++ dhd_bus_get_ids(bus, &bus_type, &bus_num, &slot_num); ++#endif ++ adapter = dhd_wifi_platform_get_adapter(bus_type, bus_num, slot_num); ++ ++ /* Allocate primary dhd_info */ ++ dhd = wifi_platform_prealloc(adapter, DHD_PREALLOC_DHD_INFO, sizeof(dhd_info_t)); ++ if (dhd == NULL) { ++ dhd = MALLOC(osh, sizeof(dhd_info_t)); ++ if (dhd == NULL) { ++ DHD_ERROR(("%s: OOM - alloc dhd_info\n", __FUNCTION__)); ++ goto fail; ++ } ++ } ++ memset(dhd, 0, sizeof(dhd_info_t)); ++ dhd_state |= DHD_ATTACH_STATE_DHD_ALLOC; ++ ++ dhd->unit = dhd_found + instance_base; /* do not increment dhd_found, yet */ ++ ++ dhd->pub.osh = osh; ++ dhd->adapter = adapter; ++ ++#ifdef GET_CUSTOM_MAC_ENABLE ++ wifi_platform_get_mac_addr(dhd->adapter, dhd->pub.mac.octet); ++#endif /* GET_CUSTOM_MAC_ENABLE */ ++ dhd->thr_dpc_ctl.thr_pid = DHD_PID_KT_TL_INVALID; ++ dhd->thr_wdt_ctl.thr_pid = DHD_PID_KT_INVALID; ++ ++ /* Initialize thread based operation and lock */ ++ sema_init(&dhd->sdsem, 1); ++ ++ /* Link to info module */ ++ dhd->pub.info = dhd; ++ ++ ++ /* Link to bus module */ ++ dhd->pub.bus = bus; ++ dhd->pub.hdrlen = bus_hdrlen; ++ ++ /* dhd_conf must be attached after linking dhd to dhd->pub.info, ++ * because dhd_detech will check .info is NULL or not. ++ */ ++ if (dhd_conf_attach(&dhd->pub) != 0) { ++ DHD_ERROR(("dhd_conf_attach failed\n")); ++ goto fail; ++ } ++ dhd_conf_reset(&dhd->pub); ++ dhd_conf_set_chiprev(&dhd->pub, dhd_bus_chip(bus), dhd_bus_chiprev(bus)); ++ dhd_conf_preinit(&dhd->pub); ++ ++ /* Some DHD modules (e.g. cfg80211) configures operation mode based on firmware name. ++ * This is indeed a hack but we have to make it work properly before we have a better ++ * solution ++ */ ++ dhd_update_fw_nv_path(dhd); ++#ifndef BUILD_IN_KERNEL ++ dhd_conf_read_config(&dhd->pub, dhd->conf_path); ++#endif ++ ++ /* Set network interface name if it was provided as module parameter */ ++ if (iface_name[0]) { ++ int len; ++ char ch; ++ strncpy(if_name, iface_name, IFNAMSIZ); ++ if_name[IFNAMSIZ - 1] = 0; ++ len = strlen(if_name); ++ ch = if_name[len - 1]; ++ if ((ch > '9' || ch < '0') && (len < IFNAMSIZ - 2)) ++ strcat(if_name, "%d"); ++ } ++ net = dhd_allocate_if(&dhd->pub, 0, if_name, NULL, 0, TRUE); ++ if (net == NULL) ++ goto fail; ++ dhd_state |= DHD_ATTACH_STATE_ADD_IF; ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)) ++ net->open = NULL; ++#else ++ net->netdev_ops = NULL; ++#endif ++ ++ sema_init(&dhd->proto_sem, 1); ++ ++#ifdef PROP_TXSTATUS ++ spin_lock_init(&dhd->wlfc_spinlock); ++ ++ dhd->pub.skip_fc = dhd_wlfc_skip_fc; ++ dhd->pub.plat_init = dhd_wlfc_plat_init; ++ dhd->pub.plat_deinit = dhd_wlfc_plat_deinit; ++#endif /* PROP_TXSTATUS */ ++ ++ /* Initialize other structure content */ ++ init_waitqueue_head(&dhd->ioctl_resp_wait); ++ init_waitqueue_head(&dhd->ctrl_wait); ++ ++ /* Initialize the spinlocks */ ++ spin_lock_init(&dhd->sdlock); ++ spin_lock_init(&dhd->txqlock); ++ spin_lock_init(&dhd->dhd_lock); ++ spin_lock_init(&dhd->rxf_lock); ++#if defined(RXFRAME_THREAD) ++ dhd->rxthread_enabled = TRUE; ++#endif /* defined(RXFRAME_THREAD) */ ++ ++#ifdef DHDTCPACK_SUPPRESS ++ spin_lock_init(&dhd->tcpack_lock); ++#endif /* DHDTCPACK_SUPPRESS */ ++ ++ /* Initialize Wakelock stuff */ ++ spin_lock_init(&dhd->wakelock_spinlock); ++ dhd->wakelock_counter = 0; ++ dhd->wakelock_wd_counter = 0; ++ dhd->wakelock_rx_timeout_enable = 0; ++ dhd->wakelock_ctrl_timeout_enable = 0; ++#ifdef CONFIG_HAS_WAKELOCK ++ wake_lock_init(&dhd->wl_wifi, WAKE_LOCK_SUSPEND, "wlan_wake"); ++ wake_lock_init(&dhd->wl_rxwake, WAKE_LOCK_SUSPEND, "wlan_rx_wake"); ++ wake_lock_init(&dhd->wl_ctrlwake, WAKE_LOCK_SUSPEND, "wlan_ctrl_wake"); ++ wake_lock_init(&dhd->wl_wdwake, WAKE_LOCK_SUSPEND, "wlan_wd_wake"); ++#ifdef BCMPCIE_OOB_HOST_WAKE ++ wake_lock_init(&dhd->wl_intrwake, WAKE_LOCK_SUSPEND, "wlan_oob_irq_wake"); ++#endif /* BCMPCIE_OOB_HOST_WAKE */ ++#endif /* CONFIG_HAS_WAKELOCK */ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1 ++ mutex_init(&dhd->dhd_net_if_mutex); ++ mutex_init(&dhd->dhd_suspend_mutex); ++#endif ++ dhd_state |= DHD_ATTACH_STATE_WAKELOCKS_INIT; ++ ++ /* Attach and link in the protocol */ ++ if (dhd_prot_attach(&dhd->pub) != 0) { ++ DHD_ERROR(("dhd_prot_attach failed\n")); ++ goto fail; ++ } ++ dhd_state |= DHD_ATTACH_STATE_PROT_ATTACH; ++ ++#ifdef WL_CFG80211 ++ /* Attach and link in the cfg80211 */ ++ if (unlikely(wl_cfg80211_attach(net, &dhd->pub))) { ++ DHD_ERROR(("wl_cfg80211_attach failed\n")); ++ goto fail; ++ } ++ ++ dhd_monitor_init(&dhd->pub); ++ dhd_state |= DHD_ATTACH_STATE_CFG80211; ++#endif ++#if defined(WL_WIRELESS_EXT) ++ /* Attach and link in the iw */ ++ if (!(dhd_state & DHD_ATTACH_STATE_CFG80211)) { ++ if (wl_iw_attach(net, (void *)&dhd->pub) != 0) { ++ DHD_ERROR(("wl_iw_attach failed\n")); ++ goto fail; ++ } ++ dhd_state |= DHD_ATTACH_STATE_WL_ATTACH; ++ } ++#endif /* defined(WL_WIRELESS_EXT) */ ++ ++#ifdef SHOW_LOGTRACE ++ dhd_init_logstrs_array(&dhd->event_data); ++#endif /* SHOW_LOGTRACE */ ++ ++ if (dhd_sta_pool_init(&dhd->pub, DHD_MAX_STA) != BCME_OK) { ++ DHD_ERROR(("%s: Initializing %u sta\n", __FUNCTION__, DHD_MAX_STA)); ++ goto fail; ++ } ++ ++ ++ /* Set up the watchdog timer */ ++ init_timer(&dhd->timer); ++ dhd->timer.data = (ulong)dhd; ++ dhd->timer.function = dhd_watchdog; ++ dhd->default_wd_interval = dhd_watchdog_ms; ++ ++ if (dhd_watchdog_prio >= 0) { ++ /* Initialize watchdog thread */ ++ PROC_START(dhd_watchdog_thread, dhd, &dhd->thr_wdt_ctl, 0, "dhd_watchdog_thread"); ++ ++ } else { ++ dhd->thr_wdt_ctl.thr_pid = -1; ++ } ++ ++#ifdef DEBUGGER ++ debugger_init((void *) bus); ++#endif ++ ++ /* Set up the bottom half handler */ ++ if (dhd_dpc_prio >= 0) { ++ /* Initialize DPC thread */ ++ PROC_START(dhd_dpc_thread, dhd, &dhd->thr_dpc_ctl, 0, "dhd_dpc"); ++ } else { ++ /* use tasklet for dpc */ ++ tasklet_init(&dhd->tasklet, dhd_dpc, (ulong)dhd); ++ dhd->thr_dpc_ctl.thr_pid = -1; ++ } ++ ++ if (dhd->rxthread_enabled) { ++ bzero(&dhd->pub.skbbuf[0], sizeof(void *) * MAXSKBPEND); ++ /* Initialize RXF thread */ ++ PROC_START(dhd_rxf_thread, dhd, &dhd->thr_rxf_ctl, 0, "dhd_rxf"); ++ } ++ ++ dhd_state |= DHD_ATTACH_STATE_THREADS_CREATED; ++ ++#if defined(CONFIG_PM_SLEEP) ++ if (!dhd_pm_notifier_registered) { ++ dhd_pm_notifier_registered = TRUE; ++ register_pm_notifier(&dhd_pm_notifier); ++ } ++#endif /* CONFIG_PM_SLEEP */ ++ ++#if defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND) ++ dhd->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 20; ++ dhd->early_suspend.suspend = dhd_early_suspend; ++ dhd->early_suspend.resume = dhd_late_resume; ++ register_early_suspend(&dhd->early_suspend); ++ dhd_state |= DHD_ATTACH_STATE_EARLYSUSPEND_DONE; ++#endif /* CONFIG_HAS_EARLYSUSPEND && DHD_USE_EARLYSUSPEND */ ++ ++#ifdef ARP_OFFLOAD_SUPPORT ++ dhd->pend_ipaddr = 0; ++ if (!dhd_inetaddr_notifier_registered) { ++ dhd_inetaddr_notifier_registered = TRUE; ++ register_inetaddr_notifier(&dhd_inetaddr_notifier); ++ } ++#endif /* ARP_OFFLOAD_SUPPORT */ ++#ifdef CONFIG_IPV6 ++ if (!dhd_inet6addr_notifier_registered) { ++ dhd_inet6addr_notifier_registered = TRUE; ++ register_inet6addr_notifier(&dhd_inet6addr_notifier); ++ } ++#endif ++ dhd->dhd_deferred_wq = dhd_deferred_work_init((void *)dhd); ++#ifdef DEBUG_CPU_FREQ ++ dhd->new_freq = alloc_percpu(int); ++ dhd->freq_trans.notifier_call = dhd_cpufreq_notifier; ++ cpufreq_register_notifier(&dhd->freq_trans, CPUFREQ_TRANSITION_NOTIFIER); ++#endif ++#ifdef DHDTCPACK_SUPPRESS ++#ifdef BCMSDIO ++ dhd_tcpack_suppress_set(&dhd->pub, TCPACK_SUP_DELAYTX); ++#elif defined(BCMPCIE) ++ dhd_tcpack_suppress_set(&dhd->pub, TCPACK_SUP_HOLD); ++#else ++ dhd_tcpack_suppress_set(&dhd->pub, TCPACK_SUP_OFF); ++#endif /* BCMSDIO */ ++#endif /* DHDTCPACK_SUPPRESS */ ++ ++ dhd_state |= DHD_ATTACH_STATE_DONE; ++ dhd->dhd_state = dhd_state; ++ ++ dhd_found++; ++#if defined(CUSTOMER_HW20) && defined(WLANAUDIO) ++ dhd_global = dhd; ++#endif /* CUSTOMER_HW20 && WLANAUDIO */ ++ return &dhd->pub; ++ ++fail: ++ if (dhd_state >= DHD_ATTACH_STATE_DHD_ALLOC) { ++ DHD_TRACE(("%s: Calling dhd_detach dhd_state 0x%x &dhd->pub %p\n", ++ __FUNCTION__, dhd_state, &dhd->pub)); ++ dhd->dhd_state = dhd_state; ++ dhd_detach(&dhd->pub); ++ dhd_free(&dhd->pub); ++ } ++ ++ return NULL; ++} ++ ++int dhd_get_fw_mode(dhd_info_t *dhdinfo) ++{ ++ if (strstr(dhdinfo->fw_path, "_apsta") != NULL) ++ return DHD_FLAG_HOSTAP_MODE; ++ if (strstr(dhdinfo->fw_path, "_p2p") != NULL) ++ return DHD_FLAG_P2P_MODE; ++ if (strstr(dhdinfo->fw_path, "_ibss") != NULL) ++ return DHD_FLAG_IBSS_MODE; ++ if (strstr(dhdinfo->fw_path, "_mfg") != NULL) ++ return DHD_FLAG_MFG_MODE; ++ ++ return DHD_FLAG_STA_MODE; ++} ++ ++bool dhd_update_fw_nv_path(dhd_info_t *dhdinfo) ++{ ++ int fw_len; ++ int nv_len; ++ int conf_len; ++ const char *fw = NULL; ++ const char *nv = NULL; ++ const char *conf = NULL; ++ wifi_adapter_info_t *adapter = dhdinfo->adapter; ++ ++ ++ /* Update firmware and nvram path. The path may be from adapter info or module parameter ++ * The path from adapter info is used for initialization only (as it won't change). ++ * ++ * The firmware_path/nvram_path module parameter may be changed by the system at run ++ * time. When it changes we need to copy it to dhdinfo->fw_path. Also Android private ++ * command may change dhdinfo->fw_path. As such we need to clear the path info in ++ * module parameter after it is copied. We won't update the path until the module parameter ++ * is changed again (first character is not '\0') ++ */ ++ ++ /* set default firmware and nvram path for built-in type driver */ ++// if (!dhd_download_fw_on_driverload) { ++#ifdef CONFIG_BCMDHD_FW_PATH ++ fw = CONFIG_BCMDHD_FW_PATH; ++#endif /* CONFIG_BCMDHD_FW_PATH */ ++#ifdef CONFIG_BCMDHD_NVRAM_PATH ++ nv = CONFIG_BCMDHD_NVRAM_PATH; ++#endif /* CONFIG_BCMDHD_NVRAM_PATH */ ++// } ++ ++ /* check if we need to initialize the path */ ++ if (dhdinfo->fw_path[0] == '\0') { ++ if (adapter && adapter->fw_path && adapter->fw_path[0] != '\0') ++ fw = adapter->fw_path; ++ ++ } ++ if (dhdinfo->nv_path[0] == '\0') { ++ if (adapter && adapter->nv_path && adapter->nv_path[0] != '\0') ++ nv = adapter->nv_path; ++ } ++ if (dhdinfo->conf_path[0] == '\0') { ++ if (adapter && adapter->conf_path && adapter->conf_path[0] != '\0') ++ conf = adapter->conf_path; ++ } ++ ++ /* Use module parameter if it is valid, EVEN IF the path has not been initialized ++ * ++ * TODO: need a solution for multi-chip, can't use the same firmware for all chips ++ */ ++ if (firmware_path[0] != '\0') ++ fw = firmware_path; ++ if (nvram_path[0] != '\0') ++ nv = nvram_path; ++ if (config_path[0] != '\0') ++ conf = config_path; ++ ++ if (fw && fw[0] != '\0') { ++ fw_len = strlen(fw); ++ if (fw_len >= sizeof(dhdinfo->fw_path)) { ++ DHD_ERROR(("fw path len exceeds max len of dhdinfo->fw_path\n")); ++ return FALSE; ++ } ++ strncpy(dhdinfo->fw_path, fw, sizeof(dhdinfo->fw_path)); ++ if (dhdinfo->fw_path[fw_len-1] == '\n') ++ dhdinfo->fw_path[fw_len-1] = '\0'; ++ } ++ if (nv && nv[0] != '\0') { ++ nv_len = strlen(nv); ++ if (nv_len >= sizeof(dhdinfo->nv_path)) { ++ DHD_ERROR(("nvram path len exceeds max len of dhdinfo->nv_path\n")); ++ return FALSE; ++ } ++ strncpy(dhdinfo->nv_path, nv, sizeof(dhdinfo->nv_path)); ++ if (dhdinfo->nv_path[nv_len-1] == '\n') ++ dhdinfo->nv_path[nv_len-1] = '\0'; ++ } ++ if (conf && conf[0] != '\0') { ++ conf_len = strlen(conf); ++ if (conf_len >= sizeof(dhdinfo->conf_path)) { ++ DHD_ERROR(("config path len exceeds max len of dhdinfo->conf_path\n")); ++ return FALSE; ++ } ++ strncpy(dhdinfo->conf_path, conf, sizeof(dhdinfo->conf_path)); ++ if (dhdinfo->conf_path[conf_len-1] == '\n') ++ dhdinfo->conf_path[conf_len-1] = '\0'; ++ } ++ ++#if 0 ++ /* clear the path in module parameter */ ++ firmware_path[0] = '\0'; ++ nvram_path[0] = '\0'; ++ config_path[0] = '\0'; ++#endif ++ ++#ifndef BCMEMBEDIMAGE ++ /* fw_path and nv_path are not mandatory for BCMEMBEDIMAGE */ ++ if (dhdinfo->fw_path[0] == '\0') { ++ DHD_ERROR(("firmware path not found\n")); ++ return FALSE; ++ } ++ if (dhdinfo->nv_path[0] == '\0') { ++ DHD_ERROR(("nvram path not found\n")); ++ return FALSE; ++ } ++ if (dhdinfo->conf_path[0] == '\0') { ++ dhd_conf_set_conf_path_by_nv_path(&dhdinfo->pub, dhdinfo->conf_path, dhdinfo->nv_path); ++ } ++#ifdef CONFIG_PATH_AUTO_SELECT ++ dhd_conf_set_conf_name_by_chip(&dhdinfo->pub, dhdinfo->conf_path); ++#endif ++#endif /* BCMEMBEDIMAGE */ ++ ++ return TRUE; ++} ++ ++ ++int ++dhd_bus_start(dhd_pub_t *dhdp) ++{ ++ int ret = -1; ++ dhd_info_t *dhd = (dhd_info_t*)dhdp->info; ++ unsigned long flags; ++ ++ ASSERT(dhd); ++ ++ DHD_TRACE(("Enter %s:\n", __FUNCTION__)); ++ ++ DHD_PERIM_LOCK(dhdp); ++ ++ /* try to download image and nvram to the dongle */ ++ if (dhd->pub.busstate == DHD_BUS_DOWN && dhd_update_fw_nv_path(dhd)) { ++ DHD_INFO(("%s download fw %s, nv %s, conf %s\n", ++ __FUNCTION__, dhd->fw_path, dhd->nv_path, dhd->conf_path)); ++ ret = dhd_bus_download_firmware(dhd->pub.bus, dhd->pub.osh, ++ dhd->fw_path, dhd->nv_path, dhd->conf_path); ++ if (ret < 0) { ++ DHD_ERROR(("%s: failed to download firmware %s\n", ++ __FUNCTION__, dhd->fw_path)); ++ DHD_PERIM_UNLOCK(dhdp); ++ return ret; ++ } ++ } ++ if (dhd->pub.busstate != DHD_BUS_LOAD) { ++ DHD_PERIM_UNLOCK(dhdp); ++ return -ENETDOWN; ++ } ++ ++ dhd_os_sdlock(dhdp); ++ ++ /* Start the watchdog timer */ ++ dhd->pub.tickcnt = 0; ++ dhd_os_wd_timer(&dhd->pub, dhd_watchdog_ms); ++ ++ /* Bring up the bus */ ++ if ((ret = dhd_bus_init(&dhd->pub, FALSE)) != 0) { ++ ++ DHD_ERROR(("%s, dhd_bus_init failed %d\n", __FUNCTION__, ret)); ++ dhd_os_sdunlock(dhdp); ++ DHD_PERIM_UNLOCK(dhdp); ++ return ret; ++ } ++#if defined(OOB_INTR_ONLY) || defined(BCMPCIE_OOB_HOST_WAKE) ++#if defined(BCMPCIE_OOB_HOST_WAKE) ++ dhd_os_sdunlock(dhdp); ++#endif /* BCMPCIE_OOB_HOST_WAKE */ ++ /* Host registration for OOB interrupt */ ++ if (dhd_bus_oob_intr_register(dhdp)) { ++ /* deactivate timer and wait for the handler to finish */ ++#if !defined(BCMPCIE_OOB_HOST_WAKE) ++ DHD_GENERAL_LOCK(&dhd->pub, flags); ++ dhd->wd_timer_valid = FALSE; ++ DHD_GENERAL_UNLOCK(&dhd->pub, flags); ++ del_timer_sync(&dhd->timer); ++ ++ dhd_os_sdunlock(dhdp); ++#endif /* BCMPCIE_OOB_HOST_WAKE */ ++ DHD_PERIM_UNLOCK(dhdp); ++ DHD_OS_WD_WAKE_UNLOCK(&dhd->pub); ++ DHD_ERROR(("%s Host failed to register for OOB\n", __FUNCTION__)); ++ return -ENODEV; ++ } ++ ++#if defined(BCMPCIE_OOB_HOST_WAKE) ++ dhd_os_sdlock(dhdp); ++ dhd_bus_oob_intr_set(dhdp, TRUE); ++#else ++ /* Enable oob at firmware */ ++ dhd_enable_oob_intr(dhd->pub.bus, TRUE); ++#endif /* BCMPCIE_OOB_HOST_WAKE */ ++#elif defined(FORCE_WOWLAN) ++ /* Enable oob at firmware */ ++ dhd_enable_oob_intr(dhd->pub.bus, TRUE); ++#endif ++#ifdef PCIE_FULL_DONGLE ++ { ++ uint8 txpush = 0; ++ uint32 num_flowrings; /* includes H2D common rings */ ++ num_flowrings = dhd_bus_max_h2d_queues(dhd->pub.bus, &txpush); ++ DHD_ERROR(("%s: Initializing %u flowrings\n", __FUNCTION__, ++ num_flowrings)); ++ if ((ret = dhd_flow_rings_init(&dhd->pub, num_flowrings)) != BCME_OK) { ++ dhd_os_sdunlock(dhdp); ++ DHD_PERIM_UNLOCK(dhdp); ++ return ret; ++ } ++ } ++#endif /* PCIE_FULL_DONGLE */ ++ ++ /* Do protocol initialization necessary for IOCTL/IOVAR */ ++ dhd_prot_init(&dhd->pub); ++ ++ /* If bus is not ready, can't come up */ ++ if (dhd->pub.busstate != DHD_BUS_DATA) { ++ DHD_GENERAL_LOCK(&dhd->pub, flags); ++ dhd->wd_timer_valid = FALSE; ++ DHD_GENERAL_UNLOCK(&dhd->pub, flags); ++ del_timer_sync(&dhd->timer); ++ DHD_ERROR(("%s failed bus is not ready\n", __FUNCTION__)); ++ dhd_os_sdunlock(dhdp); ++ DHD_PERIM_UNLOCK(dhdp); ++ DHD_OS_WD_WAKE_UNLOCK(&dhd->pub); ++ return -ENODEV; ++ } ++ ++ dhd_os_sdunlock(dhdp); ++ ++ /* Bus is ready, query any dongle information */ ++ if ((ret = dhd_sync_with_dongle(&dhd->pub)) < 0) { ++ DHD_PERIM_UNLOCK(dhdp); ++ return ret; ++ } ++ ++#ifdef ARP_OFFLOAD_SUPPORT ++ if (dhd->pend_ipaddr) { ++#ifdef AOE_IP_ALIAS_SUPPORT ++ aoe_update_host_ipv4_table(&dhd->pub, dhd->pend_ipaddr, TRUE, 0); ++#endif /* AOE_IP_ALIAS_SUPPORT */ ++ dhd->pend_ipaddr = 0; ++ } ++#endif /* ARP_OFFLOAD_SUPPORT */ ++ ++ DHD_PERIM_UNLOCK(dhdp); ++ return 0; ++} ++ ++#ifdef WLTDLS ++int _dhd_tdls_enable(dhd_pub_t *dhd, bool tdls_on, bool auto_on, struct ether_addr *mac) ++{ ++ char iovbuf[WLC_IOCTL_SMLEN]; ++ uint32 tdls = tdls_on; ++ int ret = 0; ++ uint32 tdls_auto_op = 0; ++ uint32 tdls_idle_time = CUSTOM_TDLS_IDLE_MODE_SETTING; ++ int32 tdls_rssi_high = CUSTOM_TDLS_RSSI_THRESHOLD_HIGH; ++ int32 tdls_rssi_low = CUSTOM_TDLS_RSSI_THRESHOLD_LOW; ++ BCM_REFERENCE(mac); ++ if (!FW_SUPPORTED(dhd, tdls)) ++ return BCME_ERROR; ++ ++ if (dhd->tdls_enable == tdls_on) ++ goto auto_mode; ++ bcm_mkiovar("tdls_enable", (char *)&tdls, sizeof(tdls), iovbuf, sizeof(iovbuf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) { ++ DHD_ERROR(("%s: tdls %d failed %d\n", __FUNCTION__, tdls, ret)); ++ goto exit; ++ } ++ dhd->tdls_enable = tdls_on; ++auto_mode: ++ ++ tdls_auto_op = auto_on; ++ bcm_mkiovar("tdls_auto_op", (char *)&tdls_auto_op, sizeof(tdls_auto_op), ++ iovbuf, sizeof(iovbuf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, ++ sizeof(iovbuf), TRUE, 0)) < 0) { ++ DHD_ERROR(("%s: tdls_auto_op failed %d\n", __FUNCTION__, ret)); ++ goto exit; ++ } ++ ++ if (tdls_auto_op) { ++ bcm_mkiovar("tdls_idle_time", (char *)&tdls_idle_time, ++ sizeof(tdls_idle_time), iovbuf, sizeof(iovbuf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, ++ sizeof(iovbuf), TRUE, 0)) < 0) { ++ DHD_ERROR(("%s: tdls_idle_time failed %d\n", __FUNCTION__, ret)); ++ goto exit; ++ } ++ bcm_mkiovar("tdls_rssi_high", (char *)&tdls_rssi_high, 4, iovbuf, sizeof(iovbuf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, ++ sizeof(iovbuf), TRUE, 0)) < 0) { ++ DHD_ERROR(("%s: tdls_rssi_high failed %d\n", __FUNCTION__, ret)); ++ goto exit; ++ } ++ bcm_mkiovar("tdls_rssi_low", (char *)&tdls_rssi_low, 4, iovbuf, sizeof(iovbuf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, ++ sizeof(iovbuf), TRUE, 0)) < 0) { ++ DHD_ERROR(("%s: tdls_rssi_low failed %d\n", __FUNCTION__, ret)); ++ goto exit; ++ } ++ } ++ ++exit: ++ return ret; ++} ++ ++int dhd_tdls_enable(struct net_device *dev, bool tdls_on, bool auto_on, struct ether_addr *mac) ++{ ++ dhd_info_t *dhd = DHD_DEV_INFO(dev); ++ int ret = 0; ++ if (dhd) ++ ret = _dhd_tdls_enable(&dhd->pub, tdls_on, auto_on, mac); ++ else ++ ret = BCME_ERROR; ++ return ret; ++} ++#ifdef PCIE_FULL_DONGLE ++void dhd_tdls_update_peer_info(struct net_device *dev, bool connect, uint8 *da) ++{ ++ dhd_info_t *dhd = DHD_DEV_INFO(dev); ++ dhd_pub_t *dhdp = (dhd_pub_t *)&dhd->pub; ++ tdls_peer_node_t *cur = dhdp->peer_tbl.node; ++ tdls_peer_node_t *new = NULL, *prev = NULL; ++ dhd_if_t *dhdif; ++ uint8 sa[ETHER_ADDR_LEN]; ++ int ifidx = dhd_net2idx(dhd, dev); ++ ++ if (ifidx == DHD_BAD_IF) ++ return; ++ ++ dhdif = dhd->iflist[ifidx]; ++ memcpy(sa, dhdif->mac_addr, ETHER_ADDR_LEN); ++ ++ if (connect) { ++ while (cur != NULL) { ++ if (!memcmp(da, cur->addr, ETHER_ADDR_LEN)) { ++ DHD_ERROR(("%s: TDLS Peer exist already %d\n", ++ __FUNCTION__, __LINE__)); ++ return; ++ } ++ cur = cur->next; ++ } ++ ++ new = MALLOC(dhdp->osh, sizeof(tdls_peer_node_t)); ++ if (new == NULL) { ++ DHD_ERROR(("%s: Failed to allocate memory\n", __FUNCTION__)); ++ return; ++ } ++ memcpy(new->addr, da, ETHER_ADDR_LEN); ++ new->next = dhdp->peer_tbl.node; ++ dhdp->peer_tbl.node = new; ++ dhdp->peer_tbl.tdls_peer_count++; ++ ++ } else { ++ while (cur != NULL) { ++ if (!memcmp(da, cur->addr, ETHER_ADDR_LEN)) { ++ dhd_flow_rings_delete_for_peer(dhdp, ifidx, da); ++ if (prev) ++ prev->next = cur->next; ++ else ++ dhdp->peer_tbl.node = cur->next; ++ MFREE(dhdp->osh, cur, sizeof(tdls_peer_node_t)); ++ dhdp->peer_tbl.tdls_peer_count--; ++ return; ++ } ++ prev = cur; ++ cur = cur->next; ++ } ++ DHD_ERROR(("%s: TDLS Peer Entry Not found\n", __FUNCTION__)); ++ } ++} ++#endif /* PCIE_FULL_DONGLE */ ++#endif ++ ++bool dhd_is_concurrent_mode(dhd_pub_t *dhd) ++{ ++ if (!dhd) ++ return FALSE; ++ ++ if (dhd->op_mode & DHD_FLAG_CONCURR_MULTI_CHAN_MODE) ++ return TRUE; ++ else if ((dhd->op_mode & DHD_FLAG_CONCURR_SINGLE_CHAN_MODE) == ++ DHD_FLAG_CONCURR_SINGLE_CHAN_MODE) ++ return TRUE; ++ else ++ return FALSE; ++} ++#if !defined(AP) && defined(WLP2P) ++/* From Android JerryBean release, the concurrent mode is enabled by default and the firmware ++ * name would be fw_bcmdhd.bin. So we need to determine whether P2P is enabled in the STA ++ * firmware and accordingly enable concurrent mode (Apply P2P settings). SoftAP firmware ++ * would still be named as fw_bcmdhd_apsta. ++ */ ++uint32 ++dhd_get_concurrent_capabilites(dhd_pub_t *dhd) ++{ ++ int32 ret = 0; ++ char buf[WLC_IOCTL_SMLEN]; ++ bool mchan_supported = FALSE; ++ /* if dhd->op_mode is already set for HOSTAP and Manufacturing ++ * test mode, that means we only will use the mode as it is ++ */ ++ if (dhd->op_mode & (DHD_FLAG_HOSTAP_MODE | DHD_FLAG_MFG_MODE)) ++ return 0; ++ if (FW_SUPPORTED(dhd, vsdb)) { ++ mchan_supported = TRUE; ++ } ++ if (!FW_SUPPORTED(dhd, p2p)) { ++ DHD_TRACE(("Chip does not support p2p\n")); ++ return 0; ++ } ++ else { ++ /* Chip supports p2p but ensure that p2p is really implemented in firmware or not */ ++ memset(buf, 0, sizeof(buf)); ++ bcm_mkiovar("p2p", 0, 0, buf, sizeof(buf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, buf, sizeof(buf), ++ FALSE, 0)) < 0) { ++ DHD_ERROR(("%s: Get P2P failed (error=%d)\n", __FUNCTION__, ret)); ++ return 0; ++ } ++ else { ++ if (buf[0] == 1) { ++ /* By default, chip supports single chan concurrency, ++ * now lets check for mchan ++ */ ++ ret = DHD_FLAG_CONCURR_SINGLE_CHAN_MODE; ++ if (mchan_supported) ++ ret |= DHD_FLAG_CONCURR_MULTI_CHAN_MODE; ++#if defined(WL_ENABLE_P2P_IF) || defined(WL_CFG80211_P2P_DEV_IF) ++ /* For customer_hw4, although ICS, ++ * we still support concurrent mode ++ */ ++ return ret; ++#else ++ return 0; ++#endif ++ } ++ } ++ } ++ return 0; ++} ++#endif ++ ++#ifdef SUPPORT_AP_POWERSAVE ++#define RXCHAIN_PWRSAVE_PPS 10 ++#define RXCHAIN_PWRSAVE_QUIET_TIME 10 ++#define RXCHAIN_PWRSAVE_STAS_ASSOC_CHECK 0 ++int dhd_set_ap_powersave(dhd_pub_t *dhdp, int ifidx, int enable) ++{ ++ char iovbuf[128]; ++ int32 pps = RXCHAIN_PWRSAVE_PPS; ++ int32 quiet_time = RXCHAIN_PWRSAVE_QUIET_TIME; ++ int32 stas_assoc_check = RXCHAIN_PWRSAVE_STAS_ASSOC_CHECK; ++ ++ if (enable) { ++ bcm_mkiovar("rxchain_pwrsave_enable", (char *)&enable, 4, iovbuf, sizeof(iovbuf)); ++ if (dhd_wl_ioctl_cmd(dhdp, WLC_SET_VAR, ++ iovbuf, sizeof(iovbuf), TRUE, 0) != BCME_OK) { ++ DHD_ERROR(("Failed to enable AP power save\n")); ++ } ++ bcm_mkiovar("rxchain_pwrsave_pps", (char *)&pps, 4, iovbuf, sizeof(iovbuf)); ++ if (dhd_wl_ioctl_cmd(dhdp, WLC_SET_VAR, ++ iovbuf, sizeof(iovbuf), TRUE, 0) != BCME_OK) { ++ DHD_ERROR(("Failed to set pps\n")); ++ } ++ bcm_mkiovar("rxchain_pwrsave_quiet_time", (char *)&quiet_time, ++ 4, iovbuf, sizeof(iovbuf)); ++ if (dhd_wl_ioctl_cmd(dhdp, WLC_SET_VAR, ++ iovbuf, sizeof(iovbuf), TRUE, 0) != BCME_OK) { ++ DHD_ERROR(("Failed to set quiet time\n")); ++ } ++ bcm_mkiovar("rxchain_pwrsave_stas_assoc_check", (char *)&stas_assoc_check, ++ 4, iovbuf, sizeof(iovbuf)); ++ if (dhd_wl_ioctl_cmd(dhdp, WLC_SET_VAR, ++ iovbuf, sizeof(iovbuf), TRUE, 0) != BCME_OK) { ++ DHD_ERROR(("Failed to set stas assoc check\n")); ++ } ++ } else { ++ bcm_mkiovar("rxchain_pwrsave_enable", (char *)&enable, 4, iovbuf, sizeof(iovbuf)); ++ if (dhd_wl_ioctl_cmd(dhdp, WLC_SET_VAR, ++ iovbuf, sizeof(iovbuf), TRUE, 0) != BCME_OK) { ++ DHD_ERROR(("Failed to disable AP power save\n")); ++ } ++ } ++ ++ return 0; ++} ++#endif /* SUPPORT_AP_POWERSAVE */ ++ ++ ++#if defined(READ_CONFIG_FROM_FILE) ++#include ++#include ++ ++#define strtoul(nptr, endptr, base) bcm_strtoul((nptr), (endptr), (base)) ++bool PM_control = TRUE; ++ ++static int dhd_preinit_proc(dhd_pub_t *dhd, int ifidx, char *name, char *value) ++{ ++ int var_int; ++ wl_country_t cspec = {{0}, -1, {0}}; ++ char *revstr; ++ char *endptr = NULL; ++ int iolen; ++ char smbuf[WLC_IOCTL_SMLEN*2]; ++ ++ if (!strcmp(name, "country")) { ++ revstr = strchr(value, '/'); ++ if (revstr) { ++ cspec.rev = strtoul(revstr + 1, &endptr, 10); ++ memcpy(cspec.country_abbrev, value, WLC_CNTRY_BUF_SZ); ++ cspec.country_abbrev[2] = '\0'; ++ memcpy(cspec.ccode, cspec.country_abbrev, WLC_CNTRY_BUF_SZ); ++ } else { ++ cspec.rev = -1; ++ memcpy(cspec.country_abbrev, value, WLC_CNTRY_BUF_SZ); ++ memcpy(cspec.ccode, value, WLC_CNTRY_BUF_SZ); ++ get_customized_country_code(dhd->info->adapter, ++ (char *)&cspec.country_abbrev, &cspec); ++ } ++ memset(smbuf, 0, sizeof(smbuf)); ++ DHD_ERROR(("config country code is country : %s, rev : %d !!\n", ++ cspec.country_abbrev, cspec.rev)); ++ iolen = bcm_mkiovar("country", (char*)&cspec, sizeof(cspec), ++ smbuf, sizeof(smbuf)); ++ return dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, ++ smbuf, iolen, TRUE, 0); ++ } else if (!strcmp(name, "roam_scan_period")) { ++ var_int = (int)simple_strtol(value, NULL, 0); ++ return dhd_wl_ioctl_cmd(dhd, WLC_SET_ROAM_SCAN_PERIOD, ++ &var_int, sizeof(var_int), TRUE, 0); ++ } else if (!strcmp(name, "roam_delta")) { ++ struct { ++ int val; ++ int band; ++ } x; ++ x.val = (int)simple_strtol(value, NULL, 0); ++ /* x.band = WLC_BAND_AUTO; */ ++ x.band = WLC_BAND_ALL; ++ return dhd_wl_ioctl_cmd(dhd, WLC_SET_ROAM_DELTA, &x, sizeof(x), TRUE, 0); ++ } else if (!strcmp(name, "roam_trigger")) { ++ int ret = 0; ++ ++ roam_trigger[0] = (int)simple_strtol(value, NULL, 0); ++ roam_trigger[1] = WLC_BAND_ALL; ++ ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_ROAM_TRIGGER, &roam_trigger, ++ sizeof(roam_trigger), TRUE, 0); ++ ++ return ret; ++ } else if (!strcmp(name, "PM")) { ++ int ret = 0; ++ var_int = (int)simple_strtol(value, NULL, 0); ++ ++ ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_PM, ++ &var_int, sizeof(var_int), TRUE, 0); ++ ++#if defined(CONFIG_PM_LOCK) ++ if (var_int == 0) { ++ g_pm_control = TRUE; ++ printk("%s var_int=%d don't control PM\n", __func__, var_int); ++ } else { ++ g_pm_control = FALSE; ++ printk("%s var_int=%d do control PM\n", __func__, var_int); ++ } ++#endif ++ ++ return ret; ++ } ++#ifdef WLBTAMP ++ else if (!strcmp(name, "btamp_chan")) { ++ int btamp_chan; ++ int iov_len = 0; ++ char iovbuf[128]; ++ int ret; ++ ++ btamp_chan = (int)simple_strtol(value, NULL, 0); ++ iov_len = bcm_mkiovar("btamp_chan", (char *)&btamp_chan, 4, iovbuf, sizeof(iovbuf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, iov_len, TRUE, 0) < 0)) ++ DHD_ERROR(("%s btamp_chan=%d set failed code %d\n", ++ __FUNCTION__, btamp_chan, ret)); ++ else ++ DHD_ERROR(("%s btamp_chan %d set success\n", ++ __FUNCTION__, btamp_chan)); ++ } ++#endif /* WLBTAMP */ ++ else if (!strcmp(name, "band")) { ++ int ret; ++ if (!strcmp(value, "auto")) ++ var_int = WLC_BAND_AUTO; ++ else if (!strcmp(value, "a")) ++ var_int = WLC_BAND_5G; ++ else if (!strcmp(value, "b")) ++ var_int = WLC_BAND_2G; ++ else if (!strcmp(value, "all")) ++ var_int = WLC_BAND_ALL; ++ else { ++ printk(" set band value should be one of the a or b or all\n"); ++ var_int = WLC_BAND_AUTO; ++ } ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_BAND, &var_int, ++ sizeof(var_int), TRUE, 0)) < 0) ++ printk(" set band err=%d\n", ret); ++ return ret; ++ } else if (!strcmp(name, "cur_etheraddr")) { ++ struct ether_addr ea; ++ char buf[32]; ++ uint iovlen; ++ int ret; ++ ++ bcm_ether_atoe(value, &ea); ++ ++ ret = memcmp(&ea.octet, dhd->mac.octet, ETHER_ADDR_LEN); ++ if (ret == 0) { ++ DHD_ERROR(("%s: Same Macaddr\n", __FUNCTION__)); ++ return 0; ++ } ++ ++ DHD_ERROR(("%s: Change Macaddr = %02X:%02X:%02X:%02X:%02X:%02X\n", __FUNCTION__, ++ ea.octet[0], ea.octet[1], ea.octet[2], ++ ea.octet[3], ea.octet[4], ea.octet[5])); ++ ++ iovlen = bcm_mkiovar("cur_etheraddr", (char*)&ea, ETHER_ADDR_LEN, buf, 32); ++ ++ ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, buf, iovlen, TRUE, 0); ++ if (ret < 0) { ++ DHD_ERROR(("%s: can't set MAC address , error=%d\n", __FUNCTION__, ret)); ++ return ret; ++ } ++ else { ++ memcpy(dhd->mac.octet, (void *)&ea, ETHER_ADDR_LEN); ++ return ret; ++ } ++ } else if (!strcmp(name, "lpc")) { ++ int ret = 0; ++ char buf[32]; ++ uint iovlen; ++ var_int = (int)simple_strtol(value, NULL, 0); ++ if (dhd_wl_ioctl_cmd(dhd, WLC_DOWN, NULL, 0, TRUE, 0) < 0) { ++ DHD_ERROR(("%s: wl down failed\n", __FUNCTION__)); ++ } ++ iovlen = bcm_mkiovar("lpc", (char *)&var_int, 4, buf, sizeof(buf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, buf, iovlen, TRUE, 0)) < 0) { ++ DHD_ERROR(("%s Set lpc failed %d\n", __FUNCTION__, ret)); ++ } ++ if (dhd_wl_ioctl_cmd(dhd, WLC_UP, NULL, 0, TRUE, 0) < 0) { ++ DHD_ERROR(("%s: wl up failed\n", __FUNCTION__)); ++ } ++ return ret; ++ } else if (!strcmp(name, "vht_features")) { ++ int ret = 0; ++ char buf[32]; ++ uint iovlen; ++ var_int = (int)simple_strtol(value, NULL, 0); ++ ++ if (dhd_wl_ioctl_cmd(dhd, WLC_DOWN, NULL, 0, TRUE, 0) < 0) { ++ DHD_ERROR(("%s: wl down failed\n", __FUNCTION__)); ++ } ++ iovlen = bcm_mkiovar("vht_features", (char *)&var_int, 4, buf, sizeof(buf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, buf, iovlen, TRUE, 0)) < 0) { ++ DHD_ERROR(("%s Set vht_features failed %d\n", __FUNCTION__, ret)); ++ } ++ if (dhd_wl_ioctl_cmd(dhd, WLC_UP, NULL, 0, TRUE, 0) < 0) { ++ DHD_ERROR(("%s: wl up failed\n", __FUNCTION__)); ++ } ++ return ret; ++ } else { ++ uint iovlen; ++ char iovbuf[WLC_IOCTL_SMLEN]; ++ ++ /* wlu_iovar_setint */ ++ var_int = (int)simple_strtol(value, NULL, 0); ++ ++ /* Setup timeout bcn_timeout from dhd driver 4.217.48 */ ++ if (!strcmp(name, "roam_off")) { ++ /* Setup timeout if Beacons are lost to report link down */ ++ if (var_int) { ++ uint bcn_timeout = 2; ++ bcm_mkiovar("bcn_timeout", (char *)&bcn_timeout, 4, ++ iovbuf, sizeof(iovbuf)); ++ dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); ++ } ++ } ++ /* Setup timeout bcm_timeout from dhd driver 4.217.48 */ ++ ++ DHD_INFO(("%s:[%s]=[%d]\n", __FUNCTION__, name, var_int)); ++ ++ iovlen = bcm_mkiovar(name, (char *)&var_int, sizeof(var_int), ++ iovbuf, sizeof(iovbuf)); ++ return dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, ++ iovbuf, iovlen, TRUE, 0); ++ } ++ ++ return 0; ++} ++ ++static int dhd_preinit_config(dhd_pub_t *dhd, int ifidx) ++{ ++ mm_segment_t old_fs; ++ struct kstat stat; ++ struct file *fp = NULL; ++ unsigned int len; ++ char *buf = NULL, *p, *name, *value; ++ int ret = 0; ++ char *config_path; ++ ++ config_path = CONFIG_BCMDHD_CONFIG_PATH; ++ ++ if (!config_path) ++ { ++ printk(KERN_ERR "config_path can't read. \n"); ++ return 0; ++ } ++ ++ old_fs = get_fs(); ++ set_fs(get_ds()); ++ if ((ret = vfs_stat(config_path, &stat))) { ++ set_fs(old_fs); ++ printk(KERN_ERR "%s: Failed to get information (%d)\n", ++ config_path, ret); ++ return ret; ++ } ++ set_fs(old_fs); ++ ++ if (!(buf = MALLOC(dhd->osh, stat.size + 1))) { ++ printk(KERN_ERR "Failed to allocate memory %llu bytes\n", stat.size); ++ return -ENOMEM; ++ } ++ ++ printk("dhd_preinit_config : config path : %s \n", config_path); ++ ++ if (!(fp = dhd_os_open_image(config_path)) || ++ (len = dhd_os_get_image_block(buf, stat.size, fp)) < 0) ++ goto err; ++ ++ buf[stat.size] = '\0'; ++ for (p = buf; *p; p++) { ++ if (isspace(*p)) ++ continue; ++ for (name = p++; *p && !isspace(*p); p++) { ++ if (*p == '=') { ++ *p = '\0'; ++ p++; ++ for (value = p; *p && !isspace(*p); p++); ++ *p = '\0'; ++ if ((ret = dhd_preinit_proc(dhd, ifidx, name, value)) < 0) { ++ printk(KERN_ERR "%s: %s=%s\n", ++ bcmerrorstr(ret), name, value); ++ } ++ break; ++ } ++ } ++ } ++ ret = 0; ++ ++out: ++ if (fp) ++ dhd_os_close_image(fp); ++ if (buf) ++ MFREE(dhd->osh, buf, stat.size+1); ++ return ret; ++ ++err: ++ ret = -1; ++ goto out; ++} ++#endif /* READ_CONFIG_FROM_FILE */ ++ ++int ++dhd_preinit_ioctls(dhd_pub_t *dhd) ++{ ++ int ret = 0; ++ char eventmask[WL_EVENTING_MASK_LEN]; ++ char iovbuf[WL_EVENTING_MASK_LEN + 12]; /* Room for "event_msgs" + '\0' + bitvec */ ++ uint32 buf_key_b4_m4 = 1; ++#ifndef WL_CFG80211 ++ u32 up = 0; ++#endif ++ uint8 msglen; ++ eventmsgs_ext_t *eventmask_msg = NULL; ++ char* iov_buf = NULL; ++ int ret2 = 0; ++#ifdef WLAIBSS ++ aibss_bcn_force_config_t bcn_config; ++ uint32 aibss; ++#ifdef WLAIBSS_PS ++ uint32 aibss_ps; ++#endif /* WLAIBSS_PS */ ++#endif /* WLAIBSS */ ++#if defined(BCMSUP_4WAY_HANDSHAKE) && defined(WLAN_AKM_SUITE_FT_8021X) ++ uint32 sup_wpa = 0; ++#endif ++#if defined(CUSTOM_AMPDU_BA_WSIZE) || (defined(WLAIBSS) && \ ++ defined(CUSTOM_IBSS_AMPDU_BA_WSIZE)) ++ uint32 ampdu_ba_wsize = 0; ++#endif /* CUSTOM_AMPDU_BA_WSIZE ||(WLAIBSS && CUSTOM_IBSS_AMPDU_BA_WSIZE) */ ++#if defined(CUSTOM_AMPDU_MPDU) ++ int32 ampdu_mpdu = 0; ++#endif ++#if defined(CUSTOM_AMPDU_RELEASE) ++ int32 ampdu_release = 0; ++#endif ++#if defined(CUSTOM_AMSDU_AGGSF) ++ int32 amsdu_aggsf = 0; ++#endif ++ ++#if defined(BCMSDIO) ++#ifdef PROP_TXSTATUS ++ int wlfc_enable = TRUE; ++#ifndef DISABLE_11N ++ uint32 hostreorder = 1; ++ uint wl_down = 1; ++#endif /* DISABLE_11N */ ++#endif /* PROP_TXSTATUS */ ++#endif ++#ifdef PCIE_FULL_DONGLE ++ uint32 wl_ap_isolate; ++#endif /* PCIE_FULL_DONGLE */ ++ ++#ifdef DHD_ENABLE_LPC ++ uint32 lpc = 1; ++#endif /* DHD_ENABLE_LPC */ ++ uint power_mode = PM_FAST; ++ uint32 dongle_align = DHD_SDALIGN; ++#if defined(BCMSDIO) ++ uint32 glom = CUSTOM_GLOM_SETTING; ++#endif /* defined(BCMSDIO) */ ++#if defined(CUSTOMER_HW2) && defined(USE_WL_CREDALL) ++ uint32 credall = 1; ++#endif ++ uint bcn_timeout = dhd->conf->bcn_timeout; ++ uint retry_max = 3; ++#if defined(ARP_OFFLOAD_SUPPORT) ++ int arpoe = 1; ++#endif ++ int scan_assoc_time = DHD_SCAN_ASSOC_ACTIVE_TIME; ++ int scan_unassoc_time = DHD_SCAN_UNASSOC_ACTIVE_TIME; ++ int scan_passive_time = DHD_SCAN_PASSIVE_TIME; ++ char buf[WLC_IOCTL_SMLEN]; ++ char *ptr; ++ uint32 listen_interval = CUSTOM_LISTEN_INTERVAL; /* Default Listen Interval in Beacons */ ++#ifdef ROAM_ENABLE ++ uint roamvar = 0; ++ int roam_trigger[2] = {CUSTOM_ROAM_TRIGGER_SETTING, WLC_BAND_ALL}; ++ int roam_scan_period[2] = {10, WLC_BAND_ALL}; ++ int roam_delta[2] = {CUSTOM_ROAM_DELTA_SETTING, WLC_BAND_ALL}; ++#ifdef FULL_ROAMING_SCAN_PERIOD_60_SEC ++ int roam_fullscan_period = 60; ++#else /* FULL_ROAMING_SCAN_PERIOD_60_SEC */ ++ int roam_fullscan_period = 120; ++#endif /* FULL_ROAMING_SCAN_PERIOD_60_SEC */ ++#else ++#ifdef DISABLE_BUILTIN_ROAM ++ uint roamvar = 1; ++#endif /* DISABLE_BUILTIN_ROAM */ ++#endif /* ROAM_ENABLE */ ++ ++#if defined(SOFTAP) ++ uint dtim = 1; ++#endif ++#if (defined(AP) && !defined(WLP2P)) || (!defined(AP) && defined(WL_CFG80211)) ++ uint32 mpc = 0; /* Turn MPC off for AP/APSTA mode */ ++ struct ether_addr p2p_ea; ++#endif ++#ifdef BCMCCX ++ uint32 ccx = 1; ++#endif ++#ifdef SOFTAP_UAPSD_OFF ++ uint32 wme_apsd = 0; ++#endif /* SOFTAP_UAPSD_OFF */ ++#if (defined(AP) || defined(WLP2P)) && !defined(SOFTAP_AND_GC) ++ uint32 apsta = 1; /* Enable APSTA mode */ ++#elif defined(SOFTAP_AND_GC) ++ uint32 apsta = 0; ++ int ap_mode = 1; ++#endif /* (defined(AP) || defined(WLP2P)) && !defined(SOFTAP_AND_GC) */ ++#ifdef GET_CUSTOM_MAC_ENABLE ++ struct ether_addr ea_addr; ++#endif /* GET_CUSTOM_MAC_ENABLE */ ++ ++#ifdef DISABLE_11N ++ uint32 nmode = 0; ++#endif /* DISABLE_11N */ ++ ++#if defined(DISABLE_11AC) ++ uint32 vhtmode = 0; ++#endif /* DISABLE_11AC */ ++#ifdef USE_WL_TXBF ++ uint32 txbf = 1; ++#endif /* USE_WL_TXBF */ ++#ifdef AMPDU_VO_ENABLE ++ struct ampdu_tid_control tid; ++#endif ++#ifdef USE_WL_FRAMEBURST ++ uint32 frameburst = 1; ++#endif /* USE_WL_FRAMEBURST */ ++#ifdef DHD_SET_FW_HIGHSPEED ++ uint32 ack_ratio = 250; ++ uint32 ack_ratio_depth = 64; ++#endif /* DHD_SET_FW_HIGHSPEED */ ++#ifdef SUPPORT_2G_VHT ++ uint32 vht_features = 0x3; /* 2G enable | rates all */ ++#endif /* SUPPORT_2G_VHT */ ++#ifdef CUSTOM_PSPRETEND_THR ++ uint32 pspretend_thr = CUSTOM_PSPRETEND_THR; ++#endif ++#ifdef PKT_FILTER_SUPPORT ++ dhd_pkt_filter_enable = TRUE; ++#endif /* PKT_FILTER_SUPPORT */ ++#ifdef WLTDLS ++ dhd->tdls_enable = FALSE; ++#endif /* WLTDLS */ ++ dhd->suspend_bcn_li_dtim = CUSTOM_SUSPEND_BCN_LI_DTIM; ++ DHD_TRACE(("Enter %s\n", __FUNCTION__)); ++ ++ dhd_conf_set_fw_int_cmd(dhd, "WLC_SET_BAND", WLC_SET_BAND, dhd->conf->band, 0, FALSE); ++#ifdef DHDTCPACK_SUPPRESS ++ printf("%s: Set tcpack_sup_mode %d\n", __FUNCTION__, dhd->conf->tcpack_sup_mode); ++ dhd_tcpack_suppress_set(dhd, dhd->conf->tcpack_sup_mode); ++#endif ++ ++ dhd->op_mode = 0; ++ if ((!op_mode && dhd_get_fw_mode(dhd->info) == DHD_FLAG_MFG_MODE) || ++ (op_mode == DHD_FLAG_MFG_MODE)) { ++ /* Check and adjust IOCTL response timeout for Manufactring firmware */ ++ dhd_os_set_ioctl_resp_timeout(MFG_IOCTL_RESP_TIMEOUT); ++ DHD_ERROR(("%s : Set IOCTL response time for Manufactring Firmware\n", ++ __FUNCTION__)); ++ } ++ else { ++ dhd_os_set_ioctl_resp_timeout(IOCTL_RESP_TIMEOUT); ++ DHD_INFO(("%s : Set IOCTL response time.\n", __FUNCTION__)); ++ } ++#ifdef GET_CUSTOM_MAC_ENABLE ++ ret = wifi_platform_get_mac_addr(dhd->info->adapter, ea_addr.octet); ++ if (!ret) { ++ memset(buf, 0, sizeof(buf)); ++ bcm_mkiovar("cur_etheraddr", (void *)&ea_addr, ETHER_ADDR_LEN, buf, sizeof(buf)); ++ ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, buf, sizeof(buf), TRUE, 0); ++ if (ret < 0) { ++ DHD_ERROR(("%s: can't set MAC address MAC="MACDBG", error=%d\n", ++ __FUNCTION__, MAC2STRDBG(ea_addr.octet), ret)); ++ ret = BCME_NOTUP; ++ goto done; ++ } ++ memcpy(dhd->mac.octet, ea_addr.octet, ETHER_ADDR_LEN); ++ } else { ++#endif /* GET_CUSTOM_MAC_ENABLE */ ++ /* Get the default device MAC address directly from firmware */ ++ memset(buf, 0, sizeof(buf)); ++ bcm_mkiovar("cur_etheraddr", 0, 0, buf, sizeof(buf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, buf, sizeof(buf), ++ FALSE, 0)) < 0) { ++ DHD_ERROR(("%s: can't get MAC address , error=%d\n", __FUNCTION__, ret)); ++ ret = BCME_NOTUP; ++ goto done; ++ } ++ /* Update public MAC address after reading from Firmware */ ++ memcpy(dhd->mac.octet, buf, ETHER_ADDR_LEN); ++ ++#ifdef GET_CUSTOM_MAC_ENABLE ++ } ++#endif /* GET_CUSTOM_MAC_ENABLE */ ++ ++ /* get a capabilities from firmware */ ++ memset(dhd->fw_capabilities, 0, sizeof(dhd->fw_capabilities)); ++ bcm_mkiovar("cap", 0, 0, dhd->fw_capabilities, sizeof(dhd->fw_capabilities)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, dhd->fw_capabilities, ++ sizeof(dhd->fw_capabilities), FALSE, 0)) < 0) { ++ DHD_ERROR(("%s: Get Capability failed (error=%d)\n", ++ __FUNCTION__, ret)); ++ goto done; ++ } ++ if ((!op_mode && dhd_get_fw_mode(dhd->info) == DHD_FLAG_HOSTAP_MODE) || ++ (op_mode == DHD_FLAG_HOSTAP_MODE)) { ++#ifdef SET_RANDOM_MAC_SOFTAP ++ uint rand_mac; ++#endif ++ dhd->op_mode = DHD_FLAG_HOSTAP_MODE; ++#if defined(ARP_OFFLOAD_SUPPORT) ++ arpoe = 0; ++#endif ++#ifdef PKT_FILTER_SUPPORT ++ dhd_pkt_filter_enable = FALSE; ++#endif ++#ifdef SET_RANDOM_MAC_SOFTAP ++ SRANDOM32((uint)jiffies); ++ rand_mac = RANDOM32(); ++ iovbuf[0] = 0x02; /* locally administered bit */ ++ iovbuf[1] = 0x1A; ++ iovbuf[2] = 0x11; ++ iovbuf[3] = (unsigned char)(rand_mac & 0x0F) | 0xF0; ++ iovbuf[4] = (unsigned char)(rand_mac >> 8); ++ iovbuf[5] = (unsigned char)(rand_mac >> 16); ++ ++ bcm_mkiovar("cur_etheraddr", (void *)iovbuf, ETHER_ADDR_LEN, buf, sizeof(buf)); ++ ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, buf, sizeof(buf), TRUE, 0); ++ if (ret < 0) { ++ DHD_ERROR(("%s: can't set MAC address , error=%d\n", __FUNCTION__, ret)); ++ } else ++ memcpy(dhd->mac.octet, iovbuf, ETHER_ADDR_LEN); ++#endif /* SET_RANDOM_MAC_SOFTAP */ ++#if !defined(AP) && defined(WL_CFG80211) ++ /* Turn off MPC in AP mode */ ++ bcm_mkiovar("mpc", (char *)&mpc, 4, iovbuf, sizeof(iovbuf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, ++ sizeof(iovbuf), TRUE, 0)) < 0) { ++ DHD_ERROR(("%s mpc for HostAPD failed %d\n", __FUNCTION__, ret)); ++ } ++#endif ++#ifdef SUPPORT_AP_POWERSAVE ++ dhd_set_ap_powersave(dhd, 0, TRUE); ++#endif ++#ifdef SOFTAP_UAPSD_OFF ++ bcm_mkiovar("wme_apsd", (char *)&wme_apsd, 4, iovbuf, sizeof(iovbuf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) ++ DHD_ERROR(("%s: set wme_apsd 0 fail (error=%d)\n", __FUNCTION__, ret)); ++#endif /* SOFTAP_UAPSD_OFF */ ++ } else if ((!op_mode && dhd_get_fw_mode(dhd->info) == DHD_FLAG_MFG_MODE) || ++ (op_mode == DHD_FLAG_MFG_MODE)) { ++#if defined(ARP_OFFLOAD_SUPPORT) ++ arpoe = 0; ++#endif /* ARP_OFFLOAD_SUPPORT */ ++#ifdef PKT_FILTER_SUPPORT ++ dhd_pkt_filter_enable = FALSE; ++#endif /* PKT_FILTER_SUPPORT */ ++ dhd->op_mode = DHD_FLAG_MFG_MODE; ++ } else { ++ uint32 concurrent_mode = 0; ++ if ((!op_mode && dhd_get_fw_mode(dhd->info) == DHD_FLAG_P2P_MODE) || ++ (op_mode == DHD_FLAG_P2P_MODE)) { ++#if defined(ARP_OFFLOAD_SUPPORT) ++ arpoe = 0; ++#endif ++#ifdef PKT_FILTER_SUPPORT ++ dhd_pkt_filter_enable = FALSE; ++#endif ++ dhd->op_mode = DHD_FLAG_P2P_MODE; ++ } else if ((!op_mode && dhd_get_fw_mode(dhd->info) == DHD_FLAG_IBSS_MODE) || ++ (op_mode == DHD_FLAG_IBSS_MODE)) { ++ dhd->op_mode = DHD_FLAG_IBSS_MODE; ++ } else ++ dhd->op_mode = DHD_FLAG_STA_MODE; ++#if !defined(AP) && defined(WLP2P) ++ if (dhd->op_mode != DHD_FLAG_IBSS_MODE && ++ (concurrent_mode = dhd_get_concurrent_capabilites(dhd))) { ++#if defined(ARP_OFFLOAD_SUPPORT) ++ arpoe = 1; ++#endif ++ dhd->op_mode |= concurrent_mode; ++ } ++ ++ /* Check if we are enabling p2p */ ++ if (dhd->op_mode & DHD_FLAG_P2P_MODE) { ++ bcm_mkiovar("apsta", (char *)&apsta, 4, iovbuf, sizeof(iovbuf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, ++ iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) { ++ DHD_ERROR(("%s APSTA for P2P failed ret= %d\n", __FUNCTION__, ret)); ++ } ++ ++#if defined(SOFTAP_AND_GC) ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_AP, ++ (char *)&ap_mode, sizeof(ap_mode), TRUE, 0)) < 0) { ++ DHD_ERROR(("%s WLC_SET_AP failed %d\n", __FUNCTION__, ret)); ++ } ++#endif ++ memcpy(&p2p_ea, &dhd->mac, ETHER_ADDR_LEN); ++ ETHER_SET_LOCALADDR(&p2p_ea); ++ bcm_mkiovar("p2p_da_override", (char *)&p2p_ea, ++ ETHER_ADDR_LEN, iovbuf, sizeof(iovbuf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, ++ iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) { ++ DHD_ERROR(("%s p2p_da_override ret= %d\n", __FUNCTION__, ret)); ++ } else { ++ DHD_INFO(("dhd_preinit_ioctls: p2p_da_override succeeded\n")); ++ } ++ } ++#else ++ (void)concurrent_mode; ++#endif ++ } ++ ++ DHD_ERROR(("Firmware up: op_mode=0x%04x, MAC="MACDBG"\n", ++ dhd->op_mode, MAC2STRDBG(dhd->mac.octet))); ++ /* Set Country code */ ++ if (dhd->dhd_cspec.ccode[0] != 0) { ++ printf("Set country %s, revision %d\n", dhd->dhd_cspec.ccode, dhd->dhd_cspec.rev); ++ bcm_mkiovar("country", (char *)&dhd->dhd_cspec, ++ sizeof(wl_country_t), iovbuf, sizeof(iovbuf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) ++ printf("%s: country code setting failed %d\n", __FUNCTION__, ret); ++ } else { ++ dhd_conf_set_country(dhd); ++ dhd_conf_fix_country(dhd); ++ } ++ dhd_conf_get_country(dhd, &dhd->dhd_cspec); ++ ++#if defined(DISABLE_11AC) ++ bcm_mkiovar("vhtmode", (char *)&vhtmode, 4, iovbuf, sizeof(iovbuf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) ++ DHD_ERROR(("%s wl vhtmode 0 failed %d\n", __FUNCTION__, ret)); ++#endif /* DISABLE_11AC */ ++ dhd_conf_set_fw_string_cmd(dhd, "vhtmode", dhd->conf->vhtmode, 0, TRUE); ++ ++ /* Set Listen Interval */ ++ bcm_mkiovar("assoc_listen", (char *)&listen_interval, 4, iovbuf, sizeof(iovbuf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) ++ DHD_ERROR(("%s assoc_listen failed %d\n", __FUNCTION__, ret)); ++ ++#if defined(ROAM_ENABLE) || defined(DISABLE_BUILTIN_ROAM) ++ /* Disable built-in roaming to allowed ext supplicant to take care of roaming */ ++ bcm_mkiovar("roam_off", (char *)&roamvar, 4, iovbuf, sizeof(iovbuf)); ++ dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); ++#endif /* ROAM_ENABLE || DISABLE_BUILTIN_ROAM */ ++#if defined(ROAM_ENABLE) ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_ROAM_TRIGGER, roam_trigger, ++ sizeof(roam_trigger), TRUE, 0)) < 0) ++ DHD_ERROR(("%s: roam trigger set failed %d\n", __FUNCTION__, ret)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_ROAM_SCAN_PERIOD, roam_scan_period, ++ sizeof(roam_scan_period), TRUE, 0)) < 0) ++ DHD_ERROR(("%s: roam scan period set failed %d\n", __FUNCTION__, ret)); ++ if ((dhd_wl_ioctl_cmd(dhd, WLC_SET_ROAM_DELTA, roam_delta, ++ sizeof(roam_delta), TRUE, 0)) < 0) ++ DHD_ERROR(("%s: roam delta set failed %d\n", __FUNCTION__, ret)); ++ bcm_mkiovar("fullroamperiod", (char *)&roam_fullscan_period, 4, iovbuf, sizeof(iovbuf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) ++ DHD_ERROR(("%s: roam fullscan period set failed %d\n", __FUNCTION__, ret)); ++#endif /* ROAM_ENABLE */ ++ dhd_conf_set_roam(dhd); ++ ++#ifdef BCMCCX ++ bcm_mkiovar("ccx_enable", (char *)&ccx, 4, iovbuf, sizeof(iovbuf)); ++ dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); ++#endif /* BCMCCX */ ++#ifdef WLTDLS ++ /* by default TDLS on and auto mode off */ ++ _dhd_tdls_enable(dhd, true, false, NULL); ++#endif /* WLTDLS */ ++ ++#ifdef DHD_ENABLE_LPC ++ /* Set lpc 1 */ ++ bcm_mkiovar("lpc", (char *)&lpc, 4, iovbuf, sizeof(iovbuf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, ++ sizeof(iovbuf), TRUE, 0)) < 0) { ++ DHD_ERROR(("%s Set lpc failed %d\n", __FUNCTION__, ret)); ++ } ++#endif /* DHD_ENABLE_LPC */ ++ dhd_conf_set_fw_string_cmd(dhd, "lpc", dhd->conf->lpc, 0, FALSE); ++ ++ /* Set PowerSave mode */ ++ if (dhd->conf->pm >= 0) ++ power_mode = dhd->conf->pm; ++ dhd_wl_ioctl_cmd(dhd, WLC_SET_PM, (char *)&power_mode, sizeof(power_mode), TRUE, 0); ++ ++ /* Match Host and Dongle rx alignment */ ++ bcm_mkiovar("bus:txglomalign", (char *)&dongle_align, 4, iovbuf, sizeof(iovbuf)); ++ dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); ++ ++#if defined(CUSTOMER_HW2) && defined(USE_WL_CREDALL) ++ /* enable credall to reduce the chance of no bus credit happened. */ ++ bcm_mkiovar("bus:credall", (char *)&credall, 4, iovbuf, sizeof(iovbuf)); ++ dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); ++#endif ++ ++#if defined(BCMSDIO) ++ if (glom != DEFAULT_GLOM_VALUE) { ++ DHD_INFO(("%s set glom=0x%X\n", __FUNCTION__, glom)); ++ bcm_mkiovar("bus:txglom", (char *)&glom, 4, iovbuf, sizeof(iovbuf)); ++ dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); ++ } ++#endif /* defined(BCMSDIO) */ ++ ++ /* Setup timeout if Beacons are lost and roam is off to report link down */ ++ bcm_mkiovar("bcn_timeout", (char *)&bcn_timeout, 4, iovbuf, sizeof(iovbuf)); ++ dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); ++ /* Setup assoc_retry_max count to reconnect target AP in dongle */ ++ bcm_mkiovar("assoc_retry_max", (char *)&retry_max, 4, iovbuf, sizeof(iovbuf)); ++ dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); ++#if defined(AP) && !defined(WLP2P) ++ /* Turn off MPC in AP mode */ ++ bcm_mkiovar("mpc", (char *)&mpc, 4, iovbuf, sizeof(iovbuf)); ++ dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); ++ bcm_mkiovar("apsta", (char *)&apsta, 4, iovbuf, sizeof(iovbuf)); ++ dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); ++#endif /* defined(AP) && !defined(WLP2P) */ ++ /* 0:HT20 in ALL, 1:HT40 in ALL, 2: HT20 in 2G HT40 in 5G */ ++ dhd_conf_set_fw_string_cmd(dhd, "mimo_bw_cap", dhd->conf->mimo_bw_cap, 1, TRUE); ++ dhd_conf_set_fw_string_cmd(dhd, "force_wme_ac", dhd->conf->force_wme_ac, 1, FALSE); ++ dhd_conf_set_fw_string_cmd(dhd, "stbc_tx", dhd->conf->stbc, 0, FALSE); ++ dhd_conf_set_fw_string_cmd(dhd, "stbc_rx", dhd->conf->stbc, 0, FALSE); ++ dhd_conf_set_fw_int_cmd(dhd, "WLC_SET_SRL", WLC_SET_SRL, dhd->conf->srl, 0, TRUE); ++ dhd_conf_set_fw_int_cmd(dhd, "WLC_SET_LRL", WLC_SET_LRL, dhd->conf->lrl, 0, FALSE); ++ dhd_conf_set_fw_int_cmd(dhd, "WLC_SET_SPECT_MANAGMENT", WLC_SET_SPECT_MANAGMENT, dhd->conf->spect, 0, FALSE); ++ dhd_conf_set_fw_string_cmd(dhd, "rsdb_mode", dhd->conf->rsdb_mode, -1, TRUE); ++ ++#if defined(SOFTAP) ++ if (ap_fw_loaded == TRUE) { ++ dhd_wl_ioctl_cmd(dhd, WLC_SET_DTIMPRD, (char *)&dtim, sizeof(dtim), TRUE, 0); ++ } ++#endif ++ ++#if defined(KEEP_ALIVE) ++ { ++ /* Set Keep Alive : be sure to use FW with -keepalive */ ++ int res; ++ ++#if defined(SOFTAP) ++ if (ap_fw_loaded == FALSE) ++#endif ++ if (!(dhd->op_mode & ++ (DHD_FLAG_HOSTAP_MODE | DHD_FLAG_MFG_MODE))) { ++ if ((res = dhd_keep_alive_onoff(dhd)) < 0) ++ DHD_ERROR(("%s set keeplive failed %d\n", ++ __FUNCTION__, res)); ++ } ++ } ++#endif /* defined(KEEP_ALIVE) */ ++ ++#ifdef USE_WL_TXBF ++ bcm_mkiovar("txbf", (char *)&txbf, 4, iovbuf, sizeof(iovbuf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, ++ sizeof(iovbuf), TRUE, 0)) < 0) { ++ DHD_ERROR(("%s Set txbf failed %d\n", __FUNCTION__, ret)); ++ } ++#endif /* USE_WL_TXBF */ ++ dhd_conf_set_fw_string_cmd(dhd, "txbf", dhd->conf->txbf, 0, FALSE); ++#ifdef USE_WL_FRAMEBURST ++ /* Set frameburst to value */ ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_FAKEFRAG, (char *)&frameburst, ++ sizeof(frameburst), TRUE, 0)) < 0) { ++ DHD_ERROR(("%s Set frameburst failed %d\n", __FUNCTION__, ret)); ++ } ++#endif /* USE_WL_FRAMEBURST */ ++ dhd_conf_set_fw_string_cmd(dhd, "frameburst", dhd->conf->frameburst, 0, FALSE); ++#ifdef DHD_SET_FW_HIGHSPEED ++ /* Set ack_ratio */ ++ bcm_mkiovar("ack_ratio", (char *)&ack_ratio, 4, iovbuf, sizeof(iovbuf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, ++ sizeof(iovbuf), TRUE, 0)) < 0) { ++ DHD_ERROR(("%s Set ack_ratio failed %d\n", __FUNCTION__, ret)); ++ } ++ ++ /* Set ack_ratio_depth */ ++ bcm_mkiovar("ack_ratio_depth", (char *)&ack_ratio_depth, 4, iovbuf, sizeof(iovbuf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, ++ sizeof(iovbuf), TRUE, 0)) < 0) { ++ DHD_ERROR(("%s Set ack_ratio_depth failed %d\n", __FUNCTION__, ret)); ++ } ++#endif /* DHD_SET_FW_HIGHSPEED */ ++#if defined(CUSTOM_AMPDU_BA_WSIZE) || (defined(WLAIBSS) && \ ++ defined(CUSTOM_IBSS_AMPDU_BA_WSIZE)) ++ /* Set ampdu ba wsize to 64 or 16 */ ++#ifdef CUSTOM_AMPDU_BA_WSIZE ++ ampdu_ba_wsize = CUSTOM_AMPDU_BA_WSIZE; ++#endif ++#if defined(WLAIBSS) && defined(CUSTOM_IBSS_AMPDU_BA_WSIZE) ++ if (dhd->op_mode == DHD_FLAG_IBSS_MODE) ++ ampdu_ba_wsize = CUSTOM_IBSS_AMPDU_BA_WSIZE; ++#endif /* WLAIBSS && CUSTOM_IBSS_AMPDU_BA_WSIZE */ ++ if (ampdu_ba_wsize != 0) { ++ bcm_mkiovar("ampdu_ba_wsize", (char *)&du_ba_wsize, 4, iovbuf, sizeof(iovbuf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, ++ sizeof(iovbuf), TRUE, 0)) < 0) { ++ DHD_ERROR(("%s Set ampdu_ba_wsize to %d failed %d\n", ++ __FUNCTION__, ampdu_ba_wsize, ret)); ++ } ++ } ++#endif /* CUSTOM_AMPDU_BA_WSIZE || (WLAIBSS && CUSTOM_IBSS_AMPDU_BA_WSIZE) */ ++ dhd_conf_set_fw_string_cmd(dhd, "ampdu_ba_wsize", dhd->conf->ampdu_ba_wsize, 1, FALSE); ++ ++ iov_buf = (char*)kmalloc(WLC_IOCTL_SMLEN, GFP_KERNEL); ++ if (iov_buf == NULL) { ++ DHD_ERROR(("failed to allocate %d bytes for iov_buf\n", WLC_IOCTL_SMLEN)); ++ ret = BCME_NOMEM; ++ goto done; ++ } ++#ifdef WLAIBSS ++ /* Configure custom IBSS beacon transmission */ ++ if (dhd->op_mode & DHD_FLAG_IBSS_MODE) ++ { ++ aibss = 1; ++ bcm_mkiovar("aibss", (char *)&aibss, 4, iovbuf, sizeof(iovbuf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, ++ sizeof(iovbuf), TRUE, 0)) < 0) { ++ DHD_ERROR(("%s Set aibss to %d failed %d\n", ++ __FUNCTION__, aibss, ret)); ++ } ++#ifdef WLAIBSS_PS ++ aibss_ps = 1; ++ bcm_mkiovar("aibss_ps", (char *)&aibss_ps, 4, iovbuf, sizeof(iovbuf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, ++ sizeof(iovbuf), TRUE, 0)) < 0) { ++ DHD_ERROR(("%s Set aibss PS to %d failed %d\n", ++ __FUNCTION__, aibss, ret)); ++ } ++#endif /* WLAIBSS_PS */ ++ } ++ memset(&bcn_config, 0, sizeof(bcn_config)); ++ bcn_config.initial_min_bcn_dur = AIBSS_INITIAL_MIN_BCN_DUR; ++ bcn_config.min_bcn_dur = AIBSS_MIN_BCN_DUR; ++ bcn_config.bcn_flood_dur = AIBSS_BCN_FLOOD_DUR; ++ bcn_config.version = AIBSS_BCN_FORCE_CONFIG_VER_0; ++ bcn_config.len = sizeof(bcn_config); ++ ++ bcm_mkiovar("aibss_bcn_force_config", (char *)&bcn_config, ++ sizeof(aibss_bcn_force_config_t), iov_buf, WLC_IOCTL_SMLEN); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iov_buf, ++ WLC_IOCTL_SMLEN, TRUE, 0)) < 0) { ++ DHD_ERROR(("%s Set aibss_bcn_force_config to %d, %d, %d failed %d\n", ++ __FUNCTION__, AIBSS_INITIAL_MIN_BCN_DUR, AIBSS_MIN_BCN_DUR, ++ AIBSS_BCN_FLOOD_DUR, ret)); ++ } ++#endif /* WLAIBSS */ ++ ++#if defined(CUSTOM_AMPDU_MPDU) ++ ampdu_mpdu = CUSTOM_AMPDU_MPDU; ++ if (ampdu_mpdu != 0 && (ampdu_mpdu <= ampdu_ba_wsize)) { ++ bcm_mkiovar("ampdu_mpdu", (char *)&du_mpdu, 4, iovbuf, sizeof(iovbuf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, ++ sizeof(iovbuf), TRUE, 0)) < 0) { ++ DHD_ERROR(("%s Set ampdu_mpdu to %d failed %d\n", ++ __FUNCTION__, CUSTOM_AMPDU_MPDU, ret)); ++ } ++ } ++#endif /* CUSTOM_AMPDU_MPDU */ ++ ++#if defined(CUSTOM_AMPDU_RELEASE) ++ ampdu_release = CUSTOM_AMPDU_RELEASE; ++ if (ampdu_release != 0 && (ampdu_release <= ampdu_ba_wsize)) { ++ bcm_mkiovar("ampdu_release", (char *)&du_release, 4, iovbuf, sizeof(iovbuf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, ++ sizeof(iovbuf), TRUE, 0)) < 0) { ++ DHD_ERROR(("%s Set ampdu_release to %d failed %d\n", ++ __FUNCTION__, CUSTOM_AMPDU_RELEASE, ret)); ++ } ++ } ++#endif /* CUSTOM_AMPDU_RELEASE */ ++ ++#if defined(CUSTOM_AMSDU_AGGSF) ++ amsdu_aggsf = CUSTOM_AMSDU_AGGSF; ++ if (amsdu_aggsf != 0) { ++ bcm_mkiovar("amsdu_aggsf", (char *)&amsdu_aggsf, 4, iovbuf, sizeof(iovbuf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, ++ sizeof(iovbuf), TRUE, 0)) < 0) { ++ DHD_ERROR(("%s Set amsdu_aggsf to %d failed %d\n", ++ __FUNCTION__, CUSTOM_AMSDU_AGGSF, ret)); ++ } ++ } ++#endif /* CUSTOM_AMSDU_AGGSF */ ++ ++#if defined(BCMSUP_4WAY_HANDSHAKE) && defined(WLAN_AKM_SUITE_FT_8021X) ++ /* Read 4-way handshake requirements */ ++ if (dhd_use_idsup == 1) { ++ bcm_mkiovar("sup_wpa", (char *)&sup_wpa, 4, iovbuf, sizeof(iovbuf)); ++ ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, iovbuf, sizeof(iovbuf), FALSE, 0); ++ /* sup_wpa iovar returns NOTREADY status on some platforms using modularized ++ * in-dongle supplicant. ++ */ ++ if (ret >= 0 || ret == BCME_NOTREADY) ++ dhd->fw_4way_handshake = TRUE; ++ DHD_TRACE(("4-way handshake mode is: %d\n", dhd->fw_4way_handshake)); ++ } ++#endif /* BCMSUP_4WAY_HANDSHAKE && WLAN_AKM_SUITE_FT_8021X */ ++#ifdef SUPPORT_2G_VHT ++ bcm_mkiovar("vht_features", (char *)&vht_features, 4, iovbuf, sizeof(iovbuf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) { ++ DHD_ERROR(("%s vht_features set failed %d\n", __FUNCTION__, ret)); ++ } ++#endif /* SUPPORT_2G_VHT */ ++#ifdef CUSTOM_PSPRETEND_THR ++ /* Turn off MPC in AP mode */ ++ bcm_mkiovar("pspretend_threshold", (char *)&pspretend_thr, 4, ++ iovbuf, sizeof(iovbuf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, ++ sizeof(iovbuf), TRUE, 0)) < 0) { ++ DHD_ERROR(("%s pspretend_threshold for HostAPD failed %d\n", ++ __FUNCTION__, ret)); ++ } ++#endif ++ ++ bcm_mkiovar("buf_key_b4_m4", (char *)&buf_key_b4_m4, 4, iovbuf, sizeof(iovbuf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, ++ sizeof(iovbuf), TRUE, 0)) < 0) { ++ DHD_ERROR(("%s buf_key_b4_m4 set failed %d\n", __FUNCTION__, ret)); ++ } ++ ++ /* Read event_msgs mask */ ++ bcm_mkiovar("event_msgs", eventmask, WL_EVENTING_MASK_LEN, iovbuf, sizeof(iovbuf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, iovbuf, sizeof(iovbuf), FALSE, 0)) < 0) { ++ DHD_ERROR(("%s read Event mask failed %d\n", __FUNCTION__, ret)); ++ goto done; ++ } ++ bcopy(iovbuf, eventmask, WL_EVENTING_MASK_LEN); ++ ++ /* Setup event_msgs */ ++ setbit(eventmask, WLC_E_SET_SSID); ++ setbit(eventmask, WLC_E_PRUNE); ++ setbit(eventmask, WLC_E_AUTH); ++ setbit(eventmask, WLC_E_AUTH_IND); ++ setbit(eventmask, WLC_E_ASSOC); ++ setbit(eventmask, WLC_E_REASSOC); ++ setbit(eventmask, WLC_E_REASSOC_IND); ++ setbit(eventmask, WLC_E_DEAUTH); ++ setbit(eventmask, WLC_E_DEAUTH_IND); ++ setbit(eventmask, WLC_E_DISASSOC_IND); ++ setbit(eventmask, WLC_E_DISASSOC); ++ setbit(eventmask, WLC_E_JOIN); ++ setbit(eventmask, WLC_E_START); ++ setbit(eventmask, WLC_E_ASSOC_IND); ++ setbit(eventmask, WLC_E_PSK_SUP); ++ setbit(eventmask, WLC_E_LINK); ++ setbit(eventmask, WLC_E_NDIS_LINK); ++ setbit(eventmask, WLC_E_MIC_ERROR); ++ setbit(eventmask, WLC_E_ASSOC_REQ_IE); ++ setbit(eventmask, WLC_E_ASSOC_RESP_IE); ++#ifndef WL_CFG80211 ++ setbit(eventmask, WLC_E_PMKID_CACHE); ++ setbit(eventmask, WLC_E_TXFAIL); ++#endif ++ setbit(eventmask, WLC_E_JOIN_START); ++// setbit(eventmask, WLC_E_SCAN_COMPLETE); // terence 20150628: remove redundant event ++#ifdef WLMEDIA_HTSF ++ setbit(eventmask, WLC_E_HTSFSYNC); ++#endif /* WLMEDIA_HTSF */ ++#ifdef PNO_SUPPORT ++ setbit(eventmask, WLC_E_PFN_NET_FOUND); ++ setbit(eventmask, WLC_E_PFN_BEST_BATCHING); ++ setbit(eventmask, WLC_E_PFN_BSSID_NET_FOUND); ++ setbit(eventmask, WLC_E_PFN_BSSID_NET_LOST); ++#endif /* PNO_SUPPORT */ ++ /* enable dongle roaming event */ ++ setbit(eventmask, WLC_E_ROAM); ++ setbit(eventmask, WLC_E_BSSID); ++#ifdef BCMCCX ++ setbit(eventmask, WLC_E_ADDTS_IND); ++ setbit(eventmask, WLC_E_DELTS_IND); ++#endif /* BCMCCX */ ++#ifdef WLTDLS ++ setbit(eventmask, WLC_E_TDLS_PEER_EVENT); ++#endif /* WLTDLS */ ++#ifdef WL_CFG80211 ++ setbit(eventmask, WLC_E_ESCAN_RESULT); ++ if (dhd->op_mode & DHD_FLAG_P2P_MODE) { ++ setbit(eventmask, WLC_E_ACTION_FRAME_RX); ++ setbit(eventmask, WLC_E_P2P_DISC_LISTEN_COMPLETE); ++ } ++#endif /* WL_CFG80211 */ ++#ifdef WLAIBSS ++ setbit(eventmask, WLC_E_AIBSS_TXFAIL); ++#endif /* WLAIBSS */ ++#ifdef CUSTOMER_HW10 ++ clrbit(eventmask, WLC_E_TRACE); ++#else ++ setbit(eventmask, WLC_E_TRACE); ++#endif ++ /* Write updated Event mask */ ++ bcm_mkiovar("event_msgs", eventmask, WL_EVENTING_MASK_LEN, iovbuf, sizeof(iovbuf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) { ++ DHD_ERROR(("%s Set Event mask failed %d\n", __FUNCTION__, ret)); ++ goto done; ++ } ++ ++ /* make up event mask ext message iovar for event larger than 128 */ ++ msglen = ROUNDUP(WLC_E_LAST, NBBY)/NBBY + EVENTMSGS_EXT_STRUCT_SIZE; ++ eventmask_msg = (eventmsgs_ext_t*)kmalloc(msglen, GFP_KERNEL); ++ if (eventmask_msg == NULL) { ++ DHD_ERROR(("failed to allocate %d bytes for event_msg_ext\n", msglen)); ++ ret = BCME_NOMEM; ++ goto done; ++ } ++ bzero(eventmask_msg, msglen); ++ eventmask_msg->ver = EVENTMSGS_VER; ++ eventmask_msg->len = ROUNDUP(WLC_E_LAST, NBBY)/NBBY; ++ ++ /* Read event_msgs_ext mask */ ++ bcm_mkiovar("event_msgs_ext", (char *)eventmask_msg, msglen, iov_buf, WLC_IOCTL_SMLEN); ++ ret2 = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, iov_buf, WLC_IOCTL_SMLEN, FALSE, 0); ++ if (ret2 != BCME_UNSUPPORTED) ++ ret = ret2; ++ if (ret2 == 0) { /* event_msgs_ext must be supported */ ++ bcopy(iov_buf, eventmask_msg, msglen); ++ ++#ifdef BT_WIFI_HANDOVER ++ setbit(eventmask_msg->mask, WLC_E_BT_WIFI_HANDOVER_REQ); ++#endif /* BT_WIFI_HANDOVER */ ++ ++ /* Write updated Event mask */ ++ eventmask_msg->ver = EVENTMSGS_VER; ++ eventmask_msg->command = EVENTMSGS_SET_MASK; ++ eventmask_msg->len = ROUNDUP(WLC_E_LAST, NBBY)/NBBY; ++ bcm_mkiovar("event_msgs_ext", (char *)eventmask_msg, ++ msglen, iov_buf, WLC_IOCTL_SMLEN); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, ++ iov_buf, WLC_IOCTL_SMLEN, TRUE, 0)) < 0) { ++ DHD_ERROR(("%s write event mask ext failed %d\n", __FUNCTION__, ret)); ++ goto done; ++ } ++ } else if (ret2 < 0 && ret2 != BCME_UNSUPPORTED) { ++ DHD_ERROR(("%s read event mask ext failed %d\n", __FUNCTION__, ret2)); ++ goto done; ++ } /* unsupported is ok */ ++ ++ dhd_wl_ioctl_cmd(dhd, WLC_SET_SCAN_CHANNEL_TIME, (char *)&scan_assoc_time, ++ 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 defined(SOFTAP) ++ if (arpoe && !ap_fw_loaded) ++#else ++ if (arpoe) ++#endif ++ { ++ dhd_arp_offload_enable(dhd, TRUE); ++ dhd_arp_offload_set(dhd, dhd_arp_mode); ++ } else { ++ dhd_arp_offload_enable(dhd, FALSE); ++ dhd_arp_offload_set(dhd, 0); ++ } ++ dhd_arp_enable = arpoe; ++#endif /* ARP_OFFLOAD_SUPPORT */ ++ ++#ifdef PKT_FILTER_SUPPORT ++ /* Setup default defintions for pktfilter , enable in suspend */ ++ dhd->pktfilter_count = 6; ++ /* Setup filter to allow only unicast */ ++ if (dhd_master_mode) { ++ dhd->pktfilter[DHD_UNICAST_FILTER_NUM] = "100 0 0 0 0x01 0x00"; ++ dhd->pktfilter[DHD_BROADCAST_FILTER_NUM] = NULL; ++ dhd->pktfilter[DHD_MULTICAST4_FILTER_NUM] = NULL; ++ dhd->pktfilter[DHD_MULTICAST6_FILTER_NUM] = NULL; ++ /* Add filter to pass multicastDNS packet and NOT filter out as Broadcast */ ++ dhd->pktfilter[DHD_MDNS_FILTER_NUM] = "104 0 0 0 0xFFFFFFFFFFFF 0x01005E0000FB"; ++ /* apply APP pktfilter */ ++ dhd->pktfilter[DHD_ARP_FILTER_NUM] = "105 0 0 12 0xFFFF 0x0806"; ++ } else ++ dhd_conf_discard_pkt_filter(dhd); ++ dhd_conf_add_pkt_filter(dhd); ++ ++#if defined(SOFTAP) ++ if (ap_fw_loaded) { ++ dhd_enable_packet_filter(0, dhd); ++ } ++#endif /* defined(SOFTAP) */ ++ dhd_set_packet_filter(dhd); ++#endif /* PKT_FILTER_SUPPORT */ ++#ifdef DISABLE_11N ++ bcm_mkiovar("nmode", (char *)&nmode, 4, iovbuf, sizeof(iovbuf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) ++ DHD_ERROR(("%s wl nmode 0 failed %d\n", __FUNCTION__, ret)); ++#endif /* DISABLE_11N */ ++ ++#ifdef AMPDU_VO_ENABLE ++ tid.tid = PRIO_8021D_VO; /* Enable TID(6) for voice */ ++ tid.enable = TRUE; ++ bcm_mkiovar("ampdu_tid", (char *)&tid, sizeof(tid), iovbuf, sizeof(iovbuf)); ++ dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); ++ ++ tid.tid = PRIO_8021D_NC; /* Enable TID(7) for voice */ ++ tid.enable = TRUE; ++ bcm_mkiovar("ampdu_tid", (char *)&tid, sizeof(tid), iovbuf, sizeof(iovbuf)); ++ dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); ++#endif ++#if defined(SOFTAP_TPUT_ENHANCE) ++ if (dhd->op_mode & DHD_FLAG_HOSTAP_MODE) { ++ dhd_bus_setidletime(dhd, (int)100); ++#ifdef DHDTCPACK_SUPPRESS ++ dhd->tcpack_sup_enabled = FALSE; ++#endif ++#if defined(DHD_TCP_WINSIZE_ADJUST) ++ dhd_use_tcp_window_size_adjust = TRUE; ++#endif ++ ++ memset(buf, 0, sizeof(buf)); ++ bcm_mkiovar("bus:txglom_auto_control", 0, 0, buf, sizeof(buf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, buf, sizeof(buf), FALSE, 0)) < 0) { ++ glom = 0; ++ bcm_mkiovar("bus:txglom", (char *)&glom, 4, iovbuf, sizeof(iovbuf)); ++ dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); ++ } ++ else { ++ if (buf[0] == 0) { ++ glom = 1; ++ bcm_mkiovar("bus:txglom_auto_control", (char *)&glom, 4, iovbuf, ++ sizeof(iovbuf)); ++ dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); ++ } ++ } ++ } ++#endif /* SOFTAP_TPUT_ENHANCE */ ++ ++ /* query for 'ver' to get version info from firmware */ ++ memset(buf, 0, sizeof(buf)); ++ ptr = buf; ++ bcm_mkiovar("ver", (char *)&buf, 4, buf, sizeof(buf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, buf, sizeof(buf), FALSE, 0)) < 0) ++ DHD_ERROR(("%s failed %d\n", __FUNCTION__, ret)); ++ else { ++ bcmstrtok(&ptr, "\n", 0); ++ /* Print fw version info */ ++ DHD_ERROR(("Firmware version = %s\n", buf)); ++ dhd_set_version_info(dhd, buf); ++ } ++ ++#if defined(BCMSDIO) ++ dhd_txglom_enable(dhd, dhd->conf->bus_rxglom); ++ // terence 20151210: set bus:txglom after dhd_txglom_enable since it's possible changed in dhd_conf_set_txglom_params ++ dhd_conf_set_fw_string_cmd(dhd, "bus:txglom", dhd->conf->bus_txglom, 1, FALSE); ++#endif /* defined(BCMSDIO) */ ++ ++ dhd_conf_set_disable_proptx(dhd); ++#if defined(BCMSDIO) ++#ifdef PROP_TXSTATUS ++ if (disable_proptx || ++#ifdef PROP_TXSTATUS_VSDB ++ /* enable WLFC only if the firmware is VSDB when it is in STA mode */ ++ (dhd->op_mode != DHD_FLAG_HOSTAP_MODE && ++ dhd->op_mode != DHD_FLAG_IBSS_MODE) || ++#endif /* PROP_TXSTATUS_VSDB */ ++ FALSE) { ++ wlfc_enable = FALSE; ++ } ++ ++#ifndef DISABLE_11N ++ ret = dhd_wl_ioctl_cmd(dhd, WLC_DOWN, (char *)&wl_down, sizeof(wl_down), TRUE, 0); ++ bcm_mkiovar("ampdu_hostreorder", (char *)&hostreorder, 4, iovbuf, sizeof(iovbuf)); ++ if ((ret2 = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) { ++ DHD_ERROR(("%s wl ampdu_hostreorder failed %d\n", __FUNCTION__, ret2)); ++ if (ret2 != BCME_UNSUPPORTED) ++ ret = ret2; ++ if (ret2 != BCME_OK) ++ hostreorder = 0; ++ } ++#endif /* DISABLE_11N */ ++ ++#ifdef READ_CONFIG_FROM_FILE ++ dhd_preinit_config(dhd, 0); ++#endif /* READ_CONFIG_FROM_FILE */ ++ ++ if (wlfc_enable) ++ dhd_wlfc_init(dhd); ++#ifndef DISABLE_11N ++ else if (hostreorder) ++ dhd_wlfc_hostreorder_init(dhd); ++#endif /* DISABLE_11N */ ++ ++#endif /* PROP_TXSTATUS */ ++#endif /* BCMSDIO || BCMBUS */ ++#ifdef PCIE_FULL_DONGLE ++ /* For FD we need all the packets at DHD to handle intra-BSS forwarding */ ++ if (FW_SUPPORTED(dhd, ap)) { ++ wl_ap_isolate = AP_ISOLATE_SENDUP_ALL; ++ bcm_mkiovar("ap_isolate", (char *)&wl_ap_isolate, 4, iovbuf, sizeof(iovbuf)); ++ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) ++ DHD_ERROR(("%s failed %d\n", __FUNCTION__, ret)); ++ } ++#endif /* PCIE_FULL_DONGLE */ ++#ifdef PNO_SUPPORT ++ if (!dhd->pno_state) { ++ dhd_pno_init(dhd); ++ } ++#endif ++#ifdef WL11U ++ dhd_interworking_enable(dhd); ++#endif /* WL11U */ ++#ifndef WL_CFG80211 ++ dhd_wl_ioctl_cmd(dhd, WLC_UP, (char *)&up, sizeof(up), TRUE, 0); ++#endif ++ ++done: ++ ++ if (eventmask_msg) ++ kfree(eventmask_msg); ++ if (iov_buf) ++ kfree(iov_buf); ++ ++ return ret; ++} ++ ++ ++int ++dhd_iovar(dhd_pub_t *pub, int ifidx, char *name, char *cmd_buf, uint cmd_len, int set) ++{ ++ char buf[strlen(name) + 1 + cmd_len]; ++ int len = sizeof(buf); ++ wl_ioctl_t ioc; ++ int ret; ++ ++ len = bcm_mkiovar(name, cmd_buf, cmd_len, buf, len); ++ ++ memset(&ioc, 0, sizeof(ioc)); ++ ++ ioc.cmd = set? WLC_SET_VAR : WLC_GET_VAR; ++ ioc.buf = buf; ++ ioc.len = len; ++ ioc.set = set; ++ ++ ret = dhd_wl_ioctl(pub, ifidx, &ioc, ioc.buf, ioc.len); ++ if (!set && ret >= 0) ++ memcpy(cmd_buf, buf, cmd_len); ++ ++ return ret; ++} ++ ++int dhd_change_mtu(dhd_pub_t *dhdp, int new_mtu, int ifidx) ++{ ++ struct dhd_info *dhd = dhdp->info; ++ struct net_device *dev = NULL; ++ ++ ASSERT(dhd && dhd->iflist[ifidx]); ++ dev = dhd->iflist[ifidx]->net; ++ ASSERT(dev); ++ ++ if (netif_running(dev)) { ++ DHD_ERROR(("%s: Must be down to change its MTU\n", dev->name)); ++ return BCME_NOTDOWN; ++ } ++ ++#define DHD_MIN_MTU 1500 ++#define DHD_MAX_MTU 1752 ++ ++ if ((new_mtu < DHD_MIN_MTU) || (new_mtu > DHD_MAX_MTU)) { ++ DHD_ERROR(("%s: MTU size %d is invalid.\n", __FUNCTION__, new_mtu)); ++ return BCME_BADARG; ++ } ++ ++ dev->mtu = new_mtu; ++ return 0; ++} ++ ++#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, int idx) ++{ ++ u32 ipv4_buf[MAX_IPV4_ENTRIES]; /* temp save for AOE host_ip table */ ++ int i; ++ int ret; ++ ++ bzero(ipv4_buf, sizeof(ipv4_buf)); ++ ++ /* display what we've got */ ++ ret = dhd_arp_get_arp_hostip_table(dhd_pub, ipv4_buf, sizeof(ipv4_buf), idx); ++ DHD_ARPOE(("%s: hostip table read from Dongle:\n", __FUNCTION__)); ++#ifdef AOE_DBG ++ dhd_print_buf(ipv4_buf, 32, 4); /* max 8 IPs 4b each */ ++#endif ++ /* now we saved hoste_ip table, clr it in the dongle AOE */ ++ dhd_aoe_hostip_clr(dhd_pub, idx); ++ ++ if (ret) { ++ DHD_ERROR(("%s failed\n", __FUNCTION__)); ++ return; ++ } ++ ++ for (i = 0; i < MAX_IPV4_ENTRIES; i++) { ++ if (add && (ipv4_buf[i] == 0)) { ++ 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", ++ __FUNCTION__, ipa, i)); ++ } ++ ++ if (ipv4_buf[i] != 0) { ++ /* add back host_ip entries from our local cache */ ++ dhd_arp_offload_add_ip(dhd_pub, ipv4_buf[i], idx); ++ DHD_ARPOE(("%s: added IP:%x to dongle arp_hostip[%d]\n\n", ++ __FUNCTION__, ipv4_buf[i], i)); ++ } ++ } ++#ifdef AOE_DBG ++ /* see the resulting hostip table */ ++ dhd_arp_get_arp_hostip_table(dhd_pub, ipv4_buf, sizeof(ipv4_buf), idx); ++ DHD_ARPOE(("%s: read back arp_hostip table:\n", __FUNCTION__)); ++ dhd_print_buf(ipv4_buf, 32, 4); /* max 8 IPs 4b each */ ++#endif ++} ++ ++/* ++ * Notification mechanism from kernel to our driver. This function is called by the Linux kernel ++ * whenever there is an event related to an IP address. ++ * ptr : kernel provided pointer to IP address that has changed ++ */ ++static int dhd_inetaddr_notifier_call(struct notifier_block *this, ++ unsigned long event, ++ void *ptr) ++{ ++ struct in_ifaddr *ifa = (struct in_ifaddr *)ptr; ++ ++ dhd_info_t *dhd; ++ dhd_pub_t *dhd_pub; ++ int idx; ++ ++ if (!dhd_arp_enable) ++ return NOTIFY_DONE; ++ if (!ifa || !(ifa->ifa_dev->dev)) ++ return NOTIFY_DONE; ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)) ++ /* Filter notifications meant for non Broadcom devices */ ++ if ((ifa->ifa_dev->dev->netdev_ops != &dhd_ops_pri) && ++ (ifa->ifa_dev->dev->netdev_ops != &dhd_ops_virt)) { ++#if defined(WL_ENABLE_P2P_IF) ++ if (!wl_cfgp2p_is_ifops(ifa->ifa_dev->dev->netdev_ops)) ++#endif /* WL_ENABLE_P2P_IF */ ++ return NOTIFY_DONE; ++ } ++#endif /* LINUX_VERSION_CODE */ ++ ++ dhd = DHD_DEV_INFO(ifa->ifa_dev->dev); ++ if (!dhd) ++ return NOTIFY_DONE; ++ ++ dhd_pub = &dhd->pub; ++ ++ if (dhd_pub->arp_version == 1) { ++ idx = 0; ++ } ++ else { ++ for (idx = 0; idx < DHD_MAX_IFS; idx++) { ++ if (dhd->iflist[idx] && dhd->iflist[idx]->net == ifa->ifa_dev->dev) ++ break; ++ } ++ if (idx < DHD_MAX_IFS) ++ DHD_TRACE(("ifidx : %p %s %d\n", dhd->iflist[idx]->net, ++ dhd->iflist[idx]->name, dhd->iflist[idx]->idx)); ++ else { ++ DHD_ERROR(("Cannot find ifidx for(%s) set to 0\n", ifa->ifa_label)); ++ idx = 0; ++ } ++ } ++ ++ switch (event) { ++ case NETDEV_UP: ++ DHD_ARPOE(("%s: [%s] Up IP: 0x%x\n", ++ __FUNCTION__, ifa->ifa_label, ifa->ifa_address)); ++ ++ if (dhd->pub.busstate != DHD_BUS_DATA) { ++ DHD_ERROR(("%s: bus not ready, exit\n", __FUNCTION__)); ++ if (dhd->pend_ipaddr) { ++ DHD_ERROR(("%s: overwrite pending ipaddr: 0x%x\n", ++ __FUNCTION__, dhd->pend_ipaddr)); ++ } ++ dhd->pend_ipaddr = ifa->ifa_address; ++ break; ++ } ++ ++#ifdef AOE_IP_ALIAS_SUPPORT ++ DHD_ARPOE(("%s:add aliased IP to AOE hostip cache\n", ++ __FUNCTION__)); ++ aoe_update_host_ipv4_table(dhd_pub, ifa->ifa_address, TRUE, idx); ++#endif /* AOE_IP_ALIAS_SUPPORT */ ++ break; ++ ++ case NETDEV_DOWN: ++ DHD_ARPOE(("%s: [%s] Down IP: 0x%x\n", ++ __FUNCTION__, ifa->ifa_label, ifa->ifa_address)); ++ dhd->pend_ipaddr = 0; ++#ifdef AOE_IP_ALIAS_SUPPORT ++ DHD_ARPOE(("%s:interface is down, AOE clr all for this if\n", ++ __FUNCTION__)); ++ aoe_update_host_ipv4_table(dhd_pub, ifa->ifa_address, FALSE, idx); ++#else ++ dhd_aoe_hostip_clr(&dhd->pub, idx); ++ dhd_aoe_arp_clr(&dhd->pub, idx); ++#endif /* AOE_IP_ALIAS_SUPPORT */ ++ break; ++ ++ default: ++ DHD_ARPOE(("%s: do noting for [%s] Event: %lu\n", ++ __func__, ifa->ifa_label, event)); ++ break; ++ } ++ return NOTIFY_DONE; ++} ++#endif /* ARP_OFFLOAD_SUPPORT */ ++ ++#ifdef CONFIG_IPV6 ++/* Neighbor Discovery Offload: defered handler */ ++static void ++dhd_inet6_work_handler(void *dhd_info, void *event_data, u8 event) ++{ ++ struct ipv6_work_info_t *ndo_work = (struct ipv6_work_info_t *)event_data; ++ dhd_pub_t *pub = &((dhd_info_t *)dhd_info)->pub; ++ int ret; ++ ++ if (event != DHD_WQ_WORK_IPV6_NDO) { ++ DHD_ERROR(("%s: unexpected event \n", __FUNCTION__)); ++ return; ++ } ++ ++ if (!ndo_work) { ++ DHD_ERROR(("%s: ipv6 work info is not initialized \n", __FUNCTION__)); ++ return; ++ } ++ ++ if (!pub) { ++ DHD_ERROR(("%s: dhd pub is not initialized \n", __FUNCTION__)); ++ return; ++ } ++ ++ if (ndo_work->if_idx) { ++ DHD_ERROR(("%s: idx %d \n", __FUNCTION__, ndo_work->if_idx)); ++ return; ++ } ++ ++ switch (ndo_work->event) { ++ case NETDEV_UP: ++ DHD_TRACE(("%s: Enable NDO and add ipv6 into table \n", __FUNCTION__)); ++ ret = dhd_ndo_enable(pub, TRUE); ++ if (ret < 0) { ++ DHD_ERROR(("%s: Enabling NDO Failed %d\n", __FUNCTION__, ret)); ++ } ++ ++ ret = dhd_ndo_add_ip(pub, &ndo_work->ipv6_addr[0], ndo_work->if_idx); ++ if (ret < 0) { ++ DHD_ERROR(("%s: Adding host ip for NDO failed %d\n", ++ __FUNCTION__, ret)); ++ } ++ break; ++ case NETDEV_DOWN: ++ DHD_TRACE(("%s: clear ipv6 table \n", __FUNCTION__)); ++ ret = dhd_ndo_remove_ip(pub, ndo_work->if_idx); ++ if (ret < 0) { ++ DHD_ERROR(("%s: Removing host ip for NDO failed %d\n", ++ __FUNCTION__, ret)); ++ goto done; ++ } ++ ++ ret = dhd_ndo_enable(pub, FALSE); ++ if (ret < 0) { ++ DHD_ERROR(("%s: disabling NDO Failed %d\n", __FUNCTION__, ret)); ++ goto done; ++ } ++ break; ++ default: ++ DHD_ERROR(("%s: unknown notifier event \n", __FUNCTION__)); ++ break; ++ } ++done: ++ /* free ndo_work. alloced while scheduling the work */ ++ kfree(ndo_work); ++ ++ return; ++} ++ ++/* ++ * Neighbor Discovery Offload: Called when an interface ++ * is assigned with ipv6 address. ++ * Handles only primary interface ++ */ ++static int dhd_inet6addr_notifier_call(struct notifier_block *this, ++ unsigned long event, ++ void *ptr) ++{ ++ dhd_info_t *dhd; ++ dhd_pub_t *dhd_pub; ++ struct inet6_ifaddr *inet6_ifa = ptr; ++ struct in6_addr *ipv6_addr = &inet6_ifa->addr; ++ struct ipv6_work_info_t *ndo_info; ++ int idx = 0; /* REVISIT */ ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)) ++ /* Filter notifications meant for non Broadcom devices */ ++ if (inet6_ifa->idev->dev->netdev_ops != &dhd_ops_pri) { ++ return NOTIFY_DONE; ++ } ++#endif /* LINUX_VERSION_CODE */ ++ ++ dhd = DHD_DEV_INFO(inet6_ifa->idev->dev); ++ if (!dhd) ++ return NOTIFY_DONE; ++ ++ if (dhd->iflist[idx] && dhd->iflist[idx]->net != inet6_ifa->idev->dev) ++ return NOTIFY_DONE; ++ dhd_pub = &dhd->pub; ++ if (!FW_SUPPORTED(dhd_pub, ndoe)) ++ return NOTIFY_DONE; ++ ++ ndo_info = (struct ipv6_work_info_t *)kzalloc(sizeof(struct ipv6_work_info_t), GFP_ATOMIC); ++ if (!ndo_info) { ++ DHD_ERROR(("%s: ipv6 work alloc failed\n", __FUNCTION__)); ++ return NOTIFY_DONE; ++ } ++ ++ ndo_info->event = event; ++ ndo_info->if_idx = idx; ++ memcpy(&ndo_info->ipv6_addr[0], ipv6_addr, IPV6_ADDR_LEN); ++ ++ /* defer the work to thread as it may block kernel */ ++ dhd_deferred_schedule_work(dhd->dhd_deferred_wq, (void *)ndo_info, DHD_WQ_WORK_IPV6_NDO, ++ dhd_inet6_work_handler, DHD_WORK_PRIORITY_LOW); ++ return NOTIFY_DONE; ++} ++#endif /* #ifdef CONFIG_IPV6 */ ++ ++int ++dhd_register_if(dhd_pub_t *dhdp, int ifidx, bool need_rtnl_lock) ++{ ++ dhd_info_t *dhd = (dhd_info_t *)dhdp->info; ++ dhd_if_t *ifp; ++ struct net_device *net = NULL; ++ int err = 0; ++ uint8 temp_addr[ETHER_ADDR_LEN] = { 0x00, 0x90, 0x4c, 0x11, 0x22, 0x33 }; ++ ++ DHD_TRACE(("%s: ifidx %d\n", __FUNCTION__, ifidx)); ++ ++ ASSERT(dhd && dhd->iflist[ifidx]); ++ ifp = dhd->iflist[ifidx]; ++ net = ifp->net; ++ ASSERT(net && (ifp->idx == ifidx)); ++ ++#ifndef P2PONEINT ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)) ++ ASSERT(!net->open); ++ net->get_stats = dhd_get_stats; ++ net->do_ioctl = dhd_ioctl_entry; ++ net->hard_start_xmit = dhd_start_xmit; ++ net->set_mac_address = dhd_set_mac_address; ++ net->set_multicast_list = dhd_set_multicast_list; ++ net->open = net->stop = NULL; ++#else ++ ASSERT(!net->netdev_ops); ++ net->netdev_ops = &dhd_ops_virt; ++#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) */ ++#else ++ net->netdev_ops = &dhd_cfgp2p_ops_virt; ++#endif /* P2PONEINT */ ++ ++ /* Ok, link into the network layer... */ ++ if (ifidx == 0) { ++ /* ++ * device functions for the primary interface only ++ */ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)) ++ net->open = dhd_open; ++ net->stop = dhd_stop; ++#else ++ net->netdev_ops = &dhd_ops_pri; ++#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) */ ++ if (!ETHER_ISNULLADDR(dhd->pub.mac.octet)) ++ memcpy(temp_addr, dhd->pub.mac.octet, ETHER_ADDR_LEN); ++ } else { ++ /* ++ * We have to use the primary MAC for virtual interfaces ++ */ ++ memcpy(temp_addr, ifp->mac_addr, ETHER_ADDR_LEN); ++ /* ++ * Android sets the locally administered bit to indicate that this is a ++ * portable hotspot. This will not work in simultaneous AP/STA mode, ++ * nor with P2P. Need to set the Donlge's MAC address, and then use that. ++ */ ++ if (!memcmp(temp_addr, dhd->iflist[0]->mac_addr, ++ ETHER_ADDR_LEN)) { ++ DHD_ERROR(("%s interface [%s]: set locally administered bit in MAC\n", ++ __func__, net->name)); ++ temp_addr[0] |= 0x02; ++ } ++ } ++ ++ net->hard_header_len = ETH_HLEN + dhd->pub.hdrlen; ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) ++ net->ethtool_ops = &dhd_ethtool_ops; ++#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) */ ++ ++#if defined(WL_WIRELESS_EXT) ++#if WIRELESS_EXT < 19 ++ net->get_wireless_stats = dhd_get_wireless_stats; ++#endif /* WIRELESS_EXT < 19 */ ++#if WIRELESS_EXT > 12 ++ net->wireless_handlers = (struct iw_handler_def *)&wl_iw_handler_def; ++#endif /* WIRELESS_EXT > 12 */ ++#endif /* defined(WL_WIRELESS_EXT) */ ++ ++ dhd->pub.rxsz = DBUS_RX_BUFFER_SIZE_DHD(net); ++ ++ memcpy(net->dev_addr, temp_addr, ETHER_ADDR_LEN); ++ ++ if (ifidx == 0) ++ printf("%s\n", dhd_version); ++ ++ if (need_rtnl_lock) ++ err = register_netdev(net); ++ else ++ err = register_netdevice(net); ++ ++ if (err != 0) { ++ DHD_ERROR(("couldn't register the net device [%s], err %d\n", net->name, err)); ++ goto fail; ++ } ++ ++#ifdef SET_RPS_CPUS ++ err = custom_rps_map_set(net->_rx, RPS_CPUS_MASK, strlen(RPS_CPUS_MASK)); ++ if (err < 0) ++ DHD_ERROR(("%s : custom_rps_map_set done. error : %d\n", __FUNCTION__, err)); ++#endif /* SET_RPS_CPUS */ ++ ++ ++ ++ printf("Register interface [%s] MAC: "MACDBG"\n\n", net->name, ++ MAC2STRDBG(net->dev_addr)); ++ ++#if defined(SOFTAP) && defined(WL_WIRELESS_EXT) && !defined(WL_CFG80211) ++// wl_iw_iscan_set_scan_broadcast_prep(net, 1); ++#endif ++ ++#if 1 && (defined(BCMPCIE) || (defined(BCMLXSDMMC) && (LINUX_VERSION_CODE >= \ ++ KERNEL_VERSION(2, 6, 27)))) ++ if (ifidx == 0) { ++#ifdef BCMLXSDMMC ++ up(&dhd_registration_sem); ++#endif ++ if (!dhd_download_fw_on_driverload) { ++ dhd_net_bus_devreset(net, TRUE); ++#ifdef BCMLXSDMMC ++ dhd_net_bus_suspend(net); ++#endif /* BCMLXSDMMC */ ++ wifi_platform_set_power(dhdp->info->adapter, FALSE, WIFI_TURNOFF_DELAY); ++ } ++ } ++#endif /* OEM_ANDROID && (BCMPCIE || (BCMLXSDMMC && KERNEL_VERSION >= 2.6.27)) */ ++ return 0; ++ ++fail: ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) ++ net->open = NULL; ++#else ++ net->netdev_ops = NULL; ++#endif ++ return err; ++} ++ ++void ++dhd_bus_detach(dhd_pub_t *dhdp) ++{ ++ dhd_info_t *dhd; ++ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++ if (dhdp) { ++ dhd = (dhd_info_t *)dhdp->info; ++ if (dhd) { ++ ++ /* ++ * In case of Android cfg80211 driver, the bus is down in dhd_stop, ++ * calling stop again will cuase SD read/write errors. ++ */ ++ if (dhd->pub.busstate != DHD_BUS_DOWN) { ++ /* Stop the protocol module */ ++ dhd_prot_stop(&dhd->pub); ++ ++ /* Stop the bus module */ ++ dhd_bus_stop(dhd->pub.bus, TRUE); ++ } ++ ++#if defined(OOB_INTR_ONLY) || defined(BCMPCIE_OOB_HOST_WAKE) ++ dhd_bus_oob_intr_unregister(dhdp); ++#endif ++ } ++ } ++} ++ ++ ++void dhd_detach(dhd_pub_t *dhdp) ++{ ++ dhd_info_t *dhd; ++ unsigned long flags; ++ int timer_valid = FALSE; ++ ++ if (!dhdp) ++ return; ++ ++ dhd = (dhd_info_t *)dhdp->info; ++ if (!dhd) ++ return; ++ ++#if defined(CUSTOMER_HW20) && defined(WLANAUDIO) ++ dhd_global = NULL; ++#endif /* CUSTOMER_HW20 && WLANAUDIO */ ++ ++ DHD_TRACE(("%s: Enter state 0x%x\n", __FUNCTION__, dhd->dhd_state)); ++ ++ dhd->pub.up = 0; ++ if (!(dhd->dhd_state & DHD_ATTACH_STATE_DONE)) { ++ /* Give sufficient time for threads to start running in case ++ * dhd_attach() has failed ++ */ ++ OSL_SLEEP(100); ++ } ++ ++ if (dhd->dhd_state & DHD_ATTACH_STATE_PROT_ATTACH) { ++ dhd_bus_detach(dhdp); ++#ifdef PCIE_FULL_DONGLE ++ dhd_flow_rings_deinit(dhdp); ++#endif ++ ++ if (dhdp->prot) ++ dhd_prot_detach(dhdp); ++ } ++ ++#ifdef ARP_OFFLOAD_SUPPORT ++ if (dhd_inetaddr_notifier_registered) { ++ dhd_inetaddr_notifier_registered = FALSE; ++ unregister_inetaddr_notifier(&dhd_inetaddr_notifier); ++ } ++#endif /* ARP_OFFLOAD_SUPPORT */ ++#ifdef CONFIG_IPV6 ++ if (dhd_inet6addr_notifier_registered) { ++ dhd_inet6addr_notifier_registered = FALSE; ++ unregister_inet6addr_notifier(&dhd_inet6addr_notifier); ++ } ++#endif ++ ++#if defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND) ++ if (dhd->dhd_state & DHD_ATTACH_STATE_EARLYSUSPEND_DONE) { ++ if (dhd->early_suspend.suspend) ++ unregister_early_suspend(&dhd->early_suspend); ++ } ++#endif /* CONFIG_HAS_EARLYSUSPEND && DHD_USE_EARLYSUSPEND */ ++ ++#if defined(WL_WIRELESS_EXT) ++ if (dhd->dhd_state & DHD_ATTACH_STATE_WL_ATTACH) { ++ /* Detatch and unlink in the iw */ ++ wl_iw_detach(); ++ } ++#endif /* defined(WL_WIRELESS_EXT) */ ++ ++ /* delete all interfaces, start with virtual */ ++ if (dhd->dhd_state & DHD_ATTACH_STATE_ADD_IF) { ++ int i = 1; ++ dhd_if_t *ifp; ++ ++ /* Cleanup virtual interfaces */ ++ dhd_net_if_lock_local(dhd); ++ for (i = 1; i < DHD_MAX_IFS; i++) { ++ if (dhd->iflist[i]) ++ dhd_remove_if(&dhd->pub, i, TRUE); ++ } ++ dhd_net_if_unlock_local(dhd); ++ ++ /* delete primary interface 0 */ ++ ifp = dhd->iflist[0]; ++ ASSERT(ifp); ++ ASSERT(ifp->net); ++ if (ifp && ifp->net) { ++ ++ ++ ++ /* in unregister_netdev case, the interface gets freed by net->destructor ++ * (which is set to free_netdev) ++ */ ++ if (ifp->net->reg_state == NETREG_UNINITIALIZED) ++ free_netdev(ifp->net); ++ else { ++#ifdef SET_RPS_CPUS ++ custom_rps_map_clear(ifp->net->_rx); ++#endif /* SET_RPS_CPUS */ ++ unregister_netdev(ifp->net); ++ } ++ ifp->net = NULL; ++#ifdef DHD_WMF ++ dhd_wmf_cleanup(dhdp, 0); ++#endif /* DHD_WMF */ ++ ++ dhd_if_del_sta_list(ifp); ++ ++ MFREE(dhd->pub.osh, ifp, sizeof(*ifp)); ++ dhd->iflist[0] = NULL; ++ } ++ } ++ ++ /* Clear the watchdog timer */ ++ DHD_GENERAL_LOCK(&dhd->pub, flags); ++ timer_valid = dhd->wd_timer_valid; ++ dhd->wd_timer_valid = FALSE; ++ DHD_GENERAL_UNLOCK(&dhd->pub, flags); ++ if (timer_valid) ++ del_timer_sync(&dhd->timer); ++ ++ if (dhd->dhd_state & DHD_ATTACH_STATE_THREADS_CREATED) { ++ if (dhd->thr_wdt_ctl.thr_pid >= 0) { ++ PROC_STOP(&dhd->thr_wdt_ctl); ++ } ++ ++ if (dhd->rxthread_enabled && dhd->thr_rxf_ctl.thr_pid >= 0) { ++ PROC_STOP(&dhd->thr_rxf_ctl); ++ } ++ ++ if (dhd->thr_dpc_ctl.thr_pid >= 0) { ++ PROC_STOP(&dhd->thr_dpc_ctl); ++ } else ++ tasklet_kill(&dhd->tasklet); ++ } ++#ifdef WL_CFG80211 ++ if (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211) { ++ wl_cfg80211_detach(NULL); ++ dhd_monitor_uninit(); ++ } ++#endif ++ /* free deferred work queue */ ++ dhd_deferred_work_deinit(dhd->dhd_deferred_wq); ++ dhd->dhd_deferred_wq = NULL; ++ ++#ifdef SHOW_LOGTRACE ++ if (dhd->event_data.fmts) ++ kfree(dhd->event_data.fmts); ++ if (dhd->event_data.raw_fmts) ++ kfree(dhd->event_data.raw_fmts); ++#endif /* SHOW_LOGTRACE */ ++ ++#ifdef PNO_SUPPORT ++ if (dhdp->pno_state) ++ dhd_pno_deinit(dhdp); ++#endif ++#if defined(CONFIG_PM_SLEEP) ++ if (dhd_pm_notifier_registered) { ++ unregister_pm_notifier(&dhd_pm_notifier); ++ dhd_pm_notifier_registered = FALSE; ++ } ++#endif /* CONFIG_PM_SLEEP */ ++#ifdef DEBUG_CPU_FREQ ++ if (dhd->new_freq) ++ free_percpu(dhd->new_freq); ++ dhd->new_freq = NULL; ++ cpufreq_unregister_notifier(&dhd->freq_trans, CPUFREQ_TRANSITION_NOTIFIER); ++#endif ++ if (dhd->dhd_state & DHD_ATTACH_STATE_WAKELOCKS_INIT) { ++ DHD_TRACE(("wd wakelock count:%d\n", dhd->wakelock_wd_counter)); ++#ifdef CONFIG_HAS_WAKELOCK ++ dhd->wakelock_counter = 0; ++ dhd->wakelock_wd_counter = 0; ++ dhd->wakelock_rx_timeout_enable = 0; ++ dhd->wakelock_ctrl_timeout_enable = 0; ++ wake_lock_destroy(&dhd->wl_wifi); ++ wake_lock_destroy(&dhd->wl_rxwake); ++ wake_lock_destroy(&dhd->wl_ctrlwake); ++ wake_lock_destroy(&dhd->wl_wdwake); ++#ifdef BCMPCIE_OOB_HOST_WAKE ++ wake_lock_destroy(&dhd->wl_intrwake); ++#endif /* BCMPCIE_OOB_HOST_WAKE */ ++#endif /* CONFIG_HAS_WAKELOCK */ ++ } ++ ++ ++ ++ ++#ifdef DHDTCPACK_SUPPRESS ++ /* This will free all MEM allocated for TCPACK SUPPRESS */ ++ dhd_tcpack_suppress_set(&dhd->pub, TCPACK_SUP_OFF); ++#endif /* DHDTCPACK_SUPPRESS */ ++ dhd_conf_detach(dhdp); ++} ++ ++ ++void ++dhd_free(dhd_pub_t *dhdp) ++{ ++ dhd_info_t *dhd; ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++ if (dhdp) { ++ int i; ++ for (i = 0; i < ARRAYSIZE(dhdp->reorder_bufs); i++) { ++ if (dhdp->reorder_bufs[i]) { ++ reorder_info_t *ptr; ++ uint32 buf_size = sizeof(struct reorder_info); ++ ++ ptr = dhdp->reorder_bufs[i]; ++ ++ buf_size += ((ptr->max_idx + 1) * sizeof(void*)); ++ DHD_REORDER(("free flow id buf %d, maxidx is %d, buf_size %d\n", ++ i, ptr->max_idx, buf_size)); ++ ++ MFREE(dhdp->osh, dhdp->reorder_bufs[i], buf_size); ++ dhdp->reorder_bufs[i] = NULL; ++ } ++ } ++ ++ dhd_sta_pool_fini(dhdp, DHD_MAX_STA); ++ ++ dhd = (dhd_info_t *)dhdp->info; ++ /* If pointer is allocated by dhd_os_prealloc then avoid MFREE */ ++ if (dhd && ++ dhd != (dhd_info_t *)dhd_os_prealloc(dhdp, DHD_PREALLOC_DHD_INFO, 0, FALSE)) ++ MFREE(dhd->pub.osh, dhd, sizeof(*dhd)); ++ dhd = NULL; ++ } ++} ++ ++void ++dhd_clear(dhd_pub_t *dhdp) ++{ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++ if (dhdp) { ++ int i; ++ for (i = 0; i < ARRAYSIZE(dhdp->reorder_bufs); i++) { ++ if (dhdp->reorder_bufs[i]) { ++ reorder_info_t *ptr; ++ uint32 buf_size = sizeof(struct reorder_info); ++ ++ ptr = dhdp->reorder_bufs[i]; ++ ++ buf_size += ((ptr->max_idx + 1) * sizeof(void*)); ++ DHD_REORDER(("free flow id buf %d, maxidx is %d, buf_size %d\n", ++ i, ptr->max_idx, buf_size)); ++ ++ MFREE(dhdp->osh, dhdp->reorder_bufs[i], buf_size); ++ dhdp->reorder_bufs[i] = NULL; ++ } ++ } ++ ++ dhd_sta_pool_clear(dhdp, DHD_MAX_STA); ++ } ++} ++ ++static void ++dhd_module_cleanup(void) ++{ ++ printf("%s: Enter\n", __FUNCTION__); ++ ++ dhd_bus_unregister(); ++ ++ wl_android_exit(); ++ ++ dhd_wifi_platform_unregister_drv(); ++ printf("%s: Exit\n", __FUNCTION__); ++} ++ ++static void __exit ++dhd_module_exit(void) ++{ ++ dhd_module_cleanup(); ++ unregister_reboot_notifier(&dhd_reboot_notifier); ++} ++ ++static int __init ++dhd_module_init(void) ++{ ++ int err; ++ int retry = POWERUP_MAX_RETRY; ++ ++ printf("%s: in\n", __FUNCTION__); ++ ++ DHD_PERIM_RADIO_INIT(); ++ ++ if (firmware_path[0] != '\0') { ++ strncpy(fw_bak_path, firmware_path, MOD_PARAM_PATHLEN); ++ fw_bak_path[MOD_PARAM_PATHLEN-1] = '\0'; ++ } ++ ++ if (nvram_path[0] != '\0') { ++ strncpy(nv_bak_path, nvram_path, MOD_PARAM_PATHLEN); ++ nv_bak_path[MOD_PARAM_PATHLEN-1] = '\0'; ++ } ++ ++ do { ++ err = dhd_wifi_platform_register_drv(); ++ if (!err) { ++ register_reboot_notifier(&dhd_reboot_notifier); ++ break; ++ } ++ else { ++ DHD_ERROR(("%s: Failed to load the driver, try cnt %d\n", ++ __FUNCTION__, retry)); ++ strncpy(firmware_path, fw_bak_path, MOD_PARAM_PATHLEN); ++ firmware_path[MOD_PARAM_PATHLEN-1] = '\0'; ++ strncpy(nvram_path, nv_bak_path, MOD_PARAM_PATHLEN); ++ nvram_path[MOD_PARAM_PATHLEN-1] = '\0'; ++ } ++ } while (retry--); ++ ++ if (err) { ++ DHD_ERROR(("%s: Failed to load driver max retry reached**\n", __FUNCTION__)); ++ } ++ ++ printf("%s: Exit err=%d\n", __FUNCTION__, err); ++ return err; ++} ++ ++static int ++dhd_reboot_callback(struct notifier_block *this, unsigned long code, void *unused) ++{ ++ DHD_TRACE(("%s: code = %ld\n", __FUNCTION__, code)); ++ if (code == SYS_RESTART) { ++ } ++ ++ return NOTIFY_DONE; ++} ++ ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) ++#if defined(CONFIG_DEFERRED_INITCALLS) ++deferred_module_init(dhd_module_init); ++#elif defined(USE_LATE_INITCALL_SYNC) ++late_initcall_sync(dhd_module_init); ++#else ++late_initcall(dhd_module_init); ++#endif /* USE_LATE_INITCALL_SYNC */ ++#else ++module_init(dhd_module_init); ++#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) */ ++ ++module_exit(dhd_module_exit); ++ ++/* ++ * OS specific functions required to implement DHD driver in OS independent way ++ */ ++int ++dhd_os_proto_block(dhd_pub_t *pub) ++{ ++ dhd_info_t * dhd = (dhd_info_t *)(pub->info); ++ ++ if (dhd) { ++ DHD_PERIM_UNLOCK(pub); ++ ++ down(&dhd->proto_sem); ++ ++ DHD_PERIM_LOCK(pub); ++ return 1; ++ } ++ ++ return 0; ++} ++ ++int ++dhd_os_proto_unblock(dhd_pub_t *pub) ++{ ++ dhd_info_t * dhd = (dhd_info_t *)(pub->info); ++ ++ if (dhd) { ++ up(&dhd->proto_sem); ++ return 1; ++ } ++ ++ return 0; ++} ++ ++unsigned int ++dhd_os_get_ioctl_resp_timeout(void) ++{ ++ return ((unsigned int)dhd_ioctl_timeout_msec); ++} ++ ++void ++dhd_os_set_ioctl_resp_timeout(unsigned int timeout_msec) ++{ ++ dhd_ioctl_timeout_msec = (int)timeout_msec; ++} ++ ++int ++dhd_os_ioctl_resp_wait(dhd_pub_t *pub, uint *condition, bool *pending) ++{ ++ dhd_info_t * dhd = (dhd_info_t *)(pub->info); ++ int timeout; ++ ++ /* Convert timeout in millsecond to jiffies */ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) ++ timeout = msecs_to_jiffies(dhd_ioctl_timeout_msec); ++#else ++ timeout = dhd_ioctl_timeout_msec * HZ / 1000; ++#endif ++ ++ DHD_PERIM_UNLOCK(pub); ++ ++ timeout = wait_event_timeout(dhd->ioctl_resp_wait, (*condition), timeout); ++ ++ DHD_PERIM_LOCK(pub); ++ ++ return timeout; ++} ++ ++int ++dhd_os_ioctl_resp_wake(dhd_pub_t *pub) ++{ ++ dhd_info_t *dhd = (dhd_info_t *)(pub->info); ++ ++ wake_up(&dhd->ioctl_resp_wait); ++ return 0; ++} ++ ++void ++dhd_os_wd_timer_extend(void *bus, bool extend) ++{ ++ dhd_pub_t *pub = bus; ++ dhd_info_t *dhd = (dhd_info_t *)pub->info; ++ ++ if (extend) ++ dhd_os_wd_timer(bus, WATCHDOG_EXTEND_INTERVAL); ++ else ++ dhd_os_wd_timer(bus, dhd->default_wd_interval); ++} ++ ++ ++void ++dhd_os_wd_timer(void *bus, uint wdtick) ++{ ++ dhd_pub_t *pub = bus; ++ dhd_info_t *dhd = (dhd_info_t *)pub->info; ++ unsigned long flags; ++ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++ if (!dhd) { ++ DHD_ERROR(("%s: dhd NULL\n", __FUNCTION__)); ++ return; ++ } ++ ++ DHD_GENERAL_LOCK(pub, flags); ++ ++ /* don't start the wd until fw is loaded */ ++ if (pub->busstate == DHD_BUS_DOWN) { ++ DHD_GENERAL_UNLOCK(pub, flags); ++ if (!wdtick) ++ DHD_OS_WD_WAKE_UNLOCK(pub); ++ return; ++ } ++ ++ /* Totally stop the timer */ ++ if (!wdtick && dhd->wd_timer_valid == TRUE) { ++ dhd->wd_timer_valid = FALSE; ++ DHD_GENERAL_UNLOCK(pub, flags); ++ del_timer_sync(&dhd->timer); ++ DHD_OS_WD_WAKE_UNLOCK(pub); ++ return; ++ } ++ ++ if (wdtick) { ++ DHD_OS_WD_WAKE_LOCK(pub); ++ dhd_watchdog_ms = (uint)wdtick; ++ /* Re arm the timer, at last watchdog period */ ++ mod_timer(&dhd->timer, jiffies + msecs_to_jiffies(dhd_watchdog_ms)); ++ dhd->wd_timer_valid = TRUE; ++ } ++ DHD_GENERAL_UNLOCK(pub, flags); ++} ++ ++void * ++dhd_os_open_image(char *filename) ++{ ++ struct file *fp; ++ ++ fp = filp_open(filename, O_RDONLY, 0); ++ /* ++ * 2.6.11 (FC4) supports filp_open() but later revs don't? ++ * Alternative: ++ * fp = open_namei(AT_FDCWD, filename, O_RD, 0); ++ * ??? ++ */ ++ if (IS_ERR(fp)) ++ fp = NULL; ++ ++ return fp; ++} ++ ++int ++dhd_os_get_image_block(char *buf, int len, void *image) ++{ ++ struct file *fp = (struct file *)image; ++ int rdlen; ++ ++ if (!image) ++ return 0; ++ ++ rdlen = kernel_read(fp, fp->f_pos, buf, len); ++ if (rdlen > 0) ++ fp->f_pos += rdlen; ++ ++ return rdlen; ++} ++ ++void ++dhd_os_close_image(void *image) ++{ ++ if (image) ++ filp_close((struct file *)image, NULL); ++} ++ ++void ++dhd_os_sdlock(dhd_pub_t *pub) ++{ ++ dhd_info_t *dhd; ++ ++ dhd = (dhd_info_t *)(pub->info); ++ ++ if (dhd_dpc_prio >= 0) ++ down(&dhd->sdsem); ++ else ++ spin_lock_bh(&dhd->sdlock); ++} ++ ++void ++dhd_os_sdunlock(dhd_pub_t *pub) ++{ ++ dhd_info_t *dhd; ++ ++ dhd = (dhd_info_t *)(pub->info); ++ ++ if (dhd_dpc_prio >= 0) ++ up(&dhd->sdsem); ++ else ++ spin_unlock_bh(&dhd->sdlock); ++} ++ ++void ++dhd_os_sdlock_txq(dhd_pub_t *pub) ++{ ++ dhd_info_t *dhd; ++ ++ dhd = (dhd_info_t *)(pub->info); ++ spin_lock_bh(&dhd->txqlock); ++} ++ ++void ++dhd_os_sdunlock_txq(dhd_pub_t *pub) ++{ ++ dhd_info_t *dhd; ++ ++ dhd = (dhd_info_t *)(pub->info); ++ spin_unlock_bh(&dhd->txqlock); ++} ++ ++void ++dhd_os_sdlock_rxq(dhd_pub_t *pub) ++{ ++} ++ ++void ++dhd_os_sdunlock_rxq(dhd_pub_t *pub) ++{ ++} ++ ++static void ++dhd_os_rxflock(dhd_pub_t *pub) ++{ ++ dhd_info_t *dhd; ++ ++ dhd = (dhd_info_t *)(pub->info); ++ spin_lock_bh(&dhd->rxf_lock); ++ ++} ++ ++static void ++dhd_os_rxfunlock(dhd_pub_t *pub) ++{ ++ dhd_info_t *dhd; ++ ++ dhd = (dhd_info_t *)(pub->info); ++ spin_unlock_bh(&dhd->rxf_lock); ++} ++ ++#ifdef DHDTCPACK_SUPPRESS ++void ++dhd_os_tcpacklock(dhd_pub_t *pub) ++{ ++ dhd_info_t *dhd; ++ ++ dhd = (dhd_info_t *)(pub->info); ++ spin_lock_bh(&dhd->tcpack_lock); ++ ++} ++ ++void ++dhd_os_tcpackunlock(dhd_pub_t *pub) ++{ ++ dhd_info_t *dhd; ++ ++ dhd = (dhd_info_t *)(pub->info); ++ spin_unlock_bh(&dhd->tcpack_lock); ++} ++#endif /* DHDTCPACK_SUPPRESS */ ++ ++uint8* dhd_os_prealloc(dhd_pub_t *dhdpub, int section, uint size, bool kmalloc_if_fail) ++{ ++ uint8* buf; ++ gfp_t flags = CAN_SLEEP() ? GFP_KERNEL: GFP_ATOMIC; ++ ++ buf = (uint8*)wifi_platform_prealloc(dhdpub->info->adapter, section, size); ++ if (buf == NULL) { ++ DHD_ERROR(("%s: failed to alloc memory, section: %d," ++ " size: %dbytes\n", __FUNCTION__, section, size)); ++ if (kmalloc_if_fail) ++ buf = kmalloc(size, flags); ++ } ++ ++ return buf; ++} ++ ++void dhd_os_prefree(dhd_pub_t *dhdpub, void *addr, uint size) ++{ ++} ++ ++#if defined(WL_WIRELESS_EXT) ++struct iw_statistics * ++dhd_get_wireless_stats(struct net_device *dev) ++{ ++ int res = 0; ++ dhd_info_t *dhd = DHD_DEV_INFO(dev); ++ ++ if (!dhd->pub.up) { ++ return NULL; ++ } ++ ++ res = wl_iw_get_wireless_stats(dev, &dhd->iw.wstats); ++ ++ if (res == 0) ++ return &dhd->iw.wstats; ++ else ++ return NULL; ++} ++#endif /* defined(WL_WIRELESS_EXT) */ ++ ++#if defined(CUSTOMER_HW20) && defined(WLANAUDIO) ++static int ++dhd_wlanaudio_event(dhd_info_t *dhd, int *ifidx, void *pktdata, ++ wl_event_msg_t *event, void **data) ++{ ++ int cnt; ++ char eabuf[ETHER_ADDR_STR_LEN]; ++ struct ether_addr *addr = &event->addr; ++ uint32 type = ntoh32_ua((void *)&event->event_type); ++ ++ switch (type) { ++ case WLC_E_TXFAIL: ++ if (addr != NULL) ++ bcm_ether_ntoa(addr, eabuf); ++ else ++ return (BCME_ERROR); ++ ++ for (cnt = 0; cnt < MAX_WLANAUDIO_BLACKLIST; cnt++) { ++ if (dhd->wlanaudio_blist[cnt].is_blacklist) ++ break; ++ ++ if (!bcmp(&dhd->wlanaudio_blist[cnt].blacklist_addr, ++ addr, ETHER_ADDR_LEN)) { ++ /* Mac address is Same */ ++ dhd->wlanaudio_blist[cnt].cnt++; ++ ++ if (dhd->wlanaudio_blist[cnt].cnt < 15) { ++ /* black list is false */ ++ if ((dhd->wlanaudio_blist[cnt].cnt > 10) && ++ (jiffies - dhd->wlanaudio_blist[cnt].txfail_jiffies ++ < 100)) { ++ dhd->wlanaudio_blist[cnt].is_blacklist = true; ++ dhd->is_wlanaudio_blist = true; ++ } ++ } else { ++ if ((!dhd->wlanaudio_blist[cnt].is_blacklist) && ++ (jiffies - dhd->wlanaudio_blist[cnt].txfail_jiffies ++ > 100)) { ++ ++ bzero(&dhd->wlanaudio_blist[cnt], ++ sizeof(struct wlanaudio_blacklist)); ++ } ++ } ++ break; ++ } else if ((!dhd->wlanaudio_blist[cnt].is_blacklist) && ++ (!dhd->wlanaudio_blist[cnt].cnt)) { ++ bcopy(addr, ++ (char*)&dhd->wlanaudio_blist[cnt].blacklist_addr, ++ ETHER_ADDR_LEN); ++ dhd->wlanaudio_blist[cnt].cnt++; ++ dhd->wlanaudio_blist[cnt].txfail_jiffies = jiffies; ++ ++ bcm_ether_ntoa(&dhd->wlanaudio_blist[cnt].blacklist_addr, eabuf); ++ break; ++ } ++ } ++ break; ++ case WLC_E_AUTH : ++ case WLC_E_AUTH_IND : ++ case WLC_E_DEAUTH : ++ case WLC_E_DEAUTH_IND : ++ case WLC_E_ASSOC: ++ case WLC_E_ASSOC_IND: ++ case WLC_E_REASSOC: ++ case WLC_E_REASSOC_IND: ++ case WLC_E_DISASSOC: ++ case WLC_E_DISASSOC_IND: ++ { ++ int bl_cnt = 0; ++ ++ if (addr != NULL) ++ bcm_ether_ntoa(addr, eabuf); ++ else ++ return (BCME_ERROR); ++ ++ for (cnt = 0; cnt < MAX_WLANAUDIO_BLACKLIST; cnt++) { ++ if (!bcmp(&dhd->wlanaudio_blist[cnt].blacklist_addr, ++ addr, ETHER_ADDR_LEN)) { ++ /* Mac address is Same */ ++ if (dhd->wlanaudio_blist[cnt].is_blacklist) { ++ /* black list is true */ ++ bzero(&dhd->wlanaudio_blist[cnt], ++ sizeof(struct wlanaudio_blacklist)); ++ } ++ } ++ } ++ ++ for (cnt = 0; cnt < MAX_WLANAUDIO_BLACKLIST; cnt++) { ++ if (dhd->wlanaudio_blist[cnt].is_blacklist) ++ bl_cnt++; ++ } ++ ++ if (!bl_cnt) ++ { ++ dhd->is_wlanaudio_blist = false; ++ } ++ ++ break; ++ } ++ } ++ return BCME_OK; ++} ++#endif /* CUSTOMER_HW20 && WLANAUDIO */ ++static int ++dhd_wl_host_event(dhd_info_t *dhd, int *ifidx, void *pktdata, ++ wl_event_msg_t *event, void **data) ++{ ++ int bcmerror = 0; ++ ++ ASSERT(dhd != NULL); ++ ++#if defined(CUSTOMER_HW20) && defined(WLANAUDIO) ++ bcmerror = dhd_wlanaudio_event(dhd, ifidx, pktdata, event, data); ++ ++ if (bcmerror != BCME_OK) ++ return (bcmerror); ++#endif /* CUSTOMER_HW20 && WLANAUDIO */ ++ ++#ifdef SHOW_LOGTRACE ++ bcmerror = wl_host_event(&dhd->pub, ifidx, pktdata, event, data, &dhd->event_data); ++#else ++ bcmerror = wl_host_event(&dhd->pub, ifidx, pktdata, event, data, NULL); ++#endif /* SHOW_LOGTRACE */ ++ ++ if (bcmerror != BCME_OK) ++ return (bcmerror); ++ ++#if defined(WL_WIRELESS_EXT) ++ if (event->bsscfgidx == 0) { ++ /* ++ * Wireless ext is on primary interface only ++ */ ++ ++ ASSERT(dhd->iflist[*ifidx] != NULL); ++ ASSERT(dhd->iflist[*ifidx]->net != NULL); ++ ++ if (dhd->iflist[*ifidx]->net) { ++ wl_iw_event(dhd->iflist[*ifidx]->net, event, *data); ++ } ++ } ++#endif /* defined(WL_WIRELESS_EXT) */ ++ ++#ifdef WL_CFG80211 ++ ASSERT(dhd->iflist[*ifidx] != NULL); ++ ASSERT(dhd->iflist[*ifidx]->net != NULL); ++ if (dhd->iflist[*ifidx]->net) ++ wl_cfg80211_event(dhd->iflist[*ifidx]->net, event, *data); ++#endif /* defined(WL_CFG80211) */ ++ ++ return (bcmerror); ++} ++ ++/* send up locally generated event */ ++void ++dhd_sendup_event(dhd_pub_t *dhdp, wl_event_msg_t *event, void *data) ++{ ++ switch (ntoh32(event->event_type)) { ++#ifdef WLBTAMP ++ /* Send up locally generated AMP HCI Events */ ++ case WLC_E_BTA_HCI_EVENT: { ++ struct sk_buff *p, *skb; ++ bcm_event_t *msg; ++ wl_event_msg_t *p_bcm_event; ++ char *ptr; ++ uint32 len; ++ uint32 pktlen; ++ dhd_if_t *ifp; ++ dhd_info_t *dhd; ++ uchar *eth; ++ int ifidx; ++ ++ len = ntoh32(event->datalen); ++ pktlen = sizeof(bcm_event_t) + len + 2; ++ dhd = dhdp->info; ++ ifidx = dhd_ifname2idx(dhd, event->ifname); ++ ++ if ((p = PKTGET(dhdp->osh, pktlen, FALSE))) { ++ ASSERT(ISALIGNED((uintptr)PKTDATA(dhdp->osh, p), sizeof(uint32))); ++ ++ msg = (bcm_event_t *) PKTDATA(dhdp->osh, p); ++ ++ bcopy(&dhdp->mac, &msg->eth.ether_dhost, ETHER_ADDR_LEN); ++ bcopy(&dhdp->mac, &msg->eth.ether_shost, ETHER_ADDR_LEN); ++ ETHER_TOGGLE_LOCALADDR(&msg->eth.ether_shost); ++ ++ msg->eth.ether_type = hton16(ETHER_TYPE_BRCM); ++ ++ /* BCM Vendor specific header... */ ++ msg->bcm_hdr.subtype = hton16(BCMILCP_SUBTYPE_VENDOR_LONG); ++ msg->bcm_hdr.version = BCMILCP_BCM_SUBTYPEHDR_VERSION; ++ bcopy(BRCM_OUI, &msg->bcm_hdr.oui[0], DOT11_OUI_LEN); ++ ++ /* vendor spec header length + pvt data length (private indication ++ * hdr + actual message itself) ++ */ ++ msg->bcm_hdr.length = hton16(BCMILCP_BCM_SUBTYPEHDR_MINLENGTH + ++ BCM_MSG_LEN + sizeof(wl_event_msg_t) + (uint16)len); ++ msg->bcm_hdr.usr_subtype = hton16(BCMILCP_BCM_SUBTYPE_EVENT); ++ ++ PKTSETLEN(dhdp->osh, p, (sizeof(bcm_event_t) + len + 2)); ++ ++ /* copy wl_event_msg_t into sk_buf */ ++ ++ /* pointer to wl_event_msg_t in sk_buf */ ++ p_bcm_event = &msg->event; ++ bcopy(event, p_bcm_event, sizeof(wl_event_msg_t)); ++ ++ /* copy hci event into sk_buf */ ++ bcopy(data, (p_bcm_event + 1), len); ++ ++ msg->bcm_hdr.length = hton16(sizeof(wl_event_msg_t) + ++ ntoh16(msg->bcm_hdr.length)); ++ PKTSETLEN(dhdp->osh, p, (sizeof(bcm_event_t) + len + 2)); ++ ++ ptr = (char *)(msg + 1); ++ /* Last 2 bytes of the message are 0x00 0x00 to signal that there ++ * are no ethertypes which are following this ++ */ ++ ptr[len+0] = 0x00; ++ ptr[len+1] = 0x00; ++ ++ skb = PKTTONATIVE(dhdp->osh, p); ++ eth = skb->data; ++ len = skb->len; ++ ++ ifp = dhd->iflist[ifidx]; ++ if (ifp == NULL) ++ ifp = dhd->iflist[0]; ++ ++ ASSERT(ifp); ++ skb->dev = ifp->net; ++ skb->protocol = eth_type_trans(skb, skb->dev); ++ ++ skb->data = eth; ++ skb->len = len; ++ ++ /* Strip header, count, deliver upward */ ++ skb_pull(skb, ETH_HLEN); ++ ++ /* Send the packet */ ++ if (in_interrupt()) { ++ netif_rx(skb); ++ } else { ++ netif_rx_ni(skb); ++ } ++ } ++ else { ++ /* Could not allocate a sk_buf */ ++ DHD_ERROR(("%s: unable to alloc sk_buf\n", __FUNCTION__)); ++ } ++ break; ++ } /* case WLC_E_BTA_HCI_EVENT */ ++#endif /* WLBTAMP */ ++ ++ default: ++ break; ++ } ++} ++ ++#ifdef LOG_INTO_TCPDUMP ++void ++dhd_sendup_log(dhd_pub_t *dhdp, void *data, int data_len) ++{ ++ struct sk_buff *p, *skb; ++ uint32 pktlen; ++ int len; ++ dhd_if_t *ifp; ++ dhd_info_t *dhd; ++ uchar *skb_data; ++ int ifidx = 0; ++ struct ether_header eth; ++ ++ pktlen = sizeof(eth) + data_len; ++ dhd = dhdp->info; ++ ++ if ((p = PKTGET(dhdp->osh, pktlen, FALSE))) { ++ ASSERT(ISALIGNED((uintptr)PKTDATA(dhdp->osh, p), sizeof(uint32))); ++ ++ bcopy(&dhdp->mac, ð.ether_dhost, ETHER_ADDR_LEN); ++ bcopy(&dhdp->mac, ð.ether_shost, ETHER_ADDR_LEN); ++ ETHER_TOGGLE_LOCALADDR(ð.ether_shost); ++ eth.ether_type = hton16(ETHER_TYPE_BRCM); ++ ++ bcopy((void *)ð, PKTDATA(dhdp->osh, p), sizeof(eth)); ++ bcopy(data, PKTDATA(dhdp->osh, p) + sizeof(eth), data_len); ++ skb = PKTTONATIVE(dhdp->osh, p); ++ skb_data = skb->data; ++ len = skb->len; ++ ++ ifidx = dhd_ifname2idx(dhd, "wlan0"); ++ ifp = dhd->iflist[ifidx]; ++ if (ifp == NULL) ++ ifp = dhd->iflist[0]; ++ ++ ASSERT(ifp); ++ skb->dev = ifp->net; ++ skb->protocol = eth_type_trans(skb, skb->dev); ++ skb->data = skb_data; ++ skb->len = len; ++ ++ /* Strip header, count, deliver upward */ ++ skb_pull(skb, ETH_HLEN); ++ ++ /* Send the packet */ ++ if (in_interrupt()) { ++ netif_rx(skb); ++ } else { ++ netif_rx_ni(skb); ++ } ++ } ++ else { ++ /* Could not allocate a sk_buf */ ++ DHD_ERROR(("%s: unable to alloc sk_buf\n", __FUNCTION__)); ++ } ++} ++#endif /* LOG_INTO_TCPDUMP */ ++ ++void dhd_wait_for_event(dhd_pub_t *dhd, bool *lockvar) ++{ ++#if defined(BCMSDIO) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) ++ struct dhd_info *dhdinfo = dhd->info; ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) ++ int timeout = msecs_to_jiffies(IOCTL_RESP_TIMEOUT); ++#else ++ int timeout = (IOCTL_RESP_TIMEOUT / 1000) * HZ; ++#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ ++ ++ dhd_os_sdunlock(dhd); ++ wait_event_timeout(dhdinfo->ctrl_wait, (*lockvar == FALSE), timeout); ++ dhd_os_sdlock(dhd); ++#endif /* defined(BCMSDIO) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) */ ++ return; ++} ++ ++void dhd_wait_event_wakeup(dhd_pub_t *dhd) ++{ ++#if defined(BCMSDIO) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) ++ struct dhd_info *dhdinfo = dhd->info; ++ if (waitqueue_active(&dhdinfo->ctrl_wait)) ++ wake_up(&dhdinfo->ctrl_wait); ++#endif ++ return; ++} ++ ++#if defined(BCMSDIO) || defined(BCMPCIE) ++int ++dhd_net_bus_devreset(struct net_device *dev, uint8 flag) ++{ ++ int ret = 0; ++ dhd_info_t *dhd = DHD_DEV_INFO(dev); ++ ++ if (flag == TRUE) { ++ /* Issue wl down command before resetting the chip */ ++ if (dhd_wl_ioctl_cmd(&dhd->pub, WLC_DOWN, NULL, 0, TRUE, 0) < 0) { ++ DHD_TRACE(("%s: wl down failed\n", __FUNCTION__)); ++ } ++#ifdef PROP_TXSTATUS ++ if (dhd->pub.wlfc_enabled) ++ dhd_wlfc_deinit(&dhd->pub); ++#endif /* PROP_TXSTATUS */ ++#ifdef PNO_SUPPORT ++ if (dhd->pub.pno_state) ++ dhd_pno_deinit(&dhd->pub); ++#endif ++ } ++ ++#ifdef BCMSDIO ++ if (!flag) { ++ dhd_update_fw_nv_path(dhd); ++ /* update firmware and nvram path to sdio bus */ ++ dhd_bus_update_fw_nv_path(dhd->pub.bus, ++ dhd->fw_path, dhd->nv_path, dhd->conf_path); ++ } ++#endif /* BCMSDIO */ ++ ++ ret = dhd_bus_devreset(&dhd->pub, flag); ++ if (ret) { ++ DHD_ERROR(("%s: dhd_bus_devreset: %d\n", __FUNCTION__, ret)); ++ return ret; ++ } ++ ++ return ret; ++} ++ ++#ifdef BCMSDIO ++int ++dhd_net_bus_suspend(struct net_device *dev) ++{ ++ dhd_info_t *dhd = DHD_DEV_INFO(dev); ++ return dhd_bus_suspend(&dhd->pub); ++} ++ ++int ++dhd_net_bus_resume(struct net_device *dev, uint8 stage) ++{ ++ dhd_info_t *dhd = DHD_DEV_INFO(dev); ++ return dhd_bus_resume(&dhd->pub, stage); ++} ++ ++#endif /* BCMSDIO */ ++#endif /* BCMSDIO || BCMPCIE */ ++ ++int net_os_set_suspend_disable(struct net_device *dev, int val) ++{ ++ dhd_info_t *dhd = DHD_DEV_INFO(dev); ++ int ret = 0; ++ ++ if (dhd) { ++ ret = dhd->pub.suspend_disable_flag; ++ dhd->pub.suspend_disable_flag = val; ++ } ++ return ret; ++} ++ ++int net_os_set_suspend(struct net_device *dev, int val, int force) ++{ ++ int ret = 0; ++ dhd_info_t *dhd = DHD_DEV_INFO(dev); ++ ++ if (dhd) { ++#if defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND) ++ ret = dhd_set_suspend(val, &dhd->pub); ++#else ++ ret = dhd_suspend_resume_helper(dhd, val, force); ++#endif ++#ifdef WL_CFG80211 ++ wl_cfg80211_update_power_mode(dev); ++#endif ++ } ++ return ret; ++} ++ ++int net_os_set_suspend_bcn_li_dtim(struct net_device *dev, int val) ++{ ++ dhd_info_t *dhd = DHD_DEV_INFO(dev); ++ ++ if (dhd) ++ dhd->pub.suspend_bcn_li_dtim = val; ++ ++ return 0; ++} ++ ++#ifdef PKT_FILTER_SUPPORT ++int net_os_rxfilter_add_remove(struct net_device *dev, int add_remove, int num) ++{ ++ dhd_info_t *dhd = DHD_DEV_INFO(dev); ++ char *filterp = NULL; ++ int filter_id = 0; ++ int ret = 0; ++ ++ if (!dhd_master_mode) ++ add_remove = !add_remove; ++ ++ if (!dhd || (num == DHD_UNICAST_FILTER_NUM) || ++ (num == DHD_MDNS_FILTER_NUM)) ++ return ret; ++ if (num >= dhd->pub.pktfilter_count) ++ return -EINVAL; ++ switch (num) { ++ case DHD_BROADCAST_FILTER_NUM: ++ filterp = "101 0 0 0 0xFFFFFFFFFFFF 0xFFFFFFFFFFFF"; ++ filter_id = 101; ++ break; ++ case DHD_MULTICAST4_FILTER_NUM: ++ filterp = "102 0 0 0 0xFFFFFF 0x01005E"; ++ filter_id = 102; ++ break; ++ case DHD_MULTICAST6_FILTER_NUM: ++ filterp = "103 0 0 0 0xFFFF 0x3333"; ++ filter_id = 103; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ /* Add filter */ ++ if (add_remove) { ++ dhd->pub.pktfilter[num] = filterp; ++ dhd_pktfilter_offload_set(&dhd->pub, dhd->pub.pktfilter[num]); ++ } else { /* Delete filter */ ++ if (dhd->pub.pktfilter[num] != NULL) { ++ dhd_pktfilter_offload_delete(&dhd->pub, filter_id); ++ dhd->pub.pktfilter[num] = NULL; ++ } ++ } ++ return ret; ++} ++ ++int dhd_os_enable_packet_filter(dhd_pub_t *dhdp, int val) ++ ++{ ++ int ret = 0; ++ ++ /* Packet filtering is set only if we still in early-suspend and ++ * we need either to turn it ON or turn it OFF ++ * We can always turn it OFF in case of early-suspend, but we turn it ++ * back ON only if suspend_disable_flag was not set ++ */ ++ if (dhdp && dhdp->up) { ++ if (dhdp->in_suspend) { ++ if (!val || (val && !dhdp->suspend_disable_flag)) ++ dhd_enable_packet_filter(val, dhdp); ++ } ++ } ++ return ret; ++} ++ ++/* function to enable/disable packet for Network device */ ++int net_os_enable_packet_filter(struct net_device *dev, int val) ++{ ++ dhd_info_t *dhd = DHD_DEV_INFO(dev); ++ ++ return dhd_os_enable_packet_filter(&dhd->pub, val); ++} ++#endif /* PKT_FILTER_SUPPORT */ ++ ++int ++dhd_dev_init_ioctl(struct net_device *dev) ++{ ++ dhd_info_t *dhd = DHD_DEV_INFO(dev); ++ int ret; ++ ++ if ((ret = dhd_sync_with_dongle(&dhd->pub)) < 0) ++ goto done; ++ ++done: ++ return ret; ++} ++ ++#ifdef PNO_SUPPORT ++/* Linux wrapper to call common dhd_pno_stop_for_ssid */ ++int ++dhd_dev_pno_stop_for_ssid(struct net_device *dev) ++{ ++ dhd_info_t *dhd = DHD_DEV_INFO(dev); ++ ++ return (dhd_pno_stop_for_ssid(&dhd->pub)); ++} ++/* Linux wrapper to call common dhd_pno_set_for_ssid */ ++int ++dhd_dev_pno_set_for_ssid(struct net_device *dev, wlc_ssid_t* ssids_local, int nssid, ++ uint16 scan_fr, int pno_repeat, int pno_freq_expo_max, uint16 *channel_list, int nchan) ++{ ++ dhd_info_t *dhd = DHD_DEV_INFO(dev); ++ ++ return (dhd_pno_set_for_ssid(&dhd->pub, ssids_local, nssid, scan_fr, ++ pno_repeat, pno_freq_expo_max, channel_list, nchan)); ++} ++ ++/* Linux wrapper to call common dhd_pno_enable */ ++int ++dhd_dev_pno_enable(struct net_device *dev, int enable) ++{ ++ dhd_info_t *dhd = DHD_DEV_INFO(dev); ++ ++ return (dhd_pno_enable(&dhd->pub, enable)); ++} ++ ++/* Linux wrapper to call common dhd_pno_set_for_hotlist */ ++int ++dhd_dev_pno_set_for_hotlist(struct net_device *dev, wl_pfn_bssid_t *p_pfn_bssid, ++ struct dhd_pno_hotlist_params *hotlist_params) ++{ ++ dhd_info_t *dhd = DHD_DEV_INFO(dev); ++ return (dhd_pno_set_for_hotlist(&dhd->pub, p_pfn_bssid, hotlist_params)); ++} ++/* Linux wrapper to call common dhd_dev_pno_stop_for_batch */ ++int ++dhd_dev_pno_stop_for_batch(struct net_device *dev) ++{ ++ dhd_info_t *dhd = DHD_DEV_INFO(dev); ++ return (dhd_pno_stop_for_batch(&dhd->pub)); ++} ++/* Linux wrapper to call common dhd_dev_pno_set_for_batch */ ++int ++dhd_dev_pno_set_for_batch(struct net_device *dev, struct dhd_pno_batch_params *batch_params) ++{ ++ dhd_info_t *dhd = DHD_DEV_INFO(dev); ++ return (dhd_pno_set_for_batch(&dhd->pub, batch_params)); ++} ++/* Linux wrapper to call common dhd_dev_pno_get_for_batch */ ++int ++dhd_dev_pno_get_for_batch(struct net_device *dev, char *buf, int bufsize) ++{ ++ dhd_info_t *dhd = DHD_DEV_INFO(dev); ++ return (dhd_pno_get_for_batch(&dhd->pub, buf, bufsize, PNO_STATUS_NORMAL)); ++} ++#endif /* PNO_SUPPORT */ ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && (1) ++static void dhd_hang_process(void *dhd_info, void *event_info, u8 event) ++{ ++ dhd_info_t *dhd; ++ struct net_device *dev; ++ ++ dhd = (dhd_info_t *)dhd_info; ++ dev = dhd->iflist[0]->net; ++ ++ if (dev) { ++ rtnl_lock(); ++ dev_close(dev); ++ rtnl_unlock(); ++#if defined(WL_WIRELESS_EXT) ++ wl_iw_send_priv_event(dev, "HANG"); ++#endif ++#if defined(WL_CFG80211) ++ wl_cfg80211_hang(dev, WLAN_REASON_UNSPECIFIED); ++#endif ++ } ++} ++ ++ ++int dhd_os_send_hang_message(dhd_pub_t *dhdp) ++{ ++ int ret = 0; ++ if (dhdp) { ++ if (!dhdp->hang_was_sent) { ++ dhdp->hang_was_sent = 1; ++ dhd_deferred_schedule_work(dhdp->info->dhd_deferred_wq, (void *)dhdp, ++ DHD_WQ_WORK_HANG_MSG, dhd_hang_process, DHD_WORK_PRIORITY_HIGH); ++ } ++ } ++ return ret; ++} ++ ++int net_os_send_hang_message(struct net_device *dev) ++{ ++ dhd_info_t *dhd = DHD_DEV_INFO(dev); ++ int ret = 0; ++ ++ if (dhd) { ++ /* Report FW problem when enabled */ ++ if (dhd->pub.hang_report) { ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) ++ ret = dhd_os_send_hang_message(&dhd->pub); ++#else ++ ret = wl_cfg80211_hang(dev, WLAN_REASON_UNSPECIFIED); ++#endif ++ } else { ++ DHD_ERROR(("%s: FW HANG ignored (for testing purpose) and not sent up\n", ++ __FUNCTION__)); ++ /* Enforce bus down to stop any future traffic */ ++ dhd->pub.busstate = DHD_BUS_DOWN; ++ } ++ } ++ return ret; ++} ++#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) && OEM_ANDROID */ ++ ++ ++int dhd_net_wifi_platform_set_power(struct net_device *dev, bool on, unsigned long delay_msec) ++{ ++ dhd_info_t *dhd = DHD_DEV_INFO(dev); ++ return wifi_platform_set_power(dhd->adapter, on, delay_msec); ++} ++ ++void dhd_get_customized_country_code(struct net_device *dev, char *country_iso_code, ++ wl_country_t *cspec) ++{ ++ dhd_info_t *dhd = DHD_DEV_INFO(dev); ++ get_customized_country_code(dhd->adapter, country_iso_code, cspec); ++} ++void dhd_bus_country_set(struct net_device *dev, wl_country_t *cspec, bool notify) ++{ ++ dhd_info_t *dhd = DHD_DEV_INFO(dev); ++ if (dhd && dhd->pub.up) { ++ memcpy(&dhd->pub.dhd_cspec, cspec, sizeof(wl_country_t)); ++#ifdef WL_CFG80211 ++ wl_update_wiphybands(NULL, notify); ++#endif ++ } ++} ++ ++void dhd_bus_band_set(struct net_device *dev, uint band) ++{ ++ dhd_info_t *dhd = DHD_DEV_INFO(dev); ++ if (dhd && dhd->pub.up) { ++#ifdef WL_CFG80211 ++ wl_update_wiphybands(NULL, true); ++#endif ++ } ++} ++ ++int dhd_net_set_fw_path(struct net_device *dev, char *fw) ++{ ++ dhd_info_t *dhd = DHD_DEV_INFO(dev); ++ ++ if (!fw || fw[0] == '\0') ++ return -EINVAL; ++ ++ strncpy(dhd->fw_path, fw, sizeof(dhd->fw_path) - 1); ++ dhd->fw_path[sizeof(dhd->fw_path)-1] = '\0'; ++ ++#if defined(SOFTAP) ++ if (strstr(fw, "apsta") != NULL) { ++ DHD_INFO(("GOT APSTA FIRMWARE\n")); ++ ap_fw_loaded = TRUE; ++ } else { ++ DHD_INFO(("GOT STA FIRMWARE\n")); ++ ap_fw_loaded = FALSE; ++ } ++#endif ++ return 0; ++} ++ ++void dhd_net_if_lock(struct net_device *dev) ++{ ++ dhd_info_t *dhd = DHD_DEV_INFO(dev); ++ dhd_net_if_lock_local(dhd); ++} ++ ++void dhd_net_if_unlock(struct net_device *dev) ++{ ++ dhd_info_t *dhd = DHD_DEV_INFO(dev); ++ dhd_net_if_unlock_local(dhd); ++} ++ ++static void dhd_net_if_lock_local(dhd_info_t *dhd) ++{ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1 ++ if (dhd) ++ mutex_lock(&dhd->dhd_net_if_mutex); ++#endif ++} ++ ++static void dhd_net_if_unlock_local(dhd_info_t *dhd) ++{ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1 ++ if (dhd) ++ mutex_unlock(&dhd->dhd_net_if_mutex); ++#endif ++} ++ ++static void dhd_suspend_lock(dhd_pub_t *pub) ++{ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1 ++ dhd_info_t *dhd = (dhd_info_t *)(pub->info); ++ if (dhd) ++ mutex_lock(&dhd->dhd_suspend_mutex); ++#endif ++} ++ ++static void dhd_suspend_unlock(dhd_pub_t *pub) ++{ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1 ++ dhd_info_t *dhd = (dhd_info_t *)(pub->info); ++ if (dhd) ++ mutex_unlock(&dhd->dhd_suspend_mutex); ++#endif ++} ++ ++unsigned long dhd_os_general_spin_lock(dhd_pub_t *pub) ++{ ++ dhd_info_t *dhd = (dhd_info_t *)(pub->info); ++ unsigned long flags = 0; ++ ++ if (dhd) ++ spin_lock_irqsave(&dhd->dhd_lock, flags); ++ ++ return flags; ++} ++ ++void dhd_os_general_spin_unlock(dhd_pub_t *pub, unsigned long flags) ++{ ++ dhd_info_t *dhd = (dhd_info_t *)(pub->info); ++ ++ if (dhd) ++ spin_unlock_irqrestore(&dhd->dhd_lock, flags); ++} ++ ++/* Linux specific multipurpose spinlock API */ ++void * ++dhd_os_spin_lock_init(osl_t *osh) ++{ ++ /* Adding 4 bytes since the sizeof(spinlock_t) could be 0 */ ++ /* if CONFIG_SMP and CONFIG_DEBUG_SPINLOCK are not defined */ ++ /* and this results in kernel asserts in internal builds */ ++ spinlock_t * lock = MALLOC(osh, sizeof(spinlock_t) + 4); ++ if (lock) ++ spin_lock_init(lock); ++ return ((void *)lock); ++} ++void ++dhd_os_spin_lock_deinit(osl_t *osh, void *lock) ++{ ++ MFREE(osh, lock, sizeof(spinlock_t) + 4); ++} ++unsigned long ++dhd_os_spin_lock(void *lock) ++{ ++ unsigned long flags = 0; ++ ++ if (lock) ++ spin_lock_irqsave((spinlock_t *)lock, flags); ++ ++ return flags; ++} ++void ++dhd_os_spin_unlock(void *lock, unsigned long flags) ++{ ++ if (lock) ++ spin_unlock_irqrestore((spinlock_t *)lock, flags); ++} ++ ++static int ++dhd_get_pend_8021x_cnt(dhd_info_t *dhd) ++{ ++ return (atomic_read(&dhd->pend_8021x_cnt)); ++} ++ ++#define MAX_WAIT_FOR_8021X_TX 100 ++ ++int ++dhd_wait_pend8021x(struct net_device *dev) ++{ ++ dhd_info_t *dhd = DHD_DEV_INFO(dev); ++ int timeout = msecs_to_jiffies(10); ++ int ntimes = MAX_WAIT_FOR_8021X_TX; ++ int pend = dhd_get_pend_8021x_cnt(dhd); ++ ++ while (ntimes && pend) { ++ if (pend) { ++ set_current_state(TASK_INTERRUPTIBLE); ++ DHD_PERIM_UNLOCK(&dhd->pub); ++ schedule_timeout(timeout); ++ DHD_PERIM_LOCK(&dhd->pub); ++ set_current_state(TASK_RUNNING); ++ ntimes--; ++ } ++ pend = dhd_get_pend_8021x_cnt(dhd); ++ } ++ if (ntimes == 0) ++ { ++ atomic_set(&dhd->pend_8021x_cnt, 0); ++ DHD_ERROR(("%s: TIMEOUT\n", __FUNCTION__)); ++ } ++ return pend; ++} ++ ++#ifdef DHD_DEBUG ++int ++write_to_file(dhd_pub_t *dhd, uint8 *buf, int size) ++{ ++ int ret = 0; ++ struct file *fp; ++ mm_segment_t old_fs; ++ loff_t pos = 0; ++ ++ /* change to KERNEL_DS address limit */ ++ old_fs = get_fs(); ++ set_fs(KERNEL_DS); ++ ++ /* open file to write */ ++ fp = filp_open("/tmp/mem_dump", O_WRONLY|O_CREAT, 0640); ++ if (!fp) { ++ printf("%s: open file error\n", __FUNCTION__); ++ ret = -1; ++ goto exit; ++ } ++ ++ /* Write buf to file */ ++ fp->f_op->write(fp, buf, size, &pos); ++ ++exit: ++ /* free buf before return */ ++ MFREE(dhd->osh, buf, size); ++ /* close file before return */ ++ if (fp) ++ filp_close(fp, current->files); ++ /* restore previous address limit */ ++ set_fs(old_fs); ++ ++ return ret; ++} ++#endif /* DHD_DEBUG */ ++ ++int dhd_os_wake_lock_timeout(dhd_pub_t *pub) ++{ ++ dhd_info_t *dhd = (dhd_info_t *)(pub->info); ++ unsigned long flags; ++ int ret = 0; ++ ++ if (dhd) { ++ spin_lock_irqsave(&dhd->wakelock_spinlock, flags); ++ ret = dhd->wakelock_rx_timeout_enable > dhd->wakelock_ctrl_timeout_enable ? ++ dhd->wakelock_rx_timeout_enable : dhd->wakelock_ctrl_timeout_enable; ++#ifdef CONFIG_HAS_WAKELOCK ++ if (dhd->wakelock_rx_timeout_enable) ++ wake_lock_timeout(&dhd->wl_rxwake, ++ msecs_to_jiffies(dhd->wakelock_rx_timeout_enable)); ++ if (dhd->wakelock_ctrl_timeout_enable) ++ wake_lock_timeout(&dhd->wl_ctrlwake, ++ msecs_to_jiffies(dhd->wakelock_ctrl_timeout_enable)); ++#endif ++ dhd->wakelock_rx_timeout_enable = 0; ++ dhd->wakelock_ctrl_timeout_enable = 0; ++ spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags); ++ } ++ return ret; ++} ++ ++int net_os_wake_lock_timeout(struct net_device *dev) ++{ ++ dhd_info_t *dhd = DHD_DEV_INFO(dev); ++ int ret = 0; ++ ++ if (dhd) ++ ret = dhd_os_wake_lock_timeout(&dhd->pub); ++ return ret; ++} ++ ++int dhd_os_wake_lock_rx_timeout_enable(dhd_pub_t *pub, int val) ++{ ++ dhd_info_t *dhd = (dhd_info_t *)(pub->info); ++ unsigned long flags; ++ ++ if (dhd) { ++ spin_lock_irqsave(&dhd->wakelock_spinlock, flags); ++ if (val > dhd->wakelock_rx_timeout_enable) ++ dhd->wakelock_rx_timeout_enable = val; ++ spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags); ++ } ++ return 0; ++} ++ ++int dhd_os_wake_lock_ctrl_timeout_enable(dhd_pub_t *pub, int val) ++{ ++ dhd_info_t *dhd = (dhd_info_t *)(pub->info); ++ unsigned long flags; ++ ++ if (dhd) { ++ spin_lock_irqsave(&dhd->wakelock_spinlock, flags); ++ if (val > dhd->wakelock_ctrl_timeout_enable) ++ dhd->wakelock_ctrl_timeout_enable = val; ++ spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags); ++ } ++ return 0; ++} ++ ++int dhd_os_wake_lock_ctrl_timeout_cancel(dhd_pub_t *pub) ++{ ++ dhd_info_t *dhd = (dhd_info_t *)(pub->info); ++ unsigned long flags; ++ ++ if (dhd) { ++ spin_lock_irqsave(&dhd->wakelock_spinlock, flags); ++ dhd->wakelock_ctrl_timeout_enable = 0; ++#ifdef CONFIG_HAS_WAKELOCK ++ if (wake_lock_active(&dhd->wl_ctrlwake)) ++ wake_unlock(&dhd->wl_ctrlwake); ++#endif ++ spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags); ++ } ++ return 0; ++} ++ ++int net_os_wake_lock_rx_timeout_enable(struct net_device *dev, int val) ++{ ++ dhd_info_t *dhd = DHD_DEV_INFO(dev); ++ int ret = 0; ++ ++ if (dhd) ++ ret = dhd_os_wake_lock_rx_timeout_enable(&dhd->pub, val); ++ return ret; ++} ++ ++int net_os_wake_lock_ctrl_timeout_enable(struct net_device *dev, int val) ++{ ++ dhd_info_t *dhd = DHD_DEV_INFO(dev); ++ int ret = 0; ++ ++ if (dhd) ++ ret = dhd_os_wake_lock_ctrl_timeout_enable(&dhd->pub, val); ++ return ret; ++} ++ ++int dhd_os_wake_lock(dhd_pub_t *pub) ++{ ++ dhd_info_t *dhd = (dhd_info_t *)(pub->info); ++ unsigned long flags; ++ int ret = 0; ++ ++ if (dhd) { ++ spin_lock_irqsave(&dhd->wakelock_spinlock, flags); ++ ++ if (dhd->wakelock_counter == 0 && !dhd->waive_wakelock) { ++#ifdef CONFIG_HAS_WAKELOCK ++ wake_lock(&dhd->wl_wifi); ++#elif defined(BCMSDIO) && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36)) ++ dhd_bus_dev_pm_stay_awake(pub); ++#endif ++ } ++ dhd->wakelock_counter++; ++ ret = dhd->wakelock_counter; ++ spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags); ++ } ++ return ret; ++} ++ ++int net_os_wake_lock(struct net_device *dev) ++{ ++ dhd_info_t *dhd = DHD_DEV_INFO(dev); ++ int ret = 0; ++ ++ if (dhd) ++ ret = dhd_os_wake_lock(&dhd->pub); ++ return ret; ++} ++ ++int dhd_os_wake_unlock(dhd_pub_t *pub) ++{ ++ dhd_info_t *dhd = (dhd_info_t *)(pub->info); ++ unsigned long flags; ++ int ret = 0; ++ ++ dhd_os_wake_lock_timeout(pub); ++ if (dhd) { ++ spin_lock_irqsave(&dhd->wakelock_spinlock, flags); ++ if (dhd->wakelock_counter > 0) { ++ dhd->wakelock_counter--; ++ if (dhd->wakelock_counter == 0 && !dhd->waive_wakelock) { ++#ifdef CONFIG_HAS_WAKELOCK ++ wake_unlock(&dhd->wl_wifi); ++#elif defined(BCMSDIO) && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36)) ++ dhd_bus_dev_pm_relax(pub); ++#endif ++ } ++ ret = dhd->wakelock_counter; ++ } ++ spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags); ++ } ++ return ret; ++} ++ ++int dhd_os_check_wakelock(dhd_pub_t *pub) ++{ ++#if defined(CONFIG_HAS_WAKELOCK) || (defined(BCMSDIO) && (LINUX_VERSION_CODE > \ ++ KERNEL_VERSION(2, 6, 36))) ++ dhd_info_t *dhd; ++ ++ if (!pub) ++ return 0; ++ dhd = (dhd_info_t *)(pub->info); ++#endif /* CONFIG_HAS_WAKELOCK || BCMSDIO */ ++ ++#ifdef CONFIG_HAS_WAKELOCK ++ /* Indicate to the SD Host to avoid going to suspend if internal locks are up */ ++ if (dhd && (wake_lock_active(&dhd->wl_wifi) || ++ (wake_lock_active(&dhd->wl_wdwake)))) ++ return 1; ++#elif defined(BCMSDIO) && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36)) ++ if (dhd && (dhd->wakelock_counter > 0) && dhd_bus_dev_pm_enabled(pub)) ++ return 1; ++#endif ++ return 0; ++} ++ ++int dhd_os_check_wakelock_all(dhd_pub_t *pub) ++{ ++#if defined(CONFIG_HAS_WAKELOCK) || (defined(BCMSDIO) && (LINUX_VERSION_CODE > \ ++ KERNEL_VERSION(2, 6, 36))) ++ dhd_info_t *dhd; ++ ++ if (!pub) ++ return 0; ++ dhd = (dhd_info_t *)(pub->info); ++#endif /* CONFIG_HAS_WAKELOCK || BCMSDIO */ ++ ++#ifdef CONFIG_HAS_WAKELOCK ++ /* Indicate to the SD Host to avoid going to suspend if internal locks are up */ ++ if (dhd && (wake_lock_active(&dhd->wl_wifi) || ++ wake_lock_active(&dhd->wl_wdwake) || ++ wake_lock_active(&dhd->wl_rxwake) || ++ wake_lock_active(&dhd->wl_ctrlwake))) { ++ return 1; ++ } ++#elif defined(BCMSDIO) && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36)) ++ if (dhd && (dhd->wakelock_counter > 0) && dhd_bus_dev_pm_enabled(pub)) ++ return 1; ++#endif ++ return 0; ++} ++ ++int net_os_wake_unlock(struct net_device *dev) ++{ ++ dhd_info_t *dhd = DHD_DEV_INFO(dev); ++ int ret = 0; ++ ++ if (dhd) ++ ret = dhd_os_wake_unlock(&dhd->pub); ++ return ret; ++} ++ ++int dhd_os_wd_wake_lock(dhd_pub_t *pub) ++{ ++ dhd_info_t *dhd = (dhd_info_t *)(pub->info); ++ unsigned long flags; ++ int ret = 0; ++ ++ if (dhd) { ++ spin_lock_irqsave(&dhd->wakelock_spinlock, flags); ++#ifdef CONFIG_HAS_WAKELOCK ++ /* if wakelock_wd_counter was never used : lock it at once */ ++ if (!dhd->wakelock_wd_counter) ++ wake_lock(&dhd->wl_wdwake); ++#endif ++ dhd->wakelock_wd_counter++; ++ ret = dhd->wakelock_wd_counter; ++ spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags); ++ } ++ return ret; ++} ++ ++int dhd_os_wd_wake_unlock(dhd_pub_t *pub) ++{ ++ dhd_info_t *dhd = (dhd_info_t *)(pub->info); ++ unsigned long flags; ++ int ret = 0; ++ ++ if (dhd) { ++ spin_lock_irqsave(&dhd->wakelock_spinlock, flags); ++ if (dhd->wakelock_wd_counter) { ++ dhd->wakelock_wd_counter = 0; ++#ifdef CONFIG_HAS_WAKELOCK ++ wake_unlock(&dhd->wl_wdwake); ++#endif ++ } ++ spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags); ++ } ++ return ret; ++} ++ ++#ifdef BCMPCIE_OOB_HOST_WAKE ++int dhd_os_oob_irq_wake_lock_timeout(dhd_pub_t *pub, int val) ++{ ++ dhd_info_t *dhd = (dhd_info_t *)(pub->info); ++ int ret = 0; ++ ++ if (dhd) { ++#ifdef CONFIG_HAS_WAKELOCK ++ wake_lock_timeout(&dhd->wl_intrwake, msecs_to_jiffies(val)); ++#endif ++ } ++ return ret; ++} ++ ++int dhd_os_oob_irq_wake_unlock(dhd_pub_t *pub) ++{ ++ dhd_info_t *dhd = (dhd_info_t *)(pub->info); ++ int ret = 0; ++ ++ if (dhd) { ++#ifdef CONFIG_HAS_WAKELOCK ++ /* if wl_intrwake is active, unlock it */ ++ if (wake_lock_active(&dhd->wl_intrwake)) { ++ wake_unlock(&dhd->wl_intrwake); ++ } ++#endif ++ } ++ return ret; ++} ++#endif /* BCMPCIE_OOB_HOST_WAKE */ ++ ++/* waive wakelocks for operations such as IOVARs in suspend function, must be closed ++ * by a paired function call to dhd_wakelock_restore. returns current wakelock counter ++ */ ++int dhd_os_wake_lock_waive(dhd_pub_t *pub) ++{ ++ dhd_info_t *dhd = (dhd_info_t *)(pub->info); ++ unsigned long flags; ++ int ret = 0; ++ ++ if (dhd) { ++ spin_lock_irqsave(&dhd->wakelock_spinlock, flags); ++ /* dhd_wakelock_waive/dhd_wakelock_restore must be paired */ ++ if (dhd->waive_wakelock == FALSE) { ++ /* record current lock status */ ++ dhd->wakelock_before_waive = dhd->wakelock_counter; ++ dhd->waive_wakelock = TRUE; ++ } ++ ret = dhd->wakelock_wd_counter; ++ spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags); ++ } ++ return ret; ++} ++ ++int dhd_os_wake_lock_restore(dhd_pub_t *pub) ++{ ++ dhd_info_t *dhd = (dhd_info_t *)(pub->info); ++ unsigned long flags; ++ int ret = 0; ++ ++ if (!dhd) ++ return 0; ++ ++ spin_lock_irqsave(&dhd->wakelock_spinlock, flags); ++ /* dhd_wakelock_waive/dhd_wakelock_restore must be paired */ ++ if (!dhd->waive_wakelock) ++ goto exit; ++ ++ dhd->waive_wakelock = FALSE; ++ /* if somebody else acquires wakelock between dhd_wakelock_waive/dhd_wakelock_restore, ++ * we need to make it up by calling wake_lock or pm_stay_awake. or if somebody releases ++ * the lock in between, do the same by calling wake_unlock or pm_relax ++ */ ++ if (dhd->wakelock_before_waive == 0 && dhd->wakelock_counter > 0) { ++#ifdef CONFIG_HAS_WAKELOCK ++ wake_lock(&dhd->wl_wifi); ++#elif defined(BCMSDIO) && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36)) ++ dhd_bus_dev_pm_stay_awake(&dhd->pub); ++#endif ++ } else if (dhd->wakelock_before_waive > 0 && dhd->wakelock_counter == 0) { ++#ifdef CONFIG_HAS_WAKELOCK ++ wake_unlock(&dhd->wl_wifi); ++#elif defined(BCMSDIO) && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36)) ++ dhd_bus_dev_pm_relax(&dhd->pub); ++#endif ++ } ++ dhd->wakelock_before_waive = 0; ++exit: ++ ret = dhd->wakelock_wd_counter; ++ spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags); ++ return ret; ++} ++ ++bool dhd_os_check_if_up(dhd_pub_t *pub) ++{ ++ if (!pub) ++ return FALSE; ++ return pub->up; ++} ++ ++/* function to collect firmware, chip id and chip version info */ ++void dhd_set_version_info(dhd_pub_t *dhdp, char *fw) ++{ ++ int i; ++ ++ i = snprintf(info_string, sizeof(info_string), ++ " Driver: %s\n Firmware: %s ", EPI_VERSION_STR, fw); ++ printf("%s\n", info_string); ++ ++ if (!dhdp) ++ return; ++ ++ i = snprintf(&info_string[i], sizeof(info_string) - i, ++ "\n Chip: %x Rev %x Pkg %x", dhd_bus_chip_id(dhdp), ++ dhd_bus_chiprev_id(dhdp), dhd_bus_chippkg_id(dhdp)); ++} ++ ++int dhd_ioctl_entry_local(struct net_device *net, wl_ioctl_t *ioc, int cmd) ++{ ++ int ifidx; ++ int ret = 0; ++ dhd_info_t *dhd = NULL; ++ ++ if (!net || !DEV_PRIV(net)) { ++ DHD_ERROR(("%s invalid parameter\n", __FUNCTION__)); ++ return -EINVAL; ++ } ++ ++ dhd = DHD_DEV_INFO(net); ++ if (!dhd) ++ return -EINVAL; ++ ++ ifidx = dhd_net2idx(dhd, net); ++ if (ifidx == DHD_BAD_IF) { ++ DHD_ERROR(("%s bad ifidx\n", __FUNCTION__)); ++ return -ENODEV; ++ } ++ ++ DHD_OS_WAKE_LOCK(&dhd->pub); ++ DHD_PERIM_LOCK(&dhd->pub); ++ ++ ret = dhd_wl_ioctl(&dhd->pub, ifidx, ioc, ioc->buf, ioc->len); ++ dhd_check_hang(net, &dhd->pub, ret); ++ ++ DHD_PERIM_UNLOCK(&dhd->pub); ++ DHD_OS_WAKE_UNLOCK(&dhd->pub); ++ ++ return ret; ++} ++ ++bool dhd_os_check_hang(dhd_pub_t *dhdp, int ifidx, int ret) ++{ ++ struct net_device *net; ++ ++ net = dhd_idx2net(dhdp, ifidx); ++ if (!net) { ++ DHD_ERROR(("%s : Invalid index : %d\n", __FUNCTION__, ifidx)); ++ return -EINVAL; ++ } ++ ++ return dhd_check_hang(net, dhdp, ret); ++} ++ ++/* Return instance */ ++int dhd_get_instance(dhd_pub_t *dhdp) ++{ ++ return dhdp->info->unit; ++} ++ ++ ++#ifdef PROP_TXSTATUS ++ ++void dhd_wlfc_plat_init(void *dhd) ++{ ++ return; ++} ++ ++void dhd_wlfc_plat_deinit(void *dhd) ++{ ++ return; ++} ++ ++bool dhd_wlfc_skip_fc(void) ++{ ++ return FALSE; ++} ++#endif /* PROP_TXSTATUS */ ++ ++#ifdef BCMDBGFS ++ ++#include ++ ++extern uint32 dhd_readregl(void *bp, uint32 addr); ++extern uint32 dhd_writeregl(void *bp, uint32 addr, uint32 data); ++ ++typedef struct dhd_dbgfs { ++ struct dentry *debugfs_dir; ++ struct dentry *debugfs_mem; ++ dhd_pub_t *dhdp; ++ uint32 size; ++} dhd_dbgfs_t; ++ ++dhd_dbgfs_t g_dbgfs; ++ ++static int ++dhd_dbg_state_open(struct inode *inode, struct file *file) ++{ ++ file->private_data = inode->i_private; ++ return 0; ++} ++ ++static ssize_t ++dhd_dbg_state_read(struct file *file, char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ ssize_t rval; ++ uint32 tmp; ++ loff_t pos = *ppos; ++ size_t ret; ++ ++ if (pos < 0) ++ return -EINVAL; ++ if (pos >= g_dbgfs.size || !count) ++ return 0; ++ if (count > g_dbgfs.size - pos) ++ count = g_dbgfs.size - pos; ++ ++ /* Basically enforce aligned 4 byte reads. It's up to the user to work out the details */ ++ tmp = dhd_readregl(g_dbgfs.dhdp->bus, file->f_pos & (~3)); ++ ++ ret = copy_to_user(ubuf, &tmp, 4); ++ if (ret == count) ++ return -EFAULT; ++ ++ count -= ret; ++ *ppos = pos + count; ++ rval = count; ++ ++ return rval; ++} ++ ++ ++static ssize_t ++dhd_debugfs_write(struct file *file, const char __user *ubuf, size_t count, loff_t *ppos) ++{ ++ loff_t pos = *ppos; ++ size_t ret; ++ uint32 buf; ++ ++ if (pos < 0) ++ return -EINVAL; ++ if (pos >= g_dbgfs.size || !count) ++ return 0; ++ if (count > g_dbgfs.size - pos) ++ count = g_dbgfs.size - pos; ++ ++ ret = copy_from_user(&buf, ubuf, sizeof(uint32)); ++ if (ret == count) ++ return -EFAULT; ++ ++ /* Basically enforce aligned 4 byte writes. It's up to the user to work out the details */ ++ dhd_writeregl(g_dbgfs.dhdp->bus, file->f_pos & (~3), buf); ++ ++ return count; ++} ++ ++ ++loff_t ++dhd_debugfs_lseek(struct file *file, loff_t off, int whence) ++{ ++ loff_t pos = -1; ++ ++ switch (whence) { ++ case 0: ++ pos = off; ++ break; ++ case 1: ++ pos = file->f_pos + off; ++ break; ++ case 2: ++ pos = g_dbgfs.size - off; ++ } ++ return (pos < 0 || pos > g_dbgfs.size) ? -EINVAL : (file->f_pos = pos); ++} ++ ++static const struct file_operations dhd_dbg_state_ops = { ++ .read = dhd_dbg_state_read, ++ .write = dhd_debugfs_write, ++ .open = dhd_dbg_state_open, ++ .llseek = dhd_debugfs_lseek ++}; ++ ++static void dhd_dbg_create(void) ++{ ++ if (g_dbgfs.debugfs_dir) { ++ g_dbgfs.debugfs_mem = debugfs_create_file("mem", 0644, g_dbgfs.debugfs_dir, ++ NULL, &dhd_dbg_state_ops); ++ } ++} ++ ++void dhd_dbg_init(dhd_pub_t *dhdp) ++{ ++ int err; ++ ++ g_dbgfs.dhdp = dhdp; ++ g_dbgfs.size = 0x20000000; /* Allow access to various cores regs */ ++ ++ g_dbgfs.debugfs_dir = debugfs_create_dir("dhd", 0); ++ if (IS_ERR(g_dbgfs.debugfs_dir)) { ++ err = PTR_ERR(g_dbgfs.debugfs_dir); ++ g_dbgfs.debugfs_dir = NULL; ++ return; ++ } ++ ++ dhd_dbg_create(); ++ ++ return; ++} ++ ++void dhd_dbg_remove(void) ++{ ++ debugfs_remove(g_dbgfs.debugfs_mem); ++ debugfs_remove(g_dbgfs.debugfs_dir); ++ ++ bzero((unsigned char *) &g_dbgfs, sizeof(g_dbgfs)); ++ ++} ++#endif /* ifdef BCMDBGFS */ ++ ++#ifdef WLMEDIA_HTSF ++ ++static ++void dhd_htsf_addtxts(dhd_pub_t *dhdp, void *pktbuf) ++{ ++ dhd_info_t *dhd = (dhd_info_t *)(dhdp->info); ++ struct sk_buff *skb; ++ uint32 htsf = 0; ++ uint16 dport = 0, oldmagic = 0xACAC; ++ char *p1; ++ htsfts_t ts; ++ ++ /* timestamp packet */ ++ ++ p1 = (char*) PKTDATA(dhdp->osh, pktbuf); ++ ++ if (PKTLEN(dhdp->osh, pktbuf) > HTSF_MINLEN) { ++/* memcpy(&proto, p1+26, 4); */ ++ memcpy(&dport, p1+40, 2); ++/* proto = ((ntoh32(proto))>> 16) & 0xFF; */ ++ dport = ntoh16(dport); ++ } ++ ++ /* timestamp only if icmp or udb iperf with port 5555 */ ++/* if (proto == 17 && dport == tsport) { */ ++ if (dport >= tsport && dport <= tsport + 20) { ++ ++ skb = (struct sk_buff *) pktbuf; ++ ++ htsf = dhd_get_htsf(dhd, 0); ++ memset(skb->data + 44, 0, 2); /* clear checksum */ ++ memcpy(skb->data+82, &oldmagic, 2); ++ memcpy(skb->data+84, &htsf, 4); ++ ++ memset(&ts, 0, sizeof(htsfts_t)); ++ ts.magic = HTSFMAGIC; ++ ts.prio = PKTPRIO(pktbuf); ++ ts.seqnum = htsf_seqnum++; ++ ts.c10 = get_cycles(); ++ ts.t10 = htsf; ++ ts.endmagic = HTSFENDMAGIC; ++ ++ memcpy(skb->data + HTSF_HOSTOFFSET, &ts, sizeof(ts)); ++ } ++} ++ ++static void dhd_dump_htsfhisto(histo_t *his, char *s) ++{ ++ int pktcnt = 0, curval = 0, i; ++ for (i = 0; i < (NUMBIN-2); i++) { ++ curval += 500; ++ printf("%d ", his->bin[i]); ++ pktcnt += his->bin[i]; ++ } ++ printf(" max: %d TotPkt: %d neg: %d [%s]\n", his->bin[NUMBIN-2], pktcnt, ++ his->bin[NUMBIN-1], s); ++} ++ ++static ++void sorttobin(int value, histo_t *histo) ++{ ++ int i, binval = 0; ++ ++ if (value < 0) { ++ histo->bin[NUMBIN-1]++; ++ return; ++ } ++ if (value > histo->bin[NUMBIN-2]) /* store the max value */ ++ histo->bin[NUMBIN-2] = value; ++ ++ for (i = 0; i < (NUMBIN-2); i++) { ++ binval += 500; /* 500m s bins */ ++ if (value <= binval) { ++ histo->bin[i]++; ++ return; ++ } ++ } ++ histo->bin[NUMBIN-3]++; ++} ++ ++static ++void dhd_htsf_addrxts(dhd_pub_t *dhdp, void *pktbuf) ++{ ++ dhd_info_t *dhd = (dhd_info_t *)dhdp->info; ++ struct sk_buff *skb; ++ char *p1; ++ uint16 old_magic; ++ int d1, d2, d3, end2end; ++ htsfts_t *htsf_ts; ++ uint32 htsf; ++ ++ skb = PKTTONATIVE(dhdp->osh, pktbuf); ++ p1 = (char*)PKTDATA(dhdp->osh, pktbuf); ++ ++ if (PKTLEN(osh, pktbuf) > HTSF_MINLEN) { ++ memcpy(&old_magic, p1+78, 2); ++ htsf_ts = (htsfts_t*) (p1 + HTSF_HOSTOFFSET - 4); ++ } ++ else ++ return; ++ ++ if (htsf_ts->magic == HTSFMAGIC) { ++ htsf_ts->tE0 = dhd_get_htsf(dhd, 0); ++ htsf_ts->cE0 = get_cycles(); ++ } ++ ++ if (old_magic == 0xACAC) { ++ ++ tspktcnt++; ++ htsf = dhd_get_htsf(dhd, 0); ++ memcpy(skb->data+92, &htsf, sizeof(uint32)); ++ ++ memcpy(&ts[tsidx].t1, skb->data+80, 16); ++ ++ d1 = ts[tsidx].t2 - ts[tsidx].t1; ++ d2 = ts[tsidx].t3 - ts[tsidx].t2; ++ d3 = ts[tsidx].t4 - ts[tsidx].t3; ++ end2end = ts[tsidx].t4 - ts[tsidx].t1; ++ ++ sorttobin(d1, &vi_d1); ++ sorttobin(d2, &vi_d2); ++ sorttobin(d3, &vi_d3); ++ sorttobin(end2end, &vi_d4); ++ ++ if (end2end > 0 && end2end > maxdelay) { ++ maxdelay = end2end; ++ maxdelaypktno = tspktcnt; ++ memcpy(&maxdelayts, &ts[tsidx], 16); ++ } ++ if (++tsidx >= TSMAX) ++ tsidx = 0; ++ } ++} ++ ++uint32 dhd_get_htsf(dhd_info_t *dhd, int ifidx) ++{ ++ uint32 htsf = 0, cur_cycle, delta, delta_us; ++ uint32 factor, baseval, baseval2; ++ cycles_t t; ++ ++ t = get_cycles(); ++ cur_cycle = t; ++ ++ if (cur_cycle > dhd->htsf.last_cycle) ++ delta = cur_cycle - dhd->htsf.last_cycle; ++ else { ++ delta = cur_cycle + (0xFFFFFFFF - dhd->htsf.last_cycle); ++ } ++ ++ delta = delta >> 4; ++ ++ if (dhd->htsf.coef) { ++ /* times ten to get the first digit */ ++ factor = (dhd->htsf.coef*10 + dhd->htsf.coefdec1); ++ baseval = (delta*10)/factor; ++ baseval2 = (delta*10)/(factor+1); ++ delta_us = (baseval - (((baseval - baseval2) * dhd->htsf.coefdec2)) / 10); ++ htsf = (delta_us << 4) + dhd->htsf.last_tsf + HTSF_BUS_DELAY; ++ } ++ else { ++ DHD_ERROR(("-------dhd->htsf.coef = 0 -------\n")); ++ } ++ ++ return htsf; ++} ++ ++static void dhd_dump_latency(void) ++{ ++ int i, max = 0; ++ int d1, d2, d3, d4, d5; ++ ++ printf("T1 T2 T3 T4 d1 d2 t4-t1 i \n"); ++ for (i = 0; i < TSMAX; i++) { ++ d1 = ts[i].t2 - ts[i].t1; ++ d2 = ts[i].t3 - ts[i].t2; ++ d3 = ts[i].t4 - ts[i].t3; ++ d4 = ts[i].t4 - ts[i].t1; ++ d5 = ts[max].t4-ts[max].t1; ++ if (d4 > d5 && d4 > 0) { ++ max = i; ++ } ++ printf("%08X %08X %08X %08X \t%d %d %d %d i=%d\n", ++ ts[i].t1, ts[i].t2, ts[i].t3, ts[i].t4, ++ d1, d2, d3, d4, i); ++ } ++ ++ printf("current idx = %d \n", tsidx); ++ ++ printf("Highest latency %d pkt no.%d total=%d\n", maxdelay, maxdelaypktno, tspktcnt); ++ printf("%08X %08X %08X %08X \t%d %d %d %d\n", ++ maxdelayts.t1, maxdelayts.t2, maxdelayts.t3, maxdelayts.t4, ++ maxdelayts.t2 - maxdelayts.t1, ++ maxdelayts.t3 - maxdelayts.t2, ++ maxdelayts.t4 - maxdelayts.t3, ++ maxdelayts.t4 - maxdelayts.t1); ++} ++ ++ ++static int ++dhd_ioctl_htsf_get(dhd_info_t *dhd, int ifidx) ++{ ++ wl_ioctl_t ioc; ++ char buf[32]; ++ int ret; ++ uint32 s1, s2; ++ ++ struct tsf { ++ uint32 low; ++ uint32 high; ++ } tsf_buf; ++ ++ memset(&ioc, 0, sizeof(ioc)); ++ memset(&tsf_buf, 0, sizeof(tsf_buf)); ++ ++ ioc.cmd = WLC_GET_VAR; ++ ioc.buf = buf; ++ ioc.len = (uint)sizeof(buf); ++ ioc.set = FALSE; ++ ++ strncpy(buf, "tsf", sizeof(buf) - 1); ++ buf[sizeof(buf) - 1] = '\0'; ++ s1 = dhd_get_htsf(dhd, 0); ++ if ((ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len)) < 0) { ++ if (ret == -EIO) { ++ DHD_ERROR(("%s: tsf is not supported by device\n", ++ dhd_ifname(&dhd->pub, ifidx))); ++ return -EOPNOTSUPP; ++ } ++ return ret; ++ } ++ s2 = dhd_get_htsf(dhd, 0); ++ ++ memcpy(&tsf_buf, buf, sizeof(tsf_buf)); ++ printf(" TSF_h=%04X lo=%08X Calc:htsf=%08X, coef=%d.%d%d delta=%d ", ++ tsf_buf.high, tsf_buf.low, s2, dhd->htsf.coef, dhd->htsf.coefdec1, ++ dhd->htsf.coefdec2, s2-tsf_buf.low); ++ printf("lasttsf=%08X lastcycle=%08X\n", dhd->htsf.last_tsf, dhd->htsf.last_cycle); ++ return 0; ++} ++ ++void htsf_update(dhd_info_t *dhd, void *data) ++{ ++ static ulong cur_cycle = 0, prev_cycle = 0; ++ uint32 htsf, tsf_delta = 0; ++ uint32 hfactor = 0, cyc_delta, dec1 = 0, dec2, dec3, tmp; ++ ulong b, a; ++ cycles_t t; ++ ++ /* cycles_t in inlcude/mips/timex.h */ ++ ++ t = get_cycles(); ++ ++ prev_cycle = cur_cycle; ++ cur_cycle = t; ++ ++ if (cur_cycle > prev_cycle) ++ cyc_delta = cur_cycle - prev_cycle; ++ else { ++ b = cur_cycle; ++ a = prev_cycle; ++ cyc_delta = cur_cycle + (0xFFFFFFFF - prev_cycle); ++ } ++ ++ if (data == NULL) ++ printf(" tsf update ata point er is null \n"); ++ ++ memcpy(&prev_tsf, &cur_tsf, sizeof(tsf_t)); ++ memcpy(&cur_tsf, data, sizeof(tsf_t)); ++ ++ if (cur_tsf.low == 0) { ++ DHD_INFO((" ---- 0 TSF, do not update, return\n")); ++ return; ++ } ++ ++ if (cur_tsf.low > prev_tsf.low) ++ tsf_delta = (cur_tsf.low - prev_tsf.low); ++ else { ++ DHD_INFO((" ---- tsf low is smaller cur_tsf= %08X, prev_tsf=%08X, \n", ++ cur_tsf.low, prev_tsf.low)); ++ if (cur_tsf.high > prev_tsf.high) { ++ tsf_delta = cur_tsf.low + (0xFFFFFFFF - prev_tsf.low); ++ DHD_INFO((" ---- Wrap around tsf coutner adjusted TSF=%08X\n", tsf_delta)); ++ } ++ else ++ return; /* do not update */ ++ } ++ ++ if (tsf_delta) { ++ hfactor = cyc_delta / tsf_delta; ++ tmp = (cyc_delta - (hfactor * tsf_delta))*10; ++ dec1 = tmp/tsf_delta; ++ dec2 = ((tmp - dec1*tsf_delta)*10) / tsf_delta; ++ tmp = (tmp - (dec1*tsf_delta))*10; ++ dec3 = ((tmp - dec2*tsf_delta)*10) / tsf_delta; ++ ++ if (dec3 > 4) { ++ if (dec2 == 9) { ++ dec2 = 0; ++ if (dec1 == 9) { ++ dec1 = 0; ++ hfactor++; ++ } ++ else { ++ dec1++; ++ } ++ } ++ else ++ dec2++; ++ } ++ } ++ ++ if (hfactor) { ++ htsf = ((cyc_delta * 10) / (hfactor*10+dec1)) + prev_tsf.low; ++ dhd->htsf.coef = hfactor; ++ dhd->htsf.last_cycle = cur_cycle; ++ dhd->htsf.last_tsf = cur_tsf.low; ++ dhd->htsf.coefdec1 = dec1; ++ dhd->htsf.coefdec2 = dec2; ++ } ++ else { ++ htsf = prev_tsf.low; ++ } ++} ++ ++#endif /* WLMEDIA_HTSF */ ++ ++#ifdef CUSTOM_SET_CPUCORE ++void dhd_set_cpucore(dhd_pub_t *dhd, int set) ++{ ++ int e_dpc = 0, e_rxf = 0, retry_set = 0; ++ ++ if (!(dhd->chan_isvht80)) { ++ DHD_ERROR(("%s: chan_status(%d) cpucore!!!\n", __FUNCTION__, dhd->chan_isvht80)); ++ return; ++ } ++ ++ if (DPC_CPUCORE) { ++ do { ++ if (set == TRUE) { ++ e_dpc = set_cpus_allowed_ptr(dhd->current_dpc, ++ cpumask_of(DPC_CPUCORE)); ++ } else { ++ e_dpc = set_cpus_allowed_ptr(dhd->current_dpc, ++ cpumask_of(PRIMARY_CPUCORE)); ++ } ++ if (retry_set++ > MAX_RETRY_SET_CPUCORE) { ++ DHD_ERROR(("%s: dpc(%d) invalid cpu!\n", __FUNCTION__, e_dpc)); ++ return; ++ } ++ if (e_dpc < 0) ++ OSL_SLEEP(1); ++ } while (e_dpc < 0); ++ } ++ if (RXF_CPUCORE) { ++ do { ++ if (set == TRUE) { ++ e_rxf = set_cpus_allowed_ptr(dhd->current_rxf, ++ cpumask_of(RXF_CPUCORE)); ++ } else { ++ e_rxf = set_cpus_allowed_ptr(dhd->current_rxf, ++ cpumask_of(PRIMARY_CPUCORE)); ++ } ++ if (retry_set++ > MAX_RETRY_SET_CPUCORE) { ++ DHD_ERROR(("%s: rxf(%d) invalid cpu!\n", __FUNCTION__, e_rxf)); ++ return; ++ } ++ if (e_rxf < 0) ++ OSL_SLEEP(1); ++ } while (e_rxf < 0); ++ } ++#ifdef DHD_OF_SUPPORT ++ interrupt_set_cpucore(set); ++#endif /* DHD_OF_SUPPORT */ ++ DHD_TRACE(("%s: set(%d) cpucore success!\n", __FUNCTION__, set)); ++ ++ return; ++} ++#endif /* CUSTOM_SET_CPUCORE */ ++#if defined(DHD_TCP_WINSIZE_ADJUST) ++static int dhd_port_list_match(int port) ++{ ++ int i; ++ for (i = 0; i < MAX_TARGET_PORTS; i++) { ++ if (target_ports[i] == port) ++ return 1; ++ } ++ return 0; ++} ++static void dhd_adjust_tcp_winsize(int op_mode, struct sk_buff *skb) ++{ ++ struct iphdr *ipheader; ++ struct tcphdr *tcpheader; ++ uint16 win_size; ++ int32 incremental_checksum; ++ ++ if (!(op_mode & DHD_FLAG_HOSTAP_MODE)) ++ return; ++ if (skb == NULL || skb->data == NULL) ++ return; ++ ++ ipheader = (struct iphdr*)(skb->data); ++ ++ if (ipheader->protocol == IPPROTO_TCP) { ++ tcpheader = (struct tcphdr*) skb_pull(skb, (ipheader->ihl)<<2); ++ if (tcpheader) { ++ win_size = ntoh16(tcpheader->window); ++ if (win_size < MIN_TCP_WIN_SIZE && ++ dhd_port_list_match(ntoh16(tcpheader->dest))) { ++ incremental_checksum = ntoh16(tcpheader->check); ++ incremental_checksum += win_size - win_size*WIN_SIZE_SCALE_FACTOR; ++ if (incremental_checksum < 0) ++ --incremental_checksum; ++ tcpheader->window = hton16(win_size*WIN_SIZE_SCALE_FACTOR); ++ tcpheader->check = hton16((unsigned short)incremental_checksum); ++ } ++ } ++ skb_push(skb, (ipheader->ihl)<<2); ++ } ++} ++#endif /* DHD_TCP_WINSIZE_ADJUST */ ++ ++/* Get interface specific ap_isolate configuration */ ++int dhd_get_ap_isolate(dhd_pub_t *dhdp, uint32 idx) ++{ ++ dhd_info_t *dhd = dhdp->info; ++ dhd_if_t *ifp; ++ ++ ASSERT(idx < DHD_MAX_IFS); ++ ++ ifp = dhd->iflist[idx]; ++ ++ return ifp->ap_isolate; ++} ++ ++/* Set interface specific ap_isolate configuration */ ++int dhd_set_ap_isolate(dhd_pub_t *dhdp, uint32 idx, int val) ++{ ++ dhd_info_t *dhd = dhdp->info; ++ dhd_if_t *ifp; ++ ++ ASSERT(idx < DHD_MAX_IFS); ++ ++ ifp = dhd->iflist[idx]; ++ ++ ifp->ap_isolate = val; ++ ++ return 0; ++} ++ ++#ifdef DHD_WMF ++/* Returns interface specific WMF configuration */ ++dhd_wmf_t* dhd_wmf_conf(dhd_pub_t *dhdp, uint32 idx) ++{ ++ dhd_info_t *dhd = dhdp->info; ++ dhd_if_t *ifp; ++ ++ ASSERT(idx < DHD_MAX_IFS); ++ ++ ifp = dhd->iflist[idx]; ++ return &ifp->wmf; ++} ++#endif /* DHD_WMF */ ++ ++ ++#ifdef DHD_UNICAST_DHCP ++static int ++dhd_get_pkt_ether_type(dhd_pub_t *pub, void *pktbuf, ++ uint8 **data_ptr, int *len_ptr, uint16 *et_ptr, bool *snap_ptr) ++{ ++ uint8 *frame = PKTDATA(pub->osh, pktbuf); ++ int length = PKTLEN(pub->osh, pktbuf); ++ uint8 *pt; /* Pointer to type field */ ++ uint16 ethertype; ++ bool snap = FALSE; ++ /* Process Ethernet II or SNAP-encapsulated 802.3 frames */ ++ if (length < ETHER_HDR_LEN) { ++ DHD_ERROR(("dhd: %s: short eth frame (%d)\n", ++ __FUNCTION__, length)); ++ return BCME_ERROR; ++ } else if (ntoh16_ua(frame + ETHER_TYPE_OFFSET) >= ETHER_TYPE_MIN) { ++ /* Frame is Ethernet II */ ++ pt = frame + ETHER_TYPE_OFFSET; ++ } else if (length >= ETHER_HDR_LEN + SNAP_HDR_LEN + ETHER_TYPE_LEN && ++ !bcmp(llc_snap_hdr, frame + ETHER_HDR_LEN, SNAP_HDR_LEN)) { ++ pt = frame + ETHER_HDR_LEN + SNAP_HDR_LEN; ++ snap = TRUE; ++ } else { ++ DHD_INFO(("DHD: %s: non-SNAP 802.3 frame\n", ++ __FUNCTION__)); ++ return BCME_ERROR; ++ } ++ ++ ethertype = ntoh16_ua(pt); ++ ++ /* Skip VLAN tag, if any */ ++ if (ethertype == ETHER_TYPE_8021Q) { ++ pt += VLAN_TAG_LEN; ++ ++ if ((pt + ETHER_TYPE_LEN) > (frame + length)) { ++ DHD_ERROR(("dhd: %s: short VLAN frame (%d)\n", ++ __FUNCTION__, length)); ++ return BCME_ERROR; ++ } ++ ++ ethertype = ntoh16_ua(pt); ++ } ++ ++ *data_ptr = pt + ETHER_TYPE_LEN; ++ *len_ptr = length - (pt + ETHER_TYPE_LEN - frame); ++ *et_ptr = ethertype; ++ *snap_ptr = snap; ++ return BCME_OK; ++} ++ ++static int ++dhd_get_pkt_ip_type(dhd_pub_t *pub, void *pktbuf, ++ uint8 **data_ptr, int *len_ptr, uint8 *prot_ptr) ++{ ++ struct ipv4_hdr *iph; /* IP frame pointer */ ++ int iplen; /* IP frame length */ ++ uint16 ethertype, iphdrlen, ippktlen; ++ uint16 iph_frag; ++ uint8 prot; ++ bool snap; ++ ++ if (dhd_get_pkt_ether_type(pub, pktbuf, (uint8 **)&iph, ++ &iplen, ðertype, &snap) != 0) ++ return BCME_ERROR; ++ ++ if (ethertype != ETHER_TYPE_IP) { ++ return BCME_ERROR; ++ } ++ ++ /* We support IPv4 only */ ++ if (iplen < IPV4_OPTIONS_OFFSET || (IP_VER(iph) != IP_VER_4)) { ++ return BCME_ERROR; ++ } ++ ++ /* Header length sanity */ ++ iphdrlen = IPV4_HLEN(iph); ++ ++ /* ++ * Packet length sanity; sometimes we receive eth-frame size bigger ++ * than the IP content, which results in a bad tcp chksum ++ */ ++ ippktlen = ntoh16(iph->tot_len); ++ if (ippktlen < iplen) { ++ ++ DHD_INFO(("%s: extra frame length ignored\n", ++ __FUNCTION__)); ++ iplen = ippktlen; ++ } else if (ippktlen > iplen) { ++ DHD_ERROR(("dhd: %s: truncated IP packet (%d)\n", ++ __FUNCTION__, ippktlen - iplen)); ++ return BCME_ERROR; ++ } ++ ++ if (iphdrlen < IPV4_OPTIONS_OFFSET || iphdrlen > iplen) { ++ DHD_ERROR(("DHD: %s: IP-header-len (%d) out of range (%d-%d)\n", ++ __FUNCTION__, iphdrlen, IPV4_OPTIONS_OFFSET, iplen)); ++ return BCME_ERROR; ++ } ++ ++ /* ++ * We don't handle fragmented IP packets. A first frag is indicated by the MF ++ * (more frag) bit and a subsequent frag is indicated by a non-zero frag offset. ++ */ ++ iph_frag = ntoh16(iph->frag); ++ ++ if ((iph_frag & IPV4_FRAG_MORE) || (iph_frag & IPV4_FRAG_OFFSET_MASK) != 0) { ++ DHD_INFO(("DHD:%s: IP fragment not handled\n", ++ __FUNCTION__)); ++ return BCME_ERROR; ++ } ++ ++ prot = IPV4_PROT(iph); ++ ++ *data_ptr = (((uint8 *)iph) + iphdrlen); ++ *len_ptr = iplen - iphdrlen; ++ *prot_ptr = prot; ++ return BCME_OK; ++} ++ ++/** check the packet type, if it is DHCP ACK/REPLY, convert into unicast packet */ ++static ++int dhd_convert_dhcp_broadcast_ack_to_unicast(dhd_pub_t *pub, void *pktbuf, int ifidx) ++{ ++ dhd_sta_t* stainfo; ++ uint8 *eh = PKTDATA(pub->osh, pktbuf); ++ uint8 *udph; ++ uint8 *dhcp; ++ uint8 *chaddr; ++ int udpl; ++ int dhcpl; ++ uint16 port; ++ uint8 prot; ++ ++ if (!ETHER_ISMULTI(eh + ETHER_DEST_OFFSET)) ++ return BCME_ERROR; ++ if (dhd_get_pkt_ip_type(pub, pktbuf, &udph, &udpl, &prot) != 0) ++ return BCME_ERROR; ++ if (prot != IP_PROT_UDP) ++ return BCME_ERROR; ++ /* check frame length, at least UDP_HDR_LEN */ ++ if (udpl < UDP_HDR_LEN) { ++ DHD_ERROR(("DHD: %s: short UDP frame, ignored\n", ++ __FUNCTION__)); ++ return BCME_ERROR; ++ } ++ port = ntoh16_ua(udph + UDP_DEST_PORT_OFFSET); ++ /* only process DHCP packets from server to client */ ++ if (port != DHCP_PORT_CLIENT) ++ return BCME_ERROR; ++ ++ dhcp = udph + UDP_HDR_LEN; ++ dhcpl = udpl - UDP_HDR_LEN; ++ ++ if (dhcpl < DHCP_CHADDR_OFFSET + ETHER_ADDR_LEN) { ++ DHD_ERROR(("DHD: %s: short DHCP frame, ignored\n", ++ __FUNCTION__)); ++ return BCME_ERROR; ++ } ++ /* only process DHCP reply(offer/ack) packets */ ++ if (*(dhcp + DHCP_TYPE_OFFSET) != DHCP_TYPE_REPLY) ++ return BCME_ERROR; ++ chaddr = dhcp + DHCP_CHADDR_OFFSET; ++ stainfo = dhd_find_sta(pub, ifidx, chaddr); ++ if (stainfo) { ++ bcopy(chaddr, eh + ETHER_DEST_OFFSET, ETHER_ADDR_LEN); ++ return BCME_OK; ++ } ++ return BCME_ERROR; ++} ++#endif /* DHD_UNICAST_DHD */ ++#ifdef DHD_L2_FILTER ++/* Check if packet type is ICMP ECHO */ ++static ++int dhd_l2_filter_block_ping(dhd_pub_t *pub, void *pktbuf, int ifidx) ++{ ++ struct bcmicmp_hdr *icmph; ++ int udpl; ++ uint8 prot; ++ ++ if (dhd_get_pkt_ip_type(pub, pktbuf, (uint8 **)&icmph, &udpl, &prot) != 0) ++ return BCME_ERROR; ++ if (prot == IP_PROT_ICMP) { ++ if (icmph->type == ICMP_TYPE_ECHO_REQUEST) ++ return BCME_OK; ++ } ++ return BCME_ERROR; ++} ++#endif /* DHD_L2_FILTER */ ++ ++#ifdef SET_RPS_CPUS ++int custom_rps_map_set(struct netdev_rx_queue *queue, char *buf, size_t len) ++{ ++ struct rps_map *old_map, *map; ++ cpumask_var_t mask; ++ int err, cpu, i; ++ static DEFINE_SPINLOCK(rps_map_lock); ++ ++ DHD_INFO(("%s : Entered.\n", __FUNCTION__)); ++ ++ if (!alloc_cpumask_var(&mask, GFP_KERNEL)) { ++ DHD_ERROR(("%s : alloc_cpumask_var fail.\n", __FUNCTION__)); ++ return -ENOMEM; ++ } ++ ++ err = bitmap_parse(buf, len, cpumask_bits(mask), nr_cpumask_bits); ++ if (err) { ++ free_cpumask_var(mask); ++ DHD_ERROR(("%s : bitmap_parse fail.\n", __FUNCTION__)); ++ return err; ++ } ++ ++ map = kzalloc(max_t(unsigned int, ++ RPS_MAP_SIZE(cpumask_weight(mask)), L1_CACHE_BYTES), ++ GFP_KERNEL); ++ if (!map) { ++ free_cpumask_var(mask); ++ DHD_ERROR(("%s : map malloc fail.\n", __FUNCTION__)); ++ return -ENOMEM; ++ } ++ ++ i = 0; ++ for_each_cpu(cpu, mask) ++ map->cpus[i++] = cpu; ++ ++ if (i) ++ map->len = i; ++ else { ++ kfree(map); ++ DHD_ERROR(("%s : mapping cpu fail.\n", __FUNCTION__)); ++ map = NULL; ++ } ++ ++ spin_lock(&rps_map_lock); ++ old_map = rcu_dereference_protected(queue->rps_map, ++ lockdep_is_held(&rps_map_lock)); ++ rcu_assign_pointer(queue->rps_map, map); ++ spin_unlock(&rps_map_lock); ++ ++ if (map) ++ static_key_slow_inc(&rps_needed); ++ if (old_map) { ++ kfree_rcu(old_map, rcu); ++ static_key_slow_dec(&rps_needed); ++ } ++ free_cpumask_var(mask); ++ ++ DHD_INFO(("%s : Done. mapping cpu nummber : %d\n", __FUNCTION__, map->len)); ++ return map->len; ++} ++ ++void custom_rps_map_clear(struct netdev_rx_queue *queue) ++{ ++ struct rps_map *map; ++ ++ DHD_INFO(("%s : Entered.\n", __FUNCTION__)); ++ ++ map = rcu_dereference_protected(queue->rps_map, 1); ++ if (map) { ++ RCU_INIT_POINTER(queue->rps_map, NULL); ++ kfree_rcu(map, rcu); ++ DHD_INFO(("%s : rps_cpus map clear.\n", __FUNCTION__)); ++ } ++} ++#endif /* SET_RPS_CPUS */ ++ ++#if defined(CUSTOMER_HW20) && defined(WLANAUDIO) ++void ++SDA_setSharedMemory4Send(unsigned int buffer_id, ++ unsigned char *buffer, unsigned int buffer_size, ++ unsigned int packet_size, unsigned int headroom_size) ++{ ++ dhd_info_t *dhd = dhd_global; ++ ++ sda_packet_length = packet_size; ++ ++ ASSERT(dhd); ++ if (dhd == NULL) ++ return; ++} ++ ++void ++SDA_registerCallback4SendDone(SDA_SendDoneCallBack packet_cb) ++{ ++ dhd_info_t *dhd = dhd_global; ++ ++ ASSERT(dhd); ++ if (dhd == NULL) ++ return; ++} ++ ++ ++unsigned long long ++SDA_getTsf(unsigned char vif_id) ++{ ++ dhd_info_t *dhd = dhd_global; ++ uint64 tsf_val; ++ char buf[WLC_IOCTL_SMLEN]; ++ int ifidx = 0; ++ ++ struct tsf { ++ uint32 low; ++ uint32 high; ++ } tsf_buf; ++ ++ memset(buf, 0, sizeof(buf)); ++ ++ if (vif_id == 0) /* wlan0 tsf */ ++ ifidx = dhd_ifname2idx(dhd, "wlan0"); ++ else if (vif_id == 1) /* p2p0 tsf */ ++ ifidx = dhd_ifname2idx(dhd, "p2p0"); ++ ++ bcm_mkiovar("tsf_bss", 0, 0, buf, sizeof(buf)); ++ ++ if (dhd_wl_ioctl_cmd(&dhd->pub, WLC_GET_VAR, buf, sizeof(buf), FALSE, ifidx) < 0) { ++ DHD_ERROR(("%s wl ioctl error\n", __FUNCTION__)); ++ return 0; ++ } ++ ++ memcpy(&tsf_buf, buf, sizeof(tsf_buf)); ++ tsf_val = (uint64)tsf_buf.high; ++ DHD_TRACE(("%s tsf high 0x%08x, low 0x%08x\n", ++ __FUNCTION__, tsf_buf.high, tsf_buf.low)); ++ ++ return ((tsf_val << 32) | tsf_buf.low); ++} ++EXPORT_SYMBOL(SDA_getTsf); ++ ++unsigned int ++SDA_syncTsf(void) ++{ ++ dhd_info_t *dhd = dhd_global; ++ int tsf_sync = 1; ++ char iovbuf[WLC_IOCTL_SMLEN]; ++ ++ bcm_mkiovar("wa_tsf_sync", (char *)&tsf_sync, 4, iovbuf, sizeof(iovbuf)); ++ dhd_wl_ioctl_cmd(&dhd->pub, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); ++ ++ DHD_TRACE(("%s\n", __FUNCTION__)); ++ return 0; ++} ++ ++extern struct net_device *wl0dot1_dev; ++ ++void ++BCMFASTPATH SDA_function4Send(uint buffer_id, void *packet, uint packet_size) ++{ ++ struct sk_buff *skb; ++ sda_packet_t *shm_packet = packet; ++ dhd_info_t *dhd = dhd_global; ++ int cnt; ++ ++ static unsigned int cnt_t = 1; ++ ++ ASSERT(dhd); ++ if (dhd == NULL) ++ return; ++ ++ if (dhd->is_wlanaudio_blist) { ++ for (cnt = 0; cnt < MAX_WLANAUDIO_BLACKLIST; cnt++) { ++ if (dhd->wlanaudio_blist[cnt].is_blacklist == true) { ++ if (!bcmp(dhd->wlanaudio_blist[cnt].blacklist_addr.octet, ++ shm_packet->headroom.ether_dhost, ETHER_ADDR_LEN)) ++ return; ++ } ++ } ++ } ++ ++ if ((cnt_t % 10000) == 0) ++ cnt_t = 0; ++ ++ cnt_t++; ++ ++ /* packet_size may be smaller than SDA_SHM_PKT_SIZE, remaining will be garbage */ ++#define TXOFF 26 ++ skb = __dev_alloc_skb(TXOFF + sda_packet_length - SDA_PKT_HEADER_SIZE, GFP_ATOMIC); ++ ++ skb_reserve(skb, TXOFF - SDA_HEADROOM_SIZE); ++ skb_put(skb, sda_packet_length - SDA_PKT_HEADER_SIZE + SDA_HEADROOM_SIZE); ++ skb->priority = PRIO_8021D_VO; /* PRIO_8021D_VO or PRIO_8021D_VI */ ++ ++ /* p2p_net */ ++ skb->dev = wl0dot1_dev; ++ shm_packet->txTsf = 0x0; ++ shm_packet->rxTsf = 0x0; ++ memcpy(skb->data, &shm_packet->headroom, ++ sda_packet_length - OFFSETOF(sda_packet_t, headroom)); ++ shm_packet->desc.ready_to_copy = 0; ++ ++ dhd_start_xmit(skb, skb->dev); ++} ++ ++void ++SDA_registerCallback4Recv(unsigned char *pBufferTotal, ++ unsigned int BufferTotalSize) ++{ ++ dhd_info_t *dhd = dhd_global; ++ ++ ASSERT(dhd); ++ if (dhd == NULL) ++ return; ++} ++ ++ ++void ++SDA_setSharedMemory4Recv(unsigned char *pBufferTotal, ++ unsigned int BufferTotalSize, ++ unsigned int BufferUnitSize, ++ unsigned int Headroomsize) ++{ ++ dhd_info_t *dhd = dhd_global; ++ ++ ASSERT(dhd); ++ if (dhd == NULL) ++ return; ++} ++ ++ ++void ++SDA_function4RecvDone(unsigned char * pBuffer, unsigned int BufferSize) ++{ ++ dhd_info_t *dhd = dhd_global; ++ ++ ASSERT(dhd); ++ if (dhd == NULL) ++ return; ++} ++ ++EXPORT_SYMBOL(SDA_setSharedMemory4Send); ++EXPORT_SYMBOL(SDA_registerCallback4SendDone); ++EXPORT_SYMBOL(SDA_syncTsf); ++EXPORT_SYMBOL(SDA_function4Send); ++EXPORT_SYMBOL(SDA_registerCallback4Recv); ++EXPORT_SYMBOL(SDA_setSharedMemory4Recv); ++EXPORT_SYMBOL(SDA_function4RecvDone); ++ ++#endif /* CUSTOMER_HW20 && WLANAUDIO */ ++ ++void *dhd_get_pub(struct net_device *dev) ++{ ++ dhd_info_t *dhdinfo = *(dhd_info_t **)netdev_priv(dev); ++ if (dhdinfo) ++ return (void *)&dhdinfo->pub; ++ else ++ return NULL; ++} ++ ++bool dhd_os_wd_timer_enabled(void *bus) ++{ ++ dhd_pub_t *pub = bus; ++ dhd_info_t *dhd = (dhd_info_t *)pub->info; ++ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ if (!dhd) { ++ DHD_ERROR(("%s: dhd NULL\n", __FUNCTION__)); ++ return FALSE; ++ } ++ return dhd->wd_timer_valid; ++} +diff -Nur a/drivers/net/wireless/bcmdhd/dhd_linux.h c/drivers/net/wireless/bcmdhd/dhd_linux.h +--- a/drivers/net/wireless/bcmdhd/dhd_linux.h 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/dhd_linux.h 2016-05-13 09:48:20.000000000 +0200 +@@ -32,6 +32,16 @@ + + #define DHD_REGISTRATION_TIMEOUT 12000 /* msec : allowed time to finished dhd registration */ + ++#if defined(CUSTOMER_HW) ++struct wifi_platform_data { ++ int (*set_power)(bool val); ++ int (*set_carddetect)(bool val); ++ void *(*mem_prealloc)(int section, unsigned long size); ++ int (*get_mac_addr)(unsigned char *buf); ++ void *(*get_country_code)(char *ccode); ++}; ++#endif ++ + typedef struct wifi_adapter_info { + const char *name; + uint irq_num; +diff -Nur a/drivers/net/wireless/bcmdhd/dhd_linux_platdev.c c/drivers/net/wireless/bcmdhd/dhd_linux_platdev.c +--- a/drivers/net/wireless/bcmdhd/dhd_linux_platdev.c 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/dhd_linux_platdev.c 2016-09-30 00:19:04.817804108 +0200 +@@ -26,7 +26,7 @@ + #include + #endif /* CONFIG_DTS */ + +-#ifdef CUSTOMER_HW ++#if defined(CUSTOMER_HW) + #if defined(CUSTOMER_OOB) + extern uint bcm_wlan_get_oob_irq(void); + extern uint bcm_wlan_get_oob_irq_flags(void); +@@ -34,14 +34,6 @@ + extern int bcm_wlan_set_plat_data(void); + #endif /* CUSTOMER_HW */ + +-struct wifi_platform_data { +- int (*set_power)(bool val); +- int (*set_carddetect)(bool val); +- void *(*mem_prealloc)(int section, unsigned long size); +- int (*get_mac_addr)(unsigned char *buf); +- void *(*get_country_code)(char *ccode); +-}; +- + #define WIFI_PLAT_NAME "bcmdhd_wlan" + #define WIFI_PLAT_NAME2 "bcm4329_wlan" + #define WIFI_PLAT_EXT "bcmdhd_wifi_platform" +@@ -57,13 +49,14 @@ + #if !defined(CONFIG_DTS) + #if defined(DHD_OF_SUPPORT) + static bool dts_enabled = TRUE; +-extern struct resource dhd_wlan_resources; + extern struct wifi_platform_data dhd_wlan_control; + #else + static bool dts_enabled = FALSE; + struct resource dhd_wlan_resources = {0}; ++#ifdef CUSTOMER_HW + struct wifi_platform_data dhd_wlan_control = {0}; +-#endif /* CONFIG_OF && !defined(CONFIG_ARCH_MSM) */ ++#endif ++#endif /* !defind(DHD_OF_SUPPORT) */ + #endif /* !defind(CONFIG_DTS) */ + + static int dhd_wifi_platform_load(void); +@@ -239,6 +232,7 @@ + return NULL; + } + ++#ifndef CUSTOMER_HW + static int wifi_plat_dev_drv_probe(struct platform_device *pdev) + { + struct resource *resource; +@@ -346,6 +340,7 @@ + {}, + }; + #endif /* CONFIG_DTS */ ++ + static struct platform_driver wifi_platform_dev_driver = { + .probe = wifi_plat_dev_drv_probe, + .remove = wifi_plat_dev_drv_remove, +@@ -381,17 +376,20 @@ + + return FALSE; + } ++#endif + + static int wifi_ctrlfunc_register_drv(void) + { +- int err = 0; +- struct device *dev1, *dev2; + wifi_adapter_info_t *adapter; + ++#ifndef CUSTOMER_HW ++ int err = 0; ++ struct device *dev1, *dev2; + dev1 = bus_find_device(&platform_bus_type, NULL, WIFI_PLAT_NAME, wifi_platdev_match); + dev2 = bus_find_device(&platform_bus_type, NULL, WIFI_PLAT_NAME2, wifi_platdev_match); ++#endif + +-#if !defined(CONFIG_DTS) ++#if !defined(CONFIG_DTS) && !defined(CUSTOMER_HW) + if (!dts_enabled) { + if (dev1 == NULL && dev2 == NULL) { + DHD_ERROR(("no wifi platform data, skip\n")); +@@ -415,6 +413,7 @@ + dhd_wifi_platdata->num_adapters = 1; + dhd_wifi_platdata->adapters = adapter; + ++#ifndef CUSTOMER_HW + if (dev1) { + err = platform_driver_register(&wifi_platform_dev_driver); + if (err) { +@@ -431,11 +430,12 @@ + return err; + } + } ++#endif + + #if !defined(CONFIG_DTS) + if (dts_enabled) { +- adapter->wifi_plat_data = (void *)&dhd_wlan_control; + #ifdef CUSTOMER_HW ++ adapter->wifi_plat_data = (void *)&dhd_wlan_control; + bcm_wlan_set_plat_data(); + #ifdef CUSTOMER_OOB + adapter->irq_num = bcm_wlan_get_oob_irq(); +@@ -452,7 +452,7 @@ + #endif /* !defined(CONFIG_DTS) */ + + +-#ifdef CONFIG_DTS ++#if defined(CONFIG_DTS) && !defined(CUSTOMER_HW) + wifi_plat_dev_probe_ret = platform_driver_register(&wifi_platform_dev_driver); + #endif /* CONFIG_DTS */ + +@@ -462,23 +462,26 @@ + + void wifi_ctrlfunc_unregister_drv(void) + { +- struct device *dev1, *dev2; + +-#ifdef CONFIG_DTS ++#if defined(CONFIG_DTS) && !defined(CUSTOMER_HW) + DHD_ERROR(("unregister wifi platform drivers\n")); + platform_driver_unregister(&wifi_platform_dev_driver); + #else ++#ifndef CUSTOMER_HW ++ struct device *dev1, *dev2; + dev1 = bus_find_device(&platform_bus_type, NULL, WIFI_PLAT_NAME, wifi_platdev_match); + dev2 = bus_find_device(&platform_bus_type, NULL, WIFI_PLAT_NAME2, wifi_platdev_match); + if (!dts_enabled) + if (dev1 == NULL && dev2 == NULL) + return; +- ++#endif + DHD_ERROR(("unregister wifi platform drivers\n")); ++#ifndef CUSTOMER_HW + if (dev1) + platform_driver_unregister(&wifi_platform_dev_driver); + if (dev2) + platform_driver_unregister(&wifi_platform_dev_driver_legacy); ++#endif + if (dts_enabled) { + wifi_adapter_info_t *adapter; + adapter = &dhd_wifi_platdata->adapters[0]; +@@ -496,6 +499,7 @@ + dhd_wifi_platdata = NULL; + } + ++#ifndef CUSTOMER_HW + static int bcmdhd_wifi_plat_dev_drv_probe(struct platform_device *pdev) + { + dhd_wifi_platdata = (bcmdhd_wifi_platdata_t *)(pdev->dev.platform_data); +@@ -525,10 +529,12 @@ + .name = WIFI_PLAT_EXT, + } + }; ++#endif + + int dhd_wifi_platform_register_drv(void) + { + int err = 0; ++#ifndef CUSTOMER_HW + struct device *dev; + + /* register Broadcom wifi platform data driver if multi-chip is enabled, +@@ -545,7 +551,9 @@ + return -ENXIO; + } + err = platform_driver_register(&dhd_wifi_platform_dev_driver); +- } else { ++ } else ++#endif ++ { + err = wifi_ctrlfunc_register_drv(); + + /* no wifi ctrl func either, load bus directly and ignore this error */ +@@ -650,9 +658,11 @@ + + void dhd_wifi_platform_unregister_drv(void) + { ++#ifndef CUSTOMER_HW + if (cfg_multichip) + platform_driver_unregister(&dhd_wifi_platform_dev_driver); + else ++#endif + wifi_ctrlfunc_unregister_drv(); + } + +@@ -661,7 +671,7 @@ + extern uint dhd_deferred_tx; + #if defined(BCMLXSDMMC) + extern struct semaphore dhd_registration_sem; +-#endif ++#endif + + #ifdef BCMSDIO + static int dhd_wifi_platform_load_sdio(void) +@@ -702,6 +712,7 @@ + adapter->bus_type, adapter->bus_num, adapter->slot_num)); + + do { ++#ifndef CONFIG_ARCH_SUNXI + sema_init(&dhd_chipup_sem, 0); + err = dhd_bus_reg_sdio_notify(&dhd_chipup_sem); + if (err) { +@@ -709,6 +720,7 @@ + __FUNCTION__, err)); + return err; + } ++#endif + err = wifi_platform_set_power(adapter, TRUE, WIFI_TURNON_DELAY); + if (err) { + /* WL_REG_ON state unknown, Power off forcely */ +@@ -718,6 +730,15 @@ + wifi_platform_bus_enumerate(adapter, TRUE); + err = 0; + } ++#ifdef CONFIG_ARCH_SUNXI ++ sema_init(&dhd_chipup_sem, 0); ++ err = dhd_bus_reg_sdio_notify(&dhd_chipup_sem); ++ if (err) { ++ DHD_ERROR(("%s dhd_bus_reg_sdio_notify fail(%d)\n\n", ++ __FUNCTION__, err)); ++ return err; ++ } ++#endif + + if (down_timeout(&dhd_chipup_sem, msecs_to_jiffies(POWERUP_WAIT_MS)) == 0) { + dhd_bus_unreg_sdio_notify(); +@@ -772,7 +793,7 @@ + /* x86 bring-up PC needs no power-up operations */ + err = dhd_bus_register(); + +-#endif ++#endif + + return err; + } +@@ -805,8 +826,10 @@ + end: + if (err) + wl_android_exit(); ++#if !defined(MULTIPLE_SUPPLICANT) + else + wl_android_post_init(); ++#endif + + return err; + } +diff -Nur a/drivers/net/wireless/bcmdhd/dhd_msgbuf.c c/drivers/net/wireless/bcmdhd/dhd_msgbuf.c +--- a/drivers/net/wireless/bcmdhd/dhd_msgbuf.c 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/dhd_msgbuf.c 2016-05-13 09:48:20.000000000 +0200 +@@ -6,7 +6,7 @@ + * + * $Copyright Open Broadcom Corporation$ + * +- * $Id: dhd_msgbuf.c 490973 2014-07-14 12:32:56Z $ ++ * $Id: dhd_msgbuf.c 504484 2014-09-24 10:11:20Z $ + */ + #include + #include +@@ -33,6 +33,26 @@ + + #include + #include ++#include ++#include ++ ++/* ++ * PCIE D2H DMA Complete Sync Modes ++ * ++ * Firmware may interrupt the host, prior to the D2H Mem2Mem DMA completes into ++ * Host system memory. A WAR using one of 3 approaches is needed: ++ * 1. Dongle places ia modulo-253 seqnum in last word of each D2H message ++ * 2. XOR Checksum, with epoch# in each work item. Dongle builds an XOR checksum ++ * writes in the last word of each work item. Each work item has a seqnum ++ * number = sequence num % 253. ++ * 3. Read Barrier: Dongle does a host memory read access prior to posting an ++ * interrupt. ++ * Host does not participate with option #3, other than reserving a host system ++ * memory location for the dongle to read. ++ */ ++#define PCIE_D2H_SYNC ++#define PCIE_D2H_SYNC_WAIT_TRIES 1024 ++#define PCIE_D2H_SYNC_BZERO /* bzero a message before updating the RD offset */ + + #define RETRIES 2 /* # of retries to retrieve matching ioctl response */ + #define IOCTL_HDR_LEN 12 +@@ -89,8 +109,17 @@ + #endif /* TXP_FLUSH_NITEMS */ + ring_mem_t *ringmem; + ring_state_t *ringstate; ++#if defined(PCIE_D2H_SYNC) ++ uint32 seqnum; ++#endif /* PCIE_D2H_SYNC */ ++ void *secdma; + } msgbuf_ring_t; + ++#if defined(PCIE_D2H_SYNC) ++/* Custom callback attached based upon D2H DMA Sync mode used in dongle. */ ++typedef uint8 (* d2h_sync_cb_t)(dhd_pub_t *dhd, msgbuf_ring_t *ring, ++ volatile cmn_msg_hdr_t *msg, int msglen); ++#endif /* PCIE_D2H_SYNC */ + + typedef struct dhd_prot { + osl_t *osh; /* OSL handle */ +@@ -133,6 +162,11 @@ + uint32 d2h_dma_readindx_buf_len; /* For holding dma ringupd buf - completion read */ + dhd_mem_map_t d2h_dma_readindx_buf; /* For holding dma ringupd buf - completion read */ + ++#if defined(PCIE_D2H_SYNC) ++ d2h_sync_cb_t d2h_sync_cb; /* Sync on D2H DMA done: SEQNUM or XORCSUM */ ++ ulong d2h_sync_wait_max; /* max number of wait loops to receive one msg */ ++ ulong d2h_sync_wait_tot; /* total wait loops */ ++#endif /* PCIE_D2H_SYNC */ + dhd_dmaxfer_t dmaxfer; + bool dmaxfer_in_progress; + +@@ -159,6 +193,7 @@ + static int dhd_prot_process_msgtype(dhd_pub_t *dhd, msgbuf_ring_t *ring, uint8* buf, uint16 len); + static int dhd_process_msgtype(dhd_pub_t *dhd, msgbuf_ring_t *ring, uint8* buf, uint16 len); + ++static void dhd_prot_noop(dhd_pub_t *dhd, void * buf, uint16 msglen); + static void dhd_prot_txstatus_process(dhd_pub_t *dhd, void * buf, uint16 msglen); + static void dhd_prot_ioctcmplt_process(dhd_pub_t *dhd, void * buf, uint16 msglen); + static void dhd_prot_ioctack_process(dhd_pub_t *dhd, void * buf, uint16 msglen); +@@ -217,7 +252,7 @@ + + typedef void (*dhd_msgbuf_func_t)(dhd_pub_t *dhd, void * buf, uint16 msglen); + static dhd_msgbuf_func_t table_lookup[DHD_PROT_FUNCS] = { +- NULL, ++ dhd_prot_noop, /* 0 is invalid message type */ + dhd_prot_genstatus_process, /* MSG_TYPE_GEN_STATUS */ + dhd_prot_ringstatus_process, /* MSG_TYPE_RING_STATUS */ + NULL, +@@ -241,6 +276,145 @@ + NULL, + }; + ++ ++#if defined(PCIE_D2H_SYNC) ++ ++/* ++ * D2H DMA to completion callback handlers. Based on the mode advertised by the ++ * dongle through the PCIE shared region, the appropriate callback will be ++ * registered in the proto layer to be invoked prior to precessing any message ++ * from a D2H DMA ring. If the dongle uses a read barrier or another mode that ++ * does not require host participation, then a noop callback handler will be ++ * bound that simply returns the msgtype. ++ */ ++static void dhd_prot_d2h_sync_livelock(dhd_pub_t *dhd, uint32 seqnum, ++ uint32 tries, uchar *msg, int msglen); ++static uint8 dhd_prot_d2h_sync_seqnum(dhd_pub_t *dhd, msgbuf_ring_t *ring, ++ volatile cmn_msg_hdr_t *msg, int msglen); ++static uint8 dhd_prot_d2h_sync_xorcsum(dhd_pub_t *dhd, msgbuf_ring_t *ring, ++ volatile cmn_msg_hdr_t *msg, int msglen); ++static uint8 dhd_prot_d2h_sync_none(dhd_pub_t *dhd, msgbuf_ring_t *ring, ++ volatile cmn_msg_hdr_t *msg, int msglen); ++static void dhd_prot_d2h_sync_init(dhd_pub_t *dhd, dhd_prot_t * prot); ++ ++/* Debug print a livelock avert by dropping a D2H message */ ++static void ++dhd_prot_d2h_sync_livelock(dhd_pub_t *dhd, uint32 seqnum, uint32 tries, ++ uchar *msg, int msglen) ++{ ++ DHD_ERROR(("LIVELOCK DHD<%p> seqnum<%u:%u> tries<%u> max<%lu> tot<%lu>\n", ++ dhd, seqnum, seqnum% D2H_EPOCH_MODULO, tries, ++ dhd->prot->d2h_sync_wait_max, dhd->prot->d2h_sync_wait_tot)); ++ prhex("D2H MsgBuf Failure", (uchar *)msg, msglen); ++} ++ ++/* Sync on a D2H DMA to complete using SEQNUM mode */ ++static uint8 BCMFASTPATH ++dhd_prot_d2h_sync_seqnum(dhd_pub_t *dhd, msgbuf_ring_t *ring, ++ volatile cmn_msg_hdr_t *msg, int msglen) ++{ ++ uint32 tries; ++ uint32 ring_seqnum = ring->seqnum % D2H_EPOCH_MODULO; ++ int num_words = msglen / sizeof(uint32); /* num of 32bit words */ ++ volatile uint32 *marker = (uint32 *)msg + (num_words - 1); /* last word */ ++ dhd_prot_t *prot = dhd->prot; ++ ++ ASSERT(msglen == RING_LEN_ITEMS(ring)); ++ ++ for (tries = 0; tries < PCIE_D2H_SYNC_WAIT_TRIES; tries++) { ++ uint32 msg_seqnum = *marker; ++ if (ltoh32(msg_seqnum) == ring_seqnum) { /* dma upto last word done */ ++ ring->seqnum++; /* next expected sequence number */ ++ goto dma_completed; ++ } ++ ++ if (tries > prot->d2h_sync_wait_max) ++ prot->d2h_sync_wait_max = tries; ++ ++ OSL_CACHE_INV(msg, msglen); /* invalidate and try again */ ++ ++ } /* for PCIE_D2H_SYNC_WAIT_TRIES */ ++ ++ dhd_prot_d2h_sync_livelock(dhd, ring->seqnum, tries, (uchar *)msg, msglen); ++ ++ ring->seqnum++; /* skip this message ... leak of a pktid */ ++ return 0; /* invalid msgtype 0 -> noop callback */ ++ ++dma_completed: ++ ++ prot->d2h_sync_wait_tot += tries; ++ return msg->msg_type; ++} ++ ++/* Sync on a D2H DMA to complete using XORCSUM mode */ ++static uint8 BCMFASTPATH ++dhd_prot_d2h_sync_xorcsum(dhd_pub_t *dhd, msgbuf_ring_t *ring, ++ volatile cmn_msg_hdr_t *msg, int msglen) ++{ ++ uint32 tries; ++ uint32 prot_checksum = 0; /* computed checksum */ ++ int num_words = msglen / sizeof(uint32); /* num of 32bit words */ ++ uint8 ring_seqnum = ring->seqnum % D2H_EPOCH_MODULO; ++ dhd_prot_t *prot = dhd->prot; ++ ++ ASSERT(msglen == RING_LEN_ITEMS(ring)); ++ ++ for (tries = 0; tries < PCIE_D2H_SYNC_WAIT_TRIES; tries++) { ++ prot_checksum = bcm_compute_xor32((volatile uint32 *)msg, num_words); ++ if (prot_checksum == 0U) { /* checksum is OK */ ++ if (msg->epoch == ring_seqnum) { ++ ring->seqnum++; /* next expected sequence number */ ++ goto dma_completed; ++ } ++ } ++ ++ if (tries > prot->d2h_sync_wait_max) ++ prot->d2h_sync_wait_max = tries; ++ ++ OSL_CACHE_INV(msg, msglen); /* invalidate and try again */ ++ ++ } /* for PCIE_D2H_SYNC_WAIT_TRIES */ ++ ++ dhd_prot_d2h_sync_livelock(dhd, ring->seqnum, tries, (uchar *)msg, msglen); ++ ++ ring->seqnum++; /* skip this message ... leak of a pktid */ ++ return 0; /* invalid msgtype 0 -> noop callback */ ++ ++dma_completed: ++ ++ prot->d2h_sync_wait_tot += tries; ++ return msg->msg_type; ++} ++ ++/* Do not sync on a D2H DMA */ ++static uint8 BCMFASTPATH ++dhd_prot_d2h_sync_none(dhd_pub_t *dhd, msgbuf_ring_t *ring, ++ volatile cmn_msg_hdr_t *msg, int msglen) ++{ ++ return msg->msg_type; ++} ++ ++/* Initialize the D2H DMA Sync mode, per D2H ring seqnum and dhd stats */ ++static void ++dhd_prot_d2h_sync_init(dhd_pub_t *dhd, dhd_prot_t * prot) ++{ ++ prot->d2h_sync_wait_max = 0UL; ++ prot->d2h_sync_wait_tot = 0UL; ++ ++ prot->d2hring_tx_cpln->seqnum = D2H_EPOCH_INIT_VAL; ++ prot->d2hring_rx_cpln->seqnum = D2H_EPOCH_INIT_VAL; ++ prot->d2hring_ctrl_cpln->seqnum = D2H_EPOCH_INIT_VAL; ++ ++ if (dhd->d2h_sync_mode & PCIE_SHARED_D2H_SYNC_SEQNUM) ++ prot->d2h_sync_cb = dhd_prot_d2h_sync_seqnum; ++ else if (dhd->d2h_sync_mode & PCIE_SHARED_D2H_SYNC_XORCSUM) ++ prot->d2h_sync_cb = dhd_prot_d2h_sync_xorcsum; ++ else ++ prot->d2h_sync_cb = dhd_prot_d2h_sync_none; ++} ++ ++#endif /* PCIE_D2H_SYNC */ ++ + /* + * +---------------------------------------------------------------------------+ + * PktId Map: Provides a native packet pointer to unique 32bit PktId mapping. +@@ -250,7 +424,7 @@ + * and the metadata may be retrieved using the previously allocated packet id. + * +---------------------------------------------------------------------------+ + */ +-#define MAX_PKTID_ITEMS (3072) /* Maximum number of pktids supported */ ++#define MAX_PKTID_ITEMS (8192) /* Maximum number of pktids supported */ + + typedef void * dhd_pktid_map_handle_t; /* opaque handle to a pktid map */ + +@@ -267,13 +441,13 @@ + static INLINE uint32 dhd_pktid_map_reserve(dhd_pktid_map_handle_t *handle, + void *pkt); + static INLINE void dhd_pktid_map_save(dhd_pktid_map_handle_t *handle, void *pkt, +- uint32 nkey, dmaaddr_t physaddr, uint32 len, uint8 dma); ++ uint32 nkey, dmaaddr_t physaddr, uint32 len, uint8 dma, void *secdma); + static uint32 dhd_pktid_map_alloc(dhd_pktid_map_handle_t *map, void *pkt, +- dmaaddr_t physaddr, uint32 len, uint8 dma); ++ dmaaddr_t physaddr, uint32 len, uint8 dma, void *secdma); + + /* Return an allocated pktid, retrieving previously saved pkt and metadata */ + static void *dhd_pktid_map_free(dhd_pktid_map_handle_t *map, uint32 id, +- dmaaddr_t *physaddr, uint32 *len); ++ dmaaddr_t *physaddr, uint32 *len, void **secdma); + + /* Packet metadata saved in packet id mapper */ + typedef struct dhd_pktid_item { +@@ -282,6 +456,7 @@ + uint16 len; /* length of mapped packet's buffer */ + void *pkt; /* opaque native pointer to a packet */ + dmaaddr_t physaddr; /* physical address of mapped packet's buffer */ ++ void *secdma; + } dhd_pktid_item_t; + + typedef struct dhd_pktid_map { +@@ -312,17 +487,24 @@ + #define NATIVE_TO_PKTID_CLEAR(map) dhd_pktid_map_clear(map) + + #define NATIVE_TO_PKTID_RSV(map, pkt) dhd_pktid_map_reserve((map), (pkt)) +-#define NATIVE_TO_PKTID_SAVE(map, pkt, nkey, pa, len, dma) \ +- dhd_pktid_map_save((map), (void *)(pkt), (nkey), (pa), (uint32)(len), (uint8)dma) +-#define NATIVE_TO_PKTID(map, pkt, pa, len, dma) \ +- dhd_pktid_map_alloc((map), (void *)(pkt), (pa), (uint32)(len), (uint8)dma) ++#define NATIVE_TO_PKTID_SAVE(map, pkt, nkey, pa, len, dma, secdma) \ ++ dhd_pktid_map_save((map), (void *)(pkt), (nkey), (pa), (uint32)(len), (uint8)dma, \ ++ (void *)(secdma)) ++#define NATIVE_TO_PKTID(map, pkt, pa, len, dma, secdma) \ ++ dhd_pktid_map_alloc((map), (void *)(pkt), (pa), (uint32)(len), (uint8)dma, (void *)(secdma)) + +-#define PKTID_TO_NATIVE(map, pktid, pa, len) \ ++#define PKTID_TO_NATIVE(map, pktid, pa, len, secdma) \ + dhd_pktid_map_free((map), (uint32)(pktid), \ +- (dmaaddr_t *)&(pa), (uint32 *)&(len)) ++ (dmaaddr_t *)&(pa), (uint32 *)&(len), (void **) &secdma) + + #define PKTID_AVAIL(map) dhd_pktid_map_avail_cnt(map) + ++#if defined(CONFIG_DHD_USE_STATIC_BUF) && defined(DHD_USE_STATIC_FLOWRING) ++#define FLOWRING_NAME "h2dflr" ++#define RING_IS_FLOWRING(ring) \ ++ ((strncmp(ring->name, FLOWRING_NAME, sizeof(FLOWRING_NAME))) == (0)) ++#endif /* CONFIG_DHD_USE_STATIC_BUF && DHD_USE_STATIC_FLOWRING */ ++ + /* + * +---------------------------------------------------------------------------+ + * Packet to Packet Id mapper using a paradigm. +@@ -495,7 +677,7 @@ + + static INLINE void + dhd_pktid_map_save(dhd_pktid_map_handle_t *handle, void *pkt, uint32 nkey, +- dmaaddr_t physaddr, uint32 len, uint8 dma) ++ dmaaddr_t physaddr, uint32 len, uint8 dma, void *secdma) + { + dhd_pktid_map_t *map; + dhd_pktid_item_t *locker; +@@ -511,15 +693,16 @@ + locker->dma = dma; /* store contents in locker */ + locker->physaddr = physaddr; + locker->len = (uint16)len; /* 16bit len */ ++ locker->secdma = secdma; + } + + static uint32 BCMFASTPATH + dhd_pktid_map_alloc(dhd_pktid_map_handle_t *handle, void *pkt, +- dmaaddr_t physaddr, uint32 len, uint8 dma) ++ dmaaddr_t physaddr, uint32 len, uint8 dma, void *secdma) + { + uint32 nkey = dhd_pktid_map_reserve(handle, pkt); + if (nkey != DHD_PKTID_INVALID) { +- dhd_pktid_map_save(handle, pkt, nkey, physaddr, len, dma); ++ dhd_pktid_map_save(handle, pkt, nkey, physaddr, len, dma, secdma); + } + return nkey; + } +@@ -532,7 +715,7 @@ + */ + static void * BCMFASTPATH + dhd_pktid_map_free(dhd_pktid_map_handle_t *handle, uint32 nkey, +- dmaaddr_t *physaddr, uint32 *len) ++ dmaaddr_t *physaddr, uint32 *len, void **secdma) + { + dhd_pktid_map_t *map; + dhd_pktid_item_t *locker; +@@ -557,6 +740,7 @@ + + *physaddr = locker->physaddr; /* return contents of locker */ + *len = (uint32)locker->len; ++ *secdma = locker->secdma; + + return locker->pkt; + } +@@ -687,6 +871,10 @@ + return BCME_NOMEM; + } + ++#if defined(PCIE_D2H_SYNC) ++ dhd_prot_d2h_sync_init(dhd, prot); ++#endif /* PCIE_D2H_SYNC */ ++ + prot->dmaxfer.srcmem.va = NULL; + prot->dmaxfer.destmem.va = NULL; + prot->dmaxfer_in_progress = FALSE; +@@ -718,7 +906,7 @@ + uint32 dma_block_size = 4 * length; + + if (prot == NULL) { +- DHD_ERROR(("prot is not inited\n")); ++ DHD_ERROR(("%s: prot is not inited\n", __FUNCTION__)); + return BCME_ERROR; + } + +@@ -738,8 +926,8 @@ + ASSERT(ISALIGNED(prot->h2d_dma_writeindx_buf.va, 4)); + bzero(prot->h2d_dma_writeindx_buf.va, dma_block_size); + OSL_CACHE_FLUSH((void *)prot->h2d_dma_writeindx_buf.va, dma_block_size); +- DHD_ERROR(("H2D_WRITEINDX_ARRAY_HOST: %d-bytes " +- "inited for dma'ing h2d-w indices\n", ++ DHD_ERROR(("%s: H2D_WRITEINDX_ARRAY_HOST: %d-bytes " ++ "inited for dma'ing h2d-w indices\n", __FUNCTION__, + prot->h2d_dma_writeindx_buf_len)); + break; + +@@ -757,8 +945,8 @@ + ASSERT(ISALIGNED(prot->h2d_dma_readindx_buf.va, 4)); + bzero(prot->h2d_dma_readindx_buf.va, dma_block_size); + OSL_CACHE_FLUSH((void *)prot->h2d_dma_readindx_buf.va, dma_block_size); +- DHD_ERROR(("H2D_READINDX_ARRAY_HOST %d-bytes " +- "inited for dma'ing h2d-r indices\n", ++ DHD_ERROR(("%s: H2D_READINDX_ARRAY_HOST %d-bytes " ++ "inited for dma'ing h2d-r indices\n", __FUNCTION__, + prot->h2d_dma_readindx_buf_len)); + break; + +@@ -777,8 +965,8 @@ + ASSERT(ISALIGNED(prot->d2h_dma_writeindx_buf.va, 4)); + bzero(prot->d2h_dma_writeindx_buf.va, dma_block_size); + OSL_CACHE_FLUSH((void *)prot->d2h_dma_writeindx_buf.va, dma_block_size); +- DHD_ERROR(("D2H_WRITEINDX_ARRAY_HOST %d-bytes " +- "inited for dma'ing d2h-w indices\n", ++ DHD_ERROR(("%s: D2H_WRITEINDX_ARRAY_HOST %d-bytes " ++ "inited for dma'ing d2h-w indices\n", __FUNCTION__, + prot->d2h_dma_writeindx_buf_len)); + break; + +@@ -797,8 +985,8 @@ + ASSERT(ISALIGNED(prot->d2h_dma_readindx_buf.va, 4)); + bzero(prot->d2h_dma_readindx_buf.va, dma_block_size); + OSL_CACHE_FLUSH((void *)prot->d2h_dma_readindx_buf.va, dma_block_size); +- DHD_ERROR(("D2H_READINDX_ARRAY_HOST %d-bytes " +- "inited for dma'ing d2h-r indices\n", ++ DHD_ERROR(("%s: D2H_READINDX_ARRAY_HOST %d-bytes " ++ "inited for dma'ing d2h-r indices\n", __FUNCTION__, + prot->d2h_dma_readindx_buf_len)); + break; + +@@ -916,6 +1104,10 @@ + + /* Post event buffer after shim layer is attached */ + ret = dhd_msgbuf_rxbuf_post_event_bufs(dhd); ++ if (ret <= 0) { ++ DHD_ERROR(("%s : Post event buffer fail. ret = %d\n", __FUNCTION__, ret)); ++ return ret; ++ } + + + /* Get the device rev info */ +@@ -1096,10 +1288,16 @@ + void *PKTBUF; + dmaaddr_t pa; + uint32 pa_len; +- PKTBUF = PKTID_TO_NATIVE(dhd->prot->pktid_map_handle, pktid, pa, pa_len); ++ void *secdma; ++ PKTBUF = PKTID_TO_NATIVE(dhd->prot->pktid_map_handle, pktid, pa, pa_len, secdma); + + if (PKTBUF) { +- DMA_UNMAP(dhd->osh, pa, (uint) pa_len, DMA_TX, 0, 0); ++ { ++ if (SECURE_DMA_ENAB(dhd->osh)) { ++ SECURE_DMA_UNMAP(dhd->osh, pa, (uint) pa_len, DMA_TX, 0, 0, secdma, 0); ++ } else ++ DMA_UNMAP(dhd->osh, pa, (uint) pa_len, DMA_TX, 0, 0); ++ } + PKTFREE(dhd->osh, PKTBUF, FALSE); + } + return; +@@ -1111,8 +1309,12 @@ + void *PKTBUF; + dmaaddr_t pa; + uint32 pa_len; +- PKTBUF = PKTID_TO_NATIVE(dhd->prot->pktid_map_handle, pktid, pa, pa_len); ++ void *secdma; ++ PKTBUF = PKTID_TO_NATIVE(dhd->prot->pktid_map_handle, pktid, pa, pa_len, secdma); + if (PKTBUF) { ++ if (SECURE_DMA_ENAB(dhd->osh)) ++ SECURE_DMA_UNMAP(dhd->osh, pa, (uint) pa_len, DMA_RX, 0, 0, secdma, 0); ++ else + DMA_UNMAP(dhd->osh, pa, (uint) pa_len, DMA_RX, 0, 0); + } + +@@ -1132,8 +1334,8 @@ + cnt--; + if (cnt == 0) { + /* find a better way to reschedule rx buf post if space not available */ +- DHD_ERROR(("h2d rx post ring not available to post host buffers \n")); +- DHD_ERROR(("Current posted host buf count %d \n", prot->rxbufpost)); ++ DHD_ERROR(("%s: h2d rx post ring not available to post host buffers\n", __FUNCTION__)); ++ DHD_ERROR(("%s: Current posted host buf count %d \n", __FUNCTION__, prot->rxbufpost)); + break; + } + +@@ -1194,20 +1396,29 @@ + /* Create a rx buffer */ + if ((p = PKTGET(dhd->osh, pktsz, FALSE)) == NULL) { + DHD_ERROR(("%s:%d: PKTGET for rxbuf failed\n", __FUNCTION__, __LINE__)); +- return -1; ++ break; + } + + pktlen = PKTLEN(dhd->osh, p); ++ if (SECURE_DMA_ENAB(dhd->osh)) { ++ DHD_GENERAL_LOCK(dhd, flags); ++ physaddr = SECURE_DMA_MAP(dhd->osh, PKTDATA(dhd->osh, p), pktlen, DMA_RX, p, 0, ++ ring->secdma, 0); ++ DHD_GENERAL_UNLOCK(dhd, flags); ++ } else + physaddr = DMA_MAP(dhd->osh, PKTDATA(dhd->osh, p), pktlen, DMA_RX, p, 0); ++ + if (PHYSADDRISZERO(physaddr)) { +- if (RING_WRITE_PTR(ring) < alloced - i) +- RING_WRITE_PTR(ring) = RING_MAX_ITEM(ring) - alloced + i; +- else +- RING_WRITE_PTR(ring) -= alloced - i; +- alloced = i; ++ if (SECURE_DMA_ENAB(dhd->osh)) { ++ DHD_GENERAL_LOCK(dhd, flags); ++ SECURE_DMA_UNMAP(dhd->osh, physaddr, pktlen, DMA_RX, 0, 0, ++ ring->secdma, 0); ++ DHD_GENERAL_UNLOCK(dhd, flags); ++ } else + DMA_UNMAP(dhd->osh, physaddr, pktlen, DMA_RX, 0, 0); ++ + PKTFREE(dhd->osh, p, FALSE); +- DHD_ERROR(("Invalid phyaddr 0\n")); ++ DHD_ERROR(("%s: Invalid phyaddr 0\n", __FUNCTION__)); + ASSERT(0); + break; + } +@@ -1224,20 +1435,22 @@ + + rxbuf_post->cmn_hdr.request_id = + htol32(NATIVE_TO_PKTID(dhd->prot->pktid_map_handle, p, physaddr, +- pktlen, DMA_RX)); ++ pktlen, DMA_RX, ring->secdma)); + + /* free lock */ + DHD_GENERAL_UNLOCK(dhd, flags); + + if (rxbuf_post->cmn_hdr.request_id == DHD_PKTID_INVALID) { +- if (RING_WRITE_PTR(ring) < alloced - i) +- RING_WRITE_PTR(ring) = RING_MAX_ITEM(ring) - alloced + i; +- else +- RING_WRITE_PTR(ring) -= alloced - i; +- alloced = i; ++ if (SECURE_DMA_ENAB(dhd->osh)) { ++ DHD_GENERAL_LOCK(dhd, flags); ++ SECURE_DMA_UNMAP(dhd->osh, physaddr, pktlen, DMA_RX, 0, 0, ++ ring->secdma, 0); ++ DHD_GENERAL_UNLOCK(dhd, flags); ++ } else + DMA_UNMAP(dhd->osh, physaddr, pktlen, DMA_RX, 0, 0); ++ + PKTFREE(dhd->osh, p, FALSE); +- DHD_ERROR(("Pktid pool depleted.\n")); ++ DHD_ERROR(("%s: Pktid pool depleted.\n", __FUNCTION__)); + break; + } + +@@ -1259,6 +1472,16 @@ + /* Move rxbuf_post_tmp to next item */ + rxbuf_post_tmp = rxbuf_post_tmp + RING_LEN_ITEMS(ring); + } ++ ++ if (i < alloced) { ++ if (RING_WRITE_PTR(ring) < (alloced - i)) ++ RING_WRITE_PTR(ring) = RING_MAX_ITEM(ring) - (alloced - i); ++ else ++ RING_WRITE_PTR(ring) -= (alloced - i); ++ ++ alloced = i; ++ } ++ + /* Update the write pointer in TCM & ring bell */ + if (alloced > 0) + prot_ring_write_complete(dhd, prot->h2dring_rxp_subn, msg_start, alloced); +@@ -1278,6 +1501,11 @@ + uint16 alloced = 0; + unsigned long flags; + ++ if (dhd->busstate == DHD_BUS_DOWN) { ++ DHD_ERROR(("%s: bus is already down.\n", __FUNCTION__)); ++ return -1; ++ } ++ + if (event_buf) { + /* Allocate packet for event buffer post */ + pktsz = DHD_FLOWRING_RX_BUFPOST_PKTSZ; +@@ -1287,15 +1515,24 @@ + } + + if ((p = PKTGET(dhd->osh, pktsz, FALSE)) == NULL) { +- DHD_ERROR(("%s:%d: PKTGET for ctrl rxbuf failed\n", __FUNCTION__, __LINE__)); ++ DHD_ERROR(("%s:%d: PKTGET for %s rxbuf failed\n", ++ __FUNCTION__, __LINE__, event_buf ? ++ "event" : "ioctl")); + return -1; + } + + pktlen = PKTLEN(dhd->osh, p); ++ if (SECURE_DMA_ENAB(dhd->osh)) { ++ DHD_GENERAL_LOCK(dhd, flags); ++ physaddr = SECURE_DMA_MAP(dhd->osh, PKTDATA(dhd->osh, p), pktlen, ++ DMA_RX, p, 0, prot->h2dring_ctrl_subn->secdma, 0); ++ DHD_GENERAL_UNLOCK(dhd, flags); ++ } else + physaddr = DMA_MAP(dhd->osh, PKTDATA(dhd->osh, p), pktlen, DMA_RX, p, 0); ++ + if (PHYSADDRISZERO(physaddr)) { + +- DHD_ERROR(("Invalid phyaddr 0\n")); ++ DHD_ERROR(("%s: Invalid phyaddr 0\n", __FUNCTION__)); + ASSERT(0); + goto free_pkt_return; + } +@@ -1305,9 +1542,17 @@ + prot->h2dring_ctrl_subn, DHD_FLOWRING_DEFAULT_NITEMS_POSTED_H2D, &alloced); + if (rxbuf_post == NULL) { + DHD_GENERAL_UNLOCK(dhd, flags); +- DHD_ERROR(("%s:%d: Ctrl submit Msgbuf Not available to post buffer \n", +- __FUNCTION__, __LINE__)); ++ DHD_ERROR(("%s:%d: Ctrl submit Msgbuf Not available to post buffer" ++ " for %s\n", __FUNCTION__, __LINE__, event_buf ? "event" : ++ "ioctl")); ++ if (SECURE_DMA_ENAB(dhd->osh)) { ++ DHD_GENERAL_LOCK(dhd, flags); ++ SECURE_DMA_UNMAP(dhd->osh, physaddr, pktlen, DMA_RX, 0, 0, ++ prot->h2dring_ctrl_subn->secdma, 0); ++ DHD_GENERAL_UNLOCK(dhd, flags); ++ } else + DMA_UNMAP(dhd->osh, physaddr, pktlen, DMA_RX, 0, 0); ++ + goto free_pkt_return; + } + +@@ -1319,7 +1564,8 @@ + rxbuf_post->cmn_hdr.if_id = 0; + + rxbuf_post->cmn_hdr.request_id = +- htol32(NATIVE_TO_PKTID(dhd->prot->pktid_map_handle, p, physaddr, pktlen, DMA_RX)); ++ htol32(NATIVE_TO_PKTID(dhd->prot->pktid_map_handle, p, physaddr, pktlen, DMA_RX, ++ prot->h2dring_ctrl_subn->secdma)); + + if (rxbuf_post->cmn_hdr.request_id == DHD_PKTID_INVALID) { + if (RING_WRITE_PTR(prot->h2dring_ctrl_subn) == 0) +@@ -1328,7 +1574,14 @@ + else + RING_WRITE_PTR(prot->h2dring_ctrl_subn)--; + DHD_GENERAL_UNLOCK(dhd, flags); ++ if (SECURE_DMA_ENAB(dhd->osh)) { ++ DHD_GENERAL_LOCK(dhd, flags); ++ SECURE_DMA_UNMAP(dhd->osh, physaddr, pktlen, DMA_RX, 0, 0, ++ prot->h2dring_ctrl_subn->secdma, 0); ++ DHD_GENERAL_UNLOCK(dhd, flags); ++ } else + DMA_UNMAP(dhd->osh, physaddr, pktlen, DMA_RX, 0, 0); ++ + goto free_pkt_return; + } + +@@ -1356,14 +1609,20 @@ + uint32 i = 0; + int32 ret_val; + +- DHD_INFO(("max to post %d, event %d \n", max_to_post, event_buf)); ++ DHD_INFO(("%s: max to post %d, event %d\n", __FUNCTION__, max_to_post, event_buf)); ++ ++ if (dhd->busstate == DHD_BUS_DOWN) { ++ DHD_ERROR(("%s: bus is already down.\n", __FUNCTION__)); ++ return 0; ++ } ++ + while (i < max_to_post) { + ret_val = dhd_prot_rxbufpost_ctrl(dhd, event_buf); + if (ret_val < 0) + break; + i++; + } +- DHD_INFO(("posted %d buffers to event_pool/ioctl_resp_pool %d\n", i, event_buf)); ++ DHD_INFO(("%s: posted %d buffers to event_pool/ioctl_resp_pool %d\n", __FUNCTION__, i, event_buf)); + return (uint16)i; + } + +@@ -1373,26 +1632,43 @@ + dhd_prot_t *prot = dhd->prot; + uint16 retcnt = 0; + +- DHD_INFO(("ioctl resp buf post\n")); ++ DHD_INFO(("%s: ioctl resp buf post\n", __FUNCTION__)); ++ ++ if (dhd->busstate == DHD_BUS_DOWN) { ++ DHD_ERROR(("%s: bus is already down.\n", __FUNCTION__)); ++ return 0; ++ } ++ + retcnt = dhd_msgbuf_rxbuf_post_ctrlpath(dhd, FALSE, + prot->max_ioctlrespbufpost - prot->cur_ioctlresp_bufs_posted); + prot->cur_ioctlresp_bufs_posted += retcnt; +- return 0; ++ return retcnt; + } + + static int + dhd_msgbuf_rxbuf_post_event_bufs(dhd_pub_t *dhd) + { + dhd_prot_t *prot = dhd->prot; +- prot->cur_event_bufs_posted += dhd_msgbuf_rxbuf_post_ctrlpath(dhd, TRUE, ++ uint16 retcnt = 0; ++ ++ if (dhd->busstate == DHD_BUS_DOWN) { ++ DHD_ERROR(("%s: bus is already down.\n", __FUNCTION__)); ++ return 0; ++ } ++ ++ retcnt = dhd_msgbuf_rxbuf_post_ctrlpath(dhd, TRUE, + prot->max_eventbufpost - prot->cur_event_bufs_posted); +- return 0; ++ ++ prot->cur_event_bufs_posted += retcnt; ++ return retcnt; + } + +-int BCMFASTPATH +-dhd_prot_process_msgbuf_rxcpl(dhd_pub_t *dhd) ++bool BCMFASTPATH ++dhd_prot_process_msgbuf_rxcpl(dhd_pub_t *dhd, uint bound) + { + dhd_prot_t *prot = dhd->prot; ++ bool more = TRUE; ++ uint n = 0; + + /* Process all the messages - DTOH direction */ + while (TRUE) { +@@ -1405,8 +1681,10 @@ + + /* Get the message from ring */ + src_addr = prot_get_src_addr(dhd, prot->d2hring_rx_cpln, &src_len); +- if (src_addr == NULL) ++ if (src_addr == NULL) { ++ more = FALSE; + break; ++ } + + /* Prefetch data to populate the cache */ + OSL_PREFETCH(src_addr); +@@ -1417,9 +1695,15 @@ + DHD_ERROR(("%s: Error at process rxpl msgbuf of len %d\n", + __FUNCTION__, src_len)); + } ++ ++ /* After batch processing, check RX bound */ ++ n += src_len/RING_LEN_ITEMS(prot->d2hring_rx_cpln); ++ if (n >= bound) { ++ break; ++ } + } + +- return 0; ++ return more; + } + + void +@@ -1434,7 +1718,7 @@ + ring->ringstate->r_offset = r_index; + } + +- DHD_TRACE(("flow %d, write %d read %d \n\n", flow_id, RING_WRITE_PTR(ring), ++ DHD_TRACE(("%s: flow %d, write %d read %d \n\n", __FUNCTION__, flow_id, RING_WRITE_PTR(ring), + RING_READ_PTR(ring))); + + /* Need more logic here, but for now use it directly */ +@@ -1442,10 +1726,12 @@ + } + + +-int BCMFASTPATH +-dhd_prot_process_msgbuf_txcpl(dhd_pub_t *dhd) ++bool BCMFASTPATH ++dhd_prot_process_msgbuf_txcpl(dhd_pub_t *dhd, uint bound) + { + dhd_prot_t *prot = dhd->prot; ++ bool more = TRUE; ++ uint n = 0; + + /* Process all the messages - DTOH direction */ + while (TRUE) { +@@ -1453,8 +1739,10 @@ + uint16 src_len; + + src_addr = prot_get_src_addr(dhd, prot->d2hring_tx_cpln, &src_len); +- if (src_addr == NULL) ++ if (src_addr == NULL) { ++ more = FALSE; + break; ++ } + + /* Prefetch data to populate the cache */ + OSL_PREFETCH(src_addr); +@@ -1467,9 +1755,15 @@ + + /* Write to dngl rd ptr */ + prot_upd_read_idx(dhd, prot->d2hring_tx_cpln); ++ ++ /* After batch processing, check bound */ ++ n += src_len/RING_LEN_ITEMS(prot->d2hring_tx_cpln); ++ if (n >= bound) { ++ break; ++ } + } + +- return 0; ++ return more; + } + + int BCMFASTPATH +@@ -1534,26 +1828,6 @@ + return ret; + } + +-#define PCIE_M2M_D2H_DMA_WAIT_TRIES 256 +-#define PCIE_D2H_RESET_MARK 0xdeadbeef +-void dhd_msgbuf_d2h_check_cmplt(msgbuf_ring_t *ring, void *msg) +-{ +- uint32 tries; +- uint32 *marker = (uint32 *)msg + RING_LEN_ITEMS(ring) / sizeof(uint32) - 1; +- +- for (tries = 0; tries < PCIE_M2M_D2H_DMA_WAIT_TRIES; tries++) { +- if (*(volatile uint32 *)marker != PCIE_D2H_RESET_MARK) +- return; +- OSL_CACHE_INV(msg, RING_LEN_ITEMS(ring)); +- } +- +- /* only print error for data ring */ +- if (ring->idx == BCMPCIE_D2H_MSGRING_TX_COMPLETE || +- ring->idx == BCMPCIE_D2H_MSGRING_RX_COMPLETE) +- DHD_ERROR(("%s: stale msgbuf content after %d retries\n", +- __FUNCTION__, tries)); +-} +- + static int BCMFASTPATH + dhd_process_msgtype(dhd_pub_t *dhd, msgbuf_ring_t *ring, uint8* buf, uint16 len) + { +@@ -1562,7 +1836,10 @@ + uint8 msgtype; + cmn_msg_hdr_t *msg = NULL; + int ret = BCME_OK; ++ ++#if defined(PCIE_D2H_SYNC_BZERO) + uint8 *buf_head = buf; ++#endif /* PCIE_D2H_SYNC_BZERO */ + + ASSERT(ring && ring->ringmem); + msglen = RING_LEN_ITEMS(ring); +@@ -1575,21 +1852,28 @@ + while (pktlen > 0) { + msg = (cmn_msg_hdr_t *)buf; + +- dhd_msgbuf_d2h_check_cmplt(ring, msg); +- ++#if defined(PCIE_D2H_SYNC) ++ /* Wait until DMA completes, then fetch msgtype */ ++ msgtype = dhd->prot->d2h_sync_cb(dhd, ring, msg, msglen); ++#else + msgtype = msg->msg_type; ++#endif /* !PCIE_D2H_SYNC */ + +- /* Prefetch data to populate the cache */ +- OSL_PREFETCH(buf + msglen); +- +- DHD_INFO(("msgtype %d, msglen is %d, pktlen is %d \n", ++ DHD_INFO(("%s: msgtype %d, msglen is %d, pktlen is %d\n", __FUNCTION__, + msgtype, msglen, pktlen)); + if (msgtype == MSG_TYPE_LOOPBACK) { + bcm_print_bytes("LPBK RESP: ", (uint8 *)msg, msglen); +- DHD_ERROR((" MSG_TYPE_LOOPBACK, len %d\n", msglen)); ++ DHD_ERROR(("%s: MSG_TYPE_LOOPBACK, len %d\n", __FUNCTION__, msglen)); ++ } ++ ++ ++ if (msgtype >= DHD_PROT_FUNCS) { ++ DHD_ERROR(("%s: msgtype %d, msglen is %d, pktlen is %d \n", ++ __FUNCTION__, msgtype, msglen, pktlen)); ++ ret = BCME_ERROR; ++ goto done; + } + +- ASSERT(msgtype < DHD_PROT_FUNCS); + if (table_lookup[msgtype]) { + table_lookup[msgtype](dhd, buf, msglen); + } +@@ -1601,12 +1885,14 @@ + pktlen = pktlen - msglen; + buf = buf + msglen; + +- if (msgtype == MSG_TYPE_RX_CMPLT) +- prot_early_upd_rxcpln_read_idx(dhd, +- dhd->prot->d2hring_rx_cpln); ++ if (ring->idx == BCMPCIE_D2H_MSGRING_RX_COMPLETE) ++ prot_early_upd_rxcpln_read_idx(dhd, ring); + } + done: +- OSL_CACHE_FLUSH(buf_head, len - pktlen); ++ ++#if defined(PCIE_D2H_SYNC_BZERO) ++ OSL_CACHE_FLUSH(buf_head, len - pktlen); /* Flush the bzeroed msg */ ++#endif /* PCIE_D2H_SYNC_BZERO */ + + #ifdef DHD_RX_CHAINING + dhd_rxchain_commit(dhd); +@@ -1616,10 +1902,17 @@ + } + + static void ++dhd_prot_noop(dhd_pub_t *dhd, void * buf, uint16 msglen) ++{ ++ return; ++} ++ ++static void + dhd_prot_ringstatus_process(dhd_pub_t *dhd, void * buf, uint16 msglen) + { + pcie_ring_status_t * ring_status = (pcie_ring_status_t *)buf; +- DHD_ERROR(("ring status: request_id %d, status 0x%04x, flow ring %d, w_offset %d \n", ++ DHD_ERROR(("%s: ring status: request_id %d, status 0x%04x, flow ring %d, w_offset %d \n", ++ __FUNCTION__, + ring_status->cmn_hdr.request_id, ring_status->compl_hdr.status, + ring_status->compl_hdr.flow_ring_id, ring_status->write_idx)); + /* How do we track this to pair it with ??? */ +@@ -1630,7 +1923,8 @@ + dhd_prot_genstatus_process(dhd_pub_t *dhd, void * buf, uint16 msglen) + { + pcie_gen_status_t * gen_status = (pcie_gen_status_t *)buf; +- DHD_ERROR(("gen status: request_id %d, status 0x%04x, flow ring %d \n", ++ DHD_ERROR(("%s: gen status: request_id %d, status 0x%04x, flow ring %d \n", ++ __FUNCTION__, + gen_status->cmn_hdr.request_id, gen_status->compl_hdr.status, + gen_status->compl_hdr.flow_ring_id)); + +@@ -1643,16 +1937,20 @@ + { + ioctl_req_ack_msg_t * ioct_ack = (ioctl_req_ack_msg_t *)buf; + +- DHD_CTL(("ioctl req ack: request_id %d, status 0x%04x, flow ring %d \n", ++ DHD_CTL(("%s: ioctl req ack: request_id %d, status 0x%04x, flow ring %d \n", ++ __FUNCTION__, + ioct_ack->cmn_hdr.request_id, ioct_ack->compl_hdr.status, + ioct_ack->compl_hdr.flow_ring_id)); + if (ioct_ack->compl_hdr.status != 0) { +- DHD_ERROR(("got an error status for the ioctl request...need to handle that\n")); ++ DHD_ERROR(("%s: got an error status for the ioctl request...need to handle that\n", ++ __FUNCTION__)); + } + ++#if defined(PCIE_D2H_SYNC_BZERO) + memset(buf, 0, msglen); +- ioct_ack->marker = PCIE_D2H_RESET_MARK; ++#endif /* PCIE_D2H_SYNC_BZERO */ + } ++ + static void + dhd_prot_ioctcmplt_process(dhd_pub_t *dhd, void * buf, uint16 msglen) + { +@@ -1666,10 +1964,11 @@ + pkt_id = ltoh32(ioct_resp->cmn_hdr.request_id); + status = ioct_resp->compl_hdr.status; + ++#if defined(PCIE_D2H_SYNC_BZERO) + memset(buf, 0, msglen); +- ioct_resp->marker = PCIE_D2H_RESET_MARK; ++#endif /* PCIE_D2H_SYNC_BZERO */ + +- DHD_CTL(("IOCTL_COMPLETE: pktid %x xtid %d status %x resplen %d\n", ++ DHD_CTL(("%s: IOCTL_COMPLETE: pktid %x xtid %d status %x resplen %d\n", __FUNCTION__, + pkt_id, xt_id, status, resp_len)); + + dhd_bus_update_retlen(dhd->bus, sizeof(ioctl_comp_resp_msg_t), pkt_id, status, resp_len); +@@ -1684,25 +1983,39 @@ + unsigned long flags; + uint32 pktid; + void *pkt; +- ++ ulong pa; ++ uint32 pa_len; ++ void *secdma; + /* locks required to protect circular buffer accesses */ + DHD_GENERAL_LOCK(dhd, flags); + + txstatus = (host_txbuf_cmpl_t *)buf; + pktid = ltoh32(txstatus->cmn_hdr.request_id); + +- DHD_INFO(("txstatus for pktid 0x%04x\n", pktid)); ++ DHD_INFO(("%s: txstatus for pktid 0x%04x\n", __FUNCTION__, pktid)); + if (prot->active_tx_count) + prot->active_tx_count--; + else +- DHD_ERROR(("Extra packets are freed\n")); ++ DHD_ERROR(("%s: Extra packets are freed\n", __FUNCTION__)); + + ASSERT(pktid != 0); +- pkt = dhd_prot_packet_get(dhd, pktid); ++ pkt = PKTID_TO_NATIVE(dhd->prot->pktid_map_handle, pktid, pa, pa_len, secdma); + if (pkt) { ++ if (SECURE_DMA_ENAB(dhd->osh)) { ++ int offset = 0; ++ BCM_REFERENCE(offset); ++ ++ if (dhd->prot->tx_metadata_offset) ++ offset = dhd->prot->tx_metadata_offset + ETHER_HDR_LEN; ++ SECURE_DMA_UNMAP(dhd->osh, (uint) pa, ++ (uint) dhd->prot->tx_metadata_offset, DMA_RX, 0, 0, ++ secdma, offset); ++ } else ++ DMA_UNMAP(dhd->osh, pa, (uint) pa_len, DMA_RX, 0, dmah); ++ + #if defined(BCMPCIE) + dhd_txcomplete(dhd, pkt, true); +-#endif ++#endif + + #if DHD_DBG_SHOW_METADATA + if (dhd->prot->tx_metadata_offset && txstatus->metadata_len) { +@@ -1719,8 +2032,9 @@ + PKTFREE(dhd->osh, pkt, TRUE); + } + ++#if defined(PCIE_D2H_SYNC_BZERO) + memset(buf, 0, msglen); +- txstatus->marker = PCIE_D2H_RESET_MARK; ++#endif /* PCIE_D2H_SYNC_BZERO */ + + DHD_GENERAL_UNLOCK(dhd, flags); + +@@ -1737,6 +2051,8 @@ + void* pkt; + unsigned long flags; + dhd_prot_t *prot = dhd->prot; ++ int post_cnt = 0; ++ bool zero_posted = FALSE; + + /* Event complete header */ + evnt = (wlevent_req_msg_t *)buf; +@@ -1748,10 +2064,18 @@ + /* Post another rxbuf to the device */ + if (prot->cur_event_bufs_posted) + prot->cur_event_bufs_posted--; +- dhd_msgbuf_rxbuf_post_event_bufs(dhd); ++ else ++ zero_posted = TRUE; + ++ ++ post_cnt = dhd_msgbuf_rxbuf_post_event_bufs(dhd); ++ if (zero_posted && (post_cnt <= 0)) { ++ return; ++ } ++ ++#if defined(PCIE_D2H_SYNC_BZERO) + memset(buf, 0, len); +- evnt->marker = PCIE_D2H_RESET_MARK; ++#endif /* PCIE_D2H_SYNC_BZERO */ + + /* locks required to protect pktid_map */ + DHD_GENERAL_LOCK(dhd, flags); +@@ -1797,7 +2121,8 @@ + return; + } + +- DHD_INFO(("id 0x%04x, offset %d, len %d, idx %d, phase 0x%02x, pktdata %p, metalen %d\n", ++ DHD_INFO(("%s: id 0x%04x, offset %d, len %d, idx %d, phase 0x%02x, pktdata %p, metalen %d\n", ++ __FUNCTION__, + ltoh32(rxcmplt_h->cmn_hdr.request_id), data_offset, ltoh16(rxcmplt_h->data_len), + rxcmplt_h->cmn_hdr.if_id, rxcmplt_h->cmn_hdr.flags, PKTDATA(dhd->osh, pkt), + ltoh16(rxcmplt_h->metadata_len))); +@@ -1816,7 +2141,7 @@ + current_phase = rxcmplt_h->cmn_hdr.flags; + } + if (rxcmplt_h->flags & BCMPCIE_PKT_FLAGS_FRAME_802_11) +- DHD_INFO(("D11 frame rxed \n")); ++ DHD_INFO(("%s: D11 frame rxed\n", __FUNCTION__)); + /* data_offset from buf start */ + if (data_offset) { + /* data offset given from dongle after split rx */ +@@ -1830,8 +2155,10 @@ + PKTSETLEN(dhd->osh, pkt, ltoh16(rxcmplt_h->data_len)); + + ifidx = rxcmplt_h->cmn_hdr.if_id; ++ ++#if defined(PCIE_D2H_SYNC_BZERO) + memset(buf, 0, msglen); +- rxcmplt_h->marker = PCIE_D2H_RESET_MARK; ++#endif /* PCIE_D2H_SYNC_BZERO */ + + #ifdef DHD_RX_CHAINING + /* Chain the packets */ +@@ -1875,7 +2202,7 @@ + host_txbuf_post_t *txdesc = NULL; + dmaaddr_t physaddr, meta_physaddr; + uint8 *pktdata; +- uint16 pktlen; ++ uint32 pktlen; + uint32 pktid; + uint8 prio; + uint16 flowid = 0; +@@ -1883,6 +2210,10 @@ + uint16 headroom; + + msgbuf_ring_t *msg_ring; ++ uint8 dhcp_pkt; ++ ++ if (!dhd->flow_ring_table) ++ return BCME_NORESOURCE; + + if (!dhd_bus_is_txmode_push(dhd->bus)) { + flow_ring_table_t *flow_ring_table; +@@ -1905,7 +2236,7 @@ + /* Create a unique 32-bit packet id */ + pktid = NATIVE_TO_PKTID_RSV(dhd->prot->pktid_map_handle, PKTBUF); + if (pktid == DHD_PKTID_INVALID) { +- DHD_ERROR(("Pktid pool depleted.\n")); ++ DHD_ERROR(("%s: Pktid pool depleted.\n", __FUNCTION__)); + /* + * If we return error here, the caller would queue the packet + * again. So we'll just free the skb allocated in DMA Zone. +@@ -1919,17 +2250,24 @@ + txdesc = (host_txbuf_post_t *)dhd_alloc_ring_space(dhd, + msg_ring, DHD_FLOWRING_DEFAULT_NITEMS_POSTED_H2D, &alloced); + if (txdesc == NULL) { ++ void *secdma; + DHD_INFO(("%s:%d: HTOD Msgbuf Not available TxCount = %d\n", + __FUNCTION__, __LINE__, prot->active_tx_count)); + /* Free up the PKTID */ + PKTID_TO_NATIVE(dhd->prot->pktid_map_handle, pktid, physaddr, +- pktlen); ++ pktlen, secdma); + goto err_no_res_pktfree; + } + ++ /* test if dhcp pkt */ ++ dhcp_pkt = pkt_is_dhcp(dhd->osh, PKTBUF); ++ txdesc->flag2 = (txdesc->flag2 & ~(BCMPCIE_PKT_FLAGS2_FORCELOWRATE_MASK << ++ BCMPCIE_PKT_FLAGS2_FORCELOWRATE_SHIFT)) | ((dhcp_pkt & ++ BCMPCIE_PKT_FLAGS2_FORCELOWRATE_MASK) << BCMPCIE_PKT_FLAGS2_FORCELOWRATE_SHIFT); ++ + /* Extract the data pointer and length information */ + pktdata = PKTDATA(dhd->osh, PKTBUF); +- pktlen = (uint16)PKTLEN(dhd->osh, PKTBUF); ++ pktlen = PKTLEN(dhd->osh, PKTBUF); + + /* Ethernet header: Copy before we cache flush packet using DMA_MAP */ + bcopy(pktdata, txdesc->txhdr, ETHER_HDR_LEN); +@@ -1939,15 +2277,27 @@ + pktlen -= ETHER_HDR_LEN; + + /* Map the data pointer to a DMA-able address */ ++ if (SECURE_DMA_ENAB(dhd->osh)) { ++ ++ int offset = 0; ++ BCM_REFERENCE(offset); ++ ++ if (prot->tx_metadata_offset) ++ offset = prot->tx_metadata_offset + ETHER_HDR_LEN; ++ ++ physaddr = SECURE_DMA_MAP(dhd->osh, PKTDATA(dhd->osh, PKTBUF), pktlen, ++ DMA_TX, PKTBUF, 0, msg_ring->secdma, offset); ++ } else + physaddr = DMA_MAP(dhd->osh, PKTDATA(dhd->osh, PKTBUF), pktlen, DMA_TX, PKTBUF, 0); ++ + if ((PHYSADDRHI(physaddr) == 0) && (PHYSADDRLO(physaddr) == 0)) { +- DHD_ERROR(("Something really bad, unless 0 is a valid phyaddr\n")); ++ DHD_ERROR(("%s: Something really bad, unless 0 is a valid phyaddr\n", __FUNCTION__)); + ASSERT(0); + } + + /* No need to lock. Save the rest of the packet's metadata */ + NATIVE_TO_PKTID_SAVE(dhd->prot->pktid_map_handle, PKTBUF, pktid, +- physaddr, pktlen, DMA_TX); ++ physaddr, pktlen, DMA_TX, msg_ring->secdma); + + #ifdef TXP_FLUSH_NITEMS + if (msg_ring->pend_items_count == 0) +@@ -1968,7 +2318,7 @@ + txdesc->flags |= (prio & 0x7) << BCMPCIE_PKT_FLAGS_PRIO_SHIFT; + txdesc->seg_cnt = 1; + +- txdesc->data_len = htol16(pktlen); ++ txdesc->data_len = htol16((uint16)pktlen); + txdesc->data_buf_addr.high_addr = htol32(PHYSADDRHI(physaddr)); + txdesc->data_buf_addr.low_addr = htol32(PHYSADDRLO(physaddr)); + +@@ -1978,18 +2328,24 @@ + /* Handle Tx metadata */ + headroom = (uint16)PKTHEADROOM(dhd->osh, PKTBUF); + if (prot->tx_metadata_offset && (headroom < prot->tx_metadata_offset)) +- DHD_ERROR(("No headroom for Metadata tx %d %d\n", ++ DHD_ERROR(("%s: No headroom for Metadata tx %d %d\n", __FUNCTION__, + prot->tx_metadata_offset, headroom)); + + if (prot->tx_metadata_offset && (headroom >= prot->tx_metadata_offset)) { +- DHD_TRACE(("Metadata in tx %d\n", prot->tx_metadata_offset)); ++ DHD_TRACE(("%s: Metadata in tx %d\n", __FUNCTION__, prot->tx_metadata_offset)); + + /* Adjust the data pointer to account for meta data in DMA_MAP */ + PKTPUSH(dhd->osh, PKTBUF, prot->tx_metadata_offset); ++ if (SECURE_DMA_ENAB(dhd->osh)) { ++ meta_physaddr = SECURE_DMA_MAP_TXMETA(dhd->osh, PKTDATA(dhd->osh, PKTBUF), ++ prot->tx_metadata_offset + ETHER_HDR_LEN, DMA_RX, PKTBUF, ++ 0, msg_ring->secdma); ++ } else + meta_physaddr = DMA_MAP(dhd->osh, PKTDATA(dhd->osh, PKTBUF), + prot->tx_metadata_offset, DMA_RX, PKTBUF, 0); ++ + if (PHYSADDRISZERO(meta_physaddr)) { +- DHD_ERROR(("Something really bad, unless 0 is a valid phyaddr\n")); ++ DHD_ERROR(("%s: Something really bad, unless 0 is a valid phyaddr\n", __FUNCTION__)); + ASSERT(0); + } + +@@ -2007,7 +2363,7 @@ + } + + +- DHD_TRACE(("txpost: data_len %d, pktid 0x%04x\n", txdesc->data_len, ++ DHD_TRACE(("%s: txpost: data_len %d, pktid 0x%04x\n", __FUNCTION__, txdesc->data_len, + txdesc->cmn_hdr.request_id)); + + /* Update the write pointer in TCM & ring bell */ +@@ -2047,6 +2403,8 @@ + flow_ring_node_t *flow_ring_node; + msgbuf_ring_t *msg_ring; + ++ if (!dhd->flow_ring_table) ++ return; + + if (!in_lock) { + DHD_GENERAL_LOCK(dhd, flags); +@@ -2122,7 +2480,8 @@ + goto done; + + if (prot->pending == TRUE) { +- DHD_ERROR(("packet is pending!!!! cmd=0x%x (%lu) lastcmd=0x%x (%lu)\n", ++ DHD_ERROR(("%s: packet is pending!!!! cmd=0x%x (%lu) lastcmd=0x%x (%lu)\n", ++ __FUNCTION__, + ioc->cmd, (unsigned long)ioc->cmd, prot->lastcmd, + (unsigned long)prot->lastcmd)); + if ((ioc->cmd == WLC_SET_VAR) || (ioc->cmd == WLC_GET_VAR)) { +@@ -2298,7 +2657,7 @@ + prot->dmaxfer.destmem.va, prot->dmaxfer.len); + } + else { +- DHD_INFO(("DMA successful\n")); ++ DHD_INFO(("%s: DMA successful\n", __FUNCTION__)); + } + } + dmaxfer_free_dmaaddr(dhd, &prot->dmaxfer); +@@ -2317,7 +2676,7 @@ + uint16 alloced = 0; + + if (prot->dmaxfer_in_progress) { +- DHD_ERROR(("DMA is in progress...\n")); ++ DHD_ERROR(("%s: DMA is in progress...\n", __FUNCTION__)); + return ret; + } + prot->dmaxfer_in_progress = TRUE; +@@ -2361,7 +2720,7 @@ + DHD_FLOWRING_DEFAULT_NITEMS_POSTED_H2D); + DHD_GENERAL_UNLOCK(dhd, flags); + +- DHD_ERROR(("DMA Started...\n")); ++ DHD_ERROR(("%s: DMA Started...\n", __FUNCTION__)); + + return BCME_OK; + } +@@ -2391,8 +2750,12 @@ + } + + ret = dhd_fillup_ioct_reqst_ptrbased(dhd, (uint16)len, cmd, buf, ifidx); ++ if (ret < 0) { ++ DHD_ERROR(("%s : dhd_fillup_ioct_reqst_ptrbased error : %d\n", __FUNCTION__, ret)); ++ return ret; ++ } + +- DHD_INFO(("ACTION %d ifdix %d cmd %d len %d \n", ++ DHD_INFO(("%s: ACTION %d ifdix %d cmd %d len %d \n", __FUNCTION__, + action, ifidx, cmd, len)); + + /* wait for interrupt and get first fragment */ +@@ -2409,21 +2772,34 @@ + void* pkt; + int retlen; + int msgbuf_len = 0; ++ int post_cnt = 0; + unsigned long flags; ++ bool zero_posted = FALSE; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ if (dhd->busstate == DHD_BUS_DOWN) { ++ DHD_ERROR(("%s: bus is already down.\n", __FUNCTION__)); ++ return -1; ++ } + + if (prot->cur_ioctlresp_bufs_posted) + prot->cur_ioctlresp_bufs_posted--; ++ else ++ zero_posted = TRUE; ++ ++ post_cnt = dhd_msgbuf_rxbuf_post_ioctlresp_bufs(dhd); ++ if (zero_posted && (post_cnt <= 0)) { ++ return -1; ++ } + +- dhd_msgbuf_rxbuf_post_ioctlresp_bufs(dhd); ++ memset(&ioct_resp, 0, sizeof(ioctl_comp_resp_msg_t)); + + retlen = dhd_bus_rxctl(dhd->bus, (uchar*)&ioct_resp, msgbuf_len); + if (retlen <= 0) { +- DHD_ERROR(("IOCTL request failed with error code %d\n", retlen)); ++ DHD_ERROR(("%s: IOCTL request failed with error code %d\n", __FUNCTION__, retlen)); + return retlen; + } +- DHD_INFO(("ioctl resp retlen %d status %d, resp_len %d, pktid %d\n", ++ DHD_INFO(("%s: ioctl resp retlen %d status %d, resp_len %d, pktid %d\n", __FUNCTION__, + retlen, ioct_resp.compl_hdr.status, ioct_resp.resp_len, + ioct_resp.cmn_hdr.request_id)); + if (ioct_resp.resp_len != 0) { +@@ -2431,7 +2807,7 @@ + pkt = dhd_prot_packet_get(dhd, ioct_resp.cmn_hdr.request_id); + DHD_GENERAL_UNLOCK(dhd, flags); + +- DHD_INFO(("ioctl ret buf %p retlen %d status %x \n", pkt, retlen, ++ DHD_INFO(("%s: ioctl ret buf %p retlen %d status %x\n", __FUNCTION__, pkt, retlen, + ioct_resp.compl_hdr.status)); + /* get ret buf */ + if ((buf) && (pkt)) { +@@ -2474,8 +2850,12 @@ + + /* Fill up msgbuf for ioctl req */ + ret = dhd_fillup_ioct_reqst_ptrbased(dhd, (uint16)len, cmd, buf, ifidx); ++ if (ret < 0) { ++ DHD_ERROR(("%s : dhd_fillup_ioct_reqst_ptrbased error : %d\n", __FUNCTION__, ret)); ++ return ret; ++ } + +- DHD_INFO(("ACTIOn %d ifdix %d cmd %d len %d \n", ++ DHD_INFO(("%s: ACTIOn %d ifdix %d cmd %d len %d \n", __FUNCTION__, + action, ifidx, cmd, len)); + + ret = dhdmsgbuf_cmplt(dhd, prot->reqid, len, buf, prot->retbuf.va); +@@ -2498,7 +2878,16 @@ + /* Add prot dump output to a buffer */ + void dhd_prot_dump(dhd_pub_t *dhd, struct bcmstrbuf *strbuf) + { +- ++#if defined(PCIE_D2H_SYNC) ++ if (dhd->d2h_sync_mode & PCIE_SHARED_D2H_SYNC_SEQNUM) ++ bcm_bprintf(strbuf, "\nd2h_sync: SEQNUM:"); ++ else if (dhd->d2h_sync_mode & PCIE_SHARED_D2H_SYNC_XORCSUM) ++ bcm_bprintf(strbuf, "\nd2h_sync: XORCSUM:"); ++ else ++ bcm_bprintf(strbuf, "\nd2h_sync: NONE:"); ++ bcm_bprintf(strbuf, " d2h_sync_wait max<%lu> tot<%lu>\n", ++ dhd->prot->d2h_sync_wait_max, dhd->prot->d2h_sync_wait_tot); ++#endif /* PCIE_D2H_SYNC */ + } + + /* Update local copy of dongle statistics */ +@@ -2571,7 +2960,10 @@ + ret_buf = prot_get_ring_space(ring, nitems, alloced); + + if (ret_buf == NULL) { +- DHD_INFO(("%s: Ring space not available \n", ring->name)); ++ DHD_INFO(("%s: RING space not available on ring %s for %d items \n", __FUNCTION__, ++ ring->name, nitems)); ++ DHD_INFO(("%s: write %d read %d \n\n", __FUNCTION__, RING_WRITE_PTR(ring), ++ RING_READ_PTR(ring))); + return NULL; + } + } +@@ -2610,7 +3002,7 @@ + ioct_rqst = (ioctl_req_msg_t*)dhd_alloc_ring_space(dhd, prot->h2dring_ctrl_subn, + DHD_FLOWRING_DEFAULT_NITEMS_POSTED_H2D, &alloced); + if (ioct_rqst == NULL) { +- DHD_ERROR(("couldn't allocate space on msgring to send ioctl request\n")); ++ DHD_ERROR(("%s: couldn't allocate space on msgring to send ioctl request\n", __FUNCTION__)); + DHD_GENERAL_UNLOCK(dhd, flags); + return -1; + } +@@ -2638,9 +3030,10 @@ + OSL_CACHE_FLUSH((void *) prot->ioctbuf.va, len); + + if ((ulong)ioct_buf % DMA_ALIGN_LEN) +- DHD_ERROR(("host ioct address unaligned !!!!! \n")); ++ DHD_ERROR(("%s: host ioct address unaligned !!!!! \n", __FUNCTION__)); + +- DHD_CTL(("submitted IOCTL request request_id %d, cmd %d, output_buf_len %d, tx_id %d\n", ++ DHD_CTL(("%s: submitted IOCTL request request_id %d, cmd %d, output_buf_len %d, tx_id %d\n", ++ __FUNCTION__, + ioct_rqst->cmn_hdr.request_id, cmd, ioct_rqst->output_buf_len, + ioct_rqst->trans_id)); + +@@ -2791,8 +3184,7 @@ + uint alloced = 0; + msgbuf_ring_t *ring; + dmaaddr_t physaddr; +- uint16 size, cnt; +- uint32 *marker; ++ uint16 size; + + ASSERT(name); + BCM_REFERENCE(physaddr); +@@ -2822,6 +3214,13 @@ + size = max_item * len_item; + + /* Ring Memmory allocation */ ++#if defined(CONFIG_DHD_USE_STATIC_BUF) && defined(DHD_USE_STATIC_FLOWRING) ++ if (RING_IS_FLOWRING(ring)) { ++ ring->ring_base.va = DMA_ALLOC_CONSISTENT_STATIC(prot->osh, ++ size, DMA_ALIGN_LEN, &alloced, &ring->ring_base.pa, ++ &ring->ring_base.dmah, ringid); ++ } else ++#endif /* CONFIG_DHD_USE_STATIC_BUF && DHD_USE_STATIC_FLOWRING */ + ring->ring_base.va = DMA_ALLOC_CONSISTENT(prot->osh, size, DMA_ALIGN_LEN, + &alloced, &ring->ring_base.pa, &ring->ring_base.dmah); + +@@ -2832,11 +3231,7 @@ + + ASSERT(MODX((unsigned long)ring->ring_base.va, DMA_ALIGN_LEN) == 0); + bzero(ring->ring_base.va, size); +- for (cnt = 0; cnt < max_item; cnt++) { +- marker = (uint32 *)ring->ring_base.va + +- (cnt + 1) * len_item / sizeof(uint32) - 1; +- *marker = PCIE_D2H_RESET_MARK; +- } ++ + OSL_CACHE_FLUSH((void *) ring->ring_base.va, size); + + /* Ring state init */ +@@ -2845,19 +3240,30 @@ + goto fail; + bzero(ring->ringstate, sizeof(*ring->ringstate)); + +- DHD_INFO(("RING_ATTACH : %s Max item %d len item %d total size %d " +- "ring start %p buf phys addr %x:%x \n", ++#ifdef BCM_SECURE_DMA ++ if (SECURE_DMA_ENAB(prot->osh)) { ++ ring->secdma = MALLOC(prot->osh, sizeof(sec_cma_info_t)); ++ bzero(ring->secdma, sizeof(sec_cma_info_t)); ++ if (ring->secdma == NULL) { ++ DHD_ERROR(("%s: MALLOC failure for secdma\n", __FUNCTION__)); ++ goto fail; ++ } ++ } ++#endif ++ DHD_INFO(("%s: RING_ATTACH : %s Max item %d len item %d total size %d " ++ "ring start %p buf phys addr %x:%x \n", __FUNCTION__, + ring->name, ring->ringmem->max_item, ring->ringmem->len_items, + size, ring->ring_base.va, ring->ringmem->base_addr.high_addr, + ring->ringmem->base_addr.low_addr)); + return ring; + fail: +- if (ring->ring_base.va) ++ if (ring->ring_base.va && ring->ringmem) { + PHYSADDRHISET(physaddr, ring->ringmem->base_addr.high_addr); + PHYSADDRLOSET(physaddr, ring->ringmem->base_addr.low_addr); + size = ring->ringmem->max_item * ring->ringmem->len_items; + DMA_FREE_CONSISTENT(prot->osh, ring->ring_base.va, size, ring->ring_base.pa, NULL); + ring->ring_base.va = NULL; ++ } + if (ring->ringmem) + MFREE(prot->osh, ring->ringmem, sizeof(ring_mem_t)); + MFREE(prot->osh, ring, sizeof(msgbuf_ring_t)); +@@ -2907,6 +3313,12 @@ + size = ring->ringmem->max_item * ring->ringmem->len_items; + /* Free up ring */ + if (ring->ring_base.va) { ++#if defined(CONFIG_DHD_USE_STATIC_BUF) && defined(DHD_USE_STATIC_FLOWRING) ++ if (RING_IS_FLOWRING(ring)) { ++ DMA_FREE_CONSISTENT_STATIC(prot->osh, ring->ring_base.va, size, ++ ring->ring_base.pa, ring->ring_base.dmah, ring->idx); ++ } else ++#endif /* CONFIG_DHD_USE_STATIC_BUF && DHD_USE_STATIC_FLOWRING */ + DMA_FREE_CONSISTENT(prot->osh, ring->ring_base.va, size, ring->ring_base.pa, + ring->ring_base.dmah); + ring->ring_base.va = NULL; +@@ -2923,6 +3335,13 @@ + MFREE(prot->osh, ring->ringstate, sizeof(ring_state_t)); + ring->ringstate = NULL; + } ++#ifdef BCM_SECURE_DMA ++ if (SECURE_DMA_ENAB(prot->osh)) { ++ DHD_ERROR(("%s:free secdma\n", __FUNCTION__)); ++ SECURE_DMA_UNMAP_ALL(prot->osh, ring->secdma); ++ MFREE(prot->osh, ring->secdma, sizeof(sec_cma_info_t)); ++ } ++#endif + + /* free up ring info */ + MFREE(prot->osh, ring, sizeof(msgbuf_ring_t)); +@@ -2940,10 +3359,6 @@ + RING_MAX_ITEM(ring)); + + if (ring_avail_cnt == 0) { +- DHD_INFO(("RING space not available on ring %s for %d items \n", +- ring->name, nitems)); +- DHD_INFO(("write %d read %d \n\n", RING_WRITE_PTR(ring), +- RING_READ_PTR(ring))); + return NULL; + } + *alloced = MIN(nitems, ring_avail_cnt); +@@ -3161,7 +3576,11 @@ + if (*available_len == 0) + return NULL; + +- ASSERT(*available_len <= ring->ringmem->max_item); ++ if (*available_len > ring->ringmem->max_item) { ++ DHD_ERROR(("%s: *available_len %d, ring->ringmem->max_item %d\n", ++ __FUNCTION__, *available_len, ring->ringmem->max_item)); ++ return NULL; ++ } + + /* if space available, calculate address to be read */ + ret_addr = (char*)ring->ring_base.va + (r_ptr * ring->ringmem->len_items); +@@ -3177,6 +3596,9 @@ + /* convert index to bytes */ + *available_len = *available_len * ring->ringmem->len_items; + ++ /* Cache invalidate */ ++ OSL_CACHE_INV((void *) ret_addr, *available_len); ++ + /* return read address */ + return ret_addr; + } +@@ -3365,6 +3787,7 @@ + dhd_prot_t *prot = dhd->prot; + uint16 msglen = sizeof(tx_flowring_delete_request_t); + unsigned long flags; ++ char eabuf[ETHER_ADDR_STR_LEN]; + uint16 alloced = 0; + + /* align it to 4 bytes, so that all start addr form cbuf is 4 byte aligned */ +@@ -3390,7 +3813,11 @@ + flow_delete_rqst->flow_ring_id = htol16((uint16)flow_ring_node->flowid); + flow_delete_rqst->reason = htol16(BCME_OK); + +- DHD_ERROR(("%s sending FLOW RING Delete req msglen %d \n", __FUNCTION__, msglen)); ++ bcm_ether_ntoa((struct ether_addr *)flow_ring_node->flow_info.da, eabuf); ++ DHD_ERROR(("%s sending FLOW RING ID %d for peer %s prio %d ifindex %d" ++ " Delete req msglen %d\n", __FUNCTION__, ++ flow_ring_node->flowid, eabuf, flow_ring_node->flow_info.tid, ++ flow_ring_node->flow_info.ifindex, msglen)); + + /* upd wrt ptr and raise interrupt */ + prot_ring_write_complete(dhd, prot->h2dring_ctrl_subn, flow_delete_rqst, +@@ -3408,8 +3835,18 @@ + DHD_INFO(("%s Flow Delete Response status = %d \n", __FUNCTION__, + flow_delete_resp->cmplt.status)); + ++#ifdef PCIE_TX_DEFERRAL ++ if (flow_delete_resp->cmplt.status != BCME_OK) { ++ DHD_ERROR(("%s Flow Delete Response failure error status = %d \n", ++ __FUNCTION__, flow_delete_resp->cmplt.status)); ++ return; ++ } ++ set_bit(flow_delete_resp->cmplt.flow_ring_id, dhd->bus->delete_flow_map); ++ queue_work(dhd->bus->tx_wq, &dhd->bus->delete_flow_work); ++#else + dhd_bus_flow_ring_delete_response(dhd->bus, flow_delete_resp->cmplt.flow_ring_id, + flow_delete_resp->cmplt.status); ++#endif /* PCIE_TX_DEFERRAL */ + } + + int +diff -Nur a/drivers/net/wireless/bcmdhd/dhd_pcie.c c/drivers/net/wireless/bcmdhd/dhd_pcie.c +--- a/drivers/net/wireless/bcmdhd/dhd_pcie.c 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/dhd_pcie.c 2016-05-13 09:48:20.000000000 +0200 +@@ -3,7 +3,7 @@ + * + * $Copyright Open Broadcom Corporation$ + * +- * $Id: dhd_pcie.c 491657 2014-07-17 06:29:40Z $ ++ * $Id: dhd_pcie.c 506043 2014-10-02 12:29:45Z $ + */ + + +@@ -36,6 +36,7 @@ + #ifdef DHDTCPACK_SUPPRESS + #include + #endif /* DHDTCPACK_SUPPRESS */ ++#include + + #ifdef BCMEMBEDIMAGE + #include BCMEMBEDIMAGE +@@ -48,6 +49,10 @@ + #define ARMCR4REG_BANKPDA (0x4C/sizeof(uint32)) + /* Temporary war to fix precommit till sync issue between trunk & precommit branch is resolved */ + ++#if defined(SUPPORT_MULTIPLE_BOARD_REV) ++ extern unsigned int system_rev; ++#endif /* SUPPORT_MULTIPLE_BOARD_REV */ ++ + int dhd_dongle_memsize; + int dhd_dongle_ramsize; + #ifdef DHD_DEBUG +@@ -65,8 +70,8 @@ + static int _dhdpcie_download_firmware(struct dhd_bus *bus); + static int dhdpcie_download_firmware(dhd_bus_t *bus, osl_t *osh); + static int dhdpcie_bus_write_vars(dhd_bus_t *bus); +-static void dhdpcie_bus_process_mailbox_intr(dhd_bus_t *bus, uint32 intstatus); +-static void dhdpci_bus_read_frames(dhd_bus_t *bus); ++static bool dhdpcie_bus_process_mailbox_intr(dhd_bus_t *bus, uint32 intstatus); ++static bool dhdpci_bus_read_frames(dhd_bus_t *bus); + static int dhdpcie_readshared(dhd_bus_t *bus); + static void dhdpcie_init_shared_addr(dhd_bus_t *bus); + static bool dhdpcie_dongle_attach(dhd_bus_t *bus); +@@ -85,6 +90,10 @@ + static void dhdpcie_bus_wtcm64(dhd_bus_t *bus, ulong offset, uint64 data); + static uint64 dhdpcie_bus_rtcm64(dhd_bus_t *bus, ulong offset); + static void dhdpcie_bus_cfg_set_bar0_win(dhd_bus_t *bus, uint32 data); ++#ifdef CONFIG_ARCH_MSM8994 ++static void dhdpcie_bus_cfg_set_bar1_win(dhd_bus_t *bus, uint32 data); ++static ulong dhd_bus_cmn_check_offset(dhd_bus_t *bus, ulong offset); ++#endif + static void dhdpcie_bus_reg_unmap(osl_t *osh, ulong addr, int size); + static int dhdpcie_cc_nvmshadow(dhd_bus_t *bus, struct bcmstrbuf *b); + static void dhdpcie_send_mb_data(dhd_bus_t *bus, uint32 h2d_mb_data); +@@ -130,7 +139,9 @@ + IOV_DUMP_RINGUPD_BLOCK, + IOV_DMA_RINGINDICES, + IOV_DB1_FOR_MB, +- IOV_FLOW_PRIO_MAP ++ IOV_FLOW_PRIO_MAP, ++ IOV_RXBOUND, ++ IOV_TXBOUND + }; + + +@@ -164,11 +175,22 @@ + {"txp_thresh", IOV_TXP_THRESHOLD, 0, IOVT_UINT32, 0 }, + {"buzzz_dump", IOV_BUZZZ_DUMP, 0, IOVT_UINT32, 0 }, + {"flow_prio_map", IOV_FLOW_PRIO_MAP, 0, IOVT_UINT32, 0 }, ++ {"rxbound", IOV_RXBOUND, 0, IOVT_UINT32, 0 }, ++ {"txbound", IOV_TXBOUND, 0, IOVT_UINT32, 0 }, + {NULL, 0, 0, 0, 0 } + }; + + #define MAX_READ_TIMEOUT 5 * 1000 * 1000 + ++#ifndef DHD_RXBOUND ++#define DHD_RXBOUND 64 ++#endif ++#ifndef DHD_TXBOUND ++#define DHD_TXBOUND 64 ++#endif ++uint dhd_rxbound = DHD_RXBOUND; ++uint dhd_txbound = DHD_TXBOUND; ++ + /* Register/Unregister functions are called by the main DHD entry + * point (e.g. module insertion) to link with the bus driver, in + * order to look for or await the device. +@@ -213,7 +235,7 @@ + * + * 'tcm' is the *host* virtual address at which tcm is mapped. + */ +-dhd_bus_t* dhdpcie_bus_attach(osl_t *osh, volatile char* regs, volatile char* tcm) ++dhd_bus_t* dhdpcie_bus_attach(osl_t *osh, volatile char* regs, volatile char* tcm, uint32 tcm_size) + { + dhd_bus_t *bus; + +@@ -227,15 +249,19 @@ + bzero(bus, sizeof(dhd_bus_t)); + bus->regs = regs; + bus->tcm = tcm; ++ bus->tcm_size = tcm_size; + bus->osh = osh; + + dll_init(&bus->const_flowring); + + /* Attach pcie shared structure */ + bus->pcie_sh = MALLOC(osh, sizeof(pciedev_shared_t)); ++ if (!bus->pcie_sh) { ++ DHD_ERROR(("%s: MALLOC of bus->pcie_sh failed\n", __FUNCTION__)); ++ break; ++ } + + /* dhd_common_init(osh); */ +- + if (dhdpcie_dongle_attach(bus)) { + DHD_ERROR(("%s: dhdpcie_probe_attach failed\n", __FUNCTION__)); + break; +@@ -259,6 +285,12 @@ + + DHD_TRACE(("%s: EXIT FAILURE\n", __FUNCTION__)); + ++ if (bus && bus->pcie_sh) ++ MFREE(osh, bus->pcie_sh, sizeof(pciedev_shared_t)); ++ ++ if (bus) ++ MFREE(osh, bus, sizeof(dhd_bus_t)); ++ + return NULL; + } + +@@ -349,7 +381,7 @@ + } + + if (bus->dhd->busstate == DHD_BUS_DOWN) { +- DHD_ERROR(("%s : bus is down. we have nothing to do\n", ++ DHD_TRACE(("%s : bus is down. we have nothing to do\n", + __FUNCTION__)); + break; + } +@@ -402,12 +434,19 @@ + DHD_TRACE(("%s: ENTER\n", + __FUNCTION__)); + ++ + bus->alp_only = TRUE; + bus->sih = NULL; + + /* Set bar0 window to si_enum_base */ + dhdpcie_bus_cfg_set_bar0_win(bus, SI_ENUM_BASE); + ++#ifdef CONFIG_ARCH_MSM8994 ++ /* Read bar1 window */ ++ bus->bar1_win_base = OSL_PCI_READ_CONFIG(bus->osh, PCI_BAR1_WIN, 4); ++ DHD_ERROR(("%s: PCI_BAR1_WIN = %x\n", __FUNCTION__, bus->bar1_win_base)); ++#endif ++ + /* si_attach() will provide an SI handle and scan the backplane */ + if (!(bus->sih = si_attach((uint)devid, osh, regsva, PCI_BUS, bus, + &bus->vars, &bus->varsz))) { +@@ -422,6 +461,10 @@ + /* WAR where the BAR1 window may not be sized properly */ + W_REG(osh, &sbpcieregs->configaddr, 0x4e0); + val = R_REG(osh, &sbpcieregs->configdata); ++#ifdef CONFIG_ARCH_MSM8994 ++ bus->bar1_win_mask = 0xffffffff - (bus->tcm_size - 1); ++ DHD_ERROR(("%s: BAR1 window val=%d mask=%x\n", __FUNCTION__, val, bus->bar1_win_mask)); ++#endif + W_REG(osh, &sbpcieregs->configdata, val); + + /* Get info on the ARM and SOCRAM cores... */ +@@ -465,7 +508,8 @@ + bus->dongle_ram_base = CR4_4360_RAM_BASE; + break; + case BCM4345_CHIP_ID: +- bus->dongle_ram_base = CR4_4345_RAM_BASE; ++ bus->dongle_ram_base = (bus->sih->chiprev < 6) /* changed at 4345C0 */ ++ ? CR4_4345_LT_C0_RAM_BASE : CR4_4345_GE_C0_RAM_BASE; + break; + case BCM43602_CHIP_ID: + bus->dongle_ram_base = CR4_43602_RAM_BASE; +@@ -495,6 +539,7 @@ + bus->intr = (bool)dhd_intr; + + bus->wait_for_d3_ack = 1; ++ bus->suspended = FALSE; + DHD_TRACE(("%s: EXIT: SUCCESS\n", + __FUNCTION__)); + return 0; +@@ -523,7 +568,7 @@ + void + dhdpcie_bus_intr_enable(dhd_bus_t *bus) + { +- DHD_TRACE(("enable interrupts\n")); ++ DHD_TRACE(("%s: enable interrupts\n", __FUNCTION__)); + + if (!bus || !bus->sih) + return; +@@ -559,6 +604,24 @@ + DHD_TRACE(("%s Exit\n", __FUNCTION__)); + } + ++void ++dhdpcie_bus_remove_prep(dhd_bus_t *bus) ++{ ++ DHD_TRACE(("%s Enter\n", __FUNCTION__)); ++ ++ dhd_os_sdlock(bus->dhd); ++ ++ bus->dhd->busstate = DHD_BUS_DOWN; ++ dhdpcie_bus_intr_disable(bus); ++ // terence 20150406: fix for null pointer handle ++ if (bus->sih) ++ pcie_watchdog_reset(bus->osh, bus->sih, (sbpcieregs_t *)(bus->regs)); ++ ++ dhd_os_sdunlock(bus->dhd); ++ ++ DHD_TRACE(("%s Exit\n", __FUNCTION__)); ++} ++ + + /* Detach and free everything */ + void +@@ -576,12 +639,11 @@ + + if (bus->dhd) { + dongle_isolation = bus->dhd->dongle_isolation; +- dhd_detach(bus->dhd); +- + if (bus->intr) { + dhdpcie_bus_intr_disable(bus); + dhdpcie_free_irq(bus); + } ++ dhd_detach(bus->dhd); + dhdpcie_bus_release_dongle(bus, osh, dongle_isolation, TRUE); + dhd_free(bus->dhd); + bus->dhd = NULL; +@@ -593,7 +655,7 @@ + bus->regs = NULL; + } + if (bus->tcm) { +- dhdpcie_bus_reg_unmap(osh, (ulong)bus->tcm, DONGLE_TCM_MAP_SIZE); ++ dhdpcie_bus_reg_unmap(osh, (ulong)bus->tcm, bus->tcm_size); + bus->tcm = NULL; + } + +@@ -623,8 +685,6 @@ + dhdpcie_bus_release_dongle(dhd_bus_t *bus, osl_t *osh, bool dongle_isolation, bool reset_flag) + { + +- DHD_TRACE(("%s Enter\n", __FUNCTION__)); +- + DHD_TRACE(("%s: Enter bus->dhd %p bus->dhd->dongle_reset %d \n", __FUNCTION__, + bus->dhd, bus->dhd->dongle_reset)); + +@@ -643,6 +703,8 @@ + OFFSETOF(sbpcieregs_t, u.pcie2.ltr_state), ~0, 0); + } + si_detach(bus->sih); ++ // terence 20150420: fix for sih incorrectly handled in other function ++ bus->sih = NULL; + if (bus->vars && bus->varsz) + MFREE(osh, bus->vars, bus->varsz); + bus->vars = NULL; +@@ -671,6 +733,14 @@ + OSL_PCI_WRITE_CONFIG(bus->osh, PCI_BAR0_WIN, 4, data); + } + ++#ifdef CONFIG_ARCH_MSM8994 ++void ++dhdpcie_bus_cfg_set_bar1_win(dhd_bus_t *bus, uint32 data) ++{ ++ OSL_PCI_WRITE_CONFIG(bus->osh, PCI_BAR1_WIN, 4, data); ++} ++#endif ++ + void + dhdpcie_bus_dongle_setmemsize(struct dhd_bus *bus, int mem_size) + { +@@ -760,12 +830,13 @@ + /* Download firmware image and nvram image */ + int + dhd_bus_download_firmware(struct dhd_bus *bus, osl_t *osh, +- char *pfw_path, char *pnv_path) ++ char *pfw_path, char *pnv_path, char *pconf_path) + { + int ret; + + bus->fw_path = pfw_path; + bus->nv_path = pnv_path; ++ bus->dhd->conf_path = pconf_path; + + ret = dhdpcie_download_firmware(bus, osh); + +@@ -782,6 +853,16 @@ + + DHD_OS_WAKE_LOCK(bus->dhd); + ++ /* External conf takes precedence if specified */ ++ dhd_conf_preinit(bus->dhd); ++ dhd_conf_read_config(bus->dhd, bus->dhd->conf_path); ++ dhd_conf_set_fw_name_by_chip(bus->dhd, bus->fw_path); ++ dhd_conf_set_nv_name_by_chip(bus->dhd, bus->nv_path); ++ ++ printf("Final fw_path=%s\n", bus->fw_path); ++ printf("Final nv_path=%s\n", bus->nv_path); ++ printf("Final conf_path=%s\n", bus->dhd->conf_path); ++ + ret = _dhdpcie_download_firmware(bus); + + DHD_OS_WAKE_UNLOCK(bus->dhd); +@@ -803,8 +884,10 @@ + * entry or in module param. + */ + image = dhd_os_open_image(pfw_path); +- if (image == NULL) ++ if (image == NULL) { ++ printf("%s: Open firmware file failed %s\n", __FUNCTION__, pfw_path); + goto err; ++ } + + memptr = memblock = MALLOC(bus->dhd->osh, MEMBLOCK + DHD_SDALIGN); + if (memblock == NULL) { +@@ -872,8 +955,10 @@ + + if (nvram_file_exists) { + image = dhd_os_open_image(pnv_path); +- if (image == NULL) ++ if (image == NULL) { ++ printf("%s: Open nvram file failed %s\n", __FUNCTION__, pnv_path); + goto err; ++ } + } + + memblock = MALLOC(bus->dhd->osh, MAX_NVRAMBUF_SIZE); +@@ -1139,31 +1224,26 @@ + + /* Wait until control frame is available */ + timeleft = dhd_os_ioctl_resp_wait(bus->dhd, &bus->rxlen, &pending); +- if (timeleft == 0) { +- DHD_ERROR(("%s: resumed on timeout\n", __FUNCTION__)); +- bus->ioct_resp.cmn_hdr.request_id = 0; +- bus->ioct_resp.compl_hdr.status = 0xffff; +- bus->rxlen = 0; +- } + rxlen = bus->rxlen; +- bcopy(&bus->ioct_resp, msg, sizeof(ioctl_comp_resp_msg_t)); ++ bcopy(&bus->ioct_resp, msg, MIN(rxlen, sizeof(ioctl_comp_resp_msg_t))); + bus->rxlen = 0; + + if (rxlen) { + DHD_CTL(("%s: resumed on rxctl frame, got %d\n", __FUNCTION__, rxlen)); + } else if (timeleft == 0) { + DHD_ERROR(("%s: resumed on timeout\n", __FUNCTION__)); ++ bus->ioct_resp.cmn_hdr.request_id = 0; ++ bus->ioct_resp.compl_hdr.status = 0xffff; ++ bus->dhd->rxcnt_timeout++; ++ DHD_ERROR(("%s: rxcnt_timeout=%d\n", __FUNCTION__, bus->dhd->rxcnt_timeout)); + } else if (pending == TRUE) { + DHD_CTL(("%s: canceled\n", __FUNCTION__)); + return -ERESTARTSYS; + } else { + DHD_CTL(("%s: resumed for unknown reason?\n", __FUNCTION__)); + } +- if (timeleft == 0) { +- bus->dhd->rxcnt_timeout++; +- DHD_ERROR(("%s: rxcnt_timeout=%d\n", __FUNCTION__, bus->dhd->rxcnt_timeout)); +- } +- else ++ ++ if (timeleft != 0) + bus->dhd->rxcnt_timeout = 0; + + if (rxlen) +@@ -1448,10 +1528,17 @@ + uint dsize; + int detect_endian_flag = 0x01; + bool little_endian; ++#ifdef CONFIG_ARCH_MSM8994 ++ bool is_64bit_unaligned; ++#endif + + /* Detect endianness. */ + little_endian = *(char *)&detect_endian_flag; + ++#ifdef CONFIG_ARCH_MSM8994 ++ /* Check 64bit aligned or not. */ ++ is_64bit_unaligned = (address & 0x7); ++#endif + /* In remap mode, adjust address beyond socram and redirect + * to devram at SOCDEVRAM_BP_ADDR since remap address > orig_ramsize + * is not backplane accessible +@@ -1463,9 +1550,22 @@ + /* Do the transfer(s) */ + if (write) { + while (size) { +- if (size >= sizeof(uint64) && little_endian) ++ if (size >= sizeof(uint64) && little_endian) { ++#ifdef CONFIG_ARCH_MSM8994 ++ if (is_64bit_unaligned) { ++ DHD_INFO(("%s: write unaligned %lx\n", ++ __FUNCTION__, address)); ++ dhdpcie_bus_wtcm32(bus, address, *((uint32 *)data)); ++ data += 4; ++ size -= 4; ++ address += 4; ++ is_64bit_unaligned = (address & 0x7); ++ continue; ++ } ++ else ++#endif + dhdpcie_bus_wtcm64(bus, address, *((uint64 *)data)); +- else { ++ } else { + dsize = sizeof(uint8); + dhdpcie_bus_wtcm8(bus, address, *data); + } +@@ -1478,9 +1578,22 @@ + } + } else { + while (size) { +- if (size >= sizeof(uint64) && little_endian) ++ if (size >= sizeof(uint64) && little_endian) { ++#ifdef CONFIG_ARCH_MSM8994 ++ if (is_64bit_unaligned) { ++ DHD_INFO(("%s: read unaligned %lx\n", ++ __FUNCTION__, address)); ++ *(uint32 *)data = dhdpcie_bus_rtcm32(bus, address); ++ data += 4; ++ size -= 4; ++ address += 4; ++ is_64bit_unaligned = (address & 0x7); ++ continue; ++ } ++ else ++#endif + *(uint64 *)data = dhdpcie_bus_rtcm64(bus, address); +- else { ++ } else { + dsize = sizeof(uint8); + *data = dhdpcie_bus_rtcm8(bus, address); + } +@@ -1518,13 +1631,20 @@ + + queue = &flow_ring_node->queue; /* queue associated with flow ring */ + +- DHD_QUEUE_LOCK(queue->lock, flags); ++ DHD_FLOWRING_LOCK(flow_ring_node->lock, flags); ++ ++ if (flow_ring_node->status != FLOW_RING_STATUS_OPEN) { ++ DHD_FLOWRING_UNLOCK(flow_ring_node->lock, flags); ++ return BCME_NOTREADY; ++ } + + while ((txp = dhd_flow_queue_dequeue(bus->dhd, queue)) != NULL) { + PKTORPHAN(txp); + + #ifdef DHDTCPACK_SUPPRESS +- dhd_tcpack_check_xmit(bus->dhd, txp); ++ if (bus->dhd->tcpack_sup_mode != TCPACK_SUP_HOLD) { ++ dhd_tcpack_check_xmit(bus->dhd, txp); ++ } + #endif /* DHDTCPACK_SUPPRESS */ + /* Attempt to transfer packet over flow ring */ + +@@ -1534,7 +1654,7 @@ + dhd_prot_txdata_write_flush(bus->dhd, flow_id, FALSE); + /* reinsert at head */ + dhd_flow_queue_reinsert(bus->dhd, queue, txp); +- DHD_QUEUE_UNLOCK(queue->lock, flags); ++ DHD_FLOWRING_UNLOCK(flow_ring_node->lock, flags); + + /* If we are able to requeue back, return success */ + return BCME_OK; +@@ -1543,12 +1663,13 @@ + + dhd_prot_txdata_write_flush(bus->dhd, flow_id, FALSE); + +- DHD_QUEUE_UNLOCK(queue->lock, flags); ++ DHD_FLOWRING_UNLOCK(flow_ring_node->lock, flags); + } + + return ret; + } + ++#ifndef PCIE_TX_DEFERRAL + /* Send a data frame to the dongle. Callee disposes of txp. */ + int BCMFASTPATH + dhd_bus_txdata(struct dhd_bus *bus, void *txp, uint8 ifidx) +@@ -1585,12 +1706,12 @@ + + queue = &flow_ring_node->queue; /* queue associated with flow ring */ + +- DHD_QUEUE_LOCK(queue->lock, flags); ++ DHD_FLOWRING_LOCK(flow_ring_node->lock, flags); + + if ((ret = dhd_flow_queue_enqueue(bus->dhd, queue, txp)) != BCME_OK) + txp_pend = txp; + +- DHD_QUEUE_UNLOCK(queue->lock, flags); ++ DHD_FLOWRING_UNLOCK(flow_ring_node->lock, flags); + + if (flow_ring_node->status) { + DHD_INFO(("%s: Enq pkt flowid %d, status %d active %d\n", +@@ -1606,15 +1727,15 @@ + + /* If we have anything pending, try to push into q */ + if (txp_pend) { +- DHD_QUEUE_LOCK(queue->lock, flags); ++ DHD_FLOWRING_LOCK(flow_ring_node->lock, flags); + + if ((ret = dhd_flow_queue_enqueue(bus->dhd, queue, txp_pend)) != BCME_OK) { +- DHD_QUEUE_UNLOCK(queue->lock, flags); ++ DHD_FLOWRING_UNLOCK(flow_ring_node->lock, flags); + txp = txp_pend; + goto toss; + } + +- DHD_QUEUE_UNLOCK(queue->lock, flags); ++ DHD_FLOWRING_UNLOCK(flow_ring_node->lock, flags); + } + + return ret; +@@ -1628,6 +1749,78 @@ + PKTCFREE(bus->dhd->osh, txp, TRUE); + return ret; + } ++#else /* PCIE_TX_DEFERRAL */ ++int BCMFASTPATH ++dhd_bus_txdata(struct dhd_bus *bus, void *txp, uint8 ifidx) ++{ ++ unsigned long flags; ++ int ret = BCME_OK; ++ uint16 flowid; ++ flow_queue_t *queue; ++ flow_ring_node_t *flow_ring_node; ++ uint8 *pktdata = (uint8 *)PKTDATA(bus->dhd->osh, txp); ++ struct ether_header *eh = (struct ether_header *)pktdata; ++ ++ if (!bus->dhd->flowid_allocator) { ++ DHD_ERROR(("%s: Flow ring not intited yet \n", __FUNCTION__)); ++ goto toss; ++ } ++ ++ flowid = dhd_flowid_find(bus->dhd, ifidx, ++ bus->dhd->flow_prio_map[(PKTPRIO(txp))], ++ eh->ether_shost, eh->ether_dhost); ++ if (flowid == FLOWID_INVALID) { ++ DHD_PKTTAG_SET_FLOWID((dhd_pkttag_fr_t *)PKTTAG(txp), ifidx); ++ skb_queue_tail(&bus->orphan_list, txp); ++ queue_work(bus->tx_wq, &bus->create_flow_work); ++ return BCME_OK; ++ } ++ ++ DHD_PKTTAG_SET_FLOWID((dhd_pkttag_fr_t *)PKTTAG(txp), flowid); ++ flow_ring_node = DHD_FLOW_RING(bus->dhd, flowid); ++ queue = &flow_ring_node->queue; /* queue associated with flow ring */ ++ ++ DHD_DATA(("%s: pkt flowid %d, status %d active %d\n", ++ __FUNCTION__, flowid, flow_ring_node->status, ++ flow_ring_node->active)); ++ ++ DHD_FLOWRING_LOCK(flow_ring_node->lock, flags); ++ if ((flowid >= bus->dhd->num_flow_rings) || ++ (!flow_ring_node->active) || ++ (flow_ring_node->status == FLOW_RING_STATUS_DELETE_PENDING)) { ++ DHD_FLOWRING_UNLOCK(flow_ring_node->lock, flags); ++ DHD_DATA(("%s: Dropping pkt flowid %d, status %d active %d\n", ++ __FUNCTION__, flowid, flow_ring_node->status, ++ flow_ring_node->active)); ++ ret = BCME_ERROR; ++ goto toss; ++ } ++ ++ if (flow_ring_node->status == FLOW_RING_STATUS_PENDING) { ++ DHD_FLOWRING_UNLOCK(flow_ring_node->lock, flags); ++ DHD_PKTTAG_SET_FLOWID((dhd_pkttag_fr_t *)PKTTAG(txp), ifidx); ++ skb_queue_tail(&bus->orphan_list, txp); ++ queue_work(bus->tx_wq, &bus->create_flow_work); ++ return BCME_OK; ++ } ++ ++ if ((ret = dhd_flow_queue_enqueue(bus->dhd, queue, txp)) != BCME_OK) { ++ DHD_FLOWRING_UNLOCK(flow_ring_node->lock, flags); ++ goto toss; ++ } ++ ++ DHD_FLOWRING_UNLOCK(flow_ring_node->lock, flags); ++ ++ ret = dhd_bus_schedule_queue(bus, flowid, FALSE); ++ ++ return ret; ++ ++toss: ++ DHD_DATA(("%s: Toss %d\n", __FUNCTION__, ret)); ++ PKTCFREE(bus->dhd->osh, txp, TRUE); ++ return ret; ++} ++#endif /* !PCIE_TX_DEFERRAL */ + + + void +@@ -1702,86 +1895,74 @@ + dhd_rx_frame(bus->dhd, ifidx, pkt, pkt_count, 0); + } + ++#ifdef CONFIG_ARCH_MSM8994 ++static ulong dhd_bus_cmn_check_offset(dhd_bus_t *bus, ulong offset) ++{ ++ uint new_bar1_wbase = 0; ++ ulong address = 0; ++ ++ new_bar1_wbase = (uint)offset & bus->bar1_win_mask; ++ if (bus->bar1_win_base != new_bar1_wbase) { ++ bus->bar1_win_base = new_bar1_wbase; ++ dhdpcie_bus_cfg_set_bar1_win(bus, bus->bar1_win_base); ++ DHD_ERROR(("%s: offset=%lx, switch bar1_win_base to %x\n", ++ __FUNCTION__, offset, bus->bar1_win_base)); ++ } ++ ++ address = offset - bus->bar1_win_base; ++ ++ return address; ++} ++#else ++#define dhd_bus_cmn_check_offset(x, y) y ++#endif /* CONFIG_ARCH_MSM8994 */ ++ + /** 'offset' is a backplane address */ + void + dhdpcie_bus_wtcm8(dhd_bus_t *bus, ulong offset, uint8 data) + { +-#ifdef EXYNOS5433_PCIE_WAR +- exynos_pcie_set_l1_exit(); +-#endif /* EXYNOS5433_PCIE_WAR */ +- *(volatile uint8 *)(bus->tcm + offset) = (uint8)data; +-#ifdef EXYNOS5433_PCIE_WAR +- exynos_pcie_clear_l1_exit(); +-#endif /* EXYNOS5433_PCIE_WAR */ ++ *(volatile uint8 *)(bus->tcm + dhd_bus_cmn_check_offset(bus, offset)) = (uint8)data; + } + + uint8 + dhdpcie_bus_rtcm8(dhd_bus_t *bus, ulong offset) + { + volatile uint8 data; +-#ifdef EXYNOS5433_PCIE_WAR +- exynos_pcie_set_l1_exit(); +-#endif /* EXYNOS5433_PCIE_WAR */ + #ifdef BCM47XX_ACP_WAR +- data = R_REG(bus->dhd->osh, (volatile uint8 *)(bus->tcm + offset)); ++ data = R_REG(bus->dhd->osh, ++ (volatile uint8 *)(bus->tcm + dhd_bus_cmn_check_offset(bus, offset))); + #else +- data = *(volatile uint8 *)(bus->tcm + offset); ++ data = *(volatile uint8 *)(bus->tcm + dhd_bus_cmn_check_offset(bus, offset)); + #endif +-#ifdef EXYNOS5433_PCIE_WAR +- exynos_pcie_clear_l1_exit(); +-#endif /* EXYNOS5433_PCIE_WAR */ + return data; + } + + void + dhdpcie_bus_wtcm32(dhd_bus_t *bus, ulong offset, uint32 data) + { +-#ifdef EXYNOS5433_PCIE_WAR +- exynos_pcie_set_l1_exit(); +-#endif /* EXYNOS5433_PCIE_WAR */ +- *(volatile uint32 *)(bus->tcm + offset) = (uint32)data; +-#ifdef EXYNOS5433_PCIE_WAR +- exynos_pcie_clear_l1_exit(); +-#endif /* EXYNOS5433_PCIE_WAR */ ++ *(volatile uint32 *)(bus->tcm + dhd_bus_cmn_check_offset(bus, offset)) = (uint32)data; + } + void + dhdpcie_bus_wtcm16(dhd_bus_t *bus, ulong offset, uint16 data) + { +-#ifdef EXYNOS5433_PCIE_WAR +- exynos_pcie_set_l1_exit(); +-#endif /* EXYNOS5433_PCIE_WAR */ +- *(volatile uint16 *)(bus->tcm + offset) = (uint16)data; +-#ifdef EXYNOS5433_PCIE_WAR +- exynos_pcie_clear_l1_exit(); +-#endif /* EXYNOS5433_PCIE_WAR */ ++ *(volatile uint16 *)(bus->tcm + dhd_bus_cmn_check_offset(bus, offset)) = (uint16)data; + } + void + dhdpcie_bus_wtcm64(dhd_bus_t *bus, ulong offset, uint64 data) + { +-#ifdef EXYNOS5433_PCIE_WAR +- exynos_pcie_set_l1_exit(); +-#endif /* EXYNOS5433_PCIE_WAR */ +- *(volatile uint64 *)(bus->tcm + offset) = (uint64)data; +-#ifdef EXYNOS5433_PCIE_WAR +- exynos_pcie_clear_l1_exit(); +-#endif /* EXYNOS5433_PCIE_WAR */ ++ *(volatile uint64 *)(bus->tcm + dhd_bus_cmn_check_offset(bus, offset)) = (uint64)data; + } + + uint16 + dhdpcie_bus_rtcm16(dhd_bus_t *bus, ulong offset) + { + volatile uint16 data; +-#ifdef EXYNOS5433_PCIE_WAR +- exynos_pcie_set_l1_exit(); +-#endif /* EXYNOS5433_PCIE_WAR */ + #ifdef BCM47XX_ACP_WAR +- data = R_REG(bus->dhd->osh, (volatile uint16 *)(bus->tcm + offset)); ++ data = R_REG(bus->dhd->osh, ++ (volatile uint16 *)(bus->tcm + dhd_bus_cmn_check_offset(bus, offset))); + #else +- data = *(volatile uint16 *)(bus->tcm + offset); ++ data = *(volatile uint16 *)(bus->tcm + dhd_bus_cmn_check_offset(bus, offset)); + #endif +-#ifdef EXYNOS5433_PCIE_WAR +- exynos_pcie_clear_l1_exit(); +-#endif /* EXYNOS5433_PCIE_WAR */ + return data; + } + +@@ -1789,17 +1970,12 @@ + dhdpcie_bus_rtcm32(dhd_bus_t *bus, ulong offset) + { + volatile uint32 data; +-#ifdef EXYNOS5433_PCIE_WAR +- exynos_pcie_set_l1_exit(); +-#endif /* EXYNOS5433_PCIE_WAR */ + #ifdef BCM47XX_ACP_WAR +- data = R_REG(bus->dhd->osh, (volatile uint32 *)(bus->tcm + offset)); ++ data = R_REG(bus->dhd->osh, ++ (volatile uint32 *)(bus->tcm + dhd_bus_cmn_check_offset(bus, offset))); + #else +- data = *(volatile uint32 *)(bus->tcm + offset); ++ data = *(volatile uint32 *)(bus->tcm + dhd_bus_cmn_check_offset(bus, offset)); + #endif +-#ifdef EXYNOS5433_PCIE_WAR +- exynos_pcie_clear_l1_exit(); +-#endif /* EXYNOS5433_PCIE_WAR */ + return data; + } + +@@ -1807,17 +1983,12 @@ + dhdpcie_bus_rtcm64(dhd_bus_t *bus, ulong offset) + { + volatile uint64 data; +-#ifdef EXYNOS5433_PCIE_WAR +- exynos_pcie_set_l1_exit(); +-#endif /* EXYNOS5433_PCIE_WAR */ + #ifdef BCM47XX_ACP_WAR +- data = R_REG(bus->dhd->osh, (volatile uint64 *)(bus->tcm + offset)); ++ data = R_REG(bus->dhd->osh, ++ (volatile uint64 *)(bus->tcm + dhd_bus_cmn_check_offset(bus, offset))); + #else +- data = *(volatile uint64 *)(bus->tcm + offset); ++ data = *(volatile uint64 *)(bus->tcm + dhd_bus_cmn_check_offset(bus, offset)); + #endif +-#ifdef EXYNOS5433_PCIE_WAR +- exynos_pcie_clear_l1_exit(); +-#endif /* EXYNOS5433_PCIE_WAR */ + return data; + } + +@@ -2172,7 +2343,7 @@ + break; + } + default: +- printf("Maximum one argument supported\n"); ++ printf("%s: Maximum one argument supported\n", __FUNCTION__); + break; + } + bytes += sprintf(p + bytes, "\n"); +@@ -2202,10 +2373,10 @@ + } + + if (total == 0U) { +- printf("buzzz_dump total<%u> done\n", total); ++ printf("%s: buzzz_dump total<%u> done\n", __FUNCTION__, total); + return; + } else { +- printf("buzzz_dump total<%u> : part2<%u> + part1<%u>\n", ++ printf("%s: buzzz_dump total<%u> : part2<%u> + part1<%u>\n", __FUNCTION__, + total, part2, part1); + } + +@@ -2227,7 +2398,7 @@ + log = (void*)((size_t)log + buzzz_p->log_sz); + } + +- printf("buzzz_dump done.\n"); ++ printf("%s: buzzz_dump done.\n", __FUNCTION__); + } + + int dhd_buzzz_dump_dngl(dhd_bus_t *bus) +@@ -2242,11 +2413,11 @@ + return BCME_UNSUPPORTED; + } + if ((page_p = (char *)MALLOC(bus->dhd->osh, 4096)) == NULL) { +- printf("Page memory allocation failure\n"); ++ printf("%s: Page memory allocation failure\n", __FUNCTION__); + goto done; + } + if ((buzzz_p = MALLOC(bus->dhd->osh, sizeof(buzzz_t))) == NULL) { +- printf("Buzzz memory allocation failure\n"); ++ printf("%s: Buzzz memory allocation failure\n", __FUNCTION__); + goto done; + } + +@@ -2264,26 +2435,26 @@ + dhdpcie_bus_membytes(bus, FALSE, (ulong)sh->buzzz, + (uint8 *)buzzz_p, sizeof(buzzz_t)); + if (buzzz_p->count == 0) { +- printf("Empty dongle BUZZZ trace\n\n"); ++ printf("%s: Empty dongle BUZZZ trace\n\n", __FUNCTION__); + goto done; + } + if (buzzz_p->counters != 3) { /* 3 counters for CR4 */ +- printf("Counters<%u> mismatch\n", buzzz_p->counters); ++ printf("%s: Counters<%u> mismatch\n", __FUNCTION__, buzzz_p->counters); + goto done; + } + /* Allocate memory for trace buffer and format strings */ + buffer_p = MALLOC(bus->dhd->osh, buzzz_p->buffer_sz); + if (buffer_p == NULL) { +- printf("Buffer memory allocation failure\n"); ++ printf("%s: Buffer memory allocation failure\n", __FUNCTION__); + goto done; + } + /* Fetch the trace and format strings */ + dhdpcie_bus_membytes(bus, FALSE, (uint32)buzzz_p->log, /* Trace */ + (uint8 *)buffer_p, buzzz_p->buffer_sz); + /* Process and display the trace using formatted output */ +- printf("<#cycle> <#instruction> <#ctr3> \n"); ++ printf("%s: <#cycle> <#instruction> <#ctr3> \n", __FUNCTION__); + dhd_buzzz_dump(buzzz_p, buffer_p, page_p); +- printf("----- End of dongle BUZZZ Trace -----\n\n"); ++ printf("%s: ----- End of dongle BUZZZ Trace -----\n\n", __FUNCTION__); + MFREE(bus->dhd->osh, buffer_p, buzzz_p->buffer_sz); buffer_p = NULL; + } + +@@ -2325,7 +2496,7 @@ + } + + if (i >= pcie_serdes_spinwait) { +- DHD_ERROR(("pcie_mdiosetblock: timed out\n")); ++ DHD_ERROR(("%s: pcie_mdiosetblock: timed out\n", __FUNCTION__)); + return FALSE; + } + +@@ -2396,12 +2567,20 @@ + DHD_ERROR(("%s: == Power OFF ==\n", __FUNCTION__)); + bus->dhd->up = FALSE; + if (bus->dhd->busstate != DHD_BUS_DOWN) { +- dhd_prot_clear(dhdp); ++#ifdef BCMPCIE_OOB_HOST_WAKE ++ /* Clean up any pending host wake IRQ */ ++ dhd_bus_oob_intr_set(bus->dhd, FALSE); ++ dhd_bus_oob_intr_unregister(bus->dhd); ++#endif /* BCMPCIE_OOB_HOST_WAKE */ + dhd_os_wd_timer(dhdp, 0); + dhd_bus_stop(bus, TRUE); +-#ifdef CONFIG_ARCH_MSM ++ if (bus->intr) { ++ dhdpcie_bus_intr_disable(bus); ++ dhdpcie_free_irq(bus); ++ } ++ dhd_prot_clear(dhdp); ++ dhd_clear(dhdp); + dhd_bus_release_dongle(bus); +-#endif /* CONFIG_ARCH_MSM */ + dhdpcie_bus_free_resource(bus); + bcmerror = dhdpcie_bus_disable_device(bus); + if (bcmerror) { +@@ -2419,10 +2598,18 @@ + #endif /* CONFIG_ARCH_MSM */ + bus->dhd->busstate = DHD_BUS_DOWN; + } else { ++ if (bus->intr) { ++ dhdpcie_bus_intr_disable(bus); ++ dhdpcie_free_irq(bus); ++ } ++#ifdef BCMPCIE_OOB_HOST_WAKE ++ /* Clean up any pending host wake IRQ */ ++ dhd_bus_oob_intr_set(bus->dhd, FALSE); ++ dhd_bus_oob_intr_unregister(bus->dhd); ++#endif /* BCMPCIE_OOB_HOST_WAKE */ + dhd_prot_clear(dhdp); +-#ifdef CONFIG_ARCH_MSM ++ dhd_clear(dhdp); + dhd_bus_release_dongle(bus); +-#endif /* CONFIG_ARCH_MSM */ + dhdpcie_bus_free_resource(bus); + bcmerror = dhdpcie_bus_disable_device(bus); + if (bcmerror) { +@@ -2438,7 +2625,7 @@ + __FUNCTION__, bcmerror)); + goto done; + } +-#endif /* CONFIG_ARCH_MSM */ ++#endif /* CONFIG_ARCH_MSM */ + } + + bus->dhd->dongle_reset = TRUE; +@@ -2449,7 +2636,7 @@ + /* Powering On */ + DHD_ERROR(("%s: == Power ON ==\n", __FUNCTION__)); + #ifdef CONFIG_ARCH_MSM +- while (retry--) { ++ while (--retry) { + bcmerror = dhdpcie_bus_clock_start(bus); + if (!bcmerror) { + DHD_ERROR(("%s: dhdpcie_bus_clock_start OK\n", +@@ -2482,7 +2669,14 @@ + + bcmerror = dhdpcie_bus_dongle_attach(bus); + if (bcmerror) { +- DHD_ERROR(("%s: dhdpcie_bus_dongle_attach: %d\n", ++ DHD_ERROR(("%s: dhdpcie_bus_dongle_attach failed: %d\n", ++ __FUNCTION__, bcmerror)); ++ goto done; ++ } ++ ++ bcmerror = dhd_bus_request_irq(bus); ++ if (bcmerror) { ++ DHD_ERROR(("%s: dhd_bus_request_irq failed: %d\n", + __FUNCTION__, bcmerror)); + goto done; + } +@@ -2639,7 +2833,7 @@ + { + uint val; + if (!PCIE_GEN2(bus->sih)) { +- DHD_ERROR(("supported only in pcie gen2\n")); ++ DHD_ERROR(("%s: supported only in pcie gen2\n", __FUNCTION__)); + bcmerror = BCME_ERROR; + break; + } +@@ -2647,19 +2841,19 @@ + bcopy(&val, arg, sizeof(int32)); + } + else { +- DHD_ERROR(("pcie2_mdioop failed.\n")); ++ DHD_ERROR(("%s: pcie2_mdioop failed.\n", __FUNCTION__)); + bcmerror = BCME_ERROR; + } + break; + } + case IOV_SVAL(IOV_PCIESERDESREG): + if (!PCIE_GEN2(bus->sih)) { +- DHD_ERROR(("supported only in pcie gen2\n")); ++ DHD_ERROR(("%s: supported only in pcie gen2\n", __FUNCTION__)); + bcmerror = BCME_ERROR; + break; + } + if (pcie2_mdioop(bus, int_val, int_val2, TRUE, &int_val3, FALSE)) { +- DHD_ERROR(("pcie2_mdioop failed.\n")); ++ DHD_ERROR(("%s: pcie2_mdioop failed.\n", __FUNCTION__)); + bcmerror = BCME_ERROR; + } + break; +@@ -2860,7 +3054,7 @@ + /* Can change it only during initialization/FW download */ + if (bus->dhd->busstate == DHD_BUS_DOWN) { + if ((int_val > 3) || (int_val < 0)) { +- DHD_ERROR(("Bad argument. Possible values: 0, 1, 2 & 3\n")); ++ DHD_ERROR(("%s: Bad argument. Possible values: 0, 1, 2 & 3\n", __FUNCTION__)); + bcmerror = BCME_BADARG; + } else { + bus->dhd->dma_d2h_ring_upd_support = (int_val & 1) ? TRUE : FALSE; +@@ -2933,6 +3127,24 @@ + bcopy(&int_val, arg, val_size); + break; + ++ case IOV_GVAL(IOV_TXBOUND): ++ int_val = (int32)dhd_txbound; ++ bcopy(&int_val, arg, val_size); ++ break; ++ ++ case IOV_SVAL(IOV_TXBOUND): ++ dhd_txbound = (uint)int_val; ++ break; ++ ++ case IOV_GVAL(IOV_RXBOUND): ++ int_val = (int32)dhd_rxbound; ++ bcopy(&int_val, arg, val_size); ++ break; ++ ++ case IOV_SVAL(IOV_RXBOUND): ++ dhd_rxbound = (uint)int_val; ++ break; ++ + default: + bcmerror = BCME_UNSUPPORTED; + break; +@@ -2947,15 +3159,15 @@ + dhdpcie_bus_lpback_req(struct dhd_bus *bus, uint32 len) + { + if (bus->dhd == NULL) { +- DHD_ERROR(("bus not inited\n")); ++ DHD_ERROR(("%s: bus not inited\n", __FUNCTION__)); + return 0; + } + if (bus->dhd->prot == NULL) { +- DHD_ERROR(("prot is not inited\n")); ++ DHD_ERROR(("%s: prot is not inited\n", __FUNCTION__)); + return 0; + } + if (bus->dhd->busstate != DHD_BUS_DATA) { +- DHD_ERROR(("not in a readystate to LPBK is not inited\n")); ++ DHD_ERROR(("%s: not in a readystate to LPBK is not inited\n", __FUNCTION__)); + return 0; + } + dhdmsgbuf_lpbk_req(bus->dhd, len); +@@ -2972,7 +3184,7 @@ + } + + int +-dhdpcie_bus_suspend(struct dhd_bus *bus, bool state) ++dhdpcie_bus_suspend(struct dhd_bus *bus, bool state) + { + + int timeleft; +@@ -2980,54 +3192,77 @@ + int rc = 0; + + if (bus->dhd == NULL) { +- DHD_ERROR(("bus not inited\n")); ++ DHD_ERROR(("%s: bus not inited\n", __FUNCTION__)); + return BCME_ERROR; + } + if (bus->dhd->prot == NULL) { +- DHD_ERROR(("prot is not inited\n")); ++ DHD_ERROR(("%s: prot is not inited\n", __FUNCTION__)); + return BCME_ERROR; + } + if (bus->dhd->busstate != DHD_BUS_DATA && bus->dhd->busstate != DHD_BUS_SUSPEND) { +- DHD_ERROR(("not in a readystate to LPBK is not inited\n")); ++ DHD_ERROR(("%s: not in a readystate to LPBK is not inited\n", __FUNCTION__)); + return BCME_ERROR; + } + if (bus->dhd->dongle_reset) + return -EIO; + +- if (state == (bus->dhd->busstate == DHD_BUS_SUSPEND)) /* Set to same state */ ++ if (bus->suspended == state) /* Set to same state */ + return BCME_OK; + + if (state) { +-#ifdef EXYNOS5433_PCIE_WAR +- exynos_pcie_set_l1_exit(); +-#endif /* EXYNOS5433_PCIE_WAR */ + bus->wait_for_d3_ack = 0; ++ bus->suspended = TRUE; ++ bus->dhd->busstate = DHD_BUS_SUSPEND; + DHD_OS_WAKE_LOCK_WAIVE(bus->dhd); ++ dhd_os_set_ioctl_resp_timeout(DEFAULT_IOCTL_RESP_TIMEOUT); + dhdpcie_send_mb_data(bus, H2D_HOST_D3_INFORM); + timeleft = dhd_os_ioctl_resp_wait(bus->dhd, &bus->wait_for_d3_ack, &pending); ++ dhd_os_set_ioctl_resp_timeout(IOCTL_RESP_TIMEOUT); + DHD_OS_WAKE_LOCK_RESTORE(bus->dhd); + if (bus->wait_for_d3_ack) { + /* Got D3 Ack. Suspend the bus */ +- rc = dhdpcie_pci_suspend_resume(bus->dev, state); +- bus->dhd->busstate = DHD_BUS_SUSPEND; ++ if (dhd_os_check_wakelock_all(bus->dhd)) { ++ DHD_ERROR(("%s: Suspend failed because of wakelock\n", __FUNCTION__)); ++ bus->dev->current_state = PCI_D3hot; ++ pci_set_master(bus->dev); ++ rc = pci_set_power_state(bus->dev, PCI_D0); ++ if (rc) { ++ DHD_ERROR(("%s: pci_set_power_state failed:" ++ " current_state[%d], ret[%d]\n", ++ __FUNCTION__, bus->dev->current_state, rc)); ++ } ++ bus->suspended = FALSE; ++ bus->dhd->busstate = DHD_BUS_DATA; ++ rc = BCME_ERROR; ++ } else { ++ dhdpcie_bus_intr_disable(bus); ++ rc = dhdpcie_pci_suspend_resume(bus, state); ++ } + } else if (timeleft == 0) { + DHD_ERROR(("%s: resumed on timeout\n", __FUNCTION__)); +-#ifdef EXYNOS5433_PCIE_WAR +- exynos_pcie_clear_l1_exit(); +-#endif /* EXYNOS5433_PCIE_WAR */ +- return -ETIMEDOUT; ++ bus->dev->current_state = PCI_D3hot; ++ pci_set_master(bus->dev); ++ rc = pci_set_power_state(bus->dev, PCI_D0); ++ if (rc) { ++ DHD_ERROR(("%s: pci_set_power_state failed:" ++ " current_state[%d], ret[%d]\n", ++ __FUNCTION__, bus->dev->current_state, rc)); ++ } ++ bus->suspended = FALSE; ++ bus->dhd->busstate = DHD_BUS_DATA; ++ rc = -ETIMEDOUT; + } + bus->wait_for_d3_ack = 1; +- } +- else { ++ } else { + /* Resume */ +- rc = dhdpcie_pci_suspend_resume(bus->dev, state); ++#ifdef BCMPCIE_OOB_HOST_WAKE ++ DHD_OS_OOB_IRQ_WAKE_UNLOCK(bus->dhd); ++#endif /* BCMPCIE_OOB_HOST_WAKE */ ++ rc = dhdpcie_pci_suspend_resume(bus, state); ++ bus->suspended = FALSE; + bus->dhd->busstate = DHD_BUS_DATA; +- ++ dhdpcie_bus_intr_enable(bus); + } +-#ifdef EXYNOS5433_PCIE_WAR +- exynos_pcie_clear_l1_exit(); +-#endif /* EXYNOS5433_PCIE_WAR */ + return rc; + } + +@@ -3036,20 +3271,20 @@ + dhdpcie_bus_dmaxfer_req(struct dhd_bus *bus, uint32 len, uint32 srcdelay, uint32 destdelay) + { + if (bus->dhd == NULL) { +- DHD_ERROR(("bus not inited\n")); ++ DHD_ERROR(("%s: bus not inited\n", __FUNCTION__)); + return BCME_ERROR; + } + if (bus->dhd->prot == NULL) { +- DHD_ERROR(("prot is not inited\n")); ++ DHD_ERROR(("%s: prot is not inited\n", __FUNCTION__)); + return BCME_ERROR; + } + if (bus->dhd->busstate != DHD_BUS_DATA) { +- DHD_ERROR(("not in a readystate to LPBK is not inited\n")); ++ DHD_ERROR(("%s: not in a readystate to LPBK is not inited\n", __FUNCTION__)); + return BCME_ERROR; + } + + if (len < 5 || len > 4194296) { +- DHD_ERROR(("len is too small or too large\n")); ++ DHD_ERROR(("%s: len is too small or too large\n", __FUNCTION__)); + return BCME_ERROR; + } + return dhdmsgbuf_dmaxfer_req(bus->dhd, len, srcdelay, destdelay); +@@ -3236,7 +3471,7 @@ + /* Implement read back and verify later */ + #ifdef DHD_DEBUG + /* Verify NVRAM bytes */ +- DHD_INFO(("Compare NVRAM dl & ul; varsize=%d\n", varsize)); ++ DHD_INFO(("%s: Compare NVRAM dl & ul; varsize=%d\n", __FUNCTION__, varsize)); + nvram_ularray = (uint8*)MALLOC(bus->dhd->osh, varsize); + if (!nvram_ularray) + return BCME_NOMEM; +@@ -3269,9 +3504,9 @@ + phys_size += bus->dongle_ram_base; + + /* adjust to the user specified RAM */ +- DHD_INFO(("Physical memory size: %d, usable memory size: %d\n", ++ DHD_INFO(("%s: Physical memory size: %d, usable memory size: %d\n", __FUNCTION__, + phys_size, bus->ramsize)); +- DHD_INFO(("Vars are at %d, orig varsize is %d\n", ++ DHD_INFO(("%s: Vars are at %d, orig varsize is %d\n", __FUNCTION__, + varaddr, varsize)); + varsize = ((phys_size - 4) - varaddr); + +@@ -3289,7 +3524,7 @@ + varsizew = htol32(varsizew); + } + +- DHD_INFO(("New varsize is %d, length token=0x%08x\n", varsize, varsizew)); ++ DHD_INFO(("%s: New varsize is %d, length token=0x%08x\n", __FUNCTION__, varsize, varsizew)); + + /* Write the length token to the last word */ + bcmerror = dhdpcie_bus_membytes(bus, TRUE, (phys_size - 4), +@@ -3332,6 +3567,77 @@ + return bcmerror; + } + ++#ifndef BCMPCIE_OOB_HOST_WAKE ++/* loop through the capability list and see if the pcie capabilty exists */ ++uint8 ++dhdpcie_find_pci_capability(osl_t *osh, uint8 req_cap_id) ++{ ++ uint8 cap_id; ++ uint8 cap_ptr = 0; ++ uint8 byte_val; ++ ++ /* check for Header type 0 */ ++ byte_val = read_pci_cfg_byte(PCI_CFG_HDR); ++ if ((byte_val & 0x7f) != PCI_HEADER_NORMAL) { ++ DHD_ERROR(("%s : PCI config header not normal.\n", __FUNCTION__)); ++ goto end; ++ } ++ ++ /* check if the capability pointer field exists */ ++ byte_val = read_pci_cfg_byte(PCI_CFG_STAT); ++ if (!(byte_val & PCI_CAPPTR_PRESENT)) { ++ DHD_ERROR(("%s : PCI CAP pointer not present.\n", __FUNCTION__)); ++ goto end; ++ } ++ ++ cap_ptr = read_pci_cfg_byte(PCI_CFG_CAPPTR); ++ /* check if the capability pointer is 0x00 */ ++ if (cap_ptr == 0x00) { ++ DHD_ERROR(("%s : PCI CAP pointer is 0x00.\n", __FUNCTION__)); ++ goto end; ++ } ++ ++ /* loop thr'u the capability list and see if the pcie capabilty exists */ ++ ++ cap_id = read_pci_cfg_byte(cap_ptr); ++ ++ while (cap_id != req_cap_id) { ++ cap_ptr = read_pci_cfg_byte((cap_ptr + 1)); ++ if (cap_ptr == 0x00) break; ++ cap_id = read_pci_cfg_byte(cap_ptr); ++ } ++ ++end: ++ return cap_ptr; ++} ++ ++void ++dhdpcie_pme_active(osl_t *osh, bool enable) ++{ ++ uint8 cap_ptr; ++ uint32 pme_csr; ++ ++ cap_ptr = dhdpcie_find_pci_capability(osh, PCI_CAP_POWERMGMTCAP_ID); ++ ++ if (!cap_ptr) { ++ DHD_ERROR(("%s : Power Management Capability not present\n", __FUNCTION__)); ++ return; ++ } ++ ++ pme_csr = OSL_PCI_READ_CONFIG(osh, cap_ptr + PME_CSR_OFFSET, sizeof(uint32)); ++ DHD_ERROR(("%s : pme_sts_ctrl 0x%x\n", __FUNCTION__, pme_csr)); ++ ++ pme_csr |= PME_CSR_PME_STAT; ++ if (enable) { ++ pme_csr |= PME_CSR_PME_EN; ++ } else { ++ pme_csr &= ~PME_CSR_PME_EN; ++ } ++ ++ OSL_PCI_WRITE_CONFIG(osh, cap_ptr + PME_CSR_OFFSET, sizeof(uint32), pme_csr); ++} ++#endif /* BCMPCIE_OOB_HOST_WAKE */ ++ + /* Add bus dump output to a buffer */ + void dhd_bus_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf) + { +@@ -3362,7 +3668,6 @@ + next = dll_next_p(item); + + flow_ring_node = dhd_constlist_to_flowring(item); +- ASSERT(flow_ring_node->active); + dhd_prot_update_txflowring(dhd, flow_ring_node->flowid, flow_ring_node->prot_info); + } + } +@@ -3374,16 +3679,16 @@ + { + if ((bus->sih->buscorerev == 2) || (bus->sih->buscorerev == 6) || + (bus->sih->buscorerev == 4)) { +- DHD_ERROR(("mailbox communication not supported\n")); ++ DHD_ERROR(("%s: mailbox communication not supported\n", __FUNCTION__)); + return; + } + if (bus->db1_for_mb) { + /* this is a pcie core register, not the config regsiter */ +- DHD_INFO(("writing a mail box interrupt to the device, through doorbell 1\n")); ++ DHD_INFO(("%s: writing a mail box interrupt to the device, through doorbell 1\n", __FUNCTION__)); + si_corereg(bus->sih, bus->sih->buscoreidx, PCIH2D_DB1, ~0, 0x12345678); + } + else { +- DHD_INFO(("writing a mail box interrupt to the device, through config space\n")); ++ DHD_INFO(("%s: writing a mail box interrupt to the device, through config space\n", __FUNCTION__)); + dhdpcie_bus_cfg_write_dword(bus, PCISBMbx, 4, (1 << 0)); + dhdpcie_bus_cfg_write_dword(bus, PCISBMbx, 4, (1 << 0)); + } +@@ -3398,7 +3703,7 @@ + si_corereg(bus->sih, bus->sih->buscoreidx, PCIMailBoxInt, PCIE_INTB, PCIE_INTB); + } else { + /* this is a pcie core register, not the config regsiter */ +- DHD_INFO(("writing a door bell to the device\n")); ++ DHD_INFO(("%s: writing a door bell to the device\n", __FUNCTION__)); + si_corereg(bus->sih, bus->sih->buscoreidx, PCIH2D_MailBox, ~0, 0x12345678); + } + } +@@ -3454,13 +3759,6 @@ + return 0; + } + +- if (bus->dhd->busstate == DHD_BUS_SUSPEND) { +- resched = TRUE; +- DHD_ERROR(("%s : pcie is still in suspend state!!!\n", __FUNCTION__)); +- OSL_DELAY(20 * 1000); /* 20ms */ +- return resched; +- } +- + intstatus = bus->intstatus; + + if ((bus->sih->buscorerev == 6) || (bus->sih->buscorerev == 4) || +@@ -3471,20 +3769,25 @@ + intstatus |= newstatus; + bus->intstatus = 0; + if (intstatus & I_MB) { +- dhdpcie_bus_process_mailbox_intr(bus, intstatus); ++ resched = dhdpcie_bus_process_mailbox_intr(bus, intstatus); + } + } else { + /* this is a PCIE core register..not a config register... */ + newstatus = si_corereg(bus->sih, bus->sih->buscoreidx, PCIMailBoxInt, 0, 0); + intstatus |= (newstatus & bus->def_intmask); +- si_corereg(bus->sih, bus->sih->buscoreidx, PCIMailBoxInt, intstatus, intstatus); ++ si_corereg(bus->sih, bus->sih->buscoreidx, PCIMailBoxInt, newstatus, newstatus); + if (intstatus & bus->def_intmask) { +- dhdpcie_bus_process_mailbox_intr(bus, intstatus); ++ resched = dhdpcie_bus_process_mailbox_intr(bus, intstatus); + intstatus &= ~bus->def_intmask; + } + } + +- dhdpcie_bus_intr_enable(bus); ++ if (!resched) { ++ // terence 20150420: no need to enable interrupt if busstate is down ++ if (bus->dhd->busstate) { ++ dhdpcie_bus_intr_enable(bus); ++ } ++ } + return resched; + + } +@@ -3499,13 +3802,13 @@ + + if (cur_h2d_mb_data != 0) { + uint32 i = 0; +- DHD_INFO(("GRRRRRRR: MB transaction is already pending 0x%04x\n", cur_h2d_mb_data)); ++ DHD_INFO(("%s: GRRRRRRR: MB transaction is already pending 0x%04x\n", __FUNCTION__, cur_h2d_mb_data)); + while ((i++ < 100) && cur_h2d_mb_data) { + OSL_DELAY(10); + dhd_bus_cmn_readshared(bus, &cur_h2d_mb_data, HTOD_MB_DATA, 0); + } + if (i >= 100) +- DHD_ERROR(("waited 1ms for the dngl to ack the previous mb transaction\n")); ++ DHD_ERROR(("%s: waited 1ms for the dngl to ack the previous mb transaction\n", __FUNCTION__)); + } + + dhd_bus_cmn_writeshared(bus, &h2d_mb_data, sizeof(uint32), HTOD_MB_DATA, 0); +@@ -3526,16 +3829,16 @@ + + dhd_bus_cmn_writeshared(bus, &zero, sizeof(uint32), DTOH_MB_DATA, 0); + +- DHD_INFO(("D2H_MB_DATA: 0x%04x\n", d2h_mb_data)); ++ DHD_INFO(("%s: D2H_MB_DATA: 0x%04x\n", __FUNCTION__, d2h_mb_data)); + if (d2h_mb_data & D2H_DEV_DS_ENTER_REQ) { + /* what should we do */ +- DHD_INFO(("D2H_MB_DATA: DEEP SLEEP REQ\n")); ++ DHD_INFO(("%s: D2H_MB_DATA: DEEP SLEEP REQ\n", __FUNCTION__)); + dhdpcie_send_mb_data(bus, H2D_HOST_DS_ACK); +- DHD_INFO(("D2H_MB_DATA: sent DEEP SLEEP ACK\n")); ++ DHD_INFO(("%s: D2H_MB_DATA: sent DEEP SLEEP ACK\n", __FUNCTION__)); + } + if (d2h_mb_data & D2H_DEV_DS_EXIT_NOTE) { + /* what should we do */ +- DHD_INFO(("D2H_MB_DATA: DEEP SLEEP EXIT\n")); ++ DHD_INFO(("%s: D2H_MB_DATA: DEEP SLEEP EXIT\n", __FUNCTION__)); + } + if (d2h_mb_data & D2H_DEV_D3_ACK) { + /* what should we do */ +@@ -3546,7 +3849,7 @@ + } + } + if (d2h_mb_data & D2H_DEV_FWHALT) { +- DHD_INFO(("FW trap has happened\n")); ++ DHD_INFO(("%s: FW trap has happened\n", __FUNCTION__)); + #ifdef DHD_DEBUG + dhdpcie_checkdied(bus, NULL, 0); + #endif +@@ -3554,15 +3857,16 @@ + } + } + +-static void ++static bool + dhdpcie_bus_process_mailbox_intr(dhd_bus_t *bus, uint32 intstatus) + { ++ bool resched = FALSE; + + if ((bus->sih->buscorerev == 2) || (bus->sih->buscorerev == 6) || + (bus->sih->buscorerev == 4)) { + /* Msg stream interrupt */ + if (intstatus & I_BIT1) { +- dhdpci_bus_read_frames(bus); ++ resched = dhdpci_bus_read_frames(bus); + } else if (intstatus & I_BIT0) { + /* do nothing for Now */ + } +@@ -3570,29 +3874,47 @@ + else { + if (intstatus & (PCIE_MB_TOPCIE_FN0_0 | PCIE_MB_TOPCIE_FN0_1)) + dhdpcie_handle_mb_data(bus); ++ ++ if (bus->dhd->busstate == DHD_BUS_SUSPEND) { ++ goto exit; ++ } ++ + if (intstatus & PCIE_MB_D2H_MB_MASK) { +- dhdpci_bus_read_frames(bus); ++ resched = dhdpci_bus_read_frames(bus); + } + } ++exit: ++ return resched; + } + + /* Decode dongle to host message stream */ +-static void ++static bool + dhdpci_bus_read_frames(dhd_bus_t *bus) + { ++ bool more = FALSE; ++ + /* There may be frames in both ctrl buf and data buf; check ctrl buf first */ + DHD_PERIM_LOCK(bus->dhd); /* Take the perimeter lock */ +- + dhd_prot_process_ctrlbuf(bus->dhd); ++ /* Unlock to give chance for resp to be handled */ ++ DHD_PERIM_UNLOCK(bus->dhd); /* Release the perimeter lock */ + ++ DHD_PERIM_LOCK(bus->dhd); /* Take the perimeter lock */ + /* update the flow ring cpls */ + dhd_update_txflowrings(bus->dhd); + +- dhd_prot_process_msgbuf_txcpl(bus->dhd); +- +- dhd_prot_process_msgbuf_rxcpl(bus->dhd); ++ /* With heavy TX traffic, we could get a lot of TxStatus ++ * so add bound ++ */ ++ more |= dhd_prot_process_msgbuf_txcpl(bus->dhd, dhd_txbound); + ++ /* With heavy RX traffic, this routine potentially could spend some time ++ * processing RX frames without RX bound ++ */ ++ more |= dhd_prot_process_msgbuf_rxcpl(bus->dhd, dhd_rxbound); + DHD_PERIM_UNLOCK(bus->dhd); /* Release the perimeter lock */ ++ ++ return more; + } + + static int +@@ -3617,19 +3939,19 @@ + (addr > shaddr)) { + DHD_ERROR(("%s: address (0x%08x) of pciedev_shared invalid\n", + __FUNCTION__, addr)); +- DHD_ERROR(("Waited %u usec, dongle is not ready\n", tmo.elapsed)); ++ DHD_ERROR(("%s: Waited %u usec, dongle is not ready\n", __FUNCTION__, tmo.elapsed)); + return BCME_ERROR; + } else { + bus->shared_addr = (ulong)addr; +- DHD_ERROR(("PCIe shared addr read took %u usec " +- "before dongle is ready\n", tmo.elapsed)); ++ DHD_ERROR(("%s: PCIe shared addr read took %u usec " ++ "before dongle is ready\n", __FUNCTION__, tmo.elapsed)); + } + + /* Read hndrte_shared structure */ + if ((rv = dhdpcie_bus_membytes(bus, FALSE, addr, (uint8 *)sh, + sizeof(pciedev_shared_t))) < 0) { +- DHD_ERROR(("Failed to read PCIe shared struct," +- "size read %d < %d\n", rv, (int)sizeof(pciedev_shared_t))); ++ DHD_ERROR(("%s: Failed to read PCIe shared struct," ++ "size read %d < %d\n", __FUNCTION__, rv, (int)sizeof(pciedev_shared_t))); + return rv; + } + +@@ -3653,7 +3975,7 @@ + bus->dma_rxoffset = bus->pcie_sh->dma_rxoffset; + dhd_prot_rx_dataoffset(bus->dhd, bus->dma_rxoffset); + +- DHD_ERROR(("DMA RX offset from shared Area %d\n", bus->dma_rxoffset)); ++ DHD_ERROR(("%s: DMA RX offset from shared Area %d\n", __FUNCTION__, bus->dma_rxoffset)); + + if ((sh->flags & PCIE_SHARED_VERSION_MASK) > PCIE_SHARED_VERSION) { + DHD_ERROR(("%s: pcie_shared version %d in dhd " +@@ -3672,7 +3994,7 @@ + } else + bus->txmode_push = FALSE; + } +- DHD_ERROR(("bus->txmode_push is set to %d\n", bus->txmode_push)); ++ DHD_ERROR(("%s: bus->txmode_push is set to %d\n", __FUNCTION__, bus->txmode_push)); + + /* Does the FW support DMA'ing r/w indices */ + if (sh->flags & PCIE_SHARED_DMA_INDEX) { +@@ -3750,14 +4072,18 @@ + dhd_fillup_ring_sharedptr_info(bus, &ring_info); + + bcm_print_bytes("ring_info_raw", (uchar *)&ring_info, sizeof(ring_info_t)); +- DHD_INFO(("ring_info\n")); ++ DHD_INFO(("%s: ring_info\n", __FUNCTION__)); + +- DHD_ERROR(("max H2D queues %d\n", ltoh16(ring_info.max_sub_queues))); ++ DHD_ERROR(("%s: max H2D queues %d\n", __FUNCTION__, ltoh16(ring_info.max_sub_queues))); + +- DHD_INFO(("mail box address\n")); +- DHD_INFO(("h2d_mb_data_ptr_addr 0x%04x\n", bus->h2d_mb_data_ptr_addr)); +- DHD_INFO(("d2h_mb_data_ptr_addr 0x%04x\n", bus->d2h_mb_data_ptr_addr)); ++ DHD_INFO(("%s: mail box address\n", __FUNCTION__)); ++ DHD_INFO(("%s: h2d_mb_data_ptr_addr 0x%04x\n", __FUNCTION__, bus->h2d_mb_data_ptr_addr)); ++ DHD_INFO(("%s: d2h_mb_data_ptr_addr 0x%04x\n", __FUNCTION__, bus->d2h_mb_data_ptr_addr)); + } ++ ++ bus->dhd->d2h_sync_mode = sh->flags & PCIE_SHARED_D2H_SYNC_MODE_MASK; ++ DHD_INFO(("%s: d2h_sync_mode 0x%08x\n", __FUNCTION__, bus->dhd->d2h_sync_mode)); ++ + return BCME_OK; + } + /* Read ring mem and ring state ptr info from shared are in TCM */ +@@ -3788,14 +4114,14 @@ + bus->ring_sh[i].ring_mem_addr = tcm_memloc; + /* Update mem block */ + tcm_memloc = tcm_memloc + sizeof(ring_mem_t); +- DHD_INFO(("ring id %d ring mem addr 0x%04x \n", ++ DHD_INFO(("%s: ring id %d ring mem addr 0x%04x \n", __FUNCTION__, + i, bus->ring_sh[i].ring_mem_addr)); + } + + /* Tx flow Ring */ + if (bus->txmode_push) { + bus->ring_sh[i].ring_mem_addr = tcm_memloc; +- DHD_INFO(("TX ring ring id %d ring mem addr 0x%04x \n", ++ DHD_INFO(("%s: TX ring ring id %d ring mem addr 0x%04x \n", __FUNCTION__, + i, bus->ring_sh[i].ring_mem_addr)); + } + } +@@ -3815,7 +4141,7 @@ + h2d_w_idx_ptr = h2d_w_idx_ptr + sizeof(uint32); + h2d_r_idx_ptr = h2d_r_idx_ptr + sizeof(uint32); + +- DHD_INFO(("h2d w/r : idx %d write %x read %x \n", i, ++ DHD_INFO(("%s: h2d w/r : idx %d write %x read %x \n", __FUNCTION__, i, + bus->ring_sh[i].ring_state_w, bus->ring_sh[i].ring_state_r)); + } + /* Store d2h common ring write/read pointers */ +@@ -3827,7 +4153,7 @@ + d2h_w_idx_ptr = d2h_w_idx_ptr + sizeof(uint32); + d2h_r_idx_ptr = d2h_r_idx_ptr + sizeof(uint32); + +- DHD_INFO(("d2h w/r : idx %d write %x read %x \n", i, ++ DHD_INFO(("%s: d2h w/r : idx %d write %x read %x \n", __FUNCTION__, i, + bus->ring_sh[i].ring_state_w, bus->ring_sh[i].ring_state_r)); + } + +@@ -3836,7 +4162,7 @@ + bus->ring_sh[i].ring_state_w = h2d_w_idx_ptr; + bus->ring_sh[i].ring_state_r = h2d_r_idx_ptr; + +- DHD_INFO(("txflow : idx %d write %x read %x \n", i, ++ DHD_INFO(("%s: txflow : idx %d write %x read %x \n", __FUNCTION__, i, + bus->ring_sh[i].ring_state_w, bus->ring_sh[i].ring_state_r)); + } else { + for (j = 0; j < (bus->max_sub_queues - BCMPCIE_H2D_COMMON_MSGRINGS); +@@ -3849,13 +4175,15 @@ + h2d_w_idx_ptr = h2d_w_idx_ptr + sizeof(uint32); + h2d_r_idx_ptr = h2d_r_idx_ptr + sizeof(uint32); + +- DHD_INFO(("FLOW Rings h2d w/r : idx %d write %x read %x \n", i, ++ DHD_INFO(("%s: FLOW Rings h2d w/r : idx %d write %x read %x \n", ++ __FUNCTION__, i, + bus->ring_sh[i].ring_state_w, + bus->ring_sh[i].ring_state_r)); + } + } + } + } ++ + /* Initialize bus module: prepare for communication w/dongle */ + int dhd_bus_init(dhd_pub_t *dhdp, bool enforce_mutex) + { +@@ -4106,43 +4434,42 @@ + return bus->txmode_push; + } + +-void dhd_bus_clean_flow_ring(dhd_bus_t *bus, uint16 flowid) ++void dhd_bus_clean_flow_ring(dhd_bus_t *bus, void *node) + { + void *pkt; + flow_queue_t *queue; +- flow_ring_node_t *flow_ring_node; ++ flow_ring_node_t *flow_ring_node = (flow_ring_node_t *)node; + unsigned long flags; + +- flow_ring_node = DHD_FLOW_RING(bus->dhd, flowid); +- ASSERT(flow_ring_node->flowid == flowid); +- + queue = &flow_ring_node->queue; + +- /* Call Flow ring clean up */ +- dhd_prot_clean_flow_ring(bus->dhd, flow_ring_node->prot_info); +- dhd_flowid_free(bus->dhd, flow_ring_node->flow_info.ifindex, +- flow_ring_node->flowid); +- +- /* clean up BUS level info */ +- DHD_QUEUE_LOCK(queue->lock, flags); +- + #ifdef DHDTCPACK_SUPPRESS + /* Clean tcp_ack_info_tbl in order to prevent access to flushed pkt, + * when there is a newly coming packet from network stack. + */ + dhd_tcpack_info_tbl_clean(bus->dhd); + #endif /* DHDTCPACK_SUPPRESS */ ++ ++ /* clean up BUS level info */ ++ DHD_FLOWRING_LOCK(flow_ring_node->lock, flags); ++ + /* Flush all pending packets in the queue, if any */ + while ((pkt = dhd_flow_queue_dequeue(bus->dhd, queue)) != NULL) { + PKTFREE(bus->dhd->osh, pkt, TRUE); + } + ASSERT(flow_queue_empty(queue)); + +- DHD_QUEUE_UNLOCK(queue->lock, flags); +- ++ flow_ring_node->status = FLOW_RING_STATUS_CLOSED; + flow_ring_node->active = FALSE; +- + dll_delete(&flow_ring_node->list); ++ ++ DHD_FLOWRING_UNLOCK(flow_ring_node->lock, flags); ++ ++ /* Call Flow ring clean up */ ++ dhd_prot_clean_flow_ring(bus->dhd, flow_ring_node->prot_info); ++ dhd_flowid_free(bus->dhd, flow_ring_node->flow_info.ifindex, ++ flow_ring_node->flowid); ++ + } + + /* +@@ -4158,11 +4485,8 @@ + DHD_INFO(("%s :Flow create\n", __FUNCTION__)); + + /* Send Msg to device about flow ring creation */ +- dhd_prot_flow_ring_create(bus->dhd, flow_ring_node); +- +- flow_ring_node->status = FLOW_RING_STATUS_PENDING; +- +- dll_prepend(&bus->const_flowring, &flow_ring_node->list); ++ if (dhd_prot_flow_ring_create(bus->dhd, flow_ring_node) != BCME_OK) ++ return BCME_NOMEM; + + return BCME_OK; + } +@@ -4171,6 +4495,7 @@ + dhd_bus_flow_ring_create_response(dhd_bus_t *bus, uint16 flowid, int32 status) + { + flow_ring_node_t *flow_ring_node; ++ unsigned long flags; + + DHD_INFO(("%s :Flow Response %d \n", __FUNCTION__, flowid)); + +@@ -4181,11 +4506,13 @@ + DHD_ERROR(("%s Flow create Response failure error status = %d \n", + __FUNCTION__, status)); + /* Call Flow clean up */ +- dhd_bus_clean_flow_ring(bus, flowid); ++ dhd_bus_clean_flow_ring(bus, flow_ring_node); + return; + } + ++ DHD_FLOWRING_LOCK(flow_ring_node->lock, flags); + flow_ring_node->status = FLOW_RING_STATUS_OPEN; ++ DHD_FLOWRING_UNLOCK(flow_ring_node->lock, flags); + + dhd_bus_schedule_queue(bus, flowid, FALSE); + +@@ -4204,15 +4531,16 @@ + + flow_ring_node = (flow_ring_node_t *)arg; + ++ DHD_FLOWRING_LOCK(flow_ring_node->lock, flags); + if (flow_ring_node->status & FLOW_RING_STATUS_DELETE_PENDING) { ++ DHD_FLOWRING_UNLOCK(flow_ring_node->lock, flags); + DHD_ERROR(("%s :Delete Pending\n", __FUNCTION__)); + return BCME_ERROR; + } ++ flow_ring_node->status = FLOW_RING_STATUS_DELETE_PENDING; + + queue = &flow_ring_node->queue; /* queue associated with flow ring */ + +- DHD_QUEUE_LOCK(queue->lock, flags); +- + #ifdef DHDTCPACK_SUPPRESS + /* Clean tcp_ack_info_tbl in order to prevent access to flushed pkt, + * when there is a newly coming packet from network stack. +@@ -4225,12 +4553,11 @@ + } + ASSERT(flow_queue_empty(queue)); + +- DHD_QUEUE_UNLOCK(queue->lock, flags); ++ DHD_FLOWRING_UNLOCK(flow_ring_node->lock, flags); + + /* Send Msg to device about flow ring deletion */ + dhd_prot_flow_ring_delete(bus->dhd, flow_ring_node); + +- flow_ring_node->status = FLOW_RING_STATUS_DELETE_PENDING; + return BCME_OK; + } + +@@ -4250,10 +4577,8 @@ + return; + } + /* Call Flow clean up */ +- dhd_bus_clean_flow_ring(bus, flowid); ++ dhd_bus_clean_flow_ring(bus, flow_ring_node); + +- flow_ring_node->status = FLOW_RING_STATUS_OPEN; +- flow_ring_node->active = FALSE; + return; + + } +@@ -4270,7 +4595,7 @@ + flow_ring_node = (flow_ring_node_t *)arg; + queue = &flow_ring_node->queue; /* queue associated with flow ring */ + +- DHD_QUEUE_LOCK(queue->lock, flags); ++ DHD_FLOWRING_LOCK(flow_ring_node->lock, flags); + + #ifdef DHDTCPACK_SUPPRESS + /* Clean tcp_ack_info_tbl in order to prevent access to flushed pkt, +@@ -4284,7 +4609,7 @@ + } + ASSERT(flow_queue_empty(queue)); + +- DHD_QUEUE_UNLOCK(queue->lock, flags); ++ DHD_FLOWRING_UNLOCK(flow_ring_node->lock, flags); + + /* Send Msg to device about flow ring flush */ + dhd_prot_flow_ring_flush(bus->dhd, flow_ring_node); +@@ -4357,6 +4682,12 @@ + dhdpcie_free_resource(bus); + } + ++int ++dhd_bus_request_irq(struct dhd_bus *bus) ++{ ++ return dhdpcie_bus_request_irq(bus); ++} ++ + bool + dhdpcie_bus_dongle_attach(struct dhd_bus *bus) + { +@@ -4383,3 +4714,20 @@ + + return 0; + } ++ ++#ifdef BCMPCIE_OOB_HOST_WAKE ++int dhd_bus_oob_intr_register(dhd_pub_t *dhdp) ++{ ++ return dhdpcie_oob_intr_register(dhdp->bus); ++} ++ ++void dhd_bus_oob_intr_unregister(dhd_pub_t *dhdp) ++{ ++ dhdpcie_oob_intr_unregister(dhdp->bus); ++} ++ ++void dhd_bus_oob_intr_set(dhd_pub_t *dhdp, bool enable) ++{ ++ dhdpcie_oob_intr_set(dhdp->bus, enable); ++} ++#endif /* BCMPCIE_OOB_HOST_WAKE */ +diff -Nur a/drivers/net/wireless/bcmdhd/dhd_pcie.h c/drivers/net/wireless/bcmdhd/dhd_pcie.h +--- a/drivers/net/wireless/bcmdhd/dhd_pcie.h 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/dhd_pcie.h 2016-05-13 09:48:20.000000000 +0200 +@@ -3,7 +3,7 @@ + * + * $Copyright Open Broadcom Corporation$ + * +- * $Id: dhd_pcie.h 491657 2014-07-17 06:29:40Z $ ++ * $Id: dhd_pcie.h 506084 2014-10-02 15:34:59Z $ + */ + + +@@ -12,6 +12,15 @@ + + #include + #include ++#ifdef SUPPORT_LINKDOWN_RECOVERY ++#ifdef CONFIG_ARCH_MSM ++#ifdef CONFIG_ARCH_MSM8994 ++#include ++#else ++#include ++#endif ++#endif /* CONFIG_ARCH_MSM */ ++#endif /* SUPPORT_LINKDOWN_RECOVERY */ + + /* defines */ + +@@ -110,6 +119,11 @@ + uint32 dma_rxoffset; + volatile char *regs; /* pci device memory va */ + volatile char *tcm; /* pci device memory va */ ++ uint32 tcm_size; ++#ifdef CONFIG_ARCH_MSM8994 ++ uint32 bar1_win_base; ++ uint32 bar1_win_mask; ++#endif + osl_t *osh; + uint32 nvram_csm; /* Nvram checksum */ + uint16 pollrate; +@@ -138,7 +152,21 @@ + uint8 txmode_push; + uint32 max_sub_queues; + bool db1_for_mb; +- ++ bool suspended; ++#ifdef SUPPORT_LINKDOWN_RECOVERY ++#ifdef CONFIG_ARCH_MSM ++ struct msm_pcie_register_event pcie_event; ++ bool islinkdown; ++#endif /* CONFIG_ARCH_MSM */ ++#endif /* SUPPORT_LINKDOWN_RECOVERY */ ++#ifdef PCIE_TX_DEFERRAL ++ struct workqueue_struct *tx_wq; ++ struct work_struct create_flow_work; ++ struct work_struct delete_flow_work; ++ unsigned long *delete_flow_map; ++ struct sk_buff_head orphan_list; ++#endif /* PCIE_TX_DEFERRAL */ ++ bool irq_registered; + } dhd_bus_t; + + /* function declarations */ +@@ -148,21 +176,32 @@ + extern void dhdpcie_bus_unregister(void); + extern bool dhdpcie_chipmatch(uint16 vendor, uint16 device); + +-extern struct dhd_bus* dhdpcie_bus_attach(osl_t *osh, volatile char* regs, volatile char* tcm); ++extern struct dhd_bus* dhdpcie_bus_attach(osl_t *osh, volatile char* regs, ++ volatile char* tcm, uint32 tcm_size); + extern uint32 dhdpcie_bus_cfg_read_dword(struct dhd_bus *bus, uint32 addr, uint32 size); + extern void dhdpcie_bus_cfg_write_dword(struct dhd_bus *bus, uint32 addr, uint32 size, uint32 data); + extern void dhdpcie_bus_intr_disable(struct dhd_bus *bus); ++extern void dhdpcie_bus_remove_prep(struct dhd_bus *bus); + extern void dhdpcie_bus_release(struct dhd_bus *bus); + extern int32 dhdpcie_bus_isr(struct dhd_bus *bus); + extern void dhdpcie_free_irq(dhd_bus_t *bus); + extern int dhdpcie_bus_suspend(struct dhd_bus *bus, bool state); +-extern int dhdpcie_pci_suspend_resume(struct pci_dev *dev, bool state); ++extern int dhdpcie_pci_suspend_resume(struct dhd_bus *bus, bool state); ++#ifndef BCMPCIE_OOB_HOST_WAKE ++extern void dhdpcie_pme_active(osl_t *osh, bool enable); ++#endif /* !BCMPCIE_OOB_HOST_WAKE */ + extern int dhdpcie_start_host_pcieclock(dhd_bus_t *bus); + extern int dhdpcie_stop_host_pcieclock(dhd_bus_t *bus); + extern int dhdpcie_disable_device(dhd_bus_t *bus); + extern int dhdpcie_enable_device(dhd_bus_t *bus); + extern int dhdpcie_alloc_resource(dhd_bus_t *bus); + extern void dhdpcie_free_resource(dhd_bus_t *bus); ++extern int dhdpcie_bus_request_irq(struct dhd_bus *bus); ++#ifdef BCMPCIE_OOB_HOST_WAKE ++extern int dhdpcie_oob_intr_register(dhd_bus_t *bus); ++extern void dhdpcie_oob_intr_unregister(dhd_bus_t *bus); ++extern void dhdpcie_oob_intr_set(dhd_bus_t *bus, bool enable); ++#endif /* BCMPCIE_OOB_HOST_WAKE */ + + extern int dhd_buzzz_dump_dngl(dhd_bus_t *bus); + #endif /* dhd_pcie_h */ +diff -Nur a/drivers/net/wireless/bcmdhd/dhd_pcie_linux.c c/drivers/net/wireless/bcmdhd/dhd_pcie_linux.c +--- a/drivers/net/wireless/bcmdhd/dhd_pcie_linux.c 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/dhd_pcie_linux.c 2016-05-13 09:48:20.000000000 +0200 +@@ -3,7 +3,7 @@ + * + * $Copyright Open Broadcom Corporation$ + * +- * $Id: dhd_pcie_linux.c 491657 2014-07-17 06:29:40Z $ ++ * $Id: dhd_pcie_linux.c 506043 2014-10-02 12:29:45Z $ + */ + + +@@ -31,8 +31,12 @@ + #include + #include + #ifdef CONFIG_ARCH_MSM ++#ifdef CONFIG_ARCH_MSM8994 ++#include ++#else + #include + #endif ++#endif /* CONFIG_ARCH_MSM */ + + #define PCI_CFG_RETRY 10 + #define OS_HANDLE_MAGIC 0x1234abcd /* Magic # to recognize osh */ +@@ -73,6 +77,9 @@ + int irq; + char pciname[32]; + struct pci_saved_state* state; ++#ifdef BCMPCIE_OOB_HOST_WAKE ++ void *os_cxt; /* Pointer to per-OS private data */ ++#endif /* BCMPCIE_OOB_HOST_WAKE */ + } dhdpcie_info_t; + + +@@ -86,6 +93,17 @@ + struct tasklet_struct tuning_tasklet; + }; + ++#ifdef BCMPCIE_OOB_HOST_WAKE ++typedef struct dhdpcie_os_info { ++ int oob_irq_num; /* valid when hardware or software oob in use */ ++ unsigned long oob_irq_flags; /* valid when hardware or software oob in use */ ++ bool oob_irq_registered; ++ bool oob_irq_enabled; ++ bool oob_irq_wake_enabled; ++ spinlock_t oob_irq_spinlock; ++ void *dev; /* handle to the underlying device */ ++} dhdpcie_os_info_t; ++#endif /* BCMPCIE_OOB_HOST_WAKE */ + + /* function declarations */ + static int __devinit +@@ -96,6 +114,12 @@ + static irqreturn_t dhdpcie_isr(int irq, void *arg); + /* OS Routine functions for PCI suspend/resume */ + ++#if defined(MULTIPLE_SUPPLICANT) ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) ++DEFINE_MUTEX(_dhd_sdio_mutex_lock_); ++#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) */ ++#endif ++ + static int dhdpcie_pci_suspend(struct pci_dev *dev, pm_message_t state); + static int dhdpcie_set_suspend_resume(struct pci_dev *dev, bool state); + static int dhdpcie_pci_resume(struct pci_dev *dev); +@@ -129,19 +153,6 @@ + + int dhdpcie_init_succeeded = FALSE; + +-static void dhdpcie_pme_active(struct pci_dev *pdev, bool enable) +-{ +- uint16 pmcsr; +- +- pci_read_config_word(pdev, pdev->pm_cap + PCI_PM_CTRL, &pmcsr); +- /* Clear PME Status by writing 1 to it and enable PME# */ +- pmcsr |= PCI_PM_CTRL_PME_STATUS | PCI_PM_CTRL_PME_ENABLE; +- if (!enable) +- pmcsr &= ~PCI_PM_CTRL_PME_ENABLE; +- +- pci_write_config_word(pdev, pdev->pm_cap + PCI_PM_CTRL, pmcsr); +-} +- + static int dhdpcie_set_suspend_resume(struct pci_dev *pdev, bool state) + { + int ret = 0; +@@ -155,13 +166,20 @@ + /* When firmware is not loaded do the PCI bus */ + /* suspend/resume only */ + if (bus && (bus->dhd->busstate == DHD_BUS_DOWN) && +- !bus->dhd->dongle_reset) { +- ret = dhdpcie_pci_suspend_resume(bus->dev, state); +- return ret; +- } ++#ifdef CONFIG_MACH_UNIVERSAL5433 ++ /* RB:34285 check_rev() : return 1 - new rev., 0 - old rev. */ ++ (!check_rev() || (check_rev() && !bus->dhd->dongle_reset))) ++#else ++ !bus->dhd->dongle_reset) ++#endif /* CONFIG_MACH_UNIVERSAL5433 */ ++ { ++ ret = dhdpcie_pci_suspend_resume(bus, state); ++ return ret; ++ } + + if (bus && ((bus->dhd->busstate == DHD_BUS_SUSPEND)|| +- (bus->dhd->busstate == DHD_BUS_DATA))) { ++ (bus->dhd->busstate == DHD_BUS_DATA)) && ++ (bus->suspended != state)) { + + ret = dhdpcie_bus_suspend(bus, state); + } +@@ -183,7 +201,6 @@ + { + int ret; + DHD_TRACE_HW4(("%s: Enter\n", __FUNCTION__)); +- dhdpcie_pme_active(dev, TRUE); + pci_save_state(dev); + pci_enable_wake(dev, PCI_D0, TRUE); + pci_disable_device(dev); +@@ -211,18 +228,25 @@ + printf("%s:pci_set_power_state error %d \n", __FUNCTION__, err); + return err; + } +- dhdpcie_pme_active(dev, FALSE); + return err; + } + +-int dhdpcie_pci_suspend_resume(struct pci_dev *dev, bool state) ++int dhdpcie_pci_suspend_resume(struct dhd_bus *bus, bool state) + { + int rc; ++ struct pci_dev *dev = bus->dev; + +- if (state) ++ if (state) { ++#ifndef BCMPCIE_OOB_HOST_WAKE ++ dhdpcie_pme_active(bus->osh, state); ++#endif /* BCMPCIE_OOB_HOST_WAKE */ + rc = dhdpcie_suspend_dev(dev); +- else ++ } else { + rc = dhdpcie_resume_dev(dev); ++#ifndef BCMPCIE_OOB_HOST_WAKE ++ dhdpcie_pme_active(bus->osh, state); ++#endif /* BCMPCIE_OOB_HOST_WAKE */ ++ } + return rc; + } + +@@ -300,6 +324,11 @@ + return -ENODEV; + } + ++#ifdef BCMPCIE_DISABLE_ASYNC_SUSPEND ++ /* disable async suspend */ ++ device_disable_async_suspend(&pdev->dev); ++#endif /* BCMPCIE_DISABLE_ASYNC_SUSPEND */ ++ + DHD_TRACE(("%s: PCIe Enumeration done!!\n", __FUNCTION__)); + return 0; + } +@@ -325,14 +354,52 @@ + osl_t *osh = NULL; + dhdpcie_info_t *pch = NULL; + dhd_bus_t *bus = NULL; ++#ifdef PCIE_TX_DEFERRAL ++ struct sk_buff *skb; ++#endif + + DHD_TRACE(("%s Enter\n", __FUNCTION__)); ++ ++#if defined(MULTIPLE_SUPPLICANT) ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) ++ if (mutex_is_locked(&_dhd_sdio_mutex_lock_) == 0) { ++ DHD_ERROR(("%s : no mutex held. set lock\n", __FUNCTION__)); ++ } ++ else { ++ DHD_ERROR(("%s : mutex is locked!. wait for unlocking\n", __FUNCTION__)); ++ } ++ mutex_lock(&_dhd_sdio_mutex_lock_); ++#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) */ ++#endif ++ + pch = pci_get_drvdata(pdev); + bus = pch->bus; + osh = pch->osh; + ++#ifdef PCIE_TX_DEFERRAL ++ if (bus->tx_wq) ++ destroy_workqueue(bus->tx_wq); ++ skb = skb_dequeue(&bus->orphan_list); ++ while (skb) { ++ PKTCFREE(osh, skb, TRUE); ++ skb = skb_dequeue(&bus->orphan_list); ++ } ++#endif ++ ++#ifdef SUPPORT_LINKDOWN_RECOVERY ++#ifdef CONFIG_ARCH_MSM ++ if (bus) ++ msm_pcie_deregister_event(&bus->pcie_event); ++#endif /* CONFIG_ARCH_MSM */ ++#endif /* SUPPORT_LINKDOWN_RECOVERY */ ++ ++ dhdpcie_bus_remove_prep(bus); + dhdpcie_bus_release(bus); + pci_disable_device(pdev); ++#ifdef BCMPCIE_OOB_HOST_WAKE ++ /* pcie os info detach */ ++ MFREE(osh, pch->os_cxt, sizeof(dhdpcie_os_info_t)); ++#endif /* BCMPCIE_OOB_HOST_WAKE */ + /* pcie info detach */ + dhdpcie_detach(pch); + /* osl detach */ +@@ -340,6 +407,13 @@ + + dhdpcie_init_succeeded = FALSE; + ++#if defined(MULTIPLE_SUPPLICANT) ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) ++ mutex_unlock(&_dhd_sdio_mutex_lock_); ++ DHD_ERROR(("%s : the lock is released.\n", __FUNCTION__)); ++#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) */ ++#endif /* LINUX */ ++ + DHD_TRACE(("%s Exit\n", __FUNCTION__)); + + return; +@@ -359,6 +433,7 @@ + DHD_ERROR(("%s: request_irq() failed\n", __FUNCTION__)); + return -1; + } ++ bus->irq_registered = TRUE; + + DHD_TRACE(("%s %s\n", __FUNCTION__, dhdpcie_info->pciname)); + +@@ -416,8 +491,9 @@ + } + + dhdpcie_info->regs = (volatile char *) REG_MAP(bar0_addr, DONGLE_REG_MAP_SIZE); +- dhdpcie_info->tcm = (volatile char *) REG_MAP(bar1_addr, DONGLE_TCM_MAP_SIZE); +- dhdpcie_info->tcm_size = DONGLE_TCM_MAP_SIZE; ++ dhdpcie_info->tcm_size = ++ (bar1_size < DONGLE_TCM_MAP_SIZE) ? bar1_size : DONGLE_TCM_MAP_SIZE; ++ dhdpcie_info->tcm = (volatile char *) REG_MAP(bar1_addr, dhdpcie_info->tcm_size); + + if (!dhdpcie_info->regs || !dhdpcie_info->tcm) { + DHD_ERROR(("%s:ioremap() failed\n", __FUNCTION__)); +@@ -478,6 +554,103 @@ + + } + ++#ifdef SUPPORT_LINKDOWN_RECOVERY ++#ifdef CONFIG_ARCH_MSM ++void dhdpcie_linkdown_cb(struct msm_pcie_notify *noti) ++{ ++ struct pci_dev *pdev = (struct pci_dev *)noti->user; ++ dhdpcie_info_t *pch = NULL; ++ ++ if (pdev) { ++ pch = pci_get_drvdata(pdev); ++ if (pch) { ++ dhd_bus_t *bus = pch->bus; ++ if (bus) { ++ dhd_pub_t *dhd = bus->dhd; ++ if (dhd) { ++ DHD_ERROR(("%s: Event HANG send up " ++ "due to PCIe linkdown\n", ++ __FUNCTION__)); ++ bus->islinkdown = TRUE; ++ DHD_OS_WAKE_LOCK(dhd); ++ dhd_os_check_hang(dhd, 0, -ETIMEDOUT); ++ } ++ } ++ } ++ } ++ ++} ++#endif /* CONFIG_ARCH_MSM */ ++#endif /* SUPPORT_LINKDOWN_RECOVERY */ ++ ++#ifdef PCIE_TX_DEFERRAL ++static void dhd_pcie_create_flow_worker(struct work_struct *worker) ++{ ++ dhd_bus_t *bus; ++ struct sk_buff *skb; ++ uint16 ifidx, flowid; ++ flow_queue_t *queue; ++ flow_ring_node_t *flow_ring_node; ++ unsigned long flags; ++ ++ bus = container_of(worker, dhd_bus_t, create_flow_work); ++ skb = skb_dequeue(&bus->orphan_list); ++ while (skb) { ++ ifidx = DHD_PKTTAG_FLOWID((dhd_pkttag_fr_t*)PKTTAG(skb)); ++ if (BCME_OK != dhd_flowid_update(bus->dhd, ifidx, ++ bus->dhd->flow_prio_map[(PKTPRIO(skb))], skb)) { ++ PKTCFREE(bus->dhd->osh, skb, TRUE); ++ skb = skb_dequeue(&bus->orphan_list); ++ continue; ++ } ++ flowid = DHD_PKTTAG_FLOWID((dhd_pkttag_fr_t*)PKTTAG(skb)); ++ flow_ring_node = DHD_FLOW_RING(bus->dhd, flowid); ++ queue = &flow_ring_node->queue; ++ DHD_FLOWRING_LOCK(flow_ring_node->lock, flags); ++ if ((flowid >= bus->dhd->num_flow_rings) || ++ (!flow_ring_node->active) || ++ (flow_ring_node->status == FLOW_RING_STATUS_DELETE_PENDING)) { ++ DHD_FLOWRING_UNLOCK(flow_ring_node->lock, flags); ++ DHD_ERROR(("%s: Dropping pkt flowid %d, status %d active %d\n", ++ __FUNCTION__, flowid, flow_ring_node->status, ++ flow_ring_node->active)); ++ PKTCFREE(bus->dhd->osh, skb, TRUE); ++ skb = skb_dequeue(&bus->orphan_list); ++ continue; ++ } ++ if (BCME_OK != dhd_flow_queue_enqueue(bus->dhd, queue, skb)) { ++ DHD_FLOWRING_UNLOCK(flow_ring_node->lock, flags); ++ PKTCFREE(bus->dhd->osh, skb, TRUE); ++ skb = skb_dequeue(&bus->orphan_list); ++ continue; ++ } ++ DHD_FLOWRING_UNLOCK(flow_ring_node->lock, flags); ++ ++ if (flow_ring_node->status == FLOW_RING_STATUS_OPEN) ++ dhd_bus_schedule_queue(bus, flowid, FALSE); ++ ++ skb = skb_dequeue(&bus->orphan_list); ++ } ++} ++ ++static void dhd_pcie_delete_flow_worker(struct work_struct *worker) ++{ ++ dhd_bus_t *bus; ++ uint16 flowid; ++ ++ bus = container_of(worker, dhd_bus_t, delete_flow_work); ++ for_each_set_bit(flowid, bus->delete_flow_map, bus->dhd->num_flow_rings) { ++ clear_bit(flowid, bus->delete_flow_map); ++ dhd_bus_flow_ring_delete_response(bus, flowid, BCME_OK); ++ } ++} ++ ++#endif /* PCIE_TX_DEFERRAL */ ++ ++#if defined(MULTIPLE_SUPPLICANT) ++extern void wl_android_post_init(void); // terence 20120530: fix critical section in dhd_open and dhdsdio_probe ++#endif ++ + int dhdpcie_init(struct pci_dev *pdev) + { + +@@ -485,6 +658,21 @@ + dhd_bus_t *bus = NULL; + dhdpcie_info_t *dhdpcie_info = NULL; + wifi_adapter_info_t *adapter = NULL; ++#ifdef BCMPCIE_OOB_HOST_WAKE ++ dhdpcie_os_info_t *dhdpcie_osinfo = NULL; ++#endif /* BCMPCIE_OOB_HOST_WAKE */ ++ ++#if defined(MULTIPLE_SUPPLICANT) ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) ++ if (mutex_is_locked(&_dhd_sdio_mutex_lock_) == 0) { ++ DHD_ERROR(("%s : no mutex held. set lock\n", __FUNCTION__)); ++ } ++ else { ++ DHD_ERROR(("%s : mutex is locked!. wait for unlocking\n", __FUNCTION__)); ++ } ++ mutex_lock(&_dhd_sdio_mutex_lock_); ++#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) */ ++#endif + + do { + /* osl attach */ +@@ -511,6 +699,27 @@ + dhdpcie_info->osh = osh; + dhdpcie_info->dev = pdev; + ++#ifdef BCMPCIE_OOB_HOST_WAKE ++ /* allocate OS speicific structure */ ++ dhdpcie_osinfo = MALLOC(osh, sizeof(dhdpcie_os_info_t)); ++ if (dhdpcie_osinfo == NULL) { ++ DHD_ERROR(("%s: MALLOC of dhdpcie_os_info_t failed\n", ++ __FUNCTION__)); ++ break; ++ } ++ bzero(dhdpcie_osinfo, sizeof(dhdpcie_os_info_t)); ++ dhdpcie_info->os_cxt = (void *)dhdpcie_osinfo; ++ ++ /* Initialize host wake IRQ */ ++ spin_lock_init(&dhdpcie_osinfo->oob_irq_spinlock); ++ /* Get customer specific host wake IRQ parametres: IRQ number as IRQ type */ ++ dhdpcie_osinfo->oob_irq_num = wifi_platform_get_irq_number(adapter, ++ &dhdpcie_osinfo->oob_irq_flags); ++ if (dhdpcie_osinfo->oob_irq_num < 0) { ++ DHD_ERROR(("%s: Host OOB irq is not defined\n", __FUNCTION__)); ++ } ++#endif /* BCMPCIE_OOB_HOST_WAKE */ ++ + /* Find the PCI resources, verify the */ + /* vendor and device ID, map BAR regions and irq, update in structures */ + if (dhdpcie_scan_resource(dhdpcie_info)) { +@@ -520,7 +729,8 @@ + } + + /* Bus initialization */ +- bus = dhdpcie_bus_attach(osh, dhdpcie_info->regs, dhdpcie_info->tcm); ++ bus = dhdpcie_bus_attach(osh, dhdpcie_info->regs, ++ dhdpcie_info->tcm, dhdpcie_info->tcm_size); + if (!bus) { + DHD_ERROR(("%s:dhdpcie_bus_attach() failed\n", __FUNCTION__)); + break; +@@ -529,6 +739,18 @@ + dhdpcie_info->bus = bus; + dhdpcie_info->bus->dev = pdev; + ++#ifdef SUPPORT_LINKDOWN_RECOVERY ++#ifdef CONFIG_ARCH_MSM ++ bus->pcie_event.events = MSM_PCIE_EVENT_LINKDOWN; ++ bus->pcie_event.user = pdev; ++ bus->pcie_event.mode = MSM_PCIE_TRIGGER_CALLBACK; ++ bus->pcie_event.callback = dhdpcie_linkdown_cb; ++ bus->pcie_event.options = MSM_PCIE_CONFIG_NO_RECOVERY; ++ msm_pcie_register_event(&bus->pcie_event); ++ bus->islinkdown = FALSE; ++#endif /* CONFIG_ARCH_MSM */ ++#endif /* SUPPORT_LINKDOWN_RECOVERY */ ++ + if (bus->intr) { + /* Register interrupt callback, but mask it (not operational yet). */ + DHD_INTR(("%s: Registering and masking interrupts\n", __FUNCTION__)); +@@ -544,16 +766,29 @@ + "due to polling mode\n", __FUNCTION__)); + } + ++#if 0 // terence 20150325: fix for WPA/WPA2 4-way handshake fail in hostapd + if (dhd_download_fw_on_driverload) { + if (dhd_bus_start(bus->dhd)) { + DHD_ERROR(("%s: dhd_bud_start() failed\n", __FUNCTION__)); + break; + } + } ++#endif + + /* set private data for pci_dev */ + pci_set_drvdata(pdev, dhdpcie_info); + ++#ifdef PCIE_TX_DEFERRAL ++ bus->tx_wq = create_singlethread_workqueue("bcmdhd_tx"); ++ if (bus->tx_wq == NULL) { ++ DHD_ERROR(("%s workqueue creation failed\n", __FUNCTION__)); ++ break; ++ } ++ INIT_WORK(&bus->create_flow_work, dhd_pcie_create_flow_worker); ++ INIT_WORK(&bus->delete_flow_work, dhd_pcie_delete_flow_worker); ++ skb_queue_head_init(&bus->orphan_list); ++#endif /* PCIE_TX_DEFERRAL */ ++ + /* Attach to the OS network interface */ + DHD_TRACE(("%s(): Calling dhd_register_if() \n", __FUNCTION__)); + if (dhd_register_if(bus->dhd, 0, TRUE)) { +@@ -563,6 +798,14 @@ + + dhdpcie_init_succeeded = TRUE; + ++#if defined(MULTIPLE_SUPPLICANT) ++ wl_android_post_init(); // terence 20120530: fix critical section in dhd_open and dhdsdio_probe ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) ++ mutex_unlock(&_dhd_sdio_mutex_lock_); ++ DHD_ERROR(("%s : the lock is released.\n", __FUNCTION__)); ++#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ ++#endif ++ + DHD_TRACE(("%s:Exit - SUCCESS \n", __FUNCTION__)); + return 0; /* return SUCCESS */ + +@@ -572,6 +815,11 @@ + if (bus) + dhdpcie_bus_release(bus); + ++#ifdef BCMPCIE_OOB_HOST_WAKE ++ if (dhdpcie_osinfo) ++ MFREE(osh, dhdpcie_osinfo, sizeof(dhdpcie_os_info_t)); ++#endif /* BCMPCIE_OOB_HOST_WAKE */ ++ + if (dhdpcie_info) + dhdpcie_detach(dhdpcie_info); + pci_disable_device(pdev); +@@ -579,6 +827,12 @@ + osl_detach(osh); + + dhdpcie_init_succeeded = FALSE; ++#if defined(MULTIPLE_SUPPLICANT) ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) ++ mutex_unlock(&_dhd_sdio_mutex_lock_); ++ DHD_ERROR(("%s : the lock is released.\n", __FUNCTION__)); ++#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ ++#endif + + DHD_TRACE(("%s:Exit - FAILURE \n", __FUNCTION__)); + +@@ -592,9 +846,10 @@ + struct pci_dev *pdev = NULL; + + DHD_TRACE(("%s: freeing up the IRQ\n", __FUNCTION__)); +- if (bus) { ++ if (bus && bus->irq_registered) { + pdev = bus->dev; + free_irq(pdev->irq, bus); ++ bus->irq_registered = FALSE; + } + DHD_TRACE(("%s: Exit\n", __FUNCTION__)); + return; +@@ -633,9 +888,11 @@ + dhdpcie_start_host_pcieclock(dhd_bus_t *bus) + { + int ret = 0; ++#ifdef CONFIG_ARCH_MSM + #ifdef SUPPORT_LINKDOWN_RECOVERY + int options = 0; + #endif /* SUPPORT_LINKDOWN_RECOVERY */ ++#endif /* CONFIG_ARCH_MSM */ + DHD_TRACE(("%s Enter:\n", __FUNCTION__)); + + if (bus == NULL) +@@ -644,13 +901,13 @@ + if (bus->dev == NULL) + return BCME_ERROR; + +-#if defined(CONFIG_ARCH_MSM) ++#ifdef CONFIG_ARCH_MSM + #ifdef SUPPORT_LINKDOWN_RECOVERY + if (bus->islinkdown) { + options = MSM_PCIE_CONFIG_NO_CFG_RESTORE; + } + ret = msm_pcie_pm_control(MSM_PCIE_RESUME, bus->dev->bus->number, +- NULL, NULL, options); ++ bus->dev, NULL, options); + if (bus->islinkdown && !ret) { + msm_pcie_recover_config(bus->dev); + if (bus->dhd) +@@ -659,7 +916,7 @@ + } + #else + ret = msm_pcie_pm_control(MSM_PCIE_RESUME, bus->dev->bus->number, +- NULL, NULL, 0); ++ bus->dev, NULL, 0); + #endif /* SUPPORT_LINKDOWN_RECOVERY */ + if (ret) { + DHD_ERROR(("%s Failed to bring up PCIe link\n", __FUNCTION__)); +@@ -677,9 +934,11 @@ + { + int ret = 0; + ++#ifdef CONFIG_ARCH_MSM + #ifdef SUPPORT_LINKDOWN_RECOVERY + int options = 0; +-#endif ++#endif /* SUPPORT_LINKDOWN_RECOVERY */ ++#endif /* CONFIG_ARCH_MSM */ + DHD_TRACE(("%s Enter:\n", __FUNCTION__)); + + if (bus == NULL) +@@ -688,16 +947,16 @@ + if (bus->dev == NULL) + return BCME_ERROR; + +-#if defined(CONFIG_ARCH_MSM) ++#ifdef CONFIG_ARCH_MSM + #ifdef SUPPORT_LINKDOWN_RECOVERY + if (bus->islinkdown) + options = MSM_PCIE_CONFIG_NO_CFG_RESTORE | MSM_PCIE_CONFIG_LINKDOWN; + + ret = msm_pcie_pm_control(MSM_PCIE_SUSPEND, bus->dev->bus->number, +- NULL, NULL, options); ++ bus->dev, NULL, options); + #else + ret = msm_pcie_pm_control(MSM_PCIE_SUSPEND, bus->dev->bus->number, +- NULL, NULL, 0); ++ bus->dev, NULL, 0); + #endif /* SUPPORT_LINKDOWN_RECOVERY */ + if (ret) { + DHD_ERROR(("Failed to stop PCIe link\n")); +@@ -741,18 +1000,33 @@ + if (pch == NULL) + return BCME_ERROR; + +-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)) ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)) ++ /* Updated with pci_load_and_free_saved_state to compatible ++ * with kernel 3.14 or higher ++ */ ++ if (pci_load_and_free_saved_state(bus->dev, &pch->state)) ++ pci_disable_device(bus->dev); ++ else ++#elif ((LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)) && \ ++ (LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0))) + if (pci_load_saved_state(bus->dev, pch->state)) + pci_disable_device(bus->dev); +- else { +-#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) */ ++ else ++#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) and ++ * (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)) && \ ++ * (LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0) ++ */ ++ { + pci_restore_state(bus->dev); + ret = pci_enable_device(bus->dev); + if (!ret) + pci_set_master(bus->dev); + #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)) + } +-#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) */ ++#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) and ++ * (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)) && \ ++ * (LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0) ++ */ + + if (ret) + pci_disable_device(bus->dev); +@@ -804,8 +1078,9 @@ + } + + bus->regs = dhdpcie_info->regs; +- dhdpcie_info->tcm = (volatile char *) REG_MAP(bar1_addr, DONGLE_TCM_MAP_SIZE); +- dhdpcie_info->tcm_size = DONGLE_TCM_MAP_SIZE; ++ dhdpcie_info->tcm_size = ++ (bar1_size < DONGLE_TCM_MAP_SIZE) ? bar1_size : DONGLE_TCM_MAP_SIZE; ++ dhdpcie_info->tcm = (volatile char *) REG_MAP(bar1_addr, dhdpcie_info->tcm_size); + if (!dhdpcie_info->tcm) { + DHD_ERROR(("%s: ioremap() for regs is failed\n", __FUNCTION__)); + REG_UNMAP(dhdpcie_info->regs); +@@ -814,6 +1089,7 @@ + } + + bus->tcm = dhdpcie_info->tcm; ++ bus->tcm_size = dhdpcie_info->tcm_size; + + DHD_TRACE(("%s:Phys addr : reg space = %p base addr 0x"PRINTF_RESOURCE" \n", + __FUNCTION__, dhdpcie_info->regs, bar0_addr)); +@@ -857,3 +1133,183 @@ + bus->tcm = NULL; + } + } ++ ++int ++dhdpcie_bus_request_irq(struct dhd_bus *bus) ++{ ++ dhdpcie_info_t *dhdpcie_info; ++ int ret = 0; ++ ++ if (bus == NULL) { ++ DHD_ERROR(("%s: bus is NULL\n", __FUNCTION__)); ++ return BCME_ERROR; ++ } ++ ++ if (bus->dev == NULL) { ++ DHD_ERROR(("%s: bus->dev is NULL\n", __FUNCTION__)); ++ return BCME_ERROR; ++ } ++ ++ dhdpcie_info = pci_get_drvdata(bus->dev); ++ if (dhdpcie_info == NULL) { ++ DHD_ERROR(("%s: dhdpcie_info is NULL\n", __FUNCTION__)); ++ return BCME_ERROR; ++ } ++ ++ if (bus->intr) { ++ /* Register interrupt callback, but mask it (not operational yet). */ ++ DHD_INTR(("%s: Registering and masking interrupts\n", __FUNCTION__)); ++ dhdpcie_bus_intr_disable(bus); ++ ret = dhdpcie_request_irq(dhdpcie_info); ++ if (ret) { ++ DHD_ERROR(("%s: request_irq() failed, ret=%d\n", ++ __FUNCTION__, ret)); ++ return ret; ++ } ++ } ++ ++ return ret; ++} ++ ++#ifdef BCMPCIE_OOB_HOST_WAKE ++void dhdpcie_oob_intr_set(dhd_bus_t *bus, bool enable) ++{ ++ unsigned long flags; ++ dhdpcie_info_t *pch; ++ dhdpcie_os_info_t *dhdpcie_osinfo; ++ ++ if (bus == NULL) { ++ DHD_ERROR(("%s: bus is NULL\n", __FUNCTION__)); ++ return; ++ } ++ ++ if (bus->dev == NULL) { ++ DHD_ERROR(("%s: bus->dev is NULL\n", __FUNCTION__)); ++ return; ++ } ++ ++ pch = pci_get_drvdata(bus->dev); ++ if (pch == NULL) { ++ DHD_ERROR(("%s: pch is NULL\n", __FUNCTION__)); ++ return; ++ } ++ ++ dhdpcie_osinfo = (dhdpcie_os_info_t *)pch->os_cxt; ++ spin_lock_irqsave(&dhdpcie_osinfo->oob_irq_spinlock, flags); ++ if ((dhdpcie_osinfo->oob_irq_enabled != enable) && ++ (dhdpcie_osinfo->oob_irq_num > 0)) { ++ if (enable) ++ enable_irq(dhdpcie_osinfo->oob_irq_num); ++ else ++ disable_irq_nosync(dhdpcie_osinfo->oob_irq_num); ++ dhdpcie_osinfo->oob_irq_enabled = enable; ++ } ++ spin_unlock_irqrestore(&dhdpcie_osinfo->oob_irq_spinlock, flags); ++} ++ ++static irqreturn_t wlan_oob_irq(int irq, void *data) ++{ ++ dhd_bus_t *bus; ++ DHD_TRACE(("%s: IRQ Triggered\n", __FUNCTION__)); ++ bus = (dhd_bus_t *)data; ++ if (bus->dhd->up && bus->suspended) { ++ DHD_OS_OOB_IRQ_WAKE_LOCK_TIMEOUT(bus->dhd, OOB_WAKE_LOCK_TIMEOUT); ++ } ++ return IRQ_HANDLED; ++} ++ ++int dhdpcie_oob_intr_register(dhd_bus_t *bus) ++{ ++ int err = 0; ++ dhdpcie_info_t *pch; ++ dhdpcie_os_info_t *dhdpcie_osinfo; ++ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ if (bus == NULL) { ++ DHD_ERROR(("%s: bus is NULL\n", __FUNCTION__)); ++ return -EINVAL; ++ } ++ ++ if (bus->dev == NULL) { ++ DHD_ERROR(("%s: bus->dev is NULL\n", __FUNCTION__)); ++ return -EINVAL; ++ } ++ ++ pch = pci_get_drvdata(bus->dev); ++ if (pch == NULL) { ++ DHD_ERROR(("%s: pch is NULL\n", __FUNCTION__)); ++ return -EINVAL; ++ } ++ ++ dhdpcie_osinfo = (dhdpcie_os_info_t *)pch->os_cxt; ++ if (dhdpcie_osinfo->oob_irq_registered) { ++ DHD_ERROR(("%s: irq is already registered\n", __FUNCTION__)); ++ return -EBUSY; ++ } ++ ++ if (dhdpcie_osinfo->oob_irq_num > 0) { ++ DHD_INFO_HW4(("%s OOB irq=%d flags=%X \n", __FUNCTION__, ++ (int)dhdpcie_osinfo->oob_irq_num, ++ (int)dhdpcie_osinfo->oob_irq_flags)); ++ err = request_irq(dhdpcie_osinfo->oob_irq_num, wlan_oob_irq, ++ dhdpcie_osinfo->oob_irq_flags, "dhdpcie_host_wake", ++ bus); ++ if (err) { ++ DHD_ERROR(("%s: request_irq failed with %d\n", ++ __FUNCTION__, err)); ++ return err; ++ } ++ err = enable_irq_wake(dhdpcie_osinfo->oob_irq_num); ++ if (!err) ++ dhdpcie_osinfo->oob_irq_wake_enabled = TRUE; ++ dhdpcie_osinfo->oob_irq_enabled = TRUE; ++ } ++ ++ dhdpcie_osinfo->oob_irq_registered = TRUE; ++ ++ return err; ++} ++ ++void dhdpcie_oob_intr_unregister(dhd_bus_t *bus) ++{ ++ int err = 0; ++ dhdpcie_info_t *pch; ++ dhdpcie_os_info_t *dhdpcie_osinfo; ++ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ if (bus == NULL) { ++ DHD_ERROR(("%s: bus is NULL\n", __FUNCTION__)); ++ return; ++ } ++ ++ if (bus->dev == NULL) { ++ DHD_ERROR(("%s: bus->dev is NULL\n", __FUNCTION__)); ++ return; ++ } ++ ++ pch = pci_get_drvdata(bus->dev); ++ if (pch == NULL) { ++ DHD_ERROR(("%s: pch is NULL\n", __FUNCTION__)); ++ return; ++ } ++ ++ dhdpcie_osinfo = (dhdpcie_os_info_t *)pch->os_cxt; ++ if (!dhdpcie_osinfo->oob_irq_registered) { ++ DHD_ERROR(("%s: irq is not registered\n", __FUNCTION__)); ++ return; ++ } ++ if (dhdpcie_osinfo->oob_irq_num > 0) { ++ if (dhdpcie_osinfo->oob_irq_wake_enabled) { ++ err = disable_irq_wake(dhdpcie_osinfo->oob_irq_num); ++ if (!err) ++ dhdpcie_osinfo->oob_irq_wake_enabled = FALSE; ++ } ++ if (dhdpcie_osinfo->oob_irq_enabled) { ++ disable_irq(dhdpcie_osinfo->oob_irq_num); ++ dhdpcie_osinfo->oob_irq_enabled = FALSE; ++ } ++ free_irq(dhdpcie_osinfo->oob_irq_num, bus); ++ } ++ dhdpcie_osinfo->oob_irq_registered = FALSE; ++} ++#endif /* BCMPCIE_OOB_HOST_WAKE */ +diff -Nur a/drivers/net/wireless/bcmdhd/dhd_pno.h c/drivers/net/wireless/bcmdhd/dhd_pno.h +--- a/drivers/net/wireless/bcmdhd/dhd_pno.h 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/dhd_pno.h 2016-05-13 09:48:20.000000000 +0200 +@@ -230,14 +230,18 @@ + extern int dhd_pno_event_handler(dhd_pub_t *dhd, wl_event_msg_t *event, void *event_data); + extern int dhd_pno_init(dhd_pub_t *dhd); + extern int dhd_pno_deinit(dhd_pub_t *dhd); +-#endif ++#endif + +-#if (defined(NDISVER) && (NDISVER >= 0x0630)) && defined(PNO_SUPPORT) ++#if defined(NDISVER) ++#if defined(PNO_SUPPORT) ++#if (NDISVER >= 0x0630) + extern int dhd_pno_cfg(dhd_pub_t *dhd, wl_pfn_cfg_t *pcfg); + extern int dhd_pno_suspend(dhd_pub_t *dhd, int pfn_suspend); + extern int dhd_pno_set_add(dhd_pub_t *dhd, wl_pfn_t *netinfo, int nssid, ushort scan_fr, + ushort slowscan_fr, uint8 pno_repeat, uint8 pno_freq_expo_max, int16 flags); + extern int dhd_pno_enable(dhd_pub_t *dhd, int pfn_enabled); + extern int dhd_pno_clean(dhd_pub_t *dhd); +-#endif /* (defined(NDISVER) && (NDISVER >= 0x0630)) && defined(PNO_SUPPORT) */ ++#endif /* #if (NDISVER >= 0x0630) */ ++#endif /* #if defined(PNO_SUPPORT) */ ++#endif /* #if defined(NDISVER) */ + #endif /* __DHD_PNO_H__ */ +diff -Nur a/drivers/net/wireless/bcmdhd/dhd_proto.h c/drivers/net/wireless/bcmdhd/dhd_proto.h +--- a/drivers/net/wireless/bcmdhd/dhd_proto.h 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/dhd_proto.h 2016-05-13 09:48:20.000000000 +0200 +@@ -6,7 +6,7 @@ + * + * $Copyright Open Broadcom Corporation$ + * +- * $Id: dhd_proto.h 490409 2014-07-10 16:34:27Z $ ++ * $Id: dhd_proto.h 499674 2014-08-29 21:56:23Z $ + */ + + #ifndef _dhd_proto_h_ +@@ -18,8 +18,10 @@ + #include + #endif + ++#define DEFAULT_IOCTL_RESP_TIMEOUT 2000 + #ifndef IOCTL_RESP_TIMEOUT +-#define IOCTL_RESP_TIMEOUT 2000 /* In milli second default value for Production FW */ ++/* In milli second default value for Production FW */ ++#define IOCTL_RESP_TIMEOUT DEFAULT_IOCTL_RESP_TIMEOUT + #endif /* IOCTL_RESP_TIMEOUT */ + + #ifndef MFG_IOCTL_RESP_TIMEOUT +@@ -83,8 +85,8 @@ + uint reorder_info_len, void **pkt, uint32 *free_buf_count); + + #ifdef BCMPCIE +-extern int dhd_prot_process_msgbuf_txcpl(dhd_pub_t *dhd); +-extern int dhd_prot_process_msgbuf_rxcpl(dhd_pub_t *dhd); ++extern bool dhd_prot_process_msgbuf_txcpl(dhd_pub_t *dhd, uint bound); ++extern bool dhd_prot_process_msgbuf_rxcpl(dhd_pub_t *dhd, uint bound); + extern int dhd_prot_process_ctrlbuf(dhd_pub_t * dhd); + extern bool dhd_prot_dtohsplit(dhd_pub_t * dhd); + extern int dhd_post_dummy_msg(dhd_pub_t *dhd); +diff -Nur a/drivers/net/wireless/bcmdhd/dhd_sdio.c c/drivers/net/wireless/bcmdhd/dhd_sdio.c +--- a/drivers/net/wireless/bcmdhd/dhd_sdio.c 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/dhd_sdio.c 2016-09-30 01:21:10.137991163 +0200 +@@ -3,7 +3,7 @@ + * + * $Copyright Open Broadcom Corporation$ + * +- * $Id: dhd_sdio.c 489913 2014-07-08 18:57:48Z $ ++ * $Id: dhd_sdio.c 506046 2014-10-02 12:40:12Z $ + */ + + #include +@@ -83,7 +83,7 @@ + #define DHD_TXMINMAX 1 /* Max tx frames if rx still pending */ + + #define MEMBLOCK 2048 /* Block size used for downloading of dongle image */ +-#define MAX_NVRAMBUF_SIZE 4096 /* max nvram buf size */ ++#define MAX_NVRAMBUF_SIZE (16 * 1024) /* max nvram buf size */ + #define MAX_DATA_BUF (64 * 1024) /* Must be large enough to hold biggest possible glom */ + + #ifndef DHD_FIRSTREAD +@@ -145,13 +145,18 @@ + */ + #define PKTFREE2() if ((bus->bus != SPI_BUS) || bus->usebufpool) \ + PKTFREE(bus->dhd->osh, pkt, FALSE); ++ ++#ifdef PKT_STATICS ++pkt_statics_t tx_statics = {0}; ++#endif ++ + DHD_SPINWAIT_SLEEP_INIT(sdioh_spinwait_sleep); + + #if defined(MULTIPLE_SUPPLICANT) + #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) + DEFINE_MUTEX(_dhd_sdio_mutex_lock_); + #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) */ +-#endif ++#endif + + #ifdef DHD_DEBUG + /* Device console log buffer state */ +@@ -367,6 +372,8 @@ + #ifdef DHDENABLE_TAILPAD + void *pad_pkt; + #endif /* DHDENABLE_TAILPAD */ ++ uint txglomframes; /* Number of tx glom frames (superframes) */ ++ uint txglompkts; /* Number of packets from tx glom frames */ + } dhd_bus_t; + + /* clkstate */ +@@ -423,7 +430,7 @@ + + #define ALIGNMENT 4 + +-#if defined(OOB_INTR_ONLY) && defined(HW_OOB) ++#if (defined(OOB_INTR_ONLY) && defined(HW_OOB)) || defined(FORCE_WOWLAN) + extern void bcmsdh_enable_hw_oob_intr(void *sdh, bool enable); + #endif + +@@ -448,10 +455,27 @@ + /* Try doing readahead */ + static bool dhd_readahead; + ++#if defined(SWTXGLOM) || defined(BCMSDIOH_TXGLOM_EXT) ++bool ++dhdsdio_is_dataok(dhd_bus_t *bus) { ++ return (((uint8)(bus->tx_max - bus->tx_seq) - bus->dhd->conf->tx_max_offset > 1) && \ ++ (((uint8)(bus->tx_max - bus->tx_seq) & 0x80) == 0)); ++} ++ ++uint8 ++dhdsdio_get_databufcnt(dhd_bus_t *bus) { ++ return ((uint8)(bus->tx_max - bus->tx_seq) - 1 - bus->dhd->conf->tx_max_offset); ++} ++#endif ++ + /* To check if there's window offered */ ++#if defined(SWTXGLOM) || defined(BCMSDIOH_TXGLOM_EXT) ++#define DATAOK(bus) dhdsdio_is_dataok(bus) ++#else + #define DATAOK(bus) \ + (((uint8)(bus->tx_max - bus->tx_seq) > 1) && \ + (((uint8)(bus->tx_max - bus->tx_seq) & 0x80) == 0)) ++#endif + + /* To check if there's window offered for ctrl frame */ + #define TXCTLOK(bus) \ +@@ -459,8 +483,12 @@ + (((uint8)(bus->tx_max - bus->tx_seq) & 0x80) == 0)) + + /* Number of pkts available in dongle for data RX */ ++#if defined(SWTXGLOM) || defined(BCMSDIOH_TXGLOM_EXT) ++#define DATABUFCNT(bus) dhdsdio_get_databufcnt(bus) ++#else + #define DATABUFCNT(bus) \ + ((uint8)(bus->tx_max - bus->tx_seq) - 1) ++#endif + + /* Macros to get register read/write status */ + /* NOTE: these assume a local dhdsdio_bus_t *bus! */ +@@ -568,7 +596,11 @@ + static int dhdsdio_txpkt(dhd_bus_t *bus, uint chan, void** pkts, int num_pkt, bool free_pkt); + static int dhdsdio_txpkt_preprocess(dhd_bus_t *bus, void *pkt, int chan, int txseq, + int prev_chain_total_len, bool last_chained_pkt, +- int *pad_pkt_len, void **new_pkt); ++ int *pad_pkt_len, void **new_pkt ++#if defined(BCMSDIOH_TXGLOM_EXT) ++ , int frist_frame ++#endif ++); + static int dhdsdio_txpkt_postprocess(dhd_bus_t *bus, void *pkt); + + static int dhdsdio_download_firmware(dhd_bus_t *bus, osl_t *osh, void *sdh); +@@ -713,9 +745,11 @@ + (bus->sih->chip == BCM4339_CHIP_ID) || + (bus->sih->chip == BCM43349_CHIP_ID) || + (bus->sih->chip == BCM4345_CHIP_ID) || ++ (bus->sih->chip == BCM43454_CHIP_ID) || + (bus->sih->chip == BCM4354_CHIP_ID) || + (bus->sih->chip == BCM4356_CHIP_ID) || + (bus->sih->chip == BCM4358_CHIP_ID) || ++ (bus->sih->chip == BCM4371_CHIP_ID) || + (BCM4349_CHIP(bus->sih->chip)) || + (bus->sih->chip == BCM4350_CHIP_ID)) { + core_capext = TRUE; +@@ -733,9 +767,11 @@ + (bus->sih->chip == BCM4339_CHIP_ID) || + (bus->sih->chip == BCM43349_CHIP_ID) || + (bus->sih->chip == BCM4345_CHIP_ID) || ++ (bus->sih->chip == BCM43454_CHIP_ID) || + (bus->sih->chip == BCM4354_CHIP_ID) || + (bus->sih->chip == BCM4356_CHIP_ID) || + (bus->sih->chip == BCM4358_CHIP_ID) || ++ (bus->sih->chip == BCM4371_CHIP_ID) || + (bus->sih->chip == BCM4350_CHIP_ID)) { + uint32 enabval = 0; + addr = SI_ENUM_BASE + OFFSETOF(chipcregs_t, chipcontrol_addr); +@@ -745,9 +781,11 @@ + + if ((bus->sih->chip == BCM4350_CHIP_ID) || + (bus->sih->chip == BCM4345_CHIP_ID) || ++ (bus->sih->chip == BCM43454_CHIP_ID) || + (bus->sih->chip == BCM4354_CHIP_ID) || + (bus->sih->chip == BCM4356_CHIP_ID) || +- (bus->sih->chip == BCM4358_CHIP_ID)) ++ (bus->sih->chip == BCM4358_CHIP_ID) || ++ (bus->sih->chip == BCM4371_CHIP_ID)) + enabval &= CC_CHIPCTRL3_SR_ENG_ENABLE; + + if (enabval) +@@ -792,9 +830,13 @@ + 1 << SBSDIO_FUNC1_WCTRL_HTWAIT_SHIFT, &err); + val = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_WAKEUPCTRL, NULL); + ++#ifdef USE_CMD14 + /* Add CMD14 Support */ + dhdsdio_devcap_set(bus, + (SDIOD_CCCR_BRCM_CARDCAP_CMD14_SUPPORT | SDIOD_CCCR_BRCM_CARDCAP_CMD14_EXT)); ++#endif /* USE_CMD14 */ ++ ++ dhdsdio_devcap_set(bus, SDIOD_CCCR_BRCM_CARDCAP_CMD_NODEC); + + bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, + SBSDIO_FUNC1_CHIPCLKCSR, SBSDIO_FORCE_HT, &err); +@@ -1521,12 +1563,11 @@ + return err; + } + +- +-#if defined(OOB_INTR_ONLY) ++#if defined(OOB_INTR_ONLY) || defined(FORCE_WOWLAN) + void + dhd_enable_oob_intr(struct dhd_bus *bus, bool enable) + { +-#if defined(HW_OOB) ++#if defined(HW_OOB) || defined(FORCE_WOWLAN) + bcmsdh_enable_hw_oob_intr(bus->sdh, enable); + #else + sdpcmd_regs_t *regs = bus->regs; +@@ -1551,7 +1592,7 @@ + dhdsdio_clkctl(bus, CLK_SDONLY, FALSE); + #endif /* !defined(HW_OOB) */ + } +-#endif ++#endif + + int + dhd_bus_txdata(struct dhd_bus *bus, void *pkt) +@@ -1559,10 +1600,6 @@ + int ret = BCME_ERROR; + osl_t *osh; + uint datalen, prec; +-#if defined(DHD_TX_DUMP) || defined(DHD_8021X_DUMP) +- uint8 *dump_data; +- uint16 protocol; +-#endif /* DHD_TX_DUMP || DHD_8021X_DUMP */ + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + +@@ -1585,31 +1622,6 @@ + BCM_REFERENCE(datalen); + #endif /* SDTEST */ + +-#if defined(DHD_TX_DUMP) || defined(DHD_8021X_DUMP) +- dump_data = PKTDATA(osh, pkt); +- dump_data += 4; /* skip 4 bytes header */ +- protocol = (dump_data[12] << 8) | dump_data[13]; +- +- if (protocol == ETHER_TYPE_802_1X) { +- DHD_ERROR(("ETHER_TYPE_802_1X [TX]: ver %d, type %d, replay %d\n", +- dump_data[14], dump_data[15], dump_data[30])); +- } +-#endif /* DHD_TX_DUMP || DHD_8021X_DUMP */ +- +-#if defined(DHD_TX_DUMP) && defined(DHD_TX_FULL_DUMP) +- { +- int i; +- DHD_ERROR(("TX DUMP\n")); +- +- for (i = 0; i < (datalen - 4); i++) { +- DHD_ERROR(("%02X ", dump_data[i])); +- if ((i & 15) == 15) +- printk("\n"); +- } +- DHD_ERROR(("\n")); +- } +-#endif /* DHD_TX_DUMP && DHD_TX_FULL_DUMP */ +- + prec = PRIO2PREC((PKTPRIO(pkt) & PRIOMASK)); + + /* Check for existing queue, current flow-control, pending event, or pending clock */ +@@ -1669,8 +1681,20 @@ + + /* Schedule DPC if needed to send queued packet(s) */ + if (dhd_deferred_tx && !bus->dpc_sched) { +- bus->dpc_sched = TRUE; +- dhd_sched_dpc(bus->dhd); ++ if (bus->dhd->conf->deferred_tx_len) { ++ if(dhd_os_wd_timer_enabled(bus->dhd) == FALSE) { ++ bus->dpc_sched = TRUE; ++ dhd_sched_dpc(bus->dhd); ++ } ++ if(pktq_len(&bus->txq) >= bus->dhd->conf->deferred_tx_len && ++ dhd_os_wd_timer_enabled(bus->dhd) == FALSE) { ++ bus->dpc_sched = TRUE; ++ dhd_sched_dpc(bus->dhd); ++ } ++ } else { ++ bus->dpc_sched = TRUE; ++ dhd_sched_dpc(bus->dhd); ++ } + } + } else { + int chan = SDPCM_DATA_CHANNEL; +@@ -1720,7 +1744,11 @@ + */ + static int dhdsdio_txpkt_preprocess(dhd_bus_t *bus, void *pkt, int chan, int txseq, + int prev_chain_total_len, bool last_chained_pkt, +- int *pad_pkt_len, void **new_pkt) ++ int *pad_pkt_len, void **new_pkt ++#if defined(BCMSDIOH_TXGLOM_EXT) ++ , int first_frame ++#endif ++) + { + osl_t *osh; + uint8 *frame; +@@ -1732,6 +1760,9 @@ + uint32 swhdr_offset; + bool alloc_new_pkt = FALSE; + uint8 sdpcm_hdrlen = bus->txglom_enable ? SDPCM_HDRLEN_TXGLOM : SDPCM_HDRLEN; ++#ifdef PKT_STATICS ++ uint16 len; ++#endif + + *new_pkt = NULL; + osh = bus->dhd->osh; +@@ -1761,6 +1792,34 @@ + } + } + #endif /* WLMEDIA_HTSF */ ++#ifdef PKT_STATICS ++ len = (uint16)PKTLEN(osh, pkt); ++ switch(chan) { ++ case SDPCM_CONTROL_CHANNEL: ++ tx_statics.ctrl_count++; ++ tx_statics.ctrl_size += len; ++ break; ++ case SDPCM_DATA_CHANNEL: ++ tx_statics.data_count++; ++ tx_statics.data_size += len; ++ break; ++ case SDPCM_GLOM_CHANNEL: ++ tx_statics.glom_count++; ++ tx_statics.glom_size += len; ++ break; ++ case SDPCM_EVENT_CHANNEL: ++ tx_statics.event_count++; ++ tx_statics.event_size += len; ++ break; ++ case SDPCM_TEST_CHANNEL: ++ tx_statics.test_count++; ++ tx_statics.test_size += len; ++ break; ++ ++ default: ++ break; ++ } ++#endif /* PKT_STATICS */ + #ifdef DHD_DEBUG + if (PKTPRIO(pkt) < ARRAYSIZE(tx_packets)) + tx_packets[PKTPRIO(pkt)]++; +@@ -1901,6 +1960,10 @@ + * referred to in sdioh_request_buffer(). The tail length will be excluded in + * dhdsdio_txpkt_postprocess(). + */ ++#if defined(BCMSDIOH_TXGLOM_EXT) ++ if (bus->dhd->conf->txglom_bucket_size) ++ tail_padding = 0; ++#endif + *(uint16*)frame = (uint16)htol16(pkt_len); + *(((uint16*)frame) + 1) = (uint16)htol16(~pkt_len); + pkt_len += tail_padding; +@@ -1909,13 +1972,43 @@ + if (bus->txglom_enable) { + uint32 hwheader1; + uint32 hwheader2; +- +- swhdr_offset += SDPCM_HWEXT_LEN; +- hwheader1 = (pkt_len - SDPCM_FRAMETAG_LEN - tail_padding) | +- (last_chained_pkt << 24); +- hwheader2 = (tail_padding) << 16; +- htol32_ua_store(hwheader1, frame + SDPCM_FRAMETAG_LEN); +- htol32_ua_store(hwheader2, frame + SDPCM_FRAMETAG_LEN + 4); ++#ifdef BCMSDIOH_TXGLOM_EXT ++ uint32 act_len = pkt_len - tail_padding; ++ uint32 real_pad = 0; ++ if(bus->dhd->conf->txglom_ext && !last_chained_pkt) { ++ tail_padding = 0; ++ if(first_frame == 0) { ++ // first pkt, add pad to bucket size - recv offset ++ pkt_len = bus->dhd->conf->txglom_bucket_size - TXGLOM_RECV_OFFSET; ++ } else { ++ // add pad to bucket size ++ pkt_len = bus->dhd->conf->txglom_bucket_size; ++ } ++ swhdr_offset += SDPCM_HWEXT_LEN; ++ hwheader1 = (act_len - SDPCM_FRAMETAG_LEN) | (last_chained_pkt << 24); ++ hwheader2 = (pkt_len - act_len) << 16; ++ htol32_ua_store(hwheader1, frame + SDPCM_FRAMETAG_LEN); ++ htol32_ua_store(hwheader2, frame + SDPCM_FRAMETAG_LEN + 4); ++ real_pad = pkt_len - act_len; ++ ++ if (PKTTAILROOM(osh, pkt) < real_pad) { ++ DHD_INFO(("%s : insufficient tailroom %d for %d real_pad\n", ++ __func__, (int)PKTTAILROOM(osh, pkt), real_pad)); ++ if (PKTPADTAILROOM(osh, pkt, real_pad)) { ++ DHD_ERROR(("CHK1: padding error size %d\n", real_pad)); ++ } else ++ frame = (uint8 *)PKTDATA(osh, pkt); ++ } ++ } else ++#endif ++ { ++ swhdr_offset += SDPCM_HWEXT_LEN; ++ hwheader1 = (pkt_len - SDPCM_FRAMETAG_LEN - tail_padding) | ++ (last_chained_pkt << 24); ++ hwheader2 = (tail_padding) << 16; ++ htol32_ua_store(hwheader1, frame + SDPCM_FRAMETAG_LEN); ++ htol32_ua_store(hwheader2, frame + SDPCM_FRAMETAG_LEN + 4); ++ } + } + PKTSETLEN((osh), (pkt), (pkt_len)); + +@@ -1976,6 +2069,615 @@ + return BCME_OK; + } + ++#if defined(SWTXGLOM) ++static int ++dhd_bcmsdh_send_swtxglom_buf(dhd_bus_t *bus, uint32 addr, uint fn, uint flags, uint8 *buf, uint nbytes, ++ void *pkt, bcmsdh_cmplt_fn_t complete, void *handle, int max_retry) ++{ ++ int ret; ++ int i = 0; ++ int retries = 0; ++ bcmsdh_info_t *sdh; ++ ++ if (!KSO_ENAB(bus)) { ++ DHD_ERROR(("%s: Device asleep\n", __FUNCTION__)); ++ return BCME_NODEVICE; ++ } ++ ++ sdh = bus->sdh; ++ do { ++ ret = bcmsdh_send_swtxglom_buf(bus->sdh, addr, fn, flags, buf, nbytes, ++ pkt, complete, handle); ++ ++ bus->f2txdata++; ++ ASSERT(ret != BCME_PENDING); ++ ++ if (ret == BCME_NODEVICE) { ++ DHD_ERROR(("%s: Device asleep already\n", __FUNCTION__)); ++ } else if (ret < 0) { ++ /* On failure, abort the command and terminate the frame */ ++ DHD_ERROR(("%s: sdio error %d, abort command and terminate frame.\n", ++ __FUNCTION__, ret)); ++ bus->tx_sderrs++; ++ bus->f1regdata++; ++ bus->dhd->tx_errors++; ++ bcmsdh_abort(sdh, SDIO_FUNC_2); ++ bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_FRAMECTRL, ++ SFC_WF_TERM, NULL); ++ for (i = 0; i < READ_FRM_CNT_RETRIES; i++) { ++ uint8 hi, lo; ++ hi = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_WFRAMEBCHI, ++ NULL); ++ lo = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_WFRAMEBCLO, ++ NULL); ++ bus->f1regdata += 2; ++ if ((hi == 0) && (lo == 0)) ++ break; ++ } ++ } ++ if (ret == 0) { ++#ifdef BCMSDIOH_TXGLOM ++ if (bus->txglom_enable) { ++ bus->tx_seq = (bus->tx_seq + bus->txglom_cnt) % SDPCM_SEQUENCE_WRAP; ++ } else ++#endif ++ { ++ bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP; ++ } ++ } ++ } while ((ret < 0) && retrydata && ++retries < max_retry); ++ ++ return ret; ++} ++ ++/* Writes a HW/SW header into the packet and sends it. */ ++/* Assumes: (a) header space already there, (b) caller holds lock */ ++static int ++dhdsdio_txpkt_swtxglom(dhd_bus_t *bus, void *pkt, uint chan, bool free_pkt, bool queue_only) ++{ ++ int ret; ++ osl_t *osh; ++ uint8 *frame; ++ uint16 len, pad1 = 0, act_len = 0; ++ uint32 swheader; ++ uint32 real_pad = 0; ++ bcmsdh_info_t *sdh; ++ void *new; ++ int pkt_cnt; ++#ifdef BCMSDIOH_TXGLOM ++ uint8 *frame_tmp; ++#endif ++#ifdef WLMEDIA_HTSF ++ char *p; ++ htsfts_t *htsf_ts; ++#endif ++ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++ sdh = bus->sdh; ++ osh = bus->dhd->osh; ++ ++#ifdef DHDTCPACK_SUPPRESS ++ if (dhd_tcpack_check_xmit(bus->dhd, pkt) == BCME_ERROR) { ++ DHD_ERROR(("%s %d: tcpack_suppress ERROR!!! Stop using it\n", ++ __FUNCTION__, __LINE__)); ++ dhd_tcpack_suppress_set(bus->dhd, TCPACK_SUP_OFF); ++ } ++#endif /* DHDTCPACK_SUPPRESS */ ++ ++ /* Add space for the header */ ++ PKTPUSH(osh, pkt, SDPCM_HDRLEN_TXGLOM); ++ ASSERT(ISALIGNED((uintptr)PKTDATA(osh, pkt), 2)); ++ ++ if (bus->dhd->dongle_reset) { ++ ret = BCME_NOTREADY; ++ goto done; ++ } ++ ++ frame = (uint8*)PKTDATA(osh, pkt); ++ ++#ifdef WLMEDIA_HTSF ++ if (PKTLEN(osh, pkt) >= 100) { ++ p = PKTDATA(osh, pkt); ++ htsf_ts = (htsfts_t*) (p + HTSF_HOSTOFFSET + 12); ++ if (htsf_ts->magic == HTSFMAGIC) { ++ htsf_ts->c20 = get_cycles(); ++ htsf_ts->t20 = dhd_get_htsf(bus->dhd->info, 0); ++ } ++ } ++#endif /* WLMEDIA_HTSF */ ++ ++#ifdef PKT_STATICS ++ len = (uint16)PKTLEN(osh, pkt); ++ switch(chan) { ++ case SDPCM_CONTROL_CHANNEL: ++ tx_statics.ctrl_count++; ++ tx_statics.ctrl_size += len; ++ break; ++ case SDPCM_DATA_CHANNEL: ++ tx_statics.data_count++; ++ tx_statics.data_size += len; ++ break; ++ case SDPCM_GLOM_CHANNEL: ++ tx_statics.glom_count++; ++ tx_statics.glom_size += len; ++ break; ++ case SDPCM_EVENT_CHANNEL: ++ tx_statics.event_count++; ++ tx_statics.event_size += len; ++ break; ++ case SDPCM_TEST_CHANNEL: ++ tx_statics.test_count++; ++ tx_statics.test_size += len; ++ break; ++ ++ default: ++ break; ++ } ++#endif /* PKT_STATICS */ ++ ++ /* Add alignment padding, allocate new packet if needed */ ++ if ((pad1 = ((uintptr)frame % DHD_SDALIGN))) { ++ if (PKTHEADROOM(osh, pkt) < pad1) { ++ DHD_INFO(("%s: insufficient headroom %d for %d pad1\n", ++ __FUNCTION__, (int)PKTHEADROOM(osh, pkt), pad1)); ++ bus->dhd->tx_realloc++; ++ new = PKTGET(osh, (PKTLEN(osh, pkt) + DHD_SDALIGN), TRUE); ++ if (!new) { ++ DHD_ERROR(("%s: couldn't allocate new %d-byte packet\n", ++ __FUNCTION__, PKTLEN(osh, pkt) + DHD_SDALIGN)); ++ ret = BCME_NOMEM; ++ goto done; ++ } ++ ++ PKTALIGN(osh, new, PKTLEN(osh, pkt), DHD_SDALIGN); ++ bcopy(PKTDATA(osh, pkt), PKTDATA(osh, new), PKTLEN(osh, pkt)); ++ if (free_pkt) ++ PKTFREE(osh, pkt, TRUE); ++ /* free the pkt if canned one is not used */ ++ free_pkt = TRUE; ++ pkt = new; ++ frame = (uint8*)PKTDATA(osh, pkt); ++ ASSERT(((uintptr)frame % DHD_SDALIGN) == 0); ++ pad1 = 0; ++ } else { ++ PKTPUSH(osh, pkt, pad1); ++ frame = (uint8*)PKTDATA(osh, pkt); ++ ++ ASSERT((pad1 + SDPCM_HDRLEN_TXGLOM) <= (int) PKTLEN(osh, pkt)); ++ bzero(frame, pad1 + SDPCM_HDRLEN_TXGLOM); ++ } ++ } ++ ASSERT(pad1 < DHD_SDALIGN); ++ ++ /* Hardware tag: 2 byte len followed by 2 byte ~len check (all LE) */ ++ len = (uint16)PKTLEN(osh, pkt); ++ *(uint16*)frame = htol16(len); ++ *(((uint16*)frame) + 1) = htol16(~len); ++ ++#ifdef BCMSDIOH_TXGLOM ++ if (bus->txglom_enable) { ++ uint32 hwheader1 = 0, hwheader2 = 0; ++ act_len = len; ++ ++ /* Software tag: channel, sequence number, data offset */ ++ swheader = ((chan << SDPCM_CHANNEL_SHIFT) & SDPCM_CHANNEL_MASK) | ++ ((bus->tx_seq + bus->txglom_cnt) % SDPCM_SEQUENCE_WRAP) | ++ (((pad1 + SDPCM_HDRLEN_TXGLOM) << SDPCM_DOFFSET_SHIFT) & SDPCM_DOFFSET_MASK); ++ htol32_ua_store(swheader, frame + SDPCM_FRAMETAG_LEN + SDPCM_HWEXT_LEN); ++ htol32_ua_store(0, frame + SDPCM_FRAMETAG_LEN + SDPCM_HWEXT_LEN + sizeof(swheader)); ++ ++ if (queue_only) { ++ if (bus->dhd->conf->txglom_ext) { ++ if(bus->txglom_cnt == 0) { ++ // first pkt, add pad to bucket size - recv offset ++ len = bus->dhd->conf->txglom_bucket_size - TXGLOM_RECV_OFFSET; ++ } else { ++ // add pad to bucket size ++ len = bus->dhd->conf->txglom_bucket_size; ++ } ++ } else { ++ uint8 alignment = ALIGNMENT; ++ if (forcealign && (len & (alignment - 1))) ++ len = ROUNDUP(len, alignment); ++ } ++ /* Hardware extention tag */ ++ /* 2byte frame length, 1byte-, 1byte frame flag, ++ * 2byte-hdrlength, 2byte padlenght ++ */ ++ hwheader1 = (act_len - SDPCM_FRAMETAG_LEN) | (0 << 24); ++ hwheader2 = (len - act_len) << 16; ++ htol32_ua_store(hwheader1, frame + SDPCM_FRAMETAG_LEN); ++ htol32_ua_store(hwheader2, frame + SDPCM_FRAMETAG_LEN + 4); ++ real_pad = len - act_len; ++ if (PKTTAILROOM(osh, pkt) < real_pad) { ++ DHD_INFO(("%s 1: insufficient tailroom %d for %d real_pad\n", ++ __FUNCTION__, (int)PKTTAILROOM(osh, pkt), real_pad)); ++ if (PKTPADTAILROOM(osh, pkt, real_pad)) { ++ DHD_ERROR(("CHK1: padding error size %d\n", real_pad)); ++ ret = BCME_NOMEM; ++ goto done; ++ } ++#ifndef BCMLXSDMMC ++ else ++ PKTSETLEN(osh, pkt, act_len); ++#endif ++ } ++#ifdef BCMLXSDMMC ++ PKTSETLEN(osh, pkt, len); ++#endif /* BCMLXSDMMC */ ++ /* Post the frame pointer to sdio glom array */ ++ bcmsdh_glom_post(bus->sdh, frame, pkt, len); ++ /* Save the pkt pointer in bus glom array */ ++ bus->glom_pkt_arr[bus->txglom_cnt] = pkt; ++ bus->txglom_total_len += len; ++ bus->txglom_cnt++; ++ return BCME_OK; ++ } else { ++ /* Raise len to next SDIO block to eliminate tail command */ ++ if (bus->roundup && bus->blocksize && ++ ((bus->txglom_total_len + len) > bus->blocksize)) { ++ uint16 pad2 = bus->blocksize - ++ ((bus->txglom_total_len + len) % bus->blocksize); ++ if ((pad2 <= bus->roundup) && (pad2 < bus->blocksize)) { ++ len += pad2; ++ } else { ++ } ++ } else if ((bus->txglom_total_len + len) % DHD_SDALIGN) { ++ len += DHD_SDALIGN ++ - ((bus->txglom_total_len + len) % DHD_SDALIGN); ++ } ++ if (forcealign && (len & (ALIGNMENT - 1))) { ++ len = ROUNDUP(len, ALIGNMENT); ++ } ++ ++ /* Hardware extention tag */ ++ /* 2byte frame length, 1byte-, 1byte frame flag, ++ * 2byte-hdrlength, 2byte padlenght ++ */ ++ if (bus->dhd->conf->txglom_ext) { ++ // copy way, the last packet pad2 is set to 0 it will be dropped by HW ++ hwheader1 = (act_len - SDPCM_FRAMETAG_LEN) | (1 << 24); ++ hwheader2 = 0; ++ htol32_ua_store(hwheader1, frame + SDPCM_FRAMETAG_LEN); ++ htol32_ua_store(hwheader2, frame + SDPCM_FRAMETAG_LEN + 4); ++ } else { ++ hwheader1 = (act_len - SDPCM_FRAMETAG_LEN) | (1 << 24); ++ hwheader2 = (len - act_len) << 16; ++ htol32_ua_store(hwheader1, frame + SDPCM_FRAMETAG_LEN); ++ htol32_ua_store(hwheader2, frame + SDPCM_FRAMETAG_LEN + 4); ++ } ++ real_pad = len - act_len; ++ if (PKTTAILROOM(osh, pkt) < real_pad) { ++ DHD_INFO(("%s 2: insufficient tailroom %d" ++ " for %d real_pad\n", ++ __FUNCTION__, (int)PKTTAILROOM(osh, pkt), real_pad)); ++ if (PKTPADTAILROOM(osh, pkt, real_pad)) { ++ DHD_ERROR(("CHK2: padding error size %d." ++ " %d more pkts are discarded together.\n", ++ real_pad, bus->txglom_cnt)); ++ /* Save the pkt pointer in bus glom array ++ * Otherwise, this last pkt will not be ++ * cleaned under "goto done" ++ */ ++ bus->glom_pkt_arr[bus->txglom_cnt] = pkt; ++ bus->txglom_cnt++; ++ bus->txglom_total_len += len; ++ ret = BCME_NOMEM; ++ goto done; ++ } ++#ifndef BCMLXSDMMC ++ else ++ PKTSETLEN(osh, pkt, act_len); ++#endif ++ } ++#ifdef BCMLXSDMMC ++ PKTSETLEN(osh, pkt, len); ++#endif /* BCMLXSDMMC */ ++ ++ /* Post the frame pointer to sdio glom array */ ++ bcmsdh_glom_post(bus->sdh, frame, pkt, len); ++ /* Save the pkt pointer in bus glom array */ ++ bus->glom_pkt_arr[bus->txglom_cnt] = pkt; ++ bus->txglom_cnt++; ++ if (bus->dhd->conf->txglom_ext) ++ //copy way, the last buffer padding is not need add to len ++ bus->txglom_total_len += act_len; ++ else ++ bus->txglom_total_len += len; ++ ++ /* Update the total length on the first pkt */ ++ frame_tmp = (uint8*)PKTDATA(osh, bus->glom_pkt_arr[0]); ++ *(uint16*)frame_tmp = htol16(bus->txglom_total_len); ++ *(((uint16*)frame_tmp) + 1) = htol16(~bus->txglom_total_len); ++ } ++ } else ++#endif /* BCMSDIOH_TXGLOM */ ++ { ++ act_len = len; ++ /* Software tag: channel, sequence number, data offset */ ++ swheader = ((chan << SDPCM_CHANNEL_SHIFT) & SDPCM_CHANNEL_MASK) | bus->tx_seq | ++ (((pad1 + SDPCM_HDRLEN_TXGLOM) << SDPCM_DOFFSET_SHIFT) & SDPCM_DOFFSET_MASK); ++ htol32_ua_store(swheader, frame + SDPCM_FRAMETAG_LEN); ++ htol32_ua_store(0, frame + SDPCM_FRAMETAG_LEN + sizeof(swheader)); ++ ++#ifdef DHD_DEBUG ++ if (PKTPRIO(pkt) < ARRAYSIZE(tx_packets)) { ++ tx_packets[PKTPRIO(pkt)]++; ++ } ++ if (DHD_BYTES_ON() && ++ (((DHD_CTL_ON() && (chan == SDPCM_CONTROL_CHANNEL)) || ++ (DHD_DATA_ON() && (chan != SDPCM_CONTROL_CHANNEL))))) { ++ prhex("Tx Frame", frame, len); ++ } else if (DHD_HDRS_ON()) { ++ prhex("TxHdr", frame, MIN(len, 16)); ++ } ++#endif ++ ++ /* Raise len to next SDIO block to eliminate tail command */ ++ if (bus->roundup && bus->blocksize && (len > bus->blocksize)) { ++ uint16 pad2 = bus->blocksize - (len % bus->blocksize); ++ if ((pad2 <= bus->roundup) && (pad2 < bus->blocksize)) ++#ifdef NOTUSED ++ if (pad2 <= PKTTAILROOM(osh, pkt)) ++#endif /* NOTUSED */ ++ len += pad2; ++ } else if (len % DHD_SDALIGN) { ++ len += DHD_SDALIGN - (len % DHD_SDALIGN); ++ } ++ ++ /* Some controllers have trouble with odd bytes -- round to even */ ++ if (forcealign && (len & (ALIGNMENT - 1))) { ++#ifdef NOTUSED ++ if (PKTTAILROOM(osh, pkt)) ++#endif ++ len = ROUNDUP(len, ALIGNMENT); ++#ifdef NOTUSED ++ else ++ DHD_ERROR(("%s: sending unrounded %d-byte packet\n", __FUNCTION__, len)); ++#endif ++ } ++ real_pad = len - act_len; ++ if (PKTTAILROOM(osh, pkt) < real_pad) { ++ DHD_INFO(("%s 3: insufficient tailroom %d for %d real_pad\n", ++ __FUNCTION__, (int)PKTTAILROOM(osh, pkt), real_pad)); ++ if (PKTPADTAILROOM(osh, pkt, real_pad)) { ++ DHD_ERROR(("CHK3: padding error size %d\n", real_pad)); ++ ret = BCME_NOMEM; ++ goto done; ++ } ++#ifndef BCMLXSDMMC ++ else ++ PKTSETLEN(osh, pkt, act_len); ++#endif ++ } ++#ifdef BCMLXSDMMC ++ PKTSETLEN(osh, pkt, len); ++#endif /* BCMLXSDMMC */ ++ } ++#ifdef DHD_DEBUG ++ if (PKTPRIO(pkt) < ARRAYSIZE(tx_packets)) { ++ tx_packets[PKTPRIO(pkt)]++; ++ } ++#endif ++ ret = dhd_bcmsdh_send_swtxglom_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2, F2SYNC, ++ frame, len, pkt, NULL, NULL, TXRETRIES); ++ ++done: ++ ++#ifdef BCMSDIOH_TXGLOM ++ if (bus->txglom_enable && !queue_only) { ++ bcmsdh_glom_clear(bus->sdh); ++ pkt_cnt = bus->txglom_cnt; ++ } else ++#endif ++ { ++ pkt_cnt = 1; ++ } ++ /* restore pkt buffer pointer before calling tx complete routine */ ++ while (pkt_cnt) { ++#ifdef BCMSDIOH_TXGLOM ++ uint32 doff; ++ if (bus->txglom_enable) { ++#ifdef BCMLXSDMMC ++ uint32 pad2 = 0; ++#endif /* BCMLXSDMMC */ ++ if (!queue_only) ++ pkt = bus->glom_pkt_arr[bus->txglom_cnt - pkt_cnt]; ++ ++ frame = (uint8*)PKTDATA(osh, pkt); ++ doff = ltoh32_ua(frame + SDPCM_FRAMETAG_LEN + SDPCM_HWEXT_LEN); ++ doff = (doff & SDPCM_DOFFSET_MASK) >> SDPCM_DOFFSET_SHIFT; ++#ifdef BCMLXSDMMC ++ pad2 = ltoh32_ua(frame + SDPCM_FRAMETAG_LEN + 4) >> 16; ++ PKTSETLEN(osh, pkt, PKTLEN(osh, pkt) - pad2); ++#endif /* BCMLXSDMMC */ ++ PKTPULL(osh, pkt, doff); ++ } else ++#endif /* BCMSDIOH_TXGLOM */ ++ { ++#ifdef BCMLXSDMMC ++ if (act_len > 0) ++ PKTSETLEN(osh, pkt, act_len); ++#endif /* BCMLXSDMMC */ ++ PKTPULL(osh, pkt, SDPCM_HDRLEN_TXGLOM + pad1); ++ } ++#ifdef PROP_TXSTATUS ++ if (bus->dhd->wlfc_state) { ++ dhd_os_sdunlock(bus->dhd); ++ dhd_wlfc_txcomplete(bus->dhd, pkt, ret == 0); ++ dhd_os_sdlock(bus->dhd); ++ } else { ++#endif /* PROP_TXSTATUS */ ++#ifdef SDTEST ++ if (chan != SDPCM_TEST_CHANNEL) { ++ dhd_txcomplete(bus->dhd, pkt, ret != 0); ++ } ++#else /* SDTEST */ ++ dhd_txcomplete(bus->dhd, pkt, ret != 0); ++#endif /* SDTEST */ ++ if (free_pkt) ++ PKTFREE(osh, pkt, TRUE); ++#ifdef PROP_TXSTATUS ++ } ++#endif ++ pkt_cnt--; ++ } ++ ++#ifdef BCMSDIOH_TXGLOM ++ /* Reset the glom array */ ++ if (bus->txglom_enable && !queue_only) { ++ bus->txglom_cnt = 0; ++ bus->txglom_total_len = 0; ++ } ++#endif ++ return ret; ++} ++ ++static uint ++dhdsdio_sendfromq_swtxglom(dhd_bus_t *bus, uint maxframes) ++{ ++ void *pkt; ++ uint32 intstatus = 0; ++ uint retries = 0; ++ int ret = 0, prec_out; ++ uint cnt = 0; ++ uint datalen; ++ uint8 tx_prec_map; ++ uint16 txpktqlen = 0; ++#ifdef BCMSDIOH_TXGLOM ++ uint i; ++ uint8 txglom_cnt; ++#endif ++ ++ dhd_pub_t *dhd = bus->dhd; ++ sdpcmd_regs_t *regs = bus->regs; ++ ++ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ++ ++ if (!KSO_ENAB(bus)) { ++ DHD_ERROR(("%s: Device asleep\n", __FUNCTION__)); ++ return BCME_NODEVICE; ++ } ++ ++ tx_prec_map = ~bus->flowcontrol; ++ /* Send frames until the limit or some other event */ ++ for (cnt = 0; (cnt < maxframes) && DATAOK(bus); cnt++) { ++#ifdef BCMSDIOH_TXGLOM ++ if (bus->txglom_enable) { ++ void *pkttable[SDPCM_MAXGLOM_SIZE]; ++ dhd_os_sdlock_txq(bus->dhd); ++ txglom_cnt = MIN(DATABUFCNT(bus), bus->txglomsize); ++ txglom_cnt = MIN(txglom_cnt, pktq_mlen(&bus->txq, tx_prec_map)); ++ txglom_cnt = MIN(txglom_cnt, maxframes-cnt); ++ ++ /* Limiting the size to 2pkts in case of copy */ ++ if (bus->dhd->conf->txglom_ext) ++ txglom_cnt = MIN(txglom_cnt, SDPCM_MAXGLOM_SIZE); ++ else ++ txglom_cnt = MIN(txglom_cnt, 10); ++ ++ for (i = 0; i < txglom_cnt; i++) ++ pkttable[i] = pktq_mdeq(&bus->txq, tx_prec_map, &prec_out); ++ ++ txpktqlen = pktq_len(&bus->txq); ++ dhd_os_sdunlock_txq(bus->dhd); ++ ++ if (txglom_cnt == 0) ++ break; ++ datalen = 0; ++ ++#ifdef PKT_STATICS ++ if (txglom_cnt < 2) ++ tx_statics.glom_1_count++; ++ else if (txglom_cnt < 3) ++ tx_statics.glom_3_count++; ++ else if (txglom_cnt < 8) ++ tx_statics.glom_3_8_count++; ++ else ++ tx_statics.glom_8_count++; ++ if (txglom_cnt > tx_statics.glom_max) ++ tx_statics.glom_max = txglom_cnt; ++#endif ++ for (i = 0; i < txglom_cnt; i++) { ++ uint datalen_tmp = 0; ++ ++ if ((pkt = pkttable[i]) == NULL) { ++ /* This case should not happen */ ++ DHD_ERROR(("No pkts in the queue for glomming\n")); ++ break; ++ } ++ ++ datalen_tmp = (PKTLEN(bus->dhd->osh, pkt) - SDPCM_HDRLEN_TXGLOM); ++ ++#ifndef SDTEST ++ ret = dhdsdio_txpkt_swtxglom(bus, ++ pkt, ++ SDPCM_DATA_CHANNEL, ++ TRUE, ++ (i == (txglom_cnt-1))? FALSE: TRUE); ++#else ++ ret = dhdsdio_txpkt_swtxglom(bus, ++ pkt, ++ (bus->ext_loop ? SDPCM_TEST_CHANNEL : SDPCM_DATA_CHANNEL), ++ TRUE, ++ (i == (txglom_cnt-1))? FALSE: TRUE); ++#endif ++ if (ret == BCME_OK) ++ datalen += datalen_tmp; ++ } ++ cnt += i-1; ++ } else ++#endif /* BCMSDIOH_TXGLOM */ ++ { ++ dhd_os_sdlock_txq(bus->dhd); ++ if ((pkt = pktq_mdeq(&bus->txq, tx_prec_map, &prec_out)) == NULL) { ++ txpktqlen = pktq_len(&bus->txq); ++ dhd_os_sdunlock_txq(bus->dhd); ++ break; ++ } ++ txpktqlen = pktq_len(&bus->txq); ++ dhd_os_sdunlock_txq(bus->dhd); ++ datalen = PKTLEN(bus->dhd->osh, pkt) - SDPCM_HDRLEN_TXGLOM; ++ ++#ifndef SDTEST ++ ret = dhdsdio_txpkt_swtxglom(bus, pkt, SDPCM_DATA_CHANNEL, TRUE, FALSE); ++#else ++ ret = dhdsdio_txpkt_swtxglom(bus, ++ pkt, ++ (bus->ext_loop ? SDPCM_TEST_CHANNEL : SDPCM_DATA_CHANNEL), ++ TRUE, ++ FALSE); ++#endif ++ } ++ ++ if (ret) ++ bus->dhd->tx_errors++; ++ else ++ bus->dhd->dstats.tx_bytes += datalen; ++ ++ /* In poll mode, need to check for other events */ ++ if (!bus->intr && cnt) ++ { ++ /* Check device status, signal pending interrupt */ ++ R_SDREG(intstatus, ®s->intstatus, retries); ++ bus->f2txdata++; ++ if (bcmsdh_regfail(bus->sdh)) ++ break; ++ if (intstatus & bus->hostintmask) ++ bus->ipend = TRUE; ++ } ++ } ++ ++ /* Deflow-control stack if needed */ ++ if (dhd_doflow && dhd->up && (dhd->busstate == DHD_BUS_DATA) && ++ dhd->txoff && (txpktqlen < FCLOW)) ++ dhd_txflowcontrol(dhd, ALL_INTERFACES, OFF); ++ ++ return cnt; ++} ++#endif ++ + static int dhdsdio_txpkt(dhd_bus_t *bus, uint chan, void** pkts, int num_pkt, bool free_pkt) + { + int i; +@@ -2009,7 +2711,11 @@ + ASSERT(pkt); + last_pkt = (i == num_pkt - 1); + pkt_len = dhdsdio_txpkt_preprocess(bus, pkt, chan, bus->tx_seq + i, +- total_len, last_pkt, &pad_pkt_len, &new_pkt); ++ total_len, last_pkt, &pad_pkt_len, &new_pkt ++#if defined(BCMSDIOH_TXGLOM_EXT) ++ , i ++#endif ++ ); + if (pkt_len <= 0) + goto done; + if (new_pkt) { +@@ -2049,6 +2755,12 @@ + * so it will take the aligned length and buffer pointer. + */ + pkt_chain = PKTNEXT(osh, head_pkt) ? head_pkt : NULL; ++#if defined(SWTXGLOM) ++ if (bus->dhd->conf->swtxglom) ++ ret = dhd_bcmsdh_send_swtxglom_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2, F2SYNC, ++ PKTDATA(osh, head_pkt), total_len, pkt_chain, NULL, NULL, TXRETRIES); ++ else ++#endif + ret = dhd_bcmsdh_send_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2, F2SYNC, + PKTDATA(osh, head_pkt), total_len, pkt_chain, NULL, NULL, TXRETRIES); + if (ret == BCME_OK) +@@ -2144,9 +2856,24 @@ + break; + if (dhdsdio_txpkt(bus, SDPCM_DATA_CHANNEL, pkts, i, TRUE) != BCME_OK) + dhd->tx_errors++; +- else ++ else { + dhd->dstats.tx_bytes += datalen; ++ bus->txglomframes++; ++ bus->txglompkts += num_pkt; ++ } + cnt += i; ++#ifdef PKT_STATICS ++ if (num_pkt < 2) ++ tx_statics.glom_1_count++; ++ else if (num_pkt < 3) ++ tx_statics.glom_3_count++; ++ else if (num_pkt < 8) ++ tx_statics.glom_3_8_count++; ++ else ++ tx_statics.glom_8_count++; ++ if (num_pkt > tx_statics.glom_max) ++ tx_statics.glom_max = num_pkt; ++#endif + + /* In poll mode, need to check for other events */ + if (!bus->intr && cnt) +@@ -2197,6 +2924,13 @@ + *frame_seq = bus->tx_seq; + } + ++#if defined(SWTXGLOM) ++ if (bus->dhd->conf->swtxglom) ++ ret = dhd_bcmsdh_send_swtxglom_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2, F2SYNC, ++ (uint8 *)bus->ctrl_frame_buf, (uint32)bus->ctrl_frame_len, ++ NULL, NULL, NULL, 1); ++ else ++#endif + ret = dhd_bcmsdh_send_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2, F2SYNC, + (uint8 *)bus->ctrl_frame_buf, (uint32)bus->ctrl_frame_len, + NULL, NULL, NULL, 1); +@@ -2334,6 +3068,16 @@ + prhex("TxHdr", frame, MIN(len, 16)); + } + #endif ++#ifdef PKT_STATICS ++ tx_statics.ctrl_count++; ++ tx_statics.ctrl_size += len; ++#endif ++#if defined(SWTXGLOM) ++ if (bus->dhd->conf->swtxglom) ++ ret = dhd_bcmsdh_send_swtxglom_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2, F2SYNC, ++ frame, len, NULL, NULL, NULL, TXRETRIES); ++ else ++#endif + ret = dhd_bcmsdh_send_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2, F2SYNC, + frame, len, NULL, NULL, NULL, TXRETRIES); + if (ret == BCME_OK) +@@ -2645,6 +3389,11 @@ + #endif /* DHD_DEBUG */ + bcm_bprintf(strbuf, "clkstate %d activity %d idletime %d idlecount %d sleeping %d\n", + bus->clkstate, bus->activity, bus->idletime, bus->idlecount, bus->sleeping); ++ dhd_dump_pct(strbuf, "Tx: glom pct", (100 * bus->txglompkts), bus->dhd->tx_packets); ++ dhd_dump_pct(strbuf, ", pkts/glom", bus->txglompkts, bus->txglomframes); ++ bcm_bprintf(strbuf, "\n"); ++ bcm_bprintf(strbuf, "txglomframes %u, txglompkts %u\n", bus->txglomframes, bus->txglompkts); ++ bcm_bprintf(strbuf, "\n"); + } + + void +@@ -2661,6 +3410,7 @@ + bus->tx_sderrs = bus->fc_rcvd = bus->fc_xoff = bus->fc_xon = 0; + bus->rxglomfail = bus->rxglomframes = bus->rxglompkts = 0; + bus->f2rxhdrs = bus->f2rxdata = bus->f2txdata = bus->f1regdata = 0; ++ bus->txglomframes = bus->txglompkts = 0; + } + + #ifdef SDTEST +@@ -3229,7 +3979,7 @@ + + return (int_val & uart_enab); + } +-#endif ++#endif + + static int + dhdsdio_doiovar(dhd_bus_t *bus, const bcm_iovar_t *vi, uint32 actionid, const char *name, +@@ -3703,7 +4453,7 @@ + bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_MESBUSYCTRL, + ((uint8)mesbusyctrl | 0x80), NULL); + break; +-#endif ++#endif + + + case IOV_GVAL(IOV_DONGLEISOLATION): +@@ -3818,6 +4568,13 @@ + varsize = bus->varsz ? ROUNDUP(bus->varsz, 4) : 0; + varaddr = (bus->ramsize - 4) - varsize; + ++ // terence 20150412: fix for nvram failed to download ++ if (bus->dhd->conf->chip == BCM43340_CHIP_ID || ++ bus->dhd->conf->chip == BCM43341_CHIP_ID) { ++ varsize = varsize ? ROUNDUP(varsize, 64) : 0; ++ varaddr = (bus->ramsize - 64) - varsize; ++ } ++ + varaddr += bus->dongle_ram_base; + + if (bus->vars) { +@@ -4203,9 +4960,6 @@ + + BUS_WAKE(bus); + +- /* Change our idea of bus state */ +- bus->dhd->busstate = DHD_BUS_DOWN; +- + if (KSO_ENAB(bus)) { + + /* Enable clock for device interrupts */ +@@ -4240,6 +4994,9 @@ + + /* Turn off the backplane clock (only) */ + dhdsdio_clkctl(bus, CLK_SDONLY, FALSE); ++ ++ /* Change our idea of bus state */ ++ bus->dhd->busstate = DHD_BUS_DOWN; + } + + #ifdef PROP_TXSTATUS +@@ -4318,7 +5075,8 @@ + } else + #endif /* BCMSDIOH_TXGLOM */ + bus->txglom_enable = FALSE; +- printk("%s: enable %d\n", __FUNCTION__, bus->txglom_enable); ++ printf("%s: enable %d\n", __FUNCTION__, bus->txglom_enable); ++ dhd_conf_set_txglom_params(bus->dhd, bus->txglom_enable); + } + + int +@@ -5130,7 +5888,13 @@ + dhdsdio_sendpendctl(bus); + } else if (bus->dotxinrx && (bus->clkstate == CLK_AVAIL) && + !bus->fcstate && DATAOK(bus) && +- (pktq_mlen(&bus->txq, ~bus->flowcontrol) > bus->txinrx_thres)) { ++ (pktq_mlen(&bus->txq, ~bus->flowcontrol) > bus->txinrx_thres) && ++ bus->dhd->conf->tx_in_rx) { ++#if defined(SWTXGLOM) ++ if (bus->dhd->conf->swtxglom) ++ dhdsdio_sendfromq_swtxglom(bus, dhd_txbound); ++ else ++#endif + dhdsdio_sendfromq(bus, dhd_txbound); + #ifdef DHDTCPACK_SUPPRESS + /* In TCPACK_SUP_DELAYTX mode, do txinrx only if +@@ -5667,6 +6431,15 @@ + dhd_os_sdunlock(bus->dhd); + dhd_rx_frame(bus->dhd, ifidx, pkt, pkt_count, chan); + dhd_os_sdlock(bus->dhd); ++#if defined(SDIO_ISR_THREAD) ++ /* terence 20150615: fix for below error due to bussleep in watchdog after dhd_os_sdunlock here, ++ * so call BUS_WAKE to wake up bus again ++ * dhd_bcmsdh_recv_buf: Device asleep ++ * dhdsdio_readframes: RXHEADER FAILED: -40 ++ * dhdsdio_rxfail: abort command, terminate frame, send NAK ++ */ ++ BUS_WAKE(bus); ++#endif + } + rxcount = maxframes - rxleft; + #ifdef DHD_DEBUG +@@ -6009,12 +6782,24 @@ + else if ((bus->clkstate == CLK_AVAIL) && !bus->fcstate && + pktq_mlen(&bus->txq, ~bus->flowcontrol) && txlimit && DATAOK(bus)) { + framecnt = rxdone ? txlimit : MIN(txlimit, dhd_txminmax); ++#if defined(SWTXGLOM) ++ if (bus->dhd->conf->swtxglom) ++ framecnt = dhdsdio_sendfromq_swtxglom(bus, framecnt); ++ else ++#endif + framecnt = dhdsdio_sendfromq(bus, framecnt); + txlimit -= framecnt; + } + /* Resched the DPC if ctrl cmd is pending on bus credit */ +- if (bus->ctrl_frame_stat) ++ if (bus->ctrl_frame_stat) { ++ if (bus->dhd->conf->txctl_tmo_fix) { ++ set_current_state(TASK_INTERRUPTIBLE); ++ if (!kthread_should_stop()) ++ schedule_timeout(1); ++ set_current_state(TASK_RUNNING); ++ } + resched = TRUE; ++ } + + /* Resched if events or tx frames are pending, else await next interrupt */ + /* On failed register access, all bets are off: no resched or interrupts */ +@@ -6119,7 +6904,7 @@ + #if defined(SDIO_ISR_THREAD) + DHD_TRACE(("Calling dhdsdio_dpc() from %s\n", __FUNCTION__)); + DHD_OS_WAKE_LOCK(bus->dhd); +- /* terence 20150209: dpc should be scheded again if dpc_sched is TRUE or dhd_bus_txdata can ++ /* terence 20150209: dpc should be scheded again if dpc_sched is TRUE or dhd_bus_txdata can + not schedule anymore because dpc_sched is TRUE now. + */ + if (dhdsdio_dpc(bus)) { +@@ -6138,6 +6923,28 @@ + + } + ++#ifdef PKT_STATICS ++void dhdsdio_txpktstatics(void) ++{ ++ uint total, f1, f2, f3, f4; ++ printf("Randy: TYPE EVENT: %d pkts (size=%d) transfered\n", tx_statics.event_count, tx_statics.event_size); ++ printf("Randy: TYPE CTRL: %d pkts (size=%d) transfered\n", tx_statics.ctrl_count, tx_statics.ctrl_size); ++ printf("Randy: TYPE DATA: %d pkts (size=%d) transfered\n", tx_statics.data_count, tx_statics.data_size); ++ if(tx_statics.glom_1_count || tx_statics.glom_3_count || tx_statics.glom_3_8_count || tx_statics.glom_8_count) { ++ total = tx_statics.glom_1_count + tx_statics.glom_3_count + tx_statics.glom_3_8_count + tx_statics.glom_8_count; ++ f1 = (tx_statics.glom_1_count*100) / total; ++ f2 = (tx_statics.glom_3_count*100) / total; ++ f3 = (tx_statics.glom_3_8_count*100) / total; ++ f4 = (tx_statics.glom_8_count*100) / total; ++ printf("Randy: glomsize==1: %d(%d), tglomsize==2: %d(%d), pkts 3<=glomsize<8: %d(%d), pkts glomszie>=8: %d(%d)\n", ++ tx_statics.glom_1_count, f1, tx_statics.glom_3_count, f2, tx_statics.glom_3_8_count, f3, tx_statics.glom_8_count, f4); ++ printf("Randy: data/glom=%d, glom_max=%d\n", tx_statics.data_count/total, tx_statics.glom_max); ++ } ++ printf("Randy: TYPE RX GLOM: %d pkts (size=%d) transfered\n", tx_statics.glom_count, tx_statics.glom_size); ++ printf("Randy: TYPE TEST: %d pkts (size=%d) transfered\n\n\n", tx_statics.test_count, tx_statics.test_size); ++} ++#endif ++ + #ifdef SDTEST + static void + dhdsdio_pktgen_init(dhd_bus_t *bus) +@@ -6542,6 +7349,11 @@ + bus->lastintrs = bus->intrcount; + } + ++ if ((!bus->dpc_sched) && pktq_len(&bus->txq)) { ++ bus->dpc_sched = TRUE; ++ dhd_sched_dpc(bus->dhd); ++ } ++ + #ifdef DHD_DEBUG + /* Poll for console output periodically */ + if (dhdp->busstate == DHD_BUS_DATA && dhd_console_ms != 0) { +@@ -6581,7 +7393,7 @@ + + if ((bus->idletime > 0) && (bus->idlecount >= bus->idletime)) { + DHD_TIMER(("%s: DHD Idle state!!\n", __FUNCTION__)); +- if (SLPAUTO_ENAB(bus)) { ++ if (!bus->poll && SLPAUTO_ENAB(bus)) { + if (dhdsdio_bussleep(bus, TRUE) != BCME_BUSY) + dhd_os_wd_timer(bus->dhd, 0); + } else +@@ -6596,7 +7408,7 @@ + bus->idlecount = 0; + if (bus->activity) { + bus->activity = FALSE; +- if (SLPAUTO_ENAB(bus)) { ++ if (!bus->poll && SLPAUTO_ENAB(bus)) { + if (!bus->readframes) + dhdsdio_bussleep(bus, TRUE); + else +@@ -6751,6 +7563,8 @@ + return TRUE; + if (chipid == BCM4345_CHIP_ID) + return TRUE; ++ if (chipid == BCM43454_CHIP_ID) ++ return TRUE; + if (chipid == BCM4350_CHIP_ID) + return TRUE; + if (chipid == BCM4354_CHIP_ID) +@@ -6759,6 +7573,8 @@ + return TRUE; + if (chipid == BCM4358_CHIP_ID) + return TRUE; ++ if (chipid == BCM4371_CHIP_ID) ++ return TRUE; + if (chipid == BCM43430_CHIP_ID) + return TRUE; + if (BCM4349_CHIP(chipid)) +@@ -6766,13 +7582,19 @@ + return FALSE; + } + ++#if defined(MULTIPLE_SUPPLICANT) ++extern void wl_android_post_init(void); // terence 20120530: fix critical section in dhd_open and dhdsdio_probe ++#endif ++ + static void * + dhdsdio_probe(uint16 venid, uint16 devid, uint16 bus_no, uint16 slot, + uint16 func, uint bustype, void *regsva, osl_t * osh, void *sdh) + { + int ret; + dhd_bus_t *bus; ++#ifdef GET_OTP_MAC_ENABLE + struct ether_addr ea_addr; ++#endif + + #if defined(MULTIPLE_SUPPLICANT) + #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) +@@ -6784,7 +7606,7 @@ + } + mutex_lock(&_dhd_sdio_mutex_lock_); + #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) */ +-#endif ++#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 +@@ -6888,14 +7710,6 @@ + goto fail; + } + +-#ifdef PROP_TXSTATUS +- // terence 20131215: disable_proptx should be set before dhd_attach +- if ((bus->sih->chip == BCM43362_CHIP_ID) || (bus->sih->chip == BCM4330_CHIP_ID)) { +- printf("%s: Disable prop_txstatus\n", __FUNCTION__); +- disable_proptx = 1; +- } +-#endif +- + /* Attach to the dhd/OS/network interface */ + if (!(bus->dhd = dhd_attach(osh, bus, SDPCM_RESERVE))) { + DHD_ERROR(("%s: dhd_attach failed\n", __FUNCTION__)); +@@ -6932,21 +7746,16 @@ + + /* if firmware path present try to download and bring up bus */ + bus->dhd->hang_report = TRUE; ++#if 1 // terence 20150325: fix for WPA/WPA2 4-way handshake fail in hostapd + if (dhd_download_fw_on_driverload) { + if ((ret = dhd_bus_start(bus->dhd)) != 0) { + DHD_ERROR(("%s: dhd_bus_start failed\n", __FUNCTION__)); + goto fail; + } + } ++#endif + +-#ifdef GET_CUSTOM_MAC_ENABLE +- /* Read MAC address from external customer place */ +- memset(&ea_addr, 0, sizeof(ea_addr)); +- ret = dhd_custom_get_mac_address(ea_addr.octet); +- if (!ret) { +- memcpy(bus->dhd->mac.octet, (void *)&ea_addr, ETHER_ADDR_LEN); +- } +-#else ++#ifdef GET_OTP_MAC_ENABLE + if (dhd_conf_get_mac(bus->dhd, sdh, ea_addr.octet)) { + DHD_TRACE(("%s: Can not read MAC address\n", __FUNCTION__)); + } else +@@ -6961,11 +7770,12 @@ + + + #if defined(MULTIPLE_SUPPLICANT) ++ wl_android_post_init(); // terence 20120530: fix critical section in dhd_open and dhdsdio_probe + #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) + mutex_unlock(&_dhd_sdio_mutex_lock_); + DHD_ERROR(("%s : the lock is released.\n", __FUNCTION__)); + #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ +-#endif ++#endif + + init_waitqueue_head(&bus->bus_sleep); + +@@ -6980,7 +7790,7 @@ + mutex_unlock(&_dhd_sdio_mutex_lock_); + DHD_ERROR(("%s : the lock is released.\n", __FUNCTION__)); + #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ +-#endif ++#endif + + return NULL; + } +@@ -7281,7 +8091,7 @@ + #if defined(DHD_DEBUG) + DHD_ERROR(("F1 signature read @0x18000000=0x%4x\n", + bcmsdh_reg_read(bus->sdh, SI_ENUM_BASE, 4))); +-#endif ++#endif + + + /* Force PLL off until si_attach() programs PLL control regs */ +@@ -7414,13 +8224,16 @@ + case BCM4354_CHIP_ID: + case BCM4356_CHIP_ID: + case BCM4358_CHIP_ID: ++ case BCM4371_CHIP_ID: + bus->dongle_ram_base = CR4_4350_RAM_BASE; + break; + case BCM4360_CHIP_ID: + bus->dongle_ram_base = CR4_4360_RAM_BASE; + break; + case BCM4345_CHIP_ID: +- bus->dongle_ram_base = CR4_4345_RAM_BASE; ++ case BCM43454_CHIP_ID: ++ bus->dongle_ram_base = (bus->sih->chiprev < 6) /* from 4345C0 */ ++ ? CR4_4345_LT_C0_RAM_BASE : CR4_4345_GE_C0_RAM_BASE; + break; + case BCM4349_CHIP_GRPID: + bus->dongle_ram_base = CR4_4349_RAM_BASE; +@@ -7614,6 +8427,10 @@ + /* TX first in dhdsdio_readframes() */ + bus->dotxinrx = TRUE; + ++#ifdef PKT_STATICS ++ memset((uint8*) &tx_statics, 0, sizeof(pkt_statics_t)); ++#endif ++ + return TRUE; + } + +@@ -7638,7 +8455,6 @@ + { + int ret; + +- + DHD_TRACE_HW4(("%s: firmware path=%s, nvram path=%s\n", + __FUNCTION__, bus->fw_path, bus->nv_path)); + DHD_OS_WAKE_LOCK(bus->dhd); +@@ -7648,16 +8464,30 @@ + + /* External conf takes precedence if specified */ + dhd_conf_preinit(bus->dhd); +- dhd_conf_read_config(bus->dhd); ++ dhd_conf_read_config(bus->dhd, bus->dhd->conf_path); + dhd_conf_set_fw_name_by_chip(bus->dhd, bus->fw_path); +- dhd_conf_set_fw_path(bus->dhd, bus->fw_path); +- dhd_conf_set_nv_path(bus->dhd, bus->nv_path); ++ dhd_conf_set_nv_name_by_chip(bus->dhd, bus->nv_path); + dhd_conf_set_fw_name_by_mac(bus->dhd, bus->sdh, bus->fw_path); + dhd_conf_set_nv_name_by_mac(bus->dhd, bus->sdh, bus->nv_path); +- +- printk("Final fw_path=%s\n", bus->fw_path); +- printk("Final nv_path=%s\n", bus->nv_path); +- printk("Final conf_path=%s\n", bus->dhd->conf_path); ++ if (bus->dhd->conf->dhd_poll >= 0) { ++ printf("%s: set polling mode %d\n", __FUNCTION__, bus->dhd->conf->dhd_poll); ++ bus->poll = bus->dhd->conf->dhd_poll; ++ if (!bus->pollrate) ++ bus->pollrate = 1; ++ } ++ if (bus->dhd->conf->use_rxchain >= 0) { ++ printf("%s: set use_rxchain %d\n", __FUNCTION__, bus->dhd->conf->use_rxchain); ++ bus->use_rxchain = (bool)bus->dhd->conf->use_rxchain; ++ } ++ if (bus->dhd->conf->txglomsize >= 0) { ++ printf("%s: set txglomsize %d\n", __FUNCTION__, bus->dhd->conf->txglomsize); ++ bus->txglomsize = bus->dhd->conf->txglomsize; ++ } ++ bcmsdh_set_mode(sdh, bus->dhd->conf->txglom_mode); ++ ++ printf("Final fw_path=%s\n", bus->fw_path); ++ printf("Final nv_path=%s\n", bus->nv_path); ++ printf("Final conf_path=%s\n", bus->dhd->conf_path); + + ret = _dhdsdio_download_firmware(bus); + +@@ -7788,7 +8618,7 @@ + } + mutex_lock(&_dhd_sdio_mutex_lock_); + #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) */ +-#endif ++#endif + + + if (bus) { +@@ -7838,7 +8668,7 @@ + + if (dhd_os_check_if_up(bus->dhd)) + bcmsdh_oob_intr_set(bus->sdh, TRUE); +-#endif ++#endif + return 0; + } + +@@ -7988,7 +8818,7 @@ + + image = dhd_os_open_image(pfw_path); + if (image == NULL) { +- printk("%s: Open firmware file failed %s\n", __FUNCTION__, pfw_path); ++ printf("%s: Open firmware file failed %s\n", __FUNCTION__, pfw_path); + goto err; + } + +@@ -8009,6 +8839,14 @@ + + /* Download image */ + while ((len = dhd_os_get_image_block((char*)memptr, MEMBLOCK, image))) { ++ // terence 20150412: fix for firmware failed to download ++ if (bus->dhd->conf->chip == BCM43340_CHIP_ID || ++ bus->dhd->conf->chip == BCM43341_CHIP_ID) { ++ if (len % 64 != 0) { ++ memset(memptr+len, 0, len%64); ++ len += (64 - len%64); ++ } ++ } + if (len < 0) { + DHD_ERROR(("%s: dhd_os_get_image_block failed (%d)\n", __FUNCTION__, len)); + bcmerror = BCME_ERROR; +@@ -8102,7 +8940,7 @@ + if (nvram_file_exists) { + image = dhd_os_open_image(pnv_path); + if (image == NULL) { +- printk("%s: Open nvram file failed %s\n", __FUNCTION__, pnv_path); ++ printf("%s: Open nvram file failed %s\n", __FUNCTION__, pnv_path); + goto err; + } + } +@@ -8370,7 +9208,7 @@ + dhd_enable_oob_intr(bus, FALSE); + bcmsdh_oob_intr_set(bus->sdh, FALSE); + bcmsdh_oob_intr_unregister(bus->sdh); +-#endif ++#endif + + /* Clean tx/rx buffer pointers, detach from the dongle */ + dhdsdio_release_dongle(bus, bus->dhd->osh, TRUE, TRUE); +@@ -8380,14 +9218,14 @@ + dhd_txglom_enable(dhdp, FALSE); + dhd_os_sdunlock(dhdp); + +- printk("%s: WLAN OFF DONE\n", __FUNCTION__); ++ printf("%s: WLAN OFF DONE\n", __FUNCTION__); + /* App can now remove power from device */ + } else + bcmerror = BCME_SDIO_ERROR; + } else { + /* App must have restored power to device before calling */ + +- printk("\n\n%s: == WLAN ON ==\n", __FUNCTION__); ++ printf("\n\n%s: == WLAN ON ==\n", __FUNCTION__); + + if (bus->dhd->dongle_reset) { + /* Turn on WLAN */ +@@ -8411,7 +9249,9 @@ + bcmsdh_oob_intr_register(bus->sdh, + dhdsdio_isr, bus); + bcmsdh_oob_intr_set(bus->sdh, TRUE); +-#endif ++#elif defined(FORCE_WOWLAN) ++ dhd_enable_oob_intr(bus, TRUE); ++#endif + + bus->dhd->dongle_reset = FALSE; + bus->dhd->up = TRUE; +@@ -8419,7 +9259,7 @@ + #if !defined(IGNORE_ETH0_DOWN) + /* Restore flow control */ + dhd_txflowcontrol(bus->dhd, ALL_INTERFACES, OFF); +-#endif ++#endif + dhd_os_wd_timer(dhdp, dhd_watchdog_ms); + + DHD_TRACE(("%s: WLAN ON DONE\n", __FUNCTION__)); +@@ -8436,11 +9276,11 @@ + dhd_os_sdunlock(dhdp); + } else { + bcmerror = BCME_SDIO_ERROR; +- printk("%s called when dongle is not in reset\n", ++ printf("%s called when dongle is not in reset\n", + __FUNCTION__); +- printk("Will call dhd_bus_start instead\n"); ++ printf("Will call dhd_bus_start instead\n"); + dhd_bus_resume(dhdp, 1); +-#if defined(HW_OOB) ++#if defined(HW_OOB) || defined(FORCE_WOWLAN) + dhd_conf_set_hw_oob_intr(bus->sdh, bus->sih->chip); // terence 20120615: fix for OOB initial issue + #endif + if ((bcmerror = dhd_bus_start(dhdp)) != 0) +@@ -8448,6 +9288,10 @@ + __FUNCTION__, bcmerror)); + } + } ++ ++#ifdef PKT_STATICS ++ memset((uint8*) &tx_statics, 0, sizeof(pkt_statics_t)); ++#endif + return bcmerror; + } + +diff -Nur a/drivers/net/wireless/bcmdhd/dhd_static_buf.c c/drivers/net/wireless/bcmdhd/dhd_static_buf.c +--- a/drivers/net/wireless/bcmdhd/dhd_static_buf.c 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/dhd_static_buf.c 2016-05-13 09:48:20.000000000 +0200 +@@ -1,171 +1,179 @@ +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#define CONFIG_BROADCOM_WIFI_RESERVED_MEM +- +-#ifdef CONFIG_BROADCOM_WIFI_RESERVED_MEM +- +-#define WLAN_STATIC_PKT_BUF 4 +-#define WLAN_STATIC_SCAN_BUF0 5 +-#define WLAN_STATIC_SCAN_BUF1 6 +-#define WLAN_STATIC_DHD_INFO 7 +-#define PREALLOC_WLAN_SEC_NUM 5 +-#define PREALLOC_WLAN_BUF_NUM 160 +-#define PREALLOC_WLAN_SECTION_HEADER 24 +- +-#define WLAN_SECTION_SIZE_0 (PREALLOC_WLAN_BUF_NUM * 128) +-#define WLAN_SECTION_SIZE_1 (PREALLOC_WLAN_BUF_NUM * 128) +-#define WLAN_SECTION_SIZE_2 (PREALLOC_WLAN_BUF_NUM * 512) +-#define WLAN_SECTION_SIZE_3 (PREALLOC_WLAN_BUF_NUM * 1024) +-#define WLAN_SECTION_SIZE_7 (PREALLOC_WLAN_BUF_NUM * 128) +- +-#define DHD_SKB_HDRSIZE 336 +-#define DHD_SKB_1PAGE_BUFSIZE ((PAGE_SIZE*1)-DHD_SKB_HDRSIZE) +-#define DHD_SKB_2PAGE_BUFSIZE ((PAGE_SIZE*2)-DHD_SKB_HDRSIZE) +-#define DHD_SKB_4PAGE_BUFSIZE ((PAGE_SIZE*4)-DHD_SKB_HDRSIZE) +- +-#define WLAN_SKB_BUF_NUM 17 +- +-static struct sk_buff *wlan_static_skb[WLAN_SKB_BUF_NUM]; +- +-struct wlan_mem_prealloc { +- void *mem_ptr; +- unsigned long size; +-}; +- +-static struct wlan_mem_prealloc wlan_mem_array[PREALLOC_WLAN_SEC_NUM] = { +- {NULL, (WLAN_SECTION_SIZE_0 + PREALLOC_WLAN_SECTION_HEADER)}, +- {NULL, (WLAN_SECTION_SIZE_1 + PREALLOC_WLAN_SECTION_HEADER)}, +- {NULL, (WLAN_SECTION_SIZE_2 + PREALLOC_WLAN_SECTION_HEADER)}, +- {NULL, (WLAN_SECTION_SIZE_3 + PREALLOC_WLAN_SECTION_HEADER)}, +- {NULL, (WLAN_SECTION_SIZE_7 + PREALLOC_WLAN_SECTION_HEADER)} +-}; +- +-void *wlan_static_scan_buf0; +-void *wlan_static_scan_buf1; +-void *bcmdhd_mem_prealloc(int section, unsigned long size) +-{ +- if (section == WLAN_STATIC_PKT_BUF) { +- printk("1 %s: section=%d, wlan_static_skb=%p\n", +- __FUNCTION__, section, wlan_static_skb); +- return wlan_static_skb; +- } +- if (section == WLAN_STATIC_SCAN_BUF0) { +- printk("2 %s: section=%d, wlan_static_scan_buf0=%p\n", +- __FUNCTION__, section, wlan_static_scan_buf0); +- return wlan_static_scan_buf0; +- } +- if (section == WLAN_STATIC_SCAN_BUF1) { +- printk("3 %s: section=%d, wlan_static_scan_buf1=%p\n", +- __FUNCTION__, section, wlan_static_scan_buf1); +- return wlan_static_scan_buf1; +- } +- if (section == WLAN_STATIC_DHD_INFO) { +- printk("4 %s: section=%d, wlan_mem_array[4]=%p\n", +- __FUNCTION__, section, wlan_mem_array[4].mem_ptr); +- return wlan_mem_array[4].mem_ptr; +- } +- if ((section < 0) || (section > PREALLOC_WLAN_SEC_NUM)) { +- printk("5 %s: out of section %d\n", __FUNCTION__, section); +- return NULL; +- } +- +- if (wlan_mem_array[section].size < size) { +- printk("6 %s: wlan_mem_array[section].size=%lu, size=%lu\n", +- __FUNCTION__, wlan_mem_array[section].size, size); +- return NULL; +- } +- printk("7 %s: wlan_mem_array[section].mem_ptr=%p, size=%lu\n", +- __FUNCTION__, &wlan_mem_array[section], size); +- +- return wlan_mem_array[section].mem_ptr; +-} +- +-EXPORT_SYMBOL(bcmdhd_mem_prealloc); +- +-int bcmdhd_init_wlan_mem(void) +-{ +- int i; +- int j; +- +- for (i=0; i<8; i++) { +- wlan_static_skb[i] = dev_alloc_skb(DHD_SKB_1PAGE_BUFSIZE); +- if (!wlan_static_skb[i]) +- goto err_skb_alloc; +- printk("1 %s: wlan_static_skb[%d]=%p, size=%lu\n", +- __FUNCTION__, i, wlan_static_skb[i], DHD_SKB_1PAGE_BUFSIZE); +- } +- +- for (; i<16; i++) { +- wlan_static_skb[i] = dev_alloc_skb(DHD_SKB_2PAGE_BUFSIZE); +- if (!wlan_static_skb[i]) +- goto err_skb_alloc; +- printk("2 %s: wlan_static_skb[%d]=%p, size=%lu\n", +- __FUNCTION__, i, wlan_static_skb[i], DHD_SKB_2PAGE_BUFSIZE); +- } +- +- wlan_static_skb[i] = dev_alloc_skb(DHD_SKB_4PAGE_BUFSIZE); +- if (!wlan_static_skb[i]) +- goto err_skb_alloc; +- printk("3 %s: wlan_static_skb[%d]=%p, size=%lu\n", +- __FUNCTION__, i, wlan_static_skb[i], DHD_SKB_4PAGE_BUFSIZE); +- +- for (i=0; i ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define CONFIG_BROADCOM_WIFI_RESERVED_MEM ++ ++#ifdef CONFIG_BROADCOM_WIFI_RESERVED_MEM ++ ++#define WLAN_STATIC_PKT_BUF 4 ++#define WLAN_STATIC_SCAN_BUF0 5 ++#define WLAN_STATIC_SCAN_BUF1 6 ++#define WLAN_STATIC_DHD_INFO 7 ++#define WLAN_STATIC_DHD_WLFC_INFO 8 ++#define PREALLOC_WLAN_SEC_NUM 6 ++#define PREALLOC_WLAN_BUF_NUM 160 ++#define PREALLOC_WLAN_SECTION_HEADER 24 ++ ++#define WLAN_SECTION_SIZE_0 (PREALLOC_WLAN_BUF_NUM * 128) ++#define WLAN_SECTION_SIZE_1 (PREALLOC_WLAN_BUF_NUM * 128) ++#define WLAN_SECTION_SIZE_2 (PREALLOC_WLAN_BUF_NUM * 512) ++#define WLAN_SECTION_SIZE_3 (PREALLOC_WLAN_BUF_NUM * 1024) ++#define WLAN_SECTION_SIZE_7 (PREALLOC_WLAN_BUF_NUM * 128) ++#define WLAN_SECTION_SIZE_8 (PREALLOC_WLAN_BUF_NUM * 512) ++ ++#define DHD_SKB_HDRSIZE 336 ++#define DHD_SKB_1PAGE_BUFSIZE ((PAGE_SIZE*1)-DHD_SKB_HDRSIZE) ++#define DHD_SKB_2PAGE_BUFSIZE ((PAGE_SIZE*2)-DHD_SKB_HDRSIZE) ++#define DHD_SKB_4PAGE_BUFSIZE ((PAGE_SIZE*4)-DHD_SKB_HDRSIZE) ++ ++#define WLAN_SKB_BUF_NUM 17 ++ ++static struct sk_buff *wlan_static_skb[WLAN_SKB_BUF_NUM]; ++ ++struct wlan_mem_prealloc { ++ void *mem_ptr; ++ unsigned long size; ++}; ++ ++static struct wlan_mem_prealloc wlan_mem_array[PREALLOC_WLAN_SEC_NUM] = { ++ {NULL, (WLAN_SECTION_SIZE_0 + PREALLOC_WLAN_SECTION_HEADER)}, ++ {NULL, (WLAN_SECTION_SIZE_1 + PREALLOC_WLAN_SECTION_HEADER)}, ++ {NULL, (WLAN_SECTION_SIZE_2 + PREALLOC_WLAN_SECTION_HEADER)}, ++ {NULL, (WLAN_SECTION_SIZE_3 + PREALLOC_WLAN_SECTION_HEADER)}, ++ {NULL, (WLAN_SECTION_SIZE_7 + PREALLOC_WLAN_SECTION_HEADER)}, ++ {NULL, (WLAN_SECTION_SIZE_8 + PREALLOC_WLAN_SECTION_HEADER)} ++}; ++ ++void *wlan_static_scan_buf0; ++void *wlan_static_scan_buf1; ++void *bcmdhd_mem_prealloc(int section, unsigned long size) ++{ ++ if (section == WLAN_STATIC_PKT_BUF) { ++ printk("1 %s: section=%d, wlan_static_skb=%p\n", ++ __FUNCTION__, section, wlan_static_skb); ++ return wlan_static_skb; ++ } ++ if (section == WLAN_STATIC_SCAN_BUF0) { ++ printk("2 %s: section=%d, wlan_static_scan_buf0=%p\n", ++ __FUNCTION__, section, wlan_static_scan_buf0); ++ return wlan_static_scan_buf0; ++ } ++ if (section == WLAN_STATIC_SCAN_BUF1) { ++ printk("3 %s: section=%d, wlan_static_scan_buf1=%p\n", ++ __FUNCTION__, section, wlan_static_scan_buf1); ++ return wlan_static_scan_buf1; ++ } ++ if (section == WLAN_STATIC_DHD_INFO) { ++ printk("4 %s: section=%d, wlan_mem_array[4]=%p\n", ++ __FUNCTION__, section, wlan_mem_array[4].mem_ptr); ++ return wlan_mem_array[4].mem_ptr; ++ } ++ if (section == WLAN_STATIC_DHD_WLFC_INFO) { ++ printk("5 %s: section=%d, wlan_mem_array[5]=%p\n", ++ __FUNCTION__, section, wlan_mem_array[5].mem_ptr); ++ return wlan_mem_array[5].mem_ptr; ++ } ++ if ((section < 0) || (section > PREALLOC_WLAN_SEC_NUM)) { ++ printk("6 %s: out of section %d\n", __FUNCTION__, section); ++ return NULL; ++ } ++ ++ if (wlan_mem_array[section].size < size) { ++ printk("7 %s: wlan_mem_array[section].size=%lu, size=%lu\n", ++ __FUNCTION__, wlan_mem_array[section].size, size); ++ return NULL; ++ } ++ printk("8 %s: wlan_mem_array[section].mem_ptr=%p, size=%lu\n", ++ __FUNCTION__, &wlan_mem_array[section], size); ++ ++ return wlan_mem_array[section].mem_ptr; ++} ++ ++EXPORT_SYMBOL(bcmdhd_mem_prealloc); ++ ++int bcmdhd_init_wlan_mem(void) ++{ ++ int i; ++ int j; ++ ++ for (i=0; i<8; i++) { ++ wlan_static_skb[i] = dev_alloc_skb(DHD_SKB_1PAGE_BUFSIZE); ++ if (!wlan_static_skb[i]) ++ goto err_skb_alloc; ++ printk("1 %s: wlan_static_skb[%d]=%p, size=%lu\n", ++ __FUNCTION__, i, wlan_static_skb[i], DHD_SKB_1PAGE_BUFSIZE); ++ } ++ ++ for (; i<16; i++) { ++ wlan_static_skb[i] = dev_alloc_skb(DHD_SKB_2PAGE_BUFSIZE); ++ if (!wlan_static_skb[i]) ++ goto err_skb_alloc; ++ printk("2 %s: wlan_static_skb[%d]=%p, size=%lu\n", ++ __FUNCTION__, i, wlan_static_skb[i], DHD_SKB_2PAGE_BUFSIZE); ++ } ++ ++ wlan_static_skb[i] = dev_alloc_skb(DHD_SKB_4PAGE_BUFSIZE); ++ if (!wlan_static_skb[i]) ++ goto err_skb_alloc; ++ printk("3 %s: wlan_static_skb[%d]=%p, size=%lu\n", ++ __FUNCTION__, i, wlan_static_skb[i], DHD_SKB_4PAGE_BUFSIZE); ++ ++ for (i=0; i + #include + #endif +-#ifdef DHDTCPACK_SUPPRESS + #include +-#endif /* DHDTCPACK_SUPPRESS */ + + + /* +@@ -444,7 +442,7 @@ + } + + static int +-_dhd_wlfc_pushheader(athost_wl_status_info_t* ctx, void* p, bool tim_signal, ++_dhd_wlfc_pushheader(athost_wl_status_info_t* ctx, void** packet, bool tim_signal, + uint8 tim_bmp, uint8 mac_handle, uint32 htodtag, uint16 htodseq, bool skip_wlfc_hdr) + { + uint32 wl_pktinfo = 0; +@@ -455,6 +453,7 @@ + dhd_pub_t *dhdp = (dhd_pub_t *)ctx->dhdp; + + struct bdc_header *h; ++ void *p = *packet; + + if (skip_wlfc_hdr) + goto push_bdc_hdr; +@@ -512,6 +511,7 @@ + h->flags2 = 0; + h->dataOffset = dataOffset >> 2; + BDC_SET_IF_IDX(h, DHD_PKTTAG_IF(PKTTAG(p))); ++ *packet = p; + return BCME_OK; + } + +@@ -559,8 +559,7 @@ + * STA/GC gets the Mac Entry for TDLS destinations, TDLS destinations + * have their own entry. + */ +- if ((iftype == WLC_E_IF_ROLE_STA || ETHER_ISMULTI(dstn) || +- iftype == WLC_E_IF_ROLE_P2P_CLIENT) && ++ if ((DHD_IF_ROLE_STA(iftype) || ETHER_ISMULTI(dstn)) && + (ctx->destination_entries.interfaces[ifid].occupied)) { + entry = &ctx->destination_entries.interfaces[ifid]; + } +@@ -612,7 +611,7 @@ + /* pkt in delayed q, so fake push BDC header for + * dhd_tcpack_check_xmit() and dhd_txcomplete(). + */ +- _dhd_wlfc_pushheader(ctx, p, FALSE, 0, 0, 0, 0, TRUE); ++ _dhd_wlfc_pushheader(ctx, &p, FALSE, 0, 0, 0, 0, TRUE); + + /* This packet is about to be freed, so remove it from tcp_ack_info_tbl + * This must be one of... +@@ -877,7 +876,7 @@ + if (p) { + PKTPULL(ctx->osh, p, dummylen); + DHD_PKTTAG_SET_H2DTAG(PKTTAG(p), 0); +- _dhd_wlfc_pushheader(ctx, p, TRUE, ta_bmp, entry->mac_handle, 0, 0, FALSE); ++ _dhd_wlfc_pushheader(ctx, &p, TRUE, ta_bmp, entry->mac_handle, 0, 0, FALSE); + DHD_PKTTAG_SETSIGNALONLY(PKTTAG(p), 1); + DHD_PKTTAG_WLFCPKT_SET(PKTTAG(p), 1); + #ifdef PROP_TXSTATUS_DEBUG +@@ -974,16 +973,17 @@ + + static int + _dhd_wlfc_pretx_pktprocess(athost_wl_status_info_t* ctx, +- wlfc_mac_descriptor_t* entry, void* p, int header_needed, uint32* slot) ++ wlfc_mac_descriptor_t* entry, void** packet, int header_needed, uint32* slot) + { + int rc = BCME_OK; + int hslot = WLFC_HANGER_MAXITEMS; + bool send_tim_update = FALSE; + uint32 htod = 0; + uint16 htodseq = 0; +- uint8 free_ctr; ++ uint8 free_ctr, flags = 0; + int gen = 0xff; + dhd_pub_t *dhdp = (dhd_pub_t *)ctx->dhdp; ++ void * p = *packet; + + *slot = hslot; + +@@ -1035,24 +1035,26 @@ + return BCME_ERROR; + } + +- WL_TXSTATUS_SET_FREERUNCTR(htod, free_ctr); +- WL_TXSTATUS_SET_HSLOT(htod, hslot); +- WL_TXSTATUS_SET_FIFO(htod, DHD_PKTTAG_FIFO(PKTTAG(p))); +- WL_TXSTATUS_SET_FLAGS(htod, WLFC_PKTFLAG_PKTFROMHOST); +- WL_TXSTATUS_SET_GENERATION(htod, gen); +- DHD_PKTTAG_SETPKTDIR(PKTTAG(p), 1); +- ++ flags = WLFC_PKTFLAG_PKTFROMHOST; + if (!DHD_PKTTAG_CREDITCHECK(PKTTAG(p))) { + /* + Indicate that this packet is being sent in response to an + explicit request from the firmware side. + */ +- WLFC_PKTFLAG_SET_PKTREQUESTED(htod); +- } else { +- WLFC_PKTFLAG_CLR_PKTREQUESTED(htod); ++ flags |= WLFC_PKTFLAG_PKT_REQUESTED; ++ } ++ if (pkt_is_dhcp(ctx->osh, p)) { ++ flags |= WLFC_PKTFLAG_PKT_FORCELOWRATE; + } + +- rc = _dhd_wlfc_pushheader(ctx, p, send_tim_update, ++ WL_TXSTATUS_SET_FREERUNCTR(htod, free_ctr); ++ WL_TXSTATUS_SET_HSLOT(htod, hslot); ++ WL_TXSTATUS_SET_FIFO(htod, DHD_PKTTAG_FIFO(PKTTAG(p))); ++ WL_TXSTATUS_SET_FLAGS(htod, flags); ++ WL_TXSTATUS_SET_GENERATION(htod, gen); ++ DHD_PKTTAG_SETPKTDIR(PKTTAG(p), 1); ++ ++ rc = _dhd_wlfc_pushheader(ctx, &p, send_tim_update, + entry->traffic_lastreported_bmp, entry->mac_handle, htod, htodseq, FALSE); + if (rc == BCME_OK) { + DHD_PKTTAG_SET_H2DTAG(PKTTAG(p), htod); +@@ -1081,6 +1083,7 @@ + } + } + *slot = hslot; ++ *packet = p; + return rc; + } + +@@ -1147,6 +1150,11 @@ + } + ASSERT(entry); + ++ if (entry->transit_count < 0) { ++ DHD_ERROR(("Error: %s():%d transit_count %d < 0\n", ++ __FUNCTION__, __LINE__, entry->transit_count)); ++ continue; ++ } + if (entry->occupied && _dhd_wlfc_is_destination_open(ctx, entry, prec) && + (entry->transit_count < WL_TXSTATUS_FREERUNCTR_MASK) && + !(WLFC_GET_REORDERSUPP(dhdp->wlfc_mode) && entry->suppressed)) { +@@ -1346,7 +1354,9 @@ + } else { + if (item->pkt_state & WLFC_HANGER_PKT_STATE_TXSTATUS) { + /* free slot */ +- ASSERT(item->state != WLFC_HANGER_ITEM_STATE_FREE); ++ if (item->state == WLFC_HANGER_ITEM_STATE_FREE) ++ DHD_ERROR(("Error: %s():%d get multi TXSTATUS for one packet???\n", ++ __FUNCTION__, __LINE__)); + item->state = WLFC_HANGER_ITEM_STATE_FREE; + } + } +@@ -1393,7 +1403,7 @@ + /* pkt in delayed q, so fake push BDC header for + * dhd_tcpack_check_xmit() and dhd_txcomplete(). + */ +- _dhd_wlfc_pushheader(ctx, p, FALSE, 0, 0, ++ _dhd_wlfc_pushheader(ctx, &p, FALSE, 0, 0, + 0, 0, TRUE); + #ifdef DHDTCPACK_SUPPRESS + if (dhd_tcpack_check_xmit(dhdp, p) == BCME_ERROR) { +@@ -1636,6 +1646,18 @@ + memcpy(&entry->ea[0], ea, ETHER_ADDR_LEN); + + if (action == eWLFC_MAC_ENTRY_ACTION_ADD) { ++ entry->suppressed = FALSE; ++ entry->transit_count = 0; ++ entry->suppr_transit_count = 0; ++ } ++ ++#ifdef P2PONEINT ++ if ((action == eWLFC_MAC_ENTRY_ACTION_ADD) || ++ ((action == eWLFC_MAC_ENTRY_ACTION_UPDATE) && (entry->psq.num_prec == 0))) ++#else ++ if (action == eWLFC_MAC_ENTRY_ACTION_ADD) ++#endif ++ { + dhd_pub_t *dhdp = (dhd_pub_t *)(ctx->dhdp); + pktq_init(&entry->psq, WLFC_PSQ_PREC_COUNT, WLFC_PSQ_LEN); + if (WLFC_GET_AFQ(dhdp->wlfc_mode)) { +@@ -1669,11 +1691,7 @@ + _dhd_wlfc_flow_control_check(ctx, &entry->psq, ifid); + + entry->occupied = 0; +- entry->suppressed = 0; + entry->state = WLFC_STATE_CLOSE; +- entry->requested_credit = 0; +- entry->transit_count = 0; +- entry->suppr_transit_count = 0; + memset(&entry->ea[0], 0, ETHER_ADDR_LEN); + + if (entry->next) { +@@ -1826,7 +1844,7 @@ + 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, ++ rc = _dhd_wlfc_pretx_pktprocess(ctx, commit_info->mac_entry, &commit_info->p, + commit_info->needs_hdr, &hslot); + + if (rc == BCME_OK) { +@@ -2505,7 +2523,8 @@ + } + + /* allocate space to track txstatus propagated from firmware */ +- dhd->wlfc_state = MALLOC(dhd->osh, sizeof(athost_wl_status_info_t)); ++ dhd->wlfc_state = DHD_OS_PREALLOC(dhd, DHD_PREALLOC_DHD_WLFC_INFO, ++ sizeof(athost_wl_status_info_t)); + if (dhd->wlfc_state == NULL) { + rc = BCME_NOMEM; + goto exit; +@@ -2522,7 +2541,8 @@ + if (!WLFC_GET_AFQ(dhd->wlfc_mode)) { + wlfc->hanger = _dhd_wlfc_hanger_create(dhd->osh, WLFC_HANGER_MAXITEMS); + if (wlfc->hanger == NULL) { +- MFREE(dhd->osh, dhd->wlfc_state, sizeof(athost_wl_status_info_t)); ++ DHD_OS_PREFREE(dhd, dhd->wlfc_state, ++ sizeof(athost_wl_status_info_t)); + dhd->wlfc_state = NULL; + rc = BCME_NOMEM; + goto exit; +@@ -2780,7 +2800,7 @@ + if (pktbuf) { + uint32 htod = 0; + WL_TXSTATUS_SET_FLAGS(htod, WLFC_PKTFLAG_PKTFROMHOST); +- _dhd_wlfc_pushheader(ctx, pktbuf, FALSE, 0, 0, htod, 0, FALSE); ++ _dhd_wlfc_pushheader(ctx, &pktbuf, FALSE, 0, 0, htod, 0, FALSE); + if (fcommit(commit_ctx, pktbuf)) + PKTFREE(ctx->osh, pktbuf, TRUE); + rc = BCME_OK; +@@ -3351,7 +3371,8 @@ + + + /* free top structure */ +- MFREE(dhd->osh, dhd->wlfc_state, sizeof(athost_wl_status_info_t)); ++ DHD_OS_PREFREE(dhd, dhd->wlfc_state, ++ sizeof(athost_wl_status_info_t)); + dhd->wlfc_state = NULL; + dhd->proptxstatus_mode = hostreorder ? + WLFC_ONLY_AMPDU_HOSTREORDER : WLFC_FCMODE_NONE; +diff -Nur a/drivers/net/wireless/bcmdhd/dhd_wlfc.h c/drivers/net/wireless/bcmdhd/dhd_wlfc.h +--- a/drivers/net/wireless/bcmdhd/dhd_wlfc.h 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/dhd_wlfc.h 2016-05-13 09:48:20.000000000 +0200 +@@ -1,6 +1,6 @@ + /* + * $Copyright Open 2009 Broadcom Corporation$ +-* $Id: dhd_wlfc.h 490028 2014-07-09 05:58:25Z $ ++* $Id: dhd_wlfc.h 501046 2014-09-06 01:25:16Z $ + * + */ + #ifndef __wlfc_host_driver_definitions_h__ +@@ -115,9 +115,9 @@ + uint8 send_tim_signal; + uint8 mac_handle; + /* Number of packets at dongle for this entry. */ +- uint transit_count; ++ int transit_count; + /* Numbe of suppression to wait before evict from delayQ */ +- uint suppr_transit_count; ++ int suppr_transit_count; + /* flag. TRUE when in suppress state */ + uint8 suppressed; + +diff -Nur a/drivers/net/wireless/bcmdhd/hnd_pktq.c c/drivers/net/wireless/bcmdhd/hnd_pktq.c +--- a/drivers/net/wireless/bcmdhd/hnd_pktq.c 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/hnd_pktq.c 2016-05-13 09:48:20.000000000 +0200 +@@ -573,6 +573,12 @@ + + q->len--; + ++ // terence 20150308: fix for non-null pointer of skb->prev sent from ndo_start_xmit ++ if (q->len == 0) { ++ q->head = NULL; ++ q->tail = NULL; ++ } ++ + if (prec_out) + *prec_out = prec; + +diff -Nur a/drivers/net/wireless/bcmdhd/include/bcmdefs.h c/drivers/net/wireless/bcmdhd/include/bcmdefs.h +--- a/drivers/net/wireless/bcmdhd/include/bcmdefs.h 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/include/bcmdefs.h 2016-05-13 09:48:20.000000000 +0200 +@@ -230,7 +230,7 @@ + + #if defined(BCMASSERT_LOG) + #define BCMASSERT_SUPPORT +-#endif ++#endif + + /* Macros for doing definition and get/set of bitfields + * Usage example, e.g. a three-bit field (bits 4-6): +diff -Nur a/drivers/net/wireless/bcmdhd/include/bcmdevs.h c/drivers/net/wireless/bcmdhd/include/bcmdevs.h +--- a/drivers/net/wireless/bcmdhd/include/bcmdevs.h 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/include/bcmdevs.h 2016-05-13 09:48:20.000000000 +0200 +@@ -352,6 +352,7 @@ + #define BCM43569_CHIP_ID 0xAA31 /* 43569 chipcommon chipid */ + #define BCM43570_CHIP_ID 0xAA32 /* 43570 chipcommon chipid */ + #define BCM4358_CHIP_ID 0x4358 /* 4358 chipcommon chipid */ ++#define BCM4371_CHIP_ID 0x4371 /* 4371 chipcommon chipid */ + #define BCM4350_CHIP(chipid) ((CHIPID(chipid) == BCM4350_CHIP_ID) || \ + (CHIPID(chipid) == BCM4354_CHIP_ID) || \ + (CHIPID(chipid) == BCM4356_CHIP_ID) || \ +@@ -364,6 +365,7 @@ + (CHIPID(chipid) == BCM43570_CHIP_ID) || \ + (CHIPID(chipid) == BCM4358_CHIP_ID)) /* 4350 variations */ + #define BCM4345_CHIP_ID 0x4345 /* 4345 chipcommon chipid */ ++#define BCM43454_CHIP_ID 43454 /* 43454 chipcommon chipid */ + #define BCM43430_CHIP_ID 43430 /* 43430 chipcommon chipid */ + #define BCM4349_CHIP_ID 0x4349 /* 4349 chipcommon chipid */ + #define BCM4355_CHIP_ID 0x4355 /* 4355 chipcommon chipid */ +diff -Nur a/drivers/net/wireless/bcmdhd/include/bcmmsgbuf.h c/drivers/net/wireless/bcmdhd/include/bcmmsgbuf.h +--- a/drivers/net/wireless/bcmdhd/include/bcmmsgbuf.h 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/include/bcmmsgbuf.h 2016-05-13 09:48:20.000000000 +0200 +@@ -6,7 +6,7 @@ + * + * $Copyright Open Broadcom Corporation$ + * +- * $Id: bcmmsgbuf.h 490808 2014-07-12 00:33:13Z $ ++ * $Id: bcmmsgbuf.h 499474 2014-08-28 21:30:10Z $ + */ + #ifndef _bcmmsgbuf_h_ + #define _bcmmsgbuf_h_ +@@ -479,7 +479,7 @@ + uint16 metadata_buf_len; + /* provided data buffer len to receive data */ + uint16 data_len; +- uint32 rsvd; ++ uint32 flag2; + } host_txbuf_post_t; + + #define BCMPCIE_PKT_FLAGS_FRAME_802_3 0x01 +@@ -498,6 +498,9 @@ + #define BCMPCIE_TXPOST_FLAGS_PRIO_SHIFT BCMPCIE_PKT_FLAGS_PRIO_SHIFT + #define BCMPCIE_TXPOST_FLAGS_PRIO_MASK BCMPCIE_PKT_FLAGS_PRIO_MASK + ++#define BCMPCIE_PKT_FLAGS2_FORCELOWRATE_MASK 0x01 ++#define BCMPCIE_PKT_FLAGS2_FORCELOWRATE_SHIFT 0 ++ + /* H2D Txpost ring work items */ + typedef union txbuf_submit_item { + host_txbuf_post_t txpost; +diff -Nur a/drivers/net/wireless/bcmdhd/include/bcmpcie.h c/drivers/net/wireless/bcmdhd/include/bcmpcie.h +--- a/drivers/net/wireless/bcmdhd/include/bcmpcie.h 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/include/bcmpcie.h 2016-05-13 09:48:20.000000000 +0200 +@@ -4,7 +4,7 @@ + * Explains the shared area between host and dongle + * $Copyright Open 2005 Broadcom Corporation$ + * +- * $Id: bcmpcie.h 490808 2014-07-12 00:33:13Z $ ++ * $Id: bcmpcie.h 497456 2014-08-19 15:06:33Z $ + */ + + #ifndef _bcmpcie_h_ +@@ -46,6 +46,12 @@ + #define PCIE_SHARED_EVT_SEQNUM 0x08000 + #define PCIE_SHARED_DMA_INDEX 0x10000 + ++/* D2H M2M DMA Complete Sync mechanism: Modulo-253-SeqNum or XORCSUM */ ++#define PCIE_SHARED_D2H_SYNC_SEQNUM 0x20000 ++#define PCIE_SHARED_D2H_SYNC_XORCSUM 0x40000 ++#define PCIE_SHARED_D2H_SYNC_MODE_MASK \ ++ (PCIE_SHARED_D2H_SYNC_SEQNUM | PCIE_SHARED_D2H_SYNC_XORCSUM) ++ + #define BCMPCIE_H2D_MSGRING_CONTROL_SUBMIT 0 + #define BCMPCIE_H2D_MSGRING_RXPOST_SUBMIT 1 + #define BCMPCIE_D2H_MSGRING_CONTROL_COMPLETE 2 +diff -Nur a/drivers/net/wireless/bcmdhd/include/bcmsdbus.h c/drivers/net/wireless/bcmdhd/include/bcmsdbus.h +--- a/drivers/net/wireless/bcmdhd/include/bcmsdbus.h 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/include/bcmsdbus.h 2016-05-13 09:48:20.000000000 +0200 +@@ -122,4 +122,14 @@ + extern SDIOH_API_RC sdioh_gpioouten(sdioh_info_t *sd, uint32 gpio); + extern SDIOH_API_RC sdioh_gpioout(sdioh_info_t *sd, uint32 gpio, bool enab); + ++extern uint sdioh_set_mode(sdioh_info_t *sd, uint mode); ++#if defined(SWTXGLOM) ++/* read or write any buffer using cmd53 */ ++extern SDIOH_API_RC sdioh_request_swtxglom_buffer(sdioh_info_t *si, uint pio_dma, uint fix_inc, ++ uint rw, uint fnc_num, uint32 addr, uint regwidth, uint32 buflen, uint8 *buffer, ++ void *pkt); ++extern void sdioh_glom_post(sdioh_info_t *sd, uint8 *frame, void *pkt, uint len); ++extern void sdioh_glom_clear(sdioh_info_t *sd); ++#endif ++ + #endif /* _sdio_api_h_ */ +diff -Nur a/drivers/net/wireless/bcmdhd/include/bcmsdh.h c/drivers/net/wireless/bcmdhd/include/bcmsdh.h +--- a/drivers/net/wireless/bcmdhd/include/bcmsdh.h 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/include/bcmsdh.h 2016-05-13 09:48:20.000000000 +0200 +@@ -49,7 +49,7 @@ + uint32 sbwad; /* Save backplane window address */ + void *os_cxt; /* Pointer to per-OS private data */ + }; +-#endif ++#endif + + /* Detach - freeup resources allocated in attach */ + extern int bcmsdh_detach(osl_t *osh, void *sdh); +@@ -132,6 +132,11 @@ + extern int bcmsdh_recv_buf(void *sdh, uint32 addr, uint fn, uint flags, + uint8 *buf, uint nbytes, void *pkt, + bcmsdh_cmplt_fn_t complete_fn, void *handle); ++#if defined(SWTXGLOM) ++extern int bcmsdh_send_swtxglom_buf(void *sdh, uint32 addr, uint fn, uint flags, ++ uint8 *buf, uint nbytes, void *pkt, ++ bcmsdh_cmplt_fn_t complete_fn, void *handle); ++#endif + + extern void bcmsdh_glom_post(void *sdh, uint8 *frame, void *pkt, uint len); + extern void bcmsdh_glom_clear(void *sdh); +diff -Nur a/drivers/net/wireless/bcmdhd/include/bcmsdh_sdmmc.h c/drivers/net/wireless/bcmdhd/include/bcmsdh_sdmmc.h +--- a/drivers/net/wireless/bcmdhd/include/bcmsdh_sdmmc.h 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/include/bcmsdh_sdmmc.h 2016-05-13 09:48:20.000000000 +0200 +@@ -2,13 +2,13 @@ + * BCMSDH Function Driver for the native SDIO/MMC driver in the Linux Kernel + * + * Copyright (C) 1999-2014, Broadcom Corporation +- * ++ * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: +- * ++ * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that +@@ -16,12 +16,12 @@ + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. +- * ++ * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * +- * $Id: bcmsdh_sdmmc.h 408158 2013-06-17 22:15:35Z $ ++ * $Id: bcmsdh_sdmmc.h 496576 2014-08-13 15:04:56Z $ + */ + + #ifndef __BCMSDH_SDMMC_H__ +@@ -33,6 +33,7 @@ + #define sd_debug(x) do { if (sd_msglevel & SDH_DEBUG_VAL) printf x; } while (0) + #define sd_data(x) do { if (sd_msglevel & SDH_DATA_VAL) printf x; } while (0) + #define sd_ctrl(x) do { if (sd_msglevel & SDH_CTRL_VAL) printf x; } while (0) ++#define sd_cost(x) do { if (sd_msglevel & SDH_COST_VAL) printf x; } while (0) + + + #define sd_sync_dma(sd, read, nbytes) +@@ -57,7 +58,15 @@ + /* private bus modes */ + #define SDIOH_MODE_SD4 2 + #define CLIENT_INTR 0x100 /* Get rid of this! */ +-#define SDIOH_SDMMC_MAX_SG_ENTRIES 32 ++#define SDIOH_SDMMC_MAX_SG_ENTRIES (SDPCM_MAXGLOM_SIZE+2) ++ ++#if defined(SWTXGLOM) ++typedef struct glom_buf { ++ void *glom_pkt_head; ++ void *glom_pkt_tail; ++ uint32 count; /* Total number of pkts queued */ ++} glom_buf_t; ++#endif /* SWTXGLOM */ + + struct sdioh_info { + osl_t *osh; /* osh handler */ +@@ -83,6 +92,10 @@ + struct sdio_func fake_func0; + struct sdio_func *func[SDIOD_MAX_IOFUNCS]; + ++ uint txglom_mode; /* Txglom mode: 0 - copy, 1 - multi-descriptor */ ++#if defined(SWTXGLOM) ++ glom_buf_t glom_info; /* pkt information used for glomming */ ++#endif + }; + + /************************************************************ +@@ -115,8 +128,11 @@ + extern sdioh_info_t *sdioh_attach(osl_t *osh, struct sdio_func *func); + extern SDIOH_API_RC sdioh_detach(osl_t *osh, sdioh_info_t *sd); + ++#ifdef GLOBAL_SDMMC_INSTANCE + typedef struct _BCMSDH_SDMMC_INSTANCE { + sdioh_info_t *sd; + struct sdio_func *func[SDIOD_MAX_IOFUNCS]; + } BCMSDH_SDMMC_INSTANCE, *PBCMSDH_SDMMC_INSTANCE; ++#endif ++ + #endif /* __BCMSDH_SDMMC_H__ */ +diff -Nur a/drivers/net/wireless/bcmdhd/include/bcmutils.h c/drivers/net/wireless/bcmdhd/include/bcmutils.h +--- a/drivers/net/wireless/bcmdhd/include/bcmutils.h 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/include/bcmutils.h 2016-05-13 09:48:20.000000000 +0200 +@@ -3,7 +3,7 @@ + * + * $Copyright Open Broadcom Corporation$ + * +- * $Id: bcmutils.h 490808 2014-07-12 00:33:13Z $ ++ * $Id: bcmutils.h 504037 2014-09-22 19:03:15Z $ + */ + + #ifndef _bcmutils_h_ +@@ -174,6 +174,8 @@ + extern uint pktsegcnt_war(osl_t *osh, void *p); + extern uint8 *pktdataoffset(osl_t *osh, void *p, uint offset); + extern void *pktoffset(osl_t *osh, void *p, uint offset); ++/* Add to adjust 802.1x priority */ ++extern void pktset8021xprio(void *pkt, int prio); + + /* Get priority from a packet and pass it back in scb (or equiv) */ + #define PKTPRIO_VDSCP 0x100 /* DSCP prio found after VLAN tag */ +@@ -286,7 +288,7 @@ + #if defined(WLTINYDUMP) || defined(WLMSG_INFORM) || defined(WLMSG_ASSOC) || \ + defined(WLMSG_PRPKT) || defined(WLMSG_WSEC) + extern int bcm_format_ssid(char* buf, const uchar ssid[], uint ssid_len); +-#endif ++#endif + #endif /* BCMDRIVER */ + + /* Base type definitions */ +@@ -651,6 +653,16 @@ + /* buffer length for ethernet address from bcm_ether_ntoa() */ + #define ETHER_ADDR_STR_LEN 18 /* 18-bytes of Ethernet address buffer length */ + ++static INLINE uint32 /* 32bit word aligned xor-32 */ ++bcm_compute_xor32(volatile uint32 *u32, int num_u32) ++{ ++ int i; ++ uint32 xor32 = 0; ++ for (i = 0; i < num_u32; i++) ++ xor32 ^= *(u32 + i); ++ return xor32; ++} ++ + /* crypto utility function */ + /* 128-bit xor: *dst = *src1 xor *src2. dst1, src1 and src2 may have any alignment */ + static INLINE void +@@ -895,7 +907,7 @@ + return zeros; + #else /* C equivalent */ + return C_bcm_count_leading_zeros(u32); +-#endif /* C equivalent */ ++#endif /* C equivalent */ + } + + /* INTERFACE: Multiword bitmap based small id allocator. */ +@@ -944,6 +956,7 @@ + */ + extern void * id16_map_init(osl_t *osh, uint16 total_ids, uint16 start_val16); + extern void * id16_map_fini(osl_t *osh, void * id16_map_hndl); ++extern void id16_map_clear(void * id16_map_hndl, uint16 total_ids, uint16 start_val16); + + /* Allocate a unique 16bit id */ + extern uint16 id16_map_alloc(void * id16_map_hndl); +@@ -1055,7 +1068,7 @@ + static INLINE dll_t * + dll_prev_p(dll_t *node_p) + { +- return (node_p)->next_p; ++ return (node_p)->prev_p; + } + + +@@ -1103,7 +1116,7 @@ + node_p->prev_p->next_p = node_p->next_p; + node_p->next_p->prev_p = node_p->prev_p; + } +-#endif /* ! defined(_dll_t_) */ ++#endif /* ! defined(_dll_t_) */ + + /* Elements managed in a double linked list */ + +diff -Nur a/drivers/net/wireless/bcmdhd/include/devctrl_if/wlioctl_defs.h c/drivers/net/wireless/bcmdhd/include/devctrl_if/wlioctl_defs.h +--- a/drivers/net/wireless/bcmdhd/include/devctrl_if/wlioctl_defs.h 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/include/devctrl_if/wlioctl_defs.h 2016-05-13 09:48:20.000000000 +0200 +@@ -68,11 +68,10 @@ + + #define HIGHEST_SINGLE_STREAM_MCS 7 /* MCS values greater than this enable multiple streams */ + +-/* given a proprietary MCS, get number of spatial streams */ +-#define GET_PROPRIETARY_11N_MCS_NSS(mcs) (1 + ((mcs) - 85) / 8) ++#define GET_PRO_PRIETARY_11N_MCS_NSS(mcs) (1 + ((mcs) - 85) / 8) + + #define GET_11N_MCS_NSS(mcs) ((mcs) < 32 ? (1 + ((mcs) / 8)) \ +- : ((mcs) == 32 ? 1 : GET_PROPRIETARY_11N_MCS_NSS(mcs))) ++ : ((mcs) == 32 ? 1 : GET_PRO_PRIETARY_11N_MCS_NSS(mcs))) + + #define MAX_CCA_CHANNELS 38 /* Max number of 20 Mhz wide channels */ + #define MAX_CCA_SECS 60 /* CCA keeps this many seconds history */ +diff -Nur a/drivers/net/wireless/bcmdhd/include/epivers.h c/drivers/net/wireless/bcmdhd/include/epivers.h +--- a/drivers/net/wireless/bcmdhd/include/epivers.h 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/include/epivers.h 2016-05-13 09:48:20.000000000 +0200 +@@ -12,19 +12,19 @@ + + #define EPI_MINOR_VERSION 201 + +-#define EPI_RC_NUMBER 34 ++#define EPI_RC_NUMBER 59 + + #define EPI_INCREMENTAL_NUMBER 0 + + #define EPI_BUILD_NUMBER 0 + +-#define EPI_VERSION 1, 201, 34, 0 ++#define EPI_VERSION 1, 201, 59, 0 + +-#define EPI_VERSION_NUM 0x01c92200 ++#define EPI_VERSION_NUM 0x01c93b00 + +-#define EPI_VERSION_DEV 1.201.34 ++#define EPI_VERSION_DEV 1.201.59 + + /* Driver Version String, ASCII, 32 chars max */ +-#define EPI_VERSION_STR "1.201.34.2 (r491657)" ++#define EPI_VERSION_STR "1.201.59.6 (r506368)" + + #endif /* _epivers_h_ */ +diff -Nur a/drivers/net/wireless/bcmdhd/include/epivers.sh c/drivers/net/wireless/bcmdhd/include/epivers.sh +--- a/drivers/net/wireless/bcmdhd/include/epivers.sh 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/include/epivers.sh 2016-05-13 09:48:20.000000000 +0200 +@@ -57,7 +57,7 @@ + fi + + # Following SVNURL should be expanded on checkout +- SVNURL='$HeadURL: http://svn.sj.broadcom.com/svn/wlansvn/proj/tags/DHD/DHD_REL_1_201_34/src/include/epivers.sh $' ++ SVNURL='$HeadURL: http://svn.sj.broadcom.com/svn/wlansvn/proj/tags/DHD/DHD_REL_1_201_59/src/include/epivers.sh $' + + # .gclient_info is created by gclient checkout/sync steps + # and contains "DEPS=' ..." entry +diff -Nur a/drivers/net/wireless/bcmdhd/include/event_log.h c/drivers/net/wireless/bcmdhd/include/event_log.h +--- a/drivers/net/wireless/bcmdhd/include/event_log.h 1970-01-01 01:00:00.000000000 +0100 ++++ c/drivers/net/wireless/bcmdhd/include/event_log.h 2016-05-13 09:48:20.000000000 +0200 +@@ -0,0 +1,293 @@ ++/* ++ * EVENT_LOG system definitions ++ * ++ * $Copyright Open Broadcom Corporation$ ++ * ++ * $Id: event_log.h 241182 2011-02-17 21:50:03Z $ ++ */ ++ ++#ifndef _EVENT_LOG_H_ ++#define _EVENT_LOG_H_ ++ ++#include ++ ++/* Set a maximum number of sets here. It is not dynamic for ++ * efficiency of the EVENT_LOG calls. ++ */ ++#define NUM_EVENT_LOG_SETS 4 ++#define EVENT_LOG_SET_BUS 0 ++#define EVENT_LOG_SET_WL 1 ++#define EVENT_LOG_SET_PSM 2 ++#define EVENT_LOG_SET_DBG 3 ++ ++/* Define new event log tags here */ ++#define EVENT_LOG_TAG_NULL 0 /* Special null tag */ ++#define EVENT_LOG_TAG_TS 1 /* Special timestamp tag */ ++#define EVENT_LOG_TAG_BUS_OOB 2 ++#define EVENT_LOG_TAG_BUS_STATE 3 ++#define EVENT_LOG_TAG_BUS_PROTO 4 ++#define EVENT_LOG_TAG_BUS_CTL 5 ++#define EVENT_LOG_TAG_BUS_EVENT 6 ++#define EVENT_LOG_TAG_BUS_PKT 7 ++#define EVENT_LOG_TAG_BUS_FRAME 8 ++#define EVENT_LOG_TAG_BUS_DESC 9 ++#define EVENT_LOG_TAG_BUS_SETUP 10 ++#define EVENT_LOG_TAG_BUS_MISC 11 ++#define EVENT_LOG_TAG_SRSCAN 22 ++#define EVENT_LOG_TAG_PWRSTATS_INFO 23 ++#define EVENT_LOG_TAG_UCODE_WATCHDOG 26 ++#define EVENT_LOG_TAG_UCODE_FIFO 27 ++#define EVENT_LOG_TAG_SCAN_TRACE_LOW 28 ++#define EVENT_LOG_TAG_SCAN_TRACE_HIGH 29 ++#define EVENT_LOG_TAG_SCAN_ERROR 30 ++#define EVENT_LOG_TAG_SCAN_WARN 31 ++#define EVENT_LOG_TAG_MPF_ERR 32 ++#define EVENT_LOG_TAG_MPF_WARN 33 ++#define EVENT_LOG_TAG_MPF_INFO 34 ++#define EVENT_LOG_TAG_MPF_DEBUG 35 ++#define EVENT_LOG_TAG_EVENT_INFO 36 ++#define EVENT_LOG_TAG_EVENT_ERR 37 ++#define EVENT_LOG_TAG_PWRSTATS_ERROR 38 ++#define EVENT_LOG_TAG_EXCESS_PM_ERROR 39 ++#define EVENT_LOG_TAG_IOCTL_LOG 40 ++#define EVENT_LOG_TAG_PFN_ERR 41 ++#define EVENT_LOG_TAG_PFN_WARN 42 ++#define EVENT_LOG_TAG_PFN_INFO 43 ++#define EVENT_LOG_TAG_PFN_DEBUG 44 ++#define EVENT_LOG_TAG_BEACON_LOG 45 ++#define EVENT_LOG_TAG_WNM_BSSTRANS_INFO 46 ++#define EVENT_LOG_TAG_TRACE_CHANSW 47 ++#define EVENT_LOG_TAG_PCI_ERROR 48 ++#define EVENT_LOG_TAG_PCI_TRACE 49 ++#define EVENT_LOG_TAG_PCI_WARN 50 ++#define EVENT_LOG_TAG_PCI_INFO 51 ++#define EVENT_LOG_TAG_PCI_DBG 52 ++#define EVENT_LOG_TAG_PCI_DATA 53 ++#define EVENT_LOG_TAG_PCI_RING 54 ++#define EVENT_LOG_TAG_MAX 55 /* Set to the same value of last tag, not last tag + 1 */ ++/* Note: New event should be added/reserved in trunk before adding it to branches */ ++ ++/* Flags for tag control */ ++#define EVENT_LOG_TAG_FLAG_NONE 0 ++#define EVENT_LOG_TAG_FLAG_LOG 0x80 ++#define EVENT_LOG_TAG_FLAG_PRINT 0x40 ++#define EVENT_LOG_TAG_FLAG_MASK 0x3f ++ ++/* logstrs header */ ++#define LOGSTRS_MAGIC 0x4C4F4753 ++#define LOGSTRS_VERSION 0x1 ++ ++/* We make sure that the block size will fit in a single packet ++ * (allowing for a bit of overhead on each packet ++ */ ++#define EVENT_LOG_MAX_BLOCK_SIZE 1400 ++#define EVENT_LOG_PSM_BLOCK 0x200 ++#define EVENT_LOG_BUS_BLOCK 0x200 ++#define EVENT_LOG_DBG_BLOCK 0x100 ++ ++/* ++ * There are multiple levels of objects define here: ++ * event_log_set - a set of buffers ++ * event log groups - every event log call is part of just one. All ++ * event log calls in a group are handled the ++ * same way. Each event log group is associated ++ * with an event log set or is off. ++ */ ++ ++#ifndef __ASSEMBLER__ ++ ++/* On the external system where the dumper is we need to make sure ++ * that these types are the same size as they are on the ARM the ++ * produced them ++ */ ++#ifdef EVENT_LOG_DUMPER ++#define _EL_BLOCK_PTR uint32 ++#define _EL_TYPE_PTR uint32 ++#define _EL_SET_PTR uint32 ++#define _EL_TOP_PTR uint32 ++#else ++#define _EL_BLOCK_PTR struct event_log_block * ++#define _EL_TYPE_PTR uint32 * ++#define _EL_SET_PTR struct event_log_set ** ++#define _EL_TOP_PTR struct event_log_top * ++#endif /* EVENT_LOG_DUMPER */ ++ ++/* Each event log entry has a type. The type is the LAST word of the ++ * event log. The printing code walks the event entries in reverse ++ * order to find the first entry. ++ */ ++typedef union event_log_hdr { ++ struct { ++ uint8 tag; /* Event_log entry tag */ ++ uint8 count; /* Count of 4-byte entries */ ++ uint16 fmt_num; /* Format number */ ++ }; ++ uint32 t; /* Type cheat */ ++} event_log_hdr_t; ++ ++/* Event log sets (a logical circurlar buffer) consist of one or more ++ * event_log_blocks. The blocks themselves form a logical circular ++ * list. The log entries are placed in each event_log_block until it ++ * is full. Logging continues with the next event_log_block in the ++ * event_set until the last event_log_block is reached and then ++ * logging starts over with the first event_log_block in the ++ * event_set. ++ */ ++typedef struct event_log_block { ++ _EL_BLOCK_PTR next_block; ++ _EL_BLOCK_PTR prev_block; ++ _EL_TYPE_PTR end_ptr; ++ ++ /* Start of packet sent for log tracing */ ++ uint16 pktlen; /* Size of rest of block */ ++ uint16 count; /* Logtrace counter */ ++ uint32 timestamp; /* Timestamp at start of use */ ++ uint32 event_logs; ++} event_log_block_t; ++ ++/* There can be multiple event_sets with each logging a set of ++ * associated events (i.e, "fast" and "slow" events). ++ */ ++typedef struct event_log_set { ++ _EL_BLOCK_PTR first_block; /* Pointer to first event_log block */ ++ _EL_BLOCK_PTR last_block; /* Pointer to last event_log block */ ++ _EL_BLOCK_PTR logtrace_block; /* next block traced */ ++ _EL_BLOCK_PTR cur_block; /* Pointer to current event_log block */ ++ _EL_TYPE_PTR cur_ptr; /* Current event_log pointer */ ++ uint32 blockcount; /* Number of blocks */ ++ uint16 logtrace_count; /* Last count for logtrace */ ++ uint16 blockfill_count; /* Fill count for logtrace */ ++ uint32 timestamp; /* Last timestamp event */ ++ uint32 cyclecount; /* Cycles at last timestamp event */ ++} event_log_set_t; ++ ++/* Top data structure for access to everything else */ ++typedef struct event_log_top { ++ uint32 magic; ++#define EVENT_LOG_TOP_MAGIC 0x474C8669 /* 'EVLG' */ ++ uint32 version; ++#define EVENT_LOG_VERSION 1 ++ uint32 num_sets; ++ uint32 logstrs_size; /* Size of lognums + logstrs area */ ++ uint32 timestamp; /* Last timestamp event */ ++ uint32 cyclecount; /* Cycles at last timestamp event */ ++ _EL_SET_PTR sets; /* Ptr to array of set ptrs */ ++} event_log_top_t; ++ ++/* Data structure of Keeping the Header from logstrs.bin */ ++typedef struct { ++ uint32 logstrs_size; /* Size of the file */ ++ uint32 rom_lognums_offset; /* Offset to the ROM lognum */ ++ uint32 ram_lognums_offset; /* Offset to the RAM lognum */ ++ uint32 rom_logstrs_offset; /* Offset to the ROM logstr */ ++ uint32 ram_logstrs_offset; /* Offset to the RAM logstr */ ++ /* Keep version and magic last since "header" is appended to the end of logstrs file. */ ++ uint32 version; /* Header version */ ++ uint32 log_magic; /* MAGIC number for verification 'LOGS' */ ++} logstr_header_t; ++ ++ ++#ifndef EVENT_LOG_DUMPER ++ ++#ifndef EVENT_LOG_COMPILE ++ ++/* Null define if no tracing */ ++#define EVENT_LOG(format, ...) ++ ++#else /* EVENT_LOG_COMPILE */ ++ ++/* The first few are special because they can be done more efficiently ++ * this way and they are the common case. Once there are too many ++ * parameters the code size starts to be an issue and a loop is better ++ */ ++#define _EVENT_LOG0(tag, fmt_num) \ ++ event_log0(tag, fmt_num) ++#define _EVENT_LOG1(tag, fmt_num, t1) \ ++ event_log1(tag, fmt_num, t1) ++#define _EVENT_LOG2(tag, fmt_num, t1, t2) \ ++ event_log2(tag, fmt_num, t1, t2) ++#define _EVENT_LOG3(tag, fmt_num, t1, t2, t3) \ ++ event_log3(tag, fmt_num, t1, t2, t3) ++#define _EVENT_LOG4(tag, fmt_num, t1, t2, t3, t4) \ ++ event_log4(tag, fmt_num, t1, t2, t3, t4) ++ ++/* The rest call the generic routine that takes a count */ ++#define _EVENT_LOG5(tag, fmt_num, ...) event_logn(5, tag, fmt_num, __VA_ARGS__) ++#define _EVENT_LOG6(tag, fmt_num, ...) event_logn(6, tag, fmt_num, __VA_ARGS__) ++#define _EVENT_LOG7(tag, fmt_num, ...) event_logn(7, tag, fmt_num, __VA_ARGS__) ++#define _EVENT_LOG8(tag, fmt_num, ...) event_logn(8, tag, fmt_num, __VA_ARGS__) ++#define _EVENT_LOG9(tag, fmt_num, ...) event_logn(9, tag, fmt_num, __VA_ARGS__) ++#define _EVENT_LOGA(tag, fmt_num, ...) event_logn(10, tag, fmt_num, __VA_ARGS__) ++#define _EVENT_LOGB(tag, fmt_num, ...) event_logn(11, tag, fmt_num, __VA_ARGS__) ++#define _EVENT_LOGC(tag, fmt_num, ...) event_logn(12, tag, fmt_num, __VA_ARGS__) ++#define _EVENT_LOGD(tag, fmt_num, ...) event_logn(13, tag, fmt_num, __VA_ARGS__) ++#define _EVENT_LOGE(tag, fmt_num, ...) event_logn(14, tag, fmt_num, __VA_ARGS__) ++#define _EVENT_LOGF(tag, fmt_num, ...) event_logn(15, tag, fmt_num, __VA_ARGS__) ++ ++/* Hack to make the proper routine call when variadic macros get ++ * passed. Note the max of 15 arguments. More than that can't be ++ * handled by the event_log entries anyways so best to catch it at compile ++ * time ++ */ ++ ++#define _EVENT_LOG_VA_NUM_ARGS(F, _1, _2, _3, _4, _5, _6, _7, _8, _9, \ ++ _A, _B, _C, _D, _E, _F, N, ...) F ## N ++ ++#define _EVENT_LOG(tag, fmt, ...) \ ++ static char logstr[] __attribute__ ((section(".logstrs"))) = fmt; \ ++ static uint32 fmtnum __attribute__ ((section(".lognums"))) = (uint32) &logstr; \ ++ _EVENT_LOG_VA_NUM_ARGS(_EVENT_LOG, ##__VA_ARGS__, \ ++ F, E, D, C, B, A, 9, 8, \ ++ 7, 6, 5, 4, 3, 2, 1, 0) \ ++ (tag, (int) &fmtnum , ## __VA_ARGS__); \ ++ ++ ++#define EVENT_LOG_FAST(tag, fmt, ...) \ ++ if (event_log_tag_sets != NULL) { \ ++ uint8 tag_flag = *(event_log_tag_sets + tag); \ ++ if (tag_flag != 0) { \ ++ _EVENT_LOG(tag, fmt , ## __VA_ARGS__); \ ++ } \ ++ } ++ ++#define EVENT_LOG_COMPACT(tag, fmt, ...) \ ++ if (1) { \ ++ _EVENT_LOG(tag, fmt , ## __VA_ARGS__); \ ++ } ++ ++#define EVENT_LOG(tag, fmt, ...) EVENT_LOG_COMPACT(tag, fmt , ## __VA_ARGS__) ++ ++#define EVENT_LOG_IS_LOG_ON(tag) (*(event_log_tag_sets + (tag)) & EVENT_LOG_TAG_FLAG_LOG) ++ ++#define EVENT_DUMP event_log_buffer ++ ++extern uint8 *event_log_tag_sets; ++ ++#include ++ ++extern int event_log_init(si_t *sih); ++extern int event_log_set_init(si_t *sih, int set_num, int size); ++extern int event_log_set_expand(si_t *sih, int set_num, int size); ++extern int event_log_set_shrink(si_t *sih, int set_num, int size); ++extern int event_log_tag_start(int tag, int set_num, int flags); ++extern int event_log_tag_stop(int tag); ++extern int event_log_get(int set_num, int buflen, void *buf); ++extern uint8 * event_log_next_logtrace(int set_num); ++ ++extern void event_log0(int tag, int fmtNum); ++extern void event_log1(int tag, int fmtNum, uint32 t1); ++extern void event_log2(int tag, int fmtNum, uint32 t1, uint32 t2); ++extern void event_log3(int tag, int fmtNum, uint32 t1, uint32 t2, uint32 t3); ++extern void event_log4(int tag, int fmtNum, uint32 t1, uint32 t2, uint32 t3, uint32 t4); ++extern void event_logn(int num_args, int tag, int fmtNum, ...); ++ ++extern void event_log_time_sync(void); ++extern void event_log_buffer(int tag, uint8 *buf, int size); ++ ++#endif /* EVENT_LOG_DUMPER */ ++ ++#endif /* EVENT_LOG_COMPILE */ ++ ++#endif /* __ASSEMBLER__ */ ++ ++#endif /* _EVENT_LOG_H */ +diff -Nur a/drivers/net/wireless/bcmdhd/include/hndrte_armtrap.h c/drivers/net/wireless/bcmdhd/include/hndrte_armtrap.h +--- a/drivers/net/wireless/bcmdhd/include/hndrte_armtrap.h 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/include/hndrte_armtrap.h 1970-01-01 01:00:00.000000000 +0100 +@@ -1,70 +0,0 @@ +-/* +- * HNDRTE arm trap handling. +- * +- * $Copyright Open Broadcom Corporation$ +- * +- * $Id: hndrte_armtrap.h 261365 2011-05-24 20:42:23Z $ +- */ +- +-#ifndef _hndrte_armtrap_h +-#define _hndrte_armtrap_h +- +- +-/* ARM trap handling */ +- +-/* Trap types defined by ARM (see arminc.h) */ +- +-/* Trap locations in lo memory */ +-#define TRAP_STRIDE 4 +-#define FIRST_TRAP TR_RST +-#define LAST_TRAP (TR_FIQ * TRAP_STRIDE) +- +-#if defined(__ARM_ARCH_4T__) +-#define MAX_TRAP_TYPE (TR_FIQ + 1) +-#elif defined(__ARM_ARCH_7M__) +-#define MAX_TRAP_TYPE (TR_ISR + ARMCM3_NUMINTS) +-#endif /* __ARM_ARCH_7M__ */ +- +-/* The trap structure is defined here as offsets for assembly */ +-#define TR_TYPE 0x00 +-#define TR_EPC 0x04 +-#define TR_CPSR 0x08 +-#define TR_SPSR 0x0c +-#define TR_REGS 0x10 +-#define TR_REG(n) (TR_REGS + (n) * 4) +-#define TR_SP TR_REG(13) +-#define TR_LR TR_REG(14) +-#define TR_PC TR_REG(15) +- +-#define TRAP_T_SIZE 80 +- +-#ifndef _LANGUAGE_ASSEMBLY +- +-#include +- +-typedef struct _trap_struct { +- uint32 type; +- uint32 epc; +- uint32 cpsr; +- uint32 spsr; +- uint32 r0; /* a1 */ +- uint32 r1; /* a2 */ +- uint32 r2; /* a3 */ +- uint32 r3; /* a4 */ +- uint32 r4; /* v1 */ +- uint32 r5; /* v2 */ +- uint32 r6; /* v3 */ +- uint32 r7; /* v4 */ +- uint32 r8; /* v5 */ +- uint32 r9; /* sb/v6 */ +- uint32 r10; /* sl/v7 */ +- uint32 r11; /* fp/v8 */ +- uint32 r12; /* ip */ +- uint32 r13; /* sp */ +- uint32 r14; /* lr */ +- uint32 pc; /* r15 */ +-} trap_t; +- +-#endif /* !_LANGUAGE_ASSEMBLY */ +- +-#endif /* _hndrte_armtrap_h */ +diff -Nur a/drivers/net/wireless/bcmdhd/include/hndrte_cons.h c/drivers/net/wireless/bcmdhd/include/hndrte_cons.h +--- a/drivers/net/wireless/bcmdhd/include/hndrte_cons.h 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/include/hndrte_cons.h 1970-01-01 01:00:00.000000000 +0100 +@@ -1,51 +0,0 @@ +-/* +- * Console support for hndrte. +- * +- * $Copyright Open Broadcom Corporation$ +- * +- * $Id: hndrte_cons.h 383834 2013-02-07 23:21:51Z $ +- */ +-#ifndef _HNDRTE_CONS_H +-#define _HNDRTE_CONS_H +- +-#include +- +-#define CBUF_LEN (128) +- +-#define LOG_BUF_LEN 1024 +- +-typedef struct { +- uint32 buf; /* Can't be pointer on (64-bit) hosts */ +- uint buf_size; +- uint idx; +- char *_buf_compat; /* redundant pointer for backward compat. */ +-} hndrte_log_t; +- +-typedef struct { +- /* Virtual UART +- * When there is no UART (e.g. Quickturn), the host should write a complete +- * input line directly into cbuf and then write the length into vcons_in. +- * This may also be used when there is a real UART (at risk of conflicting with +- * the real UART). vcons_out is currently unused. +- */ +- volatile uint vcons_in; +- volatile uint vcons_out; +- +- /* Output (logging) buffer +- * Console output is written to a ring buffer log_buf at index log_idx. +- * The host may read the output when it sees log_idx advance. +- * Output will be lost if the output wraps around faster than the host polls. +- */ +- hndrte_log_t log; +- +- /* Console input line buffer +- * Characters are read one at a time into cbuf until is received, then +- * the buffer is processed as a command line. Also used for virtual UART. +- */ +- uint cbuf_idx; +- char cbuf[CBUF_LEN]; +-} hndrte_cons_t; +- +-hndrte_cons_t *hndrte_get_active_cons_state(void); +- +-#endif /* _HNDRTE_CONS_H */ +diff -Nur a/drivers/net/wireless/bcmdhd/include/linux_osl.h c/drivers/net/wireless/bcmdhd/include/linux_osl.h +--- a/drivers/net/wireless/bcmdhd/include/linux_osl.h 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/include/linux_osl.h 2016-05-13 09:48:20.000000000 +0200 +@@ -3,7 +3,7 @@ + * + * $Copyright Open Broadcom Corporation$ + * +- * $Id: linux_osl.h 491170 2014-07-15 06:23:58Z $ ++ * $Id: linux_osl.h 503131 2014-09-17 12:16:08Z $ + */ + + #ifndef _linux_osl_h_ +@@ -33,10 +33,6 @@ + extern int osl_static_mem_deinit(osl_t *osh, void *adapter); + extern void osl_set_bus_handle(osl_t *osh, void *bus_handle); + extern void* osl_get_bus_handle(osl_t *osh); +-#ifdef EXYNOS5433_PCIE_WAR +-extern void exynos_pcie_set_l1_exit(void); +-extern void exynos_pcie_clear_l1_exit(void); +-#endif /* EXYNOS5433_PCIE_WAR */ + + /* Global ASSERT type */ + extern uint32 g_assert_type; +@@ -57,7 +53,7 @@ + #define ASSERT(exp) + #endif /* GCC_VERSION > 30100 */ + #endif /* __GNUC__ */ +-#endif ++#endif + + /* bcm_prefetch_32B */ + static inline void bcm_prefetch_32B(const uint8 *addr, const int cachelines_32B) +@@ -69,7 +65,7 @@ + case 2: __asm__ __volatile__("pld\t%a0" :: "p"(addr + 32) : "cc"); + case 1: __asm__ __volatile__("pld\t%a0" :: "p"(addr + 0) : "cc"); + } +-#endif ++#endif + } + + /* microsecond delay */ +@@ -154,6 +150,20 @@ + #define DMA_FREE_CONSISTENT_FORCE32(osh, va, size, pa, dmah) \ + osl_dma_free_consistent((osh), (void*)(va), (size), (pa)) + ++#if defined(BCMPCIE) ++#if defined(CONFIG_DHD_USE_STATIC_BUF) && defined(DHD_USE_STATIC_FLOWRING) ++#define DMA_ALLOC_CONSISTENT_STATIC(osh, size, align, tot, pap, dmah, idx) \ ++ osl_dma_alloc_consistent_static((osh), (size), (align), (tot), (pap), (idx)) ++#define DMA_FREE_CONSISTENT_STATIC(osh, va, size, pa, dmah, idx) \ ++ osl_dma_free_consistent_static((osh), (void*)(va), (size), (pa), (idx)) ++ ++extern void *osl_dma_alloc_consistent_static(osl_t *osh, uint size, uint16 align, ++ uint *tot, dmaaddr_t *pap, uint16 idx); ++extern void osl_dma_free_consistent_static(osl_t *osh, void *va, uint size, dmaaddr_t pa, ++ uint16 idx); ++#endif /* CONFIG_DHD_USE_STATIC_BUF && DHD_USE_STATIC_FLOWRING */ ++#endif /* BCMPCIE */ ++ + extern uint osl_dma_consistent_align(void); + extern void *osl_dma_alloc_consistent(osl_t *osh, uint size, uint16 align, + uint *tot, dmaaddr_t *pap); +@@ -192,7 +202,7 @@ + #define OSL_PREFETCH(ptr) BCM_REFERENCE(ptr) + + #define OSL_ARCH_IS_COHERENT() NULL +-#endif ++#endif + + /* register access macros */ + #if defined(BCMSDIO) +@@ -210,7 +220,7 @@ + osl_pcie_rreg(osh, (uintptr)(r), (void *)&__osl_v, sizeof(*(r))); \ + __osl_v; \ + }) +-#endif ++#endif + + #if defined(BCM47XX_ACP_WAR) + #define SELECT_BUS_WRITE(osh, mmap_op, bus_op) ({BCM_REFERENCE(osh); mmap_op;}) +@@ -225,7 +235,7 @@ + #else + #define SELECT_BUS_WRITE(osh, mmap_op, bus_op) ({BCM_REFERENCE(osh); mmap_op;}) + #define SELECT_BUS_READ(osh, mmap_op, bus_op) ({BCM_REFERENCE(osh); mmap_op;}) +-#endif ++#endif + #endif /* BCM47XX_ACP_WAR */ + + #define OSL_ERROR(bcmerror) osl_error(bcmerror) +@@ -258,26 +268,6 @@ + + /* register access macros */ + +-#ifdef EXYNOS5433_PCIE_WAR +-#define R_REG(osh, r) (\ +- SELECT_BUS_READ(osh, \ +- ({ \ +- __typeof(*(r)) __osl_v; \ +- exynos_pcie_set_l1_exit(); \ +- switch (sizeof(*(r))) { \ +- case sizeof(uint8): __osl_v = \ +- readb((volatile uint8*)(r)); break; \ +- case sizeof(uint16): __osl_v = \ +- readw((volatile uint16*)(r)); break; \ +- case sizeof(uint32): __osl_v = \ +- readl((volatile uint32*)(r)); break; \ +- } \ +- exynos_pcie_clear_l1_exit(); \ +- __osl_v; \ +- }), \ +- OSL_READ_REG(osh, r)) \ +-) +-#else + #define R_REG(osh, r) (\ + SELECT_BUS_READ(osh, \ + ({ \ +@@ -294,21 +284,7 @@ + }), \ + OSL_READ_REG(osh, r)) \ + ) +-#endif /* EXYNOS5433_PCIE_WAR */ + +-#ifdef EXYNOS5433_PCIE_WAR +-#define W_REG(osh, r, v) do { \ +- exynos_pcie_set_l1_exit(); \ +- SELECT_BUS_WRITE(osh, \ +- switch (sizeof(*(r))) { \ +- case sizeof(uint8): writeb((uint8)(v), (volatile uint8*)(r)); break; \ +- case sizeof(uint16): writew((uint16)(v), (volatile uint16*)(r)); break; \ +- case sizeof(uint32): writel((uint32)(v), (volatile uint32*)(r)); break; \ +- }, \ +- (OSL_WRITE_REG(osh, r, v))); \ +- exynos_pcie_clear_l1_exit(); \ +- } while (0) +-#else + #define W_REG(osh, r, v) do { \ + SELECT_BUS_WRITE(osh, \ + switch (sizeof(*(r))) { \ +@@ -318,7 +294,6 @@ + }, \ + (OSL_WRITE_REG(osh, r, v))); \ + } while (0) +-#endif /* EXYNOS5433_PCIE_WAR */ + + #define AND_REG(osh, r, v) W_REG(osh, (r), R_REG(osh, r) & (v)) + #define OR_REG(osh, r, v) W_REG(osh, (r), R_REG(osh, r) | (v)) +@@ -340,7 +315,7 @@ + #define OSL_GETCYCLES(x) rdtscl((x)) + #else + #define OSL_GETCYCLES(x) ((x) = 0) +-#endif ++#endif + + /* dereference an address that may cause a bus exception */ + #define BUSPROBE(val, addr) ({ (val) = R_REG(NULL, (addr)); 0; }) +@@ -689,7 +664,7 @@ + extern void osl_pkt_frmfwder(osl_t *osh, void *skbs, int skb_cnt); + #define PKTFRMFWDER(osh, skbs, skb_cnt) \ + osl_pkt_frmfwder(((osl_t *)osh), (void *)(skbs), (skb_cnt)) +-#endif ++#endif + + + /** GMAC Forwarded packet tagging for reduced cache flush/invalidate. +@@ -982,4 +957,64 @@ + extern void bzero(void *b, size_t len); + #endif /* ! BCMDRIVER */ + ++typedef struct sec_cma_info { ++ struct sec_mem_elem *sec_alloc_list; ++ struct sec_mem_elem *sec_alloc_list_tail; ++} sec_cma_info_t; ++ ++#ifdef BCM_SECURE_DMA ++ ++#define SECURE_DMA_MAP(osh, va, size, direction, p, dmah, pcma, offset) \ ++ osl_sec_dma_map((osh), (va), (size), (direction), (p), (dmah), (pcma), (offset)) ++#define SECURE_DMA_DD_MAP(osh, va, size, direction, p, dmah) \ ++ osl_sec_dma_dd_map((osh), (va), (size), (direction), (p), (dmah)) ++#define SECURE_DMA_MAP_TXMETA(osh, va, size, direction, p, dmah, pcma) \ ++ osl_sec_dma_map_txmeta((osh), (va), (size), (direction), (p), (dmah), (pcma)) ++#define SECURE_DMA_UNMAP(osh, pa, size, direction, p, dmah, pcma, offset) \ ++ osl_sec_dma_unmap((osh), (pa), (size), (direction), (p), (dmah), (pcma), (offset)) ++#define SECURE_DMA_UNMAP_ALL(osh, pcma) \ ++osl_sec_dma_unmap_all((osh), (pcma)) ++ ++#if defined(__ARM_ARCH_7A__) ++#define ACP_WAR_ENAB() 0 ++#define ACP_WIN_LIMIT 0 ++#define arch_is_coherent() 0 ++ ++#define CMA_BUFSIZE_4K 4096 ++#define CMA_BUFSIZE_2K 2048 ++#define CMA_BUFSIZE_512 512 ++ ++#define CMA_BUFNUM 2048 ++#define SEC_CMA_COHERENT_BLK 0x8000 /* 32768 */ ++#define SEC_CMA_COHERENT_MAX 32 ++#define CMA_DMA_DESC_MEMBLOCK (SEC_CMA_COHERENT_BLK * SEC_CMA_COHERENT_MAX) ++#define CMA_DMA_DATA_MEMBLOCK (CMA_BUFSIZE_4K*CMA_BUFNUM) ++#define CMA_MEMBLOCK (CMA_DMA_DESC_MEMBLOCK + CMA_DMA_DATA_MEMBLOCK) ++#define CONT_ARMREGION 0x02 /* Region CMA */ ++#else ++#define CONT_MIPREGION 0x00 /* To access the MIPs mem, Not yet... */ ++#endif /* !defined __ARM_ARCH_7A__ */ ++ ++#define SEC_DMA_ALIGN (1<<16) ++typedef struct sec_mem_elem { ++ size_t size; ++ int direction; ++ phys_addr_t pa_cma; /* physical address */ ++ void *va; /* virtual address of driver pkt */ ++ dma_addr_t dma_handle; /* bus address assign by linux */ ++ void *vac; /* virtual address of cma buffer */ ++ struct sec_mem_elem *next; ++} sec_mem_elem_t; ++ ++extern dma_addr_t osl_sec_dma_map(osl_t *osh, void *va, uint size, int direction, void *p, ++ hnddma_seg_map_t *dmah, void *ptr_cma_info, uint offset); ++extern dma_addr_t osl_sec_dma_dd_map(osl_t *osh, void *va, uint size, int direction, void *p, ++ hnddma_seg_map_t *dmah); ++extern dma_addr_t osl_sec_dma_map_txmeta(osl_t *osh, void *va, uint size, ++ int direction, void *p, hnddma_seg_map_t *dmah, void *ptr_cma_info); ++extern void osl_sec_dma_unmap(osl_t *osh, dma_addr_t dma_handle, uint size, int direction, ++ void *p, hnddma_seg_map_t *map, void *ptr_cma_info, uint offset); ++extern void osl_sec_dma_unmap_all(osl_t *osh, void *ptr_cma_info); ++ ++#endif /* BCM_SECURE_DMA */ + #endif /* _linux_osl_h_ */ +diff -Nur a/drivers/net/wireless/bcmdhd/include/linuxver.h c/drivers/net/wireless/bcmdhd/include/linuxver.h +--- a/drivers/net/wireless/bcmdhd/include/linuxver.h 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/include/linuxver.h 2016-05-13 09:48:20.000000000 +0200 +@@ -686,7 +686,7 @@ + #define WL_ISR(i, d, p) wl_isr((i), (d)) + #else + #define WL_ISR(i, d, p) wl_isr((i), (d), (p)) +-#endif /* < 2.6.20 */ ++#endif /* < 2.6.20 */ + + #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)) + #define netdev_priv(dev) dev->priv +diff -Nur a/drivers/net/wireless/bcmdhd/include/miniopt.h c/drivers/net/wireless/bcmdhd/include/miniopt.h +--- a/drivers/net/wireless/bcmdhd/include/miniopt.h 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/include/miniopt.h 2016-05-13 09:48:20.000000000 +0200 +@@ -58,4 +58,4 @@ + } + #endif + +-#endif /* MINI_OPT_H */ ++#endif /* MINI_OPT_H */ +diff -Nur a/drivers/net/wireless/bcmdhd/include/osl.h c/drivers/net/wireless/bcmdhd/include/osl.h +--- a/drivers/net/wireless/bcmdhd/include/osl.h 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/include/osl.h 2016-05-13 09:48:20.000000000 +0200 +@@ -3,7 +3,7 @@ + * + * $Copyright Open Broadcom Corporation$ + * +- * $Id: osl.h 474639 2014-05-01 23:52:31Z $ ++ * $Id: osl.h 503131 2014-09-17 12:16:08Z $ + */ + + #ifndef _osl_h_ +@@ -38,11 +38,11 @@ + + #ifndef AND_REG + #define AND_REG(osh, r, v) W_REG(osh, (r), R_REG(osh, r) & (v)) +-#endif /* !AND_REG */ ++#endif /* !AND_REG */ + + #ifndef OR_REG + #define OR_REG(osh, r, v) W_REG(osh, (r), R_REG(osh, r) | (v)) +-#endif /* !OR_REG */ ++#endif /* !OR_REG */ + + #if !defined(OSL_SYSUPTIME) + #define OSL_SYSUPTIME() (0) +@@ -127,5 +127,17 @@ + #define PKTFRAGISCHAINED(osh, i) (0) + /* TRIM Tail bytes from lfrag */ + #define PKTFRAG_TRIM_TAILBYTES(osh, p, len) PKTSETLEN(osh, p, PKTLEN(osh, p) - len) ++#ifdef BCM_SECURE_DMA ++#define SECURE_DMA_ENAB(osh) (1) ++#else ++ ++#define SECURE_DMA_ENAB(osh) (0) ++#define SECURE_DMA_MAP(osh, va, size, direction, p, dmah, pcma, offset) ((dmaaddr_t) {(0)}) ++#define SECURE_DMA_DD_MAP(osh, va, size, direction, p, dmah) 0 ++#define SECURE_DMA_MAP_TXMETA(osh, va, size, direction, p, dmah, pcma) ((dmaaddr_t) {(0)}) ++#define SECURE_DMA_UNMAP(osh, pa, size, direction, p, dmah, pcma, offset) ++#define SECURE_DMA_UNMAP_ALL(osh, pcma) ++ ++#endif + + #endif /* _osl_h_ */ +diff -Nur a/drivers/net/wireless/bcmdhd/include/pcicfg.h c/drivers/net/wireless/bcmdhd/include/pcicfg.h +--- a/drivers/net/wireless/bcmdhd/include/pcicfg.h 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/include/pcicfg.h 2016-05-13 09:48:20.000000000 +0200 +@@ -3,7 +3,7 @@ + * + * $Copyright Open Broadcom Corporation$ + * +- * $Id: pcicfg.h 465082 2014-03-26 17:37:28Z $ ++ * $Id: pcicfg.h 506084 2014-10-02 15:34:59Z $ + */ + + #ifndef _h_pcicfg_ +@@ -92,10 +92,6 @@ + #define PCIBAR_PREFETCH 0x8 + #define PCIBAR_MEM32_MASK 0xFFFFFF80 + +-/* pci config status reg has a bit to indicate that capability ptr is present */ +- +-#define PCI_CAPPTR_PRESENT 0x0010 +- + typedef struct _pci_config_regs { + uint16 vendor; + uint16 device; +@@ -126,6 +122,11 @@ + #define MINSZPCR 64 /* offsetof (dev_dep[0] */ + + #endif /* !LINUX_POSTMOGRIFY_REMOVAL */ ++ ++/* pci config status reg has a bit to indicate that capability ptr is present */ ++ ++#define PCI_CAPPTR_PRESENT 0x0010 ++ + /* A structure for the config registers is nice, but in most + * systems the config space is not memory mapped, so we need + * field offsetts. :-( +@@ -315,16 +316,6 @@ + PCI_XOR_OTHER = 0x80 + } pci_xor_subclasses; + +-/* Header types */ +-#define PCI_HEADER_MULTI 0x80 +-#define PCI_HEADER_MASK 0x7f +-typedef enum { +- PCI_HEADER_NORMAL, +- PCI_HEADER_BRIDGE, +- PCI_HEADER_CARDBUS +-} pci_header_types; +- +- + /* Overlay for a PCI-to-PCI bridge */ + + #define PPB_RSVDA_MAX 2 +@@ -372,6 +363,16 @@ + uint8 dev_dep[192]; + } ppb_config_regs; + ++/* Everything below is BRCM HND proprietary */ ++ ++ ++/* Brcm PCI configuration registers */ ++#define cap_list rsvd_a[0] ++#define bar0_window dev_dep[0x80 - 0x40] ++#define bar1_window dev_dep[0x84 - 0x40] ++#define sprom_control dev_dep[0x88 - 0x40] ++#endif /* LINUX_POSTMOGRIFY_REMOVAL */ ++ + + /* PCI CAPABILITY DEFINES */ + #define PCI_CAP_POWERMGMTCAP_ID 0x01 +@@ -461,15 +462,6 @@ + } pcie_enhanced_caphdr; + + +-/* Everything below is BRCM HND proprietary */ +- +- +-/* Brcm PCI configuration registers */ +-#define cap_list rsvd_a[0] +-#define bar0_window dev_dep[0x80 - 0x40] +-#define bar1_window dev_dep[0x84 - 0x40] +-#define sprom_control dev_dep[0x88 - 0x40] +-#endif /* LINUX_POSTMOGRIFY_REMOVAL */ + #define PCI_BAR0_WIN 0x80 /* backplane addres space accessed by BAR0 */ + #define PCI_BAR1_WIN 0x84 /* backplane addres space accessed by BAR1 */ + #define PCI_SPROM_CONTROL 0x88 /* sprom property control */ +@@ -484,7 +476,11 @@ + #define PCI_GPIO_IN 0xb0 /* pci config space gpio input (>=rev3) */ + #define PCI_GPIO_OUT 0xb4 /* pci config space gpio output (>=rev3) */ + #define PCI_GPIO_OUTEN 0xb8 /* pci config space gpio output enable (>=rev3) */ +-#define PCI_L1SS_CTRL2 0x24c /* The L1 PM Substates Control register */ ++#define PCI_LINK_CTRL 0xbc /* PCI link control register */ ++#define PCI_DEV_STAT_CTRL2 0xd4 /* PCI device status control 2 register */ ++#define PCIE_LTR_MAX_SNOOP 0x1b4 /* PCIE LTRMaxSnoopLatency */ ++#define PCI_L1SS_CTRL 0x248 /* The L1 PM Substates Control register */ ++#define PCI_L1SS_CTRL2 0x24c /* The L1 PM Substates Control 2 register */ + + /* Private Registers */ + #define PCI_STAT_CTRL 0xa80 +@@ -560,5 +556,45 @@ + #define PCI_STAT_TA 0x08000000 /* target abort status */ + #endif /* LINUX_POSTMOGRIFY_REMOVAL */ + ++/* Header types */ ++#define PCI_HEADER_MULTI 0x80 ++#define PCI_HEADER_MASK 0x7f ++typedef enum { ++ PCI_HEADER_NORMAL, ++ PCI_HEADER_BRIDGE, ++ PCI_HEADER_CARDBUS ++} pci_header_types; ++ + #define PCI_CONFIG_SPACE_SIZE 256 ++ ++#define DWORD_ALIGN(x) (x & ~(0x03)) ++#define BYTE_POS(x) (x & 0x3) ++#define WORD_POS(x) (x & 0x1) ++ ++#define BYTE_SHIFT(x) (8 * BYTE_POS(x)) ++#define WORD_SHIFT(x) (16 * WORD_POS(x)) ++ ++#define BYTE_VAL(a, x) ((a >> BYTE_SHIFT(x)) & 0xFF) ++#define WORD_VAL(a, x) ((a >> WORD_SHIFT(x)) & 0xFFFF) ++ ++#define read_pci_cfg_byte(a) \ ++ (BYTE_VAL(OSL_PCI_READ_CONFIG(osh, DWORD_ALIGN(a), 4), a) & 0xff) ++ ++#define read_pci_cfg_word(a) \ ++ (WORD_VAL(OSL_PCI_READ_CONFIG(osh, DWORD_ALIGN(a), 4), a) & 0xffff) ++ ++#define write_pci_cfg_byte(a, val) do { \ ++ uint32 tmpval; \ ++ tmpval = (OSL_PCI_READ_CONFIG(osh, DWORD_ALIGN(a), 4) & ~0xFF << BYTE_POS(a)) | \ ++ val << BYTE_POS(a); \ ++ OSL_PCI_WRITE_CONFIG(osh, DWORD_ALIGN(a), 4, tmpval); \ ++ } while (0) ++ ++#define write_pci_cfg_word(a, val) do { \ ++ uint32 tmpval; \ ++ tmpval = (OSL_PCI_READ_CONFIG(osh, DWORD_ALIGN(a), 4) & ~0xFFFF << WORD_POS(a)) | \ ++ val << WORD_POS(a); \ ++ OSL_PCI_WRITE_CONFIG(osh, DWORD_ALIGN(a), 4, tmpval); \ ++ } while (0) ++ + #endif /* _h_pcicfg_ */ +diff -Nur a/drivers/net/wireless/bcmdhd/include/proto/802.11.h c/drivers/net/wireless/bcmdhd/include/proto/802.11.h +--- a/drivers/net/wireless/bcmdhd/include/proto/802.11.h 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/include/proto/802.11.h 2016-05-13 09:48:20.000000000 +0200 +@@ -3,7 +3,7 @@ + * + * Fundamental types and constants relating to 802.11 + * +- * $Id: 802.11.h 469158 2014-04-09 21:31:31Z $ ++ * $Id: 802.11.h 495738 2014-08-08 03:36:17Z $ + */ + + #ifndef _802_11_H_ +@@ -2905,7 +2905,7 @@ + BWL_PRE_PACKED_STRUCT struct brcm_prop_ie_s { + uint8 id; /* IE ID, 221, DOT11_MNG_PROPR_ID */ + uint8 len; /* IE length */ +- uint8 oui[3]; /* Proprietary OUI, BRCM_PROP_OUI */ ++ uint8 oui[3]; + uint8 type; /* type of this IE */ + uint16 cap; /* DPT capabilities */ + } BWL_POST_PACKED_STRUCT; +@@ -2949,10 +2949,6 @@ + #define BRF_ABCOUNTER_MASK 0xf0 /* afterburner is obsolete, defined for backward compat */ + #define BRF_PROP_11N_MCS 0x10 /* re-use afterburner bit */ + +-/** +- * Support for Broadcom proprietary HT MCS rates. Re-uses afterburner bits since afterburner is not +- * used anymore. Checks for BRF_ABCAP to stay compliant with 'old' images in the field. +- */ + #define GET_BRF_PROP_11N_MCS(brcm_ie) \ + (!((brcm_ie)->flags & BRF_ABCAP) && ((brcm_ie)->flags & BRF_PROP_11N_MCS)) + +@@ -3861,7 +3857,6 @@ + /* QoS map */ + #define QOS_MAP_FIXED_LENGTH (8 * 2) /* DSCP ranges fixed with 8 entries */ + +-/* BCM proprietary IE type for AIBSS */ + #define BCM_AIBSS_IE_TYPE 56 + + /* This marks the end of a packed structure section. */ +diff -Nur a/drivers/net/wireless/bcmdhd/include/proto/bcmdhcp.h c/drivers/net/wireless/bcmdhd/include/proto/bcmdhcp.h +--- a/drivers/net/wireless/bcmdhd/include/proto/bcmdhcp.h 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/include/proto/bcmdhcp.h 2016-05-13 09:48:20.000000000 +0200 +@@ -1,7 +1,7 @@ + /* + * Copyright (C) 2014, Broadcom Corporation + * All Rights Reserved. +- * ++ * + * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Broadcom Corporation; + * the contents of this file may not be disclosed to third parties, copied + * or duplicated in any form, in whole or in part, without the prior +diff -Nur a/drivers/net/wireless/bcmdhd/include/proto/bcmevent.h c/drivers/net/wireless/bcmdhd/include/proto/bcmevent.h +--- a/drivers/net/wireless/bcmdhd/include/proto/bcmevent.h 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/include/proto/bcmevent.h 2016-05-13 09:48:20.000000000 +0200 +@@ -5,7 +5,7 @@ + * + * Dependencies: proto/bcmeth.h + * +- * $Id: bcmevent.h 490387 2014-07-10 15:12:52Z $ ++ * $Id: bcmevent.h 505096 2014-09-26 12:49:04Z $ + * + */ + +@@ -384,6 +384,21 @@ + #define WLC_E_REASON_RMC_AR_LOST 1 + #define WLC_E_REASON_RMC_AR_NO_ACK 2 + ++#ifdef WLTDLS ++/* TDLS Action Category code */ ++#define TDLS_AF_CATEGORY 12 ++/* Wi-Fi Display (WFD) Vendor Specific Category */ ++/* used for WFD Tunneled Probe Request and Response */ ++#define TDLS_VENDOR_SPECIFIC 127 ++/* TDLS Action Field Values */ ++#define TDLS_ACTION_SETUP_REQ 0 ++#define TDLS_ACTION_SETUP_RESP 1 ++#define TDLS_ACTION_SETUP_CONFIRM 2 ++#define TDLS_ACTION_TEARDOWN 3 ++#define WLAN_TDLS_SET_PROBE_WFD_IE 11 ++#define WLAN_TDLS_SET_SETUP_WFD_IE 12 ++#endif ++ + + /* GAS event data */ + typedef BWL_PRE_PACKED_STRUCT struct wl_event_gas { +diff -Nur a/drivers/net/wireless/bcmdhd/include/proto/bcmudp.h c/drivers/net/wireless/bcmdhd/include/proto/bcmudp.h +--- a/drivers/net/wireless/bcmdhd/include/proto/bcmudp.h 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/include/proto/bcmudp.h 2016-05-13 09:48:20.000000000 +0200 +@@ -1,7 +1,7 @@ + /* + * Copyright (C) 2014, Broadcom Corporation + * All Rights Reserved. +- * ++ * + * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Broadcom Corporation; + * the contents of this file may not be disclosed to third parties, copied + * or duplicated in any form, in whole or in part, without the prior +diff -Nur a/drivers/net/wireless/bcmdhd/include/proto/wpa.h c/drivers/net/wireless/bcmdhd/include/proto/wpa.h +--- a/drivers/net/wireless/bcmdhd/include/proto/wpa.h 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/include/proto/wpa.h 2016-05-13 09:48:20.000000000 +0200 +@@ -3,7 +3,7 @@ + * + * $Copyright Open Broadcom Corporation$ + * +- * $Id: wpa.h 450928 2014-01-23 14:13:38Z $ ++ * $Id: wpa.h 492853 2014-07-23 17:20:34Z $ + */ + + #ifndef _proto_wpa_h_ +@@ -177,6 +177,7 @@ + #define WPA_CAP_WPA2_PREAUTH RSN_CAP_PREAUTH + + #define WPA2_PMKID_COUNT_LEN 2 ++#define RSN_GROUPMANAGE_CIPHER_LEN 4 + + #ifdef BCMWAPI_WAI + #define WAPI_CAP_PREAUTH RSN_CAP_PREAUTH +diff -Nur a/drivers/net/wireless/bcmdhd/include/sbchipc.h c/drivers/net/wireless/bcmdhd/include/sbchipc.h +--- a/drivers/net/wireless/bcmdhd/include/sbchipc.h 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/include/sbchipc.h 2016-05-13 09:48:20.000000000 +0200 +@@ -2824,7 +2824,8 @@ + #define CCTRL2_4335_PMUWAKE (1 << 31) + #define PATCHTBL_SIZE (0x800) + #define CR4_4335_RAM_BASE (0x180000) +-#define CR4_4345_RAM_BASE (0x1b0000) ++#define CR4_4345_LT_C0_RAM_BASE (0x1b0000) ++#define CR4_4345_GE_C0_RAM_BASE (0x198000) + #define CR4_4349_RAM_BASE (0x180000) + #define CR4_4350_RAM_BASE (0x180000) + #define CR4_4360_RAM_BASE (0x0) +diff -Nur a/drivers/net/wireless/bcmdhd/include/sdiovar.h c/drivers/net/wireless/bcmdhd/include/sdiovar.h +--- a/drivers/net/wireless/bcmdhd/include/sdiovar.h 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/include/sdiovar.h 2016-05-13 09:48:20.000000000 +0200 +@@ -31,6 +31,7 @@ + #define SDH_CTRL_VAL 0x0020 /* Control Regs */ + #define SDH_LOG_VAL 0x0040 /* Enable bcmlog */ + #define SDH_DMA_VAL 0x0080 /* DMA */ ++#define SDH_COST_VAL 0x8000 /* Control Regs */ + + #define NUM_PREV_TRANSACTIONS 16 + +diff -Nur a/drivers/net/wireless/bcmdhd/include/siutils.h c/drivers/net/wireless/bcmdhd/include/siutils.h +--- a/drivers/net/wireless/bcmdhd/include/siutils.h 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/include/siutils.h 2016-05-13 09:48:20.000000000 +0200 +@@ -363,7 +363,7 @@ + + #if defined(BCMDBG_PHYDUMP) + extern void si_dumpregs(si_t *sih, struct bcmstrbuf *b); +-#endif ++#endif + + extern uint32 si_ccreg(si_t *sih, uint32 offset, uint32 mask, uint32 val); + extern uint32 si_pciereg(si_t *sih, uint32 offset, uint32 mask, uint32 val, uint type); +diff -Nur a/drivers/net/wireless/bcmdhd/include/typedefs.h c/drivers/net/wireless/bcmdhd/include/typedefs.h +--- a/drivers/net/wireless/bcmdhd/include/typedefs.h 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/include/typedefs.h 2016-05-13 09:48:20.000000000 +0200 +@@ -94,7 +94,7 @@ + #endif + #endif /* == 2.6.18 */ + #endif /* __KERNEL__ */ +-#endif /* !defined(LINUX_HYBRID) || defined(LINUX_PORT) */ ++#endif /* !defined(LINUX_HYBRID) || defined(LINUX_PORT) */ + + + /* Do not support the (u)int64 types with strict ansi for GNU C */ +@@ -132,7 +132,7 @@ + + #endif /* linux && __KERNEL__ */ + +-#endif ++#endif + + + /* use the default typedefs in the next section of this file */ +@@ -272,7 +272,7 @@ + #define BWL_COMPILER_ARMCC + #else + #error "Unknown compiler!" +-#endif ++#endif + + + #ifndef INLINE +@@ -284,7 +284,7 @@ + #define INLINE __inline + #else + #define INLINE +- #endif ++ #endif + #endif /* INLINE */ + + #undef TYPEDEF_BOOL +diff -Nur a/drivers/net/wireless/bcmdhd/include/wlfc_proto.h c/drivers/net/wireless/bcmdhd/include/wlfc_proto.h +--- a/drivers/net/wireless/bcmdhd/include/wlfc_proto.h 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/include/wlfc_proto.h 2016-05-13 09:48:20.000000000 +0200 +@@ -1,6 +1,6 @@ + /* + * $Copyright Open 2009 Broadcom Corporation$ +-* $Id: wlfc_proto.h 455301 2014-02-13 12:42:13Z $ ++* $Id: wlfc_proto.h 499510 2014-08-28 23:40:47Z $ + * + */ + #ifndef __wlfc_proto_definitions_h__ +@@ -107,8 +107,9 @@ + #define WLFC_CTL_VALUE_LEN_REQUEST_PACKET 3 /* credit, MAC-handle, prec_bitmap */ + + +-#define WLFC_PKTFLAG_PKTFROMHOST 0x01 +-#define WLFC_PKTFLAG_PKT_REQUESTED 0x02 ++#define WLFC_PKTFLAG_PKTFROMHOST 0x01 /* packet originated from hot side */ ++#define WLFC_PKTFLAG_PKT_REQUESTED 0x02 /* packet requsted by firmware side */ ++#define WLFC_PKTFLAG_PKT_FORCELOWRATE 0x04 /* force low rate for this packet */ + + #define WL_TXSTATUS_STATUS_MASK 0xff /* allow 8 bits */ + #define WL_TXSTATUS_STATUS_SHIFT 24 +@@ -199,13 +200,6 @@ + /* b[7:5] -reuse guard, b[4:0] -value */ + #define WLFC_MAC_DESC_GET_LOOKUP_INDEX(x) ((x) & 0x1f) + +-#define WLFC_PKTFLAG_SET_PKTREQUESTED(x) (x) |= \ +- (WLFC_PKTFLAG_PKT_REQUESTED << WL_TXSTATUS_FLAGS_SHIFT) +- +-#define WLFC_PKTFLAG_CLR_PKTREQUESTED(x) (x) &= \ +- ~(WLFC_PKTFLAG_PKT_REQUESTED << WL_TXSTATUS_FLAGS_SHIFT) +- +- + #define WLFC_MAX_PENDING_DATALEN 120 + + /* host is free to discard the packet */ +diff -Nur a/drivers/net/wireless/bcmdhd/include/wlioctl.h c/drivers/net/wireless/bcmdhd/include/wlioctl.h +--- a/drivers/net/wireless/bcmdhd/include/wlioctl.h 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/include/wlioctl.h 2016-05-13 09:48:20.000000000 +0200 +@@ -6,7 +6,7 @@ + * + * $Copyright Open Broadcom Corporation$ + * +- * $Id: wlioctl.h 490639 2014-07-11 08:31:53Z $ ++ * $Id: wlioctl.h 504503 2014-09-24 11:28:56Z $ + */ + + #ifndef _wlioctl_h_ +@@ -24,9 +24,6 @@ + #include + #include + +-#if 0 && (NDISVER >= 0x0600) +-#include +-#endif + + #ifndef LINUX_POSTMOGRIFY_REMOVAL + #include +@@ -2264,6 +2261,25 @@ + uint32 bphy_badplcp; + + } wl_delta_stats_t; ++ ++#if defined(CUSTOM_PLATFORM_NV_TEGRA) ++/* structure to store per-rate rx statistics */ ++typedef struct wl_scb_rx_rate_stats { ++ uint32 rx1mbps[2]; /* packets rx at 1Mbps */ ++ uint32 rx2mbps[2]; /* packets rx at 2Mbps */ ++ uint32 rx5mbps5[2]; /* packets rx at 5.5Mbps */ ++ uint32 rx6mbps[2]; /* packets rx at 6Mbps */ ++ uint32 rx9mbps[2]; /* packets rx at 9Mbps */ ++ uint32 rx11mbps[2]; /* packets rx at 11Mbps */ ++ uint32 rx12mbps[2]; /* packets rx at 12Mbps */ ++ uint32 rx18mbps[2]; /* packets rx at 18Mbps */ ++ uint32 rx24mbps[2]; /* packets rx at 24Mbps */ ++ uint32 rx36mbps[2]; /* packets rx at 36Mbps */ ++ uint32 rx48mbps[2]; /* packets rx at 48Mbps */ ++ uint32 rx54mbps[2]; /* packets rx at 54Mbps */ ++} wl_scb_rx_rate_stats_t; ++#endif /* defined(CUSTOM_PLATFORM_NV_TEGRA) */ ++ + #endif /* LINUX_POSTMOGRIFY_REMOVAL */ + + typedef struct { +@@ -2917,6 +2933,20 @@ + * Dongle pattern matching filter. + */ + ++/* Packet filter operation mode */ ++/* True: 1; False: 0 */ ++#define PKT_FILTER_MODE_FORWARD_ON_MATCH 1 ++/* Enable and disable pkt_filter as a whole */ ++#define PKT_FILTER_MODE_DISABLE 2 ++/* Cache first matched rx pkt(be queried by host later) */ ++#define PKT_FILTER_MODE_PKT_CACHE_ON_MATCH 4 ++/* If pkt_filter is enabled and no filter is set, don't forward anything */ ++#define PKT_FILTER_MODE_PKT_FORWARD_OFF_DEFAULT 8 ++#if defined(CUSTOM_PLATFORM_NV_TEGRA) ++/* Ports only filter mode */ ++#define PKT_FILTER_MODE_PORTS_ONLY 16 ++#endif /* defined(CUSTOM_PLATFORM_NV_TEGRA) */ ++ + #define MAX_WAKE_PACKET_CACHE_BYTES 128 /* Maximum cached wake packet */ + + #define MAX_WAKE_PACKET_BYTES (DOT11_A3_HDR_LEN + \ +@@ -2959,12 +2989,13 @@ + * that indicates which bits within the pattern should be matched. + */ + typedef struct wl_pkt_filter_pattern { +- union { ++// terence 20150525: fix pkt filter error -14 in 64bit OS ++// union { + uint32 offset; /* Offset within received packet to start pattern matching. + * Offset '0' is the first byte of the ethernet header. + */ +- wl_pkt_decrypter_t* decrypt_ctx; /* Decrypt context */ +- }; ++// wl_pkt_decrypter_t* decrypt_ctx; /* Decrypt context */ ++// }; + uint32 size_bytes; /* Size of the pattern. Bitmask must be the same size. */ + uint8 mask_and_pattern[1]; /* Variable length mask and pattern data. mask starts + * at offset 0. Pattern immediately follows mask. +@@ -3458,6 +3489,13 @@ + uint32 count; + } BWL_POST_PACKED_STRUCT pcie_bus_tput_stats_t; + ++#define MAX_ROAMOFFL_BSSID_NUM 100 ++ ++typedef BWL_PRE_PACKED_STRUCT struct roamoffl_bssid_list { ++ int cnt; ++ struct ether_addr bssid[1]; ++} BWL_POST_PACKED_STRUCT roamoffl_bssid_list_t; ++ + /* no default structure packing */ + #include + +@@ -3663,22 +3701,6 @@ + uint8 id; + } BWL_POST_PACKED_STRUCT; + +-#if 0 && (NDISVER >= 0x0600) +-/* Return values */ +-#define ND_REPLY_PEER 0x1 /* Reply was sent to service NS request from peer */ +-#define ND_REQ_SINK 0x2 /* Input packet should be discarded */ +-#define ND_FORCE_FORWARD 0X3 /* For the dongle to forward req to HOST */ +- +- +-/* Neighbor Solicitation Response Offload IOVAR param */ +-typedef BWL_PRE_PACKED_STRUCT struct nd_param { +- struct ipv6_addr host_ip[2]; +- struct ipv6_addr solicit_ip; +- struct ipv6_addr remote_ip; +- uint8 host_mac[ETHER_ADDR_LEN]; +- uint32 offload_id; +-} BWL_POST_PACKED_STRUCT nd_param_t; +-#endif + + typedef BWL_PRE_PACKED_STRUCT struct wl_pfn_roam_thresh { + uint32 pfn_alert_thresh; /* time in ms */ +@@ -5020,6 +5042,8 @@ + typedef BWL_PRE_PACKED_STRUCT struct nan_scan_params { + uint16 scan_time; + uint16 home_time; ++ uint16 ms_intvl; /* interval between merge scan */ ++ uint16 ms_dur; /* duration of merge scan */ + uint16 chspec_num; + chanspec_t chspec_list[NAN_SCAN_MAX_CHCNT]; /* act. used 3, 5 rfu */ + } BWL_POST_PACKED_STRUCT nan_scan_params_t; +@@ -5858,6 +5882,48 @@ + wl_roam_prof_t roam_prof[WL_MAX_ROAM_PROF_BRACKETS]; + } wl_roam_prof_band_t; + ++/* Data structures for Interface Create/Remove */ ++ ++#define WL_INTERFACE_CREATE_VER (0) ++ ++/* ++ * The flags filed of the wl_interface_create is designed to be ++ * a Bit Mask. As of now only Bit 0 and Bit 1 are used as mentioned below. ++ * The rest of the bits can be used, incase we have to provide ++ * more information to the dongle ++ */ ++ ++/* ++ * Bit 0 of flags field is used to inform whether the interface requested to ++ * be created is STA or AP. ++ * 0 - Create a STA interface ++ * 1 - Create an AP interface ++ */ ++#define WL_INTERFACE_CREATE_STA (0 << 0) ++#define WL_INTERFACE_CREATE_AP (1 << 0) ++ ++/* ++ * Bit 1 of flags field is used to inform whether MAC is present in the ++ * data structure or not. ++ * 0 - Ignore mac_addr field ++ * 1 - Use the mac_addr field ++ */ ++#define WL_INTERFACE_MAC_DONT_USE (0 << 1) ++#define WL_INTERFACE_MAC_USE (1 << 1) ++ ++typedef struct wl_interface_create { ++ uint16 ver; /* version of this struct */ ++ uint32 flags; /* flags that defines the operation */ ++ struct ether_addr mac_addr; /* Optional Mac address */ ++} wl_interface_create_t; ++ ++typedef struct wl_interface_info { ++ uint16 ver; /* version of this struct */ ++ struct ether_addr mac_addr; /* MAC address of the interface */ ++ char ifname[BCM_MSG_IFNAME_MAX]; /* name of interface */ ++ uint8 bsscfgidx; /* source bsscfg index */ ++} wl_interface_info_t; ++ + /* no default structure packing */ + #include + +diff -Nur a/drivers/net/wireless/bcmdhd/Kconfig c/drivers/net/wireless/bcmdhd/Kconfig +--- a/drivers/net/wireless/bcmdhd/Kconfig 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/Kconfig 2016-05-13 09:48:20.000000000 +0200 +@@ -18,13 +18,6 @@ + ---help--- + Path to the calibration file. + +-config BCMDHD_CONFIG_PATH +- depends on BCMDHD +- string "Config path" +- default "/system/etc/firmware/config.txt" +- ---help--- +- Path to the driver configuration file. +- + config BCMDHD_WEXT + bool "Enable WEXT support" + depends on BCMDHD && CFG80211 = n +@@ -34,18 +27,31 @@ + Enables WEXT support + + choice ++ prompt "Enable Chip Interface" + depends on BCMDHD ++ ---help--- ++ Enable Chip Interface. ++config BCMDHD_SDIO ++ bool "SDIO bus interface support" ++ depends on BCMDHD && MMC ++config BCMDHD_PCIE ++ bool "PCIe bus interface support" ++ depends on BCMDHD && PCI ++endchoice ++ ++choice ++ depends on BCMDHD && BCMDHD_SDIO + prompt "Interrupt type" + ---help--- +- Interrupt type ++ Interrupt type + config BCMDHD_OOB +- depends on BCMDHD ++ depends on BCMDHD && BCMDHD_SDIO + bool "Out-of-Band Interrupt" + default y + ---help--- +- Interrupt from WL_HOST_WAKE. ++ Interrupt from WL_HOST_WAKE. + config BCMDHD_SDIO_IRQ +- depends on BCMDHD ++ depends on BCMDHD && BCMDHD_SDIO + bool "In-Band Interrupt" + ---help--- + Interrupt from SDIO DAT[1] +diff -Nur a/drivers/net/wireless/bcmdhd/linux_osl.c c/drivers/net/wireless/bcmdhd/linux_osl.c +--- a/drivers/net/wireless/bcmdhd/linux_osl.c 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/linux_osl.c 2016-05-13 09:48:20.000000000 +0200 +@@ -3,7 +3,7 @@ + * + * $Copyright Open Broadcom Corporation$ + * +- * $Id: linux_osl.c 490846 2014-07-12 13:08:59Z $ ++ * $Id: linux_osl.c 503131 2014-09-17 12:16:08Z $ + */ + + #define LINUX_PORT +@@ -23,9 +23,31 @@ + #include + #include + #include ++#include + + + ++#ifdef BCM_SECURE_DMA ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#if defined(__ARM_ARCH_7A__) ++#include ++#include ++#endif ++#include ++#endif /* BCM_SECURE_DMA */ ++ + #include + + #ifdef BCM47XX_ACP_WAR +@@ -33,6 +55,12 @@ + extern spinlock_t l2x0_reg_lock; + #endif + ++#if defined(BCMPCIE) ++#if defined(CONFIG_DHD_USE_STATIC_BUF) && defined(DHD_USE_STATIC_FLOWRING) ++#include ++#endif /* CONFIG_DHD_USE_STATIC_BUF && DHD_USE_STATIC_FLOWRING */ ++#endif /* BCMPCIE */ ++ + #define PCI_CFG_RETRY 10 + + #define OS_HANDLE_MAGIC 0x1234abcd /* Magic # to recognize osh */ +@@ -78,6 +106,20 @@ + + static bcm_static_pkt_t *bcm_static_skb = 0; + ++#if defined(BCMPCIE) && defined(DHD_USE_STATIC_FLOWRING) ++#define STATIC_BUF_FLOWRING_SIZE ((PAGE_SIZE)*(7)) ++#define STATIC_BUF_FLOWRING_NUM 42 ++#define RINGID_TO_FLOWID(idx) ((idx) + (BCMPCIE_H2D_COMMON_MSGRINGS) \ ++ - (BCMPCIE_H2D_TXFLOWRINGID)) ++typedef struct bcm_static_flowring_buf { ++ spinlock_t flowring_lock; ++ void *buf_ptr[STATIC_BUF_FLOWRING_NUM]; ++ unsigned char buf_use[STATIC_BUF_FLOWRING_NUM]; ++} bcm_static_flowring_buf_t; ++ ++bcm_static_flowring_buf_t *bcm_static_flowring = 0; ++#endif /* BCMPCIE && DHD_USE_STATIC_FLOWRING */ ++ + void* wifi_platform_prealloc(void *adapter, int section, unsigned long size); + #endif /* CONFIG_DHD_USE_STATIC_BUF */ + +@@ -118,7 +160,50 @@ + int ctrace_num; + #endif /* BCMDBG_CTRACE */ + uint32 flags; /* If specific cases to be handled in the OSL */ ++#ifdef BCM_SECURE_DMA ++ struct cma_dev *cma; ++ struct sec_mem_elem *sec_list_512; ++ struct sec_mem_elem *sec_list_base_512; ++ struct sec_mem_elem *sec_list_2048; ++ struct sec_mem_elem *sec_list_base_2048; ++ struct sec_mem_elem *sec_list_4096; ++ struct sec_mem_elem *sec_list_base_4096; ++ phys_addr_t contig_base; ++ void *contig_base_va; ++ phys_addr_t contig_base_alloc; ++ void *contig_base_alloc_va; ++ phys_addr_t contig_base_alloc_coherent; ++ void *contig_base_alloc_coherent_va; ++ phys_addr_t contig_delta_va_pa; ++ struct { ++ phys_addr_t pa; ++ void *va; ++ bool avail; ++ } sec_cma_coherent[SEC_CMA_COHERENT_MAX]; ++ ++#endif /* BCM_SECURE_DMA */ ++ + }; ++#ifdef BCM_SECURE_DMA ++phys_addr_t g_contig_delta_va_pa; ++static void osl_sec_dma_setup_contig_mem(osl_t *osh, unsigned long memsize, int regn); ++static int osl_sec_dma_alloc_contig_mem(osl_t *osh, unsigned long memsize, int regn); ++static void osl_sec_dma_free_contig_mem(osl_t *osh, u32 memsize, int regn); ++static void * osl_sec_dma_ioremap(osl_t *osh, struct page *page, size_t size, ++ bool iscache, bool isdecr); ++static void osl_sec_dma_iounmap(osl_t *osh, void *contig_base_va, size_t size); ++static void osl_sec_dma_init_elem_mem_block(osl_t *osh, size_t mbsize, int max, ++ sec_mem_elem_t **list); ++static void osl_sec_dma_deinit_elem_mem_block(osl_t *osh, size_t mbsize, int max, ++ void *sec_list_base); ++static sec_mem_elem_t * osl_sec_dma_alloc_mem_elem(osl_t *osh, void *va, uint size, ++ int direction, struct sec_cma_info *ptr_cma_info, uint offset); ++static void osl_sec_dma_free_mem_elem(osl_t *osh, sec_mem_elem_t *sec_mem_elem); ++static void osl_sec_dma_init_consistent(osl_t *osh); ++static void *osl_sec_dma_alloc_consistent(osl_t *osh, uint size, uint16 align_bits, ++ ulong *pap); ++static void osl_sec_dma_free_consistent(osl_t *osh, void *va, uint size, dmaaddr_t pa); ++#endif /* BCM_SECURE_DMA */ + + #define OSL_PKTTAG_CLEAR(p) \ + do { \ +@@ -213,14 +298,15 @@ + /* Array bounds covered by ASSERT in osl_attach */ + return linuxbcmerrormap[-bcmerror]; + } +-#ifdef SHARED_OSL_CMN ++ + osl_t * ++#ifdef SHARED_OSL_CMN + osl_attach(void *pdev, uint bustype, bool pkttag, void **osl_cmn) +-{ + #else +-osl_t * + osl_attach(void *pdev, uint bustype, bool pkttag) ++#endif /* SHARED_OSL_CMN */ + { ++#ifndef SHARED_OSL_CMN + void **osl_cmn = NULL; + #endif /* SHARED_OSL_CMN */ + osl_t *osh; +@@ -261,6 +347,39 @@ + osh->pub.pkttag = pkttag; + osh->bustype = bustype; + osh->magic = OS_HANDLE_MAGIC; ++#ifdef BCM_SECURE_DMA ++ ++ osl_sec_dma_setup_contig_mem(osh, CMA_MEMBLOCK, CONT_ARMREGION); ++ ++#ifdef BCM47XX_CA9 ++ osh->contig_base_alloc_coherent_va = osl_sec_dma_ioremap(osh, ++ phys_to_page((u32)osh->contig_base_alloc), ++ CMA_DMA_DESC_MEMBLOCK, TRUE, TRUE); ++#else ++ osh->contig_base_alloc_coherent_va = osl_sec_dma_ioremap(osh, ++ phys_to_page((u32)osh->contig_base_alloc), ++ CMA_DMA_DESC_MEMBLOCK, FALSE, TRUE); ++#endif /* BCM47XX_CA9 */ ++ ++ osh->contig_base_alloc_coherent = osh->contig_base_alloc; ++ osl_sec_dma_init_consistent(osh); ++ ++ osh->contig_base_alloc += CMA_DMA_DESC_MEMBLOCK; ++ ++ osh->contig_base_alloc_va = osl_sec_dma_ioremap(osh, ++ phys_to_page((u32)osh->contig_base_alloc), CMA_DMA_DATA_MEMBLOCK, TRUE, FALSE); ++ osh->contig_base_va = osh->contig_base_alloc_va; ++ ++ /* ++ * osl_sec_dma_init_elem_mem_block(osh, CMA_BUFSIZE_512, CMA_BUFNUM, &osh->sec_list_512); ++ * osh->sec_list_base_512 = osh->sec_list_512; ++ * osl_sec_dma_init_elem_mem_block(osh, CMA_BUFSIZE_2K, CMA_BUFNUM, &osh->sec_list_2048); ++ * osh->sec_list_base_2048 = osh->sec_list_2048; ++ */ ++ osl_sec_dma_init_elem_mem_block(osh, CMA_BUFSIZE_4K, CMA_BUFNUM, &osh->sec_list_4096); ++ osh->sec_list_base_4096 = osh->sec_list_4096; ++ ++#endif /* BCM_SECURE_DMA */ + + switch (bustype) { + case PCI_BUS: +@@ -293,47 +412,70 @@ + int osl_static_mem_init(osl_t *osh, void *adapter) + { + #ifdef CONFIG_DHD_USE_STATIC_BUF +- if (!bcm_static_buf && adapter) { +- if (!(bcm_static_buf = (bcm_static_buf_t *)wifi_platform_prealloc(adapter, +- 3, STATIC_BUF_SIZE + STATIC_BUF_TOTAL_LEN))) { +- printk("can not alloc static buf!\n"); +- bcm_static_skb = NULL; +- ASSERT(osh->magic == OS_HANDLE_MAGIC); +- kfree(osh); +- return -ENOMEM; +- } +- else +- printk("alloc static buf at %x!\n", (unsigned int)bcm_static_buf); ++ if (!bcm_static_buf && adapter) { ++ if (!(bcm_static_buf = (bcm_static_buf_t *)wifi_platform_prealloc(adapter, ++ 3, STATIC_BUF_SIZE + STATIC_BUF_TOTAL_LEN))) { ++ printk("can not alloc static buf!\n"); ++ bcm_static_skb = NULL; ++ ASSERT(osh->magic == OS_HANDLE_MAGIC); ++ return -ENOMEM; ++ } ++ else ++ printk("alloc static buf at %p!\n", bcm_static_buf); + + +- sema_init(&bcm_static_buf->static_sem, 1); ++ sema_init(&bcm_static_buf->static_sem, 1); + +- bcm_static_buf->buf_ptr = (unsigned char *)bcm_static_buf + STATIC_BUF_SIZE; +- } ++ bcm_static_buf->buf_ptr = (unsigned char *)bcm_static_buf + STATIC_BUF_SIZE; ++ } + + #ifdef BCMSDIO +- if (!bcm_static_skb && adapter) { +- int i; +- void *skb_buff_ptr = 0; +- bcm_static_skb = (bcm_static_pkt_t *)((char *)bcm_static_buf + 2048); +- skb_buff_ptr = wifi_platform_prealloc(adapter, 4, 0); +- if (!skb_buff_ptr) { +- printk("cannot alloc static buf!\n"); +- bcm_static_buf = NULL; +- bcm_static_skb = NULL; +- ASSERT(osh->magic == OS_HANDLE_MAGIC); +- kfree(osh); +- return -ENOMEM; +- } ++ if (!bcm_static_skb && adapter) { ++ int i; ++ void *skb_buff_ptr = 0; ++ bcm_static_skb = (bcm_static_pkt_t *)((char *)bcm_static_buf + 2048); ++ skb_buff_ptr = wifi_platform_prealloc(adapter, 4, 0); ++ if (!skb_buff_ptr) { ++ printk("cannot alloc static buf!\n"); ++ bcm_static_buf = NULL; ++ bcm_static_skb = NULL; ++ ASSERT(osh->magic == OS_HANDLE_MAGIC); ++ return -ENOMEM; ++ } + +- bcopy(skb_buff_ptr, bcm_static_skb, sizeof(struct sk_buff *) * +- (STATIC_PKT_MAX_NUM * 2 + STATIC_PKT_4PAGE_NUM)); +- for (i = 0; i < STATIC_PKT_MAX_NUM * 2 + STATIC_PKT_4PAGE_NUM; i++) +- bcm_static_skb->pkt_use[i] = 0; ++ bcopy(skb_buff_ptr, bcm_static_skb, sizeof(struct sk_buff *) * ++ (STATIC_PKT_MAX_NUM * 2 + STATIC_PKT_4PAGE_NUM)); ++ for (i = 0; i < STATIC_PKT_MAX_NUM * 2 + STATIC_PKT_4PAGE_NUM; i++) ++ bcm_static_skb->pkt_use[i] = 0; + +- sema_init(&bcm_static_skb->osl_pkt_sem, 1); +- } ++ sema_init(&bcm_static_skb->osl_pkt_sem, 1); ++ } + #endif /* BCMSDIO */ ++#if defined(BCMPCIE) && defined(DHD_USE_STATIC_FLOWRING) ++ if (!bcm_static_flowring && adapter) { ++ int i; ++ void *flowring_ptr = 0; ++ bcm_static_flowring = ++ (bcm_static_flowring_buf_t *)((char *)bcm_static_buf + 4096); ++ flowring_ptr = wifi_platform_prealloc(adapter, 10, 0); ++ if (!flowring_ptr) { ++ printk("%s: flowring_ptr is NULL\n", __FUNCTION__); ++ bcm_static_buf = NULL; ++ bcm_static_skb = NULL; ++ bcm_static_flowring = NULL; ++ ASSERT(osh->magic == OS_HANDLE_MAGIC); ++ return -ENOMEM; ++ } ++ ++ bcopy(flowring_ptr, bcm_static_flowring->buf_ptr, ++ sizeof(void *) * STATIC_BUF_FLOWRING_NUM); ++ for (i = 0; i < STATIC_BUF_FLOWRING_NUM; i++) { ++ bcm_static_flowring->buf_use[i] = 0; ++ } ++ ++ spin_lock_init(&bcm_static_flowring->flowring_lock); ++ } ++#endif /* BCMPCIE && DHD_USE_STATIC_FLOWRING */ + #endif /* CONFIG_DHD_USE_STATIC_BUF */ + + return 0; +@@ -354,6 +496,13 @@ + { + if (osh == NULL) + return; ++#ifdef BCM_SECURE_DMA ++ osl_sec_dma_free_contig_mem(osh, CMA_MEMBLOCK, CONT_ARMREGION); ++ osl_sec_dma_deinit_elem_mem_block(osh, CMA_BUFSIZE_512, CMA_BUFNUM, osh->sec_list_base_512); ++ osl_sec_dma_deinit_elem_mem_block(osh, CMA_BUFSIZE_2K, CMA_BUFNUM, osh->sec_list_base_2048); ++ osl_sec_dma_deinit_elem_mem_block(osh, CMA_BUFSIZE_4K, CMA_BUFNUM, osh->sec_list_base_4096); ++ osl_sec_dma_iounmap(osh, osh->contig_base_va, CMA_MEMBLOCK); ++#endif /* BCM_SECURE_DMA */ + + ASSERT(osh->magic == OS_HANDLE_MAGIC); + atomic_sub(1, &osh->cmn->refcount); +@@ -374,6 +523,11 @@ + bcm_static_skb = 0; + } + #endif /* BCMSDIO */ ++#if defined(BCMPCIE) && defined(DHD_USE_STATIC_FLOWRING) ++ if (bcm_static_flowring) { ++ bcm_static_flowring = 0; ++ } ++#endif /* BCMPCIE && DHD_USE_STATIC_FLOWRING */ + #endif /* CONFIG_DHD_USE_STATIC_BUF */ + return 0; + } +@@ -543,6 +697,11 @@ + bcm_static_skb = 0; + } + #endif /* BCMSDIO */ ++#if defined(BCMPCIE) && defined(DHD_USE_STATIC_FLOWRING) ++ if (bcm_static_flowring) { ++ bcm_static_flowring = 0; ++ } ++#endif /* BCMPCIE && DHD_USE_STATIC_FLOWRING */ + #endif /* CONFIG_DHD_USE_STATIC_BUF */ + + bb = b; +@@ -633,18 +792,17 @@ + /* Account for a downstream forwarder delivered packet to a WL/DHD driver. + * Increment a GMAC forwarder interface's pktalloced count. + */ +-#ifdef BCMDBG_CTRACE + void BCMFASTPATH ++#ifdef BCMDBG_CTRACE + osl_pkt_frmfwder(osl_t *osh, void *skbs, int skb_cnt, int line, char *file) + #else +-void BCMFASTPATH + osl_pkt_frmfwder(osl_t *osh, void *skbs, int skb_cnt) + #endif /* BCMDBG_CTRACE */ + { + #if defined(BCMDBG_CTRACE) + int i; + struct sk_buff *skb; +-#endif ++#endif + + #if defined(BCMDBG_CTRACE) + if (skb_cnt > 1) { +@@ -663,7 +821,7 @@ + ADD_CTRACE(osh, skb, file, line); + #endif /* BCMDBG_CTRACE */ + } +-#endif ++#endif + + atomic_add(skb_cnt, &osh->cmn->pktalloced); + } +@@ -709,11 +867,10 @@ + * In the process, native packet is destroyed, there is no copying + * Also, a packettag is zeroed out + */ +-#ifdef BCMDBG_CTRACE + void * BCMFASTPATH ++#ifdef BCMDBG_CTRACE + osl_pkt_frmnative(osl_t *osh, void *pkt, int line, char *file) + #else +-void * BCMFASTPATH + osl_pkt_frmnative(osl_t *osh, void *pkt) + #endif /* BCMDBG_CTRACE */ + { +@@ -745,11 +902,10 @@ + } + + /* Return a new packet. zero out pkttag */ +-#ifdef BCMDBG_CTRACE + void * BCMFASTPATH ++#ifdef BCMDBG_CTRACE + osl_pktget(osl_t *osh, uint len, int line, char *file) + #else +-void * BCMFASTPATH + osl_pktget(osl_t *osh, uint len) + #endif /* BCMDBG_CTRACE */ + { +@@ -758,10 +914,11 @@ + #ifdef CTFPOOL + /* Allocate from local pool */ + skb = osl_pktfastget(osh, len); +- if ((skb != NULL) || ((skb = osl_alloc_skb(osh, len)) != NULL)) { ++ if ((skb != NULL) || ((skb = osl_alloc_skb(osh, len)) != NULL)) + #else /* CTFPOOL */ +- if ((skb = osl_alloc_skb(osh, len))) { ++ if ((skb = osl_alloc_skb(osh, len))) + #endif /* CTFPOOL */ ++ { + skb->tail += len; + skb->len += len; + skb->priority = 0; +@@ -872,6 +1029,9 @@ + int i = 0; + struct sk_buff *skb; + ++ if (!bcm_static_skb) ++ return osl_pktget(osh, len); ++ + if (len > DHD_SKB_MAX_BUFSIZE) { + printk("%s: attempt to allocate huge packet (0x%x)\n", __FUNCTION__, len); + return osl_pktget(osh, len); +@@ -889,7 +1049,11 @@ + bcm_static_skb->pkt_use[i] = 1; + + skb = bcm_static_skb->skb_4k[i]; ++#ifdef NET_SKBUFF_DATA_USES_OFFSET ++ skb_set_tail_pointer(skb, len); ++#else + skb->tail = skb->data + len; ++#endif /* NET_SKBUFF_DATA_USES_OFFSET */ + skb->len = len; + + up(&bcm_static_skb->osl_pkt_sem); +@@ -907,7 +1071,11 @@ + if (i != STATIC_PKT_MAX_NUM) { + bcm_static_skb->pkt_use[i + STATIC_PKT_MAX_NUM] = 1; + skb = bcm_static_skb->skb_8k[i]; ++#ifdef NET_SKBUFF_DATA_USES_OFFSET ++ skb_set_tail_pointer(skb, len); ++#else + skb->tail = skb->data + len; ++#endif /* NET_SKBUFF_DATA_USES_OFFSET */ + skb->len = len; + + up(&bcm_static_skb->osl_pkt_sem); +@@ -920,13 +1088,17 @@ + bcm_static_skb->pkt_use[STATIC_PKT_MAX_NUM * 2] = 1; + + skb = bcm_static_skb->skb_16k; ++#ifdef NET_SKBUFF_DATA_USES_OFFSET ++ skb_set_tail_pointer(skb, len); ++#else + skb->tail = skb->data + len; ++#endif /* NET_SKBUFF_DATA_USES_OFFSET */ + skb->len = len; + + up(&bcm_static_skb->osl_pkt_sem); + return skb; + } +-#endif ++#endif /* ENHANCED_STATIC_BUF */ + + up(&bcm_static_skb->osl_pkt_sem); + printk("%s: all static pkt in use!\n", __FUNCTION__); +@@ -968,6 +1140,92 @@ + up(&bcm_static_skb->osl_pkt_sem); + osl_pktfree(osh, p, send); + } ++ ++#if defined(BCMPCIE) && defined(DHD_USE_STATIC_FLOWRING) ++void* ++osl_dma_alloc_consistent_static(osl_t *osh, uint size, uint16 align_bits, ++ uint *alloced, dmaaddr_t *pap, uint16 idx) ++{ ++ void *va = NULL; ++ uint16 align = (1 << align_bits); ++ uint16 flow_id = RINGID_TO_FLOWID(idx); ++ unsigned long flags; ++ ++ ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC))); ++ ++ if (!ISALIGNED(DMA_CONSISTENT_ALIGN, align)) ++ size += align; ++ ++ if ((flow_id < 0) || (flow_id >= STATIC_BUF_FLOWRING_NUM)) { ++ printk("%s: flow_id %d is wrong\n", __FUNCTION__, flow_id); ++ return osl_dma_alloc_consistent(osh, size, align_bits, ++ alloced, pap); ++ } ++ ++ if (!bcm_static_flowring) { ++ printk("%s: bcm_static_flowring is not initialized\n", ++ __FUNCTION__); ++ return osl_dma_alloc_consistent(osh, size, align_bits, ++ alloced, pap); ++ } ++ ++ if (size > STATIC_BUF_FLOWRING_SIZE) { ++ printk("%s: attempt to allocate huge packet, size=%d\n", ++ __FUNCTION__, size); ++ return osl_dma_alloc_consistent(osh, size, align_bits, ++ alloced, pap); ++ } ++ ++ *alloced = size; ++ ++ spin_lock_irqsave(&bcm_static_flowring->flowring_lock, flags); ++ if (bcm_static_flowring->buf_use[flow_id]) { ++ printk("%s: flowring %d is already alloced\n", ++ __FUNCTION__, flow_id); ++ spin_unlock_irqrestore(&bcm_static_flowring->flowring_lock, flags); ++ return NULL; ++ } ++ ++ va = bcm_static_flowring->buf_ptr[flow_id]; ++ if (va) { ++ *pap = (ulong)__virt_to_phys((ulong)va); ++ bcm_static_flowring->buf_use[flow_id] = 1; ++ } ++ spin_unlock_irqrestore(&bcm_static_flowring->flowring_lock, flags); ++ ++ return va; ++} ++ ++void ++osl_dma_free_consistent_static(osl_t *osh, void *va, uint size, ++ dmaaddr_t pa, uint16 idx) ++{ ++ uint16 flow_id = RINGID_TO_FLOWID(idx); ++ unsigned long flags; ++ ++ ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC))); ++ ++ if ((flow_id < 0) || (flow_id >= STATIC_BUF_FLOWRING_NUM)) { ++ printk("%s: flow_id %d is wrong\n", __FUNCTION__, flow_id); ++ return osl_dma_free_consistent(osh, va, size, pa); ++ } ++ ++ if (!bcm_static_flowring) { ++ printk("%s: bcm_static_flowring is not initialized\n", ++ __FUNCTION__); ++ return osl_dma_free_consistent(osh, va, size, pa); ++ } ++ ++ spin_lock_irqsave(&bcm_static_flowring->flowring_lock, flags); ++ if (bcm_static_flowring->buf_use[flow_id]) { ++ bcm_static_flowring->buf_use[flow_id] = 0; ++ } else { ++ printk("%s: flowring %d is already freed\n", ++ __FUNCTION__, flow_id); ++ } ++ spin_unlock_irqrestore(&bcm_static_flowring->flowring_lock, flags); ++} ++#endif /* BCMPCIE && DHD_USE_STATIC_FLOWRING */ + #endif /* CONFIG_DHD_USE_STATIC_BUF */ + + uint32 +@@ -1094,7 +1352,7 @@ + if (bcm_static_buf) + { + int i = 0; +- if ((size >= PAGE_SIZE)&&(size <= STATIC_BUF_SIZE)) ++ if ((size >= PAGE_SIZE) && (size <= STATIC_BUF_SIZE)) + { + down(&bcm_static_buf->static_sem); + +@@ -1227,6 +1485,7 @@ + size += align; + *alloced = size; + ++#ifndef BCM_SECURE_DMA + #if defined(BCM47XX_CA9) && defined(__ARM_ARCH_7A__) + va = kmalloc(size, GFP_ATOMIC | __GFP_ZERO); + if (va) +@@ -1234,23 +1493,38 @@ + #else + { + dma_addr_t pap_lin; +- va = pci_alloc_consistent(osh->pdev, size, &pap_lin); ++ struct pci_dev *hwdev = osh->pdev; ++#ifdef PCIE_TX_DEFERRAL ++ va = dma_alloc_coherent(&hwdev->dev, size, &pap_lin, GFP_KERNEL); ++#else ++ va = dma_alloc_coherent(&hwdev->dev, size, &pap_lin, GFP_ATOMIC); ++#endif + *pap = (dmaaddr_t)pap_lin; + } + #endif /* BCM47XX_CA9 && __ARM_ARCH_7A__ */ ++#else ++ va = osl_sec_dma_alloc_consistent(osh, size, align_bits, pap); ++#endif /* BCM_SECURE_DMA */ + return va; + } + + void + osl_dma_free_consistent(osl_t *osh, void *va, uint size, dmaaddr_t pa) + { ++#ifndef BCM_SECURE_DMA ++#if !defined(BCM47XX_CA9) || !defined(__ARM_ARCH_7A__) ++ struct pci_dev *hwdev = osh->pdev; ++#endif + ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC))); + + #if defined(BCM47XX_CA9) && defined(__ARM_ARCH_7A__) + kfree(va); + #else +- pci_free_consistent(osh->pdev, size, va, (dma_addr_t)pa); ++ dma_free_coherent(&hwdev->dev, size, va, (dma_addr_t)pa); + #endif /* BCM47XX_CA9 && __ARM_ARCH_7A__ */ ++#else ++ osl_sec_dma_free_consistent(osh, va, size, pa); ++#endif /* BCM_SECURE_DMA */ + } + + dmaaddr_t BCMFASTPATH +@@ -1338,22 +1612,33 @@ + inline void BCMFASTPATH + osl_cache_flush(void *va, uint size) + { ++#ifndef BCM_SECURE_DMA + #ifdef BCM47XX_ACP_WAR + if (virt_to_phys(va) < ACP_WIN_LIMIT) + return; + #endif + if (size > 0) + dma_sync_single_for_device(OSH_NULL, virt_to_dma(OSH_NULL, va), size, DMA_TX); ++#else ++ phys_addr_t orig_pa = (phys_addr_t)(va - g_contig_delta_va_pa); ++ if (size > 0) ++ dma_sync_single_for_device(OSH_NULL, orig_pa, size, DMA_TX); ++#endif /* defined BCM_SECURE_DMA */ + } + + inline void BCMFASTPATH + osl_cache_inv(void *va, uint size) + { ++#ifndef BCM_SECURE_DMA + #ifdef BCM47XX_ACP_WAR + if (virt_to_phys(va) < ACP_WIN_LIMIT) + return; + #endif + dma_sync_single_for_cpu(OSH_NULL, virt_to_dma(OSH_NULL, va), size, DMA_RX); ++#else ++ phys_addr_t orig_pa = (phys_addr_t)(va - g_contig_delta_va_pa); ++ dma_sync_single_for_cpu(OSH_NULL, orig_pa, size, DMA_RX); ++#endif /* defined BCM_SECURE_DMA */ + } + + inline void osl_prefetch(const void *ptr) +@@ -1374,7 +1659,7 @@ + return arch_is_coherent(); + #endif + } +-#endif ++#endif + + #if defined(BCMASSERT_LOG) + void +@@ -1399,7 +1684,7 @@ + + + } +-#endif ++#endif + + void + osl_delay(uint usec) +@@ -1429,11 +1714,10 @@ + /* Clone a packet. + * The pkttag contents are NOT cloned. + */ +-#ifdef BCMDBG_CTRACE + void * ++#ifdef BCMDBG_CTRACE + osl_pktdup(osl_t *osh, void *skb, int line, char *file) + #else +-void * + osl_pktdup(osl_t *osh, void *skb) + #endif /* BCMDBG_CTRACE */ + { +@@ -1678,3 +1962,528 @@ + { + return (osh->flags & mask); + } ++#ifdef BCM_SECURE_DMA ++ ++static void ++osl_sec_dma_setup_contig_mem(osl_t *osh, unsigned long memsize, int regn) ++{ ++ int ret; ++ ++#if defined(__ARM_ARCH_7A__) ++ if (regn == CONT_ARMREGION) { ++ ret = osl_sec_dma_alloc_contig_mem(osh, memsize, regn); ++ if (ret != BCME_OK) ++ printk("linux_osl.c: CMA memory access failed\n"); ++ } ++#endif ++ /* implement the MIPS Here */ ++} ++ ++static int ++osl_sec_dma_alloc_contig_mem(osl_t *osh, unsigned long memsize, int regn) ++{ ++ u64 addr; ++ ++ printk("linux_osl.c: The value of cma mem block size = %ld\n", memsize); ++ osh->cma = cma_dev_get_cma_dev(regn); ++ printk("The value of cma = %p\n", osh->cma); ++ if (!osh->cma) { ++ printk("linux_osl.c:contig_region index is invalid\n"); ++ return BCME_ERROR; ++ } ++ if (cma_dev_get_mem(osh->cma, &addr, (u32)memsize, SEC_DMA_ALIGN) < 0) { ++ printk("linux_osl.c: contiguous memory block allocation failure\n"); ++ return BCME_ERROR; ++ } ++ osh->contig_base_alloc = (phys_addr_t)addr; ++ osh->contig_base = (phys_addr_t)osh->contig_base_alloc; ++ printk("contig base alloc=%lx \n", (ulong)osh->contig_base_alloc); ++ ++ return BCME_OK; ++} ++ ++static void ++osl_sec_dma_free_contig_mem(osl_t *osh, u32 memsize, int regn) ++{ ++ int ret; ++ ++ ret = cma_dev_put_mem(osh->cma, (u64)osh->contig_base, memsize); ++ if (ret) ++ printf("%s contig base free failed\n", __FUNCTION__); ++} ++ ++static void * ++osl_sec_dma_ioremap(osl_t *osh, struct page *page, size_t size, bool iscache, bool isdecr) ++{ ++ ++ struct page **map; ++ int order, i; ++ void *addr = NULL; ++ ++ size = PAGE_ALIGN(size); ++ order = get_order(size); ++ ++ map = kmalloc(sizeof(struct page *) << order, GFP_ATOMIC); ++ ++ if (map == NULL) ++ return NULL; ++ ++ for (i = 0; i < (size >> PAGE_SHIFT); i++) ++ map[i] = page + i; ++ ++ if (iscache) { ++ addr = vmap(map, size >> PAGE_SHIFT, VM_MAP, __pgprot(PAGE_KERNEL)); ++ if (isdecr) { ++ osh->contig_delta_va_pa = (phys_addr_t)(addr - page_to_phys(page)); ++ g_contig_delta_va_pa = osh->contig_delta_va_pa; ++ } ++ } ++ else { ++ ++#if defined(__ARM_ARCH_7A__) ++ addr = vmap(map, size >> PAGE_SHIFT, VM_MAP, ++ pgprot_noncached(__pgprot(PAGE_KERNEL))); ++#endif ++ if (isdecr) { ++ osh->contig_delta_va_pa = (phys_addr_t)(addr - page_to_phys(page)); ++ g_contig_delta_va_pa = osh->contig_delta_va_pa; ++ } ++ } ++ ++ kfree(map); ++ return (void *)addr; ++} ++ ++static void ++osl_sec_dma_iounmap(osl_t *osh, void *contig_base_va, size_t size) ++{ ++ vunmap(contig_base_va); ++} ++ ++static void ++osl_sec_dma_deinit_elem_mem_block(osl_t *osh, size_t mbsize, int max, void *sec_list_base) ++{ ++ if (sec_list_base) ++ kfree(sec_list_base); ++} ++ ++static void ++osl_sec_dma_init_elem_mem_block(osl_t *osh, size_t mbsize, int max, sec_mem_elem_t **list) ++{ ++ int i; ++ sec_mem_elem_t *sec_mem_elem; ++ ++ if ((sec_mem_elem = kmalloc(sizeof(sec_mem_elem_t)*(max), GFP_ATOMIC)) != NULL) { ++ ++ *list = sec_mem_elem; ++ bzero(sec_mem_elem, sizeof(sec_mem_elem_t)*(max)); ++ for (i = 0; i < max-1; i++) { ++ sec_mem_elem->next = (sec_mem_elem + 1); ++ sec_mem_elem->size = mbsize; ++ sec_mem_elem->pa_cma = (u32)osh->contig_base_alloc; ++ sec_mem_elem->vac = osh->contig_base_alloc_va; ++ ++ osh->contig_base_alloc += mbsize; ++ osh->contig_base_alloc_va += mbsize; ++ ++ sec_mem_elem = sec_mem_elem + 1; ++ } ++ sec_mem_elem->next = NULL; ++ sec_mem_elem->size = mbsize; ++ sec_mem_elem->pa_cma = (u32)osh->contig_base_alloc; ++ sec_mem_elem->vac = osh->contig_base_alloc_va; ++ ++ osh->contig_base_alloc += mbsize; ++ osh->contig_base_alloc_va += mbsize; ++ ++ } ++ else ++ printf("%s sec mem elem kmalloc failed\n", __FUNCTION__); ++} ++ ++ ++static sec_mem_elem_t * BCMFASTPATH ++osl_sec_dma_alloc_mem_elem(osl_t *osh, void *va, uint size, int direction, ++ struct sec_cma_info *ptr_cma_info, uint offset) ++{ ++ sec_mem_elem_t *sec_mem_elem = NULL; ++ ++ if (size <= 512 && osh->sec_list_512) { ++ sec_mem_elem = osh->sec_list_512; ++ osh->sec_list_512 = sec_mem_elem->next; ++ } ++ else if (size <= 2048 && osh->sec_list_2048) { ++ sec_mem_elem = osh->sec_list_2048; ++ osh->sec_list_2048 = sec_mem_elem->next; ++ } ++ else if (osh->sec_list_4096) { ++ sec_mem_elem = osh->sec_list_4096; ++ osh->sec_list_4096 = sec_mem_elem->next; ++ } else { ++ printf("%s No matching Pool available size=%d \n", __FUNCTION__, size); ++ return NULL; ++ } ++ ++ if (sec_mem_elem != NULL) { ++ sec_mem_elem->next = NULL; ++ ++ if (ptr_cma_info->sec_alloc_list_tail) { ++ ptr_cma_info->sec_alloc_list_tail->next = sec_mem_elem; ++ } ++ ++ ptr_cma_info->sec_alloc_list_tail = sec_mem_elem; ++ if (ptr_cma_info->sec_alloc_list == NULL) ++ ptr_cma_info->sec_alloc_list = sec_mem_elem; ++ } ++ return sec_mem_elem; ++} ++ ++static void BCMFASTPATH ++osl_sec_dma_free_mem_elem(osl_t *osh, sec_mem_elem_t *sec_mem_elem) ++{ ++ sec_mem_elem->dma_handle = 0x0; ++ sec_mem_elem->va = NULL; ++ ++ if (sec_mem_elem->size == 512) { ++ sec_mem_elem->next = osh->sec_list_512; ++ osh->sec_list_512 = sec_mem_elem; ++ } ++ else if (sec_mem_elem->size == 2048) { ++ sec_mem_elem->next = osh->sec_list_2048; ++ osh->sec_list_2048 = sec_mem_elem; ++ } ++ else if (sec_mem_elem->size == 4096) { ++ sec_mem_elem->next = osh->sec_list_4096; ++ osh->sec_list_4096 = sec_mem_elem; ++ } ++ else ++ printf("%s free failed size=%d \n", __FUNCTION__, sec_mem_elem->size); ++} ++ ++ ++static sec_mem_elem_t * BCMFASTPATH ++osl_sec_dma_find_rem_elem(osl_t *osh, struct sec_cma_info *ptr_cma_info, dma_addr_t dma_handle) ++{ ++ sec_mem_elem_t *sec_mem_elem = ptr_cma_info->sec_alloc_list; ++ sec_mem_elem_t *sec_prv_elem = ptr_cma_info->sec_alloc_list; ++ ++ if (sec_mem_elem->dma_handle == dma_handle) { ++ ++ ptr_cma_info->sec_alloc_list = sec_mem_elem->next; ++ ++ if (sec_mem_elem == ptr_cma_info->sec_alloc_list_tail) { ++ ptr_cma_info->sec_alloc_list_tail = NULL; ++ ASSERT(ptr_cma_info->sec_alloc_list == NULL); ++ } ++ ++ return sec_mem_elem; ++ } ++ ++ while (sec_mem_elem != NULL) { ++ ++ if (sec_mem_elem->dma_handle == dma_handle) { ++ ++ sec_prv_elem->next = sec_mem_elem->next; ++ if (sec_mem_elem == ptr_cma_info->sec_alloc_list_tail) ++ ptr_cma_info->sec_alloc_list_tail = sec_prv_elem; ++ ++ return sec_mem_elem; ++ } ++ sec_prv_elem = sec_mem_elem; ++ sec_mem_elem = sec_mem_elem->next; ++ } ++ return NULL; ++} ++ ++static sec_mem_elem_t * ++osl_sec_dma_rem_first_elem(osl_t *osh, struct sec_cma_info *ptr_cma_info) ++{ ++ sec_mem_elem_t *sec_mem_elem = ptr_cma_info->sec_alloc_list; ++ ++ if (sec_mem_elem) { ++ ++ ptr_cma_info->sec_alloc_list = sec_mem_elem->next; ++ ++ if (ptr_cma_info->sec_alloc_list == NULL) ++ ptr_cma_info->sec_alloc_list_tail = NULL; ++ ++ return sec_mem_elem; ++ ++ } else ++ return NULL; ++} ++ ++static void * BCMFASTPATH ++osl_sec_dma_last_elem(osl_t *osh, struct sec_cma_info *ptr_cma_info) ++{ ++ return ptr_cma_info->sec_alloc_list_tail; ++} ++ ++dma_addr_t BCMFASTPATH ++osl_sec_dma_map_txmeta(osl_t *osh, void *va, uint size, int direction, void *p, ++ hnddma_seg_map_t *dmah, void *ptr_cma_info) ++{ ++ sec_mem_elem_t *sec_mem_elem; ++ struct page *pa_cma_page; ++ uint loffset; ++ void *vaorig = va + size; ++ dma_addr_t dma_handle = 0x0; ++ /* packet will be the one added with osl_sec_dma_map() just before this call */ ++ ++ sec_mem_elem = osl_sec_dma_last_elem(osh, ptr_cma_info); ++ ++ if (sec_mem_elem && sec_mem_elem->va == vaorig) { ++ ++ pa_cma_page = phys_to_page(sec_mem_elem->pa_cma); ++ loffset = sec_mem_elem->pa_cma -(sec_mem_elem->pa_cma & ~(PAGE_SIZE-1)); ++ ++ dma_handle = dma_map_page(osh->cma->dev, pa_cma_page, loffset, size, ++ (direction == DMA_TX ? DMA_TO_DEVICE:DMA_FROM_DEVICE)); ++ ++ } else { ++ printf("%s: error orig va not found va = 0x%p \n", ++ __FUNCTION__, vaorig); ++ } ++ return dma_handle; ++} ++ ++dma_addr_t BCMFASTPATH ++osl_sec_dma_map(osl_t *osh, void *va, uint size, int direction, void *p, ++ hnddma_seg_map_t *dmah, void *ptr_cma_info, uint offset) ++{ ++ ++ sec_mem_elem_t *sec_mem_elem; ++ struct page *pa_cma_page; ++ void *pa_cma_kmap_va = NULL; ++ int *fragva; ++ uint buflen = 0; ++ struct sk_buff *skb; ++ dma_addr_t dma_handle = 0x0; ++ uint loffset; ++ int i = 0; ++ ++ sec_mem_elem = osl_sec_dma_alloc_mem_elem(osh, va, size, direction, ptr_cma_info, offset); ++ ++ if (sec_mem_elem == NULL) { ++ printk("linux_osl.c: osl_sec_dma_map - cma allocation failed\n"); ++ return 0; ++ } ++ sec_mem_elem->va = va; ++ sec_mem_elem->direction = direction; ++ pa_cma_page = phys_to_page(sec_mem_elem->pa_cma); ++ ++ loffset = sec_mem_elem->pa_cma -(sec_mem_elem->pa_cma & ~(PAGE_SIZE-1)); ++ /* pa_cma_kmap_va = kmap_atomic(pa_cma_page); ++ * pa_cma_kmap_va += loffset; ++ */ ++ ++ pa_cma_kmap_va = sec_mem_elem->vac; ++ ++ if (direction == DMA_TX) { ++ ++ if (p == NULL) { ++ ++ memcpy(pa_cma_kmap_va+offset, va, size); ++ buflen = size; ++ } ++ else { ++ for (skb = (struct sk_buff *)p; skb != NULL; skb = PKTNEXT(osh, skb)) { ++ if (skb_is_nonlinear(skb)) { ++ ++ ++ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { ++ skb_frag_t *f = &skb_shinfo(skb)->frags[i]; ++ fragva = kmap_atomic(skb_frag_page(f)); ++ memcpy((pa_cma_kmap_va+offset+buflen), ++ (fragva + f->page_offset), skb_frag_size(f)); ++ kunmap_atomic(fragva); ++ buflen += skb_frag_size(f); ++ } ++ } ++ else { ++ memcpy((pa_cma_kmap_va+offset+buflen), skb->data, skb->len); ++ buflen += skb->len; ++ } ++ } ++ ++ } ++ if (dmah) { ++ dmah->nsegs = 1; ++ dmah->origsize = buflen; ++ } ++ } ++ ++ else if (direction == DMA_RX) ++ { ++ buflen = size; ++ if ((p != NULL) && (dmah != NULL)) { ++ dmah->nsegs = 1; ++ dmah->origsize = buflen; ++ } ++ } ++ if (direction == DMA_RX || direction == DMA_TX) { ++ ++ dma_handle = dma_map_page(osh->cma->dev, pa_cma_page, loffset+offset, buflen, ++ (direction == DMA_TX ? DMA_TO_DEVICE:DMA_FROM_DEVICE)); ++ ++ } ++ if (dmah) { ++ dmah->segs[0].addr = dma_handle; ++ dmah->segs[0].length = buflen; ++ } ++ sec_mem_elem->dma_handle = dma_handle; ++ /* kunmap_atomic(pa_cma_kmap_va-loffset); */ ++ return dma_handle; ++} ++ ++dma_addr_t BCMFASTPATH ++osl_sec_dma_dd_map(osl_t *osh, void *va, uint size, int direction, void *p, hnddma_seg_map_t *map) ++{ ++ ++ struct page *pa_cma_page; ++ phys_addr_t pa_cma; ++ dma_addr_t dma_handle = 0x0; ++ uint loffset; ++ ++ pa_cma = (phys_addr_t)(va - osh->contig_delta_va_pa); ++ pa_cma_page = phys_to_page(pa_cma); ++ loffset = pa_cma -(pa_cma & ~(PAGE_SIZE-1)); ++ ++ dma_handle = dma_map_page(osh->cma->dev, pa_cma_page, loffset, size, ++ (direction == DMA_TX ? DMA_TO_DEVICE:DMA_FROM_DEVICE)); ++ ++ return dma_handle; ++ ++} ++ ++void BCMFASTPATH ++osl_sec_dma_unmap(osl_t *osh, dma_addr_t dma_handle, uint size, int direction, ++void *p, hnddma_seg_map_t *map, void *ptr_cma_info, uint offset) ++{ ++ sec_mem_elem_t *sec_mem_elem; ++ struct page *pa_cma_page; ++ void *pa_cma_kmap_va = NULL; ++ uint buflen = 0; ++ dma_addr_t pa_cma; ++ void *va; ++ uint loffset = 0; ++ int read_count = 0; ++ BCM_REFERENCE(buflen); ++ BCM_REFERENCE(read_count); ++ ++ sec_mem_elem = osl_sec_dma_find_rem_elem(osh, ptr_cma_info, dma_handle); ++ if (sec_mem_elem == NULL) { ++ printf("%s sec_mem_elem is NULL and dma_handle =0x%lx and dir=%d\n", ++ __FUNCTION__, (ulong)dma_handle, direction); ++ return; ++ } ++ ++ va = sec_mem_elem->va; ++ va -= offset; ++ pa_cma = sec_mem_elem->pa_cma; ++ ++ pa_cma_page = phys_to_page(pa_cma); ++ loffset = sec_mem_elem->pa_cma -(sec_mem_elem->pa_cma & ~(PAGE_SIZE-1)); ++ ++ if (direction == DMA_RX) { ++ ++ if (p == NULL) { ++ ++ /* pa_cma_kmap_va = kmap_atomic(pa_cma_page); ++ * pa_cma_kmap_va += loffset; ++ */ ++ ++ pa_cma_kmap_va = sec_mem_elem->vac; ++ ++ dma_unmap_page(osh->cma->dev, pa_cma, size, DMA_FROM_DEVICE); ++ memcpy(va, pa_cma_kmap_va, size); ++ /* kunmap_atomic(pa_cma_kmap_va); */ ++ } ++ } else { ++ dma_unmap_page(osh->cma->dev, pa_cma, size+offset, DMA_TO_DEVICE); ++ } ++ ++ osl_sec_dma_free_mem_elem(osh, sec_mem_elem); ++} ++ ++void ++osl_sec_dma_unmap_all(osl_t *osh, void *ptr_cma_info) ++{ ++ ++ sec_mem_elem_t *sec_mem_elem; ++ ++ sec_mem_elem = osl_sec_dma_rem_first_elem(osh, ptr_cma_info); ++ ++ while (sec_mem_elem != NULL) { ++ ++ dma_unmap_page(osh->cma->dev, sec_mem_elem->pa_cma, sec_mem_elem->size, ++ sec_mem_elem->direction == DMA_TX ? DMA_TO_DEVICE : DMA_FROM_DEVICE); ++ osl_sec_dma_free_mem_elem(osh, sec_mem_elem); ++ ++ sec_mem_elem = osl_sec_dma_rem_first_elem(osh, ptr_cma_info); ++ } ++} ++ ++static void ++osl_sec_dma_init_consistent(osl_t *osh) ++{ ++ int i; ++ void *temp_va = osh->contig_base_alloc_coherent_va; ++ phys_addr_t temp_pa = osh->contig_base_alloc_coherent; ++ ++ for (i = 0; i < SEC_CMA_COHERENT_MAX; i++) { ++ osh->sec_cma_coherent[i].avail = TRUE; ++ osh->sec_cma_coherent[i].va = temp_va; ++ osh->sec_cma_coherent[i].pa = temp_pa; ++ temp_va += SEC_CMA_COHERENT_BLK; ++ temp_pa += SEC_CMA_COHERENT_BLK; ++ } ++} ++ ++static void * ++osl_sec_dma_alloc_consistent(osl_t *osh, uint size, uint16 align_bits, ulong *pap) ++{ ++ ++ void *temp_va = NULL; ++ ulong temp_pa = 0; ++ int i; ++ ++ if (size > SEC_CMA_COHERENT_BLK) { ++ printf("%s unsupported size\n", __FUNCTION__); ++ return NULL; ++ } ++ ++ for (i = 0; i < SEC_CMA_COHERENT_MAX; i++) { ++ if (osh->sec_cma_coherent[i].avail == TRUE) { ++ temp_va = osh->sec_cma_coherent[i].va; ++ temp_pa = osh->sec_cma_coherent[i].pa; ++ osh->sec_cma_coherent[i].avail = FALSE; ++ break; ++ } ++ } ++ ++ if (i == SEC_CMA_COHERENT_MAX) ++ printf("%s:No coherent mem: va = 0x%p pa = 0x%lx size = %d\n", __FUNCTION__, ++ temp_va, (ulong)temp_pa, size); ++ ++ *pap = (unsigned long)temp_pa; ++ return temp_va; ++} ++ ++static void ++osl_sec_dma_free_consistent(osl_t *osh, void *va, uint size, dmaaddr_t pa) ++{ ++ int i = 0; ++ ++ for (i = 0; i < SEC_CMA_COHERENT_MAX; i++) { ++ if (osh->sec_cma_coherent[i].va == va) { ++ osh->sec_cma_coherent[i].avail = TRUE; ++ break; ++ } ++ } ++ if (i == SEC_CMA_COHERENT_MAX) ++ printf("%s:Error: va = 0x%p pa = 0x%lx size = %d\n", __FUNCTION__, ++ va, (ulong)pa, size); ++} ++ ++#endif /* BCM_SECURE_DMA */ +diff -Nur a/drivers/net/wireless/bcmdhd/Makefile c/drivers/net/wireless/bcmdhd/Makefile +--- a/drivers/net/wireless/bcmdhd/Makefile 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/Makefile 2016-09-30 02:00:11.748108740 +0200 +@@ -1,24 +1,15 @@ + # bcmdhd + # 1. WL_IFACE_COMB_NUM_CHANNELS must be added if Android version is 4.4 with Kernel version 3.0~3.4, + # otherwise please remove it. +-DHDCFLAGS = -Wall -Wstrict-prototypes -Dlinux -DBCMDRIVER \ ++ ++DHDCFLAGS = -Wall -Wstrict-prototypes -Dlinux -DBCMDRIVER -DSDTEST \ + -DBCMDONGLEHOST -DUNRELEASEDCHIP -DBCMDMA32 -DBCMFILEIMAGE \ +- -DDHDTHREAD -DDHD_DEBUG -DSHOW_EVENTS -DBCMDBG \ +- -DWIFI_ACT_FRAME -DARP_OFFLOAD_SUPPORT \ +- -DKEEP_ALIVE -DPKT_FILTER_SUPPORT \ +- -DEMBEDDED_PLATFORM -DENABLE_INSMOD_NO_FW_LOAD -DPNO_SUPPORT \ +- -DDHD_USE_IDLECOUNT -DSET_RANDOM_MAC_SOFTAP -DVSDB \ +- -DWL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST \ +- -DESCAN_RESULT_PATCH -DSUPPORT_PM2_ONLY -DWLTDLS \ ++ -DDHDTHREAD -DDHD_DEBUG -DSHOW_EVENTS -DBCMDBG -DGET_OTP_MAC_ENABLE \ ++ -DWIFI_ACT_FRAME -DARP_OFFLOAD_SUPPORT -DSUPPORT_PM2_ONLY \ ++ -DKEEP_ALIVE -DPKT_FILTER_SUPPORT -DPNO_SUPPORT -DDHDTCPACK_SUPPRESS \ + -DDHD_DONOT_FORWARD_BCMEVENT_AS_NETWORK_PKT -DRXFRAME_THREAD \ +- -DMIRACAST_AMPDU_SIZE=8 -DGET_CUSTOM_MAC_ENABLE \ +- -DSDTEST -DBDC -DDHD_BCMEVENTS -DPROP_TXSTATUS -DPROP_TXSTATUS_VSDB \ +- -DWL_SUPPORT_BACKPORTED_KPATCHES -DDHDTCPACK_SUPPRESS \ +- -Idrivers/net/wireless/bcmdhd -Idrivers/net/wireless/bcmdhd/include +- +-DHDCFLAGS += \ +- -DMMC_SDIO_ABORT -DBCMSDIO -DBCMLXSDMMC -DSDIO_CRC_ERROR_FIX \ +- -DCUSTOM_SDIO_F2_BLKSIZE=128 -DUSE_SDIOFIFO_IOVAR ++ -DSWTXGLOM \ ++ -I$(src) -I$(src)/include + + DHDOFILES = aiutils.o siutils.o sbutils.o bcmutils.o bcmwifi_channels.o \ + dhd_linux.o dhd_linux_platdev.o dhd_linux_sched.o dhd_pno.o \ +@@ -26,23 +17,15 @@ + bcmevent.o hndpmu.o linux_osl.o wldev_common.o wl_android.o \ + hnd_pktq.o hnd_pktpool.o dhd_config.o + ++ifneq ($(CONFIG_BCMDHD_SDIO),) ++DHDCFLAGS += \ ++ -DBCMSDIO -DMMC_SDIO_ABORT -DBCMLXSDMMC -DUSE_SDIOFIFO_IOVAR \ ++ -DBDC -DPROP_TXSTATUS -DDHD_USE_IDLECOUNT -DBCMSDIOH_TXGLOM \ ++ -DCUSTOM_SDIO_F2_BLKSIZE=128 ++ + DHDOFILES += bcmsdh.o bcmsdh_linux.o bcmsdh_sdmmc.o bcmsdh_sdmmc_linux.o \ + dhd_sdio.o dhd_cdc.o dhd_wlfc.o + +-obj-$(CONFIG_BCMDHD) += bcmdhd.o +-bcmdhd-objs += $(DHDOFILES) +- +-#ifeq ($(CONFIG_MACH_ODROID_4210),y) +-DHDOFILES += dhd_gpio.o +-DHDCFLAGS += -DCUSTOMER_HW -DDHD_OF_SUPPORT +-#DHDCFLAGS += -DBCMWAPI_WPI -DBCMWAPI_WAI +-#endif +-ifeq ($(CONFIG_ARCH_SUNXI),y) +-DHDOFILES += dhd_gpio.o +-DHDCFLAGS += -Iarch/arm/mach-sunxi/include +-DHDCFLAGS += -DCUSTOMER_HW -DDHD_OF_SUPPORT +-endif +- + ifeq ($(CONFIG_BCMDHD_OOB),y) + DHDCFLAGS += -DOOB_INTR_ONLY -DHW_OOB -DCUSTOMER_OOB + ifeq ($(CONFIG_BCMDHD_DISABLE_WOWLAN),y) +@@ -51,33 +34,62 @@ + else + DHDCFLAGS += -DSDIO_ISR_THREAD + endif ++endif ++ ++ifneq ($(CONFIG_BCMDHD_PCIE),) ++DHDCFLAGS += \ ++ -DPCIE_FULL_DONGLE -DBCMPCIE -DSHOW_LOGTRACE -DDPCIE_TX_DEFERRAL \ ++ -DCUSTOM_DPC_PRIO_SETTING=-1 ++ ++DHDOFILES += dhd_pcie.o dhd_pcie_linux.o pcie_core.o dhd_flowring.o \ ++ dhd_msgbuf.o ++endif ++ ++obj-$(CONFIG_BCMDHD) += dhd.o ++dhd-objs += $(DHDOFILES) ++ ++#ifeq ($(CONFIG_MACH_ODROID_4210),y) ++DHDOFILES += dhd_gpio.o ++DHDCFLAGS += -DCUSTOMER_HW -DDHD_OF_SUPPORT ++DHDCFLAGS += -Iarch/arm/mach-sunxi/include ++#DHDCFLAGS += -DBCMWAPI_WPI -DBCMWAPI_WAI ++#endif + + ifeq ($(CONFIG_BCMDHD_AG),y) + DHDCFLAGS += -DBAND_AG + endif + + ifeq ($(CONFIG_DHD_USE_STATIC_BUF),y) +-DHDCFLAGS += -DSTATIC_WL_PRIV_STRUCT ++# add dhd_static_buf to kernel image build ++#obj-y += dhd_static_buf.o ++DHDCFLAGS += -DCONFIG_DHD_USE_STATIC_BUF ++DHDCFLAGS += -DSTATIC_WL_PRIV_STRUCT -DENHANCED_STATIC_BUF + endif + + ifneq ($(CONFIG_WIRELESS_EXT),) +-bcmdhd-objs += wl_iw.o ++DHDOFILES += wl_iw.o + DHDCFLAGS += -DSOFTAP -DWL_WIRELESS_EXT -DUSE_IW + endif + ifneq ($(CONFIG_CFG80211),) +-bcmdhd-objs += wl_cfg80211.o wl_cfgp2p.o wl_linux_mon.o dhd_cfg80211.o wl_cfg_btcoex.o +-DHDCFLAGS += -DWL_CFG80211 -DWLP2P -DWL_CFG80211_STA_EVENT -DWL_ENABLE_P2P_IF ++DHDOFILES += wl_cfg80211.o wl_cfgp2p.o wl_linux_mon.o wl_cfg_btcoex.o ++DHDOFILES += dhd_cfg80211.o dhd_cfg_vendor.o ++DHDCFLAGS += -DWL_CFG80211 -DWL_CFG80211_STA_EVENT ++#DHDCFLAGS += -DWLP2P -DWL_ENABLE_P2P_IF + DHDCFLAGS += -DWL_IFACE_COMB_NUM_CHANNELS + DHDCFLAGS += -DCUSTOM_ROAM_TRIGGER_SETTING=-65 + DHDCFLAGS += -DCUSTOM_ROAM_DELTA_SETTING=15 + DHDCFLAGS += -DCUSTOM_KEEP_ALIVE_SETTING=28000 + DHDCFLAGS += -DCUSTOM_PNO_EVENT_LOCK_xTIME=7 + DHDCFLAGS += -DWL_SUPPORT_AUTO_CHANNEL +-endif +-ifneq ($(CONFIG_DHD_USE_SCHED_SCAN),) +-DHDCFLAGS += -DWL_SCHED_SCAN ++DHDCFLAGS += -DWL_SUPPORT_BACKPORTED_KPATCHES ++DHDCFLAGS += -DESCAN_RESULT_PATCH ++DHDCFLAGS += -DVSDB -DWL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST ++DHDCFLAGS += -DWLTDLS -DMIRACAST_AMPDU_SIZE=8 -DPROP_TXSTATUS_VSDB + endif + EXTRA_CFLAGS = $(DHDCFLAGS) + ifeq ($(CONFIG_BCMDHD),m) +-EXTRA_LDFLAGS += --strip-debug ++#DHDCFLAGS += -DMULTIPLE_SUPPLICANT ++#EXTRA_LDFLAGS += --strip-debug ++else ++DHDCFLAGS += -DBUILD_IN_KERNEL + endif +diff -Nur a/drivers/net/wireless/bcmdhd/sbutils.c c/drivers/net/wireless/bcmdhd/sbutils.c +--- a/drivers/net/wireless/bcmdhd/sbutils.c 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/sbutils.c 2016-05-13 09:48:20.000000000 +0200 +@@ -1084,4 +1084,4 @@ + sb_setcoreidx(sih, origidx); + INTR_RESTORE(sii, intr_val); + } +-#endif ++#endif +diff -Nur a/drivers/net/wireless/bcmdhd/siutils.c c/drivers/net/wireless/bcmdhd/siutils.c +--- a/drivers/net/wireless/bcmdhd/siutils.c 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/siutils.c 2016-05-13 09:48:20.000000000 +0200 +@@ -4,7 +4,7 @@ + * + * $Copyright Open Broadcom Corporation$ + * +- * $Id: siutils.c 481602 2014-05-29 22:43:34Z $ ++ * $Id: siutils.c 497460 2014-08-19 15:14:13Z $ + */ + + #include +@@ -70,6 +70,7 @@ + #endif /* BCMLTECOEX */ + + ++ + /* global variable to indicate reservation/release of gpio's */ + static uint32 si_gpioreservation = 0; + +@@ -291,7 +292,8 @@ + /* for SDIO but downloaded on PCIE dev */ + if (cid == PCIE2_CORE_ID) { + if ((CHIPID(sii->pub.chip) == BCM43602_CHIP_ID) || +- ((CHIPID(sii->pub.chip) == BCM4345_CHIP_ID) && ++ ((CHIPID(sii->pub.chip) == BCM4345_CHIP_ID || ++ CHIPID(sii->pub.chip) == BCM43454_CHIP_ID) && + CST4345_CHIPMODE_PCIE(sii->pub.chipst))) { + pcieidx = i; + pcierev = crev; +@@ -426,7 +428,7 @@ + char *pvars = NULL; + uint origidx; + #if !defined(_CFEZ_) || defined(CFG_WL) +-#endif ++#endif + + ASSERT(GOODREGS(regs)); + +@@ -486,6 +488,18 @@ + SI_ERROR(("%s: chipcommon register space is null \n", __FUNCTION__)); + return NULL; + } ++#ifdef COSTOMER_HW4 ++#ifdef CONFIG_MACH_UNIVERSAL5433 ++ /* old revision check */ ++ if (!check_rev()) { ++ /* abnormal link status */ ++ if (!check_pcie_link_status()) { ++ printk("%s : PCIE LINK is abnormal status\n", __FUNCTION__); ++ return NULL; ++ } ++ } ++#endif /* CONFIG_MACH_UNIVERSAL5433 */ ++#endif + w = R_REG(osh, &cc->chipid); + if ((w & 0xfffff) == 148277) w -= 65532; + sih->socitype = (w & CID_TYPE_MASK) >> CID_TYPE_SHIFT; +@@ -494,7 +508,7 @@ + sih->chiprev = (w & CID_REV_MASK) >> CID_REV_SHIFT; + sih->chippkg = (w & CID_PKG_MASK) >> CID_PKG_SHIFT; + +-#if defined(HW_OOB) ++#if defined(HW_OOB) || defined(FORCE_WOWLAN) + dhd_conf_set_hw_oob_intr(sdh, sih->chip); + #endif + +@@ -571,7 +585,7 @@ + if (bustype == PCI_BUS) { + + } +-#endif ++#endif + #ifdef BCM_SDRBL + /* 4360 rom bootloader in PCIE case, if the SDR is enabled, But preotection is + * not turned on, then we want to hold arm in reset. +@@ -1400,6 +1414,7 @@ + break; + + case BCM4345_CHIP_ID: ++ case BCM43454_CHIP_ID: + if (CST4345_CHIPMODE_USB20D(sih->chipst) || CST4345_CHIPMODE_HSIC(sih->chipst)) + hosti = CHIP_HOSTIF_USBMODE; + else if (CST4345_CHIPMODE_SDIOD(sih->chipst)) +@@ -1461,7 +1476,7 @@ + si_core_disable(sih, 1); + si_setcore(sih, CC_CORE_ID, 0); + } +-#endif ++#endif + + nb = (sih->ccrev < 26) ? 16 : ((sih->ccrev >= 37) ? 32 : 24); + /* The mips compiler uses the sllv instruction, +@@ -2643,7 +2658,7 @@ + } + si_setcoreidx(sih, origidx); + } +-#endif ++#endif + + uint + si_pll_reset(si_t *sih) +@@ -2804,6 +2819,7 @@ + !(sih->chipst & CST4324_SFLASH_MASK)); + case BCM4335_CHIP_ID: + case BCM4345_CHIP_ID: ++ case BCM43454_CHIP_ID: + return ((sih->chipst & CST4335_SPROM_MASK) && + !(sih->chipst & CST4335_SFLASH_MASK)); + case BCM4349_CHIP_GRPID: +diff -Nur a/drivers/net/wireless/bcmdhd/siutils_priv.h c/drivers/net/wireless/bcmdhd/siutils_priv.h +--- a/drivers/net/wireless/bcmdhd/siutils_priv.h 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/siutils_priv.h 2016-05-13 09:48:20.000000000 +0200 +@@ -196,7 +196,7 @@ + + #if defined(BCMDBG_PHYDUMP) + extern void sb_dumpregs(si_t *sih, struct bcmstrbuf *b); +-#endif ++#endif + + /* Wake-on-wireless-LAN (WOWL) */ + extern bool sb_pci_pmecap(si_t *sih); +@@ -239,7 +239,7 @@ + + #if defined(BCMDBG_PHYDUMP) + extern void ai_dumpregs(si_t *sih, struct bcmstrbuf *b); +-#endif ++#endif + + + #define ub_scan(a, b, c) do {} while (0) +diff -Nur a/drivers/net/wireless/bcmdhd/wl_android.c c/drivers/net/wireless/bcmdhd/wl_android.c +--- a/drivers/net/wireless/bcmdhd/wl_android.c 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/wl_android.c 2016-05-13 09:48:20.000000000 +0200 +@@ -3,7 +3,7 @@ + * + * $Copyright Open Broadcom Corporation$ + * +- * $Id: wl_android.c 490852 2014-07-12 15:20:53Z $ ++ * $Id: wl_android.c 505064 2014-09-26 09:40:28Z $ + */ + + #include +@@ -85,10 +85,16 @@ + #define CMD_SCAN_PASSIVE "SCAN-PASSIVE" + #define CMD_RSSI "RSSI" + #define CMD_LINKSPEED "LINKSPEED" ++#ifdef PKT_FILTER_SUPPORT + #define CMD_RXFILTER_START "RXFILTER-START" + #define CMD_RXFILTER_STOP "RXFILTER-STOP" + #define CMD_RXFILTER_ADD "RXFILTER-ADD" + #define CMD_RXFILTER_REMOVE "RXFILTER-REMOVE" ++#if defined(CUSTOM_PLATFORM_NV_TEGRA) ++#define CMD_PKT_FILTER_MODE "PKT_FILTER_MODE" ++#define CMD_PKT_FILTER_PORTS "PKT_FILTER_PORTS" ++#endif /* defined(CUSTOM_PLATFORM_NV_TEGRA) */ ++#endif /* PKT_FILTER_SUPPORT */ + #define CMD_BTCOEXSCAN_START "BTCOEXSCAN-START" + #define CMD_BTCOEXSCAN_STOP "BTCOEXSCAN-STOP" + #define CMD_BTCOEXMODE "BTCOEXMODE" +@@ -111,7 +117,7 @@ + #define CMD_MIRACAST "MIRACAST" + #define CMD_NAN "NAN_" + #define CMD_GET_CHANNEL "GET_CHANNEL" +-#define CMD_SET_ROAM "SET_ROAM_TRIGGER" ++#define CMD_SET_ROAM "SET_ROAM_TRIGGER" + #define CMD_GET_ROAM "GET_ROAM_TRIGGER" + #define CMD_GET_KEEP_ALIVE "GET_KEEP_ALIVE" + #define CMD_GET_PM "GET_PM" +@@ -122,6 +128,12 @@ + #define CMD_GET_BEST_CHANNELS "GET_BEST_CHANNELS" + #endif /* WL_SUPPORT_AUTO_CHANNEL */ + ++#if defined(CUSTOM_PLATFORM_NV_TEGRA) ++#define CMD_SETMIRACAST "SETMIRACAST" ++#define CMD_ASSOCRESPIE "ASSOCRESPIE" ++#define CMD_RXRATESTATS "RXRATESTATS" ++#endif /* defined(CUSTOM_PLATFORM_NV_TEGRA) */ ++ + #define CMD_KEEP_ALIVE "KEEPALIVE" + + /* CCX Private Commands */ +@@ -158,6 +170,19 @@ + #endif /* WLAIBSS */ + + #define CMD_ROAM_OFFLOAD "SETROAMOFFLOAD" ++#define CMD_ROAM_OFFLOAD_APLIST "SETROAMOFFLAPLIST" ++#define CMD_GET_LINK_STATUS "GETLINKSTATUS" ++ ++#ifdef P2PRESP_WFDIE_SRC ++#define CMD_P2P_SET_WFDIE_RESP "P2P_SET_WFDIE_RESP" ++#define CMD_P2P_GET_WFDIE_RESP "P2P_GET_WFDIE_RESP" ++#endif /* P2PRESP_WFDIE_SRC */ ++ ++/* related with CMD_GET_LINK_STATUS */ ++#define WL_ANDROID_LINK_VHT 0x01 ++#define WL_ANDROID_LINK_MIMO 0x02 ++#define WL_ANDROID_LINK_AP_VHT_SUPPORT 0x04 ++#define WL_ANDROID_LINK_AP_MIMO_SUPPORT 0x08 + + /* miracast related definition */ + #define MIRACAST_MODE_OFF 0 +@@ -176,6 +201,26 @@ + #define MIRACAST_MCHAN_BW 25 + #endif + ++#ifdef CONNECTION_STATISTICS ++#define CMD_GET_CONNECTION_STATS "GET_CONNECTION_STATS" ++ ++struct connection_stats { ++ u32 txframe; ++ u32 txbyte; ++ u32 txerror; ++ u32 rxframe; ++ u32 rxbyte; ++ u32 txfail; ++ u32 txretry; ++ u32 txretrie; ++ u32 txrts; ++ u32 txnocts; ++ u32 txexptime; ++ u32 txrate; ++ u8 chan_idle; ++}; ++#endif /* CONNECTION_STATISTICS */ ++ + static LIST_HEAD(miracast_resume_list); + #ifdef WL_CFG80211 + static u8 miracast_cur_mode; +@@ -862,16 +907,26 @@ + int wl_android_wifi_on(struct net_device *dev) + { + int ret = 0; ++#ifdef CONFIG_MACH_UNIVERSAL5433 ++ int retry; ++ /* Do not retry old revision Helsinki Prime */ ++ if (!check_rev()) { ++ retry = 1; ++ } else { ++ retry = POWERUP_MAX_RETRY; ++ } ++#else + int retry = POWERUP_MAX_RETRY; ++#endif /* CONFIG_MACH_UNIVERSAL5433 */ + + if (!dev) { + ANDROID_ERROR(("%s: dev is null\n", __FUNCTION__)); + return -EINVAL; + } + +- printk("%s in 1\n", __FUNCTION__); ++ printf("%s in 1\n", __FUNCTION__); + dhd_net_if_lock(dev); +- printk("%s in 2: g_wifi_on=%d\n", __FUNCTION__, g_wifi_on); ++ printf("%s in 2: g_wifi_on=%d\n", __FUNCTION__, g_wifi_on); + if (!g_wifi_on) { + do { + dhd_net_wifi_platform_set_power(dev, TRUE, WIFI_TURNON_DELAY); +@@ -913,20 +968,19 @@ + } + + exit: +- printk("%s: Success\n", __FUNCTION__); ++ printf("%s: Success\n", __FUNCTION__); + dhd_net_if_unlock(dev); + return ret; + +-err: + #ifdef BCMSDIO ++err: + dhd_net_bus_devreset(dev, TRUE); + dhd_net_bus_suspend(dev); +-#endif + dhd_net_wifi_platform_set_power(dev, FALSE, WIFI_TURNOFF_DELAY); +- printk("%s: Failed\n", __FUNCTION__); ++ printf("%s: Failed\n", __FUNCTION__); + dhd_net_if_unlock(dev); +- + return ret; ++#endif + } + + int wl_android_wifi_off(struct net_device *dev) +@@ -938,9 +992,9 @@ + return -EINVAL; + } + +- printk("%s in 1\n", __FUNCTION__); ++ printf("%s in 1\n", __FUNCTION__); + dhd_net_if_lock(dev); +- printk("%s in 2: g_wifi_on=%d\n", __FUNCTION__, g_wifi_on); ++ printf("%s in 2: g_wifi_on=%d\n", __FUNCTION__, g_wifi_on); + if (g_wifi_on) { + #if defined(BCMSDIO) || defined(BCMPCIE) + ret = dhd_net_bus_devreset(dev, TRUE); +@@ -951,7 +1005,7 @@ + dhd_net_wifi_platform_set_power(dev, FALSE, WIFI_TURNOFF_DELAY); + g_wifi_on = FALSE; + } +- printk("%s out\n", __FUNCTION__); ++ printf("%s out\n", __FUNCTION__); + dhd_net_if_unlock(dev); + + return ret; +@@ -964,6 +1018,145 @@ + return dhd_net_set_fw_path(net, command + strlen(CMD_SETFWPATH) + 1); + } + ++#ifdef CONNECTION_STATISTICS ++static int ++wl_chanim_stats(struct net_device *dev, u8 *chan_idle) ++{ ++ int err; ++ wl_chanim_stats_t *list; ++ /* Parameter _and_ returned buffer of chanim_stats. */ ++ wl_chanim_stats_t param; ++ u8 result[WLC_IOCTL_SMLEN]; ++ chanim_stats_t *stats; ++ ++ memset(¶m, 0, sizeof(param)); ++ memset(result, 0, sizeof(result)); ++ ++ param.buflen = htod32(sizeof(wl_chanim_stats_t)); ++ param.count = htod32(WL_CHANIM_COUNT_ONE); ++ ++ if ((err = wldev_iovar_getbuf(dev, "chanim_stats", (char*)¶m, sizeof(wl_chanim_stats_t), ++ (char*)result, sizeof(result), 0)) < 0) { ++ ANDROID_ERROR(("Failed to get chanim results %d \n", err)); ++ return err; ++ } ++ ++ list = (wl_chanim_stats_t*)result; ++ ++ list->buflen = dtoh32(list->buflen); ++ list->version = dtoh32(list->version); ++ list->count = dtoh32(list->count); ++ ++ if (list->buflen == 0) { ++ list->version = 0; ++ list->count = 0; ++ } else if (list->version != WL_CHANIM_STATS_VERSION) { ++ ANDROID_ERROR(("Sorry, firmware has wl_chanim_stats version %d " ++ "but driver supports only version %d.\n", ++ list->version, WL_CHANIM_STATS_VERSION)); ++ list->buflen = 0; ++ list->count = 0; ++ } ++ ++ stats = list->stats; ++ stats->glitchcnt = dtoh32(stats->glitchcnt); ++ stats->badplcp = dtoh32(stats->badplcp); ++ stats->chanspec = dtoh16(stats->chanspec); ++ stats->timestamp = dtoh32(stats->timestamp); ++ stats->chan_idle = dtoh32(stats->chan_idle); ++ ++ ANDROID_INFO(("chanspec: 0x%4x glitch: %d badplcp: %d idle: %d timestamp: %d\n", ++ stats->chanspec, stats->glitchcnt, stats->badplcp, stats->chan_idle, ++ stats->timestamp)); ++ ++ *chan_idle = stats->chan_idle; ++ ++ return (err); ++} ++ ++static int ++wl_android_get_connection_stats(struct net_device *dev, char *command, int total_len) ++{ ++ wl_cnt_t* cnt = NULL; ++ int link_speed = 0; ++ struct connection_stats *output; ++ unsigned int bufsize = 0; ++ int bytes_written = 0; ++ int ret = 0; ++ ++ ANDROID_INFO(("%s: enter Get Connection Stats\n", __FUNCTION__)); ++ ++ if (total_len <= 0) { ++ ANDROID_ERROR(("%s: invalid buffer size %d\n", __FUNCTION__, total_len)); ++ goto error; ++ } ++ ++ bufsize = total_len; ++ if (bufsize < sizeof(struct connection_stats)) { ++ ANDROID_ERROR(("%s: not enough buffer size, provided=%u, requires=%u\n", ++ __FUNCTION__, bufsize, ++ sizeof(struct connection_stats))); ++ goto error; ++ } ++ ++ if ((cnt = kmalloc(sizeof(*cnt), GFP_KERNEL)) == NULL) { ++ ANDROID_ERROR(("kmalloc failed\n")); ++ return -1; ++ } ++ memset(cnt, 0, sizeof(*cnt)); ++ ++ ret = wldev_iovar_getbuf(dev, "counters", NULL, 0, (char *)cnt, sizeof(wl_cnt_t), NULL); ++ if (ret) { ++ ANDROID_ERROR(("%s: wldev_iovar_getbuf() failed, ret=%d\n", ++ __FUNCTION__, ret)); ++ goto error; ++ } ++ ++ if (dtoh16(cnt->version) > WL_CNT_T_VERSION) { ++ ANDROID_ERROR(("%s: incorrect version of wl_cnt_t, expected=%u got=%u\n", ++ __FUNCTION__, WL_CNT_T_VERSION, cnt->version)); ++ goto error; ++ } ++ ++ /* link_speed is in kbps */ ++ ret = wldev_get_link_speed(dev, &link_speed); ++ if (ret || link_speed < 0) { ++ ANDROID_ERROR(("%s: wldev_get_link_speed() failed, ret=%d, speed=%d\n", ++ __FUNCTION__, ret, link_speed)); ++ goto error; ++ } ++ ++ output = (struct connection_stats *)command; ++ output->txframe = dtoh32(cnt->txframe); ++ output->txbyte = dtoh32(cnt->txbyte); ++ output->txerror = dtoh32(cnt->txerror); ++ output->rxframe = dtoh32(cnt->rxframe); ++ output->rxbyte = dtoh32(cnt->rxbyte); ++ output->txfail = dtoh32(cnt->txfail); ++ output->txretry = dtoh32(cnt->txretry); ++ output->txretrie = dtoh32(cnt->txretrie); ++ output->txrts = dtoh32(cnt->txrts); ++ output->txnocts = dtoh32(cnt->txnocts); ++ output->txexptime = dtoh32(cnt->txexptime); ++ output->txrate = link_speed; ++ ++ /* Channel idle ratio. */ ++ if (wl_chanim_stats(dev, &(output->chan_idle)) < 0) { ++ output->chan_idle = 0; ++ }; ++ ++ kfree(cnt); ++ ++ bytes_written = sizeof(struct connection_stats); ++ return bytes_written; ++ ++error: ++ if (cnt) { ++ kfree(cnt); ++ } ++ return -1; ++} ++#endif /* CONNECTION_STATISTICS */ + + static int + wl_android_set_pmk(struct net_device *dev, char *command, int total_len) +@@ -1869,6 +2062,228 @@ + return res; + } + ++ ++static const char * ++get_string_by_separator(char *result, int result_len, const char *src, char separator) ++{ ++ char *end = result + result_len - 1; ++ while ((result != end) && (*src != separator) && (*src)) { ++ *result++ = *src++; ++ } ++ *result = 0; ++ if (*src == separator) ++ ++src; ++ return src; ++} ++ ++int ++wl_android_set_roam_offload_bssid_list(struct net_device *dev, const char *cmd) ++{ ++ char sbuf[32]; ++ int i, cnt, size, err, ioctl_buf_len; ++ roamoffl_bssid_list_t *bssid_list; ++ const char *str = cmd; ++ char *ioctl_buf; ++ ++ str = get_string_by_separator(sbuf, 32, str, ','); ++ cnt = bcm_atoi(sbuf); ++ cnt = MIN(cnt, MAX_ROAMOFFL_BSSID_NUM); ++ size = sizeof(int) + sizeof(struct ether_addr) * cnt; ++ ANDROID_ERROR(("ROAM OFFLOAD BSSID LIST %d BSSIDs, size %d\n", cnt, size)); ++ bssid_list = kmalloc(size, GFP_KERNEL); ++ if (bssid_list == NULL) { ++ ANDROID_ERROR(("%s: memory alloc for bssid list(%d) failed\n", ++ __FUNCTION__, size)); ++ return -ENOMEM; ++ } ++ ioctl_buf_len = size + 64; ++ ioctl_buf = kmalloc(ioctl_buf_len, GFP_KERNEL); ++ if (ioctl_buf == NULL) { ++ ANDROID_ERROR(("%s: memory alloc for ioctl_buf(%d) failed\n", ++ __FUNCTION__, ioctl_buf_len)); ++ kfree(bssid_list); ++ return -ENOMEM; ++ } ++ ++ for (i = 0; i < cnt; i++) { ++ str = get_string_by_separator(sbuf, 32, str, ','); ++ if (bcm_ether_atoe(sbuf, &bssid_list->bssid[i]) == 0) { ++ ANDROID_ERROR(("%s: Invalid station MAC Address!!!\n", __FUNCTION__)); ++ kfree(bssid_list); ++ kfree(ioctl_buf); ++ return -1; ++ } ++ } ++ ++ bssid_list->cnt = cnt; ++ err = wldev_iovar_setbuf(dev, "roamoffl_bssid_list", ++ bssid_list, size, ioctl_buf, ioctl_buf_len, NULL); ++ kfree(bssid_list); ++ kfree(ioctl_buf); ++ ++ return err; ++} ++ ++#ifdef P2PRESP_WFDIE_SRC ++static int wl_android_get_wfdie_resp(struct net_device *dev, char *command, int total_len) ++{ ++ int error = 0; ++ int bytes_written = 0; ++ int only_resp_wfdsrc = 0; ++ ++ error = wldev_iovar_getint(dev, "p2p_only_resp_wfdsrc", &only_resp_wfdsrc); ++ if (error) { ++ ANDROID_ERROR(("%s: Failed to get the mode for only_resp_wfdsrc, error = %d\n", ++ __FUNCTION__, error)); ++ return -1; ++ } ++ ++ bytes_written = snprintf(command, total_len, "%s %d", ++ CMD_P2P_GET_WFDIE_RESP, only_resp_wfdsrc); ++ ++ return bytes_written; ++} ++ ++static int wl_android_set_wfdie_resp(struct net_device *dev, int only_resp_wfdsrc) ++{ ++ int error = 0; ++ ++ error = wldev_iovar_setint(dev, "p2p_only_resp_wfdsrc", only_resp_wfdsrc); ++ if (error) { ++ ANDROID_ERROR(("%s: Failed to set only_resp_wfdsrc %d, error = %d\n", ++ __FUNCTION__, only_resp_wfdsrc, error)); ++ return -1; ++ } ++ ++ return 0; ++} ++#endif /* P2PRESP_WFDIE_SRC */ ++ ++static int wl_android_get_link_status(struct net_device *dev, char *command, ++ int total_len) ++{ ++ int bytes_written, error, result = 0, single_stream, stf = -1, i, nss = 0, mcs_map; ++ uint32 rspec; ++ uint encode, rate, txexp; ++ struct wl_bss_info *bi; ++ int datalen = sizeof(uint32) + sizeof(wl_bss_info_t); ++ char buf[datalen]; ++ ++ /* get BSS information */ ++ *(u32 *) buf = htod32(datalen); ++ error = wldev_ioctl(dev, WLC_GET_BSS_INFO, (void *)buf, datalen, false); ++ if (unlikely(error)) { ++ ANDROID_ERROR(("Could not get bss info %d\n", error)); ++ return -1; ++ } ++ ++ bi = (struct wl_bss_info *) (buf + sizeof(uint32)); ++ ++ for (i = 0; i < ETHER_ADDR_LEN; i++) { ++ if (bi->BSSID.octet[i] > 0) { ++ break; ++ } ++ } ++ ++ if (i == ETHER_ADDR_LEN) { ++ ANDROID_TRACE(("No BSSID\n")); ++ return -1; ++ } ++ ++ /* check VHT capability at beacon */ ++ if (bi->vht_cap) { ++ if (CHSPEC_IS5G(bi->chanspec)) { ++ result |= WL_ANDROID_LINK_AP_VHT_SUPPORT; ++ } ++ } ++ ++ /* get a rspec (radio spectrum) rate */ ++ error = wldev_iovar_getint(dev, "nrate", &rspec); ++ if (unlikely(error) || rspec == 0) { ++ ANDROID_ERROR(("get link status error (%d)\n", error)); ++ return -1; ++ } ++ ++ encode = (rspec & WL_RSPEC_ENCODING_MASK); ++ rate = (rspec & WL_RSPEC_RATE_MASK); ++ txexp = (rspec & WL_RSPEC_TXEXP_MASK) >> WL_RSPEC_TXEXP_SHIFT; ++ ++ switch (encode) { ++ case WL_RSPEC_ENCODE_HT: ++ /* check Rx MCS Map for HT */ ++ for (i = 0; i < MAX_STREAMS_SUPPORTED; i++) { ++ int8 bitmap = 0xFF; ++ if (i == MAX_STREAMS_SUPPORTED-1) { ++ bitmap = 0x7F; ++ } ++ if (bi->basic_mcs[i] & bitmap) { ++ nss++; ++ } ++ } ++ break; ++ case WL_RSPEC_ENCODE_VHT: ++ /* check Rx MCS Map for VHT */ ++ for (i = 1; i <= VHT_CAP_MCS_MAP_NSS_MAX; i++) { ++ mcs_map = VHT_MCS_MAP_GET_MCS_PER_SS(i, dtoh16(bi->vht_rxmcsmap)); ++ if (mcs_map != VHT_CAP_MCS_MAP_NONE) { ++ nss++; ++ } ++ } ++ break; ++ } ++ ++ /* check MIMO capability with nss in beacon */ ++ if (nss > 1) { ++ result |= WL_ANDROID_LINK_AP_MIMO_SUPPORT; ++ } ++ ++ single_stream = (encode == WL_RSPEC_ENCODE_RATE) || ++ ((encode == WL_RSPEC_ENCODE_HT) && rate < 8) || ++ ((encode == WL_RSPEC_ENCODE_VHT) && ++ ((rspec & WL_RSPEC_VHT_NSS_MASK) >> WL_RSPEC_VHT_NSS_SHIFT) == 1); ++ ++ if (txexp == 0) { ++ if ((rspec & WL_RSPEC_STBC) && single_stream) { ++ stf = OLD_NRATE_STF_STBC; ++ } else { ++ stf = (single_stream) ? OLD_NRATE_STF_SISO : OLD_NRATE_STF_SDM; ++ } ++ } else if (txexp == 1 && single_stream) { ++ stf = OLD_NRATE_STF_CDD; ++ } ++ ++ /* check 11ac (VHT) */ ++ if (encode == WL_RSPEC_ENCODE_VHT) { ++ if (CHSPEC_IS5G(bi->chanspec)) { ++ result |= WL_ANDROID_LINK_VHT; ++ } ++ } ++ ++ /* check MIMO */ ++ if (result & WL_ANDROID_LINK_AP_MIMO_SUPPORT) { ++ switch (stf) { ++ case OLD_NRATE_STF_SISO: ++ break; ++ case OLD_NRATE_STF_CDD: ++ case OLD_NRATE_STF_STBC: ++ result |= WL_ANDROID_LINK_MIMO; ++ break; ++ case OLD_NRATE_STF_SDM: ++ if (!single_stream) { ++ result |= WL_ANDROID_LINK_MIMO; ++ } ++ break; ++ } ++ } ++ ++ ANDROID_TRACE(("%s:result=%d, stf=%d, single_stream=%d, mcs map=%d\n", ++ __FUNCTION__, result, stf, single_stream, nss)); ++ ++ bytes_written = sprintf(command, "%s %d", CMD_GET_LINK_STATUS, result); ++ ++ return bytes_written; ++} ++ + int + wl_android_get_channel( + struct net_device *dev, char* command, int total_len) +@@ -1897,7 +2312,7 @@ + + sscanf(command, "%*s %10d", &roam_trigger[0]); + roam_trigger[1] = WLC_BAND_ALL; +- ++ + ret = wldev_ioctl(dev, WLC_SET_ROAM_TRIGGER, roam_trigger, sizeof(roam_trigger), 1); + if (ret) + ANDROID_ERROR(("WLC_SET_ROAM_TRIGGER ERROR %d ret=%d\n", roam_trigger[0], ret)); +@@ -1913,21 +2328,21 @@ + int bytes_written; + int roam_trigger[2] = {0, 0}; + int trigger[2]= {0, 0}; +- ++ + roam_trigger[1] = WLC_BAND_2G; + ret = wldev_ioctl(dev, WLC_GET_ROAM_TRIGGER, roam_trigger, sizeof(roam_trigger), 0); + if (!ret) + trigger[0] = roam_trigger[0]; +- else ++ else + ANDROID_ERROR(("2G WLC_GET_ROAM_TRIGGER ERROR %d ret=%d\n", roam_trigger[0], ret)); + + roam_trigger[1] = WLC_BAND_5G; + ret = wldev_ioctl(dev, WLC_GET_ROAM_TRIGGER, roam_trigger, sizeof(roam_trigger), 0); + if (!ret) + trigger[1] = roam_trigger[0]; +- else ++ else + ANDROID_ERROR(("5G WLC_GET_ROAM_TRIGGER ERROR %d ret=%d\n", roam_trigger[0], ret)); +- ++ + ANDROID_TRACE(("roam_trigger %d %d\n", trigger[0], trigger[1])); + bytes_written = snprintf(command, total_len, "%d %d", trigger[0], trigger[1]); + +@@ -2135,6 +2550,15 @@ + int filter_num = *(command + strlen(CMD_RXFILTER_REMOVE) + 1) - '0'; + bytes_written = net_os_rxfilter_add_remove(net, FALSE, filter_num); + } ++#if defined(CUSTOM_PLATFORM_NV_TEGRA) ++ else if (strnicmp(command, CMD_PKT_FILTER_MODE, strlen(CMD_PKT_FILTER_MODE)) == 0) { ++ dhd_set_packet_filter_mode(net, &command[strlen(CMD_PKT_FILTER_MODE) + 1]); ++ } else if (strnicmp(command, CMD_PKT_FILTER_PORTS, strlen(CMD_PKT_FILTER_PORTS)) == 0) { ++ bytes_written = dhd_set_packet_filter_ports(net, ++ &command[strlen(CMD_PKT_FILTER_PORTS) + 1]); ++ ret = bytes_written; ++ } ++#endif /* defined(CUSTOM_PLATFORM_NV_TEGRA) */ + #endif /* PKT_FILTER_SUPPORT */ + else if (strnicmp(command, CMD_BTCOEXSCAN_START, strlen(CMD_BTCOEXSCAN_START)) == 0) { + /* TBD: BTCOEXSCAN-START */ +@@ -2165,6 +2589,12 @@ + } + else if (strnicmp(command, CMD_SETBAND, strlen(CMD_SETBAND)) == 0) { + uint band = *(command + strlen(CMD_SETBAND) + 1) - '0'; ++ ++ if (dhd_conf_get_band(dhd_get_pub(net)) != WLC_BAND_AUTO) { ++ printf("%s: Band is fixed in config.txt\n", __FUNCTION__); ++ goto exit; ++ } ++ + #ifdef WL_HOST_BAND_MGMT + s32 ret = 0; + if ((ret = wl_cfg80211_set_band(net, band)) < 0) { +@@ -2190,7 +2620,12 @@ + /* CUSTOMER_SET_COUNTRY feature is define for only GGSM model */ + else if (strnicmp(command, CMD_COUNTRY, strlen(CMD_COUNTRY)) == 0) { + char *country_code = command + strlen(CMD_COUNTRY) + 1; ++#ifdef CUSTOMER_HW5 ++ /* Customer_hw5 want to keep connections */ ++ bytes_written = wldev_set_country(net, country_code, true, false); ++#else + bytes_written = wldev_set_country(net, country_code, true, true); ++#endif + } + #endif /* WL_CFG80211 */ + +@@ -2302,6 +2737,14 @@ + #ifdef WL_CFG80211 + else if (strnicmp(command, CMD_MIRACAST, strlen(CMD_MIRACAST)) == 0) + bytes_written = wl_android_set_miracast(net, command, priv_cmd.total_len); ++#if defined(CUSTOM_PLATFORM_NV_TEGRA) ++ else if (strnicmp(command, CMD_SETMIRACAST, strlen(CMD_SETMIRACAST)) == 0) ++ bytes_written = wldev_miracast_tuning(net, command, priv_cmd.total_len); ++ else if (strnicmp(command, CMD_ASSOCRESPIE, strlen(CMD_ASSOCRESPIE)) == 0) ++ bytes_written = wldev_get_assoc_resp_ie(net, command, priv_cmd.total_len); ++ else if (strnicmp(command, CMD_RXRATESTATS, strlen(CMD_RXRATESTATS)) == 0) ++ bytes_written = wldev_get_rx_rate_stats(net, command, priv_cmd.total_len); ++#endif /* defined(CUSTOM_PLATFORM_NV_TEGRA) */ + else if (strnicmp(command, CMD_SETIBSSBEACONOUIDATA, strlen(CMD_SETIBSSBEACONOUIDATA)) == 0) + bytes_written = wl_android_set_ibss_beacon_ouidata(net, + command, priv_cmd.total_len); +@@ -2336,6 +2779,30 @@ + int enable = *(command + strlen(CMD_ROAM_OFFLOAD) + 1) - '0'; + bytes_written = wl_cfg80211_enable_roam_offload(net, enable); + } ++ else if (strnicmp(command, CMD_ROAM_OFFLOAD_APLIST, strlen(CMD_ROAM_OFFLOAD_APLIST)) == 0) { ++ bytes_written = wl_android_set_roam_offload_bssid_list(net, ++ command + strlen(CMD_ROAM_OFFLOAD_APLIST) + 1); ++ } ++#endif ++#ifdef P2PRESP_WFDIE_SRC ++ else if (strnicmp(command, CMD_P2P_SET_WFDIE_RESP, ++ strlen(CMD_P2P_SET_WFDIE_RESP)) == 0) { ++ int mode = *(command + strlen(CMD_P2P_SET_WFDIE_RESP) + 1) - '0'; ++ bytes_written = wl_android_set_wfdie_resp(net, mode); ++ } else if (strnicmp(command, CMD_P2P_GET_WFDIE_RESP, ++ strlen(CMD_P2P_GET_WFDIE_RESP)) == 0) { ++ bytes_written = wl_android_get_wfdie_resp(net, command, priv_cmd.total_len); ++ } ++#endif /* P2PRESP_WFDIE_SRC */ ++ else if (strnicmp(command, CMD_GET_LINK_STATUS, strlen(CMD_GET_LINK_STATUS)) == 0) { ++ bytes_written = wl_android_get_link_status(net, command, priv_cmd.total_len); ++ } ++#ifdef CONNECTION_STATISTICS ++ else if (strnicmp(command, CMD_GET_CONNECTION_STATS, ++ strlen(CMD_GET_CONNECTION_STATS)) == 0) { ++ bytes_written = wl_android_get_connection_stats(net, command, ++ priv_cmd.total_len); ++ } + #endif + else if(strnicmp(command, CMD_GET_CHANNEL, strlen(CMD_GET_CHANNEL)) == 0) { + bytes_written = wl_android_get_channel(net, command, priv_cmd.total_len); +@@ -2848,7 +3315,7 @@ + if (!memcmp(&node->BSSID, &bssid, ETHER_ADDR_LEN)) { + ANDROID_INFO(("%s: Update %d with BSSID %pM, RSSI=%d\n", + __FUNCTION__, k, &bssid, rssi)); +- for(j=0; jRSSI[j] = node->RSSI[j+1]; + node->RSSI[j] = rssi; + node->dirty = 0; +@@ -2863,10 +3330,10 @@ + leaf = kmalloc(sizeof(wl_rssi_cache_t), GFP_KERNEL); + if (!leaf) { + ANDROID_ERROR(("%s: Memory alloc failure %d\n", +- __FUNCTION__, sizeof(wl_rssi_cache_t))); ++ __FUNCTION__, (int)sizeof(wl_rssi_cache_t))); + return 0; + } +- ANDROID_INFO(("%s: Add %d with cached BSSID %pM, RSSI=%d in the leaf\n", ++ ANDROID_INFO(("%s: Add %d with cached BSSID %pM, RSSI=%3d in the leaf\n", + __FUNCTION__, k, &bssid, rssi)); + + leaf->next = NULL; +@@ -2919,9 +3386,9 @@ + bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : ss_list->bss_info; + for (;node;) { + if (!memcmp(&node->BSSID, &bi->BSSID, ETHER_ADDR_LEN)) { +- ANDROID_INFO(("%s: Update %d with BSSID %pM, RSSI=%d, SSID \"%s\"\n", ++ ANDROID_INFO(("%s: Update %d with BSSID %pM, RSSI=%3d, SSID \"%s\"\n", + __FUNCTION__, k, &bi->BSSID, dtoh16(bi->RSSI), bi->SSID)); +- for(j=0; jRSSI[j] = node->RSSI[j+1]; + node->RSSI[j] = dtoh16(bi->RSSI); + node->dirty = 0; +@@ -2939,10 +3406,10 @@ + leaf = kmalloc(sizeof(wl_rssi_cache_t), GFP_KERNEL); + if (!leaf) { + ANDROID_ERROR(("%s: Memory alloc failure %d\n", +- __FUNCTION__, sizeof(wl_rssi_cache_t))); ++ __FUNCTION__, (int)sizeof(wl_rssi_cache_t))); + return; + } +- ANDROID_INFO(("%s: Add %d with cached BSSID %pM, RSSI=%d, SSID \"%s\" in the leaf\n", ++ ANDROID_INFO(("%s: Add %d with cached BSSID %pM, RSSI=%3d, SSID \"%s\" in the leaf\n", + __FUNCTION__, k, &bi->BSSID, dtoh16(bi->RSSI), bi->SSID)); + + leaf->next = NULL; +@@ -2992,32 +3459,27 @@ + int + wl_update_rssi_offset(struct net_device *net, int rssi) + { +- uint chip, chiprev; ++#if defined(RSSIOFFSET_NEW) ++ int j; ++#endif + + if (!g_wifi_on) + return rssi; + +- chip = dhd_conf_get_chip(dhd_get_pub(net)); +- chiprev = dhd_conf_get_chiprev(dhd_get_pub(net)); +- if (chip == BCM4330_CHIP_ID && chiprev == BCM4330B2_CHIP_REV) { + #if defined(RSSIOFFSET_NEW) +- int j; +- for (j=0; jnext = node->next; + } +- ANDROID_TRACE(("%s: Del %d with BSSID %pM, RSSI=%d, SSID \"%s\"\n", ++ ANDROID_TRACE(("%s: Del %d with BSSID %pM, RSSI=%3d, SSID \"%s\"\n", + __FUNCTION__, i, &node->results.bss_info->BSSID, + dtoh16(node->results.bss_info->RSSI), node->results.bss_info->SSID)); + kfree(node); +@@ -3098,7 +3560,7 @@ + tmp = 0; + prev->next = node->next; + } +- ANDROID_TRACE(("%s: Del %d with BSSID %pM, RSSI=%d, SSID \"%s\"\n", ++ ANDROID_TRACE(("%s: Del %d with BSSID %pM, RSSI=%3d, SSID \"%s\"\n", + __FUNCTION__, i, &node->results.bss_info->BSSID, + dtoh16(node->results.bss_info->RSSI), node->results.bss_info->SSID)); + kfree(node); +@@ -3130,12 +3592,41 @@ + } + } + ++void dump_bss_cache( ++#if defined(RSSIAVG) ++ wl_rssi_cache_ctrl_t *rssi_cache_ctrl, ++#endif ++ wl_bss_cache_t *node) ++{ ++ int k = 0; ++ int16 rssi; ++ ++ for (;node;) { ++#if defined(RSSIAVG) ++ rssi = wl_get_avg_rssi(rssi_cache_ctrl, &node->results.bss_info->BSSID); ++#else ++ rssi = dtoh16(node->results.bss_info->RSSI); ++#endif ++ ANDROID_TRACE(("%s: dump %d with cached BSSID %pM, RSSI=%3d, SSID \"%s\"\n", ++ __FUNCTION__, k, &node->results.bss_info->BSSID, rssi, node->results.bss_info->SSID)); ++ k++; ++ node = node->next; ++ } ++} ++ + void +-wl_update_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl, wl_scan_results_t *ss_list) ++wl_update_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl, ++#if defined(RSSIAVG) ++ wl_rssi_cache_ctrl_t *rssi_cache_ctrl, ++#endif ++ wl_scan_results_t *ss_list) + { +- wl_bss_cache_t *node, *prev, *leaf, *tmp, **bss_head; ++ wl_bss_cache_t *node, *prev, *leaf, **bss_head; + wl_bss_info_t *bi = NULL; + int i, k=0; ++#if defined(SORT_BSS_BY_RSSI) ++ int16 rssi, rssi_node; ++#endif + struct timeval now, timeout; + + if (!ss_list->count) +@@ -3158,50 +3649,33 @@ + node = *bss_head; + prev = NULL; + bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : ss_list->bss_info; +- ++ + for (;node;) { + if (!memcmp(&node->results.bss_info->BSSID, &bi->BSSID, ETHER_ADDR_LEN)) { +- tmp = node; +- leaf = kmalloc(dtoh32(bi->length) + WLC_IW_SS_CACHE_CTRL_FIELD_MAXLEN, GFP_KERNEL); +- if (!leaf) { +- ANDROID_ERROR(("%s: Memory alloc failure %d and keep old BSS info\n", +- __FUNCTION__, dtoh32(bi->length) + WLC_IW_SS_CACHE_CTRL_FIELD_MAXLEN)); +- break; ++ if (node == *bss_head) ++ *bss_head = node->next; ++ else { ++ prev->next = node->next; + } +- +- memcpy(leaf->results.bss_info, bi, dtoh32(bi->length)); +- leaf->next = node->next; +- leaf->dirty = 0; +- leaf->tv = timeout; +- leaf->results.count = 1; +- leaf->results.version = ss_list->version; +- ANDROID_TRACE(("%s: Update %d with BSSID %pM, RSSI=%d, SSID \"%s\", length=%d\n", +- __FUNCTION__, k, &bi->BSSID, dtoh16(bi->RSSI), bi->SSID, dtoh32(bi->length))); +- if (!prev) +- *bss_head = leaf; +- else +- prev->next = leaf; +- node = leaf; +- prev = node; +- +- kfree(tmp); +- k++; + break; + } + prev = node; + node = node->next; + } + +- if (node) +- continue; +- +- leaf = kmalloc(dtoh32(bi->length) + WLC_IW_SS_CACHE_CTRL_FIELD_MAXLEN, GFP_KERNEL); ++ leaf = kmalloc(dtoh32(bi->length) + sizeof(wl_bss_cache_t), GFP_KERNEL); + if (!leaf) { + ANDROID_ERROR(("%s: Memory alloc failure %d\n", __FUNCTION__, +- dtoh32(bi->length) + WLC_IW_SS_CACHE_CTRL_FIELD_MAXLEN)); ++ dtoh32(bi->length) + (int)sizeof(wl_bss_cache_t))); + return; + } +- ANDROID_TRACE(("%s: Add %d with cached BSSID %pM, RSSI=%d, SSID \"%s\" in the leaf\n", ++ if (node) { ++ kfree(node); ++ node = NULL; ++ ANDROID_TRACE(("%s: Update %d with cached BSSID %pM, RSSI=%3d, SSID \"%s\"\n", ++ __FUNCTION__, k, &bi->BSSID, dtoh16(bi->RSSI), bi->SSID)); ++ } else ++ ANDROID_TRACE(("%s: Add %d with cached BSSID %pM, RSSI=%3d, SSID \"%s\"\n", + __FUNCTION__, k, &bi->BSSID, dtoh16(bi->RSSI), bi->SSID)); + + memcpy(leaf->results.bss_info, bi, dtoh32(bi->length)); +@@ -3212,11 +3686,46 @@ + leaf->results.version = ss_list->version; + k++; + +- if (!prev) ++ if (*bss_head == NULL) + *bss_head = leaf; +- else +- prev->next = leaf; ++ else { ++#if defined(SORT_BSS_BY_RSSI) ++ node = *bss_head; ++#if defined(RSSIAVG) ++ rssi = wl_get_avg_rssi(rssi_cache_ctrl, &leaf->results.bss_info->BSSID); ++#else ++ rssi = dtoh16(leaf->results.bss_info->RSSI); ++#endif ++ for (;node;) { ++#if defined(RSSIAVG) ++ rssi_node = wl_get_avg_rssi(rssi_cache_ctrl, &node->results.bss_info->BSSID); ++#else ++ rssi_node = dtoh16(node->results.bss_info->RSSI); ++#endif ++ if (rssi > rssi_node) { ++ leaf->next = node; ++ if (node == *bss_head) ++ *bss_head = leaf; ++ else ++ prev->next = leaf; ++ break; ++ } ++ prev = node; ++ node = node->next; ++ } ++ if (node == NULL) ++ prev->next = leaf; ++#else ++ leaf->next = *bss_head; ++ *bss_head = leaf; ++#endif ++ } + } ++ dump_bss_cache( ++#if defined(RSSIAVG) ++ rssi_cache_ctrl, ++#endif ++ *bss_head); + } + + void +diff -Nur a/drivers/net/wireless/bcmdhd/wl_android.h c/drivers/net/wireless/bcmdhd/wl_android.h +--- a/drivers/net/wireless/bcmdhd/wl_android.h 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/wl_android.h 2016-05-13 09:48:20.000000000 +0200 +@@ -104,11 +104,13 @@ + * BSSCACHE: Cache bss list + * RSSAVG: Average RSSI of BSS list + * RSSIOFFSET: RSSI offset ++ * SORT_BSS_BY_RSSI: Sort BSS by RSSI + */ +-#define BSSCACHE +-#define RSSIAVG +-#define RSSIOFFSET ++//#define BSSCACHE ++//#define RSSIAVG ++//#define RSSIOFFSET + //#define RSSIOFFSET_NEW ++//#define SORT_BSS_BY_RSSI + + #define RSSI_MAXVAL -2 + #define RSSI_MINVAL -200 +@@ -174,7 +176,11 @@ + void wl_delete_dirty_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl); + void wl_delete_disconnected_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl, u8 *bssid); + void wl_reset_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl); +-void wl_update_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl, wl_scan_results_t *ss_list); ++void wl_update_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl, ++#if defined(RSSIAVG) ++ wl_rssi_cache_ctrl_t *rssi_cache_ctrl, ++#endif ++ wl_scan_results_t *ss_list); + void wl_release_bss_cache_ctrl(wl_bss_cache_ctrl_t *bss_cache_ctrl); + #endif + #endif /* _wl_android_ */ +diff -Nur a/drivers/net/wireless/bcmdhd/wl_cfg80211.c c/drivers/net/wireless/bcmdhd/wl_cfg80211.c +--- a/drivers/net/wireless/bcmdhd/wl_cfg80211.c 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/wl_cfg80211.c 2016-05-13 09:48:20.000000000 +0200 +@@ -3,7 +3,7 @@ + * + * $Copyright Open Broadcom Corporation$ + * +- * $Id: wl_cfg80211.c 491569 2014-07-16 21:28:40Z $ ++ * $Id: wl_cfg80211.c 506036 2014-10-02 11:33:14Z $ + */ + /* */ + #include +@@ -101,7 +101,6 @@ + static struct bcm_cfg80211 *g_bcm_cfg = NULL; + u32 wl_dbg_level = WL_DBG_ERR; + +-#define MAX_WAIT_TIME 1500 + #ifdef WLAIBSS_MCHAN + #define IBSS_IF_NAME "ibss%d" + #endif /* WLAIBSS_MCHAN */ +@@ -135,7 +134,6 @@ + #define WL_IS_P2P_DEV_EVENT(e) ((e->emsg.ifidx == 0) && \ + (e->emsg.bsscfgidx == P2PAPI_BSSCFG_DEVICE)) + +-#define DNGL_FUNC(func, parameters) func parameters + #define COEX_DHCP + + #define WLAN_EID_SSID 0 +@@ -271,9 +269,6 @@ + #define WPS_CONFIG_VIRT_DISPLAY 0x2008 + #define WPS_CONFIG_PHY_DISPLAY 0x4008 + +-#define PM_BLOCK 1 +-#define PM_ENABLE 0 +- + #ifdef BCMCCX + #ifndef WLAN_AKM_SUITE_CCKM + #define WLAN_AKM_SUITE_CCKM 0x00409600 +@@ -284,6 +279,8 @@ + #ifdef MFP + #define WL_AKM_SUITE_MFP_1X 0x000FAC05 + #define WL_AKM_SUITE_MFP_PSK 0x000FAC06 ++#define WL_MFP_CAPABLE 0x1 ++#define WL_MFP_REQUIRED 0x2 + #endif /* MFP */ + + #ifndef IBSS_COALESCE_ALLOWED +@@ -381,10 +378,23 @@ + struct cfg80211_pmksa *pmksa); + static s32 wl_cfg80211_flush_pmksa(struct wiphy *wiphy, + struct net_device *dev); +-static void wl_cfg80211_scan_abort(struct bcm_cfg80211 *cfg); ++#ifdef P2PONEINT ++void wl_cfg80211_scan_abort(struct bcm_cfg80211 *cfg); ++#else ++void wl_cfg80211_scan_abort(struct bcm_cfg80211 *cfg); ++#endif + static s32 wl_notify_escan_complete(struct bcm_cfg80211 *cfg, + struct net_device *ndev, bool aborted, bool fw_abort); + #if (LINUX_VERSION_CODE > KERNEL_VERSION(3, 2, 0)) || defined(WL_COMPAT_WIRELESS) ++#if defined(CONFIG_ARCH_MSM) && defined(TDLS_MGMT_VERSION2) ++static s32 wl_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, ++ u8 *peer, u8 action_code, u8 dialog_token, u16 status_code, ++ u32 peer_capability, const u8 *data, size_t len); ++#else ++static s32 wl_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, ++ u8 *peer, u8 action_code, u8 dialog_token, u16 status_code, const u8 *data, ++ size_t len); ++#endif /* CONFIG_ARCH_MSM && TDLS_MGMT_VERSION2 */ + static s32 wl_cfg80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, + u8 *peer, enum nl80211_tdls_operation oper); + #endif /* LINUX_VERSION > KERNEL_VERSION(3,2,0) || WL_COMPAT_WIRELESS */ +@@ -522,7 +532,7 @@ + uint8 ie_id, uint8 *data, uint8 data_len); + #endif /* WL11U */ + +-static s32 wl_setup_wiphy(struct wireless_dev *wdev, struct device *dev, void *data); ++static s32 wl_setup_wiphy(struct wireless_dev *wdev, struct device *dev, dhd_pub_t *data); + static void wl_free_wdev(struct bcm_cfg80211 *cfg); + #ifdef CONFIG_CFG80211_INTERNAL_REGDB + static int +@@ -532,7 +542,11 @@ + static s32 wl_inform_bss(struct bcm_cfg80211 *cfg); + static s32 wl_inform_single_bss(struct bcm_cfg80211 *cfg, struct wl_bss_info *bi, bool roam); + static s32 wl_update_bss_info(struct bcm_cfg80211 *cfg, struct net_device *ndev, bool roam); +-static chanspec_t wl_cfg80211_get_shared_freq(struct wiphy *wiphy); ++#ifdef P2PONEINT ++chanspec_t wl_cfg80211_get_shared_freq(struct wiphy *wiphy); ++#else ++chanspec_t wl_cfg80211_get_shared_freq(struct wiphy *wiphy); ++#endif + s32 wl_cfg80211_channel_to_freq(u32 channel); + + #if defined(DHCP_SCAN_SUPPRESS) +@@ -654,6 +668,8 @@ + extern int disable_proptx; + #endif /* PROP_TXSTATUS_VSDB */ + ++extern int passive_channel_skip; ++ + #if (WL_DBG_LEVEL > 0) + #define WL_DBG_ESTR_MAX 50 + static s8 wl_dbg_estr[][WL_DBG_ESTR_MAX] = { +@@ -1287,7 +1303,12 @@ + return err; + } + +-static chanspec_t wl_cfg80211_get_shared_freq(struct wiphy *wiphy) ++chanspec_t ++#ifdef P2PONEINT ++wl_cfg80211_get_shared_freq(struct wiphy *wiphy) ++#else ++wl_cfg80211_get_shared_freq(struct wiphy *wiphy) ++#endif + { + chanspec_t chspec; + int err = 0; +@@ -1362,8 +1383,11 @@ + s32 up = 1; + dhd_pub_t *dhd; + bool enabled; +-#endif ++#endif + #endif /* PROP_TXSTATUS_VSDB */ ++#if defined(SUPPORT_AP_POWERSAVE) ++ dhd_pub_t *dhd; ++#endif + + if (!cfg) + return ERR_PTR(-EINVAL); +@@ -1371,9 +1395,11 @@ + #ifdef PROP_TXSTATUS_VSDB + #if defined(BCMSDIO) + dhd = (dhd_pub_t *)(cfg->pub); +-#endif ++#endif + #endif /* PROP_TXSTATUS_VSDB */ +- ++#if defined(SUPPORT_AP_POWERSAVE) ++ dhd = (dhd_pub_t *)(cfg->pub); ++#endif + + /* Use primary I/F for sending cmds down to firmware */ + primary_ndev = bcmcfg_to_prmry_ndev(cfg); +@@ -1443,7 +1469,7 @@ + #if defined(BCMSDIO) + if (!dhd) + return ERR_PTR(-ENODEV); +-#endif ++#endif + #endif /* PROP_TXSTATUS_VSDB */ + if (!cfg->p2p) + return ERR_PTR(-ENODEV); +@@ -1474,7 +1500,7 @@ + } + cfg->wlfc_on = true; + } +-#endif ++#endif + #endif /* PROP_TXSTATUS_VSDB */ + + /* In concurrency case, STA may be already associated in a particular channel. +@@ -1549,6 +1575,11 @@ + "created net attach done\n", cfg->p2p->vir_ifname)); + if (mode == WL_MODE_AP) + wl_set_drv_status(cfg, CONNECTED, new_ndev); ++#ifdef SUPPORT_AP_POWERSAVE ++ if (mode == WL_MODE_AP) { ++ dhd_set_ap_powersave(dhd, 0, TRUE); ++ } ++#endif + if (type == NL80211_IFTYPE_P2P_CLIENT) + dhd_mode = DHD_FLAG_P2P_GC_MODE; + else if (type == NL80211_IFTYPE_P2P_GO) +@@ -1564,6 +1595,35 @@ + } else { + wl_clr_p2p_status(cfg, IF_ADDING); + WL_ERR((" virtual interface(%s) is not created \n", cfg->p2p->vir_ifname)); ++ ++ WL_ERR(("left timeout : %d\n", timeout)); ++ WL_ERR(("IF_ADDING status : %d\n", wl_get_p2p_status(cfg, IF_ADDING))); ++ WL_ERR(("event valid : %d\n", cfg->if_event_info.valid)); ++ ++ wl_clr_p2p_status(cfg, GO_NEG_PHASE); ++ wl_set_p2p_status(cfg, IF_DELETING); ++ ++ err = wl_cfgp2p_ifdel(cfg, &cfg->p2p->int_addr); ++ if (err == BCME_OK) { ++ timeout = wait_event_interruptible_timeout(cfg->netif_change_event, ++ (wl_get_p2p_status(cfg, IF_DELETING) == false), ++ msecs_to_jiffies(MAX_WAIT_TIME)); ++ if (timeout > 0 && !wl_get_p2p_status(cfg, IF_DELETING) && ++ cfg->if_event_info.valid) { ++ WL_ERR(("IFDEL operation done\n")); ++ } else { ++ WL_ERR(("IFDEL didn't complete properly\n")); ++ err = BCME_ERROR; ++ } ++ } ++ if (err != BCME_OK) { ++ struct net_device *ndev = bcmcfg_to_prmry_ndev(cfg); ++ ++ WL_ERR(("p2p_ifdel failed, error %d, sent HANG event to %s\n", ++ err, ndev->name)); ++ net_os_send_hang_message(ndev); ++ } ++ + memset(cfg->p2p->vir_ifname, '\0', IFNAMSIZ); + cfg->p2p->vif_created = false; + #ifdef PROP_TXSTATUS_VSDB +@@ -1574,7 +1634,7 @@ + dhd_wlfc_deinit(dhd); + cfg->wlfc_on = false; + } +-#endif ++#endif + #endif /* PROP_TXSTATUS_VSDB */ + } + } +@@ -1683,7 +1743,7 @@ + ret, ndev->name)); + #if defined(BCMDONGLEHOST) && defined(OEM_ANDROID) + net_os_send_hang_message(ndev); +- #endif ++ #endif + } else { + /* Wait for IF_DEL operation to be finished */ + timeout = wait_event_interruptible_timeout(cfg->netif_change_event, +@@ -1767,7 +1827,7 @@ + chspec = wl_cfg80211_get_shared_freq(wiphy); + + wlif_type = WL_P2P_IF_GO; +- printk("%s : ap (%d), infra (%d), iftype: (%d)\n", ++ printf("%s : ap (%d), infra (%d), iftype: (%d)\n", + ndev->name, ap, infra, type); + wl_set_p2p_status(cfg, IF_CHANGING); + wl_clr_p2p_status(cfg, IF_CHANGED); +@@ -1782,6 +1842,9 @@ + wl_clr_p2p_status(cfg, IF_CHANGED); + if (mode == WL_MODE_AP) + wl_set_drv_status(cfg, CONNECTED, ndev); ++#ifdef SUPPORT_AP_POWERSAVE ++ dhd_set_ap_powersave(dhd, 0, TRUE); ++#endif + } else if (ndev == bcmcfg_to_prmry_ndev(cfg) && + !wl_get_drv_status(cfg, AP_CREATED, ndev)) { + wl_set_drv_status(cfg, AP_CREATING, ndev); +@@ -1796,6 +1859,52 @@ + } + } else { + WL_DBG(("Change_virtual_iface for transition from GO/AP to client/STA")); ++#ifdef SUPPORT_AP_POWERSAVE ++ dhd_set_ap_powersave(dhd, 0, FALSE); ++#endif ++#ifdef P2PONEINT ++ wl_set_mode_by_netdev(cfg, ndev, mode); ++ if (cfg->p2p_supported && cfg->p2p->vif_created) { ++ WL_DBG(("p2p_vif_created (%d) p2p_on (%d)\n", cfg->p2p->vif_created, ++ p2p_on(cfg))); ++ wldev_iovar_setint(ndev, "mpc", 0); ++ wl_notify_escan_complete(cfg, ndev, true, true); ++ ++ /* In concurrency case, STA may be already associated in a particular ++ * channel. so retrieve the current channel of primary interface and ++ * then start the virtual interface on that. ++ */ ++ chspec = wl_cfg80211_get_shared_freq(wiphy); ++ ++ wlif_type = WL_P2P_IF_CLIENT; ++ WL_ERR(("%s : ap (%d), infra (%d), iftype: (%d) chspec 0x%x \n", ++ ndev->name, ap, infra, type, chspec)); ++ wl_set_p2p_status(cfg, IF_CHANGING); ++ wl_clr_p2p_status(cfg, IF_CHANGED); ++ wl_cfgp2p_ifchange(cfg, &cfg->p2p->int_addr, htod32(wlif_type), chspec); ++ wait_event_interruptible_timeout(cfg->netif_change_event, ++ (wl_get_p2p_status(cfg, IF_CHANGED) == true), ++ msecs_to_jiffies(MAX_WAIT_TIME)); ++ wl_set_mode_by_netdev(cfg, ndev, mode); ++ dhd->op_mode |= DHD_FLAG_P2P_GC_MODE; ++ dhd->op_mode &= ~DHD_FLAG_P2P_GO_MODE; ++ wl_clr_p2p_status(cfg, IF_CHANGING); ++ wl_clr_p2p_status(cfg, IF_CHANGED); ++ ++#define INIT_IE(IE_TYPE, BSS_TYPE) \ ++ do { \ ++ memset(wl_to_p2p_bss_saved_ie(cfg, BSS_TYPE).p2p_ ## IE_TYPE ## _ie, 0, \ ++ sizeof(wl_to_p2p_bss_saved_ie(cfg, BSS_TYPE).p2p_ ## IE_TYPE ## _ie)); \ ++ wl_to_p2p_bss_saved_ie(cfg, BSS_TYPE).p2p_ ## IE_TYPE ## _ie_len = 0; \ ++ } while (0); ++ ++ INIT_IE(probe_req, P2PAPI_BSSCFG_CONNECTION); ++ INIT_IE(probe_res, P2PAPI_BSSCFG_CONNECTION); ++ INIT_IE(assoc_req, P2PAPI_BSSCFG_CONNECTION); ++ INIT_IE(assoc_res, P2PAPI_BSSCFG_CONNECTION); ++ INIT_IE(beacon, P2PAPI_BSSCFG_CONNECTION); ++ } ++#endif /* P2PONEINT */ + } + + if (ibss) { +@@ -1899,7 +2008,7 @@ + #if defined(BCMSDIO) + dhd_pub_t *dhd = (dhd_pub_t *)(cfg->pub); + bool enabled; +-#endif ++#endif + #endif /* PROP_TXSTATUS_VSDB */ + + bssidx = if_event_info->bssidx; +@@ -1935,7 +2044,7 @@ + dhd_wlfc_deinit(dhd); + cfg->wlfc_on = false; + } +-#endif ++#endif + #endif /* PROP_TXSTATUS_VSDB */ + } + +@@ -2054,6 +2163,8 @@ + (IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IR))) + #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0) */ + continue; ++ if (!dhd_conf_match_channel(cfg->pub, channel)) ++ continue; + + if (request->channels[i]->band == IEEE80211_BAND_2GHZ) { + #ifdef WL_HOST_BAND_MGMT +@@ -2142,7 +2253,7 @@ + #if defined(USE_INITIAL_SHORT_DWELL_TIME) + #define FIRST_SCAN_ACTIVE_DWELL_TIME_MS 40 + bool g_first_broadcast_scan = TRUE; +-#endif ++#endif + + static s32 + wl_run_escan(struct bcm_cfg80211 *cfg, struct net_device *ndev, +@@ -2189,7 +2300,7 @@ + is_first_init_2g_scan = true; + g_first_broadcast_scan = false; + } +-#endif ++#endif + + /* if scan request is not empty parse scan request paramters */ + if (request != NULL) { +@@ -2215,7 +2326,7 @@ + /* Override active_time to reduce scan time if it's first bradcast scan. */ + if (is_first_init_2g_scan) + params->params.active_time = FIRST_SCAN_ACTIVE_DWELL_TIME_MS; +-#endif ++#endif + + params->version = htod32(ESCAN_REQ_VERSION); + params->action = htod16(action); +@@ -2365,6 +2476,8 @@ + { + s32 err = BCME_OK; + s32 passive_scan; ++ s32 passive_scan_time; ++ s32 passive_scan_time_org; + wl_scan_results_t *results; + WL_SCAN(("Enter \n")); + mutex_lock(&cfg->usr_sync); +@@ -2385,7 +2498,43 @@ + goto exit; + } + ++ if (passive_channel_skip) { ++ ++ err = wldev_ioctl(ndev, WLC_GET_SCAN_PASSIVE_TIME, ++ &passive_scan_time_org, sizeof(passive_scan_time_org), false); ++ if (unlikely(err)) { ++ WL_ERR(("== error (%d)\n", err)); ++ goto exit; ++ } ++ ++ WL_SCAN(("PASSIVE SCAN time : %d \n", passive_scan_time_org)); ++ ++ passive_scan_time = 0; ++ err = wldev_ioctl(ndev, WLC_SET_SCAN_PASSIVE_TIME, ++ &passive_scan_time, sizeof(passive_scan_time), true); ++ if (unlikely(err)) { ++ WL_ERR(("== error (%d)\n", err)); ++ goto exit; ++ } ++ ++ WL_SCAN(("PASSIVE SCAN SKIPED!! (passive_channel_skip:%d) \n", ++ passive_channel_skip)); ++ } ++ + err = wl_run_escan(cfg, ndev, request, WL_SCAN_ACTION_START); ++ ++ if (passive_channel_skip) { ++ err = wldev_ioctl(ndev, WLC_SET_SCAN_PASSIVE_TIME, ++ &passive_scan_time_org, sizeof(passive_scan_time_org), true); ++ if (unlikely(err)) { ++ WL_ERR(("== error (%d)\n", err)); ++ goto exit; ++ } ++ ++ WL_SCAN(("PASSIVE SCAN RECOVERED!! (passive_scan_time_org:%d) \n", ++ passive_scan_time_org)); ++ } ++ + exit: + mutex_unlock(&cfg->usr_sync); + return err; +@@ -2562,6 +2711,10 @@ + ssids = this_ssid; + } + ++ if (request && cfg->p2p && !p2p_scan(cfg)) { ++ WL_TRACE_HW4(("START SCAN\n")); ++ } ++ + cfg->scan_request = request; + wl_set_drv_status(cfg, SCANNING, ndev); + +@@ -2672,6 +2825,11 @@ + WL_DBG(("Enter \n")); + RETURN_EIO_IF_NOT_UP(cfg); + ++#ifdef P2PONEINT ++ ndev = bcmcfg_to_prmry_ndev(cfg); ++ WL_DBG(("scan use [dev name %s ] \n", ndev->name)); ++#endif ++ + err = __wl_cfg80211_scan(wiphy, ndev, request, NULL); + if (unlikely(err)) { + if ((err == BCME_EPERM) && cfg->scan_suppressed) +@@ -3005,6 +3163,51 @@ + } + #endif /* WLAIBSS_MCHAN */ + ++s32 ++wl_cfg80211_interface_ops(struct bcm_cfg80211 *cfg, ++ struct net_device *ndev, s32 bsscfg_idx, ++ enum nl80211_iftype iface_type, s32 del, u8 *addr) ++{ ++ wl_interface_create_t iface; ++ s32 ret; ++ wl_interface_info_t *info; ++ ++ bzero(&iface, sizeof(wl_interface_create_t)); ++ ++ iface.ver = WL_INTERFACE_CREATE_VER; ++ ++ if (iface_type == NL80211_IFTYPE_AP) ++ iface.flags = WL_INTERFACE_CREATE_AP; ++ else ++ iface.flags = WL_INTERFACE_CREATE_STA; ++ ++ if (del) { ++ ret = wldev_iovar_setbuf(ndev, "interface_remove", ++ NULL, 0, cfg->ioctl_buf, WLC_IOCTL_MEDLEN, NULL); ++ } else { ++ if (addr) { ++ memcpy(&iface.mac_addr.octet, addr, ETH_ALEN); ++ iface.flags |= WL_INTERFACE_MAC_USE; ++ } ++ ret = wldev_iovar_getbuf(ndev, "interface_create", ++ &iface, sizeof(wl_interface_create_t), ++ cfg->ioctl_buf, WLC_IOCTL_MAXLEN, &cfg->ioctl_buf_sync); ++ if (ret == 0) { ++ /* success */ ++ info = (wl_interface_info_t *)cfg->ioctl_buf; ++ WL_DBG(("wl interface create success!! bssidx:%d \n", ++ info->bsscfgidx)); ++ ret = info->bsscfgidx; ++ } ++ } ++ ++ if (ret < 0) ++ WL_ERR(("Interface %s failed!! ret %d\n", ++ del ? "remove" : "create", ret)); ++ ++ return ret; ++} ++ + #if defined(DUAL_STA) || defined(DUAL_STA_STATIC_IF) + s32 + wl_cfg80211_add_del_bss(struct bcm_cfg80211 *cfg, +@@ -3119,9 +3322,20 @@ + /* + * Intialize the firmware I/F. + */ +- if ((ret = wl_cfg80211_add_del_bss(cfg, primary_ndev, +- bsscfg_idx, iface_type, 0, addr)) < 0) { +- return NULL; ++ ret = wl_cfg80211_interface_ops(cfg, primary_ndev, bsscfg_idx, ++ NL80211_IFTYPE_STATION, 0, addr); ++ if (ret == BCME_UNSUPPORTED) { ++ /* Use bssidx 1 by default */ ++ if ((ret = wl_cfg80211_add_del_bss(cfg, primary_ndev, ++ bsscfg_idx, iface_type, 0, addr)) < 0) { ++ return NULL; ++ } ++ } else if (ret < 0) { ++ WL_ERR(("Interface create failed!! ret:%d \n", ret)); ++ goto fail; ++ } else { ++ /* Success */ ++ bsscfg_idx = ret; + } + + /* +@@ -3210,6 +3424,7 @@ + s32 ret = BCME_OK; + s32 bsscfg_idx = 1; + u32 timeout; ++ u32 ifidx; + enum nl80211_iftype iface_type = NL80211_IFTYPE_STATION; + + WL_DBG(("Enter\n")); +@@ -3230,10 +3445,17 @@ + memset(&cfg->if_event_info, 0, sizeof(cfg->if_event_info)); + + /* Delete the firmware interface */ +- if ((ret = wl_cfg80211_add_del_bss(cfg, ndev, +- bsscfg_idx, iface_type, true, NULL)) < 0) { +- WL_ERR(("DEL bss failed ret:%d \n", ret)); +- return ret; ++ ret = wl_cfg80211_interface_ops(cfg, ndev, cfg->cfgdev_bssidx, ++ NL80211_IFTYPE_STATION, 1, NULL); ++ if (ret == BCME_UNSUPPORTED) { ++ if ((ret = wl_cfg80211_add_del_bss(cfg, ndev, ++ bsscfg_idx, iface_type, true, NULL)) < 0) { ++ WL_ERR(("DEL bss failed ret:%d \n", ret)); ++ return ret; ++ } ++ } else if (ret < 0) { ++ WL_ERR(("Interface DEL failed ret:%d \n", ret)); ++ return ret; + } + + timeout = wait_event_interruptible_timeout(cfg->netif_change_event, +@@ -3241,8 +3463,8 @@ + if (timeout <= 0 || cfg->bss_pending_op) { + WL_ERR(("timeout in waiting IF_DEL event\n")); + } +- +- wl_cfg80211_remove_if(cfg, cfg->if_event_info.ifidx, ndev); ++ ifidx = dhd_net2idx(((struct dhd_pub *)(cfg->pub))->info, ndev); ++ wl_cfg80211_remove_if(cfg, ifidx, ndev); + cfg->bss_cfgdev = NULL; + cfg->cfgdev_bssidx = -1; + cfg->bss_pending_op = FALSE; +@@ -3771,6 +3993,13 @@ + wsec_val |= MFP_CAPABLE; + if (rsn_cap[0] & RSN_CAP_MFPR) + wsec_val |= MFP_REQUIRED; ++ ++ if (rsn_cap[0] & RSN_CAP_MFPR) ++ mfp = WL_MFP_REQUIRED; ++ else ++ mfp = WL_MFP_CAPABLE; ++ err = wldev_iovar_setint_bsscfg(dev, "mfp", ++ mfp, bssidx); + } + } + } +@@ -4137,11 +4366,19 @@ + wpaie = (wpa_ie != NULL) ? (u8 *)wpa_ie : (u8 *)wpa2_ie; + wpaie_len = (wpa_ie != NULL) ? wpa_ie->length : wpa2_ie->len; + wpaie_len += WPA_RSN_IE_TAG_FIXED_LEN; +- wldev_iovar_setbuf(dev, "wpaie", wpaie, wpaie_len, ++ err = wldev_iovar_setbuf(dev, "wpaie", wpaie, wpaie_len, + cfg->ioctl_buf, WLC_IOCTL_MAXLEN, &cfg->ioctl_buf_sync); ++ if (unlikely(err)) { ++ WL_ERR(("wpaie set error (%d)\n", err)); ++ return err; ++ } + } else { +- wldev_iovar_setbuf(dev, "wpaie", NULL, 0, ++ err = wldev_iovar_setbuf(dev, "wpaie", NULL, 0, + cfg->ioctl_buf, WLC_IOCTL_MAXLEN, &cfg->ioctl_buf_sync); ++ if (unlikely(err)) { ++ WL_ERR(("wpaie set error (%d)\n", err)); ++ return err; ++ } + } + + if (wl_cfgp2p_find_idx(cfg, dev, &bssidx) != BCME_OK) { +@@ -4272,7 +4509,7 @@ + err = wldev_iovar_setbuf_bsscfg(dev, "join", ext_join_params, join_params_size, + cfg->ioctl_buf, WLC_IOCTL_MAXLEN, bssidx, &cfg->ioctl_buf_sync); + +- printk("Connectting with " MACDBG " channel (%d) ssid \"%s\", len (%d)\n\n", ++ printf("Connectting with " MACDBG " channel (%d) ssid \"%s\", len (%d)\n\n", + MAC2STRDBG((u8*)(&ext_join_params->assoc.bssid)), cfg->channel, + ext_join_params->ssid.SSID, ext_join_params->ssid.SSID_len); + +@@ -4335,6 +4572,14 @@ + RETURN_EIO_IF_NOT_UP(cfg); + act = *(bool *) wl_read_prof(cfg, dev, WL_PROF_ACT); + curbssid = wl_read_prof(cfg, dev, WL_PROF_BSSID); ++#ifdef ESCAN_RESULT_PATCH ++ if (wl_get_drv_status(cfg, CONNECTING, dev) && curbssid && ++ (memcmp(curbssid, connect_req_bssid, ETHER_ADDR_LEN) == 0)) { ++ WL_ERR(("Disconnecting from connecting device: " MACDBG "\n", ++ MAC2STRDBG(curbssid))); ++ act = true; ++ } ++#endif /* ESCAN_RESULT_PATCH */ + if (act) { + /* + * Cancel ongoing scan to sync up with sme state machine of cfg80211. +@@ -4574,7 +4819,7 @@ + } + + int +-wl_cfg80211_enable_roam_offload(struct net_device *dev, bool enable) ++wl_cfg80211_enable_roam_offload(struct net_device *dev, int enable) + { + int err; + wl_eventmsg_buf_t ev_buf; +@@ -4583,7 +4828,7 @@ + /* roam offload is only for the primary device */ + return -1; + } +- err = wldev_iovar_setint(dev, "roam_offload", (int)enable); ++ err = wldev_iovar_setint(dev, "roam_offload", enable); + if (err) + return err; + +@@ -4863,13 +5108,9 @@ + return err; + } + +-// terence 20130703: Fix for wrong group_capab (timing issue) +-int p2p_disconnected = 0; +-struct ether_addr p2p_disconnected_bssid; +- + #if defined(RSSIAVG) + static wl_rssi_cache_ctrl_t g_rssi_cache_ctrl; +-static wl_rssi_cache_ctrl_t g_rssi2_cache_ctrl; ++static wl_rssi_cache_ctrl_t g_connected_rssi_cache_ctrl; + #endif + #if defined(BSSCACHE) + static wl_bss_cache_ctrl_t g_bss_cache_ctrl; +@@ -4879,8 +5120,12 @@ + wl_cfg80211_config_default_mgmt_key(struct wiphy *wiphy, + struct net_device *dev, u8 key_idx) + { ++#ifdef MFP ++ return 0; ++#else + WL_INFORM(("Not supported\n")); + return -EOPNOTSUPP; ++#endif /* MFP */ + } + + static s32 +@@ -4997,17 +5242,21 @@ + } + rssi = dtoh32(scb_val.val); + #if defined(RSSIAVG) +- err = wl_update_connected_rssi_cache(dev, &g_rssi2_cache_ctrl, &rssi); ++ err = wl_update_connected_rssi_cache(dev, &g_connected_rssi_cache_ctrl, &rssi); + if (err) { + WL_ERR(("Could not get rssi (%d)\n", err)); + goto get_station_err; + } +- wl_delete_dirty_rssi_cache(&g_rssi2_cache_ctrl); +- wl_reset_rssi_cache(&g_rssi2_cache_ctrl); ++ wl_delete_dirty_rssi_cache(&g_connected_rssi_cache_ctrl); ++ wl_reset_rssi_cache(&g_connected_rssi_cache_ctrl); + #endif + #if defined(RSSIOFFSET) + rssi = wl_update_rssi_offset(dev, rssi); + #endif ++#if !defined(RSSIAVG) && !defined(RSSIOFFSET) ++ // terence 20150419: limit the max. rssi to -2 or the bss will be filtered out in android OS ++ rssi = MIN(rssi, RSSI_MAXVAL); ++#endif + sinfo->filled |= STATION_INFO_SIGNAL; + sinfo->signal = rssi; + WL_DBG(("RSSI %d dBm\n", rssi)); +@@ -5051,6 +5300,7 @@ + s32 err = 0; + struct bcm_cfg80211 *cfg = wiphy_priv(wiphy); + struct net_info *_net_info = wl_get_netinfo_by_netdev(cfg, dev); ++ dhd_pub_t *dhd = (dhd_pub_t *)(cfg->pub); + + RETURN_EIO_IF_NOT_UP(cfg); + WL_DBG(("Enter\n")); +@@ -5068,6 +5318,8 @@ + dev->name, _net_info->pm_block)); + pm = PM_OFF; + } ++ if (enabled && dhd_conf_get_pm(dhd) >= 0) ++ pm = dhd_conf_get_pm(dhd); + pm = htod32(pm); + WL_DBG(("%s:power save %s\n", dev->name, (pm ? "enabled" : "disabled"))); + err = wldev_ioctl(dev, WLC_SET_PM, &pm, sizeof(pm), true); +@@ -5483,6 +5735,11 @@ + { + s32 err = 0; + ++#ifdef P2PLISTEN_AP_SAMECHN ++ struct bcm_cfg80211 *cfg = g_bcm_cfg; ++ struct net_device *dev; ++#endif /* P2PLISTEN_AP_SAMECHN */ ++ + #if defined(WL_CFG80211_P2P_DEV_IF) + if (cfgdev->iftype == NL80211_IFTYPE_P2P_DEVICE) { + WL_DBG((" enter ) on P2P dedicated discover interface\n")); +@@ -5490,6 +5747,15 @@ + #else + WL_DBG((" enter ) netdev_ifidx: %d \n", cfgdev->ifindex)); + #endif /* WL_CFG80211_P2P_DEV_IF */ ++ ++#ifdef P2PLISTEN_AP_SAMECHN ++ if (cfg && cfg->p2p_resp_apchn_status) { ++ dev = bcmcfg_to_prmry_ndev(cfg); ++ wl_cfg80211_set_p2p_resp_ap_chn(dev, 0); ++ cfg->p2p_resp_apchn_status = false; ++ WL_DBG(("p2p_resp_apchn_status Turn OFF \n")); ++ } ++#endif /* P2PLISTEN_AP_SAMECHN */ + return err; + } + +@@ -6035,8 +6301,17 @@ + + dev = cfgdev_to_wlc_ndev(cfgdev, cfg); + ++ if (!dev) { ++ WL_ERR(("dev is NULL\n")); ++ return -EINVAL; ++ } ++ + /* set bsscfg idx for iovar (wlan0: P2PAPI_BSSCFG_PRIMARY, p2p: P2PAPI_BSSCFG_DEVICE) */ + if (discover_cfgdev(cfgdev, cfg)) { ++ if (!cfg->p2p_supported || !cfg->p2p) { ++ WL_ERR(("P2P doesn't setup completed yet\n")); ++ return -EINVAL; ++ } + bssidx = wl_to_p2p_bss_bssidx(cfg, P2PAPI_BSSCFG_DEVICE); + } + else { +@@ -6067,6 +6342,10 @@ + if (ieee80211_is_probe_resp(mgmt->frame_control)) { + s32 ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN; + s32 ie_len = len - ie_offset; ++#ifdef P2PONEINT ++ if (dev == wl_to_p2p_bss_ndev(cfg, P2PAPI_BSSCFG_CONNECTION)) ++ dev = bcmcfg_to_prmry_ndev(cfg); ++#endif + if ((dev == bcmcfg_to_prmry_ndev(cfg)) && cfg->p2p) + bssidx = wl_to_p2p_bss_bssidx(cfg, P2PAPI_BSSCFG_DEVICE); + wl_cfgp2p_set_management_ie(cfg, dev, bssidx, +@@ -6300,9 +6579,15 @@ + } + + static s32 ++#if ((LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)) && !defined(WL_COMPAT_WIRELESS)) ++wl_cfg80211_set_channel(struct wiphy *wiphy, struct net_device *dev, ++ struct ieee80211_channel *chan, ++ struct cfg80211_chan_def chandef) ++#else + wl_cfg80211_set_channel(struct wiphy *wiphy, struct net_device *dev, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type) ++#endif /* ((LINUX_VERSION >= VERSION(3, 6, 0) && !WL_COMPAT_WIRELESS) */ + { + s32 _chan; + chanspec_t chspec = 0; +@@ -6320,11 +6605,40 @@ + dhd_pub_t *dhd = (dhd_pub_t *)(cfg->pub); + #endif /* CUSTOM_SET_CPUCORE */ + ++#if ((LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)) && !defined(WL_COMPAT_WIRELESS)) ++ enum nl80211_channel_type channel_type = NL80211_CHAN_HT20; ++#endif /* ((LINUX_VERSION >= VERSION(3, 6, 0) && !WL_COMPAT_WIRELESS) */ ++ ++#ifndef P2PONEINT + dev = ndev_to_wlc_ndev(dev, cfg); ++#endif + _chan = ieee80211_frequency_to_channel(chan->center_freq); +- printk("netdev_ifidx(%d), chan_type(%d) target channel(%d) \n", ++ printf("netdev_ifidx(%d), chan_type(%d) target channel(%d) \n", + dev->ifindex, channel_type, _chan); + ++#ifdef CUSTOM_PLATFORM_NV_TEGRA ++#if ((LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)) && !defined(WL_COMPAT_WIRELESS)) ++ WL_ERR(("chan_width = %d\n", chandef.width)); ++ switch (chandef.width) { ++ case NL80211_CHAN_WIDTH_40: ++ bw = WL_CHANSPEC_BW_40; ++ break; ++ case NL80211_CHAN_WIDTH_80: ++ bw = WL_CHANSPEC_BW_80; ++ break; ++ case NL80211_CHAN_WIDTH_80P80: ++ bw = WL_CHANSPEC_BW_8080; ++ break; ++ case NL80211_CHAN_WIDTH_160: ++ bw = WL_CHANSPEC_BW_160; ++ break; ++ default: ++ bw = WL_CHANSPEC_BW_20; ++ break; ++ } ++ goto set_channel; ++#endif /* ((LINUX_VERSION >= VERSION(3, 8, 0) && !WL_COMPAT_WIRELESS) */ ++#endif /* CUSTOM_PLATFORM_NV_TEGRA */ + + if (chan->band == IEEE80211_BAND_5GHZ) { + param.band = WLC_BAND_5G; +@@ -6459,6 +6773,12 @@ + wpa_suite_mcast_t *mcast; + wpa_suite_ucast_t *ucast; + wpa_suite_auth_key_mgmt_t *mgmt; ++ wpa_pmkid_list_t *pmkid; ++ int cnt = 0; ++#ifdef MFP ++ int mfp = 0; ++ struct bcm_cfg80211 *cfg = g_bcm_cfg; ++#endif /* MFP */ + + u16 suite_count; + u8 rsn_cap[2]; +@@ -6468,7 +6788,7 @@ + goto exit; + + WL_DBG(("Enter \n")); +- len = wpa2ie->len; ++ len = wpa2ie->len - WPA2_VERSION_LEN; + /* check the mcast cipher */ + mcast = (wpa_suite_mcast_t *)&wpa2ie->data[WPA2_VERSION_LEN]; + switch (mcast->type) { +@@ -6529,19 +6849,31 @@ + wsec = (pval | gval | SES_OW_ENABLED); + /* check the AKM */ + mgmt = (wpa_suite_auth_key_mgmt_t *)&ucast->list[suite_count]; +- suite_count = ltoh16_ua(&mgmt->count); +- switch (mgmt->list[0].type) { +- case RSN_AKM_NONE: +- wpa_auth = WPA_AUTH_NONE; +- break; +- case RSN_AKM_UNSPECIFIED: +- wpa_auth = WPA2_AUTH_UNSPECIFIED; +- break; +- case RSN_AKM_PSK: +- wpa_auth = WPA2_AUTH_PSK; +- break; +- default: +- WL_ERR(("No Key Mgmt Info\n")); ++ suite_count = cnt = ltoh16_ua(&mgmt->count); ++ while (cnt--) { ++ switch (mgmt->list[cnt].type) { ++ case RSN_AKM_NONE: ++ wpa_auth = WPA_AUTH_NONE; ++ break; ++ case RSN_AKM_UNSPECIFIED: ++ wpa_auth = WPA2_AUTH_UNSPECIFIED; ++ break; ++ case RSN_AKM_PSK: ++ wpa_auth = WPA2_AUTH_PSK; ++ break; ++#ifdef MFP ++ case RSN_AKM_MFP_PSK: ++ wpa_auth |= WPA2_AUTH_PSK; ++ wsec |= MFP_SHA256; ++ break; ++ case RSN_AKM_MFP_1X: ++ wpa_auth |= WPA2_AUTH_UNSPECIFIED; ++ wsec |= MFP_SHA256; ++ break; ++#endif /* MFP */ ++ default: ++ WL_ERR(("No Key Mgmt Info\n")); ++ } + } + + if ((len -= (WPA_IE_SUITE_COUNT_LEN + (WPA_SUITE_LEN * suite_count))) >= RSN_CAP_LEN) { +@@ -6554,6 +6886,16 @@ + wme_bss_disable = 1; + } + ++#ifdef MFP ++ if (rsn_cap[0] & RSN_CAP_MFPR) { ++ WL_DBG(("MFP Required \n")); ++ mfp = WL_MFP_REQUIRED; ++ } else if (rsn_cap[0] & RSN_CAP_MFPC) { ++ WL_DBG(("MFP Capable \n")); ++ mfp = WL_MFP_CAPABLE; ++ } ++#endif /* MFP */ ++ + /* set wme_bss_disable to sync RSN Capabilities */ + err = wldev_iovar_setint_bsscfg(dev, "wme_bss_disable", wme_bss_disable, bssidx); + if (err < 0) { +@@ -6564,6 +6906,30 @@ + WL_DBG(("There is no RSN Capabilities. remained len %d\n", len)); + } + ++ if ((len -= RSN_CAP_LEN) >= WPA2_PMKID_COUNT_LEN) { ++ pmkid = (wpa_pmkid_list_t *)((u8 *)&mgmt->list[suite_count] + RSN_CAP_LEN); ++ cnt = ltoh16_ua(&pmkid->count); ++ if (cnt != 0) { ++ WL_ERR(("AP has non-zero PMKID count. Wrong!\n")); ++ return BCME_ERROR; ++ } ++ /* since PMKID cnt is known to be 0 for AP, */ ++ /* so don't bother to send down this info to firmware */ ++ } ++ ++#ifdef MFP ++ if ((len -= WPA2_PMKID_COUNT_LEN) >= RSN_GROUPMANAGE_CIPHER_LEN) { ++ err = wldev_iovar_setbuf_bsscfg(dev, "bip", ++ (void *)((u8 *)&mgmt->list[suite_count] + RSN_CAP_LEN + WPA2_PMKID_COUNT_LEN), ++ RSN_GROUPMANAGE_CIPHER_LEN, ++ cfg->ioctl_buf, WLC_IOCTL_SMLEN, bssidx, &cfg->ioctl_buf_sync); ++ if (err < 0) { ++ WL_ERR(("bip set error %d\n", err)); ++ return BCME_ERROR; ++ } ++ } ++#endif ++ + /* set auth */ + err = wldev_iovar_setint_bsscfg(dev, "auth", auth, bssidx); + if (err < 0) { +@@ -6576,6 +6942,19 @@ + WL_ERR(("wsec error %d\n", err)); + return BCME_ERROR; + } ++ ++#ifdef MFP ++ if (mfp) { ++ /* This needs to go after wsec otherwise the wsec command will ++ * overwrite the values set by MFP ++ */ ++ if ((err = wldev_iovar_setint_bsscfg(dev, "mfp", mfp, bssidx)) < 0) { ++ WL_ERR(("MFP Setting failed. ret = %d \n", err)); ++ return err; ++ } ++ } ++#endif /* MFP */ ++ + /* set upper-layer auth */ + err = wldev_iovar_setint_bsscfg(dev, "wpa_auth", wpa_auth, bssidx); + if (err < 0) { +@@ -7506,7 +7885,7 @@ + sizeof(scb_val_t), true); + if (err < 0) + WL_ERR(("WLC_SCB_DEAUTHENTICATE_FOR_REASON err %d\n", err)); +- printk("Disconnect STA : %s scb_val.val %d\n", ++ printf("Disconnect STA : %s scb_val.val %d\n", + bcm_ether_ntoa((const struct ether_addr *)mac_addr, eabuf), + scb_val.val); + +@@ -7567,7 +7946,9 @@ + else if (dev == cfg->p2p_net) { + /* Group Add request on p2p0 */ + WL_DBG(("Start AP req on P2P iface: GO\n")); ++#ifndef P2PONEINT + dev = bcmcfg_to_prmry_ndev(cfg); ++#endif + dev_role = NL80211_IFTYPE_P2P_GO; + } + #endif /* WL_ENABLE_P2P_IF */ +@@ -7588,7 +7969,7 @@ + #if ((LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)) && !defined(WL_COMPAT_WIRELESS)) + if ((err = wl_cfg80211_set_channel(wiphy, dev, + dev->ieee80211_ptr->preset_chandef.chan, +- NL80211_CHAN_HT20) < 0)) { ++ dev->ieee80211_ptr->preset_chandef) < 0)) { + WL_ERR(("Set channel failed \n")); + goto fail; + } +@@ -7670,7 +8051,9 @@ + #if defined(WL_ENABLE_P2P_IF) + else if (dev == cfg->p2p_net) { + /* Group Add request on p2p0 */ ++#ifndef P2PONEINT + dev = bcmcfg_to_prmry_ndev(cfg); ++#endif + dev_role = NL80211_IFTYPE_P2P_GO; + } + #endif /* WL_ENABLE_P2P_IF */ +@@ -7752,7 +8135,9 @@ + #if defined(WL_ENABLE_P2P_IF) + else if (dev == cfg->p2p_net) { + /* Group Add request on p2p0 */ ++#ifndef P2PONEINT + dev = bcmcfg_to_prmry_ndev(cfg); ++#endif + dev_role = NL80211_IFTYPE_P2P_GO; + } + #endif /* WL_ENABLE_P2P_IF */ +@@ -7829,7 +8214,9 @@ + #if defined(WL_ENABLE_P2P_IF) + else if (dev == cfg->p2p_net) { + /* Group Add request on p2p0 */ ++#ifndef P2PONEINT + dev = bcmcfg_to_prmry_ndev(cfg); ++#endif + dev_role = NL80211_IFTYPE_P2P_GO; + } + #endif /* WL_ENABLE_P2P_IF */ +@@ -8312,6 +8699,7 @@ + .mgmt_tx_cancel_wait = wl_cfg80211_mgmt_tx_cancel_wait, + #endif /* WL_SUPPORT_BACKPORTED_KPATCHES || KERNEL_VERSION >= (3,2,0) */ + #if (LINUX_VERSION_CODE > KERNEL_VERSION(3, 2, 0)) || defined(WL_COMPAT_WIRELESS) ++ .tdls_mgmt = wl_cfg80211_tdls_mgmt, + .tdls_oper = wl_cfg80211_tdls_oper, + #endif /* LINUX_VERSION > VERSION(3, 2, 0) || WL_COMPAT_WIRELESS */ + #ifdef WL_SUPPORT_ACS +@@ -8388,7 +8776,7 @@ + #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) */ + #endif /* CONFIG_PM */ + +-static s32 wl_setup_wiphy(struct wireless_dev *wdev, struct device *sdiofunc_dev, void *context) ++static s32 wl_setup_wiphy(struct wireless_dev *wdev, struct device *sdiofunc_dev, dhd_pub_t *context) + { + s32 err = 0; + #if 1 && (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) || defined(WL_COMPAT_WIRELESS)) +@@ -8400,7 +8788,7 @@ + err = -ENODEV; + return err; + } +-#endif ++#endif + + wdev->wiphy = + wiphy_new(&wl_cfg80211_ops, sizeof(struct bcm_cfg80211)); +@@ -8501,7 +8889,7 @@ + wdev->wiphy->flags |= WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD; + wdev->wiphy->probe_resp_offload = 0; + } +-#endif ++#endif + #endif /* WL_SUPPORT_BACKPORTED_KPATCHES) || (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)) */ + + #ifdef CONFIG_CFG80211_INTERNAL_REGDB +@@ -8593,69 +8981,82 @@ + + bss_list = cfg->bss_list; + +-#if defined(BSSCACHE) ++ /* Free cache in p2p scanning*/ + if (p2p_is_on(cfg) && p2p_scan(cfg)) { + #if defined(RSSIAVG) + wl_free_rssi_cache(&g_rssi_cache_ctrl); + #endif ++#if defined(BSSCACHE) + wl_free_bss_cache(&g_bss_cache_ctrl); ++#endif + } +- wl_update_bss_cache(&g_bss_cache_ctrl, bss_list); +- wl_delete_dirty_bss_cache(&g_bss_cache_ctrl); +- wl_reset_bss_cache(&g_bss_cache_ctrl); ++ ++ /* Delete disconnected cache */ ++#if defined(BSSCACHE) ++ wl_delete_disconnected_bss_cache(&g_bss_cache_ctrl, (u8*)&cfg->disconnected_bssid); ++#if defined(RSSIAVG) ++ wl_delete_disconnected_rssi_cache(&g_rssi_cache_ctrl, (u8*)&cfg->disconnected_bssid); ++#endif ++ if (cfg->p2p_disconnected == 0) ++ memset(&cfg->disconnected_bssid, 0, ETHER_ADDR_LEN); + #endif + ++ /* Update cache */ + #if defined(RSSIAVG) +-#if defined(BSSCACHE) +- node = g_bss_cache_ctrl.m_cache_head; +- for (;node;) { +- wl_update_rssi_cache(&g_rssi_cache_ctrl, &node->results); +- node = node->next; +- } +-#else + wl_update_rssi_cache(&g_rssi_cache_ctrl, bss_list); +-#endif + if (!in_atomic()) + wl_update_connected_rssi_cache(ndev, &g_rssi_cache_ctrl, &rssi); ++#endif ++#if defined(BSSCACHE) ++ wl_update_bss_cache(&g_bss_cache_ctrl, ++#if defined(RSSIAVG) ++ &g_rssi_cache_ctrl, ++#endif ++ bss_list); ++#endif ++ ++ /* delete dirty cache */ ++#if defined(RSSIAVG) + wl_delete_dirty_rssi_cache(&g_rssi_cache_ctrl); + wl_reset_rssi_cache(&g_rssi_cache_ctrl); + #endif ++#if defined(BSSCACHE) ++ wl_delete_dirty_bss_cache(&g_bss_cache_ctrl); ++ wl_reset_bss_cache(&g_bss_cache_ctrl); ++#endif + + #if defined(BSSCACHE) +- if (p2p_disconnected > 0) { ++ if (cfg->p2p_disconnected > 0) { + // terence 20130703: Fix for wrong group_capab (timing issue) +- wl_delete_disconnected_bss_cache(&g_bss_cache_ctrl, (u8*)&p2p_disconnected_bssid); ++ wl_delete_disconnected_bss_cache(&g_bss_cache_ctrl, (u8*)&cfg->disconnected_bssid); + #if defined(RSSIAVG) +- wl_delete_disconnected_rssi_cache(&g_rssi_cache_ctrl, (u8*)&p2p_disconnected_bssid); ++ wl_delete_disconnected_rssi_cache(&g_rssi_cache_ctrl, (u8*)&cfg->disconnected_bssid); + #endif + } +- WL_SCAN(("Inform cached AP list\n")); ++ WL_SCAN(("scanned AP count (%d)\n", bss_list->count)); + node = g_bss_cache_ctrl.m_cache_head; + for (i=0; node && idirty > 1) { +- // just inform dirty bss +- bi = node->results.bss_info; +- err = wl_inform_single_bss(cfg, bi, false); +- } ++ bi = node->results.bss_info; ++ err = wl_inform_single_bss(cfg, bi, false); + node = node->next; + } +- bi = NULL; +-#endif +- ++#else + WL_SCAN(("scanned AP count (%d)\n", bss_list->count)); +- + bi = next_bss(bss_list, bi); + for_each_bss(bss_list, bi, i) { +- if (p2p_disconnected > 0 && !memcmp(&bi->BSSID, &p2p_disconnected_bssid, ETHER_ADDR_LEN)) ++ if (cfg->p2p_disconnected > 0 && !memcmp(&bi->BSSID, &cfg->disconnected_bssid, ETHER_ADDR_LEN)) + continue; + err = wl_inform_single_bss(cfg, bi, false); + } ++#endif + +- if (p2p_disconnected > 0) { ++ if (cfg->p2p_disconnected > 0) { + // terence 20130703: Fix for wrong group_capab (timing issue) +- p2p_disconnected++; +- if (p2p_disconnected >= REPEATED_SCAN_RESULT_CNT+1) +- p2p_disconnected = 0; ++ cfg->p2p_disconnected++; ++ if (cfg->p2p_disconnected >= REPEATED_SCAN_RESULT_CNT+1) { ++ cfg->p2p_disconnected = 0; ++ memset(&cfg->disconnected_bssid, 0, ETHER_ADDR_LEN); ++ } + } + + return err; +@@ -8710,6 +9111,10 @@ + #if defined(RSSIOFFSET) + notif_bss_info->rssi = wl_update_rssi_offset(bcmcfg_to_prmry_ndev(cfg), notif_bss_info->rssi); + #endif ++#if !defined(RSSIAVG) && !defined(RSSIOFFSET) ++ // terence 20150419: limit the max. rssi to -2 or the bss will be filtered out in android OS ++ notif_bss_info->rssi = MIN(notif_bss_info->rssi, RSSI_MAXVAL); ++#endif + memcpy(mgmt->bssid, &bi->BSSID, ETHER_ADDR_LEN); + mgmt_type = cfg->active_scan ? + IEEE80211_STYPE_PROBE_RESP : IEEE80211_STYPE_BEACON; +@@ -8741,16 +9146,16 @@ + return -EINVAL; + } + channel = ieee80211_get_channel(wiphy, freq); ++ WL_SCAN(("BSSID %pM, channel %2d, rssi %3d, capa 0x04%x, mgmt_type %d, " ++ "frame_len %d, SSID \"%s\"\n", &bi->BSSID, notif_bss_info->channel, ++ notif_bss_info->rssi, mgmt->u.beacon.capab_info, mgmt_type, ++ notif_bss_info->frame_len, bi->SSID)); + if (unlikely(!channel)) { + WL_ERR(("ieee80211_get_channel error, freq=%d, channel=%d\n", + freq, notif_bss_info->channel)); + kfree(notif_bss_info); + return -EINVAL; + } +- WL_SCAN(("BSSID %pM, channel %d, rssi %d, capa 0x04%x, mgmt_type %d, " +- "frame_len %d, SSID \"%s\"\n", &bi->BSSID, notif_bss_info->channel, +- notif_bss_info->rssi, mgmt->u.beacon.capab_info, mgmt_type, +- notif_bss_info->frame_len, bi->SSID)); + + signal = notif_bss_info->rssi * 100; + if (!mgmt->u.probe_resp.timestamp) { +@@ -8970,25 +9375,34 @@ + isfree = true; + + if (event == WLC_E_ASSOC_IND && reason == DOT11_SC_SUCCESS) { +-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)) ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)) ++ cfg80211_rx_mgmt(ndev, freq, 0, mgmt_frame, len, 0); ++#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)) + cfg80211_rx_mgmt(ndev, freq, 0, mgmt_frame, len, 0, GFP_ATOMIC); +-#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)) || defined(WL_COMPAT_WIRELESS) ++#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)) || \ ++ defined(WL_COMPAT_WIRELESS) + cfg80211_rx_mgmt(ndev, freq, 0, mgmt_frame, len, GFP_ATOMIC); + #else + cfg80211_rx_mgmt(ndev, freq, mgmt_frame, len, GFP_ATOMIC); + #endif /* LINUX_VERSION >= VERSION(3, 12, 0) */ + } else if (event == WLC_E_DISASSOC_IND) { +-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)) ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)) ++ cfg80211_rx_mgmt(ndev, freq, 0, mgmt_frame, len, 0); ++#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)) + cfg80211_rx_mgmt(ndev, freq, 0, mgmt_frame, len, 0, GFP_ATOMIC); +-#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)) || defined(WL_COMPAT_WIRELESS) ++#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)) || \ ++ defined(WL_COMPAT_WIRELESS) + cfg80211_rx_mgmt(ndev, freq, 0, mgmt_frame, len, GFP_ATOMIC); + #else + cfg80211_rx_mgmt(ndev, freq, mgmt_frame, len, GFP_ATOMIC); + #endif /* LINUX_VERSION >= VERSION(3, 12, 0) */ + } else if ((event == WLC_E_DEAUTH_IND) || (event == WLC_E_DEAUTH)) { +-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)) ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)) ++ cfg80211_rx_mgmt(ndev, freq, 0, mgmt_frame, len, 0); ++#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)) + cfg80211_rx_mgmt(ndev, freq, 0, mgmt_frame, len, 0, GFP_ATOMIC); +-#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)) || defined(WL_COMPAT_WIRELESS) ++#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)) || \ ++ defined(WL_COMPAT_WIRELESS) + cfg80211_rx_mgmt(ndev, freq, 0, mgmt_frame, len, GFP_ATOMIC); + #else + cfg80211_rx_mgmt(ndev, freq, mgmt_frame, len, GFP_ATOMIC); +@@ -9011,13 +9425,13 @@ + } + sinfo.assoc_req_ies = data; + sinfo.assoc_req_ies_len = len; +- printk("%s: connected device "MACDBG"\n", __FUNCTION__, MAC2STRDBG(e->addr.octet)); ++ printf("%s: connected device "MACDBG"\n", __FUNCTION__, MAC2STRDBG(e->addr.octet)); + cfg80211_new_sta(ndev, e->addr.octet, &sinfo, GFP_ATOMIC); + } else if (event == WLC_E_DISASSOC_IND) { +- printk("%s: disassociated device "MACDBG"\n", __FUNCTION__, MAC2STRDBG(e->addr.octet)); ++ printf("%s: disassociated device "MACDBG"\n", __FUNCTION__, MAC2STRDBG(e->addr.octet)); + cfg80211_del_sta(ndev, e->addr.octet, GFP_ATOMIC); + } else if ((event == WLC_E_DEAUTH_IND) || (event == WLC_E_DEAUTH)) { +- printk("%s: deauthenticated device "MACDBG"\n", __FUNCTION__, MAC2STRDBG(e->addr.octet)); ++ printf("%s: deauthenticated device "MACDBG"\n", __FUNCTION__, MAC2STRDBG(e->addr.octet)); + cfg80211_del_sta(ndev, e->addr.octet, GFP_ATOMIC); + } + #endif /* LINUX_VERSION < VERSION(3,2,0) && !WL_CFG80211_STA_EVENT && !WL_COMPAT_WIRELESS */ +@@ -9054,6 +9468,12 @@ + u16 flags = ntoh16(e->flags); + u32 status = ntoh32(e->status); + bool active; ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0) ++ struct ieee80211_channel *channel = NULL; ++ struct wiphy *wiphy = bcmcfg_to_wiphy(cfg); ++ u32 chanspec, chan; ++ u32 freq, band; ++#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0) */ + + if (event == WLC_E_JOIN) { + WL_DBG(("joined in IBSS network\n")); +@@ -9063,6 +9483,17 @@ + } + if (event == WLC_E_JOIN || event == WLC_E_START || + (event == WLC_E_LINK && (flags == WLC_EVENT_MSG_LINK))) { ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0) ++ err = wldev_iovar_getint(ndev, "chanspec", (s32 *)&chanspec); ++ if (unlikely(err)) { ++ WL_ERR(("Could not get chanspec %d\n", err)); ++ return err; ++ } ++ chan = wf_chspec_ctlchan(wl_chspec_driver_to_host(chanspec)); ++ band = (chan <= CH_MAX_2G_CHANNEL) ? IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; ++ freq = ieee80211_channel_to_frequency(chan, band); ++ channel = ieee80211_get_channel(wiphy, freq); ++#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0) */ + if (wl_get_drv_status(cfg, CONNECTED, ndev)) { + /* ROAM or Redundant */ + u8 *cur_bssid = wl_read_prof(cfg, ndev, WL_PROF_BSSID); +@@ -9076,7 +9507,11 @@ + wl_get_assoc_ies(cfg, ndev); + wl_update_prof(cfg, ndev, NULL, (void *)&e->addr, WL_PROF_BSSID); + wl_update_bss_info(cfg, ndev, false); ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0) ++ cfg80211_ibss_joined(ndev, (s8 *)&e->addr, channel, GFP_KERNEL); ++#else + cfg80211_ibss_joined(ndev, (s8 *)&e->addr, GFP_KERNEL); ++#endif + } + else { + /* New connection */ +@@ -9085,7 +9520,11 @@ + wl_get_assoc_ies(cfg, ndev); + wl_update_prof(cfg, ndev, NULL, (void *)&e->addr, WL_PROF_BSSID); + wl_update_bss_info(cfg, ndev, false); ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0) ++ cfg80211_ibss_joined(ndev, (s8 *)&e->addr, channel, GFP_KERNEL); ++#else + cfg80211_ibss_joined(ndev, (s8 *)&e->addr, GFP_KERNEL); ++#endif + wl_set_drv_status(cfg, CONNECTED, ndev); + active = true; + wl_update_prof(cfg, ndev, NULL, (void *)&active, WL_PROF_ACT); +@@ -9131,19 +9570,27 @@ + wl_link_up(cfg); + act = true; + if (!wl_get_drv_status(cfg, DISCONNECTING, ndev)) { +- printk("wl_bss_connect_done succeeded with " MACDBG "\n", ++ printf("wl_bss_connect_done succeeded with " MACDBG "\n", + MAC2STRDBG((u8*)(&e->addr))); + wl_bss_connect_done(cfg, ndev, e, data, true); +- dhd_conf_set_phyoclscdenable((dhd_pub_t *)cfg->pub); ++ dhd_conf_set_fw_string_cmd(cfg->pub, "phy_oclscdenable", cfg->pub->conf->phy_oclscdenable, 0, FALSE); + WL_DBG(("joined in BSS network \"%s\"\n", + ((struct wlc_ssid *) + wl_read_prof(cfg, ndev, WL_PROF_SSID))->SSID)); + } + wl_update_prof(cfg, ndev, e, &act, WL_PROF_ACT); + wl_update_prof(cfg, ndev, NULL, (void *)&e->addr, WL_PROF_BSSID); +- dhd_conf_set_wme((dhd_pub_t *)cfg->pub); ++ dhd_conf_set_wme(cfg->pub); + + } else if (wl_is_linkdown(cfg, e)) { ++#ifdef P2PLISTEN_AP_SAMECHN ++ if (ndev == bcmcfg_to_prmry_ndev(cfg)) { ++ wl_cfg80211_set_p2p_resp_ap_chn(ndev, 0); ++ cfg->p2p_resp_apchn_status = false; ++ WL_DBG(("p2p_resp_apchn_status Turn OFF \n")); ++ } ++#endif /* P2PLISTEN_AP_SAMECHN */ ++ + if (cfg->scan_request) + wl_notify_escan_complete(cfg, ndev, true, true); + if (wl_get_drv_status(cfg, CONNECTED, ndev)) { +@@ -9155,7 +9602,7 @@ + /* WLAN_REASON_UNSPECIFIED is used for hang up event in Android */ + reason = (reason == WLAN_REASON_UNSPECIFIED)? 0 : reason; + +- printk("link down if %s may call cfg80211_disconnected. " ++ printf("link down if %s may call cfg80211_disconnected. " + "event : %d, reason=%d from " MACDBG "\n", + ndev->name, event, ntoh32(e->reason), + MAC2STRDBG((u8*)(&e->addr))); +@@ -9168,9 +9615,9 @@ + } + if (!memcmp(ndev->name, WL_P2P_INTERFACE_PREFIX, strlen(WL_P2P_INTERFACE_PREFIX))) { + // terence 20130703: Fix for wrong group_capab (timing issue) +- p2p_disconnected = 1; +- memcpy(&p2p_disconnected_bssid, curbssid, ETHER_ADDR_LEN); ++ cfg->p2p_disconnected = 1; + } ++ memcpy(&cfg->disconnected_bssid, curbssid, ETHER_ADDR_LEN); + wl_clr_drv_status(cfg, CONNECTED, ndev); + if (! wl_get_drv_status(cfg, DISCONNECTING, ndev)) { + /* To make sure disconnect, explictly send dissassoc +@@ -9192,7 +9639,7 @@ + } + } + else if (wl_get_drv_status(cfg, CONNECTING, ndev)) { +- printk("link down, during connecting\n"); ++ printf("link down, during connecting\n"); + #ifdef ESCAN_RESULT_PATCH + if ((memcmp(connect_req_bssid, broad_bssid, ETHER_ADDR_LEN) == 0) || + (memcmp(&e->addr, broad_bssid, ETHER_ADDR_LEN) == 0) || +@@ -9208,7 +9655,7 @@ + complete(&cfg->iface_disable); + + } else if (wl_is_nonetwork(cfg, e)) { +- printk("connect failed event=%d e->status %d e->reason %d \n", ++ printf("connect failed event=%d e->status %d e->reason %d \n", + event, (int)ntoh32(e->status), (int)ntoh32(e->reason)); + /* Clean up any pending scan request */ + if (cfg->scan_request) +@@ -9313,7 +9760,7 @@ + act = true; + wl_update_prof(cfg, ndev, e, &act, WL_PROF_ACT); + wl_update_prof(cfg, ndev, NULL, (void *)&e->addr, WL_PROF_BSSID); +- dhd_conf_set_wme((dhd_pub_t *)cfg->pub); ++ dhd_conf_set_wme(cfg->pub); + } + return err; + } +@@ -9434,14 +9881,13 @@ + s32 err = 0; + struct wiphy *wiphy; + u32 channel; ++ struct ieee80211_channel *cur_channel; ++ u32 freq, band; + + wiphy = bcmcfg_to_wiphy(cfg); + + ssid = (struct wlc_ssid *)wl_read_prof(cfg, ndev, WL_PROF_SSID); + curbssid = wl_read_prof(cfg, ndev, WL_PROF_BSSID); +- bss = cfg80211_get_bss(wiphy, NULL, curbssid, +- ssid->SSID, ssid->SSID_len, WLAN_CAPABILITY_ESS, +- WLAN_CAPABILITY_ESS); + + mutex_lock(&cfg->usr_sync); + +@@ -9455,6 +9901,17 @@ + bi = (struct wl_bss_info *)(cfg->extra_buf + 4); + channel = wf_chspec_ctlchan(wl_chspec_driver_to_host(bi->chanspec)); + wl_update_prof(cfg, ndev, NULL, &channel, WL_PROF_CHAN); ++#if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 38) && !defined(WL_COMPAT_WIRELESS) ++ freq = ieee80211_channel_to_frequency(channel); ++#else ++ band = (channel <= CH_MAX_2G_CHANNEL) ? IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; ++ freq = ieee80211_channel_to_frequency(channel, band); ++#endif ++ cur_channel = ieee80211_get_channel(wiphy, freq); ++ ++ bss = cfg80211_get_bss(wiphy, cur_channel, curbssid, ++ ssid->SSID, ssid->SSID_len, WLAN_CAPABILITY_ESS, ++ WLAN_CAPABILITY_ESS); + + if (!bss) { + WL_DBG(("Could not find the AP\n")); +@@ -9561,9 +10018,9 @@ + memcpy(cfg->fbt_key, data, FBT_KEYLEN); + } + #endif /* WLFBT */ +- printk("wl_bss_roaming_done succeeded to " MACDBG "\n", ++ printf("wl_bss_roaming_done succeeded to " MACDBG "\n", + MAC2STRDBG((u8*)(&e->addr))); +- dhd_conf_set_wme((dhd_pub_t *)cfg->pub); ++ dhd_conf_set_wme(cfg->pub); + + cfg80211_roamed(ndev, + #if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) || defined(WL_COMPAT_WIRELESS) +@@ -9587,7 +10044,7 @@ + struct wl_security *sec = wl_read_prof(cfg, ndev, WL_PROF_SEC); + #if defined(CUSTOM_SET_CPUCORE) + dhd_pub_t *dhd = (dhd_pub_t *)(cfg->pub); +-#endif ++#endif + s32 err = 0; + u8 *curbssid = wl_read_prof(cfg, ndev, WL_PROF_BSSID); + if (!sec) { +@@ -9656,7 +10113,7 @@ + GFP_KERNEL); + if (completed) { + WL_INFORM(("Report connect result - connection succeeded\n")); +- dhd_conf_set_wme((dhd_pub_t *)cfg->pub); ++ dhd_conf_set_wme(cfg->pub); + } else + WL_ERR(("Report connect result - connection failed\n")); + } +@@ -9912,6 +10369,9 @@ + memset(&bssid, 0, ETHER_ADDR_LEN); + + ndev = cfgdev_to_wlc_ndev(cfgdev, cfg); ++#ifdef P2PONEINT ++ WL_DBG((" device name is ndev %s \n", ndev->name)); ++#endif + + if (channel <= CH_MAX_2G_CHANNEL) + band = wiphy->bands[IEEE80211_BAND_2GHZ]; +@@ -9976,6 +10436,21 @@ + } + } + (void) sd_act_frm; ++#ifdef WLTDLS ++ } else if (mgmt_frame[DOT11_MGMT_HDR_LEN] == TDLS_AF_CATEGORY) { ++ WL_DBG((" TDLS Action Frame Received type = %d \n", ++ mgmt_frame[DOT11_MGMT_HDR_LEN + 1])); ++ ++ if (mgmt_frame[DOT11_MGMT_HDR_LEN + 1] == TDLS_ACTION_SETUP_RESP) { ++ cfg->tdls_mgmt_frame = mgmt_frame; ++ cfg->tdls_mgmt_frame_len = mgmt_frame_len; ++ cfg->tdls_mgmt_freq = freq; ++ return 0; ++ } ++ ++ } else if (mgmt_frame[DOT11_MGMT_HDR_LEN] == TDLS_VENDOR_SPECIFIC) { ++ WL_DBG((" TDLS Vendor Specific Received type \n")); ++#endif + } else { + + if (cfg->next_af_subtype != P2P_PAF_SUBTYPE_INVALID) { +@@ -10079,7 +10554,17 @@ + } + } + +-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)) ++#ifdef P2PONEINT ++ if (ndev == cfg->p2p_net && ndev->ieee80211_ptr->iftype == NL80211_IFTYPE_P2P_GO) { ++ ndev = bcmcfg_to_prmry_ndev(cfg); ++ cfgdev = ndev_to_cfgdev(ndev); ++ } ++ WL_DBG((" device name is ndev %s \n", ndev->name)); ++#endif ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)) ++ retval = cfg80211_rx_mgmt(cfgdev, freq, 0, mgmt_frame, mgmt_frame_len, 0); ++#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)) + retval = cfg80211_rx_mgmt(cfgdev, freq, 0, mgmt_frame, mgmt_frame_len, 0, GFP_ATOMIC); + #elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)) || \ + defined(WL_COMPAT_WIRELESS) +@@ -10422,6 +10907,12 @@ + kfree(cfg->ap_info); + cfg->ap_info = NULL; + } ++#ifdef WLTDLS ++ if (cfg->tdls_mgmt_frame) { ++ kfree(cfg->tdls_mgmt_frame); ++ cfg->tdls_mgmt_frame = NULL; ++ } ++#endif /* WLTDLS */ + } + + static s32 wl_create_event_handler(struct bcm_cfg80211 *cfg) +@@ -10471,9 +10962,14 @@ + static s32 + wl_cfg80211_netdev_notifier_call(struct notifier_block * nb, + unsigned long state, +- void *ndev) ++ void *ptr) + { +- struct net_device *dev = ndev; ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 11, 0)) ++ struct net_device *dev = ptr; ++#else ++ // terence 20150701: fix for p2p connection issue ++ struct net_device *dev = netdev_notifier_info_to_dev(ptr); ++#endif + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct bcm_cfg80211 *cfg = g_bcm_cfg; + +@@ -10521,7 +11017,7 @@ + + case NETDEV_UNREGISTER: + /* after calling list_del_rcu(&wdev->list) */ +- wl_dealloc_netinfo(cfg, ndev); ++ wl_dealloc_netinfo(cfg, dev); + break; + case NETDEV_GOING_DOWN: + /* At NETDEV_DOWN state, wdev_cleanup_work work will be called. +@@ -10544,7 +11040,12 @@ + */ + static bool wl_cfg80211_netdev_notifier_registered = FALSE; + +-static void wl_cfg80211_scan_abort(struct bcm_cfg80211 *cfg) ++void ++#ifdef P2PONEINT ++wl_cfg80211_scan_abort(struct bcm_cfg80211 *cfg) ++#else ++wl_cfg80211_scan_abort(struct bcm_cfg80211 *cfg) ++#endif + { + wl_scan_params_t *params = NULL; + s32 params_size = 0; +@@ -10638,6 +11139,64 @@ + return err; + } + ++#ifdef ESCAN_BUF_OVERFLOW_MGMT ++static void ++wl_cfg80211_find_removal_candidate(wl_bss_info_t *bss, removal_element_t *candidate) ++{ ++ int idx; ++ for (idx = 0; idx < BUF_OVERFLOW_MGMT_COUNT; idx++) { ++ int len = BUF_OVERFLOW_MGMT_COUNT - idx - 1; ++ if (bss->RSSI < candidate[idx].RSSI) { ++ if (len) ++ memcpy(&candidate[idx + 1], &candidate[idx], ++ sizeof(removal_element_t) * len); ++ candidate[idx].RSSI = bss->RSSI; ++ candidate[idx].length = bss->length; ++ memcpy(&candidate[idx].BSSID, &bss->BSSID, ETHER_ADDR_LEN); ++ return; ++ } ++ } ++} ++ ++static void ++wl_cfg80211_remove_lowRSSI_info(wl_scan_results_t *list, removal_element_t *candidate, ++ wl_bss_info_t *bi) ++{ ++ int idx1, idx2; ++ int total_delete_len = 0; ++ for (idx1 = 0; idx1 < BUF_OVERFLOW_MGMT_COUNT; idx1++) { ++ int cur_len = WL_SCAN_RESULTS_FIXED_SIZE; ++ wl_bss_info_t *bss = NULL; ++ if (candidate[idx1].RSSI >= bi->RSSI) ++ continue; ++ for (idx2 = 0; idx2 < list->count; idx2++) { ++ bss = bss ? (wl_bss_info_t *)((uintptr)bss + dtoh32(bss->length)) : ++ list->bss_info; ++ if (!bcmp(&candidate[idx1].BSSID, &bss->BSSID, ETHER_ADDR_LEN) && ++ candidate[idx1].RSSI == bss->RSSI && ++ candidate[idx1].length == dtoh32(bss->length)) { ++ u32 delete_len = dtoh32(bss->length); ++ WL_DBG(("delete scan info of " MACDBG " to add new AP\n", ++ MAC2STRDBG(bss->BSSID.octet))); ++ if (idx2 < list->count -1) { ++ memmove((u8 *)bss, (u8 *)bss + delete_len, ++ list->buflen - cur_len - delete_len); ++ } ++ list->buflen -= delete_len; ++ list->count--; ++ total_delete_len += delete_len; ++ /* if delete_len is greater than or equal to result length */ ++ if (total_delete_len >= bi->length) { ++ return; ++ } ++ break; ++ } ++ cur_len += dtoh32(bss->length); ++ } ++ } ++} ++#endif /* ESCAN_BUF_OVERFLOW_MGMT */ ++ + static s32 wl_escan_handler(struct bcm_cfg80211 *cfg, bcm_struct_cfgdev *cfgdev, + const wl_event_msg_t *e, void *data) + { +@@ -10709,7 +11268,7 @@ + WL_ERR(("No valid band\n")); + goto exit; + } +- if (!dhd_conf_match_channel((dhd_pub_t *)cfg->pub, channel)) ++ if (!dhd_conf_match_channel(cfg->pub, channel)) + goto exit; + /* ----- terence 20130524: skip invalid bss */ + +@@ -10747,6 +11306,13 @@ + + } else { + int cur_len = WL_SCAN_RESULTS_FIXED_SIZE; ++#ifdef ESCAN_BUF_OVERFLOW_MGMT ++ removal_element_t candidate[BUF_OVERFLOW_MGMT_COUNT]; ++ int remove_lower_rssi = FALSE; ++ ++ bzero(candidate, sizeof(removal_element_t)*BUF_OVERFLOW_MGMT_COUNT); ++#endif /* ESCAN_BUF_OVERFLOW_MGMT */ ++ + list = wl_escan_get_buf(cfg, FALSE); + if (scan_req_match(cfg)) { + #ifdef WL_HOST_BAND_MGMT +@@ -10778,11 +11344,24 @@ + } + #endif /* WL_HOST_BAND_MGMT */ + } ++#ifdef ESCAN_BUF_OVERFLOW_MGMT ++ if (bi_length > ESCAN_BUF_SIZE - list->buflen) ++ remove_lower_rssi = TRUE; ++#endif /* ESCAN_BUF_OVERFLOW_MGMT */ ++ + WL_SCAN(("%s("MACDBG") RSSI %d flags 0x%x length %d\n", bi->SSID, + MAC2STRDBG(bi->BSSID.octet), bi->RSSI, bi->flags, bi->length)); + for (i = 0; i < list->count; i++) { + bss = bss ? (wl_bss_info_t *)((uintptr)bss + dtoh32(bss->length)) + : list->bss_info; ++#ifdef ESCAN_BUF_OVERFLOW_MGMT ++ WL_TRACE(("%s("MACDBG"), i=%d bss: RSSI %d list->count %d\n", ++ bss->SSID, MAC2STRDBG(bss->BSSID.octet), ++ i, bss->RSSI, list->count)); ++ ++ if (remove_lower_rssi) ++ wl_cfg80211_find_removal_candidate(bss, candidate); ++#endif /* ESCAN_BUF_OVERFLOW_MGMT */ + + if (!bcmp(&bi->BSSID, &bss->BSSID, ETHER_ADDR_LEN) && + (CHSPEC_BAND(wl_chspec_driver_to_host(bi->chanspec)) +@@ -10861,8 +11440,17 @@ + cur_len += dtoh32(bss->length); + } + if (bi_length > ESCAN_BUF_SIZE - list->buflen) { ++#ifdef ESCAN_BUF_OVERFLOW_MGMT ++ wl_cfg80211_remove_lowRSSI_info(list, candidate, bi); ++ if (bi_length > ESCAN_BUF_SIZE - list->buflen) { ++ WL_DBG(("RSSI(" MACDBG ") is too low(%d) to add Buffer\n", ++ MAC2STRDBG(bi->BSSID.octet), bi->RSSI)); ++ goto exit; ++ } ++#else + WL_ERR(("Buffer is too small: ignoring\n")); + goto exit; ++#endif /* ESCAN_BUF_OVERFLOW_MGMT */ + } + if (strlen(bi->SSID) == 0) { // terence: fix for hidden SSID + WL_SCAN(("Skip hidden SSID %pM\n", &bi->BSSID)); +@@ -10878,6 +11466,17 @@ + + } + else if (status == WLC_E_STATUS_SUCCESS) { ++#if defined(CUSTOM_PLATFORM_NV_TEGRA) ++#if defined(P2P_DISCOVERY_WAR) ++ if (cfg->p2p_net && cfg->scan_request && ++ cfg->scan_request->dev == cfg->p2p_net && ++ !cfg->p2p->vif_created) { ++ if (wldev_iovar_setint(wl_to_prmry_ndev(cfg), "mpc", 1) < 0) { ++ WL_ERR(("mpc enabling back failed\n")); ++ } ++ } ++#endif /* defined(P2P_DISCOVERY_WAR) */ ++#endif /* defined(CUSTOM_PLATFORM_NV_TEGRA) */ + cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE; + wl_escan_print_sync_id(status, cfg->escan_info.cur_sync_id, + escan_result->sync_id); +@@ -10901,6 +11500,17 @@ + wl_escan_increment_sync_id(cfg, SCAN_BUF_NEXT); + } + else if (status == WLC_E_STATUS_ABORT) { ++#if defined(CUSTOM_PLATFORM_NV_TEGRA) ++#if defined(P2P_DISCOVERY_WAR) ++ if (cfg->p2p_net && cfg->scan_request && ++ cfg->scan_request->dev == cfg->p2p_net && ++ !cfg->p2p->vif_created) { ++ if (wldev_iovar_setint(wl_to_prmry_ndev(cfg), "mpc", 1) < 0) { ++ WL_ERR(("mpc enabling back failed\n")); ++ } ++ } ++#endif /* defined(P2P_DISCOVERY_WAR) */ ++#endif /* defined(CUSTOM_PLATFORM_NV_TEGRA) */ + cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE; + wl_escan_print_sync_id(status, escan_result->sync_id, + cfg->escan_info.cur_sync_id); +@@ -11030,7 +11640,7 @@ + } + } + } +- printk("%s concurrency is enabled\n", cfg->vsdb_mode ? "Multi Channel" : "Same Channel"); ++ printf("%s concurrency is enabled\n", cfg->vsdb_mode ? "Multi Channel" : "Same Channel"); + return; + } + +@@ -11043,6 +11653,7 @@ + u32 chan = 0; + struct net_info *iter, *next; + struct net_device *primary_dev = bcmcfg_to_prmry_ndev(cfg); ++ dhd_pub_t *dhd = (dhd_pub_t *)(cfg->pub); + WL_DBG(("Enter state %d set %d _net_info->pm_restore %d iface %s\n", + state, set, _net_info->pm_restore, _net_info->ndev->name)); + +@@ -11066,6 +11677,8 @@ + */ + if (!_net_info->pm_block && (mode == WL_MODE_BSS)) { + _net_info->pm = PM_FAST; ++ if (dhd_conf_get_pm(dhd) >= 0) ++ _net_info->pm = dhd_conf_get_pm(dhd); + _net_info->pm_restore = true; + } + pm = PM_OFF; +@@ -11085,6 +11698,8 @@ + for_each_ndev(cfg, iter, next) { + if (!wl_get_drv_status(cfg, CONNECTED, iter->ndev)) + continue; ++ if (pm != PM_OFF && dhd_conf_get_pm(dhd) >= 0) ++ pm = dhd_conf_get_pm(dhd); + if ((err = wldev_ioctl(iter->ndev, WLC_SET_PM, &pm, + sizeof(pm), true)) != 0) { + if (err == -ENODEV) +@@ -11122,6 +11737,8 @@ + for_each_ndev(cfg, iter, next) { + if (!wl_get_drv_status(cfg, CONNECTED, iter->ndev)) + continue; ++ if (pm != PM_OFF && dhd_conf_get_pm(dhd) >= 0) ++ pm = dhd_conf_get_pm(dhd); + if ((err = wldev_ioctl(iter->ndev, WLC_SET_PM, &pm, + sizeof(pm), true)) != 0) { + if (err == -ENODEV) +@@ -11159,6 +11776,8 @@ + if (iter->pm_restore && iter->pm) { + WL_DBG(("%s:restoring power save %s\n", + iter->ndev->name, (iter->pm ? "enabled" : "disabled"))); ++ if (iter->pm != PM_OFF && dhd_conf_get_pm(dhd) >= 0) ++ iter->pm = dhd_conf_get_pm(dhd); + err = wldev_ioctl(iter->ndev, + WLC_SET_PM, &iter->pm, sizeof(iter->pm), true); + if (unlikely(err)) { +@@ -11211,7 +11830,7 @@ + cfg->vsdb_mode = false; + #if defined(BCMSDIO) + cfg->wlfc_on = false; +-#endif ++#endif + cfg->roamoff_on_concurrent = true; + cfg->disable_roam_event = false; + /* register interested state */ +@@ -11255,7 +11874,12 @@ + } + } + +-#if defined(WL_ENABLE_P2P_IF) || defined(WL_NEWCFG_PRIVCMD_SUPPORT) ++#if defined(CUSTOMER_HW20) && defined(WLANAUDIO) ++struct net_device *wl0dot1_dev; ++#endif /* CUSTOMER_HW20 && WLANAUDIO */ ++ ++#if defined(WL_ENABLE_P2P_IF) || defined(WL_NEWCFG_PRIVCMD_SUPPORT) || \ ++ defined(P2PONEINT) + static s32 wl_cfg80211_attach_p2p(void) + { + struct bcm_cfg80211 *cfg = g_bcm_cfg; +@@ -11267,9 +11891,15 @@ + return -ENODEV; + } + ++#if defined(CUSTOMER_HW20) && defined(WLANAUDIO) ++ wl0dot1_dev = cfg->p2p_net; ++#endif /* CUSTOMER_HW20 && WLANAUDIO */ ++ + return 0; + } ++#endif /* WL_ENABLE_P2P_IF || WL_NEWCFG_PRIVCMD_SUPPORT || P2PONEINT */ + ++#if defined(WL_ENABLE_P2P_IF) || defined(WL_NEWCFG_PRIVCMD_SUPPORT) + static s32 wl_cfg80211_detach_p2p(void) + { + struct bcm_cfg80211 *cfg = g_bcm_cfg; +@@ -11329,12 +11959,23 @@ + if ((err = wl_cfgp2p_init_priv(cfg)) != 0) + goto fail; + +-#if defined(WL_ENABLE_P2P_IF) ++#ifdef P2PONEINT ++ if (!cfg->p2p_net) { ++ cfg->p2p_supported = true; ++ ++ err = wl_cfg80211_attach_p2p(); ++ if (err) ++ goto fail; ++ ++ cfg->p2p_supported = true; ++ } ++#endif ++#if defined(WL_ENABLE_P2P_IF) || defined(P2PONEINT) + if (cfg->p2p_net) { + /* Update MAC addr for p2p0 interface here. */ + memcpy(cfg->p2p_net->dev_addr, ndev->dev_addr, ETH_ALEN); + cfg->p2p_net->dev_addr[0] |= 0x02; +- printk("%s: p2p_dev_addr="MACDBG "\n", ++ printf("%s: p2p_dev_addr="MACDBG "\n", + cfg->p2p_net->name, + MAC2STRDBG(cfg->p2p_net->dev_addr)); + } else { +@@ -11343,7 +11984,9 @@ + return -ENODEV; + } + #endif /* WL_ENABLE_P2P_IF */ ++#ifndef P2PONEINT + cfg->p2p_supported = true; ++#endif + } else if (ret == 0) { + if ((err = wl_cfgp2p_init_priv(cfg)) != 0) + goto fail; +@@ -11359,7 +12002,7 @@ + return err; + } + +-s32 wl_cfg80211_attach(struct net_device *ndev, void *context) ++s32 wl_cfg80211_attach(struct net_device *ndev, dhd_pub_t *context) + { + struct wireless_dev *wdev; + struct bcm_cfg80211 *cfg; +@@ -11434,9 +12077,11 @@ + g_bcm_cfg = cfg; + + #if defined(WL_ENABLE_P2P_IF) || defined(WL_NEWCFG_PRIVCMD_SUPPORT) ++#ifndef P2PONEINT + err = wl_cfg80211_attach_p2p(); + if (err) + goto cfg80211_attach_out; ++#endif + #endif /* WL_ENABLE_P2P_IF || WL_NEWCFG_PRIVCMD_SUPPORT */ + + return err; +@@ -11461,7 +12106,7 @@ + #if defined(COEX_DHCP) + wl_cfg80211_btcoex_deinit(); + cfg->btcoex_info = NULL; +-#endif ++#endif + + wl_setup_rfkill(cfg, FALSE); + #ifdef DEBUGFS_CFG80211 +@@ -11490,7 +12135,7 @@ + wl_free_wdev(cfg); + #if defined(RSSIAVG) + wl_free_rssi_cache(&g_rssi_cache_ctrl); +- wl_free_rssi_cache(&g_rssi2_cache_ctrl); ++ wl_free_rssi_cache(&g_connected_rssi_cache_ctrl); + #endif + #if defined(BSSCACHE) + wl_release_bss_cache_ctrl(&g_bss_cache_ctrl); +@@ -11509,43 +12154,54 @@ + } + } + +-#if defined(WL_ENABLE_P2P_IF) ++#if defined(P2PONEINT) || defined(WL_ENABLE_P2P_IF) + static int wl_is_p2p_event(struct wl_event_q *e) + { +- switch (e->etype) { +- /* We have to seperate out the P2P events received +- * on primary interface so that it can be send up +- * via p2p0 interface. +- */ +- case WLC_E_P2P_PROBREQ_MSG: +- case WLC_E_P2P_DISC_LISTEN_COMPLETE: +- case WLC_E_ACTION_FRAME_RX: +- case WLC_E_ACTION_FRAME_OFF_CHAN_COMPLETE: +- case WLC_E_ACTION_FRAME_COMPLETE: ++ struct bcm_cfg80211 *cfg = g_bcm_cfg; + +- if (e->emsg.ifidx != 0) { +- WL_TRACE(("P2P event(%d) on virtual interface(ifidx:%d)\n", +- e->etype, e->emsg.ifidx)); +- /* We are only bothered about the P2P events received +- * on primary interface. For rest of them return false +- * so that it is sent over the interface corresponding +- * to the ifidx. +- */ +- return FALSE; +- } else { ++ switch (e->etype) { ++ case WLC_E_IF: + WL_TRACE(("P2P event(%d) on interface(ifidx:%d)\n", + e->etype, e->emsg.ifidx)); +- return TRUE; +- } +- break; + +- default: +- WL_TRACE(("NON-P2P event(%d) on interface(ifidx:%d)\n", +- e->etype, e->emsg.ifidx)); +- return FALSE; ++ (void)schedule_timeout(20); ++ ++ if (wl_get_p2p_status(cfg, IF_ADDING) || ++ wl_get_p2p_status(cfg, IF_DELETING) || ++ wl_get_p2p_status(cfg, IF_CHANGING) || ++ wl_get_p2p_status(cfg, IF_CHANGED)) { ++ WL_TRACE(("P2P Event on Primary I/F (ifidx:%d)." ++ " Sent it to p2p0 \n", e->emsg.ifidx)); ++ return TRUE; ++ } else { ++ WL_TRACE(("Event is Not p2p event return False \n")); ++ return FALSE; ++ } ++ ++ case WLC_E_P2P_PROBREQ_MSG: ++ case WLC_E_P2P_DISC_LISTEN_COMPLETE: ++ case WLC_E_ACTION_FRAME_RX: ++ case WLC_E_ACTION_FRAME_OFF_CHAN_COMPLETE: ++ case WLC_E_ACTION_FRAME_COMPLETE: ++ ++ if (e->emsg.ifidx != 0) { ++ WL_TRACE(("P2P event(%d) on virtual interface(ifidx:%d)\n", ++ e->etype, e->emsg.ifidx)); ++ return FALSE; ++ } else { ++ WL_TRACE(("P2P event(%d) on interface(ifidx:%d)\n", ++ e->etype, e->emsg.ifidx)); ++ return TRUE; ++ } ++ break; ++ ++ default: ++ WL_TRACE(("NON-P2P event(%d) on interface(ifidx:%d)\n", ++ e->etype, e->emsg.ifidx)); ++ return FALSE; + } + } +-#endif /* BCMDONGLEHOST && (WL_CFG80211_P2P_DEV_IF || WL_ENABLE_P2P_IF) */ ++#endif + + static s32 wl_event_handler(void *data) + { +@@ -11556,7 +12212,7 @@ + + cfg = (struct bcm_cfg80211 *)tsk->parent; + +- printk("tsk Enter, tsk = 0x%p\n", tsk); ++ printf("tsk Enter, tsk = 0x%p\n", tsk); + + while (down_interruptible (&tsk->sema) == 0) { + SMP_RD_BARRIER_DEPENDS(); +@@ -11569,7 +12225,12 @@ + * interface. + */ + #if defined(WL_CFG80211_P2P_DEV_IF) +- if (WL_IS_P2P_DEV_EVENT(e) && (cfg->p2p_wdev)) { ++#ifdef P2PONEINT ++ if ((wl_is_p2p_event(e) == TRUE) && (cfg->p2p_wdev)) ++#else ++ if (WL_IS_P2P_DEV_EVENT(e) && (cfg->p2p_wdev)) ++#endif ++ { + cfgdev = bcmcfg_to_p2p_wdev(cfg); + } else { + struct net_device *ndev = NULL; +@@ -11577,6 +12238,22 @@ + ndev = dhd_idx2net((struct dhd_pub *)(cfg->pub), e->emsg.ifidx); + if (ndev) + cfgdev = ndev_to_wdev(ndev); ++#ifdef P2PONEINT ++ else if (e->etype == WLC_E_IF) { ++ wl_put_event(e); ++ DHD_OS_WAKE_UNLOCK(cfg->pub); ++ continue; ++ } ++ ++ if (cfgdev == NULL) { ++ if (e->etype == WLC_E_IF) ++ cfgdev = bcmcfg_to_prmry_wdev(cfg); ++ else { ++ cfgdev = ndev_to_wdev(wl_to_p2p_bss_ndev(cfg, ++ P2PAPI_BSSCFG_CONNECTION)); ++ } ++ } ++#endif + } + #elif defined(WL_ENABLE_P2P_IF) + // terence 20150116: fix for p2p connection in kernel 3.4 +@@ -11592,7 +12269,7 @@ + if (!cfgdev) { + #if defined(WL_CFG80211_P2P_DEV_IF) + cfgdev = bcmcfg_to_prmry_wdev(cfg); +-#elif defined(WL_ENABLE_P2P_IF) ++#else + cfgdev = bcmcfg_to_prmry_ndev(cfg); + #endif /* WL_CFG80211_P2P_DEV_IF */ + } +@@ -11605,7 +12282,7 @@ + } + DHD_OS_WAKE_UNLOCK(cfg->pub); + } +- printk("%s: was terminated\n", __FUNCTION__); ++ printf("%s: was terminated\n", __FUNCTION__); + complete_and_exit(&tsk->completed, 0); + return 0; + } +@@ -11955,6 +12632,8 @@ + index = j; + else + index = *n_cnt; ++ if (!dhd_conf_match_channel(cfg->pub, channel)) ++ continue; + if (index < array_size) { + #if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 38) && !defined(WL_COMPAT_WIRELESS) + band_chan_arr[index].center_freq = +@@ -11964,6 +12643,7 @@ + ieee80211_channel_to_frequency(channel, band); + #endif + band_chan_arr[index].hw_value = channel; ++ WL_DBG(("channel = %d\n", channel)); + + if (CHSPEC_IS40(c) && ht40_allowed) { + /* assuming the order is HT20, HT40 Upper, +@@ -12211,7 +12891,7 @@ + #ifdef PROP_TXSTATUS_VSDB + #if defined(BCMSDIO) + dhd_pub_t *dhd = (dhd_pub_t *)(cfg->pub); +-#endif ++#endif + #endif /* PROP_TXSTATUS_VSDB */ + WL_DBG(("In\n")); + /* Delete pm_enable_work */ +@@ -12234,7 +12914,7 @@ + cfg->wlfc_on = false; + } + } +-#endif ++#endif + #endif /* PROP_TXSTATUS_VSDB */ + } + +@@ -12690,6 +13370,21 @@ + + return wl_cfgp2p_set_p2p_ps(cfg, net, buf, len); + } ++#ifdef P2PLISTEN_AP_SAMECHN ++s32 wl_cfg80211_set_p2p_resp_ap_chn(struct net_device *net, s32 enable) ++{ ++ s32 ret = wldev_iovar_setint(net, "p2p_resp_ap_chn", enable); ++ ++ if ((ret == 0) && enable) { ++ /* disable PM for p2p responding on infra AP channel */ ++ s32 pm = PM_OFF; ++ ++ ret = wldev_ioctl(net, WLC_SET_PM, &pm, sizeof(pm), true); ++ } ++ ++ return ret; ++} ++#endif /* P2PLISTEN_AP_SAMECHN */ + + s32 wl_cfg80211_channel_to_freq(u32 channel) + { +@@ -12774,6 +13469,10 @@ + #if defined(RSSIOFFSET) + info.rssi = wl_update_rssi_offset(ndev, info.rssi); + #endif ++#if !defined(RSSIAVG) && !defined(RSSIOFFSET) ++ // terence 20150419: limit the max. rssi to -2 or the bss will be filtered out in android OS ++ info.rssi = MIN(info.rssi, RSSI_MAXVAL); ++#endif + memcpy(info.bssid, &bi->BSSID, ETH_ALEN); + info.ie_len = buflen; + +@@ -13578,12 +14277,36 @@ + #ifdef PCIE_FULL_DONGLE + dhd_tdls_update_peer_info(ndev, TRUE, (uint8 *)&e->addr.octet[0]); + #endif /* PCIE_FULL_DONGLE */ ++ if (cfg->tdls_mgmt_frame) { ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)) ++ cfg80211_rx_mgmt(cfgdev, cfg->tdls_mgmt_freq, 0, ++ cfg->tdls_mgmt_frame, cfg->tdls_mgmt_frame_len, 0); ++#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)) ++ cfg80211_rx_mgmt(cfgdev, cfg->tdls_mgmt_freq, 0, ++ cfg->tdls_mgmt_frame, cfg->tdls_mgmt_frame_len, ++ 0, GFP_ATOMIC); ++#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)) || \ ++ defined(WL_COMPAT_WIRELESS) ++ cfg80211_rx_mgmt(cfgdev, cfg->tdls_mgmt_freq, 0, ++ cfg->tdls_mgmt_frame, cfg->tdls_mgmt_frame_len, ++ GFP_ATOMIC); ++#else ++ cfg80211_rx_mgmt(cfgdev, cfg->tdls_mgmt_freq, ++ cfg->tdls_mgmt_frame, cfg->tdls_mgmt_frame_len, ++ GFP_ATOMIC); ++#endif /* LINUX_VERSION >= VERSION(3, 12, 0) */ ++ } + msg = " TDLS PEER CONNECTED "; + break; + case WLC_E_TDLS_PEER_DISCONNECTED : + #ifdef PCIE_FULL_DONGLE + dhd_tdls_update_peer_info(ndev, FALSE, (uint8 *)&e->addr.octet[0]); + #endif /* PCIE_FULL_DONGLE */ ++ if (cfg->tdls_mgmt_frame) { ++ kfree(cfg->tdls_mgmt_frame); ++ cfg->tdls_mgmt_frame = NULL; ++ cfg->tdls_mgmt_freq = 0; ++ } + msg = "TDLS PEER DISCONNECTED "; + break; + } +@@ -13594,10 +14317,65 @@ + return 0; + + } +-#endif /* WLTDLS */ ++#endif /* WLTDLS */ + + #if (LINUX_VERSION_CODE > KERNEL_VERSION(3, 2, 0)) || defined(WL_COMPAT_WIRELESS) + static s32 ++#if defined(CONFIG_ARCH_MSM) && defined(TDLS_MGMT_VERSION2) ++wl_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, ++ u8 *peer, u8 action_code, u8 dialog_token, u16 status_code, ++ u32 peer_capability, const u8 *data, size_t len) ++#else ++wl_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, ++ u8 *peer, u8 action_code, u8 dialog_token, u16 status_code, const u8 *data, ++ size_t len) ++#endif /* CONFIG_ARCH_MSM && TDLS_MGMT_VERSION2 */ ++{ ++ s32 ret = 0; ++#ifdef WLTDLS ++ struct bcm_cfg80211 *cfg; ++ tdls_wfd_ie_iovar_t info; ++ memset(&info, 0, sizeof(tdls_wfd_ie_iovar_t)); ++ cfg = g_bcm_cfg; ++ ++#if defined(CONFIG_ARCH_MSM) && defined(TDLS_MGMT_VERSION2) ++ /* Some customer platform back ported this feature from kernel 3.15 to kernel 3.10 ++ * and that cuases build error ++ */ ++ BCM_REFERENCE(peer_capability); ++#endif /* CONFIG_ARCH_MSM && TDLS_MGMT_VERSION2 */ ++ ++ switch (action_code) { ++ /* We need to set TDLS Wifi Display IE to firmware ++ * using tdls_wfd_ie iovar ++ */ ++ case WLAN_TDLS_SET_PROBE_WFD_IE: ++ info.mode = TDLS_WFD_PROBE_IE_TX; ++ memcpy(&info.data, data, len); ++ info.length = len; ++ break; ++ case WLAN_TDLS_SET_SETUP_WFD_IE: ++ info.mode = TDLS_WFD_IE_TX; ++ memcpy(&info.data, data, len); ++ info.length = len; ++ break; ++ default: ++ WL_ERR(("Unsupported action code : %d\n", action_code)); ++ goto out; ++ } ++ ++ ret = wldev_iovar_setbuf(dev, "tdls_wfd_ie", &info, sizeof(info), ++ cfg->ioctl_buf, WLC_IOCTL_MAXLEN, &cfg->ioctl_buf_sync); ++ ++ if (ret) { ++ WL_ERR(("tdls_wfd_ie error %d\n", ret)); ++ } ++out: ++#endif /* WLTDLS */ ++ return ret; ++} ++ ++static s32 + wl_cfg80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, + u8 *peer, enum nl80211_tdls_operation oper) + { +@@ -13615,7 +14393,15 @@ + ret = dhd_tdls_enable(dev, true, false, NULL); + if (ret < 0) + return ret; +- info.mode = TDLS_MANUAL_EP_DISCOVERY; ++ /* If the discovery request is broadcast then we need to set ++ * info.mode to Tunneled Probe Request ++ */ ++ if (memcmp(peer, (const uint8 *)BSSID_BROADCAST, ETHER_ADDR_LEN) == 0) { ++ info.mode = TDLS_MANUAL_EP_WFD_TPQ; ++ } ++ else { ++ info.mode = TDLS_MANUAL_EP_DISCOVERY; ++ } + break; + case NL80211_TDLS_SETUP: + /* auto mode on */ +@@ -14131,7 +14917,7 @@ + uint i, tokens, log_on = 0; + memset(tbuf, 0, sizeof(tbuf)); + memset(sublog, 0, sizeof(sublog)); +- if (copy_from_user(&tbuf, userbuf, min_t(size_t, sizeof(tbuf), count))) ++ if (copy_from_user(&tbuf, userbuf, min_t(size_t, (sizeof(tbuf) - 1), count))) + return -EFAULT; + + params = &tbuf[0]; +@@ -14278,8 +15064,10 @@ + if (!cfg || !cfg->wdev) + return -EINVAL; + ++#if !defined(P2PONEINT) + if (dhd_do_driver_init(cfg->wdev->netdev) < 0) + return -1; ++#endif /* BCMDONGLEHOST */ + + return 0; + } +@@ -14287,7 +15075,7 @@ + void wl_cfg80211_enable_trace(u32 level) + { + wl_dbg_level = level; +- printk("%s: wl_dbg_level = 0x%x\n", __FUNCTION__, wl_dbg_level); ++ printf("%s: wl_dbg_level = 0x%x\n", __FUNCTION__, wl_dbg_level); + } + + #if defined(WL_SUPPORT_BACKPORTED_KPATCHES) || (LINUX_VERSION_CODE >= KERNEL_VERSION(3, \ +@@ -14558,6 +15346,7 @@ + struct net_info *iter, *next; + s32 err = BCME_OK; + s32 pm = PM_FAST; ++ dhd_pub_t *dhd; + + cfg = container_of(work, struct bcm_cfg80211, pm_enable_work.work); + WL_DBG(("Enter \n")); +@@ -14568,6 +15357,9 @@ + (wl_get_mode_by_netdev(cfg, iter->ndev) != WL_MODE_BSS)) + continue; + if (iter->ndev) { ++ dhd = (dhd_pub_t *)(cfg->pub); ++ if (pm != PM_OFF && dhd_conf_get_pm(dhd) >= 0) ++ pm = dhd_conf_get_pm(dhd); + if ((err = wldev_ioctl(iter->ndev, WLC_SET_PM, + &pm, sizeof(pm), true)) != 0) { + if (err == -ENODEV) +@@ -14633,7 +15425,7 @@ + wl_event_msg_t e; + + bzero(&e, sizeof(e)); +- e.event_type = cpu_to_be32(WLC_E_ROAM); ++ e.event_type = cpu_to_be32(WLC_E_BSSID); + memcpy(&e.addr, bssid, ETHER_ADDR_LEN); + /* trigger the roam event handler */ + err = wl_notify_roaming_status(cfg, ndev_to_cfgdev(ndev), &e, NULL); +diff -Nur a/drivers/net/wireless/bcmdhd/wl_cfg80211.h c/drivers/net/wireless/bcmdhd/wl_cfg80211.h +--- a/drivers/net/wireless/bcmdhd/wl_cfg80211.h 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/wl_cfg80211.h 2016-05-13 09:48:20.000000000 +0200 +@@ -3,7 +3,7 @@ + * + * $Copyright Open Broadcom Corporation$ + * +- * $Id: wl_cfg80211.h 491407 2014-07-16 09:23:04Z $ ++ * $Id: wl_cfg80211.h 505096 2014-09-26 12:49:04Z $ + */ + + /** +@@ -21,6 +21,8 @@ + #include + #include + ++#include ++#include + #include + + struct wl_conf; +@@ -50,6 +52,12 @@ + + #define CFG80211_ERROR_TEXT "CFG80211-ERROR) " + ++#define MAX_WAIT_TIME 1500 ++#define DNGL_FUNC(func, parameters) func parameters; ++ ++#define PM_BLOCK 1 ++#define PM_ENABLE 0 ++ + #if defined(DHD_DEBUG) + #define WL_ERR(args) \ + do { \ +@@ -394,6 +402,15 @@ + struct net_device *ndev; + }; + ++#ifdef ESCAN_BUF_OVERFLOW_MGMT ++#define BUF_OVERFLOW_MGMT_COUNT 3 ++typedef struct { ++ int RSSI; ++ int length; ++ struct ether_addr BSSID; ++} removal_element_t; ++#endif /* ESCAN_BUF_OVERFLOW_MGMT */ ++ + struct ap_info { + /* Structure to hold WPS, WPA IEs for a AP */ + u8 probe_res_ie[VNDR_IES_MAX_BUF_LEN]; +@@ -550,7 +567,7 @@ + #endif /* DEBUGFS_CFG80211 */ + struct wl_pmk_list *pmk_list; /* wpa2 pmk list */ + tsk_ctl_t event_tsk; /* task of main event handler thread */ +- void *pub; ++ dhd_pub_t *pub; + u32 iface_cnt; + u32 channel; /* current channel */ + u32 af_sent_channel; /* channel action frame is sent */ +@@ -570,7 +587,7 @@ + bool scan_tried; /* indicates if first scan attempted */ + #if defined(BCMSDIO) || defined(BCMPCIE) + bool wlfc_on; +-#endif ++#endif + bool vsdb_mode; + bool roamoff_on_concurrent; + u8 *ioctl_buf; /* ioctl buffer */ +@@ -638,8 +655,18 @@ + #ifdef WLFBT + uint8 fbt_key[FBT_KEYLEN]; + #endif +- bool roam_offload; ++ int roam_offload; + bool nan_running; ++#ifdef P2PLISTEN_AP_SAMECHN ++ bool p2p_resp_apchn_status; ++#endif /* P2PLISTEN_AP_SAMECHN */ ++#ifdef WLTDLS ++ u8 *tdls_mgmt_frame; ++ u32 tdls_mgmt_frame_len; ++ s32 tdls_mgmt_freq; ++#endif /* WLTDLS */ ++ int p2p_disconnected; // terence 20130703: Fix for wrong group_capab (timing issue) ++ struct ether_addr disconnected_bssid; + }; + + +@@ -910,7 +937,7 @@ + ((wl_cfgp2p_find_wpsie((u8 *)_sme->ie, _sme->ie_len) != NULL) && \ + (!_sme->crypto.n_ciphers_pairwise) && \ + (!_sme->crypto.cipher_group)) +-extern s32 wl_cfg80211_attach(struct net_device *ndev, void *context); ++extern s32 wl_cfg80211_attach(struct net_device *ndev, dhd_pub_t *context); + extern s32 wl_cfg80211_attach_post(struct net_device *ndev); + extern void wl_cfg80211_detach(void *para); + +@@ -939,6 +966,9 @@ + extern s32 wl_cfg80211_set_wps_p2p_ie(struct net_device *net, char *buf, int len, + enum wl_management_type type); + extern s32 wl_cfg80211_set_p2p_ps(struct net_device *net, char* buf, int len); ++#ifdef P2PLISTEN_AP_SAMECHN ++extern s32 wl_cfg80211_set_p2p_resp_ap_chn(struct net_device *net, s32 enable); ++#endif /* P2PLISTEN_AP_SAMECHN */ + + /* btcoex functions */ + void* wl_cfg80211_btcoex_init(struct net_device *ndev); +@@ -1036,11 +1066,15 @@ + #endif /* WL_SUPPORT_ACS */ + + extern int wl_cfg80211_get_ioctl_version(void); +-extern int wl_cfg80211_enable_roam_offload(struct net_device *dev, bool enable); ++extern int wl_cfg80211_enable_roam_offload(struct net_device *dev, int enable); + + #ifdef WL_NAN + extern int wl_cfg80211_nan_cmd_handler(struct net_device *ndev, char *cmd, + int cmd_len); + #endif /* WL_NAN */ + +-#endif /* _wl_cfg80211_h_ */ ++#ifdef WL_CFG80211_P2P_DEV_IF ++extern void wl_cfg80211_del_p2p_wdev(void); ++#endif /* WL_CFG80211_P2P_DEV_IF */ ++ ++#endif /* _wl_cfg80211_h_ */ +diff -Nur a/drivers/net/wireless/bcmdhd/wl_cfgp2p.c c/drivers/net/wireless/bcmdhd/wl_cfgp2p.c +--- a/drivers/net/wireless/bcmdhd/wl_cfgp2p.c 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/wl_cfgp2p.c 2016-05-13 09:48:20.000000000 +0200 +@@ -3,7 +3,7 @@ + * + * $Copyright Open Broadcom Corporation$ + * +- * $Id: wl_cfgp2p.c 490694 2014-07-11 14:37:00Z $ ++ * $Id: wl_cfgp2p.c 504573 2014-09-24 15:21:25Z $ + * + */ + #include +@@ -30,6 +30,11 @@ + #include + #include + ++#if defined(P2PONEINT) ++#include ++#include ++#endif ++ + static s8 scanparambuf[WLC_IOCTL_SMLEN]; + static s8 g_mgmt_ie_buf[2048]; + static bool +@@ -41,17 +46,27 @@ + static s32 wl_cfgp2p_cancel_listen(struct bcm_cfg80211 *cfg, struct net_device *ndev, + struct wireless_dev *wdev, bool notify); + ++#ifdef P2PONEINT ++void wl_cfg80211_scan_abort(struct bcm_cfg80211 *cfg); ++chanspec_t wl_cfg80211_get_shared_freq(struct wiphy *wiphy); ++s32 dhd_cfg80211_set_p2p_info(struct bcm_cfg80211 *cfg, int val); ++int wl_cfgp2p_if_open(struct net_device *net); ++int wl_cfgp2p_if_stop(struct net_device *net); ++#endif ++ + #if defined(WL_ENABLE_P2P_IF) + static int wl_cfgp2p_start_xmit(struct sk_buff *skb, struct net_device *ndev); + static int wl_cfgp2p_do_ioctl(struct net_device *net, struct ifreq *ifr, int cmd); +-static int wl_cfgp2p_if_open(struct net_device *net); +-static int wl_cfgp2p_if_stop(struct net_device *net); ++int wl_cfgp2p_if_open(struct net_device *net); ++int wl_cfgp2p_if_stop(struct net_device *net); + + static const struct net_device_ops wl_cfgp2p_if_ops = { + .ndo_open = wl_cfgp2p_if_open, + .ndo_stop = wl_cfgp2p_if_stop, + .ndo_do_ioctl = wl_cfgp2p_do_ioctl, ++#ifndef P2PONEINT + .ndo_start_xmit = wl_cfgp2p_start_xmit, ++#endif + }; + #endif /* WL_ENABLE_P2P_IF */ + +@@ -559,6 +574,38 @@ + return BCME_NOTFOUND; + } + ++#ifdef P2PLISTEN_AP_SAMECHN ++ CFGP2P_DBG(("p2p0 listen channel %d AP connection chan %d \n", ++ channel, cfg->channel)); ++ if ((mode == WL_P2P_DISC_ST_LISTEN) && (cfg->channel == channel)) { ++ struct net_device *primary_ndev = bcmcfg_to_prmry_ndev(cfg); ++ ++ if (cfg->p2p_resp_apchn_status) { ++ CFGP2P_DBG(("p2p_resp_apchn_status already ON \n")); ++ return BCME_OK; ++ } ++ ++ if (wl_get_drv_status(cfg, CONNECTED, primary_ndev)) { ++ ret = wl_cfg80211_set_p2p_resp_ap_chn(primary_ndev, 1); ++ cfg->p2p_resp_apchn_status = true; ++ CFGP2P_DBG(("p2p_resp_apchn_status ON \n")); ++ return ret; ++ } ++ } ++#endif /* P2PLISTEN_AP_SAMECHN */ ++ ++#if defined(CUSTOM_PLATFORM_NV_TEGRA) ++#if defined(P2P_DISCOVERY_WAR) ++ if (mode == WL_P2P_DISC_ST_LISTEN || mode == WL_P2P_DISC_ST_SEARCH) { ++ if (!cfg->p2p->vif_created) { ++ if (wldev_iovar_setint(wl_to_prmry_ndev(cfg), "mpc", 0) < 0) { ++ WL_ERR(("mpc disabling failed\n")); ++ } ++ } ++ } ++#endif /* defined(P2P_DISCOVERY_WAR) */ ++#endif /* defined(CUSTOM_PLATFORM_NV_TEGRA) */ ++ + /* Put the WL driver into P2P Listen Mode to respond to P2P probe reqs */ + discovery_mode.state = mode; + discovery_mode.chspec = wl_ch_host_to_driver(channel); +@@ -641,7 +688,7 @@ + s32 ret = BCME_OK; + CFGP2P_DBG(("enter\n")); + +- if (wl_to_p2p_bss_bssidx(cfg, P2PAPI_BSSCFG_DEVICE) == 0) { ++ if (wl_to_p2p_bss_bssidx(cfg, P2PAPI_BSSCFG_DEVICE) <= 0) { + CFGP2P_ERR(("do nothing, not initialized\n")); + return -1; + } +@@ -736,7 +783,7 @@ + CFGP2P_DBG((" enter\n")); + wl_clr_p2p_status(cfg, DISCOVERY_ON); + +- if(!cfg->p2p) { // terence 20130113: Fix for p2p NULL pointer ++ if (!cfg->p2p) { // terence 20130113: Fix for p2p NULL pointer + ret = BCME_ERROR; + CFGP2P_ERR(("wl->p2p is NULL\n")); + goto exit; +@@ -1066,7 +1113,7 @@ + CFGP2P_DBG((" bssidx %d, pktflag : 0x%02X\n", bssidx, pktflag)); + + #ifdef DUAL_STA +- if ((cfg->p2p != NULL) && (bssidx != cfg->cfgdev_bssidx)) ++ if ((cfg->p2p != NULL) && ((bssidx == 0) || (bssidx != cfg->cfgdev_bssidx))) + #else + if (cfg->p2p != NULL) + #endif +@@ -1109,6 +1156,11 @@ + return BCME_ERROR; + } + } else if (wl_get_mode_by_netdev(cfg, ndev) == WL_MODE_AP) { ++ if (cfg->ap_info == NULL) { ++ CFGP2P_ERR(("hostapd ap_info null ptr refrence while setting IE\n")); ++ return BCME_ERROR; ++ ++ } + switch (pktflag) { + case VNDR_IE_PRBRSP_FLAG : + mgmt_ie_buf = cfg->ap_info->probe_res_ie; +@@ -1560,6 +1612,16 @@ + + ndev = cfgdev_to_wlc_ndev(cfgdev, cfg); + ++#if defined(CUSTOM_PLATFORM_NV_TEGRA) ++#if defined(P2P_DISCOVERY_WAR) ++ if (!cfg->p2p->vif_created) { ++ if (wldev_iovar_setint(ndev, "mpc", 1) < 0) { ++ WL_ERR(("mpc enabling back failed\n")); ++ } ++ } ++#endif /* defined(P2P_DISCOVERY_WAR) */ ++#endif /* defined(CUSTOM_PLATFORM_NV_TEGRA) */ ++ + if (wl_get_p2p_status(cfg, LISTEN_EXPIRED) == 0) { + wl_set_p2p_status(cfg, LISTEN_EXPIRED); + if (timer_pending(&cfg->p2p->listen_timer)) { +@@ -1591,17 +1653,24 @@ + wl_get_drv_status_all(cfg, FAKE_REMAINING_ON_CHANNEL)) + #endif /* WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST */ + { +- WL_DBG(("Listen DONE for ramain on channel expired\n")); ++ WL_DBG(("Listen DONE for remain on channel expired\n")); + wl_clr_drv_status(cfg, REMAINING_ON_CHANNEL, ndev); + #ifdef WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST + wl_clr_drv_status(cfg, FAKE_REMAINING_ON_CHANNEL, ndev); + #endif /* WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST */ + if (ndev && (ndev->ieee80211_ptr != NULL)) { + #if defined(WL_CFG80211_P2P_DEV_IF) +- // terence 20141221: Fix p2p connection issue in both p2p device in Android 5.0 +- // error log: CFG80211-ERROR) wl_cfg80211_send_action_frame : couldn't find peer's channel. +- cfg80211_remain_on_channel_expired(bcmcfg_to_p2p_wdev(cfg), +- cfg->last_roc_id, &cfg->remain_on_chan, GFP_KERNEL); ++ if (cfgdev && ((struct wireless_dev *)cfgdev)->wiphy) { ++ /* ++ * To prevent kernel panic, ++ * if cfgdev->wiphy may be invalid, adding explicit check ++ */ ++ cfg80211_remain_on_channel_expired(cfgdev, cfg->last_roc_id, ++ &cfg->remain_on_chan, GFP_KERNEL); ++ } else { ++ CFGP2P_ERR(("Invalid cfgdev. Dropping the" ++ "remain_on_channel_expired event.\n")); ++ } + #else + cfg80211_remain_on_channel_expired(cfgdev, cfg->last_roc_id, + &cfg->remain_on_chan, cfg->remain_on_chan_type, GFP_KERNEL); +@@ -1655,9 +1724,11 @@ + del_timer_sync(&cfg->p2p->listen_timer); + if (notify) { + #if defined(WL_CFG80211_P2P_DEV_IF) ++#ifdef P2PONEINT ++ if (wdev == NULL) ++ wdev = bcmcfg_to_p2p_wdev(cfg); ++#endif + if (wdev) +- // terence 20141221: Fix p2p connection issue in both p2p device in Android 5.0 +- // error log: CFG80211-ERROR) wl_cfg80211_send_action_frame : couldn't find peer's channel. + cfg80211_remain_on_channel_expired(bcmcfg_to_p2p_wdev(cfg), + cfg->last_roc_id, &cfg->remain_on_chan, GFP_KERNEL); + #else +@@ -1898,7 +1969,9 @@ + * different from the P2P Device Address. + */ + memcpy(out_int_addr, out_dev_addr, sizeof(*out_int_addr)); ++#ifndef P2PONEINT + out_int_addr->octet[4] ^= 0x80; ++#endif + + } + +@@ -2043,8 +2116,12 @@ + s32 i = 0, index = -1; + + #if defined(WL_CFG80211_P2P_DEV_IF) +- ndev = bcmcfg_to_prmry_ndev(cfg); + wdev = bcmcfg_to_p2p_wdev(cfg); ++#ifdef P2PONEINT ++ ndev = wdev_to_ndev(wdev); ++#else ++ ndev = bcmcfg_to_prmry_ndev(cfg); ++#endif + #elif defined(WL_ENABLE_P2P_IF) + ndev = cfg->p2p_net ? cfg->p2p_net : bcmcfg_to_prmry_ndev(cfg); + wdev = ndev_to_wdev(ndev); +@@ -2357,7 +2434,153 @@ + }; + #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) */ + +-#if defined(WL_ENABLE_P2P_IF) || defined(WL_NEWCFG_PRIVCMD_SUPPORT) ++#if defined(WL_ENABLE_P2P_IF) || defined(WL_NEWCFG_PRIVCMD_SUPPORT) || \ ++ defined(P2PONEINT) ++#ifdef P2PONEINT ++s32 ++wl_cfgp2p_register_ndev(struct bcm_cfg80211 *cfg) ++{ ++ ++ struct net_device *_ndev; ++ struct ether_addr primary_mac; ++ struct net_device *new_ndev; ++ chanspec_t chspec; ++ uint8 name[IFNAMSIZ]; ++ s32 mode = 0; ++ s32 val = 0; ++ ++ ++ s32 wlif_type = -1; ++ s32 err, timeout = -1; ++ ++ memset(name, 0, IFNAMSIZ); ++ strncpy(name, "p2p0", 4); ++ name[IFNAMSIZ - 1] = '\0'; ++ ++ if (cfg->p2p_net) { ++ CFGP2P_ERR(("p2p_net defined already.\n")); ++ return -EINVAL; ++ } ++ ++ if (!cfg->p2p) ++ return -EINVAL; ++ ++ if (cfg->p2p && !cfg->p2p->on && strstr(name, WL_P2P_INTERFACE_PREFIX)) { ++ p2p_on(cfg) = true; ++ wl_cfgp2p_set_firm_p2p(cfg); ++ wl_cfgp2p_init_discovery(cfg); ++ get_primary_mac(cfg, &primary_mac); ++ wl_cfgp2p_generate_bss_mac(&primary_mac, ++ &cfg->p2p->dev_addr, &cfg->p2p->int_addr); ++ } ++ ++ _ndev = bcmcfg_to_prmry_ndev(cfg); ++ memset(cfg->p2p->vir_ifname, 0, IFNAMSIZ); ++ strncpy(cfg->p2p->vir_ifname, name, IFNAMSIZ - 1); ++ ++ wl_cfg80211_scan_abort(cfg); ++ ++ ++ /* In concurrency case, STA may be already associated in a particular channel. ++ * so retrieve the current channel of primary interface and then start the virtual ++ * interface on that. ++ */ ++ chspec = wl_cfg80211_get_shared_freq(cfg->wdev->wiphy); ++ ++ /* For P2P mode, use P2P-specific driver features to create the ++ * bss: "cfg p2p_ifadd" ++ */ ++ wl_set_p2p_status(cfg, IF_ADDING); ++ memset(&cfg->if_event_info, 0, sizeof(cfg->if_event_info)); ++ wlif_type = WL_P2P_IF_CLIENT; ++ ++ ++ err = wl_cfgp2p_ifadd(cfg, &cfg->p2p->int_addr, htod32(wlif_type), chspec); ++ if (unlikely(err)) { ++ wl_clr_p2p_status(cfg, IF_ADDING); ++ WL_ERR((" virtual iface add failed (%d) \n", err)); ++ return -ENOMEM; ++ } ++ ++ timeout = wait_event_interruptible_timeout(cfg->netif_change_event, ++ (wl_get_p2p_status(cfg, IF_ADDING) == false), ++ msecs_to_jiffies(MAX_WAIT_TIME)); ++ ++ ++ if (timeout > 0 && !wl_get_p2p_status(cfg, IF_ADDING) && cfg->if_event_info.valid) { ++ struct wireless_dev *vwdev; ++ int pm_mode = PM_ENABLE; ++ wl_if_event_info *event = &cfg->if_event_info; ++ ++ /* IF_ADD event has come back, we can proceed to to register ++ * the new interface now, use the interface name provided by caller (thus ++ * ignore the one from wlc) ++ */ ++ strncpy(cfg->if_event_info.name, name, IFNAMSIZ - 1); ++ new_ndev = wl_cfg80211_allocate_if(cfg, event->ifidx, cfg->p2p->vir_ifname, ++ event->mac, event->bssidx); ++ if (new_ndev == NULL) ++ goto fail; ++ ++ wl_to_p2p_bss_ndev(cfg, P2PAPI_BSSCFG_CONNECTION) = new_ndev; ++ wl_to_p2p_bss_bssidx(cfg, P2PAPI_BSSCFG_CONNECTION) = event->bssidx; ++ ++ vwdev = kzalloc(sizeof(*vwdev), GFP_KERNEL); ++ if (unlikely(!vwdev)) { ++ WL_ERR(("Could not allocate wireless device\n")); ++ goto fail; ++ } ++ vwdev->wiphy = cfg->wdev->wiphy; ++ WL_TRACE(("virtual interface(%s) is created\n", cfg->p2p->vir_ifname)); ++ vwdev->iftype = NL80211_IFTYPE_P2P_DEVICE; ++ vwdev->netdev = new_ndev; ++ new_ndev->ieee80211_ptr = vwdev; ++ SET_NETDEV_DEV(new_ndev, wiphy_dev(vwdev->wiphy)); ++ wl_set_drv_status(cfg, READY, new_ndev); ++ cfg->p2p->vif_created = true; ++ wl_set_mode_by_netdev(cfg, new_ndev, mode); ++ ++ if (wl_cfg80211_register_if(cfg, event->ifidx, new_ndev) != BCME_OK) { ++ wl_cfg80211_remove_if(cfg, event->ifidx, new_ndev); ++ goto fail; ++ } ++ ++ wl_alloc_netinfo(cfg, new_ndev, vwdev, mode, pm_mode); ++ val = 1; ++ /* Disable firmware roaming for P2P interface */ ++ wldev_iovar_setint(new_ndev, "roam_off", val); ++ ++ if (mode != WL_MODE_AP) ++ wldev_iovar_setint(new_ndev, "buf_key_b4_m4", 1); ++ ++ WL_ERR((" virtual interface(%s) is " ++ "created net attach done\n", cfg->p2p->vir_ifname)); ++ ++ /* reinitialize completion to clear previous count */ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0)) ++ INIT_COMPLETION(cfg->iface_disable); ++#else ++ init_completion(&cfg->iface_disable); ++#endif ++ cfg->p2p_net = new_ndev; ++ cfg->p2p_wdev = vwdev; ++ ++ return 0; ++ } else { ++ wl_clr_p2p_status(cfg, IF_ADDING); ++ WL_ERR((" virtual interface(%s) is not created \n", cfg->p2p->vir_ifname)); ++ memset(cfg->p2p->vir_ifname, '\0', IFNAMSIZ); ++ cfg->p2p->vif_created = false; ++ } ++ ++ ++fail: ++ if (wlif_type == WL_P2P_IF_GO) ++ wldev_iovar_setint(_ndev, "mpc", 1); ++ return -ENODEV; ++ ++} ++#else + s32 + wl_cfgp2p_register_ndev(struct bcm_cfg80211 *cfg) + { +@@ -2445,10 +2668,11 @@ + #endif /* WL_NEWCFG_PRIVCMD_SUPPORT */ + cfg->p2p_net = net; + +- printk("%s: P2P Interface Registered\n", net->name); ++ printf("%s: P2P Interface Registered\n", net->name); + + return ret; + } ++#endif /* P2PONEINT */ + + s32 + wl_cfgp2p_unregister_ndev(struct bcm_cfg80211 *cfg) +@@ -2465,6 +2689,7 @@ + return 0; + } + ++#ifndef P2PONEINT + static int wl_cfgp2p_start_xmit(struct sk_buff *skb, struct net_device *ndev) + { + +@@ -2499,10 +2724,16 @@ + + return ret; + } +-#endif /* WL_ENABLE_P2P_IF || WL_NEWCFG_PRIVCMD_SUPPORT */ ++#endif /* P2PONEINT */ ++#endif /* WL_ENABLE_P2P_IF || WL_NEWCFG_PRIVCMD_SUPPORT || defined(P2PONEINT) */ + +-#if defined(WL_ENABLE_P2P_IF) +-static int wl_cfgp2p_if_open(struct net_device *net) ++#if defined(WL_ENABLE_P2P_IF) || defined(P2PONEINT) ++int ++#ifdef P2PONEINT ++wl_cfgp2p_if_open(struct net_device *net) ++#else ++wl_cfgp2p_if_open(struct net_device *net) ++#endif + { + struct wireless_dev *wdev = net->ieee80211_ptr; + +@@ -2524,14 +2755,26 @@ + return 0; + } + +-static int wl_cfgp2p_if_stop(struct net_device *net) ++int ++#ifdef P2PONEINT ++wl_cfgp2p_if_stop(struct net_device *net) ++#else ++wl_cfgp2p_if_stop(struct net_device *net) ++#endif + { + struct wireless_dev *wdev = net->ieee80211_ptr; +- ++#ifdef P2PONEINT ++ bcm_struct_cfgdev *cfgdev; ++#endif + if (!wdev) + return -EINVAL; + ++#ifdef P2PONEINT ++ cfgdev = ndev_to_cfgdev(net); ++ wl_cfg80211_scan_stop(cfgdev); ++#else + wl_cfg80211_scan_stop(net); ++#endif + + #if !defined(WL_IFACE_COMB_NUM_CHANNELS) + wdev->wiphy->interface_modes = (wdev->wiphy->interface_modes) +@@ -2540,7 +2783,9 @@ + #endif /* !WL_IFACE_COMB_NUM_CHANNELS */ + return 0; + } ++#endif /* defined(WL_ENABLE_P2P_IF) || defined(P2PONEINT) */ + ++#if defined(WL_ENABLE_P2P_IF) + bool wl_cfgp2p_is_ifops(const struct net_device_ops *if_ops) + { + return (if_ops == &wl_cfgp2p_if_ops); +@@ -2554,7 +2799,7 @@ + struct wireless_dev *wdev = NULL; + struct ether_addr primary_mac; + +- if (!cfg) ++ if (!cfg || !cfg->p2p_supported) + return ERR_PTR(-EINVAL); + + WL_TRACE(("Enter\n")); +@@ -2566,7 +2811,7 @@ + CFGP2P_ERR(("p2p_wdev deleted.\n")); + #else + return ERR_PTR(-ENFILE); +-#endif ++#endif + } + + wdev = kzalloc(sizeof(*wdev), GFP_KERNEL); +@@ -2592,7 +2837,7 @@ + /* store p2p wdev ptr for further reference. */ + cfg->p2p_wdev = wdev; + +- WL_TRACE(("P2P interface registered\n")); ++ printf("P2P interface registered\n"); + + return wdev; + } +@@ -2622,7 +2867,7 @@ + + p2p_on(cfg) = true; + +- CFGP2P_DBG(("P2P interface started\n")); ++ printf("P2P interface started\n"); + + exit: + return ret; +@@ -2654,7 +2899,7 @@ + + p2p_on(cfg) = false; + +- CFGP2P_DBG(("P2P interface stopped\n")); ++ printf("P2P interface stopped\n"); + + return; + } +@@ -2667,6 +2912,10 @@ + if (!wdev) + return -EINVAL; + ++#ifdef P2PONEINT ++ return -EINVAL; ++#endif ++ + WL_TRACE(("Enter\n")); + + if (!rtnl_is_locked()) { +@@ -2684,7 +2933,7 @@ + if (cfg) + cfg->p2p_wdev = NULL; + +- CFGP2P_ERR(("P2P interface unregistered\n")); ++ printf("P2P interface unregistered\n"); + + return 0; + } +diff -Nur a/drivers/net/wireless/bcmdhd/wl_cfgp2p.h c/drivers/net/wireless/bcmdhd/wl_cfgp2p.h +--- a/drivers/net/wireless/bcmdhd/wl_cfgp2p.h 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/wl_cfgp2p.h 2016-05-13 09:48:20.000000000 +0200 +@@ -3,7 +3,7 @@ + * + * $Copyright Open Broadcom Corporation$ + * +- * $Id: wl_cfgp2p.h 472818 2014-04-25 08:07:56Z $ ++ * $Id: wl_cfgp2p.h 497431 2014-08-19 11:03:27Z $ + */ + #ifndef _wl_cfgp2p_h_ + #define _wl_cfgp2p_h_ +@@ -56,7 +56,7 @@ + }; + + struct p2p_bss { +- u32 bssidx; ++ s32 bssidx; + struct net_device *dev; + struct p2p_saved_ie saved_ie; + void *private_data; +diff -Nur a/drivers/net/wireless/bcmdhd/wl_cfgvendor.h c/drivers/net/wireless/bcmdhd/wl_cfgvendor.h +--- a/drivers/net/wireless/bcmdhd/wl_cfgvendor.h 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/wl_cfgvendor.h 2016-05-13 09:48:20.000000000 +0200 +@@ -6,10 +6,6 @@ + * $Id: wl_cfgvendor.h 455257 2014-02-20 08:10:24Z $ + */ + +-/* +- * New vendor interface additon to nl80211/cfg80211 to allow vendors +- * to implement proprietary features over the cfg80211 stack. +- */ + + #ifndef _wl_cfgvendor_h_ + #define _wl_cfgvendor_h_ +diff -Nur a/drivers/net/wireless/bcmdhd/wl_dbg.h c/drivers/net/wireless/bcmdhd/wl_dbg.h +--- a/drivers/net/wireless/bcmdhd/wl_dbg.h 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/wl_dbg.h 2016-05-13 09:48:20.000000000 +0200 +@@ -385,7 +385,7 @@ + #endif + #define WL_PCIE(args) do {if (wl_msg_level2 & WL_PCIE_VAL) WL_PRINT(args);} while (0) + #define WL_PCIE_ON() (wl_msg_level2 & WL_PCIE_VAL) +-#endif ++#endif + + extern uint32 wl_msg_level; + extern uint32 wl_msg_level2; +diff -Nur a/drivers/net/wireless/bcmdhd/wldev_common.c c/drivers/net/wireless/bcmdhd/wldev_common.c +--- a/drivers/net/wireless/bcmdhd/wldev_common.c 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/wldev_common.c 2016-05-13 09:48:20.000000000 +0200 +@@ -3,7 +3,7 @@ + * + * $Copyright Open Broadcom Corporation$ + * +- * $Id: wldev_common.c 467328 2014-04-03 01:23:40Z $ ++ * $Id: wldev_common.c 504503 2014-09-24 11:28:56Z $ + */ + + #include +@@ -352,7 +352,9 @@ + cspec.rev = -1; + memcpy(cspec.country_abbrev, country_code, WLC_CNTRY_BUF_SZ); + memcpy(cspec.ccode, country_code, WLC_CNTRY_BUF_SZ); +- dhd_get_customized_country_code(dev, (char *)&cspec.country_abbrev, &cspec); ++ error = dhd_conf_get_country_from_config(dhd_get_pub(dev), &cspec); ++ if (error) ++ dhd_get_customized_country_code(dev, (char *)&cspec.country_abbrev, &cspec); + error = wldev_iovar_setbuf(dev, "country", &cspec, sizeof(cspec), + smbuf, sizeof(smbuf), NULL); + if (error < 0) { +@@ -363,8 +365,8 @@ + dhd_conf_fix_country(dhd_get_pub(dev)); + dhd_conf_get_country(dhd_get_pub(dev), &cspec); + dhd_bus_country_set(dev, &cspec, notify); +- WLDEV_ERROR(("%s: set country for %s as %s rev %d\n", +- __FUNCTION__, country_code, cspec.ccode, cspec.rev)); ++ printf("%s: set country for %s as %s rev %d\n", ++ __FUNCTION__, country_code, cspec.ccode, cspec.rev); + } + return 0; + } +diff -Nur a/drivers/net/wireless/bcmdhd/wldev_common.h c/drivers/net/wireless/bcmdhd/wldev_common.h +--- a/drivers/net/wireless/bcmdhd/wldev_common.h 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/wldev_common.h 2016-05-13 09:48:20.000000000 +0200 +@@ -3,7 +3,7 @@ + * + * $Copyright Open Broadcom Corporation$ + * +- * $Id: wldev_common.h 467328 2014-04-03 01:23:40Z $ ++ * $Id: wldev_common.h 504503 2014-09-24 11:28:56Z $ + */ + #ifndef __WLDEV_COMMON_H__ + #define __WLDEV_COMMON_H__ +@@ -97,4 +97,10 @@ + + int wldev_set_band(struct net_device *dev, uint band); + ++#if defined(CUSTOM_PLATFORM_NV_TEGRA) ++int wldev_miracast_tuning(struct net_device *dev, char *command, int total_len); ++int wldev_get_assoc_resp_ie(struct net_device *dev, char *command, int total_len); ++int wldev_get_rx_rate_stats(struct net_device *dev, char *command, int total_len); ++#endif /* defined(CUSTOM_PLATFORM_NV_TEGRA) */ ++ + #endif /* __WLDEV_COMMON_H__ */ +diff -Nur a/drivers/net/wireless/bcmdhd/wl_iw.c c/drivers/net/wireless/bcmdhd/wl_iw.c +--- a/drivers/net/wireless/bcmdhd/wl_iw.c 2016-06-09 19:33:24.000000000 +0200 ++++ c/drivers/net/wireless/bcmdhd/wl_iw.c 2016-05-13 09:48:20.000000000 +0200 +@@ -22,6 +22,7 @@ + + typedef const struct si_pub si_t; + #include ++#include + + + /* message levels */ +@@ -458,9 +459,6 @@ + error = dev_wlc_ioctl(dev, WLC_SET_PM, &pm, sizeof(pm)); + return error; + } +- +-#if WIRELESS_EXT > 17 +-#endif /* WIRELESS_EXT > 17 */ + #endif /* WIRELESS_EXT > 12 */ + + int +@@ -599,9 +597,12 @@ + + chan = wf_mhz2channel(fwrq->m, sf); + } ++ WL_ERROR(("%s: chan=%d\n", __FUNCTION__, chan)); + chan = htod32(chan); +- if ((error = dev_wlc_ioctl(dev, WLC_SET_CHANNEL, &chan, sizeof(chan)))) ++ if ((error = dev_wlc_ioctl(dev, WLC_SET_CHANNEL, &chan, sizeof(chan)))) { ++ WL_ERROR(("%s: WLC_DISASSOC failed (%d).\n", __FUNCTION__, error)); + return error; ++ } + + /* -EINPROGRESS: Call commit handler */ + return -EINPROGRESS; +@@ -993,6 +994,7 @@ + if (ETHER_ISBCAST(awrq->sa_data) || ETHER_ISNULLADDR(awrq->sa_data)) { + scb_val_t scbval; + bzero(&scbval, sizeof(scb_val_t)); ++ WL_ERROR(("%s: WLC_DISASSOC\n", __FUNCTION__)); + if ((error = dev_wlc_ioctl(dev, WLC_DISASSOC, &scbval, sizeof(scb_val_t)))) { + WL_ERROR(("%s: WLC_DISASSOC failed (%d).\n", __FUNCTION__, error)); + } +@@ -1006,6 +1008,7 @@ + WL_ERROR(("%s: WLC_REASSOC failed (%d).\n", __FUNCTION__, error)); + return error; + } ++ WL_ERROR(("%s: join BSSID="MACSTR"\n", __FUNCTION__, MAC2STR((u8 *)awrq->sa_data))); + + return 0; + } +@@ -1085,6 +1088,7 @@ + wl_bss_info_t *bi = NULL; + int error, i; + uint buflen = dwrq->length; ++ int16 rssi; + + WL_TRACE(("%s: SIOCGIWAPLIST\n", dev->name)); + +@@ -1119,8 +1123,10 @@ + /* BSSID */ + memcpy(addr[dwrq->length].sa_data, &bi->BSSID, ETHER_ADDR_LEN); + addr[dwrq->length].sa_family = ARPHRD_ETHER; +- qual[dwrq->length].qual = rssi_to_qual(dtoh16(bi->RSSI)); +- qual[dwrq->length].level = 0x100 + dtoh16(bi->RSSI); ++ // terence 20150419: limit the max. rssi to -2 or the bss will be filtered out in android OS ++ rssi = MIN(dtoh16(bi->RSSI), RSSI_MAXVAL); ++ qual[dwrq->length].qual = rssi_to_qual(rssi); ++ qual[dwrq->length].level = 0x100 + rssi; + qual[dwrq->length].noise = 0x100 + bi->phy_noise; + + /* Updated qual, level, and noise */ +@@ -1160,6 +1166,7 @@ + struct iw_quality qual[IW_MAX_AP]; + wl_bss_info_t *bi = NULL; + int i; ++ int16 rssi; + + WL_TRACE(("%s: SIOCGIWAPLIST\n", dev->name)); + +@@ -1189,8 +1196,10 @@ + /* BSSID */ + memcpy(addr[dwrq->length].sa_data, &bi->BSSID, ETHER_ADDR_LEN); + addr[dwrq->length].sa_family = ARPHRD_ETHER; +- qual[dwrq->length].qual = rssi_to_qual(dtoh16(bi->RSSI)); +- qual[dwrq->length].level = 0x100 + dtoh16(bi->RSSI); ++ // terence 20150419: limit the max. rssi to -2 or the bss will be filtered out in android OS ++ rssi = MIN(dtoh16(bi->RSSI), RSSI_MAXVAL); ++ qual[dwrq->length].qual = rssi_to_qual(rssi); ++ qual[dwrq->length].level = 0x100 + rssi; + qual[dwrq->length].noise = 0x100 + bi->phy_noise; + + /* Updated qual, level, and noise */ +@@ -1476,12 +1485,13 @@ + break; + } + #endif /* BCMWAPI_WPI */ +- *event_p = event; ++ *event_p = event; + } + + #endif /* WIRELESS_EXT > 17 */ + return 0; + } ++ + static int + wl_iw_get_scan( + struct net_device *dev, +@@ -1497,6 +1507,8 @@ + int error, i, j; + char *event = extra, *end = extra + dwrq->length, *value; + uint buflen = dwrq->length; ++ int16 rssi; ++ int channel; + + WL_TRACE(("%s: %s SIOCGIWSCAN\n", __FUNCTION__, dev->name)); + +@@ -1531,6 +1543,12 @@ + ASSERT(((uintptr)bi + dtoh32(bi->length)) <= ((uintptr)list + + buflen)); + ++ // terence 20150419: limit the max. rssi to -2 or the bss will be filtered out in android OS ++ rssi = MIN(dtoh16(bi->RSSI), RSSI_MAXVAL); ++ channel = (bi->ctl_ch == 0) ? CHSPEC_CHANNEL(bi->chanspec) : bi->ctl_ch; ++ WL_SCAN(("%s: BSSID="MACSTR", channel=%d, RSSI=%d, merge broadcast SSID=\"%s\"\n", ++ __FUNCTION__, MAC2STR(bi->BSSID.octet), channel, rssi, bi->SSID)); ++ + /* First entry must be the BSSID */ + iwe.cmd = SIOCGIWAP; + iwe.u.ap_addr.sa_family = ARPHRD_ETHER; +@@ -1555,8 +1573,7 @@ + + /* Channel */ + iwe.cmd = SIOCGIWFREQ; +- +- iwe.u.freq.m = wf_channel2mhz(CHSPEC_CHANNEL(bi->chanspec), ++ iwe.u.freq.m = wf_channel2mhz(channel, + (CHSPEC_IS2G(bi->chanspec)) ? + WF_CHAN_FACTOR_2_4_G : WF_CHAN_FACTOR_5_G); + iwe.u.freq.e = 6; +@@ -1564,8 +1581,8 @@ + + /* Channel quality */ + iwe.cmd = IWEVQUAL; +- iwe.u.qual.qual = rssi_to_qual(dtoh16(bi->RSSI)); +- iwe.u.qual.level = 0x100 + dtoh16(bi->RSSI); ++ iwe.u.qual.qual = rssi_to_qual(rssi); ++ iwe.u.qual.level = 0x100 + rssi; + iwe.u.qual.noise = 0x100 + bi->phy_noise; + event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_QUAL_LEN); + +@@ -1620,6 +1637,8 @@ + char *event = extra, *end = extra + dwrq->length, *value; + iscan_info_t *iscan = g_iscan; + iscan_buf_t * p_buf; ++ int16 rssi; ++ int channel; + + WL_TRACE(("%s: %s SIOCGIWSCAN\n", __FUNCTION__, dev->name)); + +@@ -1632,97 +1651,105 @@ + } + + /* Check for scan in progress */ +- if (iscan->iscan_state == ISCAN_STATE_SCANING) ++ if (iscan->iscan_state == ISCAN_STATE_SCANING) { ++ WL_TRACE(("%s: SIOCGIWSCAN GET still scanning\n", dev->name)); + return -EAGAIN; ++ } + + apcnt = 0; + p_buf = iscan->list_hdr; + /* Get scan results */ + while (p_buf != iscan->list_cur) { +- list = &((wl_iscan_results_t*)p_buf->iscan_buf)->results; +- +- if (list->version != WL_BSS_INFO_VERSION) { +- WL_ERROR(("list->version %d != WL_BSS_INFO_VERSION\n", list->version)); +- } ++ list = &((wl_iscan_results_t*)p_buf->iscan_buf)->results; + +- bi = NULL; +- for (ii = 0; ii < list->count && apcnt < IW_MAX_AP; apcnt++, ii++) { +- bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : list->bss_info; +- ASSERT(((uintptr)bi + dtoh32(bi->length)) <= ((uintptr)list + +- WLC_IW_ISCAN_MAXLEN)); +- +- /* overflow check cover fields before wpa IEs */ +- if (event + ETHER_ADDR_LEN + bi->SSID_len + IW_EV_UINT_LEN + IW_EV_FREQ_LEN + +- IW_EV_QUAL_LEN >= end) +- return -E2BIG; +- /* First entry must be the BSSID */ +- iwe.cmd = SIOCGIWAP; +- iwe.u.ap_addr.sa_family = ARPHRD_ETHER; +- memcpy(iwe.u.ap_addr.sa_data, &bi->BSSID, ETHER_ADDR_LEN); +- event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_ADDR_LEN); +- +- /* SSID */ +- iwe.u.data.length = dtoh32(bi->SSID_len); +- iwe.cmd = SIOCGIWESSID; +- iwe.u.data.flags = 1; +- event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, bi->SSID); +- +- /* Mode */ +- if (dtoh16(bi->capability) & (DOT11_CAP_ESS | DOT11_CAP_IBSS)) { +- iwe.cmd = SIOCGIWMODE; +- if (dtoh16(bi->capability) & DOT11_CAP_ESS) +- iwe.u.mode = IW_MODE_INFRA; +- else +- iwe.u.mode = IW_MODE_ADHOC; +- event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_UINT_LEN); ++ if (list->version != WL_BSS_INFO_VERSION) { ++ WL_ERROR(("list->version %d != WL_BSS_INFO_VERSION\n", list->version)); + } + +- /* Channel */ +- iwe.cmd = SIOCGIWFREQ; +- +- iwe.u.freq.m = wf_channel2mhz(CHSPEC_CHANNEL(bi->chanspec), +- (CHSPEC_IS2G(bi->chanspec)) ? +- WF_CHAN_FACTOR_2_4_G : WF_CHAN_FACTOR_5_G); +- iwe.u.freq.e = 6; +- event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_FREQ_LEN); +- +- /* Channel quality */ +- iwe.cmd = IWEVQUAL; +- iwe.u.qual.qual = rssi_to_qual(dtoh16(bi->RSSI)); +- iwe.u.qual.level = 0x100 + dtoh16(bi->RSSI); +- iwe.u.qual.noise = 0x100 + bi->phy_noise; +- event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_QUAL_LEN); ++ bi = NULL; ++ for (ii = 0; ii < list->count && apcnt < IW_MAX_AP; apcnt++, ii++) { ++ bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : list->bss_info; ++ ASSERT(((uintptr)bi + dtoh32(bi->length)) <= ((uintptr)list + ++ WLC_IW_ISCAN_MAXLEN)); + +- /* WPA, WPA2, WPS, WAPI IEs */ +- wl_iw_handle_scanresults_ies(&event, end, info, bi); +- +- /* Encryption */ +- iwe.cmd = SIOCGIWENCODE; +- if (dtoh16(bi->capability) & DOT11_CAP_PRIVACY) +- iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; +- else +- iwe.u.data.flags = IW_ENCODE_DISABLED; +- iwe.u.data.length = 0; +- event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)event); +- +- /* Rates */ +- if (bi->rateset.count <= sizeof(bi->rateset.rates)) { +- if (event + IW_MAX_BITRATES*IW_EV_PARAM_LEN >= end) ++ /* overflow check cover fields before wpa IEs */ ++ if (event + ETHER_ADDR_LEN + bi->SSID_len + IW_EV_UINT_LEN + IW_EV_FREQ_LEN + ++ IW_EV_QUAL_LEN >= end) + return -E2BIG; + +- value = event + IW_EV_LCP_LEN; +- iwe.cmd = SIOCGIWRATE; +- /* Those two flags are ignored... */ +- iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; +- for (j = 0; j < bi->rateset.count && j < IW_MAX_BITRATES; j++) { +- iwe.u.bitrate.value = (bi->rateset.rates[j] & 0x7f) * 500000; +- value = IWE_STREAM_ADD_VALUE(info, event, value, end, &iwe, +- IW_EV_PARAM_LEN); ++ // terence 20150419: limit the max. rssi to -2 or the bss will be filtered out in android OS ++ rssi = MIN(dtoh16(bi->RSSI), RSSI_MAXVAL); ++ channel = (bi->ctl_ch == 0) ? CHSPEC_CHANNEL(bi->chanspec) : bi->ctl_ch; ++ WL_SCAN(("%s: BSSID="MACSTR", channel=%d, RSSI=%d, merge broadcast SSID=\"%s\"\n", ++ __FUNCTION__, MAC2STR(bi->BSSID.octet), channel, rssi, bi->SSID)); ++ ++ /* First entry must be the BSSID */ ++ iwe.cmd = SIOCGIWAP; ++ iwe.u.ap_addr.sa_family = ARPHRD_ETHER; ++ memcpy(iwe.u.ap_addr.sa_data, &bi->BSSID, ETHER_ADDR_LEN); ++ event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_ADDR_LEN); ++ ++ /* SSID */ ++ iwe.u.data.length = dtoh32(bi->SSID_len); ++ iwe.cmd = SIOCGIWESSID; ++ iwe.u.data.flags = 1; ++ event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, bi->SSID); ++ ++ /* Mode */ ++ if (dtoh16(bi->capability) & (DOT11_CAP_ESS | DOT11_CAP_IBSS)) { ++ iwe.cmd = SIOCGIWMODE; ++ if (dtoh16(bi->capability) & DOT11_CAP_ESS) ++ iwe.u.mode = IW_MODE_INFRA; ++ else ++ iwe.u.mode = IW_MODE_ADHOC; ++ event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_UINT_LEN); ++ } ++ ++ /* Channel */ ++ iwe.cmd = SIOCGIWFREQ; ++ iwe.u.freq.m = wf_channel2mhz(channel, ++ (CHSPEC_IS2G(bi->chanspec)) ? ++ WF_CHAN_FACTOR_2_4_G : WF_CHAN_FACTOR_5_G); ++ iwe.u.freq.e = 6; ++ event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_FREQ_LEN); ++ ++ /* Channel quality */ ++ iwe.cmd = IWEVQUAL; ++ iwe.u.qual.qual = rssi_to_qual(rssi); ++ iwe.u.qual.level = 0x100 + rssi; ++ iwe.u.qual.noise = 0x100 + bi->phy_noise; ++ event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_QUAL_LEN); ++ ++ /* WPA, WPA2, WPS, WAPI IEs */ ++ wl_iw_handle_scanresults_ies(&event, end, info, bi); ++ ++ /* Encryption */ ++ iwe.cmd = SIOCGIWENCODE; ++ if (dtoh16(bi->capability) & DOT11_CAP_PRIVACY) ++ iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; ++ else ++ iwe.u.data.flags = IW_ENCODE_DISABLED; ++ iwe.u.data.length = 0; ++ event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)event); ++ ++ /* Rates */ ++ if (bi->rateset.count <= sizeof(bi->rateset.rates)) { ++ if (event + IW_MAX_BITRATES*IW_EV_PARAM_LEN >= end) ++ return -E2BIG; ++ ++ value = event + IW_EV_LCP_LEN; ++ iwe.cmd = SIOCGIWRATE; ++ /* Those two flags are ignored... */ ++ iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; ++ for (j = 0; j < bi->rateset.count && j < IW_MAX_BITRATES; j++) { ++ iwe.u.bitrate.value = (bi->rateset.rates[j] & 0x7f) * 500000; ++ value = IWE_STREAM_ADD_VALUE(info, event, value, end, &iwe, ++ IW_EV_PARAM_LEN); ++ } ++ event = value; + } +- event = value; + } +- } +- p_buf = p_buf->next; ++ p_buf = p_buf->next; + } /* while (p_buf) */ + + dwrq->length = event - extra; +@@ -1758,15 +1785,21 @@ + memcpy(ssid.SSID, extra, ssid.SSID_len); + ssid.SSID_len = htod32(ssid.SSID_len); + +- if ((error = dev_wlc_ioctl(dev, WLC_SET_SSID, &ssid, sizeof(ssid)))) ++ if ((error = dev_wlc_ioctl(dev, WLC_SET_SSID, &ssid, sizeof(ssid)))) { ++ WL_ERROR(("%s: WLC_SET_SSID failed (%d).\n", __FUNCTION__, error)); + return error; ++ } ++ WL_ERROR(("%s: join SSID=%s\n", __FUNCTION__, ssid.SSID)); + } + /* If essid null then it is "iwconfig essid off" command */ + else { + scb_val_t scbval; + bzero(&scbval, sizeof(scb_val_t)); +- if ((error = dev_wlc_ioctl(dev, WLC_DISASSOC, &scbval, sizeof(scb_val_t)))) ++ WL_ERROR(("%s: WLC_DISASSOC\n", __FUNCTION__)); ++ if ((error = dev_wlc_ioctl(dev, WLC_DISASSOC, &scbval, sizeof(scb_val_t)))) { ++ WL_ERROR(("%s: WLC_DISASSOC failed (%d).\n", __FUNCTION__, error)); + return error; ++ } + } + return 0; + } +@@ -2495,9 +2528,12 @@ + bcopy(keystring, pmk.key, len); + pmk.flags = htod16(WSEC_PASSPHRASE); + ++ WL_WSEC(("%s: set key %s\n", __FUNCTION__, keystring)); + error = dev_wlc_ioctl(dev, WLC_SET_WSEC_PMK, &pmk, sizeof(pmk)); +- if (error) ++ if (error) { ++ WL_ERROR(("%s: WLC_SET_WSEC_PMK error %d\n", __FUNCTION__, error)); + return error; ++ } + } + + else { +@@ -2568,7 +2604,6 @@ + } + + +-#if WIRELESS_EXT > 17 + struct { + pmkid_list_t pmkids; + pmkid_t foo[MAXPMKID-1]; +@@ -2655,7 +2690,6 @@ + dev_wlc_bufvar_set(dev, "pmkid_info", (char *)&pmkid_list, sizeof(pmkid_list)); + return 0; + } +-#endif /* WIRELESS_EXT > 17 */ + + static int + wl_iw_get_encodeext( +@@ -2722,8 +2756,11 @@ + iw->gwsec = paramval; + } + +- if ((error = dev_wlc_intvar_get(dev, "wsec", &val))) ++ if ((error = dev_wlc_intvar_get(dev, "wsec", &val))) { ++ WL_ERROR(("%s: wsec error %d\n", __FUNCTION__, error)); + return error; ++ } ++ WL_WSEC(("%s: get wsec=0x%x\n", __FUNCTION__, val)); + + cipher_combined = iw->gwsec | iw->pwsec; + val &= ~(WEP_ENABLED | TKIP_ENABLED | AES_ENABLED); +@@ -2753,21 +2790,29 @@ + } + } + +- if ((error = dev_wlc_intvar_set(dev, "wsec", val))) ++ WL_WSEC(("%s: set wsec=0x%x\n", __FUNCTION__, val)); ++ if ((error = dev_wlc_intvar_set(dev, "wsec", val))) { ++ WL_ERROR(("%s: wsec error %d\n", __FUNCTION__, error)); + return error; ++ } + + /* Ensure in-dongle supplicant is turned on when FBT wants to do the 4-way + * handshake. + */ + if (dev_wlc_intvar_get(dev, "fbt_cap", &fbt_cap) == 0) { ++ WL_WSEC(("%s: get fbt_cap=0x%x\n", __FUNCTION__, fbt_cap)); + if (fbt_cap == WLC_FBT_CAP_DRV_4WAY_AND_REASSOC) { + if ((paramid == IW_AUTH_CIPHER_PAIRWISE) && (val & AES_ENABLED)) { +- if ((error = dev_wlc_intvar_set(dev, "sup_wpa", 1))) ++ if ((error = dev_wlc_intvar_set(dev, "sup_wpa", 1))) { ++ WL_ERROR(("%s: sup_wpa 1 error %d\n", __FUNCTION__, error)); + return error; ++ } + } + else if (val == 0) { +- if ((error = dev_wlc_intvar_set(dev, "sup_wpa", 0))) ++ if ((error = dev_wlc_intvar_set(dev, "sup_wpa", 0))) { ++ WL_ERROR(("%s: sup_wpa 0 error %d\n", __FUNCTION__, error)); + return error; ++ } + } + } + } +@@ -2775,8 +2820,11 @@ + } + + case IW_AUTH_KEY_MGMT: +- if ((error = dev_wlc_intvar_get(dev, "wpa_auth", &val))) ++ if ((error = dev_wlc_intvar_get(dev, "wpa_auth", &val))) { ++ WL_ERROR(("%s: wpa_auth error %d\n", __FUNCTION__, error)); + return error; ++ } ++ WL_WSEC(("%s: get wpa_auth to %d\n", __FUNCTION__, val)); + + if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) { + if (paramval & (IW_AUTH_KEY_MGMT_FT_PSK | IW_AUTH_KEY_MGMT_PSK)) +@@ -3085,8 +3133,6 @@ + WL_IW_SET_LEDDC = SIOCIWFIRSTPRIV, + WL_IW_SET_VLANMODE, + WL_IW_SET_PM, +-#if WIRELESS_EXT > 17 +-#endif /* WIRELESS_EXT > 17 */ + WL_IW_SET_LAST + }; + +@@ -3094,8 +3140,6 @@ + wl_iw_set_leddc, + wl_iw_set_vlanmode, + wl_iw_set_pm, +-#if WIRELESS_EXT > 17 +-#endif /* WIRELESS_EXT > 17 */ + NULL + }; + +@@ -3118,8 +3162,6 @@ + 0, + "set_pm" + }, +-#if WIRELESS_EXT > 17 +-#endif /* WIRELESS_EXT > 17 */ + { 0, 0, 0, { 0 } } + }; + +@@ -3383,8 +3425,13 @@ + cmd = SIOCGIWAP; + wrqu.data.length = strlen(extra); + if (!(flags & WLC_EVENT_MSG_LINK)) { ++ printf("%s: Link Down with BSSID="MACSTR"\n", __FUNCTION__, ++ MAC2STR((u8 *)wrqu.addr.sa_data)); + bzero(wrqu.addr.sa_data, ETHER_ADDR_LEN); + bzero(&extra, ETHER_ADDR_LEN); ++ } else { ++ printf("%s: Link UP with BSSID="MACSTR"\n", __FUNCTION__, ++ MAC2STR((u8 *)wrqu.addr.sa_data)); + } + break; + case WLC_E_ACTION_FRAME: +@@ -3482,9 +3529,11 @@ + } + + if (cmd) { +- if (cmd == SIOCGIWSCAN) +- wireless_send_event(dev, cmd, &wrqu, NULL); +- else ++ if (cmd == SIOCGIWSCAN) { ++ if ((!g_iscan) || (g_iscan->sysioc_pid < 0)) { ++ wireless_send_event(dev, cmd, &wrqu, NULL); ++ }; ++ } else + wireless_send_event(dev, cmd, &wrqu, extra); + } + +@@ -3741,6 +3790,7 @@ + uint32 status; + iscan_info_t *iscan = (iscan_info_t *)data; + ++ printf("%s: thread Enter\n", __FUNCTION__); + DAEMONIZE("iscan_sysioc"); + + status = WL_SCAN_RESULTS_PARTIAL; +@@ -3796,6 +3846,7 @@ + break; + } + } ++ printf("%s: was terminated\n", __FUNCTION__); + complete_and_exit(&iscan->sysioc_exited, 0); + } + +@@ -3804,7 +3855,7 @@ + { + iscan_info_t *iscan = NULL; + +- WL_TRACE(("%s: iscan=%p\n", __FUNCTION__, iscan)); ++ printf("%s: Enter\n", __FUNCTION__); + + if (!dev) + return 0;