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;