diff options
Diffstat (limited to 'drivers/net/wireless/bcmdhd/dhd_sdio.c')
-rw-r--r-- | drivers/net/wireless/bcmdhd/dhd_sdio.c | 2755 |
1 files changed, 1514 insertions, 1241 deletions
diff --git a/drivers/net/wireless/bcmdhd/dhd_sdio.c b/drivers/net/wireless/bcmdhd/dhd_sdio.c index 138e8e5..745ebf7 100644 --- a/drivers/net/wireless/bcmdhd/dhd_sdio.c +++ b/drivers/net/wireless/bcmdhd/dhd_sdio.c @@ -1,7 +1,7 @@ /* * DHD Bus Module for SDIO * - * Copyright (C) 1999-2012, Broadcom Corporation + * 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 @@ -21,7 +21,7 @@ * 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: dhd_sdio.c 394690 2013-04-03 09:18:44Z $ + * $Id: dhd_sdio.c 456607 2014-02-19 09:26:42Z $ */ #include <typedefs.h> @@ -69,16 +69,29 @@ #include <dhdioctl.h> #include <sdiovar.h> +#ifdef PROP_TXSTATUS +#include <dhd_wlfc.h> +#endif +#ifdef DHDTCPACK_SUPPRESS +#include <dhd_ip.h> +#endif /* DHDTCPACK_SUPPRESS */ + +bool dhd_mp_halting(dhd_pub_t *dhdp); +extern void bcmsdh_waitfor_iodrain(void *sdh); +extern void bcmsdh_reject_ioreqs(void *sdh, bool reject); +extern bool bcmsdh_fatal_error(void *sdh); + #ifndef DHDSDIO_MEM_DUMP_FNAME #define DHDSDIO_MEM_DUMP_FNAME "mem_dump" #endif -#define QLEN 256 /* bulk rx and tx queue lengths */ +#define QLEN (1024) /* bulk rx and tx queue lengths */ #define FCHI (QLEN - 10) #define FCLOW (FCHI / 2) #define PRIOMASK 7 #define TXRETRIES 2 /* # of retries for tx frames */ +#define READ_FRM_CNT_RETRIES 3 #ifndef DHD_RXBOUND #define DHD_RXBOUND 50 /* Default for max rx frames in one scheduling */ #endif @@ -91,7 +104,7 @@ #define MEMBLOCK 2048 /* Block size used for downloading of dongle image */ #define MAX_NVRAMBUF_SIZE 4096 /* max nvram buf size */ -#define MAX_DATA_BUF (32 * 1024) /* Must be large enough to hold biggest possible glom */ +#define MAX_DATA_BUF (64 * 1024) /* Must be large enough to hold biggest possible glom */ #ifndef DHD_FIRSTREAD #define DHD_FIRSTREAD 32 @@ -100,16 +113,10 @@ #error DHD_FIRSTREAD is not a power of 2! #endif -#ifdef BCMSDIOH_TXGLOM -/* Total length of TX frame header for dongle protocol */ -#define SDPCM_HDRLEN (SDPCM_FRAMETAG_LEN + SDPCM_HWEXT_LEN + SDPCM_SWHEADER_LEN) -/* Total length of RX frame for dongle protocol */ -#else -/* Total length of TX frame header for dongle protocol */ +/* Total length of frame header for dongle protocol */ #define SDPCM_HDRLEN (SDPCM_FRAMETAG_LEN + SDPCM_SWHEADER_LEN) -#endif - -#define SDPCM_HDRLEN_RX (SDPCM_FRAMETAG_LEN + SDPCM_SWHEADER_LEN) +#define SDPCM_HDRLEN_TXGLOM (SDPCM_HDRLEN + SDPCM_HWEXT_LEN) +#define MAX_TX_PKTCHAIN_CNT SDPCM_MAXGLOM_SIZE #ifdef SDTEST #define SDPCM_RESERVE (SDPCM_HDRLEN + SDPCM_TEST_HDRLEN + DHD_SDALIGN) @@ -140,6 +147,12 @@ #define PMU_MAX_TRANSITION_DLY 1000000 #endif +/* hooks for limiting threshold custom tx num in rx processing */ +#define DEFAULT_TXINRX_THRES 0 +#ifndef CUSTOM_TXINRX_THRES +#define CUSTOM_TXINRX_THRES DEFAULT_TXINRX_THRES +#endif + /* Value for ChipClockCSR during initial setup */ #define DHD_INIT_CLKCTL1 (SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ) #define DHD_INIT_CLKCTL2 (SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_FORCE_ALP) @@ -153,17 +166,12 @@ #define PKTFREE2() if ((bus->bus != SPI_BUS) || bus->usebufpool) \ PKTFREE(bus->dhd->osh, pkt, FALSE); DHD_SPINWAIT_SLEEP_INIT(sdioh_spinwait_sleep); -#if defined(OOB_INTR_ONLY) || defined(BCMSPI_ANDROID) -extern void bcmsdh_set_irq(int flag); -#endif /* defined(OOB_INTR_ONLY) || defined(BCMSPI_ANDROID) */ -#ifdef PROP_TXSTATUS -extern void dhd_wlfc_txcomplete(dhd_pub_t *dhd, void *txp, bool success); -extern void dhd_wlfc_trigger_pktcommit(dhd_pub_t *dhd); -#endif +#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 #ifdef DHD_DEBUG /* Device console log buffer state */ @@ -196,7 +204,8 @@ typedef struct dhd_console { #define GPIO_DEV_SRSTATE_TIMEOUT 320000 /* 320ms */ #define GPIO_DEV_WAKEUP 17 /* Host gpio17 mapped to device gpio1 wakeup */ #define CC_CHIPCTRL2_GPIO1_WAKEUP (1 << 0) -#define OVERFLOW_BLKSZ512_WM 48 +#define CC_CHIPCTRL3_SR_ENG_ENABLE (1 << 2) +#define OVERFLOW_BLKSZ512_WM 96 #define OVERFLOW_BLKSZ512_MES 80 #define CC_PMUCC3 (0x3) @@ -219,7 +228,9 @@ typedef struct dhd_bus { uint32 srmemsize; /* Size of SRMEM */ uint32 bus; /* gSPI or SDIO bus */ - uint32 hostintmask; /* Copy of Host Interrupt Mask */ + uint32 bus_num; /* bus number */ + uint32 slot_num; /* slot ID */ + uint32 hostintmask; /* Copy of Host Interrupt Mask */ uint32 intstatus; /* Intstatus bits (events) pending */ bool dpc_sched; /* Indicates DPC schedule (intrpt rcvd) */ bool fcstate; /* State of dongle flow-control */ @@ -290,6 +301,7 @@ typedef struct dhd_bus { bool alp_only; /* Don't use HT clock (ALP only) */ /* Field to decide if rx of control frames happen in rxbuf or lb-pool */ bool usebufpool; + int32 txinrx_thres; /* num of in-queued pkts */ #ifdef SDTEST /* external loopback */ @@ -348,7 +360,10 @@ typedef struct dhd_bus { #ifdef BCMSPI bool dwordmode; #endif /* BCMSPI */ - +#ifdef DHDENABLE_TAILPAD + uint tx_tailpad_chain; /* Number of tail padding by chaining pad_pkt */ + uint tx_tailpad_pktget; /* Number of tail padding by new PKTGET */ +#endif uint8 *ctrl_frame_buf; uint32 ctrl_frame_len; bool ctrl_frame_stat; @@ -367,14 +382,13 @@ typedef struct dhd_bus { bool reqbussleep; uint32 resetinstr; uint32 dongle_ram_base; -#ifdef BCMSDIOH_TXGLOM + void *glom_pkt_arr[SDPCM_MAXGLOM_SIZE]; /* Array of pkts for glomming */ - uint16 glom_cnt; /* Number of pkts in the glom array */ - uint16 glom_total_len; /* Total length of pkts in glom array */ - bool glom_enable; /* Flag to indicate whether tx glom is enabled/disabled */ - uint8 glom_mode; /* Glom mode - 0-copy mode, 1 - Multi-descriptor mode */ - uint32 glomsize; /* Glom size limitation */ -#endif + uint32 txglom_cnt; /* Number of pkts in the glom array */ + uint32 txglom_total_len; /* Total length of pkts in glom array */ + bool txglom_enable; /* Flag to indicate whether tx glom is enabled/disabled */ + uint32 txglomsize; /* Glom size limitation */ + void *pad_pkt; } dhd_bus_t; /* clkstate */ @@ -394,6 +408,10 @@ static int tx_packets[NUMPRIO]; const uint dhd_deferred_tx = 1; extern uint dhd_watchdog_ms; +#ifdef BCMSPI_ANDROID +extern uint *dhd_spi_lockcount; +#endif /* BCMSPI_ANDROID */ + extern void dhd_os_wd_timer(void *bus, uint wdtick); /* Tx/Rx bounds */ @@ -402,14 +420,15 @@ uint dhd_rxbound; uint dhd_txminmax = DHD_TXMINMAX; /* override the RAM size if possible */ -#define DONGLE_MIN_MEMSIZE (128 *1024) -int dhd_dongle_memsize; +#define DONGLE_MIN_RAMSIZE (128 *1024) +int dhd_dongle_ramsize; + +uint dhd_doflow = TRUE; +uint dhd_dpcpoll = FALSE; + +module_param(dhd_doflow, uint, 0644); +module_param(dhd_dpcpoll, uint, 0644); -#ifndef REPEAT_READFRAME -static bool dhd_doflow; -#else -extern bool dhd_doflow; -#endif /* REPEAT_READFRAME */ static bool dhd_alignctl; static bool sd1idle; @@ -419,19 +438,14 @@ static bool retrydata; #ifdef BCMSPI /* At a watermark around 8 the spid hits underflow error. */ -static const uint watermark = 32; -static const uint mesbusyctrl = 0; -#elif defined(SDIO_CRC_ERROR_FIX) -static uint watermark = 48; -static uint mesbusyctrl = 80; +static uint watermark = 32; +static uint mesbusyctrl = 0; #else -static const uint watermark = 8; -static const uint mesbusyctrl = 0; +static uint watermark = 8; +static uint mesbusyctrl = 0; #endif /* BCMSPI */ static const uint firstread = DHD_FIRSTREAD; -#define HDATLEN (firstread - (SDPCM_HDRLEN)) - /* Retry count for register access failures */ static const uint retry_limit = 2; @@ -449,13 +463,13 @@ extern void bcmsdh_enable_hw_oob_intr(void *sdh, bool enable); #endif /* defined(OOB_INTR_ONLY) && defined(SDIO_ISR_THREAD) */ #define PKTALIGN(osh, p, len, align) \ do { \ - uint datalign; \ + uintptr datalign; \ datalign = (uintptr)PKTDATA((osh), (p)); \ datalign = ROUNDUP(datalign, (align)) - datalign; \ ASSERT(datalign < (align)); \ ASSERT(PKTLEN((osh), (p)) >= ((len) + datalign)); \ if (datalign) \ - PKTPULL((osh), (p), datalign); \ + PKTPULL((osh), (p), (uint)datalign); \ PKTSETLEN((osh), (p), (len)); \ } while (0) @@ -604,19 +618,20 @@ static bool dhdsdio_probe_init(dhd_bus_t *bus, osl_t *osh, void *sdh); static void dhdsdio_release_dongle(dhd_bus_t *bus, osl_t *osh, bool dongle_isolation, bool reset_flag); -static void dhd_dongle_setmemsize(struct dhd_bus *bus, int mem_size); +static void dhd_dongle_setramsize(struct dhd_bus *bus, int mem_size); static int dhd_bcmsdh_recv_buf(dhd_bus_t *bus, uint32 addr, uint fn, uint flags, uint8 *buf, uint nbytes, void *pkt, bcmsdh_cmplt_fn_t complete, void *handle); static int dhd_bcmsdh_send_buf(dhd_bus_t *bus, uint32 addr, uint fn, uint flags, uint8 *buf, uint nbytes, - void *pkt, bcmsdh_cmplt_fn_t complete, void *handle); -#ifdef BCMSDIOH_TXGLOM -static void dhd_bcmsdh_glom_post(dhd_bus_t *bus, uint8 *frame, void *pkt, uint len); -static void dhd_bcmsdh_glom_clear(dhd_bus_t *bus); -#endif - -static bool dhdsdio_download_firmware(dhd_bus_t *bus, osl_t *osh, void *sdh); + void *pkt, bcmsdh_cmplt_fn_t complete, void *handle, int max_retry); +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); +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); static int _dhdsdio_download_firmware(dhd_bus_t *bus); static int dhdsdio_download_code_file(dhd_bus_t *bus, char *image_path); @@ -634,31 +649,43 @@ extern uint32 dhd_get_htsf(void *dhd, int ifidx); #endif /* WLMEDIA_HTSF */ static void -dhd_overflow_war(struct dhd_bus *bus) +dhdsdio_tune_fifoparam(struct dhd_bus *bus) { int err; uint8 devctl, wm, mes; - /* See .ppt in PR for these recommended values */ - if (bus->blocksize == 512) { - wm = OVERFLOW_BLKSZ512_WM; - mes = OVERFLOW_BLKSZ512_MES; + if (bus->sih->buscorerev >= 15) { + /* See .ppt in PR for these recommended values */ + if (bus->blocksize == 512) { + wm = OVERFLOW_BLKSZ512_WM; + mes = OVERFLOW_BLKSZ512_MES; + } else { + mes = bus->blocksize/4; + wm = bus->blocksize/4; + } + + watermark = wm; + mesbusyctrl = mes; } else { - mes = bus->blocksize/4; - wm = bus->blocksize/4; + DHD_INFO(("skip fifotune: SdioRev(%d) is lower than minimal requested ver\n", + bus->sih->buscorerev)); + return; } - /* Update watermark */ - bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_WATERMARK, wm, &err); + if (wm > 0) { + bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_WATERMARK, wm, &err); - devctl = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, &err); - devctl |= SBSDIO_DEVCTL_F2WM_ENAB; - bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, devctl, &err); + devctl = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, &err); + devctl |= SBSDIO_DEVCTL_F2WM_ENAB; + bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, devctl, &err); + } /* Update MES */ - bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_MESBUSYCTRL, - (mes | SBSDIO_MESBUSYCTRL_ENAB), &err); + if (mes > 0) { + bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_MESBUSYCTRL, + (mes | SBSDIO_MESBUSYCTRL_ENAB), &err); + } DHD_INFO(("Apply overflow WAR: 0x%02x 0x%02x 0x%02x\n", bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, &err), @@ -667,15 +694,15 @@ dhd_overflow_war(struct dhd_bus *bus) } static void -dhd_dongle_setmemsize(struct dhd_bus *bus, int mem_size) +dhd_dongle_setramsize(struct dhd_bus *bus, int mem_size) { - int32 min_size = DONGLE_MIN_MEMSIZE; - /* Restrict the memsize to user specified limit */ + int32 min_size = DONGLE_MIN_RAMSIZE; + /* Restrict the ramsize to user specified limit */ DHD_ERROR(("user: Restrict the dongle ram size to %d, min accepted %d\n", - dhd_dongle_memsize, min_size)); - if ((dhd_dongle_memsize > min_size) && - (dhd_dongle_memsize < (int32)bus->orig_ramsize)) - bus->ramsize = dhd_dongle_memsize; + dhd_dongle_ramsize, min_size)); + if ((dhd_dongle_ramsize > min_size) && + (dhd_dongle_ramsize < (int32)bus->orig_ramsize)) + bus->ramsize = dhd_dongle_ramsize; } static int @@ -748,6 +775,7 @@ dhdsdio_sr_cap(dhd_bus_t *bus) { bool cap = FALSE; uint32 core_capext, addr, data; + if (bus->sih->chip == BCM4324_CHIP_ID) { addr = SI_ENUM_BASE + OFFSETOF(chipcregs_t, chipcontrol_addr); data = SI_ENUM_BASE + OFFSETOF(chipcregs_t, chipcontrol_data); @@ -755,7 +783,12 @@ dhdsdio_sr_cap(dhd_bus_t *bus) core_capext = bcmsdh_reg_read(bus->sdh, data, 4); } else if (bus->sih->chip == BCM4330_CHIP_ID) { core_capext = FALSE; - } else if (bus->sih->chip == BCM4335_CHIP_ID) { + } else if ((bus->sih->chip == BCM4335_CHIP_ID) || + (bus->sih->chip == BCM4339_CHIP_ID) || + (bus->sih->chip == BCM43349_CHIP_ID) || + (bus->sih->chip == BCM4345_CHIP_ID) || + (bus->sih->chip == BCM4354_CHIP_ID) || + (bus->sih->chip == BCM4350_CHIP_ID)) { core_capext = TRUE; } else { core_capext = bcmsdh_reg_read(bus->sdh, CORE_CAPEXT_ADDR, 4); @@ -767,13 +800,23 @@ dhdsdio_sr_cap(dhd_bus_t *bus) if (bus->sih->chip == BCM4324_CHIP_ID) { /* FIX: Should change to query SR control register instead */ cap = TRUE; - } else if (bus->sih->chip == BCM4335_CHIP_ID) { + } else if ((bus->sih->chip == BCM4335_CHIP_ID) || + (bus->sih->chip == BCM4339_CHIP_ID) || + (bus->sih->chip == BCM43349_CHIP_ID) || + (bus->sih->chip == BCM4345_CHIP_ID) || + (bus->sih->chip == BCM4354_CHIP_ID) || + (bus->sih->chip == BCM4350_CHIP_ID)) { uint32 enabval = 0; addr = SI_ENUM_BASE + OFFSETOF(chipcregs_t, chipcontrol_addr); data = SI_ENUM_BASE + OFFSETOF(chipcregs_t, chipcontrol_data); bcmsdh_reg_write(bus->sdh, addr, 4, CC_PMUCC3); enabval = bcmsdh_reg_read(bus->sdh, data, 4); + if ((bus->sih->chip == BCM4350_CHIP_ID) || + (bus->sih->chip == BCM4345_CHIP_ID) || + (bus->sih->chip == BCM4354_CHIP_ID)) + enabval &= CC_CHIPCTRL3_SR_ENG_ENABLE; + if (enabval) cap = TRUE; } else { @@ -789,7 +832,6 @@ dhdsdio_sr_cap(dhd_bus_t *bus) static int dhdsdio_srwar_init(dhd_bus_t *bus) { - bcmsdh_gpio_init(bus->sdh); #ifdef USE_OOB_GPIO1 @@ -860,6 +902,10 @@ dhdsdio_clk_kso_init(dhd_bus_t *bus) #define KSO_DBG(x) #define KSO_WAIT_US 50 +#define KSO_WAIT_MS 1 +#define KSO_SLEEP_RETRY_COUNT 20 +#define ERROR_BCME_NODEVICE_MAX 1 + #if defined(CUSTOMER_HW4) #define MAX_KSO_ATTEMPTS 64 #else @@ -882,7 +928,7 @@ dhdsdio_clk_kso_enab(dhd_bus_t *bus, bool on) cmp_val = SBSDIO_FUNC1_SLEEPCSR_KSO_MASK | SBSDIO_FUNC1_SLEEPCSR_DEVON_MASK; bmask = cmp_val; - msleep(3); + OSL_SLEEP(3); } else { /* Put device to sleep, turn off KSO */ cmp_val = 0; @@ -895,7 +941,11 @@ dhdsdio_clk_kso_enab(dhd_bus_t *bus, bool on) break; KSO_DBG(("%s> KSO wr/rd retry:%d, ERR:%x \n", __FUNCTION__, try_cnt, err)); - OSL_DELAY(KSO_WAIT_US); + + if (((try_cnt + 1) % KSO_SLEEP_RETRY_COUNT) == 0) { + OSL_SLEEP(KSO_WAIT_MS); + } else + OSL_DELAY(KSO_WAIT_US); bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SLEEPCSR, wr_val, &err); } while (try_cnt++ < MAX_KSO_ATTEMPTS); @@ -935,16 +985,7 @@ dhdsdio_clk_kso_iovar(dhd_bus_t *bus, bool on) dhdsdio_clkctl(bus, CLK_SDONLY, FALSE); } - /* Double-write to be safe in case transition of AOS */ - dhdsdio_clk_kso_enab(bus, TRUE); dhdsdio_clk_kso_enab(bus, TRUE); - OSL_DELAY(4000); - - /* Wait for device ready during transition to wake-up */ - SPINWAIT(((dhdsdio_sleepcsr_get(bus)) != - (SBSDIO_FUNC1_SLEEPCSR_KSO_MASK | - SBSDIO_FUNC1_SLEEPCSR_DEVON_MASK)), - (10000)); DHD_ERROR(("%s: sleepcsr: 0x%x\n", __FUNCTION__, dhdsdio_sleepcsr_get(bus))); @@ -1026,8 +1067,10 @@ dhdsdio_clk_devsleep_iovar(dhd_bus_t *bus, bool on) #else err = dhdsdio_clk_kso_enab(bus, FALSE); if (OOB_WAKEUP_ENAB(bus)) + { err = bcmsdh_gpioout(bus->sdh, GPIO_DEV_WAKEUP, FALSE); /* GPIO_1 is off */ -#endif + } +#endif /* USE_CMD14 */ } else { /* Exit Sleep */ /* Make sure we have SD bus access */ @@ -1037,7 +1080,8 @@ dhdsdio_clk_devsleep_iovar(dhd_bus_t *bus, bool on) } if ((bus->sih->chip == BCM4334_CHIP_ID) && (bus->sih->chiprev == 2)) { - SPINWAIT((bcmsdh_gpioin(bus->sdh, GPIO_DEV_SRSTATE) != TRUE), + SPINWAIT_SLEEP(sdioh_spinwait_sleep, + (bcmsdh_gpioin(bus->sdh, GPIO_DEV_SRSTATE) != TRUE), GPIO_DEV_SRSTATE_TIMEOUT); if (bcmsdh_gpioin(bus->sdh, GPIO_DEV_SRSTATE) == FALSE) { @@ -1073,12 +1117,13 @@ dhdsdio_clk_devsleep_iovar(dhd_bus_t *bus, bool on) } #else if (OOB_WAKEUP_ENAB(bus)) + { err = bcmsdh_gpioout(bus->sdh, GPIO_DEV_WAKEUP, TRUE); /* GPIO_1 is on */ - + } do { err = dhdsdio_clk_kso_enab(bus, TRUE); if (err) - OSL_DELAY(10000); + OSL_SLEEP(10); } while ((err != 0) && (++retry < 3)); if (err != 0) { @@ -1091,7 +1136,8 @@ dhdsdio_clk_devsleep_iovar(dhd_bus_t *bus, bool on) uint8 csr; /* Wait for device ready during transition to wake-up */ - SPINWAIT((((csr = dhdsdio_sleepcsr_get(bus)) & + SPINWAIT_SLEEP(sdioh_spinwait_sleep, + (((csr = dhdsdio_sleepcsr_get(bus)) & SBSDIO_FUNC1_SLEEPCSR_DEVON_MASK) != (SBSDIO_FUNC1_SLEEPCSR_DEVON_MASK)), (20000)); @@ -1103,10 +1149,17 @@ dhdsdio_clk_devsleep_iovar(dhd_bus_t *bus, bool on) err = BCME_NODEVICE; } - SPINWAIT((((csr = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1, + SPINWAIT_SLEEP(sdioh_spinwait_sleep, + (((csr = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, &err)) & SBSDIO_HT_AVAIL) != (SBSDIO_HT_AVAIL)), (10000)); + DHD_TRACE(("%s: SBSDIO_FUNC1_CHIPCLKCSR : 0x%x\n", __FUNCTION__, csr)); + if (!err && ((csr & SBSDIO_HT_AVAIL) != SBSDIO_HT_AVAIL)) { + DHD_ERROR(("%s:ERROR: device NOT Ready! 0x%x\n", + __FUNCTION__, csr)); + err = BCME_NODEVICE; + } } } @@ -1114,9 +1167,10 @@ dhdsdio_clk_devsleep_iovar(dhd_bus_t *bus, bool on) if (err == 0) bus->kso = on ? FALSE : TRUE; else { - DHD_ERROR(("%s: Sleep request failed: on:%d err:%d\n", __FUNCTION__, on, err)); + DHD_ERROR(("%s: Sleep request failed: kso:%d on:%d err:%d\n", + __FUNCTION__, bus->kso, on, err)); if (!on && retry > 2) - bus->kso = TRUE; + bus->kso = FALSE; } return err; @@ -1446,6 +1500,9 @@ dhdsdio_bussleep(dhd_bus_t *bus, bool sleep) (sleep ? "SLEEP" : "WAKE"), (bus->sleeping ? "SLEEP" : "WAKE"))); + if (bus->dhd->hang_was_sent) + return BCME_ERROR; + /* Done if we're already in the requested state */ if (sleep == bus->sleeping) return BCME_OK; @@ -1454,7 +1511,8 @@ dhdsdio_bussleep(dhd_bus_t *bus, bool sleep) if (sleep) { /* Don't sleep if something is pending */ #ifdef CUSTOMER_HW4 - if (bus->dpc_sched || bus->rxskip || pktq_len(&bus->txq) || bus->readframes) + if (bus->dpc_sched || bus->rxskip || pktq_len(&bus->txq) || bus->readframes || + bus->ctrl_frame_stat) #else if (bus->dpc_sched || bus->rxskip || pktq_len(&bus->txq)) #endif /* CUSTOMER_HW4 */ @@ -1568,11 +1626,13 @@ int dhdsdio_func_blocksize(dhd_pub_t *dhd, int function_num, int block_size) } #endif /* CUSTOMER_HW4 && USE_DYNAMIC_F2_BLKSIZE */ -#if defined(OOB_INTR_ONLY) +#if defined(OOB_INTR_ONLY) || defined(BCMSPI_ANDROID) void dhd_enable_oob_intr(struct dhd_bus *bus, bool enable) { -#if defined(HW_OOB) +#if defined(BCMSPI_ANDROID) + bcmsdh_intr_enable(bus->sdh); +#elif defined(HW_OOB) bcmsdh_enable_hw_oob_intr(bus->sdh, enable); #else sdpcmd_regs_t *regs = bus->regs; @@ -1597,745 +1657,542 @@ dhd_enable_oob_intr(struct dhd_bus *bus, bool enable) dhdsdio_clkctl(bus, CLK_SDONLY, FALSE); #endif /* !defined(HW_OOB) */ } -#endif /* defined(OOB_INTR_ONLY) */ - - -#ifdef DHDTCPACK_SUPPRESS -extern bool dhd_use_tcpack_suppress; +#endif /* defined(OOB_INTR_ONLY) || defined(BCMSPI_ANDROID) */ -/* Please be sure this function is called under dhd_os_tcpacklock() */ -void dhd_onoff_tcpack_sup(void *pub, bool on) +int +dhd_bus_txdata(struct dhd_bus *bus, void *pkt) { - dhd_pub_t *dhdp = (dhd_pub_t *)pub; - - if (dhd_use_tcpack_suppress != on) { - - DHD_ERROR(("dhd_onoff_tcpack_sup: %d -> %d\n", dhd_use_tcpack_suppress, on)); - dhd_use_tcpack_suppress = on; - dhdp->tcp_ack_info_cnt = 0; - bzero(dhdp->tcp_ack_info_tbl, sizeof(struct tcp_ack_info)*MAXTCPSTREAMS); - - } else - DHD_ERROR(("dhd_onoff_tcpack_sup: alread %d\n", on)); - - return; -} + 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 */ -inline void dhd_tcpack_check_xmit(dhd_pub_t *dhdp, void *pkt) -{ - uint8 i; - tcp_ack_info_t *tcp_ack_info = NULL; - int tbl_cnt; - - dhd_os_tcpacklock(dhdp); - tbl_cnt = dhdp->tcp_ack_info_cnt; - for (i = 0; i < tbl_cnt; i++) { - tcp_ack_info = &dhdp->tcp_ack_info_tbl[i]; - if (tcp_ack_info->p_tcpackinqueue == pkt) { - /* This pkt is being transmitted so remove the tcp_ack_info of it. - * compact the array unless the last element, - * then the pkt's array is removed. - */ - if (i < tbl_cnt-1) { - memmove(&dhdp->tcp_ack_info_tbl[i], - &dhdp->tcp_ack_info_tbl[i+1], - sizeof(struct tcp_ack_info)*(tbl_cnt - (i+1))); - } - bzero(&dhdp->tcp_ack_info_tbl[tbl_cnt-1], sizeof(struct tcp_ack_info)); - if (--dhdp->tcp_ack_info_cnt < 0) { - DHD_ERROR(("dhdsdio_sendfromq:(ERROR) tcp_ack_info_cnt %d" - " Stop using tcpack_suppress\n", dhdp->tcp_ack_info_cnt)); - dhd_onoff_tcpack_sup(dhdp, FALSE); - } - break; - } - } - dhd_os_tcpackunlock(dhdp); -} + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); -bool -dhd_tcpack_suppress(dhd_pub_t *dhdp, void *pkt) -{ - uint8 *eh_header; - uint16 eh_type; - uint8 *ip_header; - uint8 *tcp_header; - uint32 ip_hdr_len; - uint32 cur_framelen; - uint8 bdc_hdr_len = BDC_HEADER_LEN; - uint8 wlfc_hdr_len = 0; - uint8 *data = PKTDATA(dhdp->osh, pkt); - cur_framelen = PKTLEN(dhdp->osh, pkt); + osh = bus->dhd->osh; + datalen = PKTLEN(osh, pkt); -#ifdef PROP_TXSTATUS - /* In this case, BDC header is not pushed in dhd_sendpkt() */ - if (dhdp->wlfc_state) { - bdc_hdr_len = 0; - wlfc_hdr_len = 8; - } -#endif - if (cur_framelen < bdc_hdr_len + ETHER_HDR_LEN) { - DHD_TRACE(("dhd_tcpack_suppress: Too short packet length %d\n", cur_framelen)); - return FALSE; +#ifdef SDTEST + /* Push the test header if doing loopback */ + if (bus->ext_loop) { + uint8* data; + PKTPUSH(osh, pkt, SDPCM_TEST_HDRLEN); + data = PKTDATA(osh, pkt); + *data++ = SDPCM_TEST_ECHOREQ; + *data++ = (uint8)bus->loopid++; + *data++ = (datalen >> 0); + *data++ = (datalen >> 8); + datalen += SDPCM_TEST_HDRLEN; } +#else /* SDTEST */ + BCM_REFERENCE(datalen); +#endif /* SDTEST */ - /* Get rid of BDC header */ - eh_header = data + bdc_hdr_len; - cur_framelen -= bdc_hdr_len; - eh_type = eh_header[12] << 8 | eh_header[13]; +#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 (eh_type != ETHER_TYPE_IP) { - DHD_TRACE(("dhd_tcpack_suppress: Not a IP packet 0x%x\n", eh_type)); - return FALSE; + 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 */ - DHD_TRACE(("dhd_tcpack_suppress: IP pkt! 0x%x\n", eh_type)); - - ip_header = eh_header + ETHER_HDR_LEN; - cur_framelen -= ETHER_HDR_LEN; - ip_hdr_len = 4 * (ip_header[0] & 0x0f); +#if defined(DHD_TX_DUMP) && defined(DHD_TX_FULL_DUMP) + { + int i; + DHD_ERROR(("TX DUMP\n")); - if ((ip_header[0] & 0xf0) != 0x40) { - DHD_TRACE(("dhd_tcpack_suppress: Not IPv4!\n")); - return FALSE; + 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 */ - if (cur_framelen < ip_hdr_len) { - DHD_ERROR(("dhd_tcpack_suppress: IP packet length %d wrong!\n", cur_framelen)); - return FALSE; - } + prec = PRIO2PREC((PKTPRIO(pkt) & PRIOMASK)); - /* not tcp */ - if (ip_header[9] != 0x06) { - DHD_TRACE(("dhd_tcpack_suppress: Not a TCP packet 0x%x\n", ip_header[9])); - return FALSE; - } + /* Check for existing queue, current flow-control, pending event, or pending clock */ + if (dhd_deferred_tx || bus->fcstate || pktq_len(&bus->txq) || bus->dpc_sched || + (!DATAOK(bus)) || (bus->flowcontrol & NBITVAL(prec)) || + (bus->clkstate != CLK_AVAIL)) { + bool deq_ret; + int pkq_len; - DHD_TRACE(("dhd_tcpack_suppress: TCP pkt!\n")); + DHD_TRACE(("%s: deferring pktq len %d\n", __FUNCTION__, pktq_len(&bus->txq))); + bus->fcqueued++; - tcp_header = ip_header + ip_hdr_len; + /* Priority based enq */ + dhd_os_sdlock_txq(bus->dhd); + deq_ret = dhd_prec_enq(bus->dhd, &bus->txq, pkt, prec); + dhd_os_sdunlock_txq(bus->dhd); - /* is it an ack ? */ - if (tcp_header[13] == 0x10) { -#if defined(DHD_DEBUG) - uint32 tcp_seq_num = tcp_header[4] << 24 | tcp_header[5] << 16 | - tcp_header[6] << 8 | tcp_header[7]; -#endif - uint32 tcp_ack_num = tcp_header[8] << 24 | tcp_header[9] << 16 | - tcp_header[10] << 8 | tcp_header[11]; - uint16 ip_tcp_ttllen = (ip_header[3] & 0xff) + (ip_header[2] << 8); - uint32 tcp_hdr_len = 4*((tcp_header[12] & 0xf0) >> 4); - DHD_TRACE(("dhd_tcpack_suppress: TCP ACK seq %ud ack %ud\n", - tcp_seq_num, tcp_ack_num)); - - - /* zero length ? */ - if (ip_tcp_ttllen == ip_hdr_len + tcp_hdr_len) { - int i; - tcp_ack_info_t *tcp_ack_info = NULL; - DHD_TRACE(("dhd_tcpack_suppress: TCP ACK zero length\n")); - /* Look for tcp_ack_info that has the same - * ip src/dst addrs and tcp src/dst ports - */ - dhd_os_tcpacklock(dhdp); - for (i = 0; i < dhdp->tcp_ack_info_cnt; i++) { - if (dhdp->tcp_ack_info_tbl[i].p_tcpackinqueue && - !memcmp(&ip_header[12], dhdp->tcp_ack_info_tbl[i].ipaddrs, 8) && - !memcmp(tcp_header, dhdp->tcp_ack_info_tbl[i].tcpports, 4)) { - tcp_ack_info = &dhdp->tcp_ack_info_tbl[i]; - break; + if (!deq_ret) { +#ifdef PROP_TXSTATUS + if (DHD_PKTTAG_WLFCPKT(PKTTAG(pkt)) == 0) +#endif /* PROP_TXSTATUS */ + { +#ifdef DHDTCPACK_SUPPRESS + if (dhd_tcpack_check_xmit(bus->dhd, pkt) == BCME_ERROR) { + DHD_ERROR(("%s %d: tcpack_suppress ERROR!!! Stop using\n", + __FUNCTION__, __LINE__)); + dhd_tcpack_suppress_set(bus->dhd, FALSE); } +#endif /* DHDTCPACK_SUPPRESS */ + dhd_txcomplete(bus->dhd, pkt, FALSE); + PKTFREE(osh, pkt, TRUE); } + ret = BCME_NORESOURCE; + } else + ret = BCME_OK; - if (i == dhdp->tcp_ack_info_cnt && i < MAXTCPSTREAMS) - tcp_ack_info = &dhdp->tcp_ack_info_tbl[dhdp->tcp_ack_info_cnt++]; - - if (!tcp_ack_info) { - DHD_TRACE(("dhd_tcpack_suppress: No empty tcp ack info" - "%d %d %d %d, %d %d %d %d\n", - tcp_header[0], tcp_header[1], tcp_header[2], tcp_header[3], - dhdp->tcp_ack_info_tbl[i].tcpports[0], - dhdp->tcp_ack_info_tbl[i].tcpports[1], - dhdp->tcp_ack_info_tbl[i].tcpports[2], - dhdp->tcp_ack_info_tbl[i].tcpports[3])); - dhd_os_tcpackunlock(dhdp); - return FALSE; - } - - if (tcp_ack_info->p_tcpackinqueue) { - if (tcp_ack_num > tcp_ack_info->tcpack_number) { - void *prevpkt = tcp_ack_info->p_tcpackinqueue; - uint8 pushed_len = SDPCM_HDRLEN + - (BDC_HEADER_LEN - bdc_hdr_len) + wlfc_hdr_len; + dhd_os_sdlock_txq(bus->dhd); + pkq_len = pktq_len(&bus->txq); + dhd_os_sdunlock_txq(bus->dhd); + if (pkq_len >= FCHI) { + bool wlfc_enabled = FALSE; #ifdef PROP_TXSTATUS - /* In case the prev pkt is delayenqueued - * but not delayedequeued yet, it may not have - * any additional header yet. - */ - if (dhdp->wlfc_state && (PKTLEN(dhdp->osh, prevpkt) == - tcp_ack_info->ip_tcp_ttllen + ETHER_HDR_LEN)) - pushed_len = 0; + wlfc_enabled = (dhd_wlfc_flowcontrol(bus->dhd, ON, FALSE) != + WLFC_UNSUPPORTED); #endif - if ((ip_tcp_ttllen == tcp_ack_info->ip_tcp_ttllen) && - (PKTLEN(dhdp->osh, pkt) == - PKTLEN(dhdp->osh, prevpkt) - pushed_len)) { - bcopy(PKTDATA(dhdp->osh, pkt), - PKTDATA(dhdp->osh, prevpkt) + pushed_len, - PKTLEN(dhdp->osh, pkt)); - PKTFREE(dhdp->osh, pkt, FALSE); - DHD_TRACE(("dhd_tcpack_suppress: pkt 0x%p" - " TCP ACK replace %ud -> %ud\n", prevpkt, - tcp_ack_info->tcpack_number, tcp_ack_num)); - tcp_ack_info->tcpack_number = tcp_ack_num; - dhd_os_tcpackunlock(dhdp); - return TRUE; - } else - DHD_TRACE(("dhd_tcpack_suppress: len mismatch" - " %d(%d) %d(%d)\n", - PKTLEN(dhdp->osh, pkt), ip_tcp_ttllen, - PKTLEN(dhdp->osh, prevpkt), - tcp_ack_info->ip_tcp_ttllen)); - } else { -#ifdef TCPACK_TEST - void *prevpkt = tcp_ack_info->p_tcpackinqueue; + if (!wlfc_enabled && dhd_doflow) { + dhd_txflowcontrol(bus->dhd, ALL_INTERFACES, ON); + } + } + +#ifdef DHD_DEBUG + dhd_os_sdlock_txq(bus->dhd); + if (pktq_plen(&bus->txq, prec) > qcount[prec]) + qcount[prec] = pktq_plen(&bus->txq, prec); + dhd_os_sdunlock_txq(bus->dhd); #endif - DHD_TRACE(("dhd_tcpack_suppress: TCP ACK number reverse" - " prev %ud (0x%p) new %ud (0x%p)\n", - tcp_ack_info->tcpack_number, - tcp_ack_info->p_tcpackinqueue, - tcp_ack_num, pkt)); -#ifdef TCPACK_TEST - if (PKTLEN(dhdp->osh, pkt) == PKTLEN(dhdp->osh, prevpkt)) { - PKTFREE(dhdp->osh, pkt, FALSE); - dhd_os_tcpackunlock(dhdp); - return TRUE; - } + + /* 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); + } + } else { + int chan = SDPCM_DATA_CHANNEL; + +#ifdef SDTEST + chan = (bus->ext_loop ? SDPCM_TEST_CHANNEL : SDPCM_DATA_CHANNEL); #endif - } - } else { - tcp_ack_info->p_tcpackinqueue = pkt; - tcp_ack_info->tcpack_number = tcp_ack_num; - tcp_ack_info->ip_tcp_ttllen = ip_tcp_ttllen; - bcopy(&ip_header[12], tcp_ack_info->ipaddrs, 8); - bcopy(tcp_header, tcp_ack_info->tcpports, 4); - } - dhd_os_tcpackunlock(dhdp); - } else - DHD_TRACE(("dhd_tcpack_suppress: TCP ACK with DATA len %d\n", - ip_tcp_ttllen - ip_hdr_len - tcp_hdr_len)); + /* Lock: we're about to use shared data/code (and SDIO) */ + dhd_os_sdlock(bus->dhd); + + /* Otherwise, send it now */ + BUS_WAKE(bus); + /* Make sure back plane ht clk is on, no pending allowed */ + dhdsdio_clkctl(bus, CLK_AVAIL, TRUE); + + ret = dhdsdio_txpkt(bus, chan, &pkt, 1, TRUE); + + if (ret != BCME_OK) + bus->dhd->tx_errors++; + else + bus->dhd->dstats.tx_bytes += datalen; + + if ((bus->idletime == DHD_IDLE_IMMEDIATE) && !bus->dpc_sched) { + bus->activity = FALSE; + dhdsdio_clkctl(bus, CLK_NONE, TRUE); + } + + dhd_os_sdunlock(bus->dhd); } - return FALSE; -} -#endif /* DHDTCPACK_SUPPRESS */ + 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(dhd_bus_t *bus, void *pkt, uint chan, bool free_pkt, bool queue_only) +/* align packet data pointer and packet length to n-byte boundary, process packet headers, + * a new packet may be allocated if there is not enough head and/or tail from for padding. + * the caller is responsible for updating the glom size in the head packet (when glom is + * used) + * + * pad_pkt_len: returns the length of extra padding needed from the padding packet, this parameter + * is taken in tx glom mode only + * + * new_pkt: out, pointer of the new packet allocated due to insufficient head room for alignment + * padding, NULL if not needed, the caller is responsible for freeing the new packet + * + * return: positive value - length of the packet, including head and tail padding + * negative value - errors + */ +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 ret; osl_t *osh; uint8 *frame; - uint16 len, pad1 = 0; + int pkt_len; + int modulo; + int head_padding; + int tail_padding = 0; uint32 swheader; - uint retries = 0; - uint32 real_pad = 0; - bcmsdh_info_t *sdh; - void *new; - int i; - 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__)); + uint32 swhdr_offset; + bool alloc_new_pkt = FALSE; + uint8 sdpcm_hdrlen = bus->txglom_enable ? SDPCM_HDRLEN_TXGLOM : SDPCM_HDRLEN; - sdh = bus->sdh; + *new_pkt = NULL; osh = bus->dhd->osh; #ifdef DHDTCPACK_SUPPRESS - if (dhd_use_tcpack_suppress) { - dhd_tcpack_check_xmit(bus->dhd, pkt); + 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, FALSE); } #endif /* DHDTCPACK_SUPPRESS */ - if (bus->dhd->dongle_reset) { - ret = BCME_NOTREADY; - goto done; - } + /* Add space for the SDPCM hardware/software headers */ + PKTPUSH(osh, pkt, sdpcm_hdrlen); + ASSERT(ISALIGNED((uintptr)PKTDATA(osh, pkt), 2)); frame = (uint8*)PKTDATA(osh, pkt); + pkt_len = (uint16)PKTLEN(osh, pkt); #ifdef WLMEDIA_HTSF + frame = (uint8*)PKTDATA(osh, pkt); if (PKTLEN(osh, pkt) >= 100) { - p = PKTDATA(osh, pkt); - htsf_ts = (htsfts_t*) (p + HTSF_HOSTOFFSET + 12); + htsf_ts = (htsfts_t*) (frame + 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 DHD_DEBUG + if (PKTPRIO(pkt) < ARRAYSIZE(tx_packets)) + tx_packets[PKTPRIO(pkt)]++; +#endif /* DHD_DEBUG */ - /* 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; + /* align the data pointer, allocate a new packet if there is not enough space (new + * packet data pointer will be aligned thus no padding will be needed) + */ + head_padding = (ulong)frame % DHD_SDALIGN; + if (PKTHEADROOM(osh, pkt) < head_padding) { + head_padding = 0; + alloc_new_pkt = TRUE; + } else { + uint cur_chain_total_len; + int chain_tail_padding = 0; + + /* All packets need to be aligned by DHD_SDALIGN */ + modulo = (pkt_len + head_padding) % DHD_SDALIGN; + tail_padding = modulo > 0 ? (DHD_SDALIGN - modulo) : 0; + + /* Total pkt chain length needs to be aligned by block size, + * unless it is a single pkt chain with total length less than one block size, + * which we prefer sending by byte mode. + * + * Do the chain alignment here if + * 1. This is the last pkt of the chain of multiple pkts or a single pkt. + * 2-1. This chain is of multiple pkts, or + * 2-2. This is a single pkt whose size is longer than one block size. + */ + cur_chain_total_len = prev_chain_total_len + + (head_padding + pkt_len + tail_padding); + if (last_chained_pkt && bus->blocksize != 0 && + (cur_chain_total_len > (int)bus->blocksize || prev_chain_total_len > 0)) { + modulo = cur_chain_total_len % bus->blocksize; + chain_tail_padding = modulo > 0 ? (bus->blocksize - modulo) : 0; + } + +#ifdef DHDENABLE_TAILPAD + if (PKTTAILROOM(osh, pkt) < tail_padding) { + /* We don't have tail room to align by DHD_SDALIGN */ + alloc_new_pkt = TRUE; + bus->tx_tailpad_pktget++; + } else if (PKTTAILROOM(osh, pkt) < tail_padding + chain_tail_padding) { + /* We have tail room for tail_padding of this pkt itself, but not for + * total pkt chain alignment by block size. + * Use the padding packet to avoid memory copy if applicable, + * otherwise, just allocate a new pkt. + */ + if (bus->pad_pkt) { + *pad_pkt_len = chain_tail_padding; + bus->tx_tailpad_chain++; + } else { + alloc_new_pkt = TRUE; + bus->tx_tailpad_pktget++; } + } else + /* This last pkt's tailroom is sufficient to hold both tail_padding + * of the pkt itself and chain_tail_padding of total pkt chain + */ +#endif /* DHDENABLE_TAILPAD */ + tail_padding += chain_tail_padding; + } - 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); + DHD_INFO(("%s sdhdr len + orig_pkt_len %d h_pad %d t_pad %d pad_pkt_len %d\n", + __FUNCTION__, pkt_len, head_padding, tail_padding, *pad_pkt_len)); - ASSERT((pad1 + SDPCM_HDRLEN) <= (int) PKTLEN(osh, pkt)); - bzero(frame, pad1 + SDPCM_HDRLEN); - } - } - ASSERT(pad1 < DHD_SDALIGN); + if (alloc_new_pkt) { + void *tmp_pkt; + int newpkt_size; + int cur_total_len; - /* 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); + ASSERT(*pad_pkt_len == 0); -#ifdef BCMSDIOH_TXGLOM - if (bus->glom_enable) { - uint32 hwheader1 = 0, hwheader2 = 0, act_len = len; + DHD_INFO(("%s allocating new packet for padding\n", __FUNCTION__)); - /* Software tag: channel, sequence number, data offset */ - swheader = ((chan << SDPCM_CHANNEL_SHIFT) & SDPCM_CHANNEL_MASK) | - ((bus->tx_seq + bus->glom_cnt) % SDPCM_SEQUENCE_WRAP) | - (((pad1 + SDPCM_HDRLEN) << 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) { - uint8 alignment = ALIGNMENT; -#if defined(BCMLXSDMMC) && defined(CUSTOMER_HW4) - if (bus->glom_mode == SDPCM_TXGLOM_MDESC) - alignment = DHD_SDALIGN; -#endif /* defined(BCMLXSDMMC) && defined(CUSTOMER_HW4) */ - 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(("padding error size %d\n", real_pad)); - } - } -#ifdef BCMLXSDMMC - PKTSETLEN(osh, pkt, len); -#endif /* BCMLXSDMMC */ - /* Post the frame pointer to sdio glom array */ - dhd_bcmsdh_glom_post(bus, frame, pkt, len); - /* Save the pkt pointer in bus glom array */ - bus->glom_pkt_arr[bus->glom_cnt] = pkt; - bus->glom_total_len += len; - bus->glom_cnt++; - return BCME_OK; - } else { - /* Raise len to next SDIO block to eliminate tail command */ - if (bus->roundup && bus->blocksize && - ((bus->glom_total_len + len) > bus->blocksize)) { - uint16 pad2 = bus->blocksize - - ((bus->glom_total_len + len) % bus->blocksize); - if ((pad2 <= bus->roundup) && (pad2 < bus->blocksize)) { - len += pad2; - } else { - } - } else if ((bus->glom_total_len + len) % DHD_SDALIGN) { - len += DHD_SDALIGN - - ((bus->glom_total_len + len) % DHD_SDALIGN); - } - if (forcealign && (len & (ALIGNMENT - 1))) { - len = ROUNDUP(len, ALIGNMENT); - } + /* head pointer is aligned now, no padding needed */ + head_padding = 0; - /* Hardware extention tag */ - /* 2byte frame length, 1byte-, 1byte frame flag, - * 2byte-hdrlength, 2byte padlenght - */ - 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(("padding error size %d\n", real_pad)); - } - } -#ifdef BCMLXSDMMC - PKTSETLEN(osh, pkt, len); -#endif /* BCMLXSDMMC */ - /* Post the frame pointer to sdio glom array */ - dhd_bcmsdh_glom_post(bus, frame, pkt, len); - /* Save the pkt pointer in bus glom array */ - bus->glom_pkt_arr[bus->glom_cnt] = pkt; - bus->glom_cnt++; - bus->glom_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->glom_total_len); - *(((uint16*)frame_tmp) + 1) = htol16(~bus->glom_total_len); + /* update the tail padding as it depends on the head padding, since a new packet is + * allocated, the head padding is non longer needed and packet length is chagned + */ + + cur_total_len = prev_chain_total_len + pkt_len; + if (last_chained_pkt && bus->blocksize != 0 && + (cur_total_len > (int)bus->blocksize || prev_chain_total_len > 0)) { + modulo = cur_total_len % bus->blocksize; + tail_padding = modulo > 0 ? (bus->blocksize - modulo) : 0; + } + else { + modulo = pkt_len % DHD_SDALIGN; + tail_padding = modulo > 0 ? (DHD_SDALIGN - modulo) : 0; } - } else -#endif /* BCMSDIOH_TXGLOM */ - { - uint32 act_len = len; - /* Software tag: channel, sequence number, data offset */ - swheader = ((chan << SDPCM_CHANNEL_SHIFT) & SDPCM_CHANNEL_MASK) | bus->tx_seq | - (((pad1 + SDPCM_HDRLEN) << 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)); + newpkt_size = PKTLEN(osh, pkt) + bus->blocksize + DHD_SDALIGN; + bus->dhd->tx_realloc++; + tmp_pkt = PKTGET(osh, newpkt_size, TRUE); + if (tmp_pkt == NULL) { + DHD_ERROR(("failed to alloc new %d byte packet\n", newpkt_size)); + return BCME_NOMEM; + } + PKTALIGN(osh, tmp_pkt, PKTLEN(osh, pkt), DHD_SDALIGN); + bcopy(PKTDATA(osh, pkt), PKTDATA(osh, tmp_pkt), PKTLEN(osh, pkt)); + *new_pkt = tmp_pkt; + pkt = tmp_pkt; } -#endif -#ifndef BCMSPI - /* 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); - } -#endif /* BCMSPI */ + if (head_padding) + PKTPUSH(osh, pkt, head_padding); - /* 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(("padding error size %d\n", real_pad)); - } - } + frame = (uint8*)PKTDATA(osh, pkt); + bzero(frame, head_padding + sdpcm_hdrlen); + pkt_len = (uint16)PKTLEN(osh, pkt); + + /* the header has the followming format + * 4-byte HW frame tag: length, ~length (for glom this is the total length) + * + * 8-byte HW extesion flags (glom mode only) as the following: + * 2-byte packet length, excluding HW tag and padding + * 2-byte frame channel and frame flags (e.g. next frame following) + * 2-byte header length + * 2-byte tail padding size + * + * 8-byte SW frame tags as the following + * 4-byte flags: host tx seq, channel, data offset + * 4-byte flags: TBD + */ + + swhdr_offset = SDPCM_FRAMETAG_LEN; + + /* hardware frame tag: + * + * in tx-glom mode, dongle only checks the hardware frame tag in the first + * packet and sees it as the total lenght of the glom (including tail padding), + * for each packet in the glom, the packet length needs to be updated, (see + * below PKTSETLEN) + * + * in non tx-glom mode, PKTLEN still need to include tail padding as to be + * referred to in sdioh_request_buffer(). The tail length will be excluded in + * dhdsdio_txpkt_postprocess(). + */ + *(uint16*)frame = (uint16)htol16(pkt_len); + *(((uint16*)frame) + 1) = (uint16)htol16(~pkt_len); + pkt_len += tail_padding; + + /* hardware extesion flags */ + 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); } - do { - ret = dhd_bcmsdh_send_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2, F2SYNC, - frame, len, pkt, NULL, NULL); - bus->f2txdata++; - ASSERT(ret != BCME_PENDING); + PKTSETLEN((osh), (pkt), (pkt_len)); - 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++; + /* software frame tags */ + swheader = ((chan << SDPCM_CHANNEL_SHIFT) & SDPCM_CHANNEL_MASK) + | (txseq % SDPCM_SEQUENCE_WRAP) | + (((head_padding + sdpcm_hdrlen) << SDPCM_DOFFSET_SHIFT) & SDPCM_DOFFSET_MASK); + htol32_ua_store(swheader, frame + swhdr_offset); + htol32_ua_store(0, frame + swhdr_offset + sizeof(swheader)); - bcmsdh_abort(sdh, SDIO_FUNC_2); -#ifdef BCMSPI - DHD_ERROR(("%s: gSPI transmit error. Check Overflow or F2-fifo-not-ready" - " counters.\n", __FUNCTION__)); -#endif /* BCMSPI */ - bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_FRAMECTRL, - SFC_WF_TERM, NULL); - bus->f1regdata++; + return pkt_len; +} - for (i = 0; i < 3; 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->glom_enable) { - bus->tx_seq = (bus->tx_seq + bus->glom_cnt) % SDPCM_SEQUENCE_WRAP; - } else -#endif - { - bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP; - } - } - } while ((ret < 0) && retrydata && retries++ < TXRETRIES); +static int dhdsdio_txpkt_postprocess(dhd_bus_t *bus, void *pkt) +{ + osl_t *osh; + uint8 *frame; + int data_offset; + int tail_padding; + int swhdr_offset = SDPCM_FRAMETAG_LEN + (bus->txglom_enable ? SDPCM_HWEXT_LEN : 0); -done: + (void)osh; + osh = bus->dhd->osh; -#ifdef BCMSDIOH_TXGLOM - if (bus->glom_enable) { - dhd_bcmsdh_glom_clear(bus); - pkt_cnt = bus->glom_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->glom_enable) { -#ifdef BCMLXSDMMC - uint32 pad2 = 0; -#endif /* BCMLXSDMMC */ - pkt = bus->glom_pkt_arr[bus->glom_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 */ - { - PKTPULL(osh, pkt, SDPCM_HDRLEN + 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); + /* restore pkt buffer pointer, but keeps the header pushed by dhd_prot_hdrpush */ + frame = (uint8*)PKTDATA(osh, pkt); + + DHD_INFO(("%s PKTLEN before postprocess %d", + __FUNCTION__, PKTLEN(osh, pkt))); + + /* PKTLEN still includes tail_padding, so exclude it. + * We shall have head_padding + original pkt_len for PKTLEN afterwards. + */ + if (bus->txglom_enable) { + /* txglom pkts have tail_padding length in HW ext header */ + tail_padding = ltoh32_ua(frame + SDPCM_FRAMETAG_LEN + 4) >> 16; + PKTSETLEN(osh, pkt, PKTLEN(osh, pkt) - tail_padding); + DHD_INFO((" txglom pkt: tail_padding %d PKTLEN %d\n", + tail_padding, PKTLEN(osh, pkt))); } else { -#endif /* PROP_TXSTATUS */ -#ifdef SDTEST - if (chan != SDPCM_TEST_CHANNEL) { - dhd_txcomplete(bus->dhd, pkt, ret != 0); + /* non-txglom pkts have head_padding + original pkt length in HW frame tag. + * We cannot refer to this field for txglom pkts as the first pkt of the chain will + * have the field for the total length of the chain. + */ + PKTSETLEN(osh, pkt, *(uint16*)frame); + DHD_INFO((" non-txglom pkt: HW frame tag len %d after PKTLEN %d\n", + *(uint16*)frame, PKTLEN(osh, pkt))); } -#else /* SDTEST */ - dhd_txcomplete(bus->dhd, pkt, ret != 0); -#endif /* SDTEST */ - if (free_pkt) - PKTFREE(osh, pkt, TRUE); -#ifdef PROP_TXSTATUS - } -#endif - pkt_cnt--; - } + data_offset = ltoh32_ua(frame + swhdr_offset); + data_offset = (data_offset & SDPCM_DOFFSET_MASK) >> SDPCM_DOFFSET_SHIFT; + /* Get rid of sdpcm header + head_padding */ + PKTPULL(osh, pkt, data_offset); -#ifdef BCMSDIOH_TXGLOM - /* Reset the glom array */ - if (bus->glom_enable) { - bus->glom_cnt = 0; - bus->glom_total_len = 0; - } -#endif - return ret; + DHD_INFO(("%s data_offset %d, PKTLEN %d\n", + __FUNCTION__, data_offset, PKTLEN(osh, pkt))); + + return BCME_OK; } -int -dhd_bus_txdata(struct dhd_bus *bus, void *pkt) +static int dhdsdio_txpkt(dhd_bus_t *bus, uint chan, void** pkts, int num_pkt, bool free_pkt) { - int ret = BCME_ERROR; - osl_t *osh; - uint datalen, prec; -#ifdef DHD_TX_DUMP - uint8 *dump_data; - uint16 protocol; -#ifdef DHD_TX_FULL_DUMP int i; -#endif /* DHD_TX_FULL_DUMP */ -#endif /* DHD_TX_DUMP */ - DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + int ret = 0; + osl_t *osh; + bcmsdh_info_t *sdh; + void *pkt = NULL; + void *pkt_chain; + int total_len = 0; + void *head_pkt = NULL; + void *prev_pkt = NULL; + int pad_pkt_len = 0; + int new_pkt_num = 0; + void *new_pkts[MAX_TX_PKTCHAIN_CNT]; + bool wlfc_enabled = FALSE; + + if (bus->dhd->dongle_reset) + return BCME_NOTREADY; + sdh = bus->sdh; osh = bus->dhd->osh; - datalen = PKTLEN(osh, pkt); + /* init new_pkts[0] to make some compiler happy, not necessary as we check new_pkt_num */ + new_pkts[0] = NULL; + + for (i = 0; i < num_pkt; i++) { + int pkt_len; + bool last_pkt; + void *new_pkt = NULL; + + pkt = pkts[i]; + 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); + if (pkt_len <= 0) + goto done; + if (new_pkt) { + pkt = new_pkt; + new_pkts[new_pkt_num++] = new_pkt; + } + total_len += pkt_len; + + PKTSETNEXT(osh, pkt, NULL); + /* insert the packet into the list */ + head_pkt ? PKTSETNEXT(osh, prev_pkt, pkt) : (head_pkt = pkt); + prev_pkt = pkt; -#ifdef SDTEST - /* Push the test header if doing loopback */ - if (bus->ext_loop) { - uint8* data; - PKTPUSH(osh, pkt, SDPCM_TEST_HDRLEN); - data = PKTDATA(osh, pkt); - *data++ = SDPCM_TEST_ECHOREQ; - *data++ = (uint8)bus->loopid++; - *data++ = (datalen >> 0); - *data++ = (datalen >> 8); - datalen += SDPCM_TEST_HDRLEN; } -#endif /* SDTEST */ -#ifdef DHD_TX_DUMP - dump_data = PKTDATA(osh, pkt); - dump_data += 4; /* skip 4 bytes header */ - protocol = (dump_data[12] << 8) | dump_data[13]; -#ifdef DHD_TX_FULL_DUMP - DHD_ERROR(("TX DUMP\n")); + /* Update the HW frame tag (total length) in the first pkt of the glom */ + if (bus->txglom_enable) { + uint8 *frame; + + total_len += pad_pkt_len; + frame = (uint8*)PKTDATA(osh, head_pkt); + *(uint16*)frame = (uint16)htol16(total_len); + *(((uint16*)frame) + 1) = (uint16)htol16(~total_len); - 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_FULL_DUMP */ - 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])); + /* if a padding packet if needed, insert it to the end of the link list */ + if (pad_pkt_len) { + PKTSETLEN(osh, bus->pad_pkt, pad_pkt_len); + PKTSETNEXT(osh, pkt, bus->pad_pkt); } -#endif /* DHD_TX_DUMP */ - /* Add space for the header */ - PKTPUSH(osh, pkt, SDPCM_HDRLEN); - ASSERT(ISALIGNED((uintptr)PKTDATA(osh, pkt), 2)); + /* dhd_bcmsdh_send_buf ignores the buffer pointer if he packet + * parameter is not NULL, for non packet chian we pass NULL pkt pointer + * so it will take the aligned length and buffer pointer. + */ + pkt_chain = PKTNEXT(osh, head_pkt) ? head_pkt : NULL; + 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) + bus->tx_seq = (bus->tx_seq + num_pkt) % SDPCM_SEQUENCE_WRAP; - prec = PRIO2PREC((PKTPRIO(pkt) & PRIOMASK)); -#ifndef DHDTHREAD - /* Lock: we're about to use shared data/code (and SDIO) */ - dhd_os_sdlock(bus->dhd); -#endif /* DHDTHREAD */ + /* if a padding packet was needed, remove it from the link list as it not a data pkt */ + if (pad_pkt_len && pkt) + PKTSETNEXT(osh, pkt, NULL); - /* Check for existing queue, current flow-control, pending event, or pending clock */ - if (dhd_deferred_tx || bus->fcstate || pktq_len(&bus->txq) || bus->dpc_sched || - (!DATAOK(bus)) || (bus->flowcontrol & NBITVAL(prec)) || - (bus->clkstate != CLK_AVAIL)) { - DHD_TRACE(("%s: deferring pktq len %d\n", __FUNCTION__, - pktq_len(&bus->txq))); - bus->fcqueued++; +done: + pkt = head_pkt; + while (pkt) { + void *pkt_next = PKTNEXT(osh, pkt); + PKTSETNEXT(osh, pkt, NULL); + dhdsdio_txpkt_postprocess(bus, pkt); + pkt = pkt_next; + } - /* Priority based enq */ - dhd_os_sdlock_txq(bus->dhd); - if (dhd_prec_enq(bus->dhd, &bus->txq, pkt, prec) == FALSE) { - PKTPULL(osh, pkt, SDPCM_HDRLEN); -#ifndef DHDTHREAD - /* Need to also release txqlock before releasing sdlock. - * This thread still has txqlock and releases sdlock. - * Deadlock happens when dpc() grabs sdlock first then - * attempts to grab txqlock. - */ - dhd_os_sdunlock_txq(bus->dhd); - dhd_os_sdunlock(bus->dhd); -#endif -#ifdef PROP_TXSTATUS - if (bus->dhd->wlfc_state) - dhd_wlfc_txcomplete(bus->dhd, pkt, FALSE); - else -#endif - dhd_txcomplete(bus->dhd, pkt, FALSE); -#ifndef DHDTHREAD - dhd_os_sdlock(bus->dhd); - dhd_os_sdlock_txq(bus->dhd); -#endif + /* new packets might be allocated due to insufficient room for padding, but we + * still have to indicate the original packets to upper layer + */ + for (i = 0; i < num_pkt; i++) { + pkt = pkts[i]; + wlfc_enabled = FALSE; #ifdef PROP_TXSTATUS - /* let the caller decide whether to free the packet */ - if (!bus->dhd->wlfc_state) -#endif - PKTFREE(osh, pkt, TRUE); - ret = BCME_NORESOURCE; - } - else - ret = BCME_OK; - if ((pktq_len(&bus->txq) >= FCHI) && dhd_doflow) - dhd_txflowcontrol(bus->dhd, ALL_INTERFACES, ON); - -#ifdef DHD_DEBUG - if (pktq_plen(&bus->txq, prec) > qcount[prec]) - qcount[prec] = pktq_plen(&bus->txq, prec); -#endif - dhd_os_sdunlock_txq(bus->dhd); - - /* 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 (DHD_PKTTAG_WLFCPKT(PKTTAG(pkt))) { + wlfc_enabled = (dhd_wlfc_txcomplete(bus->dhd, pkt, ret == 0) != + WLFC_UNSUPPORTED); } - } else { -#ifdef DHDTHREAD - /* Lock: we're about to use shared data/code (and SDIO) */ - dhd_os_sdlock(bus->dhd); -#endif /* DHDTHREAD */ - - /* Otherwise, send it now */ - BUS_WAKE(bus); - /* Make sure back plane ht clk is on, no pending allowed */ - dhdsdio_clkctl(bus, CLK_AVAIL, TRUE); -#ifndef SDTEST - ret = dhdsdio_txpkt(bus, pkt, SDPCM_DATA_CHANNEL, TRUE, FALSE); -#else - ret = dhdsdio_txpkt(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; - - if ((bus->idletime == DHD_IDLE_IMMEDIATE) && !bus->dpc_sched) { - bus->activity = FALSE; - dhdsdio_clkctl(bus, CLK_NONE, TRUE); +#endif /* PROP_TXSTATUS */ + if (!wlfc_enabled) { + PKTSETNEXT(osh, pkt, NULL); + dhd_txcomplete(bus->dhd, pkt, ret != 0); + if (free_pkt) + PKTFREE(osh, pkt, TRUE); } - -#ifdef DHDTHREAD - dhd_os_sdunlock(bus->dhd); -#endif /* DHDTHREAD */ } -#ifndef DHDTHREAD - dhd_os_sdunlock(bus->dhd); -#endif /* DHDTHREAD */ + for (i = 0; i < new_pkt_num; i++) + PKTFREE(osh, new_pkts[i], TRUE); return ret; } @@ -2343,19 +2200,13 @@ dhd_bus_txdata(struct dhd_bus *bus, void *pkt) static uint dhdsdio_sendfromq(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; - uint8 txpktqlen = 0; -#ifdef BCMSDIOH_TXGLOM - uint i; - uint8 glom_cnt; -#endif - + uint16 txpktqlen = 0; + uint32 intstatus = 0; + uint retries = 0; + osl_t *osh; + uint datalen = 0; dhd_pub_t *dhd = bus->dhd; sdpcmd_regs_t *regs = bus->regs; @@ -2366,81 +2217,33 @@ dhdsdio_sendfromq(dhd_bus_t *bus, uint maxframes) return BCME_NODEVICE; } + osh = dhd->osh; 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->glom_enable) { - void *pkttable[SDPCM_MAXGLOM_SIZE]; - dhd_os_sdlock_txq(bus->dhd); - glom_cnt = MIN(DATABUFCNT(bus), bus->glomsize); - glom_cnt = MIN(glom_cnt, pktq_mlen(&bus->txq, tx_prec_map)); - glom_cnt = MIN(glom_cnt, maxframes-cnt); - - /* Limiting the size to 2pkts in case of copy */ - if (bus->glom_mode == SDPCM_TXGLOM_CPY) - glom_cnt = MIN(glom_cnt, 10); - - for (i = 0; i < glom_cnt; i++) - pkttable[i] = pktq_mdeq(&bus->txq, tx_prec_map, &prec_out); + for (cnt = 0; (cnt < maxframes) && DATAOK(bus);) { + int i; + int num_pkt = 1; + void *pkts[MAX_TX_PKTCHAIN_CNT]; + int prec_out; - txpktqlen = pktq_len(&bus->txq); - dhd_os_sdunlock_txq(bus->dhd); - - if (glom_cnt == 0) - break; - datalen = 0; - for (i = 0; i < glom_cnt; i++) { - if ((pkt = pkttable[i]) == NULL) { - /* This case should not happen */ - DHD_ERROR(("No pkts in the queue for glomming\n")); - break; - } - - datalen += (PKTLEN(bus->dhd->osh, pkt) - SDPCM_HDRLEN); -#ifndef SDTEST - ret = dhdsdio_txpkt(bus, - pkt, - SDPCM_DATA_CHANNEL, - TRUE, - (i == (glom_cnt-1))? FALSE: TRUE); -#else - ret = dhdsdio_txpkt(bus, - pkt, - (bus->ext_loop ? SDPCM_TEST_CHANNEL : SDPCM_DATA_CHANNEL), - TRUE, - (i == (glom_cnt-1))? FALSE: TRUE); -#endif - } - 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; + if (bus->txglom_enable) { + num_pkt = MIN((uint32)DATABUFCNT(bus), (uint32)bus->txglomsize); + num_pkt = MIN(num_pkt, ARRAYSIZE(pkts)); } - txpktqlen = pktq_len(&bus->txq); - dhd_os_sdunlock_txq(bus->dhd); - datalen = PKTLEN(bus->dhd->osh, pkt) - SDPCM_HDRLEN; - -#ifndef SDTEST - ret = dhdsdio_txpkt(bus, pkt, SDPCM_DATA_CHANNEL, TRUE, FALSE); -#else - ret = dhdsdio_txpkt(bus, - pkt, - (bus->ext_loop ? SDPCM_TEST_CHANNEL : SDPCM_DATA_CHANNEL), - TRUE, - FALSE); -#endif + num_pkt = MIN(num_pkt, pktq_mlen(&bus->txq, tx_prec_map)); + for (i = 0; i < num_pkt; i++) { + pkts[i] = pktq_mdeq(&bus->txq, ~bus->flowcontrol, &prec_out); + datalen += PKTLEN(osh, pkts[i]); } + dhd_os_sdunlock_txq(bus->dhd); - if (ret) - bus->dhd->tx_errors++; + if (i == 0) + break; + if (dhdsdio_txpkt(bus, SDPCM_DATA_CHANNEL, pkts, i, TRUE) != BCME_OK) + dhd->tx_errors++; else - bus->dhd->dstats.tx_bytes += datalen; + dhd->dstats.tx_bytes += datalen; + cnt += i; /* In poll mode, need to check for other events */ if (!bus->intr && cnt) @@ -2453,12 +2256,23 @@ dhdsdio_sendfromq(dhd_bus_t *bus, uint maxframes) 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); + dhd_os_sdlock_txq(bus->dhd); + txpktqlen = pktq_len(&bus->txq); + dhd_os_sdunlock_txq(bus->dhd); + + /* Do flow-control if needed */ + if (dhd->up && (dhd->busstate == DHD_BUS_DATA) && (txpktqlen < FCLOW)) { + bool wlfc_enabled = FALSE; +#ifdef PROP_TXSTATUS + wlfc_enabled = (dhd_wlfc_flowcontrol(dhd, OFF, TRUE) != WLFC_UNSUPPORTED); +#endif + if (!wlfc_enabled && dhd_doflow && dhd->txoff) { + dhd_txflowcontrol(dhd, ALL_INTERFACES, OFF); + } + } return cnt; } @@ -2467,13 +2281,11 @@ static void dhdsdio_sendpendctl(dhd_bus_t *bus) { bcmsdh_info_t *sdh = bus->sdh; - int ret, i; + int ret; uint8* frame_seq = bus->ctrl_frame_buf + SDPCM_FRAMETAG_LEN; -#ifdef BCMSDIOH_TXGLOM - if (bus->glom_enable) + if (bus->txglom_enable) frame_seq += SDPCM_HWEXT_LEN; -#endif if (*frame_seq != bus->tx_seq) { DHD_INFO(("%s IOCTL frame seq lag detected!" @@ -2483,37 +2295,10 @@ dhdsdio_sendpendctl(dhd_bus_t *bus) } 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); - 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_INFO(("%s: sdio error %d, abort command and terminate frame.\n", - __FUNCTION__, ret)); - bus->tx_sderrs++; - - bcmsdh_abort(sdh, SDIO_FUNC_2); - - bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_FRAMECTRL, - SFC_WF_TERM, NULL); - bus->f1regdata++; - - for (i = 0; i < 3; 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) { + (uint8 *)bus->ctrl_frame_buf, (uint32)bus->ctrl_frame_len, + NULL, NULL, NULL, 1); + if (ret == BCME_OK) bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP; - } bus->ctrl_frame_stat = FALSE; dhd_wait_event_wakeup(bus->dhd); @@ -2522,14 +2307,14 @@ dhdsdio_sendpendctl(dhd_bus_t *bus) int dhd_bus_txctl(struct dhd_bus *bus, uchar *msg, uint msglen) { + static int err_nodevice = 0; uint8 *frame; uint16 len; uint32 swheader; - uint retries = 0; bcmsdh_info_t *sdh = bus->sdh; uint8 doff = 0; int ret = -1; - int i; + uint8 sdpcm_hdrlen = bus->txglom_enable ? SDPCM_HDRLEN_TXGLOM : SDPCM_HDRLEN; DHD_TRACE(("%s: Enter\n", __FUNCTION__)); @@ -2537,8 +2322,8 @@ dhd_bus_txctl(struct dhd_bus *bus, uchar *msg, uint msglen) return -EIO; /* Back the pointer to make a room for bus header */ - frame = msg - SDPCM_HDRLEN; - len = (msglen += SDPCM_HDRLEN); + frame = msg - sdpcm_hdrlen; + len = (msglen += sdpcm_hdrlen); /* Add alignment padding (optional for ctl frames) */ if (dhd_alignctl) { @@ -2546,11 +2331,11 @@ dhd_bus_txctl(struct dhd_bus *bus, uchar *msg, uint msglen) frame -= doff; len += doff; msglen += doff; - bzero(frame, doff + SDPCM_HDRLEN); + bzero(frame, doff + sdpcm_hdrlen); } ASSERT(doff < DHD_SDALIGN); } - doff += SDPCM_HDRLEN; + doff += sdpcm_hdrlen; #ifndef BCMSPI /* Round send length to next SDIO block */ @@ -2582,8 +2367,7 @@ dhd_bus_txctl(struct dhd_bus *bus, uchar *msg, uint msglen) *(uint16*)frame = htol16((uint16)msglen); *(((uint16*)frame) + 1) = htol16(~msglen); -#ifdef BCMSDIOH_TXGLOM - if (bus->glom_enable) { + if (bus->txglom_enable) { uint32 hwheader1, hwheader2; /* Software tag: channel, sequence number, data offset */ swheader = ((SDPCM_CONTROL_CHANNEL << SDPCM_CHANNEL_SHIFT) & SDPCM_CHANNEL_MASK) @@ -2600,14 +2384,12 @@ dhd_bus_txctl(struct dhd_bus *bus, uchar *msg, uint msglen) *(uint16*)frame = htol16(len); *(((uint16*)frame) + 1) = htol16(~(len)); - } else -#endif /* BCMSDIOH_TXGLOM */ - { - /* Software tag: channel, sequence number, data offset */ - swheader = ((SDPCM_CONTROL_CHANNEL << SDPCM_CHANNEL_SHIFT) & SDPCM_CHANNEL_MASK) - | bus->tx_seq | ((doff << SDPCM_DOFFSET_SHIFT) & SDPCM_DOFFSET_MASK); - htol32_ua_store(swheader, frame + SDPCM_FRAMETAG_LEN); - htol32_ua_store(0, frame + SDPCM_FRAMETAG_LEN + sizeof(swheader)); + } else { + /* Software tag: channel, sequence number, data offset */ + swheader = ((SDPCM_CONTROL_CHANNEL << SDPCM_CHANNEL_SHIFT) & SDPCM_CHANNEL_MASK) + | bus->tx_seq | ((doff << SDPCM_DOFFSET_SHIFT) & SDPCM_DOFFSET_MASK); + htol32_ua_store(swheader, frame + SDPCM_FRAMETAG_LEN); + htol32_ua_store(0, frame + SDPCM_FRAMETAG_LEN + sizeof(swheader)); } if (!TXCTLOK(bus)) { DHD_INFO(("%s: No bus credit bus->tx_max %d, bus->tx_seq %d\n", @@ -2649,6 +2431,7 @@ dhd_bus_txctl(struct dhd_bus *bus, uchar *msg, uint msglen) } bus->dhd->txcnt_timeout = 0; + bus->ctrl_frame_stat = TRUE; if (ret == -1) { #ifdef DHD_DEBUG @@ -2658,47 +2441,12 @@ dhd_bus_txctl(struct dhd_bus *bus, uchar *msg, uint msglen) prhex("TxHdr", frame, MIN(len, 16)); } #endif - - do { - ret = dhd_bcmsdh_send_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2, F2SYNC, - frame, len, NULL, NULL, NULL); - 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_INFO(("%s: sdio error %d, abort command and terminate frame.\n", - __FUNCTION__, ret)); - bus->tx_sderrs++; - - bcmsdh_abort(sdh, SDIO_FUNC_2); - -#ifdef BCMSPI - DHD_ERROR(("%s: Check Overflow or F2-fifo-not-ready counters." - " gSPI transmit error on control channel.\n", - __FUNCTION__)); -#endif /* BCMSPI */ - bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_FRAMECTRL, - SFC_WF_TERM, NULL); - bus->f1regdata++; - - for (i = 0; i < 3; 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) { - bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP; - } - } while ((ret < 0) && retries++ < TXRETRIES); + ret = dhd_bcmsdh_send_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2, F2SYNC, + frame, len, NULL, NULL, NULL, TXRETRIES); + if (ret == BCME_OK) + bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP; } + bus->ctrl_frame_stat = FALSE; done: if ((bus->idletime == DHD_IDLE_IMMEDIATE) && !bus->dpc_sched) { @@ -2716,7 +2464,12 @@ done: if (bus->dhd->txcnt_timeout >= MAX_CNTL_TX_TIMEOUT) return -ETIMEDOUT; - return ret ? -EIO : 0; + if (ret == BCME_NODEVICE) + err_nodevice++; + else + err_nodevice = 0; + + return ret ? err_nodevice >= ERROR_BCME_NODEVICE_MAX ? -ETIMEDOUT : -EIO : 0; } int @@ -2801,7 +2554,8 @@ enum { IOV_SBREG, IOV_SDCIS, IOV_MEMBYTES, - IOV_MEMSIZE, + IOV_RAMSIZE, + IOV_RAMSTART, #ifdef DHD_DEBUG IOV_CHECKDIED, IOV_SERIALCONS, @@ -2816,10 +2570,10 @@ enum { IOV_SDALIGN, IOV_DEVRESET, IOV_CPU, -#if defined(SDIO_CRC_ERROR_FIX) +#if defined(USE_SDIOFIFO_IOVAR) IOV_WATERMARK, IOV_MESBUSYCTRL, -#endif /* SDIO_CRC_ERROR_FIX */ +#endif /* USE_SDIOFIFO_IOVAR */ #ifdef SDTEST IOV_PKTGEN, IOV_EXTLOOP, @@ -2841,7 +2595,9 @@ enum { IOV_FWPATH, #endif IOV_TXGLOMSIZE, - IOV_TXGLOMMODE + IOV_TXGLOMMODE, + IOV_HANGREPORT, + IOV_TXINRX_THRES }; const bcm_iovar_t dhdsdio_iovars[] = { @@ -2852,7 +2608,8 @@ const bcm_iovar_t dhdsdio_iovars[] = { {"idleclock", IOV_IDLECLOCK, 0, IOVT_INT32, 0 }, {"sd1idle", IOV_SD1IDLE, 0, IOVT_BOOL, 0 }, {"membytes", IOV_MEMBYTES, 0, IOVT_BUFFER, 2 * sizeof(int) }, - {"memsize", IOV_MEMSIZE, 0, IOVT_UINT32, 0 }, + {"ramsize", IOV_RAMSIZE, 0, IOVT_UINT32, 0 }, + {"ramstart", IOV_RAMSTART, 0, IOVT_UINT32, 0 }, {"dwnldstate", IOV_SET_DOWNLOAD_STATE, 0, IOVT_BOOL, 0 }, {"socram_state", IOV_SOCRAM_STATE, 0, IOVT_BOOL, 0 }, {"vars", IOV_VARS, 0, IOVT_BUFFER, 0 }, @@ -2880,10 +2637,10 @@ const bcm_iovar_t dhdsdio_iovars[] = { {"extloop", IOV_EXTLOOP, 0, IOVT_BOOL, 0 }, {"pktgen", IOV_PKTGEN, 0, IOVT_BUFFER, sizeof(dhd_pktgen_t) }, #endif /* SDTEST */ -#if defined(SDIO_CRC_ERROR_FIX) +#if defined(USE_SDIOFIFO_IOVAR) {"watermark", IOV_WATERMARK, 0, IOVT_UINT32, 0 }, {"mesbusyctrl", IOV_MESBUSYCTRL, 0, IOVT_UINT32, 0 }, -#endif /* SDIO_CRC_ERROR_FIX */ +#endif /* USE_SDIOFIFO_IOVAR */ {"devcap", IOV_DEVCAP, 0, IOVT_UINT32, 0 }, {"dngl_isolation", IOV_DONGLEISOLATION, 0, IOVT_UINT32, 0 }, {"kso", IOV_KSO, 0, IOVT_UINT32, 0 }, @@ -2892,7 +2649,8 @@ const bcm_iovar_t dhdsdio_iovars[] = { {"fwpath", IOV_FWPATH, 0, IOVT_BUFFER, 0 }, #endif {"txglomsize", IOV_TXGLOMSIZE, 0, IOVT_UINT32, 0 }, - {"txglommode", IOV_TXGLOMMODE, 0, IOVT_UINT32, 0 }, + {"fw_hang_report", IOV_HANGREPORT, 0, IOVT_BOOL, 0 }, + {"txinrx_thres", IOV_TXINRX_THRES, 0, IOVT_INT32, 0 }, {NULL, 0, 0, 0, 0 } }; @@ -2918,25 +2676,29 @@ dhd_bus_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf) bcm_bprintf(strbuf, "Bus SDIO structure:\n"); bcm_bprintf(strbuf, "hostintmask 0x%08x intstatus 0x%08x sdpcm_ver %d\n", bus->hostintmask, bus->intstatus, bus->sdpcm_ver); - bcm_bprintf(strbuf, "fcstate %d qlen %d tx_seq %d, max %d, rxskip %d rxlen %d rx_seq %d\n", + bcm_bprintf(strbuf, "fcstate %d qlen %u tx_seq %d, max %d, rxskip %d rxlen %u rx_seq %d\n", bus->fcstate, pktq_len(&bus->txq), bus->tx_seq, bus->tx_max, bus->rxskip, bus->rxlen, bus->rx_seq); - bcm_bprintf(strbuf, "intr %d intrcount %d lastintrs %d spurious %d\n", + bcm_bprintf(strbuf, "intr %d intrcount %u lastintrs %u spurious %u\n", bus->intr, bus->intrcount, bus->lastintrs, bus->spurious); - bcm_bprintf(strbuf, "pollrate %d pollcnt %d regfails %d\n", + bcm_bprintf(strbuf, "pollrate %u pollcnt %u regfails %u\n", bus->pollrate, bus->pollcnt, bus->regfails); bcm_bprintf(strbuf, "\nAdditional counters:\n"); - bcm_bprintf(strbuf, "tx_sderrs %d fcqueued %d rxrtx %d rx_toolong %d rxc_errors %d\n", +#ifdef DHDENABLE_TAILPAD + bcm_bprintf(strbuf, "tx_tailpad_chain %u tx_tailpad_pktget %u\n", + bus->tx_tailpad_chain, bus->tx_tailpad_pktget); +#endif + bcm_bprintf(strbuf, "tx_sderrs %u fcqueued %u rxrtx %u rx_toolong %u rxc_errors %u\n", bus->tx_sderrs, bus->fcqueued, bus->rxrtx, bus->rx_toolong, bus->rxc_errors); - bcm_bprintf(strbuf, "rx_hdrfail %d badhdr %d badseq %d\n", + bcm_bprintf(strbuf, "rx_hdrfail %u badhdr %u badseq %u\n", bus->rx_hdrfail, bus->rx_badhdr, bus->rx_badseq); - bcm_bprintf(strbuf, "fc_rcvd %d, fc_xoff %d, fc_xon %d\n", + bcm_bprintf(strbuf, "fc_rcvd %u, fc_xoff %u, fc_xon %u\n", bus->fc_rcvd, bus->fc_xoff, bus->fc_xon); - bcm_bprintf(strbuf, "rxglomfail %d, rxglomframes %d, rxglompkts %d\n", + bcm_bprintf(strbuf, "rxglomfail %u, rxglomframes %u, rxglompkts %u\n", bus->rxglomfail, bus->rxglomframes, bus->rxglompkts); - bcm_bprintf(strbuf, "f2rx (hdrs/data) %d (%d/%d), f2tx %d f1regs %d\n", + bcm_bprintf(strbuf, "f2rx (hdrs/data) %u (%u/%u), f2tx %u f1regs %u\n", (bus->f2rxhdrs + bus->f2rxdata), bus->f2rxhdrs, bus->f2rxdata, bus->f2txdata, bus->f1regdata); { @@ -2976,17 +2738,17 @@ dhd_bus_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf) #ifdef SDTEST if (bus->pktgen_count) { bcm_bprintf(strbuf, "pktgen config and count:\n"); - bcm_bprintf(strbuf, "freq %d count %d print %d total %d min %d len %d\n", + bcm_bprintf(strbuf, "freq %u count %u print %u total %u min %u len %u\n", bus->pktgen_freq, bus->pktgen_count, bus->pktgen_print, bus->pktgen_total, bus->pktgen_minlen, bus->pktgen_maxlen); - bcm_bprintf(strbuf, "send attempts %d rcvd %d fail %d\n", + bcm_bprintf(strbuf, "send attempts %u rcvd %u fail %u\n", bus->pktgen_sent, bus->pktgen_rcvd, bus->pktgen_fail); } #endif /* SDTEST */ #ifdef DHD_DEBUG bcm_bprintf(strbuf, "dpc_sched %d host interrupt%spending\n", bus->dpc_sched, (bcmsdh_intr_pending(bus->sdh) ? " " : " not ")); - bcm_bprintf(strbuf, "blocksize %d roundup %d\n", bus->blocksize, bus->roundup); + bcm_bprintf(strbuf, "blocksize %u roundup %u\n", bus->blocksize, bus->roundup); #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); @@ -3000,6 +2762,9 @@ dhd_bus_clearcounts(dhd_pub_t *dhdp) bus->intrcount = bus->lastintrs = bus->spurious = bus->regfails = 0; bus->rxrtx = bus->rx_toolong = bus->rxc_errors = 0; bus->rx_hdrfail = bus->rx_badhdr = bus->rx_badseq = 0; +#ifdef DHDENABLE_TAILPAD + bus->tx_tailpad_chain = bus->tx_tailpad_pktget = 0; +#endif 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; @@ -3269,6 +3034,9 @@ dhdsdio_readconsole(dhd_bus_t *bus) n--; line[n] = 0; printf("CONSOLE: %s\n", line); +#ifdef LOG_INTO_TCPDUMP + dhd_sendup_log(bus->dhd, line, n); +#endif /* LOG_INTO_TCPDUMP */ } } break2: @@ -3525,7 +3293,9 @@ dhd_serialconsole(dhd_bus_t *bus, bool set, bool enable, int *bcmerror) uart_enab = CC_PLL_CHIPCTRL_SERIAL_ENAB; } else if (bus->sih->chip == BCM4334_CHIP_ID || + bus->sih->chip == BCM43340_CHIP_ID || bus->sih->chip == BCM43341_CHIP_ID || + bus->sih->chip == BCM43342_CHIP_ID || 0) { if (enable) { /* Moved to PMU chipcontrol 1 from 4330 */ @@ -3724,13 +3494,13 @@ dhdsdio_doiovar(dhd_bus_t *bus, const bcm_iovar_t *vi, uint32 actionid, const ch /* check if CR4 */ if (si_setcore(bus->sih, ARMCR4_CORE_ID, 0)) { - /* if address is 0, store the reset instruction to be written in 0 */ - - if (address == 0) { + /* + * If address is start of RAM (i.e. a downloaded image), + * store the reset instruction to be written in 0 + */ + if (set && address == bus->dongle_ram_base) { bus->resetinstr = *(((uint32*)params) + 2); } - /* Add start of RAM address to the address given by user */ - address += bus->dongle_ram_base; } else { /* If we know about SOCRAM, check for a fit */ if ((bus->orig_ramsize) && @@ -3784,11 +3554,16 @@ dhdsdio_doiovar(dhd_bus_t *bus, const bcm_iovar_t *vi, uint32 actionid, const ch break; } - case IOV_GVAL(IOV_MEMSIZE): + case IOV_GVAL(IOV_RAMSIZE): int_val = (int32)bus->ramsize; bcopy(&int_val, arg, val_size); break; + case IOV_GVAL(IOV_RAMSTART): + int_val = (int32)bus->dongle_ram_base; + bcopy(&int_val, arg, val_size); + break; + case IOV_GVAL(IOV_SDIOD_DRIVE): int_val = (int32)dhd_sdiod_drive_strength; bcopy(&int_val, arg, val_size); @@ -3866,7 +3641,7 @@ dhdsdio_doiovar(dhd_bus_t *bus, const bcm_iovar_t *vi, uint32 actionid, const ch sd_ptr = (sdreg_t *)params; - addr = (uintptr)bus->regs + sd_ptr->offset; + addr = (ulong)bus->regs + sd_ptr->offset; size = sd_ptr->func; int_val = (int32)bcmsdh_reg_read(bus->sdh, addr, size); if (bcmsdh_regfail(bus->sdh)) @@ -3882,7 +3657,7 @@ dhdsdio_doiovar(dhd_bus_t *bus, const bcm_iovar_t *vi, uint32 actionid, const ch sd_ptr = (sdreg_t *)params; - addr = (uintptr)bus->regs + sd_ptr->offset; + addr = (ulong)bus->regs + sd_ptr->offset; size = sd_ptr->func; bcmsdh_reg_write(bus->sdh, addr, size, sd_ptr->value); if (bcmsdh_regfail(bus->sdh)) @@ -3984,7 +3759,6 @@ dhdsdio_doiovar(dhd_bus_t *bus, const bcm_iovar_t *vi, uint32 actionid, const ch break; - #endif /* DHD_DEBUG */ @@ -4007,7 +3781,7 @@ dhdsdio_doiovar(dhd_bus_t *bus, const bcm_iovar_t *vi, uint32 actionid, const ch break; #endif /* SDTEST */ -#if defined(SDIO_CRC_ERROR_FIX) +#if defined(USE_SDIOFIFO_IOVAR) case IOV_GVAL(IOV_WATERMARK): int_val = (int32)watermark; bcopy(&int_val, arg, val_size); @@ -4033,7 +3807,7 @@ dhdsdio_doiovar(dhd_bus_t *bus, const bcm_iovar_t *vi, uint32 actionid, const ch bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_MESBUSYCTRL, ((uint8)mesbusyctrl | 0x80), NULL); break; -#endif /* SDIO_CRC_ERROR_FIX */ +#endif case IOV_GVAL(IOV_DONGLEISOLATION): @@ -4056,45 +3830,10 @@ dhdsdio_doiovar(dhd_bus_t *bus, const bcm_iovar_t *vi, uint32 actionid, const ch dhd_bus_devreset(bus->dhd, (uint8)bool_val); break; -#ifdef SOFTAP - case IOV_GVAL(IOV_FWPATH): - { - uint32 fw_path_len; - - fw_path_len = strlen(bus->fw_path); - DHD_INFO(("[softap] get fwpath, l=%d\n", len)); - - if (fw_path_len > len-1) { - bcmerror = BCME_BUFTOOSHORT; - break; - } - - if (fw_path_len) { - bcopy(bus->fw_path, arg, fw_path_len); - ((uchar*)arg)[fw_path_len] = 0; - } - break; - } - - case IOV_SVAL(IOV_FWPATH): - DHD_INFO(("[softap] set fwpath, idx=%d\n", int_val)); - - switch (int_val) { - case 1: - bus->fw_path = fw_path; /* ordinary one */ - break; - case 2: - bus->fw_path = fw_path2; - break; - default: - bcmerror = BCME_BADARG; - break; - } - - DHD_INFO(("[softap] new fw path: %s\n", (bus->fw_path[0] ? bus->fw_path : "NULL"))); - break; + /* + * softap firmware is updated through module parameter or android private command + */ -#endif /* SOFTAP */ case IOV_GVAL(IOV_DEVRESET): DHD_TRACE(("%s: Called get IOV_DEVRESET\n", __FUNCTION__)); @@ -4117,10 +3856,8 @@ dhdsdio_doiovar(dhd_bus_t *bus, const bcm_iovar_t *vi, uint32 actionid, const ch case IOV_SVAL(IOV_DEVCAP): dhdsdio_devcap_set(bus, (uint8) int_val); break; - -#ifdef BCMSDIOH_TXGLOM case IOV_GVAL(IOV_TXGLOMSIZE): - int_val = (int32)bus->glomsize; + int_val = (int32)bus->txglomsize; bcopy(&int_val, arg, val_size); break; @@ -4128,23 +3865,31 @@ dhdsdio_doiovar(dhd_bus_t *bus, const bcm_iovar_t *vi, uint32 actionid, const ch if (int_val > SDPCM_MAXGLOM_SIZE) { bcmerror = BCME_ERROR; } else { - bus->glomsize = (uint)int_val; + bus->txglomsize = (uint)int_val; } break; - case IOV_GVAL(IOV_TXGLOMMODE): - int_val = (int32)bus->glom_mode; + case IOV_SVAL(IOV_HANGREPORT): + bus->dhd->hang_report = bool_val; + DHD_ERROR(("%s: Set hang_report as %d\n", __FUNCTION__, bus->dhd->hang_report)); + break; + + case IOV_GVAL(IOV_HANGREPORT): + int_val = (int32)bus->dhd->hang_report; bcopy(&int_val, arg, val_size); break; - case IOV_SVAL(IOV_TXGLOMMODE): - if ((int_val != SDPCM_TXGLOM_CPY) && (int_val != SDPCM_TXGLOM_MDESC)) { - bcmerror = BCME_RANGE; + case IOV_GVAL(IOV_TXINRX_THRES): + int_val = bus->txinrx_thres; + bcopy(&int_val, arg, val_size); + break; + case IOV_SVAL(IOV_TXINRX_THRES): + if (int_val < 0) { + bcmerror = BCME_BADARG; } else { - if ((bus->glom_mode = bcmsdh_set_mode(bus->sdh, (uint)int_val)) != int_val) - bcmerror = BCME_ERROR; + bus->txinrx_thres = int_val; } break; -#endif /* BCMSDIOH_TXGLOM */ + default: bcmerror = BCME_UNSUPPORTED; break; @@ -4487,8 +4232,7 @@ dhd_bus_iovar_op(dhd_pub_t *dhdp, const char *name, DHD_INFO(("%s: noted %s update, value now %d\n", __FUNCTION__, "sd_blocksize", bus->blocksize)); - if (bus->sih->chip == BCM4335_CHIP_ID) - dhd_overflow_war(bus); + dhdsdio_tune_fifoparam(bus); } } bus->roundup = MIN(max_roundup, bus->blocksize); @@ -4533,16 +4277,18 @@ dhd_bus_stop(struct dhd_bus *bus, bool enforce_mutex) { osl_t *osh; uint32 local_hostintmask; - uint8 saveclk, dat; + uint8 saveclk; uint retries; int err; + bool wlfc_enabled = FALSE; + if (!bus->dhd) return; osh = bus->dhd->osh; DHD_TRACE(("%s: Enter\n", __FUNCTION__)); - bcmsdh_waitlockfree(NULL); + bcmsdh_waitlockfree(bus->sdh); if (enforce_mutex) dhd_os_sdlock(bus->dhd); @@ -4556,13 +4302,6 @@ dhd_bus_stop(struct dhd_bus *bus, bool enforce_mutex) BUS_WAKE(bus); - if (KSO_ENAB(bus)) { - /* Mask the interrupt */ - dat = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_0, SDIOD_CCCR_INTEN, NULL); - dat &= ~(INTR_CTL_FUNC1_EN | INTR_CTL_FUNC2_EN); - bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_0, SDIOD_CCCR_INTEN, dat, NULL); - } - /* Change our idea of bus state */ bus->dhd->busstate = DHD_BUS_DOWN; @@ -4602,8 +4341,19 @@ dhd_bus_stop(struct dhd_bus *bus, bool enforce_mutex) dhdsdio_clkctl(bus, CLK_SDONLY, FALSE); } - /* Clear the data packet queues */ - pktq_flush(osh, &bus->txq, TRUE, NULL, 0); +#ifdef PROP_TXSTATUS + wlfc_enabled = (dhd_wlfc_cleanup_txq(bus->dhd, NULL, 0) != WLFC_UNSUPPORTED); +#endif + if (!wlfc_enabled) { +#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 */ + /* Clear the data packet queues */ + pktq_flush(osh, &bus->txq, TRUE, NULL, 0); + } /* Clear any held glomming stuff */ if (bus->glomd) @@ -4628,33 +4378,46 @@ dhd_bus_stop(struct dhd_bus *bus, bool enforce_mutex) dhd_os_sdunlock(bus->dhd); } -#ifdef BCMSDIOH_TXGLOM +#if defined(BCMSDIOH_TXGLOM) && defined(BCMSDIOH_STD) +extern uint sd_txglom; +#endif void dhd_txglom_enable(dhd_pub_t *dhdp, bool enable) { + /* can't enable host txglom by default, some platforms have no + * (or crappy) ADMA support and txglom will cause kernel assertions (e.g. + * panda board) + */ dhd_bus_t *bus = dhdp->bus; - +#ifdef BCMSDIOH_TXGLOM char buf[256]; uint32 rxglom; int32 ret; + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + +#ifdef BCMSDIOH_STD + if (enable) + enable = sd_txglom; +#endif /* BCMSDIOH_STD */ + if (enable) { rxglom = 1; memset(buf, 0, sizeof(buf)); - bcm_mkiovar("bus:rxglom", - (void *)&rxglom, - 4, buf, sizeof(buf)); - ret = dhd_wl_ioctl_cmd(dhdp, - WLC_SET_VAR, buf, - sizeof(buf), TRUE, 0); - if (!(ret < 0)) { - bus->glom_enable = TRUE; + bcm_mkiovar("bus:rxglom", (void *)&rxglom, 4, buf, sizeof(buf)); + ret = dhd_wl_ioctl_cmd(dhdp, WLC_SET_VAR, buf, sizeof(buf), TRUE, 0); + if (ret >= 0) + bus->txglom_enable = TRUE; + else { +#ifdef BCMSDIOH_STD + sd_txglom = 0; +#endif /* BCMSDIOH_STD */ + bus->txglom_enable = FALSE; } - } else { - bus->glom_enable = FALSE; - } -} + } else #endif /* BCMSDIOH_TXGLOM */ + bus->txglom_enable = FALSE; +} int dhd_bus_init(dhd_pub_t *dhdp, bool enforce_mutex) @@ -4761,21 +4524,11 @@ dhd_bus_init(dhd_pub_t *dhdp, bool enforce_mutex) } #endif /* BCMSPI */ W_SDREG(bus->hostintmask, &bus->regs->hostintmask, retries); -#ifdef SDIO_CRC_ERROR_FIX - if (bus->blocksize < 512) { - mesbusyctrl = watermark = bus->blocksize / 4; - } -#endif /* SDIO_CRC_ERROR_FIX */ - if (bus->sih->chip != BCM4335_CHIP_ID) { + + if (bus->sih->buscorerev < 15) { bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_WATERMARK, (uint8)watermark, &err); } -#ifdef SDIO_CRC_ERROR_FIX - bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_MESBUSYCTRL, - (uint8)mesbusyctrl|0x80, &err); - bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, - SBSDIO_DEVCTL_EN_F2_BLK_WATERMARK, NULL); -#endif /* SDIO_CRC_ERROR_FIX */ /* Set bus state according to enable result */ dhdp->busstate = DHD_BUS_DATA; @@ -4785,7 +4538,9 @@ dhd_bus_init(dhd_pub_t *dhdp, bool enforce_mutex) bus->intdis = FALSE; if (bus->intr) { DHD_INTR(("%s: enable SDIO device interrupts\n", __FUNCTION__)); +#ifndef BCMSPI_ANDROID bcmsdh_intr_enable(bus->sdh); +#endif /* !BCMSPI_ANDROID */ } else { DHD_INTR(("%s: disable SDIO interrupts\n", __FUNCTION__)); bcmsdh_intr_disable(bus->sdh); @@ -4801,8 +4556,14 @@ dhd_bus_init(dhd_pub_t *dhdp, bool enforce_mutex) bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_0, SDIOD_CCCR_IOEN, enable, NULL); } - if (dhdsdio_sr_cap(bus)) + if (dhdsdio_sr_cap(bus)) { dhdsdio_sr_init(bus); + /* Masking the chip active interrupt permanantly */ + bus->hostintmask &= ~I_CHIPACTIVE; + W_SDREG(bus->hostintmask, &bus->regs->hostintmask, retries); + DHD_INFO(("%s: disable I_CHIPACTIVE in hostintmask[0x%08x]\n", + __FUNCTION__, bus->hostintmask)); + } else bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, saveclk, &err); @@ -4992,6 +4753,9 @@ done: /* Awake any waiters */ dhd_os_ioctl_resp_wake(bus->dhd); } +int +dhd_process_pkt_reorder_info(dhd_pub_t *dhd, uchar *reorder_info_buf, uint reorder_info_len, + void **pkt, uint32 *pkt_count); static uint8 dhdsdio_rxglom(dhd_bus_t *bus, uint8 rxseq) @@ -5038,8 +4802,8 @@ dhdsdio_rxglom(dhd_bus_t *bus, uint8 rxseq) sublen = ltoh16_ua(dptr); dlen -= sizeof(uint16); dptr += sizeof(uint16); - if ((sublen < SDPCM_HDRLEN_RX) || - ((num == 0) && (sublen < (2 * SDPCM_HDRLEN_RX)))) { + if ((sublen < SDPCM_HDRLEN) || + ((num == 0) && (sublen < (2 * SDPCM_HDRLEN)))) { DHD_ERROR(("%s: descriptor len %d bad: %d\n", __FUNCTION__, num, sublen)); pnext = NULL; @@ -5208,11 +4972,11 @@ dhdsdio_rxglom(dhd_bus_t *bus, uint8 rxseq) } else if (SDPCM_GLOMDESC(&dptr[SDPCM_FRAMETAG_LEN])) { DHD_ERROR(("%s (superframe): got second descriptor?\n", __FUNCTION__)); errcode = -1; - } else if ((doff < SDPCM_HDRLEN_RX) || - (doff > (PKTLEN(osh, pfirst) - SDPCM_HDRLEN_RX))) { + } else if ((doff < SDPCM_HDRLEN) || + (doff > (PKTLEN(osh, pfirst) - SDPCM_HDRLEN))) { DHD_ERROR(("%s (superframe): Bad data offset %d: HW %d pkt %d min %d\n", __FUNCTION__, doff, sublen, PKTLEN(osh, pfirst), - SDPCM_HDRLEN_RX)); + SDPCM_HDRLEN)); errcode = -1; } @@ -5225,7 +4989,7 @@ dhdsdio_rxglom(dhd_bus_t *bus, uint8 rxseq) } /* Check window for sanity */ - if ((uint8)(txmax - bus->tx_seq) > 0x40) { + if ((uint8)(txmax - bus->tx_seq) > 0x70) { DHD_ERROR(("%s: got unlikely tx max %d with tx_seq %d\n", __FUNCTION__, txmax, bus->tx_seq)); txmax = bus->tx_max; @@ -5256,7 +5020,7 @@ dhdsdio_rxglom(dhd_bus_t *bus, uint8 rxseq) "len/check 0x%04x/0x%04x\n", __FUNCTION__, num, sublen, check)); errcode = -1; - } else if ((sublen > dlen) || (sublen < SDPCM_HDRLEN_RX)) { + } else if ((sublen > dlen) || (sublen < SDPCM_HDRLEN)) { DHD_ERROR(("%s (subframe %d): length mismatch: " "len 0x%04x, expect 0x%04x\n", __FUNCTION__, num, sublen, dlen)); @@ -5266,9 +5030,9 @@ dhdsdio_rxglom(dhd_bus_t *bus, uint8 rxseq) DHD_ERROR(("%s (subframe %d): bad channel %d\n", __FUNCTION__, num, chan)); errcode = -1; - } else if ((doff < SDPCM_HDRLEN_RX) || (doff > sublen)) { + } else if ((doff < SDPCM_HDRLEN) || (doff > sublen)) { DHD_ERROR(("%s (subframe %d): Bad data offset %d: HW %d min %d\n", - __FUNCTION__, num, doff, sublen, SDPCM_HDRLEN_RX)); + __FUNCTION__, num, doff, sublen, SDPCM_HDRLEN)); errcode = -1; } } @@ -5362,14 +5126,11 @@ dhdsdio_rxglom(dhd_bus_t *bus, uint8 rxseq) temp = PKTNEXT(osh, temp); } pfirst = temp; - if (list_tail[ifidx] == NULL) { + if (list_tail[ifidx] == NULL) list_head[ifidx] = ppfirst; - list_tail[ifidx] = pfirst; - } - else { + else PKTSETNEXT(osh, list_tail[ifidx], ppfirst); - list_tail[ifidx] = pfirst; - } + list_tail[ifidx] = pfirst; } num += (uint8)free_buf_count; @@ -5424,12 +5185,7 @@ dhdsdio_rxglom(dhd_bus_t *bus, uint8 rxseq) /* Return TRUE if there may be more frames to read */ static uint -#ifdef REPEAT_READFRAME -dhdsdio_readframes(dhd_bus_t *bus, uint maxframes, bool *finished, bool tx_enable) -#else dhdsdio_readframes(dhd_bus_t *bus, uint maxframes, bool *finished) -#endif - { osl_t *osh = bus->dhd->osh; bcmsdh_info_t *sdh = bus->sdh; @@ -5483,6 +5239,7 @@ dhdsdio_readframes(dhd_bus_t *bus, uint maxframes, bool *finished) /* Not finished unless we encounter no more frames indication */ *finished = FALSE; + #ifdef BCMSPI /* Get pktlen from gSPI device F0 reg. */ if (bus->bus == SPI_BUS) { @@ -5520,24 +5277,13 @@ dhdsdio_readframes(dhd_bus_t *bus, uint maxframes, bool *finished) !bus->rxskip && rxleft && bus->dhd->busstate != DHD_BUS_DOWN; rxseq++, rxleft--) { -#ifdef DHDTHREAD - - if ( -#ifdef REPEAT_READFRAME - tx_enable && -#endif - TXCTLOK(bus) && bus->ctrl_frame_stat && (bus->clkstate == CLK_AVAIL)) - dhdsdio_sendpendctl(bus); /* tx more to improve rx performance */ - else if ( -#ifdef REPEAT_READFRAME - tx_enable && -#endif - (bus->clkstate == CLK_AVAIL) && !bus->fcstate && - pktq_mlen(&bus->txq, ~bus->flowcontrol) && DATAOK(bus)) { + if (TXCTLOK(bus) && bus->ctrl_frame_stat && (bus->clkstate == CLK_AVAIL)) { + dhdsdio_sendpendctl(bus); + } else if ((bus->clkstate == CLK_AVAIL) && !bus->fcstate && DATAOK(bus) && + (pktq_mlen(&bus->txq, ~bus->flowcontrol) > bus->txinrx_thres)) { dhdsdio_sendfromq(bus, dhd_txbound); } -#endif /* DHDTHREAD */ /* Handle glomming separately */ if (bus->glom || bus->glomd) { @@ -5682,7 +5428,7 @@ dhdsdio_readframes(dhd_bus_t *bus, uint maxframes, bool *finished) dhd_os_sdunlock_rxq(bus->dhd); /* Now check the header */ - bcopy(rxbuf, bus->rxhdr, SDPCM_HDRLEN_RX); + bcopy(rxbuf, bus->rxhdr, SDPCM_HDRLEN); /* Extract hardware header fields */ len = ltoh16_ua(bus->rxhdr); @@ -5714,7 +5460,7 @@ dhdsdio_readframes(dhd_bus_t *bus, uint maxframes, bool *finished) } /* Validate frame length */ - if (len < SDPCM_HDRLEN_RX) { + if (len < SDPCM_HDRLEN) { DHD_ERROR(("%s (nextlen): HW hdr length invalid: %d\n", __FUNCTION__, len)); dhd_os_sdlock_rxq(bus->dhd); @@ -5824,7 +5570,7 @@ dhdsdio_readframes(dhd_bus_t *bus, uint maxframes, bool *finished) } /* Check window for sanity */ - if ((uint8)(txmax - bus->tx_seq) > 0x40) { + if ((uint8)(txmax - bus->tx_seq) > 0x70) { #ifdef BCMSPI if ((bus->bus == SPI_BUS) && !(dstatus & STATUS_F2_RX_READY)) { DHD_ERROR(("%s: got unlikely tx max %d with tx_seq %d\n", @@ -5845,7 +5591,7 @@ dhdsdio_readframes(dhd_bus_t *bus, uint maxframes, bool *finished) if (DHD_BYTES_ON() && DHD_DATA_ON()) { prhex("Rx Data", rxbuf, len); } else if (DHD_HDRS_ON()) { - prhex("RxHdr", bus->rxhdr, SDPCM_HDRLEN_RX); + prhex("RxHdr", bus->rxhdr, SDPCM_HDRLEN); } #endif @@ -5878,9 +5624,9 @@ dhdsdio_readframes(dhd_bus_t *bus, uint maxframes, bool *finished) } /* Validate data offset */ - if ((doff < SDPCM_HDRLEN_RX) || (doff > len)) { + if ((doff < SDPCM_HDRLEN) || (doff > len)) { DHD_ERROR(("%s (nextlen): bad data offset %d: HW len %d min %d\n", - __FUNCTION__, doff, len, SDPCM_HDRLEN_RX)); + __FUNCTION__, doff, len, SDPCM_HDRLEN)); dhd_os_sdlock_rxq(bus->dhd); PKTFREE2(); dhd_os_sdunlock_rxq(bus->dhd); @@ -5912,7 +5658,7 @@ dhdsdio_readframes(dhd_bus_t *bus, uint maxframes, bool *finished) #ifdef DHD_DEBUG if (DHD_BYTES_ON() || DHD_HDRS_ON()) { - prhex("RxHdr", bus->rxhdr, SDPCM_HDRLEN_RX); + prhex("RxHdr", bus->rxhdr, SDPCM_HDRLEN); } #endif @@ -5936,7 +5682,7 @@ dhdsdio_readframes(dhd_bus_t *bus, uint maxframes, bool *finished) } /* Validate frame length */ - if (len < SDPCM_HDRLEN_RX) { + if (len < SDPCM_HDRLEN) { DHD_ERROR(("%s: HW hdr length invalid: %d\n", __FUNCTION__, len)); continue; } @@ -5948,9 +5694,9 @@ dhdsdio_readframes(dhd_bus_t *bus, uint maxframes, bool *finished) txmax = SDPCM_WINDOW_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]); /* Validate data offset */ - if ((doff < SDPCM_HDRLEN_RX) || (doff > len)) { + if ((doff < SDPCM_HDRLEN) || (doff > len)) { DHD_ERROR(("%s: Bad data offset %d: HW len %d, min %d seq %d\n", - __FUNCTION__, doff, len, SDPCM_HDRLEN_RX, seq)); + __FUNCTION__, doff, len, SDPCM_HDRLEN, seq)); bus->rx_badhdr++; ASSERT(0); dhdsdio_rxfail(bus, FALSE, FALSE); @@ -5991,7 +5737,7 @@ dhdsdio_readframes(dhd_bus_t *bus, uint maxframes, bool *finished) } /* Check window for sanity */ - if ((uint8)(txmax - bus->tx_seq) > 0x40) { + if ((uint8)(txmax - bus->tx_seq) > 0x70) { DHD_ERROR(("%s: got unlikely tx max %d with tx_seq %d\n", __FUNCTION__, txmax, bus->tx_seq)); txmax = bus->tx_max; @@ -6091,8 +5837,8 @@ deliver: } #endif PKTSETLEN(osh, pkt, len); - ASSERT(doff == SDPCM_HDRLEN_RX); - PKTPULL(osh, pkt, SDPCM_HDRLEN_RX); + ASSERT(doff == SDPCM_HDRLEN); + PKTPULL(osh, pkt, SDPCM_HDRLEN); bus->glomd = pkt; } else { DHD_ERROR(("%s: glom superframe w/o descriptor!\n", __FUNCTION__)); @@ -6267,10 +6013,6 @@ dhdsdio_hostmail(dhd_bus_t *bus) return intstatus; } -#ifdef REPEAT_READFRAME -extern uint dhd_dpcpoll; -#endif - static bool dhdsdio_dpc(dhd_bus_t *bus) { @@ -6283,20 +6025,23 @@ dhdsdio_dpc(dhd_bus_t *bus) uint framecnt = 0; /* Temporary counter of tx/rx frames */ bool rxdone = TRUE; /* Flag for no more read data */ bool resched = FALSE; /* Flag indicating resched wanted */ - +#if defined(CUSTOMER_HW4) + bool is_resched_by_readframe = FALSE; +#endif DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + dhd_os_sdlock(bus->dhd); + if (bus->dhd->busstate == DHD_BUS_DOWN) { DHD_ERROR(("%s: Bus down, ret\n", __FUNCTION__)); bus->intstatus = 0; + dhd_os_sdunlock(bus->dhd); return 0; } /* Start with leftover status bits */ intstatus = bus->intstatus; - dhd_os_sdlock(bus->dhd); - if (!SLPAUTO_ENAB(bus) && !KSO_ENAB(bus)) { DHD_ERROR(("%s: Device asleep\n", __FUNCTION__)); goto exit; @@ -6348,6 +6093,7 @@ dhdsdio_dpc(dhd_bus_t *bus) } BUS_WAKE(bus); + /* Make sure backplane clock is on */ dhdsdio_clkctl(bus, CLK_AVAIL, TRUE); if (bus->clkstate != CLK_AVAIL) @@ -6378,7 +6124,6 @@ dhdsdio_dpc(dhd_bus_t *bus) intstatus |= newstatus; bus->intstatus = 0; - /* Handle flow-control change: read new state in case our ack * crossed another change interrupt. If change still set, assume * FC ON for safety, let next loop through do the debounce. @@ -6426,6 +6171,11 @@ dhdsdio_dpc(dhd_bus_t *bus) intstatus &= ~I_CHIPACTIVE; } + if (intstatus & I_HMB_FC_STATE) { + DHD_INFO(("Dongle reports HMB_FC_STATE\n")); + intstatus &= ~I_HMB_FC_STATE; + } + /* Ignore frame indications if rxskip is set */ if (bus->rxskip) { intstatus &= ~FRAME_AVAIL_MASK(bus); @@ -6433,11 +6183,7 @@ dhdsdio_dpc(dhd_bus_t *bus) /* On frame indication, read available frames */ if (PKT_AVAILABLE(bus, intstatus)) { -#ifdef REPEAT_READFRAME - framecnt = dhdsdio_readframes(bus, rxlimit, &rxdone, true); -#else framecnt = dhdsdio_readframes(bus, rxlimit, &rxdone); -#endif if (rxdone || bus->rxskip) intstatus &= ~FRAME_AVAIL_MASK(bus); rxlimit -= MIN(framecnt, rxlimit); @@ -6455,10 +6201,14 @@ clkwait: DHD_INTR(("%s: enable SDIO interrupts, rxdone %d framecnt %d\n", __FUNCTION__, rxdone, framecnt)); bus->intdis = FALSE; -#if defined(OOB_INTR_ONLY) || defined(BCMSPI_ANDROID) - bcmsdh_oob_intr_set(1); -#endif /* defined(OOB_INTR_ONLY) || defined(BCMSPI_ANDROID) */ +#if defined(OOB_INTR_ONLY) + bcmsdh_oob_intr_set(bus->sdh, TRUE); +#endif /* defined(OOB_INTR_ONLY) */ bcmsdh_intr_enable(sdh); +#ifdef BCMSPI_ANDROID + if (*dhd_spi_lockcount == 0) + bcmsdh_oob_intr_set(bus->sdh, TRUE); +#endif /* BCMSPI_ANDROID */ } #if defined(OOB_INTR_ONLY) && !defined(HW_OOB) @@ -6478,12 +6228,12 @@ clkwait: #endif /* defined(OOB_INTR_ONLY) && !defined(HW_OOB) */ #ifdef PROP_TXSTATUS - dhd_wlfc_trigger_pktcommit(bus->dhd); + dhd_wlfc_commit_packets(bus->dhd, (f_commitpkt_t)dhd_bus_txdata, (void *)bus, NULL, FALSE); #endif - if (TXCTLOK(bus) && bus->ctrl_frame_stat && (bus->clkstate == CLK_AVAIL)) dhdsdio_sendpendctl(bus); + /* Send queued frames (limit 1 if rx may still be pending) */ else if ((bus->clkstate == CLK_AVAIL) && !bus->fcstate && pktq_mlen(&bus->txq, ~bus->flowcontrol) && txlimit && DATAOK(bus)) { @@ -6526,13 +6276,27 @@ clkwait: } exit: -#ifdef REPEAT_READFRAME + if (!resched && dhd_dpcpoll) { - resched = dhdsdio_readframes(bus, dhd_rxbound, &rxdone, true); - } + if (dhdsdio_readframes(bus, dhd_rxbound, &rxdone) != 0) + resched = TRUE; +#if defined(CUSTOMER_HW4) + is_resched_by_readframe = TRUE; #endif + } dhd_os_sdunlock(bus->dhd); +#if defined(CUSTOMER_HW4) + if (bus->dhd->dhd_bug_on) { + DHD_ERROR(("%s: resched = %d ctrl_frame_stat = %d intstatus 0x%08x" + " ipend = %d pktq_mlen = %d is_resched_by_readframe = %d \n", + __FUNCTION__, resched, bus->ctrl_frame_stat, + bus->intstatus, bus->ipend, + pktq_mlen(&bus->txq, ~bus->flowcontrol), is_resched_by_readframe)); + + bus->dhd->dhd_bug_on = FALSE; + } +#endif return resched; } @@ -6590,19 +6354,23 @@ dhdsdio_isr(void *arg) DHD_ERROR(("dhdsdio_isr() w/o interrupt configured!\n")); } +#ifdef BCMSPI_ANDROID + bcmsdh_oob_intr_set(bus->sdh, FALSE); +#endif /* BCMSPI_ANDROID */ bcmsdh_intr_disable(sdh); bus->intdis = TRUE; #if defined(SDIO_ISR_THREAD) DHD_TRACE(("Calling dhdsdio_dpc() from %s\n", __FUNCTION__)); DHD_OS_WAKE_LOCK(bus->dhd); - while (dhdsdio_dpc(bus)); + dhdsdio_dpc(bus); DHD_OS_WAKE_UNLOCK(bus->dhd); #else + bus->dpc_sched = TRUE; dhd_sched_dpc(bus->dhd); -#endif +#endif /* defined(SDIO_ISR_THREAD) */ } @@ -6747,7 +6515,7 @@ dhdsdio_pktgen(dhd_bus_t *bus) #endif /* Send it */ - if (dhdsdio_txpkt(bus, pkt, SDPCM_TEST_CHANNEL, TRUE, FALSE)) { + if (dhdsdio_txpkt(bus, SDPCM_TEST_CHANNEL, &pkt, 1, TRUE) != BCME_OK) { bus->pktgen_fail++; if (bus->pktgen_stop && bus->pktgen_stop == bus->pktgen_fail) bus->pktgen_count = 0; @@ -6792,7 +6560,7 @@ dhdsdio_sdtest_set(dhd_bus_t *bus, uint count) *data++ = (uint8)(count >> 24); /* Send it */ - if (dhdsdio_txpkt(bus, pkt, SDPCM_TEST_CHANNEL, TRUE, FALSE)) + if (dhdsdio_txpkt(bus, SDPCM_TEST_CHANNEL, &pkt, 1, TRUE) != BCME_OK) bus->pktgen_fail++; } @@ -6837,7 +6605,7 @@ dhdsdio_testrcv(dhd_bus_t *bus, void *pkt, uint seq) case SDPCM_TEST_ECHOREQ: /* Rx->Tx turnaround ok (even on NDIS w/current implementation) */ *(uint8 *)(PKTDATA(osh, pkt)) = SDPCM_TEST_ECHORSP; - if (dhdsdio_txpkt(bus, pkt, SDPCM_TEST_CHANNEL, TRUE, FALSE) == 0) { + if (dhdsdio_txpkt(bus, SDPCM_TEST_CHANNEL, &pkt, 1, TRUE) == BCME_OK) { bus->pktgen_sent++; } else { bus->pktgen_fail++; @@ -6909,12 +6677,46 @@ dhdsdio_testrcv(dhd_bus_t *bus, void *pkt, uint seq) } #endif /* SDTEST */ -extern void -dhd_disable_intr(dhd_pub_t *dhdp) +int dhd_bus_oob_intr_register(dhd_pub_t *dhdp) { - dhd_bus_t *bus; - bus = dhdp->bus; - bcmsdh_intr_disable(bus->sdh); + int err = 0; + +#if defined(OOB_INTR_ONLY) || defined(BCMSPI_ANDROID) + err = bcmsdh_oob_intr_register(dhdp->bus->sdh, dhdsdio_isr, dhdp->bus); +#endif + return err; +} + +void dhd_bus_oob_intr_unregister(dhd_pub_t *dhdp) +{ +#if defined(OOB_INTR_ONLY) || defined(BCMSPI_ANDROID) + bcmsdh_oob_intr_unregister(dhdp->bus->sdh); +#endif +} + +void dhd_bus_oob_intr_set(dhd_pub_t *dhdp, bool enable) +{ +#if defined(OOB_INTR_ONLY) || defined(BCMSPI_ANDROID) + bcmsdh_oob_intr_set(dhdp->bus->sdh, enable); +#endif +} + +void dhd_bus_dev_pm_stay_awake(dhd_pub_t *dhdpub) +{ + bcmsdh_dev_pm_stay_awake(dhdpub->bus->sdh); +} + +void dhd_bus_dev_pm_relax(dhd_pub_t *dhdpub) +{ + bcmsdh_dev_relax(dhdpub->bus->sdh); +} + +bool dhd_bus_dev_pm_enabled(dhd_pub_t *dhdpub) +{ + bool enabled = FALSE; + + enabled = bcmsdh_dev_pm_enabled(dhdpub->bus->sdh); + return enabled; } extern bool @@ -6929,6 +6731,9 @@ dhd_bus_watchdog(dhd_pub_t *dhdp) if (bus->dhd->dongle_reset) return FALSE; + if (bus->dhd->hang_was_sent) + return FALSE; + /* Ignore the timer if simulating bus down */ if (!SLPAUTO_ENAB(bus) && bus->sleeping) return FALSE; @@ -6971,7 +6776,6 @@ dhd_bus_watchdog(dhd_pub_t *dhdp) } bus->dpc_sched = TRUE; dhd_sched_dpc(bus->dhd); - } } @@ -7016,7 +6820,7 @@ dhd_bus_watchdog(dhd_pub_t *dhdp) else { bus->idlecount++; - if (bus->idlecount >= bus->idletime) { + if ((bus->idletime > 0) && (bus->idlecount >= bus->idletime)) { DHD_TIMER(("%s: DHD Idle state!!\n", __FUNCTION__)); if (SLPAUTO_ENAB(bus)) { if (dhdsdio_bussleep(bus, TRUE) != BCME_BUSY) @@ -7029,7 +6833,7 @@ dhd_bus_watchdog(dhd_pub_t *dhdp) } #else if ((bus->idletime > 0) && (bus->clkstate == CLK_AVAIL)) { - if (++bus->idlecount > bus->idletime) { + if (++bus->idlecount >= bus->idletime) { bus->idlecount = 0; if (bus->activity) { bus->activity = FALSE; @@ -7097,7 +6901,7 @@ dhd_bus_console_in(dhd_pub_t *dhdp, uchar *msg, uint msglen) * sdpcm_sendup (RX) checks for virtual console input. */ if ((pkt = PKTGET(bus->dhd->osh, 4 + SDPCM_RESERVE, TRUE)) != NULL) - dhdsdio_txpkt(bus, pkt, SDPCM_EVENT_CHANNEL, TRUE, FALSE); + rv = dhdsdio_txpkt(bus, SDPCM_EVENT_CHANNEL, &pkt, 1, TRUE); done: if ((bus->idletime == DHD_IDLE_IMMEDIATE) && !bus->dpc_sched) { @@ -7164,6 +6968,8 @@ dhdsdio_chipmatch(uint16 chipid) return TRUE; if (chipid == BCM43242_CHIP_ID) return TRUE; + if (chipid == BCM43340_CHIP_ID) + return TRUE; if (chipid == BCM43341_CHIP_ID) return TRUE; if (chipid == BCM43143_CHIP_ID) @@ -7178,8 +6984,16 @@ dhdsdio_chipmatch(uint16 chipid) return TRUE; if (chipid == BCM4335_CHIP_ID) return TRUE; + if (chipid == BCM4339_CHIP_ID) + return TRUE; + if (chipid == BCM43349_CHIP_ID) + return TRUE; + if (chipid == BCM4345_CHIP_ID) + return TRUE; if (chipid == BCM4350_CHIP_ID) return TRUE; + if (chipid == BCM4354_CHIP_ID) + return TRUE; return FALSE; } @@ -7189,10 +7003,8 @@ dhdsdio_probe(uint16 venid, uint16 devid, uint16 bus_no, uint16 slot, { int ret; dhd_bus_t *bus; -#ifdef GET_CUSTOM_MAC_ENABLE - struct ether_addr ea_addr; -#endif /* GET_CUSTOM_MAC_ENABLE */ +#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__)); @@ -7202,6 +7014,7 @@ dhdsdio_probe(uint16 venid, uint16 devid, uint16 bus_no, uint16 slot, } mutex_lock(&_dhd_sdio_mutex_lock_); #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) */ +#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 @@ -7219,12 +7032,12 @@ dhdsdio_probe(uint16 venid, uint16 devid, uint16 bus_no, uint16 slot, sd1idle = TRUE; dhd_readahead = TRUE; retrydata = FALSE; -#ifndef REPEAT_READFRAME +#if !defined(PLATFORM_MPS) && !defined(CUSTOMER_HW4) dhd_doflow = FALSE; #else dhd_doflow = TRUE; -#endif /* REPEAT_READFRAME */ - dhd_dongle_memsize = 0; +#endif /* OEM_ANDROID */ + dhd_dongle_ramsize = 0; dhd_txminmax = DHD_TXMINMAX; #ifdef BCMSPI @@ -7289,11 +7102,8 @@ dhdsdio_probe(uint16 venid, uint16 devid, uint16 bus_no, uint16 slot, } if (osh == NULL) { - /* Ask the OS interface part for an OSL handle */ - if (!(osh = dhd_osl_attach(sdh, DHD_BUS))) { - DHD_ERROR(("%s: osl_attach failed!\n", __FUNCTION__)); - goto forcereturn; - } + DHD_ERROR(("%s: osh is NULL!\n", __FUNCTION__)); + goto forcereturn; } /* Allocate private bus interface state */ @@ -7305,12 +7115,11 @@ dhdsdio_probe(uint16 venid, uint16 devid, uint16 bus_no, uint16 slot, bus->sdh = sdh; bus->cl_devid = (uint16)devid; bus->bus = DHD_BUS; + bus->bus_num = bus_no; + bus->slot_num = slot; bus->tx_seq = SDPCM_SEQUENCE_WRAP - 1; bus->usebufpool = FALSE; /* Use bufpool if allocated, else use locally malloced rxbuf */ - /* attach the common module */ - dhd_common_init(osh); - /* attempt to attach to the dongle */ if (!(dhdsdio_probe_attach(bus, osh, sdh, regsva, devid))) { DHD_ERROR(("%s: dhdsdio_probe_attach failed\n", __FUNCTION__)); @@ -7351,37 +7160,44 @@ dhdsdio_probe(uint16 venid, uint16 devid, uint16 bus_no, uint16 slot, DHD_INFO(("%s: completed!!\n", __FUNCTION__)); -#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); - } -#endif /* GET_CUSTOM_MAC_ENABLE */ - /* if firmware path present try to download and bring up bus */ + bus->dhd->hang_report = TRUE; 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; } } +#ifdef CUSTOMER_HW4 + else { + /* Set ramdom MAC address during boot time */ + get_random_bytes(&bus->dhd->mac.octet[3], 3); + /* Adding BRCM OUI */ + bus->dhd->mac.octet[0] = 0; + bus->dhd->mac.octet[1] = 0x90; + bus->dhd->mac.octet[2] = 0x4C; + } +#endif /* CUSTOMER_HW4 */ /* Ok, have the per-port tell the stack we're open for business */ - if (dhd_net_attach(bus->dhd, 0) != 0) { + if (dhd_register_if(bus->dhd, 0, TRUE) != 0) { DHD_ERROR(("%s: Net attach failed!!\n", __FUNCTION__)); goto fail; } #if defined(CUSTOMER_HW4) && defined(BCMHOST_XTAL_PU_TIME_MOD) bcmsdh_reg_write(bus->sdh, 0x18000620, 2, 11); +#ifdef BCM4330_CHIP + bcmsdh_reg_write(bus->sdh, 0x18000628, 4, 0x0000F801); +#else bcmsdh_reg_write(bus->sdh, 0x18000628, 4, 0x00F80001); -#endif - +#endif /* BCM4330_CHIP */ +#endif /* defined(CUSTOMER_HW4) && defined(BCMHOST_XTAL_PU_TIME_MOD */ +#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 return bus; @@ -7389,10 +7205,12 @@ fail: dhdsdio_release(bus, osh); forcereturn: +#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 return NULL; } @@ -7692,13 +7510,7 @@ dhdsdio_probe_attach(struct dhd_bus *bus, osl_t *osh, void *sdh, void *regsva, DHD_ERROR(("%s: FAILED to return to SI_ENUM_BASE\n", __FUNCTION__)); } -#ifdef DHD_DEBUG - DHD_ERROR(("F1 signature read @0x18000000=0x%4x\n", - bcmsdh_reg_read(bus->sdh, SI_ENUM_BASE, 4))); - -#endif /* DHD_DEBUG */ - -#ifndef BCMSPI /* wake-wlan in gSPI will bring up the htavail/alpavail clocks. */ +#ifndef BCMSPI /* wake-wlan in gSPI will bring up the htavail/alpavail clocks. */ /* Force PLL off until si_attach() programs PLL control regs */ @@ -7773,6 +7585,11 @@ dhdsdio_probe_attach(struct dhd_bus *bus, osl_t *osh, void *sdh, void *regsva, goto fail; } +#ifdef DHD_DEBUG + DHD_ERROR(("F1 signature OK, socitype:0x%x chip:0x%4x rev:0x%x pkg:0x%x\n", + bus->sih->socitype, bus->sih->chip, bus->sih->chiprev, bus->sih->chippkg)); +#endif /* DHD_DEBUG */ + #ifdef REGON_BP_HANG_FIX /* WAR - for 43241 B0-B1-B2. B3 onwards do not need this */ if (((uint16)bus->sih->chip == BCM4324_CHIP_ID) && (bus->sih->chiprev < 3)) @@ -7823,14 +7640,20 @@ dhdsdio_probe_attach(struct dhd_bus *bus, osl_t *osh, void *sdh, void *regsva, /* also populate base address */ switch ((uint16)bus->sih->chip) { case BCM4335_CHIP_ID: + case BCM4339_CHIP_ID: + case BCM43349_CHIP_ID: bus->dongle_ram_base = CR4_4335_RAM_BASE; break; case BCM4350_CHIP_ID: + case BCM4354_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; + break; default: bus->dongle_ram_base = 0; DHD_ERROR(("%s: WARNING: Using default ram base at 0x%x\n", @@ -7838,8 +7661,8 @@ dhdsdio_probe_attach(struct dhd_bus *bus, osl_t *osh, void *sdh, void *regsva, } } bus->ramsize = bus->orig_ramsize; - if (dhd_dongle_memsize) - dhd_dongle_setmemsize(bus, dhd_dongle_memsize); + if (dhd_dongle_ramsize) + dhd_dongle_setramsize(bus, dhd_dongle_ramsize); DHD_ERROR(("DHD: dongle ram size is set to %d(orig %d) at 0x%x\n", bus->ramsize, bus->orig_ramsize, bus->dongle_ram_base)); @@ -7883,12 +7706,8 @@ dhdsdio_probe_attach(struct dhd_bus *bus, osl_t *osh, void *sdh, void *regsva, if ((bus->poll = (bool)dhd_poll)) bus->pollrate = 1; -#ifdef BCMSDIOH_TXGLOM - /* Setting default Glom mode */ - bus->glom_mode = bcmsdh_set_mode(bus->sdh, SDPCM_DEFGLOM_MODE); /* Setting default Glom size */ - bus->glomsize = SDPCM_DEFGLOM_SIZE; -#endif + bus->txglomsize = SDPCM_DEFGLOM_SIZE; return TRUE; @@ -7906,20 +7725,20 @@ dhdsdio_probe_malloc(dhd_bus_t *bus, osl_t *osh, void *sdh) DHD_TRACE(("%s: Enter\n", __FUNCTION__)); if (bus->dhd->maxctl) { - bus->rxblen = ROUNDUP((bus->dhd->maxctl + SDPCM_HDRLEN), ALIGNMENT) + DHD_SDALIGN; - if (!(bus->rxbuf = DHD_OS_PREALLOC(osh, DHD_PREALLOC_RXBUF, bus->rxblen))) { + bus->rxblen = ROUNDUP((bus->dhd->maxctl+SDPCM_HDRLEN), ALIGNMENT) + DHD_SDALIGN; + if (!(bus->rxbuf = DHD_OS_PREALLOC(bus->dhd, DHD_PREALLOC_RXBUF, bus->rxblen))) { DHD_ERROR(("%s: MALLOC of %d-byte rxbuf failed\n", __FUNCTION__, bus->rxblen)); goto fail; } } /* Allocate buffer to receive glomed packet */ - if (!(bus->databuf = DHD_OS_PREALLOC(osh, DHD_PREALLOC_DATABUF, MAX_DATA_BUF))) { + if (!(bus->databuf = DHD_OS_PREALLOC(bus->dhd, DHD_PREALLOC_DATABUF, MAX_DATA_BUF))) { DHD_ERROR(("%s: MALLOC of %d-byte databuf failed\n", __FUNCTION__, MAX_DATA_BUF)); /* release rxbuf which was already located as above */ if (!bus->rxblen) - DHD_OS_PREFREE(osh, bus->rxbuf, bus->rxblen); + DHD_OS_PREFREE(bus->dhd, bus->rxbuf, bus->rxblen); goto fail; } @@ -7942,6 +7761,8 @@ dhdsdio_probe_init(dhd_bus_t *bus, osl_t *osh, void *sdh) DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + bus->_srenab = FALSE; + #ifdef SDTEST dhdsdio_pktgen_init(bus); #endif /* SDTEST */ @@ -7996,11 +7817,23 @@ dhdsdio_probe_init(dhd_bus_t *bus, osl_t *osh, void *sdh) DHD_INFO(("%s: Initial value for %s is %d\n", __FUNCTION__, "sd_blocksize", bus->blocksize)); - if (bus->sih->chip == BCM4335_CHIP_ID) - dhd_overflow_war(bus); + dhdsdio_tune_fifoparam(bus); } bus->roundup = MIN(max_roundup, bus->blocksize); + if (bus->pad_pkt) + PKTFREE(osh, bus->pad_pkt, FALSE); + bus->pad_pkt = PKTGET(osh, SDIO_MAX_BLOCK_SIZE, FALSE); + if (bus->pad_pkt == NULL) + DHD_ERROR(("failed to allocate padding packet\n")); + else { + int alignment_offset = 0; + uintptr pktprt = (uintptr)PKTDATA(osh, bus->pad_pkt); + if (!(pktprt&1) && (pktprt = (pktprt % DHD_SDALIGN))) + PKTPUSH(osh, bus->pad_pkt, alignment_offset); + PKTSETNEXT(osh, bus->pad_pkt, NULL); + } + /* Query if bus module supports packet chaining, default to use if supported */ if (bcmsdh_iovar_op(sdh, "sd_rxchain", NULL, 0, &bus->sd_rxchain, sizeof(int32), FALSE) != BCME_OK) { @@ -8010,44 +7843,55 @@ dhdsdio_probe_init(dhd_bus_t *bus, osl_t *osh, void *sdh) __FUNCTION__, (bus->sd_rxchain ? "supports" : "does not support"))); } bus->use_rxchain = (bool)bus->sd_rxchain; + bus->txinrx_thres = CUSTOM_TXINRX_THRES; return TRUE; } -bool +int dhd_bus_download_firmware(struct dhd_bus *bus, osl_t *osh, char *pfw_path, char *pnv_path) { - bool ret; + int ret; + bus->fw_path = pfw_path; bus->nv_path = pnv_path; ret = dhdsdio_download_firmware(bus, osh, bus->sdh); -#ifdef BCMSPI -#ifdef GSPI_DWORD_MODE +#if defined(BCMSPI) && defined(GSPI_DWORD_MODE) /* Enable the dwordmode in gSPI before first F2 transaction */ if ((bus->sih->chip == BCM4329_CHIP_ID) && (bus->sih->chiprev > 1)) { bcmsdh_dwordmode(bus->sdh, TRUE); bus->dwordmode = TRUE; } -#endif /* GSPI_DWORD_MODE */ -#endif /* BCMSPI */ +#endif /* defined(BCMSPI) && defined(GSPI_DWORD_MODE) */ return ret; } -static bool +static int dhdsdio_download_firmware(struct dhd_bus *bus, osl_t *osh, void *sdh) { - bool ret; + int ret; + +#if defined(SUPPORT_MULTIPLE_REVISION) + if (concate_revision(bus, bus->fw_path, sizeof(bus->fw_path), + bus->nv_path, sizeof(bus->nv_path)) != 0) { + DHD_ERROR(("%s: fail to concatnate revison \n", + __FUNCTION__)); + return BCME_BADARG; + } +#endif /* SUPPORT_MULTIPLE_REVISION */ + DHD_TRACE_HW4(("%s: firmware path=%s, nvram path=%s\n", + __FUNCTION__, bus->fw_path, bus->nv_path)); DHD_OS_WAKE_LOCK(bus->dhd); /* Download the firmware */ dhdsdio_clkctl(bus, CLK_AVAIL, FALSE); - ret = _dhdsdio_download_firmware(bus) == 0; + ret = _dhdsdio_download_firmware(bus); dhdsdio_clkctl(bus, CLK_SDONLY, FALSE); @@ -8087,12 +7931,12 @@ dhdsdio_release(dhd_bus_t *bus, osl_t *osh) MFREE(osh, bus->console.buf, bus->console.bufsize); #endif + if (bus->pad_pkt) + PKTFREE(osh, bus->pad_pkt, FALSE); + MFREE(osh, bus, sizeof(dhd_bus_t)); } - if (osh) - dhd_osl_detach(osh); - DHD_TRACE(("%s: Disconnected\n", __FUNCTION__)); } @@ -8164,6 +8008,7 @@ dhdsdio_disconnect(void *ptr) 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__)); @@ -8173,6 +8018,7 @@ dhdsdio_disconnect(void *ptr) } mutex_lock(&_dhd_sdio_mutex_lock_); #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) */ +#endif if (bus) { @@ -8180,15 +8026,39 @@ dhdsdio_disconnect(void *ptr) dhdsdio_release(bus, bus->dhd->osh); } +#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: Disconnected\n", __FUNCTION__)); } +static int +dhdsdio_suspend(void *context) +{ + int ret = 0; + + dhd_bus_t *bus = (dhd_bus_t*)context; + ret = dhd_os_check_wakelock(bus->dhd); + return ret; +} + +static int +dhdsdio_resume(void *context) +{ +#if defined(OOB_INTR_ONLY) || defined(BCMSPI_ANDROID) + dhd_bus_t *bus = (dhd_bus_t*)context; + + if (dhd_os_check_if_up(bus->dhd)) + bcmsdh_oob_intr_set(bus->sdh, TRUE); +#endif /* defined(OOB_INTR_ONLY) || defined(BCMSPI_ANDROID) */ + return 0; +} + /* Register/Unregister functions are called by the main DHD entry * point (e.g. module insertion) to link with the bus driver, in @@ -8197,7 +8067,9 @@ dhdsdio_disconnect(void *ptr) static bcmsdh_driver_t dhd_sdio = { dhdsdio_probe, - dhdsdio_disconnect + dhdsdio_disconnect, + dhdsdio_suspend, + dhdsdio_resume }; int @@ -8568,29 +8440,48 @@ dhd_bcmsdh_recv_buf(dhd_bus_t *bus, uint32 addr, uint fn, uint flags, uint8 *buf static int dhd_bcmsdh_send_buf(dhd_bus_t *bus, uint32 addr, uint fn, uint flags, uint8 *buf, uint nbytes, - void *pkt, bcmsdh_cmplt_fn_t complete, void *handle) + void *pkt, bcmsdh_cmplt_fn_t complete, void *handle, int max_retry) { - if (!KSO_ENAB(bus)) { - DHD_ERROR(("%s: Device asleep\n", __FUNCTION__)); - return BCME_NODEVICE; - } + int ret; + int i = 0; + int retries = 0; + bcmsdh_info_t *sdh; - return (bcmsdh_send_buf(bus->sdh, addr, fn, flags, buf, nbytes, pkt, complete, handle)); -} + sdh = bus->sdh; + do { + ret = bcmsdh_send_buf(bus->sdh, addr, fn, flags, buf, nbytes, + pkt, complete, handle); -#ifdef BCMSDIOH_TXGLOM -static void -dhd_bcmsdh_glom_post(dhd_bus_t *bus, uint8 *frame, void *pkt, uint len) -{ - bcmsdh_glom_post(bus->sdh, frame, pkt, len); -} + bus->f2txdata++; + ASSERT(ret != BCME_PENDING); -static void -dhd_bcmsdh_glom_clear(dhd_bus_t *bus) -{ - bcmsdh_glom_clear(bus->sdh); + 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; + } + } + } while ((ret < 0) && retrydata && ++retries < max_retry); + + return ret; } -#endif uint dhd_bus_chip(struct dhd_bus *bus) @@ -8599,6 +8490,14 @@ dhd_bus_chip(struct dhd_bus *bus) return bus->sih->chip; } +uint +dhd_bus_chiprev(struct dhd_bus *bus) +{ + ASSERT(bus); + ASSERT(bus->sih != NULL); + return bus->sih->chiprev; +} + void * dhd_bus_pub(struct dhd_bus *bus) { @@ -8606,15 +8505,15 @@ dhd_bus_pub(struct dhd_bus *bus) } void * -dhd_bus_txq(struct dhd_bus *bus) +dhd_bus_sih(struct dhd_bus *bus) { - return &bus->txq; + return (void *)bus->sih; } -uint -dhd_bus_hdrlen(struct dhd_bus *bus) +void * +dhd_bus_txq(struct dhd_bus *bus) { - return SDPCM_HDRLEN; + return &bus->txq; } int @@ -8639,7 +8538,9 @@ dhd_bus_devreset(dhd_pub_t *dhdp, uint8 flag) #if defined(OOB_INTR_ONLY) || defined(BCMSPI_ANDROID) /* Clean up any pending IRQ */ - bcmsdh_set_irq(FALSE); + dhd_enable_oob_intr(bus, FALSE); + bcmsdh_oob_intr_set(bus->sdh, FALSE); + bcmsdh_oob_intr_unregister(bus->sdh); #endif /* defined(OOB_INTR_ONLY) || defined(BCMSPI_ANDROID) */ /* Clean tx/rx buffer pointers, detach from the dongle */ @@ -8647,9 +8548,7 @@ dhd_bus_devreset(dhd_pub_t *dhdp, uint8 flag) bus->dhd->dongle_reset = TRUE; bus->dhd->up = FALSE; -#ifdef BCMSDIOH_TXGLOM dhd_txglom_enable(dhdp, FALSE); -#endif dhd_os_sdunlock(dhdp); DHD_TRACE(("%s: WLAN OFF DONE\n", __FUNCTION__)); @@ -8663,28 +8562,26 @@ dhd_bus_devreset(dhd_pub_t *dhdp, uint8 flag) if (bus->dhd->dongle_reset) { /* Turn on WLAN */ -#ifdef DHDTHREAD dhd_os_sdlock(dhdp); -#endif /* DHDTHREAD */ /* Reset SD client */ bcmsdh_reset(bus->sdh); /* Attempt to re-attach & download */ if (dhdsdio_probe_attach(bus, bus->dhd->osh, bus->sdh, - (uint32 *)SI_ENUM_BASE, - bus->cl_devid)) { + (uint32 *)SI_ENUM_BASE, + bus->cl_devid)) { /* Attempt to download binary to the dongle */ if (dhdsdio_probe_init(bus, bus->dhd->osh, bus->sdh) && - dhdsdio_download_firmware(bus, bus->dhd->osh, bus->sdh)) { + dhdsdio_download_firmware(bus, bus->dhd->osh, bus->sdh) >= 0) { /* Re-init bus, enable F2 transfer */ bcmerror = dhd_bus_init((dhd_pub_t *) bus->dhd, FALSE); if (bcmerror == BCME_OK) { #if defined(OOB_INTR_ONLY) || defined(BCMSPI_ANDROID) - bcmsdh_set_irq(TRUE); -#ifndef BCMSPI_ANDROID dhd_enable_oob_intr(bus, TRUE); -#endif /* !BCMSPI_ANDROID */ + bcmsdh_oob_intr_register(bus->sdh, + dhdsdio_isr, bus); + bcmsdh_oob_intr_set(bus->sdh, TRUE); #endif /* defined(OOB_INTR_ONLY) || defined(BCMSPI_ANDROID) */ bus->dhd->dongle_reset = FALSE; @@ -8695,12 +8592,7 @@ dhd_bus_devreset(dhd_pub_t *dhdp, uint8 flag) dhd_txflowcontrol(bus->dhd, ALL_INTERFACES, OFF); #endif dhd_os_wd_timer(dhdp, dhd_watchdog_ms); -#ifdef BCMSDIOH_TXGLOM - if ((dhdp->busstate == DHD_BUS_DATA) && - bcmsdh_glom_enabled()) { - dhd_txglom_enable(dhdp, TRUE); - } -#endif /* BCMSDIOH_TXGLOM */ + DHD_TRACE(("%s: WLAN ON DONE\n", __FUNCTION__)); } else { dhd_bus_stop(bus, FALSE); @@ -8712,15 +8604,13 @@ dhd_bus_devreset(dhd_pub_t *dhdp, uint8 flag) } else bcmerror = BCME_SDIO_ERROR; -#ifdef DHDTHREAD dhd_os_sdunlock(dhdp); -#endif /* DHDTHREAD */ } else { bcmerror = BCME_SDIO_ERROR; DHD_INFO(("%s called when dongle is not in reset\n", __FUNCTION__)); DHD_INFO(("Will call dhd_bus_start instead\n")); - sdioh_start(NULL, 1); + dhd_bus_resume(dhdp, 1); if ((bcmerror = dhd_bus_start(dhdp)) != 0) DHD_ERROR(("%s: dhd_bus_start fail with %d\n", __FUNCTION__, bcmerror)); @@ -8729,6 +8619,16 @@ dhd_bus_devreset(dhd_pub_t *dhdp, uint8 flag) return bcmerror; } +int dhd_bus_suspend(dhd_pub_t *dhdpub) +{ + return bcmsdh_stop(dhdpub->bus->sdh); +} + +int dhd_bus_resume(dhd_pub_t *dhdpub, int stage) +{ + return bcmsdh_start(dhdpub->bus->sdh, stage); +} + /* Get Chip ID version */ uint dhd_bus_chip_id(dhd_pub_t *dhdp) { @@ -8736,6 +8636,31 @@ uint dhd_bus_chip_id(dhd_pub_t *dhdp) return bus->sih->chip; } + +/* Get Chip Rev ID version */ +uint dhd_bus_chiprev_id(dhd_pub_t *dhdp) +{ + dhd_bus_t *bus = dhdp->bus; + + return bus->sih->chiprev; +} + +/* Get Chip Pkg ID version */ +uint dhd_bus_chippkg_id(dhd_pub_t *dhdp) +{ + dhd_bus_t *bus = dhdp->bus; + + return bus->sih->chippkg; +} + +int dhd_bus_get_ids(struct dhd_bus *bus, uint32 *bus_type, uint32 *bus_num, uint32 *slot_num) +{ + *bus_type = bus->bus; + *bus_num = bus->bus_num; + *slot_num = bus->slot_num; + return 0; +} + int dhd_bus_membytes(dhd_pub_t *dhdp, bool set, uint32 address, uint8 *data, uint size) { @@ -8744,9 +8669,26 @@ dhd_bus_membytes(dhd_pub_t *dhdp, bool set, uint32 address, uint8 *data, uint si bus = dhdp->bus; return dhdsdio_membytes(bus, set, address, data, size); } -#if defined(CUSTOMER_HW4) && defined(SUPPORT_MULTIPLE_REVISION) +#if defined(SUPPORT_MULTIPLE_REVISION) +/* Just print chip revision for BCM4330 */ +static int +concate_revision_bcm4330(dhd_bus_t *bus) +{ + uint chipver; + chipver = bus->sih->chiprev; + + if (chipver == 3) + DHD_ERROR(("---- CHIP bcm4330 B1 ----\n")); + else if (chipver == 4) + DHD_ERROR(("---- CHIP bcm4330 B2 ----\n")); + else + DHD_ERROR(("----- Invalid chip version -----\n")); + return 0; +} + static int -concate_revision_bcm4334(dhd_bus_t *bus, char *path, int path_len) +concate_revision_bcm4334(dhd_bus_t *bus, + char *fw_path, int fw_path_len, char *nv_path, int nv_path_len) { #define REV_ID_ADDR 0x1E008F90 #define BCM4334_B1_UNIQUE 0x30312E36 @@ -8754,7 +8696,11 @@ concate_revision_bcm4334(dhd_bus_t *bus, char *path, int path_len) uint chipver; uint32 unique_id; uint8 data[4]; - char chipver_tag[4] = "_b?"; +#if defined(SUPPORT_MULTIPLE_CHIPS) + char chipver_tag[10] = "_4334"; +#else + char chipver_tag[4] = ""; +#endif /* defined(SUPPORT_MULTIPLE_CHIPS) */ DHD_TRACE(("%s: BCM4334 Multiple Revision Check\n", __FUNCTION__)); if (bus->sih->chip != BCM4334_CHIP_ID) { @@ -8771,19 +8717,24 @@ concate_revision_bcm4334(dhd_bus_t *bus, char *path, int path_len) DHD_ERROR(("CHIP VER = [0x%x]\n", chipver)); if (chipver == 1) { DHD_ERROR(("----- CHIP bcm4334_B0 -----\n")); - strcpy(chipver_tag, "_b0"); + strcat(chipver_tag, "_b0"); } else if (chipver == 2) { DHD_ERROR(("----- CHIP bcm4334_B1 -----\n")); - strcpy(chipver_tag, "_b1"); + strcat(chipver_tag, "_b1"); } else if (chipver == 3) { DHD_ERROR(("----- CHIP bcm4334_B2 -----\n")); - strcpy(chipver_tag, "_b2"); + strcat(chipver_tag, "_b2"); } else { DHD_ERROR(("----- Invalid chip version -----\n")); return -1; } - strcat(path, chipver_tag); + + strcat(fw_path, chipver_tag); +#if defined(SUPPORT_MULTIPLE_CHIPS) + strcat(nv_path, chipver_tag); +#endif /* defined(SUPPORT_MULTIPLE_CHIPS) */ + #undef REV_ID_ADDR #undef BCM4334_B1_UNIQUE return 0; @@ -8795,7 +8746,11 @@ concate_revision_bcm4335 { uint chipver; +#if defined(SUPPORT_MULTIPLE_CHIPS) + char chipver_tag[10] = "_4335"; +#else char chipver_tag[4] = {0, }; +#endif /* defined(SUPPORT_MULTIPLE_CHIPS) */ DHD_TRACE(("%s: BCM4335 Multiple Revision Check\n", __FUNCTION__)); if (bus->sih->chip != BCM4335_CHIP_ID) { @@ -8806,32 +8761,350 @@ concate_revision_bcm4335 DHD_ERROR(("CHIP VER = [0x%x]\n", chipver)); if (chipver == 0x0) { DHD_ERROR(("----- CHIP bcm4335_A0 -----\n")); - strcpy(chipver_tag, "_a0"); + strcat(chipver_tag, "_a0"); } else if (chipver == 0x1) { DHD_ERROR(("----- CHIP bcm4335_B0 -----\n")); +#if defined(SUPPORT_MULTIPLE_CHIPS) + strcat(chipver_tag, "_b0"); +#endif /* defined(SUPPORT_MULTIPLE_CHIPS) */ + } + + strcat(fw_path, chipver_tag); + strcat(nv_path, chipver_tag); + return 0; +} + +static int +concate_revision_bcm4339 + (dhd_bus_t *bus, char *fw_path, int fw_path_len, char *nv_path, int nv_path_len) +{ + + uint chipver; +#if defined(SUPPORT_MULTIPLE_CHIPS) + char chipver_tag[10] = "_4339"; +#else + char chipver_tag[4] = {0, }; +#endif /* defined(SUPPORT_MULTIPLE_CHIPS) */ + + DHD_TRACE(("%s: BCM4339 Multiple Revision Check\n", __FUNCTION__)); + if (bus->sih->chip != BCM4339_CHIP_ID) { + DHD_ERROR(("%s:Chip is not BCM4339\n", __FUNCTION__)); + return -1; + } + chipver = bus->sih->chiprev; + DHD_ERROR(("CHIP VER = [0x%x]\n", chipver)); + if (chipver == 0x1) { + DHD_ERROR(("----- CHIP bcm4339_A0 -----\n")); + strcat(chipver_tag, "_a0"); + } else { + DHD_ERROR(("----- CHIP bcm4339 unknown revision %d -----\n", + chipver)); + } + + strcat(fw_path, chipver_tag); + strcat(nv_path, chipver_tag); + return 0; +} + +static int +concate_revision_bcm43349 + (dhd_bus_t *bus, char *fw_path, int fw_path_len, char *nv_path, int nv_path_len) +{ + + uint chipver; +#if defined(SUPPORT_MULTIPLE_CHIPS) + char chipver_tag[10] = "_43349"; +#else + char chipver_tag[4] = {0, }; +#endif /* defined(SUPPORT_MULTIPLE_CHIPS) */ + + DHD_TRACE(("%s: BCM43349 Multiple Revision Check\n", __FUNCTION__)); + if (bus->sih->chip != BCM43349_CHIP_ID) { + DHD_ERROR(("%s:Chip is not BCM43349\n", __FUNCTION__)); + return -1; + } + chipver = bus->sih->chiprev; + DHD_ERROR(("CHIP VER = [0x%x]\n", chipver)); + if (chipver == 0x1) { + DHD_ERROR(("----- CHIP bcm43349_A0 -----\n")); + strcat(chipver_tag, "_a0"); + } else { + DHD_ERROR(("----- CHIP bcm43349 unknown revision %d -----\n", + chipver)); + } + + strcat(fw_path, chipver_tag); + strcat(nv_path, chipver_tag); + return 0; +} + +static int concate_revision_bcm43241(dhd_bus_t *bus, + char *fw_path, int fw_path_len, char *nv_path, int nv_path_len) +{ + uint32 chip_id, chip_ver; +#if defined(SUPPORT_MULTIPLE_CHIPS) + char chipver_tag[10] = "_43241"; +#else + char chipver_tag[4] = {0, }; +#endif /* defined(SUPPORT_MULTIPLE_CHIPS) */ + + DHD_TRACE(("%s: BCM43241 Multiple Revision Check\n", __FUNCTION__)); + + chip_id = bus->sih->chip; + chip_ver = bus->sih->chiprev; + + if (chip_ver == 2) { + DHD_ERROR(("----- CHIP bcm43241_B0 -----\n")); + strcat(chipver_tag, "_b0"); + } else if (chip_ver == 5) { + DHD_ERROR(("----- CHIP bcm43241_B4 -----\n")); + strcat(chipver_tag, "_b4"); + } else { + DHD_ERROR(("----- Invalid chip version -----\n")); + return -1; } + strcat(fw_path, chipver_tag); strcat(nv_path, chipver_tag); + return 0; +} + +static int concate_revision_bcm4350(dhd_bus_t *bus, + char *fw_path, int fw_path_len, char *nv_path, int nv_path_len) +{ + uint32 chip_id, chip_ver; +#if defined(SUPPORT_MULTIPLE_CHIPS) + char chipver_tag[10] = {0, }; +#else + char chipver_tag[4] = {0, }; +#endif /* defined(SUPPORT_MULTIPLE_CHIPS) */ + chip_id = bus->sih->chip; + chip_ver = bus->sih->chiprev; + +#if defined(SUPPORT_MULTIPLE_CHIPS) + if (chip_ver == 3) + strcat(chipver_tag, "_4354"); + else + strcat(chipver_tag, "_4350"); +#endif + + if (chip_ver == 3) { + DHD_ERROR(("----- CHIP 4354 A0 -----\n")); + strcat(chipver_tag, "_a0"); + } else { + DHD_ERROR(("----- Unknown chip version, ver=%x -----\n", chip_ver)); + } + strcat(fw_path, chipver_tag); + strcat(nv_path, chipver_tag); return 0; +} + +static int concate_revision_bcm4354(dhd_bus_t *bus, + char *fw_path, int fw_path_len, char *nv_path, int nv_path_len) +{ + uint32 chip_id, chip_ver; +#if defined(SUPPORT_MULTIPLE_CHIPS) + char chipver_tag[10] = "_4354"; +#else +#if !defined(CUSTOMER_HW4) + char chipver_tag[4] = {0, }; +#endif /* !CUSTOMER_HW4 */ +#endif /* SUPPORT_MULTIPLE_CHIPS */ + chip_id = bus->sih->chip; + chip_ver = bus->sih->chiprev; +#if !defined(SUPPORT_MULTIPLE_CHIPS) && defined(CUSTOMER_HW4) + DHD_INFO(("----- CHIP 4354, ver=%x -----\n", chip_ver)); +#else + if (chip_ver == 1) { + DHD_ERROR(("----- CHIP 4354 A1 -----\n")); + strcat(chipver_tag, "_a1"); + } else { + DHD_ERROR(("----- Unknown chip version, ver=%x -----\n", chip_ver)); + } + + strcat(fw_path, chipver_tag); + strcat(nv_path, chipver_tag); +#endif /* !SUPPORT_MULTIPLE_CHIPS && CUSTOMER_HW4 */ + + return 0; } + int concate_revision(dhd_bus_t *bus, char *fw_path, int fw_path_len, char *nv_path, int nv_path_len) { + int res = 0; + if (!bus || !bus->sih) { DHD_ERROR(("%s:Bus is Invalid\n", __FUNCTION__)); return -1; } + +#if defined(PLATFORM_MPS) +/* get the base name (prefix) for firmware & nvram from the platform */ + wifi_get_fw_nv_path(fw_path, nv_path); +#endif + switch (bus->sih->chip) { - case BCM4334_CHIP_ID: - return concate_revision_bcm4334(bus, fw_path, fw_path_len); - case BCM4335_CHIP_ID: - return concate_revision_bcm4335(bus, fw_path, fw_path_len, - nv_path, nv_path_len); + case BCM4330_CHIP_ID: + res = concate_revision_bcm4330(bus); + break; + case BCM4334_CHIP_ID: + res = concate_revision_bcm4334(bus, fw_path, fw_path_len, + nv_path, nv_path_len); + break; + case BCM4335_CHIP_ID: + res = concate_revision_bcm4335(bus, fw_path, fw_path_len, + nv_path, nv_path_len); + break; + case BCM4339_CHIP_ID: + res = concate_revision_bcm4339(bus, fw_path, fw_path_len, + nv_path, nv_path_len); + break; + case BCM43349_CHIP_ID: + res = concate_revision_bcm43349(bus, fw_path, fw_path_len, + nv_path, nv_path_len); + break; + case BCM4324_CHIP_ID: + res = concate_revision_bcm43241(bus, fw_path, fw_path_len, nv_path, nv_path_len); + break; + case BCM4350_CHIP_ID: + res = concate_revision_bcm4350(bus, fw_path, fw_path_len, nv_path, nv_path_len); + break; + case BCM4354_CHIP_ID: + res = concate_revision_bcm4354(bus, fw_path, fw_path_len, nv_path, nv_path_len); + break; + + + default: + DHD_ERROR(("REVISION SPECIFIC feature is not required\n")); + return res; } - DHD_ERROR(("REVISION SPECIFIC feature is not required\n")); - return 0; + if (res == 0) { + } + return res; +} +#endif /* SUPPORT_MULTIPLE_REVISION */ + + +void +dhd_bus_update_fw_nv_path(struct dhd_bus *bus, char *pfw_path, char *pnv_path) +{ + bus->fw_path = pfw_path; + bus->nv_path = pnv_path; +} + +int +dhd_enableOOB(dhd_pub_t *dhd, bool sleep) +{ + dhd_bus_t *bus = dhd->bus; + sdpcmd_regs_t *regs = bus->regs; + uint retries = 0; + + if (sleep) { + dhdsdio_clkctl(bus, CLK_AVAIL, FALSE); + /* Tell device to start using OOB wakeup */ + W_SDREG(SMB_USE_OOB, ®s->tosbmailbox, retries); + if (retries > retry_limit) { + DHD_ERROR(("CANNOT SIGNAL CHIP, WILL NOT WAKE UP!!\n")); + return BCME_BUSY; + } + /* Turn off our contribution to the HT clock request */ + dhdsdio_clkctl(bus, CLK_SDONLY, FALSE); + } else { + /* Make sure the controller has the bus up */ + dhdsdio_clkctl(bus, CLK_AVAIL, FALSE); + + /* Send misc interrupt to indicate OOB not needed */ + W_SDREG(0, ®s->tosbmailboxdata, retries); + if (retries <= retry_limit) + W_SDREG(SMB_DEV_INT, ®s->tosbmailbox, retries); + + if (retries > retry_limit) + DHD_ERROR(("CANNOT SIGNAL CHIP TO CLEAR OOB!!\n")); + + /* Make sure we have SD bus access */ + dhdsdio_clkctl(bus, CLK_SDONLY, FALSE); + } + return BCME_OK; +} + +void +dhd_bus_pktq_flush(dhd_pub_t *dhdp) +{ + dhd_bus_t *bus = dhdp->bus; + bool wlfc_enabled = FALSE; + +#ifdef PROP_TXSTATUS + wlfc_enabled = (dhd_wlfc_cleanup_txq(dhdp, NULL, 0) != WLFC_UNSUPPORTED); +#endif + if (!wlfc_enabled) { +#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 */ + /* Clear the data packet queues */ + pktq_flush(dhdp->osh, &bus->txq, TRUE, NULL, 0); + } +} + +int +dhd_sr_config(dhd_pub_t *dhd, bool on) +{ + dhd_bus_t *bus = dhd->bus; + + if (!bus->_srenab) + return -1; + + return dhdsdio_clk_devsleep_iovar(bus, on); +} + +uint16 +dhd_get_chipid(dhd_pub_t *dhd) +{ + dhd_bus_t *bus = dhd->bus; + + if (bus && bus->sih) + return (uint16)bus->sih->chip; + else + return 0; +} + +#ifdef DEBUGGER +uint32 dhd_sdio_reg_read(void *h, uint32 addr) +{ + uint32 rval; + struct dhd_bus *bus = (struct dhd_bus *) h; + + dhd_os_sdlock(bus->dhd); + + BUS_WAKE(bus); + + dhdsdio_clkctl(bus, CLK_AVAIL, FALSE); + + rval = bcmsdh_reg_read(bus->sdh, addr, 4); + + dhd_os_sdunlock(bus->dhd); + + return rval; +} + +void dhd_sdio_reg_write(void *h, uint32 addr, uint32 val) +{ + struct dhd_bus *bus = (struct dhd_bus *) h; + + dhd_os_sdlock(bus->dhd); + + BUS_WAKE(bus); + + dhdsdio_clkctl(bus, CLK_AVAIL, FALSE); + + bcmsdh_reg_write(bus->sdh, addr, 4, val); + + dhd_os_sdunlock(bus->dhd); } -#endif /* CUSTOMER_HW4 && SUPPORT_MULTIPLE_REVISION */ +#endif /* DEBUGGER */ |