diff options
author | Dmitry Shmidt <dimitrysh@google.com> | 2010-09-17 15:45:46 -0700 |
---|---|---|
committer | Dmitry Shmidt <dimitrysh@google.com> | 2010-09-17 15:45:46 -0700 |
commit | 9984c42af1f24da360515edd81441ef5dfe23da4 (patch) | |
tree | 2abfefacbe77996fce91b36d145f8df3a7a0672e | |
parent | d81065e3c1b57aabcc5a29fa5be9e02a5a8527a8 (diff) | |
download | android_hardware_broadcom_wlan-9984c42af1f24da360515edd81441ef5dfe23da4.tar.gz android_hardware_broadcom_wlan-9984c42af1f24da360515edd81441ef5dfe23da4.tar.bz2 android_hardware_broadcom_wlan-9984c42af1f24da360515edd81441ef5dfe23da4.zip |
bcm4329: Update to version 4.218.248.6
Auto-merge: no
Change-Id: I33451efb37f03abe4008d1bfeef378bc61249c69
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
83 files changed, 9230 insertions, 775 deletions
diff --git a/bcm4329/src/bcmsdio/sys/bcmpcispi.c b/bcm4329/src/bcmsdio/sys/bcmpcispi.c index ba3e0c5..1a8b671 100644 --- a/bcm4329/src/bcmsdio/sys/bcmpcispi.c +++ b/bcm4329/src/bcmsdio/sys/bcmpcispi.c @@ -1,7 +1,7 @@ /* * Broadcom SPI over PCI-SPI Host Controller, low-level hardware driver * - * Copyright (C) 1999-2009, Broadcom Corporation + * Copyright (C) 1999-2010, 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: bcmpcispi.c,v 1.22.2.4.4.5 2008/07/09 21:23:30 Exp $ + * $Id: bcmpcispi.c,v 1.22.2.4.4.5.6.1 2010/08/13 00:26:05 Exp $ */ #include <typedefs.h> @@ -606,18 +606,23 @@ spi_spinbits(sdioh_info_t *sd) spin_count = 0; while ((SPIPCI_RREG(sd->osh, ®s->spih_stat) & SPIH_WFEMPTY) == 0) { if (spin_count > SPI_SPIN_BOUND) { - ASSERT(FALSE); /* Spin bound exceeded */ + sd_err(("%s: SPIH_WFEMPTY spin bits out of bound %u times \n", + __FUNCTION__, spin_count)); + ASSERT(FALSE); } spin_count++; } - spin_count = 0; + /* Wait for SPI Transfer state machine to return to IDLE state. * The state bits are only implemented in Rev >= 5 FPGA. These * bits are hardwired to 00 for Rev < 5, so this check doesn't cause * any problems. */ + spin_count = 0; while ((SPIPCI_RREG(osh, ®s->spih_stat) & SPIH_STATE_MASK) != 0) { if (spin_count > SPI_SPIN_BOUND) { + sd_err(("%s: SPIH_STATE_MASK spin bits out of bound %u times \n", + __FUNCTION__, spin_count)); ASSERT(FALSE); } spin_count++; diff --git a/bcm4329/src/bcmsdio/sys/bcmsdh.c b/bcm4329/src/bcmsdio/sys/bcmsdh.c index aed6859..4bf5889 100644 --- a/bcm4329/src/bcmsdio/sys/bcmsdh.c +++ b/bcm4329/src/bcmsdio/sys/bcmsdh.c @@ -40,9 +40,7 @@ #include <sdio.h> /* sdio spec */ -/* Defines number of access retries to configuration registers */ #define SDIOH_API_ACCESS_RETRY_LIMIT 2 - const uint bcmsdh_msglevel = BCMSDH_ERROR_VAL; diff --git a/bcm4329/src/bcmsdio/sys/bcmsdh_linux.c b/bcm4329/src/bcmsdio/sys/bcmsdh_linux.c index 0555f87..559f481 100644 --- a/bcm4329/src/bcmsdio/sys/bcmsdh_linux.c +++ b/bcm4329/src/bcmsdio/sys/bcmsdh_linux.c @@ -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: bcmsdh_linux.c,v 1.42.10.10.2.12 2010/03/10 03:09:48 Exp $ + * $Id: bcmsdh_linux.c,v 1.42.10.10.2.14.4.2 2010/09/15 00:30:11 Exp $ */ /** @@ -75,7 +75,11 @@ struct bcmsdh_hc { bcmsdh_info_t *sdh; /* SDIO Host Controller handle */ void *ch; unsigned int oob_irq; - unsigned long oob_flags; + unsigned long oob_flags; /* OOB Host specifiction as edge and etc */ + bool oob_irq_registered; +#if defined(OOB_INTR_ONLY) + spinlock_t irq_lock; +#endif }; static bcmsdh_hc_t *sdhcinfo = NULL; @@ -176,7 +180,7 @@ int bcmsdh_probe(struct device *dev) #endif /* BCMLXSDMMC */ int irq = 0; uint32 vendevid; - unsigned long irq_flags = IRQF_TRIGGER_FALLING; + unsigned long irq_flags = 0; #if !defined(BCMLXSDMMC) && defined(BCMPLATFORM_BUS) pdev = to_platform_device(dev); @@ -187,6 +191,12 @@ int bcmsdh_probe(struct device *dev) #endif /* BCMLXSDMMC */ #if defined(OOB_INTR_ONLY) +#ifdef HW_OOB + irq_flags = \ + IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL | IORESOURCE_IRQ_SHAREABLE; +#else + irq_flags = IRQF_TRIGGER_FALLING; +#endif /* HW_OOB */ irq = dhd_customer_oob_irq_map(&irq_flags); if (irq < 0) { SDLX_MSG(("%s: Host irq is not defined\n", __FUNCTION__)); @@ -225,6 +235,10 @@ int bcmsdh_probe(struct device *dev) sdhc->sdh = sdh; sdhc->oob_irq = irq; sdhc->oob_flags = irq_flags; + sdhc->oob_irq_registered = FALSE; /* to make sure.. */ +#if defined(OOB_INTR_ONLY) + spin_lock_init(&sdhc->irq_lock); +#endif /* chain SDIO Host Controller info together */ sdhc->next = sdhcinfo; @@ -338,7 +352,7 @@ extern uint sd_pci_slot; /* Force detection to a particular PCI */ /* slot only . Allows for having multiple */ /* WL devices at once in a PC */ /* Only one instance of dhd will be */ - /* useable at a time */ + /* usable at a time */ /* Upper word is bus number, */ /* lower word is slot number */ /* Default value of 0xFFFFffff turns this */ @@ -365,20 +379,21 @@ bcmsdh_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (pdev->bus->number != (sd_pci_slot>>16) || PCI_SLOT(pdev->devfn) != (sd_pci_slot&0xffff)) { SDLX_MSG(("%s: %s: bus %X, slot %X, vend %X, dev %X\n", - __FUNCTION__, - bcmsdh_chipmatch(pdev->vendor, pdev->device) - ? "Found compatible SDIOHC" - : "Probing unknown device", - pdev->bus->number, PCI_SLOT(pdev->devfn), pdev->vendor, - pdev->device)); + __FUNCTION__, + bcmsdh_chipmatch(pdev->vendor, pdev->device) ? + "Found compatible SDIOHC" : + "Probing unknown device", + pdev->bus->number, PCI_SLOT(pdev->devfn), + pdev->vendor, pdev->device)); return -ENODEV; } SDLX_MSG(("%s: %s: bus %X, slot %X, vendor %X, device %X (good PCI location)\n", - __FUNCTION__, - bcmsdh_chipmatch(pdev->vendor, pdev->device) - ? "Using compatible SDIOHC" - : "WARNING, forced use of unkown device", - pdev->bus->number, PCI_SLOT(pdev->devfn), pdev->vendor, pdev->device)); + __FUNCTION__, + bcmsdh_chipmatch(pdev->vendor, pdev->device) ? + "Using compatible SDIOHC" : + "WARNING, forced use of unkown device", + pdev->bus->number, PCI_SLOT(pdev->devfn), + pdev->vendor, pdev->device)); } if ((pdev->vendor == VENDOR_TI) && ((pdev->device == PCIXX21_FLASHMEDIA_ID) || @@ -439,7 +454,7 @@ bcmsdh_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) pci_set_master(pdev); rc = pci_enable_device(pdev); if (rc) { - SDLX_MSG(("%s: Cannot enble PCI device\n", __FUNCTION__)); + SDLX_MSG(("%s: Cannot enable PCI device\n", __FUNCTION__)); goto err; } if (!(sdh = bcmsdh_attach(osh, (void *)(uintptr)pci_resource_start(pdev, 0), @@ -566,15 +581,34 @@ bcmsdh_unregister(void) #endif /* BCMPLATFORM_BUS */ } + + #if defined(OOB_INTR_ONLY) +void bcmsdh_oob_intr_set(bool enable) +{ + static bool curstate = 1; + unsigned long flags; + + spin_lock_irqsave(&sdhcinfo->irq_lock, flags); + if (curstate != enable) { + if (enable) + enable_irq(sdhcinfo->oob_irq); + else + disable_irq_nosync(sdhcinfo->oob_irq); + curstate = enable; + } + spin_unlock_irqrestore(&sdhcinfo->irq_lock, flags); +} + static irqreturn_t wlan_oob_irq(int irq, void *dev_id) { dhd_pub_t *dhdp; dhdp = (dhd_pub_t *)dev_get_drvdata(sdhcinfo->dev); + bcmsdh_oob_intr_set(0); + if (dhdp == NULL) { - disable_irq(sdhcinfo->oob_irq); SDLX_MSG(("Out of band GPIO interrupt fired way too early\n")); return IRQ_HANDLED; } @@ -592,14 +626,18 @@ int bcmsdh_register_oob_intr(void * dhdp) dev_set_drvdata(sdhcinfo->dev, dhdp); - /* Refer to customer Host IRQ docs about proper irqflags definition */ - error = request_irq(sdhcinfo->oob_irq, wlan_oob_irq, sdhcinfo->oob_flags, - "bcmsdh_sdmmc", NULL); - - if (error) - return -ENODEV; + if (!sdhcinfo->oob_irq_registered) { + SDLX_MSG(("%s IRQ=%d Type=%X \n", __FUNCTION__, \ + (int)sdhcinfo->oob_irq, (int)sdhcinfo->oob_flags)); + /* Refer to customer Host IRQ docs about proper irqflags definition */ + error = request_irq(sdhcinfo->oob_irq, wlan_oob_irq, sdhcinfo->oob_flags, + "bcmsdh_sdmmc", NULL); + if (error) + return -ENODEV; - set_irq_wake(sdhcinfo->oob_irq, 1); + set_irq_wake(sdhcinfo->oob_irq, 1); + sdhcinfo->oob_irq_registered = TRUE; + } return 0; } @@ -611,14 +649,7 @@ void bcmsdh_unregister_oob_intr(void) set_irq_wake(sdhcinfo->oob_irq, 0); disable_irq(sdhcinfo->oob_irq); /* just in case.. */ free_irq(sdhcinfo->oob_irq, NULL); -} - -void bcmsdh_oob_intr_set(bool enable) -{ - if (enable) - enable_irq(sdhcinfo->oob_irq); - else - disable_irq(sdhcinfo->oob_irq); + sdhcinfo->oob_irq_registered = FALSE; } #endif /* defined(OOB_INTR_ONLY) */ /* Module parameters specific to each host-controller driver */ diff --git a/bcm4329/src/bcmsdio/sys/bcmsdh_sdmmc.c b/bcm4329/src/bcmsdio/sys/bcmsdh_sdmmc.c index 48d3f37..bda9193 100644 --- a/bcm4329/src/bcmsdio/sys/bcmsdh_sdmmc.c +++ b/bcm4329/src/bcmsdio/sys/bcmsdh_sdmmc.c @@ -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: bcmsdh_sdmmc.c,v 1.1.2.5.6.29 2010/03/19 17:16:08 Exp $ + * $Id: bcmsdh_sdmmc.c,v 1.1.2.5.6.30.4.1 2010/09/02 23:12:21 Exp $ */ #include <typedefs.h> @@ -55,7 +55,7 @@ extern void sdio_function_cleanup(void); #if !defined(OOB_INTR_ONLY) static void IRQHandler(struct sdio_func *func); static void IRQHandlerF2(struct sdio_func *func); -#endif +#endif /* !defined(OOB_INTR_ONLY) */ static int sdioh_sdmmc_get_cisaddr(sdioh_info_t *sd, uint32 regaddr); extern int sdio_reset_comm(struct mmc_card *card); @@ -676,6 +676,8 @@ sdioh_enable_hw_oob_intr(sdioh_info_t *sd, bool enable) else data = 4; /* disable hw oob interrupt */ + data |= 4; /* Active HIGH */ + status = sdioh_request_byte(sd, SDIOH_WRITE, 0, 0xf2, &data); return status; } @@ -1064,11 +1066,13 @@ sdioh_request_buffer(sdioh_info_t *sd, uint pio_dma, uint fix_inc, uint write, u return (Status); } +/* this function performs "abort" for both of host & device */ extern int sdioh_abort(sdioh_info_t *sd, uint func) { +#if defined(MMC_SDIO_ABORT) char t_func = (char) func; - +#endif /* defined(MMC_SDIO_ABORT) */ sd_trace(("%s: Enter\n", __FUNCTION__)); #if defined(MMC_SDIO_ABORT) diff --git a/bcm4329/src/bcmsdio/sys/bcmsdh_sdmmc_linux.c b/bcm4329/src/bcmsdio/sys/bcmsdh_sdmmc_linux.c index 196ad4f..8992a42 100644 --- a/bcm4329/src/bcmsdio/sys/bcmsdh_sdmmc_linux.c +++ b/bcm4329/src/bcmsdio/sys/bcmsdh_sdmmc_linux.c @@ -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: bcmsdh_sdmmc_linux.c,v 1.1.2.5.6.15 2010/04/14 21:11:46 Exp $ + * $Id: bcmsdh_sdmmc_linux.c,v 1.1.2.5.6.17 2010/08/13 00:36:19 Exp $ */ #include <typedefs.h> @@ -39,13 +39,22 @@ #if !defined(SDIO_VENDOR_ID_BROADCOM) #define SDIO_VENDOR_ID_BROADCOM 0x02d0 -#endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4325) */ +#endif /* !defined(SDIO_VENDOR_ID_BROADCOM) */ + +#define SDIO_DEVICE_ID_BROADCOM_DEFAULT 0x0000 + +#if !defined(SDIO_DEVICE_ID_BROADCOM_4325_SDGWB) +#define SDIO_DEVICE_ID_BROADCOM_4325_SDGWB 0x0492 /* BCM94325SDGWB */ +#endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4325_SDGWB) */ #if !defined(SDIO_DEVICE_ID_BROADCOM_4325) -#define SDIO_DEVICE_ID_BROADCOM_4325 0x0000 +#define SDIO_DEVICE_ID_BROADCOM_4325 0x0493 #endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4325) */ #if !defined(SDIO_DEVICE_ID_BROADCOM_4329) #define SDIO_DEVICE_ID_BROADCOM_4329 0x4329 #endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4329) */ +#if !defined(SDIO_DEVICE_ID_BROADCOM_4319) +#define SDIO_DEVICE_ID_BROADCOM_4319 0x4319 +#endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4329) */ #include <bcmsdh_sdmmc.h> @@ -116,15 +125,18 @@ static void bcmsdh_sdmmc_remove(struct sdio_func *func) sd_info(("Function#: 0x%04x\n", func->num)); if (func->num == 2) { - sd_trace(("F2 found, calling bcmsdh_probe...\n")); + sd_trace(("F2 found, calling bcmsdh_remove...\n")); bcmsdh_remove(&sdmmc_dev); } } /* devices we support, null terminated */ static const struct sdio_device_id bcmsdh_sdmmc_ids[] = { + { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_DEFAULT) }, + { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4325_SDGWB) }, { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4325) }, { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4329) }, + { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4319) }, { /* end: all zeroes */ }, }; diff --git a/bcm4329/src/bcmsdio/sys/bcmsdspi_linux.c b/bcm4329/src/bcmsdio/sys/bcmsdspi_linux.c index 1046a17..e2e0ca6 100644 --- a/bcm4329/src/bcmsdio/sys/bcmsdspi_linux.c +++ b/bcm4329/src/bcmsdio/sys/bcmsdspi_linux.c @@ -1,7 +1,7 @@ /* * Broadcom SPI Host Controller Driver - Linux Per-port * - * Copyright (C) 1999-2009, Broadcom Corporation + * Copyright (C) 1999-2010, Broadcom Corporation * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you diff --git a/bcm4329/src/bcmsdio/sys/bcmsdstd.c b/bcm4329/src/bcmsdio/sys/bcmsdstd.c index 0b1b575..0ca1f8f 100644 --- a/bcm4329/src/bcmsdio/sys/bcmsdstd.c +++ b/bcm4329/src/bcmsdio/sys/bcmsdstd.c @@ -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: bcmsdstd.c,v 1.64.4.1.4.4.2.17 2010/03/10 03:09:48 Exp $ + * $Id: bcmsdstd.c,v 1.64.4.1.4.4.2.18 2010/08/17 17:00:48 Exp $ */ #include <typedefs.h> @@ -119,7 +119,7 @@ extern void sdstd_wreg16(sdioh_info_t *sd, uint reg, uint16 data); void sdstd_wreg16(sdioh_info_t *sd, uint reg, uint16 data) { - *(volatile uint16 *)(sd->mem_space + reg) = (volatile uint16) data; + *(volatile uint16 *)(sd->mem_space + reg) = (uint16)data; sd_ctrl(("16: W Reg 0x%02x, Data 0x%x\n", reg, data)); } @@ -129,7 +129,7 @@ sdstd_or_reg16(sdioh_info_t *sd, uint reg, uint16 val) volatile uint16 data = *(volatile uint16 *)(sd->mem_space + reg); sd_ctrl(("16: OR Reg 0x%02x, Val 0x%x\n", reg, val)); data |= val; - *(volatile uint16 *)(sd->mem_space + reg) = (volatile uint16)data; + *(volatile uint16 *)(sd->mem_space + reg) = (uint16)data; } static void @@ -140,7 +140,7 @@ sdstd_mod_reg16(sdioh_info_t *sd, uint reg, int16 mask, uint16 val) sd_ctrl(("16: MOD Reg 0x%02x, Mask 0x%x, Val 0x%x\n", reg, mask, val)); data &= ~mask; data |= (val & mask); - *(volatile uint16 *)(sd->mem_space + reg) = (volatile uint16)data; + *(volatile uint16 *)(sd->mem_space + reg) = (uint16)data; } @@ -155,7 +155,7 @@ sdstd_rreg(sdioh_info_t *sd, uint reg) static inline void sdstd_wreg(sdioh_info_t *sd, uint reg, uint32 data) { - *(volatile uint32 *)(sd->mem_space + reg) = (volatile uint32)data; + *(volatile uint32 *)(sd->mem_space + reg) = (uint32)data; sd_ctrl(("32: W Reg 0x%02x, Data 0x%x\n", reg, data)); } @@ -164,7 +164,7 @@ sdstd_wreg(sdioh_info_t *sd, uint reg, uint32 data) static inline void sdstd_wreg8(sdioh_info_t *sd, uint reg, uint8 data) { - *(volatile uint8 *)(sd->mem_space + reg) = (volatile uint8)data; + *(volatile uint8 *)(sd->mem_space + reg) = (uint8)data; sd_ctrl(("08: W Reg 0x%02x, Data 0x%x\n", reg, data)); } static uint8 @@ -287,7 +287,7 @@ sdioh_detach(osl_t *osh, sdioh_info_t *sd) return SDIOH_API_RC_SUCCESS; } -/* Configure callback to client when we recieve client interrupt */ +/* Configure callback to client when we receive client interrupt */ extern SDIOH_API_RC sdioh_interrupt_register(sdioh_info_t *sd, sdioh_cb_fn_t fn, void *argh) { @@ -2778,10 +2778,6 @@ sdstd_card_buf(sdioh_info_t *sd, int rw, int func, bool fifo, uint32 addr, int n data++; } - /* Handle < 4 bytes. wlc_pio.c currently (as of 12/20/05) truncates buflen - * to be evenly divisable by 4. However dongle passes arbitrary lengths, - * so handle it here - */ bytes = blocksize % 4; /* If no leftover bytes, go to next block */ @@ -2898,7 +2894,8 @@ set_client_block_size(sdioh_info_t *sd, int func, int block_size) } /* Reset and re-initialize the device */ -int sdioh_sdio_reset(sdioh_info_t *si) +int +sdioh_sdio_reset(sdioh_info_t *si) { uint8 hreg; diff --git a/bcm4329/src/bcmsdio/sys/bcmsdstd_linux.c b/bcm4329/src/bcmsdio/sys/bcmsdstd_linux.c index 5746b08..a8b98e2 100644 --- a/bcm4329/src/bcmsdio/sys/bcmsdstd_linux.c +++ b/bcm4329/src/bcmsdio/sys/bcmsdstd_linux.c @@ -1,7 +1,7 @@ /* * 'Standard' SDIO HOST CONTROLLER driver - linux portion * - * Copyright (C) 1999-2009, Broadcom Corporation + * Copyright (C) 1999-2010, 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: bcmsdstd_linux.c,v 1.11.18.2 2008/05/28 18:36:56 Exp $ + * $Id: bcmsdstd_linux.c,v 1.11.18.2.16.1 2010/08/17 17:03:13 Exp $ */ #include <typedefs.h> @@ -186,7 +186,9 @@ sdstd_lock(sdioh_info_t *sd) spin_lock_irqsave(&sdos->lock, flags); if (sd->lockcount) { - sd_err(("%s: Already locked!\n", __FUNCTION__)); + sd_err(("%s: Already locked! called from %p\n", + __FUNCTION__, + __builtin_return_address(0))); ASSERT(sd->lockcount == 0); } sdstd_devintr_off(sd); diff --git a/bcm4329/src/dhd/exe/dhdu.c b/bcm4329/src/dhd/exe/dhdu.c index afd8b61..2d3ec73 100644 --- a/bcm4329/src/dhd/exe/dhdu.c +++ b/bcm4329/src/dhd/exe/dhdu.c @@ -828,7 +828,7 @@ dhd_sdreg(void *dhd, cmd_t *cmd, char **argv) UNUSED_PARAMETER(cmd); - memset(&sdreg, 0, sizeof(sdreg)); + bzero(&sdreg, sizeof(sdreg)); /* arg count */ for (argc = 0; argv[argc]; argc++); @@ -1880,7 +1880,7 @@ dhd_sd_reg(void *dhd, cmd_t *cmd, char **argv) uint argc; void *ptr = NULL; - memset(&sdreg, 0, sizeof(sdreg)); + bzero(&sdreg, sizeof(sdreg)); /* arg count */ for (argc = 0; argv[argc]; argc++); diff --git a/bcm4329/src/dhd/linux/Makefile b/bcm4329/src/dhd/linux/Makefile index 768dd51..4d5a957 100644 --- a/bcm4329/src/dhd/linux/Makefile +++ b/bcm4329/src/dhd/linux/Makefile @@ -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: Makefile,v 1.55.2.6.2.10.6.28 2010/03/26 21:49:31 Exp $ +# $Id: Makefile,v 1.55.2.6.2.10.6.42 2010/08/20 00:15:16 Exp $ # # Try a couple of places for LINUXDIR if not specified @@ -161,17 +161,23 @@ endif ifeq ($(CONFIG_NET_RADIO),y) CFILES += bcmwifi.c - ifeq ($(findstring -cfg-,-$(TARGET)-),) - CFILES += wl_iw.c - endif +CFILES += wl_iw.c else ifeq ($(CONFIG_WIRELESS_EXT),y) CFILES += bcmwifi.c - ifeq ($(findstring -cfg-,-$(TARGET)-),) - CFILES += wl_iw.c - endif + CFILES += wl_iw.c endif endif +ifeq ($(BCM_KVER), 2.6) +CFG80211_KVER:=$(shell echo $(LINUXVER) | cut -c5-6 | sed 's/3[1-9]/true/') +ifeq ($(CFG80211_KVER), true) + ifeq ($(CONFIG_CFG80211),m) + DFLAGS += -DCONFIG_CFG80211 + CFILES += wl_cfg80211.c + endif +endif +endif + OFILES=$(CFILES:.c=.o) @@ -221,10 +227,18 @@ ifneq ($(findstring -softap-,-$(TARGET)-),) DFLAGS += -DSOFTAP CFILES += sha1.c md5.c endif -ifneq ($(findstring -cfg-,-$(TARGET)-),) -DFLAGS += -DWL_CFG80211 -CFILES += wl_cfg80211.c -endif +ifneq ($(findstring -nexus-,-$(TARGET)-),) +DFLAGS += -DOEM_ANDROID -DEMBEDDED_PLATFORM -DARP_OFFLOAD_SUPPORT -DPKT_FILTER_SUPPORT +DFLAGS += -Dlinux +DFLAGS += -DBCMDBG +DFLAGS += -DDHD_USE_STATIC_BUF +DFLAGS += -DCUSTOMER_HW2 +DFLAGS += -DCUSTOM_OOB_GPIO_NUM=152 +DFLAGS += -DOOB_INTR_ONLY +DFLAGS += -DMMC_SDIO_ABORT +DFLAGS += -DSOFTAP +DFLAGS += -DPNO_SUPPORT +else ifneq ($(findstring -oob-,-$(TARGET)-),) DFLAGS += -DOOB_INTR_ONLY DFLAGS += -DHW_OOB @@ -234,6 +248,7 @@ ifneq ($(findstring -sdmmc-,-$(TARGET)-),) DFLAGS += -DSDIO_ISR_THREAD endif endif +endif ifneq ($(findstring -sdmmc-,-$(TARGET)-),) DFLAGS += -DBCMSDIO -DDHD_GPL -DBCMLXSDMMC -DBCMPLATFORM_BUS CFILES += dhd_sdio.c bcmsdh_sdmmc.c bcmsdh.c bcmsdh_linux.c bcmsdh_sdmmc_linux.c @@ -253,6 +268,13 @@ ifneq ($(findstring -intc1,$(shell echo $(LINUXVER))),) DFLAGS += -DSANDGATE2G endif +ifeq ($(OEM_ANDROID),1) +DFLAGS += -DOEM_ANDROID -DEMBEDDED_PLATFORM -DARP_OFFLOAD_SUPPORT -DPKT_FILTER_SUPPORT +endif +ifeq ($(OEM_CHROMIUMOS),1) +DFLAGS += -DOEM_CHROMIUMOS -DEMBEDDED_PLATFORM -DARP_OFFLOAD_SUPPORT -DPKT_FILTER_SUPPORT +endif + CFLAGS += -fshort-wchar $(DFLAGS) $(WFLAGS) $(IFLAGS) $(CUSTOM_FLAGS) @@ -284,11 +306,10 @@ endif TARGETS := \ dhd-cdc-usb dhd-cdc-sdstd \ - dhd-cdc-sdspi-pci dhd-cdc-sdmmc-gpl dhd-cdc-sdmmc-oob-gpl \ + dhd-cdc-sdspi-pci dhd-cdc-sdmmc-gpl dhd-cdc-sdmmc-oob-gpl dhd-cdc-sdmmc-nexus-gpl\ dhd-cdc-usb-apsta dhd-cdc-usb-gpl \ dhd-cdc-sdstd-apsta \ - dhd-cdc-sdmmc-softap-gpl \ - dhd-cdc-sdmmc-cfg-gpl + dhd-cdc-sdmmc-softap-gpl TARGETS += \ @@ -335,7 +356,7 @@ endif ifeq ($(BCM_KVER), 2.6) $(OBJCOPY) --strip-unneeded $(OBJDIR)/$(KMODULES) $(OBJDIR)/$(KMODULES).stripped else - $(OBJCOPY) --strip-unneeded $(OBJDIR)/$(MODULES) $(OBJDIR)/$(MODULES).stripped + $(OBJCOPY) --strip-unneeded $(OBJDIR)/dhd.o $(OBJDIR)/dhd.o.stripped endif dep: $(foreach file,$(CFILES),.$(file).depend) diff --git a/bcm4329/src/dhd/linux/makefile.26 b/bcm4329/src/dhd/linux/makefile.26 index eceee8e..da7319b 100644 --- a/bcm4329/src/dhd/linux/makefile.26 +++ b/bcm4329/src/dhd/linux/makefile.26 @@ -22,8 +22,13 @@ # 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: makefile.26,v 1.1.144.1 2008/05/07 22:53:44 Exp $ +# $Id: makefile.26,v 1.1.144.1.14.1 2010/04/16 20:27:14 Exp $ +ifneq ($(findstring -nexus-,-$(TARGET)-),) +obj-m += bcm4329.o +bcm4329-objs = $(DHDOFILES) +else obj-m += dhd.o dhd-objs = $(DHDOFILES) +endif EXTRA_CFLAGS = $(DHDCFLAGS) diff --git a/bcm4329/src/dhd/sys/dhd.h b/bcm4329/src/dhd/sys/dhd.h index 3b22eb7..cc7e8cb 100644 --- a/bcm4329/src/dhd/sys/dhd.h +++ b/bcm4329/src/dhd/sys/dhd.h @@ -24,7 +24,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.h,v 1.32.4.7.2.4.14.29 2010/02/23 06:58:21 Exp $ + * $Id: dhd.h,v 1.32.4.7.2.4.14.49 2010/08/20 17:32:48 Exp $ */ /**************** @@ -59,6 +59,11 @@ #include <wlioctl.h> +#ifdef DHD_DEBUG +#ifndef DHD_DEBUG_TRAP +#define DHD_DEBUG_TRAP +#endif +#endif /* Forward decls */ struct dhd_bus; @@ -145,33 +150,48 @@ typedef struct dhd_pub { /* Last error from dongle */ int dongle_error; + /* Suspend disable flag and "in suspend" flag */ + int suspend_disable_flag; /* "1" to disable all extra powersaving during suspend */ + int in_suspend; /* flag set to 1 when early suspend called */ +#ifdef PNO_SUPPORT + int pno_enable; /* pno status : "1" is pno enable */ +#endif /* PNO_SUPPORT */ + int dtim_skip; /* dtim skip , default 0 means wake each dtim */ + + /* Pkt filter defination */ + char * pktfilter[100]; + int pktfilter_count; + uint8 country_code[WLC_CNTRY_BUF_SZ]; + char eventmask[WL_EVENTING_MASK_LEN]; + } dhd_pub_t; - #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) #define DHD_PM_RESUME_WAIT_INIT(a) DECLARE_WAIT_QUEUE_HEAD(a); - #define _DHD_PM_RESUME_WAIT(a, b) do {\ + #define _DHD_PM_RESUME_WAIT(a, b) do { \ int retry = 0; \ + smp_mb(); \ while (dhd_mmc_suspend && retry++ != b) { \ wait_event_interruptible_timeout(a, FALSE, HZ/100); \ } \ } while (0) - #define DHD_PM_RESUME_WAIT(a) _DHD_PM_RESUME_WAIT(a, 30) + #define DHD_PM_RESUME_WAIT(a) _DHD_PM_RESUME_WAIT(a, 30) #define DHD_PM_RESUME_WAIT_FOREVER(a) _DHD_PM_RESUME_WAIT(a, ~0) #define DHD_PM_RESUME_RETURN_ERROR(a) do { if (dhd_mmc_suspend) return a; } while (0) #define DHD_PM_RESUME_RETURN do { if (dhd_mmc_suspend) return; } while (0) #define DHD_SPINWAIT_SLEEP_INIT(a) DECLARE_WAIT_QUEUE_HEAD(a); #define SPINWAIT_SLEEP(a, exp, us) do { \ - uint countdown = (us) + 9; \ - while ((exp) && (countdown >= 10)) { \ + uint countdown = (us) + 9999; \ + while ((exp) && (countdown >= 10000)) { \ wait_event_interruptible_timeout(a, FALSE, HZ/100); \ - countdown -= 10; \ + countdown -= 10000; \ } \ } while (0) - #else +#else #define DHD_PM_RESUME_WAIT_INIT(a) #define DHD_PM_RESUME_WAIT(a) @@ -188,7 +208,8 @@ typedef struct dhd_pub { } \ } while (0) - #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) */ +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) */ + #define DHD_IF_VIF 0x01 /* Virtual IF (Hidden from user) */ /* Wakelock Functions */ @@ -262,9 +283,13 @@ extern void dhd_os_sdlock_rxq(dhd_pub_t * pub); extern void dhd_os_sdunlock_rxq(dhd_pub_t * pub); extern void dhd_os_sdlock_sndup_rxq(dhd_pub_t * pub); extern void dhd_customer_gpio_wlan_ctrl(int onoff); +extern int dhd_custom_get_mac_address(unsigned char *buf); extern void dhd_os_sdunlock_sndup_rxq(dhd_pub_t * pub); extern void dhd_os_sdlock_eventq(dhd_pub_t * pub); extern void dhd_os_sdunlock_eventq(dhd_pub_t * pub); +#ifdef DHD_DEBUG +extern int write_to_file(dhd_pub_t *dhd, uint8 *buf, int size); +#endif /* DHD_DEBUG */ #if defined(OOB_INTR_ONLY) extern int dhd_customer_oob_irq_map(unsigned long *irq_flags_ptr); #endif /* defined(OOB_INTR_ONLY) */ @@ -311,6 +336,7 @@ extern int dhd_bus_devreset(dhd_pub_t *dhdp, uint8 flag); extern uint dhd_bus_status(dhd_pub_t *dhdp); extern int dhd_bus_start(dhd_pub_t *dhdp); +extern void print_buf(void *pbuf, int len, int bytes_per_line); typedef enum cust_gpio_modes { @@ -320,6 +346,7 @@ typedef enum cust_gpio_modes { WLAN_POWER_OFF } cust_gpio_modes_t; extern int wl_iw_iscan_set_scan_broadcast_prep(struct net_device *dev, uint flag); +extern int wl_iw_send_priv_event(struct net_device *dev, char *flag); /* * Insmod parameters for debug/test */ @@ -327,6 +354,10 @@ extern int wl_iw_iscan_set_scan_broadcast_prep(struct net_device *dev, uint flag /* Watchdog timer interval */ extern uint dhd_watchdog_ms; +#if defined(DHD_DEBUG) +/* Console output poll interval */ +extern uint dhd_console_ms; +#endif /* defined(DHD_DEBUG) */ /* Use interrupts */ extern uint dhd_intr; @@ -334,6 +365,27 @@ extern uint dhd_intr; /* Use polling */ extern uint dhd_poll; +/* ARP offload agent mode */ +extern uint dhd_arp_mode; + +/* ARP offload enable */ +extern uint dhd_arp_enable; + +/* Pkt filte enable control */ +extern uint dhd_pkt_filter_enable; + +/* Pkt filter init setup */ +extern uint dhd_pkt_filter_init; + +/* Pkt filter mode control */ +extern uint dhd_master_mode; + +/* Roaming mode control */ +extern uint dhd_roam; + +/* Roaming mode control */ +extern uint dhd_radio_up; + /* Initial idletime ticks (may be -1 for immediate idle, 0 for no idle) */ extern int dhd_idletime; #define DHD_IDLETIME_TICKS 1 @@ -364,9 +416,6 @@ extern char nv_path[MOD_PARAM_PATHLEN]; #define DHD_DEL_IF -0xe #define DHD_BAD_IF -0xf -#ifdef APSTA_PINGTEST -#define MAX_GUEST 8 -#endif extern void dhd_wait_for_event(dhd_pub_t *dhd, bool *lockvar); extern void dhd_wait_event_wakeup(dhd_pub_t*dhd); diff --git a/bcm4329/src/dhd/sys/dhd_bus.h b/bcm4329/src/dhd/sys/dhd_bus.h index 93392f9..9e29fb9 100644 --- a/bcm4329/src/dhd/sys/dhd_bus.h +++ b/bcm4329/src/dhd/sys/dhd_bus.h @@ -4,7 +4,7 @@ * Provides type definitions and function prototypes used to link the * DHD OS, bus, and protocol modules. * - * Copyright (C) 1999-2009, Broadcom Corporation + * Copyright (C) 1999-2010, Broadcom Corporation * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you @@ -24,7 +24,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_bus.h,v 1.4.6.3.2.3.6.5 2009/06/02 21:56:30 Exp $ + * $Id: dhd_bus.h,v 1.4.6.3.2.3.6.6 2010/05/17 18:18:13 Exp $ */ #ifndef _dhd_bus_h_ @@ -60,6 +60,10 @@ extern int dhd_bus_rxctl(struct dhd_bus *bus, uchar *msg, uint msglen); /* Watchdog timer function */ extern bool dhd_bus_watchdog(dhd_pub_t *dhd); +#ifdef DHD_DEBUG +/* Device console input function */ +extern int dhd_bus_console_in(dhd_pub_t *dhd, uchar *msg, uint msglen); +#endif /* Deferred processing for the bus, return TRUE requests reschedule */ extern bool dhd_bus_dpc(struct dhd_bus *bus); diff --git a/bcm4329/src/dhd/sys/dhd_cdc.c b/bcm4329/src/dhd/sys/dhd_cdc.c index 43b21c3..61f6a6f 100644 --- a/bcm4329/src/dhd/sys/dhd_cdc.c +++ b/bcm4329/src/dhd/sys/dhd_cdc.c @@ -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_cdc.c,v 1.22.4.2.4.7.2.34 2010/01/21 22:08:34 Exp $ + * $Id: dhd_cdc.c,v 1.22.4.2.4.7.2.41 2010/06/23 19:58:18 Exp $ * * BDC is like CDC, except it includes a header for data packets to convey * packet priority over the bus, and flags (e.g. to indicate checksum status @@ -41,6 +41,7 @@ #include <dhd_bus.h> #include <dhd_dbg.h> +extern int dhd_preinit_ioctls(dhd_pub_t *dhd); /* Packet alignment for most efficient SDIO (can change based on platform) */ #ifndef DHD_SDALIGN @@ -193,7 +194,7 @@ done: return ret; } -static int +int dhdcdc_set_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len) { dhd_prot_t *prot = dhd->prot; @@ -210,7 +211,7 @@ dhdcdc_set_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len) msg->len = htol32(len); msg->flags = (++prot->reqid << CDCF_IOC_ID_SHIFT) | CDCF_IOC_SET; CDC_SET_IF_IDX(msg, ifidx); - msg->flags |= htol32(msg->flags); + msg->flags = htol32(msg->flags); if (buf) memcpy(prot->buf, buf, len); @@ -322,23 +323,12 @@ dhd_prot_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf) bcm_bprintf(strbuf, "Protocol CDC: reqid %d\n", dhdp->prot->reqid); } -#ifdef APSTA_PINGTEST -extern struct ether_addr guest_eas[MAX_GUEST]; -#endif void dhd_prot_hdrpush(dhd_pub_t *dhd, int ifidx, void *pktbuf) { #ifdef BDC struct bdc_header *h; -#ifdef APSTA_PINGTEST - struct ether_header *eh; - int i; -#ifdef DHD_DEBUG - char eabuf1[ETHER_ADDR_STR_LEN]; - char eabuf2[ETHER_ADDR_STR_LEN]; -#endif /* DHD_DEBUG */ -#endif /* APSTA_PINGTEST */ #endif /* BDC */ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); @@ -346,9 +336,6 @@ dhd_prot_hdrpush(dhd_pub_t *dhd, int ifidx, void *pktbuf) #ifdef BDC /* Push BDC header used to convey priority for buses that don't */ -#ifdef APSTA_PINGTEST - eh = (struct ether_header *)PKTDATA(dhd->osh, pktbuf); -#endif PKTPUSH(dhd->osh, pktbuf, BDC_HEADER_LEN); @@ -361,19 +348,6 @@ dhd_prot_hdrpush(dhd_pub_t *dhd, int ifidx, void *pktbuf) h->priority = (PKTPRIO(pktbuf) & BDC_PRIORITY_MASK); h->flags2 = 0; -#ifdef APSTA_PINGTEST - for (i = 0; i < MAX_GUEST; ++i) { - if (!ETHER_ISNULLADDR(eh->ether_dhost) && - bcmp(eh->ether_dhost, guest_eas[i].octet, ETHER_ADDR_LEN) == 0) { - DHD_TRACE(("send on if 1; sa %s, da %s\n", - bcm_ether_ntoa((struct ether_addr *)(eh->ether_shost), eabuf1), - bcm_ether_ntoa((struct ether_addr *)(eh->ether_dhost), eabuf2))); - /* assume all guest STAs are on interface 1 */ - h->flags2 = 1; - break; - } - } -#endif /* APSTA_PINGTEST */ h->rssi = 0; #endif /* BDC */ BDC_SET_IF_IDX(h, ifidx); @@ -510,266 +484,30 @@ dhd_prot_dstats(dhd_pub_t *dhd) return; } -int dhd_set_suspend(int value, dhd_pub_t *dhd) -{ - int power_mode = PM_MAX; - wl_pkt_filter_enable_t enable_parm; - char iovbuf[32]; - int bcn_li_dtim = 3; -#ifdef CUSTOMER_HW2 - uint roamvar = 1; -#endif /* CUSTOMER_HW2 */ - -#define htod32(i) i - - if (dhd && dhd->up) { - dhd_os_proto_block(dhd); - if (value) { - dhdcdc_set_ioctl(dhd, 0, WLC_SET_PM, - (char *)&power_mode, sizeof(power_mode)); - /* Enable packet filter, only allow unicast packet to send up */ - enable_parm.id = htod32(100); - enable_parm.enable = htod32(1); - bcm_mkiovar("pkt_filter_enable", (char *)&enable_parm, - sizeof(wl_pkt_filter_enable_t), iovbuf, sizeof(iovbuf)); - dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf)); - /* set bcn_li_dtim */ - bcm_mkiovar("bcn_li_dtim", (char *)&bcn_li_dtim, - 4, iovbuf, sizeof(iovbuf)); - dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf)); -#ifdef CUSTOMER_HW2 - /* Disable build-in roaming to allowed ext supplicant to take of romaing */ - bcm_mkiovar("roam_off", (char *)&roamvar, 4, iovbuf, sizeof(iovbuf)); - dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf)); -#endif /* CUSTOMER_HW2 */ - } else { - power_mode = PM_FAST; - dhdcdc_set_ioctl(dhd, 0, WLC_SET_PM, (char *)&power_mode, - sizeof(power_mode)); - /* disable pkt filter */ - enable_parm.id = htod32(100); - enable_parm.enable = htod32(0); - bcm_mkiovar("pkt_filter_enable", (char *)&enable_parm, - sizeof(wl_pkt_filter_enable_t), iovbuf, sizeof(iovbuf)); - dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf)); - /* set bcn_li_dtim */ - bcn_li_dtim = 0; - bcm_mkiovar("bcn_li_dtim", (char *)&bcn_li_dtim, - 4, iovbuf, sizeof(iovbuf)); - dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf)); -#ifdef CUSTOMER_HW2 - roamvar = 0; - bcm_mkiovar("roam_off", (char *)&roamvar, 4, iovbuf, sizeof(iovbuf)); - dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf)); -#endif /* CUSTOMER_HW2 */ - } - dhd_os_proto_unblock(dhd); - } - - return 0; -} - -#define strtoul(nptr, endptr, base) bcm_strtoul((nptr), (endptr), (base)) - -/* Convert user's input in hex pattern to byte-size mask */ -static int -wl_pattern_atoh(char *src, char *dst) -{ - int i; - if (strncmp(src, "0x", 2) != 0 && - strncmp(src, "0X", 2) != 0) { - printf("Mask invalid format. Needs to start with 0x\n"); - return -1; - } - src = src + 2; /* Skip past 0x */ - if (strlen(src) % 2 != 0) { - printf("Mask invalid format. Needs to be of even length\n"); - return -1; - } - for (i = 0; *src != '\0'; i++) { - char num[3]; - strncpy(num, src, 2); - num[2] = '\0'; - dst[i] = (uint8)strtoul(num, NULL, 16); - src += 2; - } - return i; -} - int -dhd_preinit_ioctls(dhd_pub_t *dhd) +dhd_prot_init(dhd_pub_t *dhd) { - char eventmask[WL_EVENTING_MASK_LEN]; - char iovbuf[WL_EVENTING_MASK_LEN + 12]; /* Room for "event_msgs" + '\0' + bitvec */ - int ret; - uint up = 0; -#ifdef CUSTOMER_HW2 - uint roamvar = 0; -#else - uint roamvar = 1; -#endif - uint power_mode = PM_FAST; - uint32 dongle_align = DHD_SDALIGN; - uint32 glom = 0; - - uint bcn_timeout = 3; - int arpoe = 1; - int arp_ol = 0xf; - int scan_assoc_time = 40; - int scan_unassoc_time = 80; - const char *str; - wl_pkt_filter_t pkt_filter; - wl_pkt_filter_t *pkt_filterp; - int buf_len; - int str_len; - uint32 mask_size; - uint32 pattern_size; - char buf[256]; - uint filter_mode = 1; - - dhd_os_proto_block(dhd); - /* Get the device MAC address */ - strcpy(iovbuf, "cur_etheraddr"); - if ((ret = dhdcdc_query_ioctl(dhd, 0, WLC_GET_VAR, iovbuf, sizeof(iovbuf))) < 0) { - DHD_ERROR(("%s: can't get MAC address , error=%d\n", __FUNCTION__, ret)); - dhd_os_proto_unblock(dhd); - return BCME_NOTUP; - } - memcpy(dhd->mac.octet, iovbuf, ETHER_ADDR_LEN); - - /* Set Country code */ - if (dhd->country_code[0] != 0) { - if (dhdcdc_set_ioctl(dhd, 0, WLC_SET_COUNTRY, - dhd->country_code, sizeof(dhd->country_code)) < 0) { - DHD_ERROR(("%s: country code setting failed\n", __FUNCTION__)); - } - } - - /* Set PowerSave mode */ - dhdcdc_set_ioctl(dhd, 0, WLC_SET_PM, (char *)&power_mode, sizeof(power_mode)); + int ret = 0; + char buf[128]; - /* Match Host and Dongle rx alignment */ - bcm_mkiovar("bus:txglomalign", (char *)&dongle_align, 4, iovbuf, sizeof(iovbuf)); - dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf)); + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); - /* disable glom option per default */ - bcm_mkiovar("bus:txglom", (char *)&glom, 4, iovbuf, sizeof(iovbuf)); - dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf)); - /* Setup timeout if Beacons are lost and roam is off to report link down */ - if (roamvar) { - bcm_mkiovar("bcn_timeout", (char *)&bcn_timeout, 4, iovbuf, sizeof(iovbuf)); - dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf)); - } + dhd_os_proto_block(dhd); - /* Enable/Disable build-in roaming to allowed ext supplicant to take of romaing */ - bcm_mkiovar("roam_off", (char *)&roamvar, 4, iovbuf, sizeof(iovbuf)); - dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf)); - - /* Force STA UP */ - dhdcdc_set_ioctl(dhd, 0, WLC_UP, (char *)&up, sizeof(up)); - - /* Setup event_msgs */ - bcm_mkiovar("event_msgs", eventmask, WL_EVENTING_MASK_LEN, iovbuf, sizeof(iovbuf)); - dhdcdc_query_ioctl(dhd, 0, WLC_GET_VAR, iovbuf, sizeof(iovbuf)); - bcopy(iovbuf, eventmask, WL_EVENTING_MASK_LEN); - - setbit(eventmask, WLC_E_SET_SSID); - setbit(eventmask, WLC_E_PRUNE); - setbit(eventmask, WLC_E_AUTH); - setbit(eventmask, WLC_E_REASSOC); - setbit(eventmask, WLC_E_REASSOC_IND); - setbit(eventmask, WLC_E_DEAUTH_IND); - setbit(eventmask, WLC_E_DISASSOC_IND); - setbit(eventmask, WLC_E_DISASSOC); - setbit(eventmask, WLC_E_JOIN); - setbit(eventmask, WLC_E_ASSOC_IND); - setbit(eventmask, WLC_E_PSK_SUP); - setbit(eventmask, WLC_E_LINK); - setbit(eventmask, WLC_E_NDIS_LINK); - setbit(eventmask, WLC_E_MIC_ERROR); - setbit(eventmask, WLC_E_PMKID_CACHE); - setbit(eventmask, WLC_E_TXFAIL); - setbit(eventmask, WLC_E_JOIN_START); - setbit(eventmask, WLC_E_SCAN_COMPLETE); - - bcm_mkiovar("event_msgs", eventmask, WL_EVENTING_MASK_LEN, iovbuf, sizeof(iovbuf)); - dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf)); - - dhdcdc_set_ioctl(dhd, 0, WLC_SET_SCAN_CHANNEL_TIME, (char *)&scan_assoc_time, - sizeof(scan_assoc_time)); - dhdcdc_set_ioctl(dhd, 0, WLC_SET_SCAN_UNASSOC_TIME, (char *)&scan_unassoc_time, - sizeof(scan_unassoc_time)); - - /* Set ARP offload */ - bcm_mkiovar("arpoe", (char *)&arpoe, 4, iovbuf, sizeof(iovbuf)); - dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf)); - bcm_mkiovar("arp_ol", (char *)&arp_ol, 4, iovbuf, sizeof(iovbuf)); - dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf)); - - /* add a default packet filter pattern */ - str = "pkt_filter_add"; - str_len = strlen(str); - strncpy(buf, str, str_len); - buf[ str_len ] = '\0'; - buf_len = str_len + 1; - - pkt_filterp = (wl_pkt_filter_t *) (buf + str_len + 1); - - /* Parse packet filter id. */ - pkt_filter.id = htod32(100); - - /* Parse filter polarity. */ - pkt_filter.negate_match = htod32(0); - - /* Parse filter type. */ - pkt_filter.type = htod32(0); - - /* Parse pattern filter offset. */ - pkt_filter.u.pattern.offset = htod32(0); - - /* Parse pattern filter mask. */ - mask_size = htod32(wl_pattern_atoh("0xff", - (char *) pkt_filterp->u.pattern.mask_and_pattern)); - - /* Parse pattern filter pattern. */ - pattern_size = htod32(wl_pattern_atoh("0x00", - (char *) &pkt_filterp->u.pattern.mask_and_pattern[mask_size])); - - if (mask_size != pattern_size) { - DHD_ERROR(("Mask and pattern not the same size\n")); + /* Get the device MAC address */ + strcpy(buf, "cur_etheraddr"); + ret = dhdcdc_query_ioctl(dhd, 0, WLC_GET_VAR, buf, sizeof(buf)); + if (ret < 0) { dhd_os_proto_unblock(dhd); - return -EINVAL; + return ret; } - - pkt_filter.u.pattern.size_bytes = mask_size; - buf_len += WL_PKT_FILTER_FIXED_LEN; - buf_len += (WL_PKT_FILTER_PATTERN_FIXED_LEN + 2 * mask_size); - - /* Keep-alive attributes are set in local variable (keep_alive_pkt), and - ** then memcpy'ed into buffer (keep_alive_pktp) since there is no - ** guarantee that the buffer is properly aligned. - */ - memcpy((char *)pkt_filterp, &pkt_filter, - WL_PKT_FILTER_FIXED_LEN + WL_PKT_FILTER_PATTERN_FIXED_LEN); - - dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, buf, buf_len); - - /* set mode to allow pattern */ - bcm_mkiovar("pkt_filter_mode", (char *)&filter_mode, 4, iovbuf, sizeof(iovbuf)); - dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf)); + memcpy(dhd->mac.octet, buf, ETHER_ADDR_LEN); dhd_os_proto_unblock(dhd); - return 0; -} - -int -dhd_prot_init(dhd_pub_t *dhd) -{ - int ret = 0; - DHD_TRACE(("%s: Enter\n", __FUNCTION__)); - +#ifdef EMBEDDED_PLATFORM ret = dhd_preinit_ioctls(dhd); +#endif /* EMBEDDED_PLATFORM */ /* Always assumes wl for now */ dhd->iswl = TRUE; diff --git a/bcm4329/src/dhd/sys/dhd_common.c b/bcm4329/src/dhd/sys/dhd_common.c index a0e6a73..bea33b6 100644 --- a/bcm4329/src/dhd/sys/dhd_common.c +++ b/bcm4329/src/dhd/sys/dhd_common.c @@ -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_common.c,v 1.5.6.8.2.6.6.41 2010/02/24 01:52:41 Exp $ + * $Id: dhd_common.c,v 1.5.6.8.2.6.6.69.4.3 2010/09/10 21:30:16 Exp $ */ #include <typedefs.h> #include <osl.h> @@ -37,9 +37,21 @@ #include <dhd_dbg.h> #include <msgtrace.h> +#include <wlioctl.h> + +#ifdef SET_RANDOM_MAC_SOFTAP +#include <linux/random.h> +#include <linux/jiffies.h> +#endif + +#ifdef GET_CUSTOM_MAC_ENABLE +int wifi_get_mac_addr(unsigned char *buf); +#endif /* GET_CUSTOM_MAC_ENABLE */ int dhd_msg_level; +#include <wl_iw.h> + char fw_path[MOD_PARAM_PATHLEN]; char nv_path[MOD_PARAM_PATHLEN]; @@ -48,6 +60,25 @@ uint32 dhd_conn_event; uint32 dhd_conn_status; uint32 dhd_conn_reason; +#define htod32(i) i +#define htod16(i) i +#define dtoh32(i) i +#define dtoh16(i) i + +extern int dhdcdc_set_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len); +extern void dhd_ind_scan_confirm(void *h, bool status); +extern int dhd_wl_ioctl(dhd_pub_t *dhd, uint cmd, char *buf, uint buflen); +void dhd_iscan_lock(void); +void dhd_iscan_unlock(void); + +/* Packet alignment for most efficient SDIO (can change based on platform) */ +#ifndef DHD_SDALIGN +#define DHD_SDALIGN 32 +#endif +#if !ISPOWEROF2(DHD_SDALIGN) +#error DHD_SDALIGN is not a power of 2! +#endif + #ifdef DHD_DEBUG const char dhd_version[] = "Dongle Host Driver, version " EPI_VERSION_STR "\nCompiled on " __DATE__ " at " __TIME__; @@ -57,7 +88,6 @@ const char dhd_version[] = "Dongle Host Driver, version " EPI_VERSION_STR; void dhd_set_timer(void *bus, uint wdtick); - /* IOVar table */ enum { IOV_VERSION = 1, @@ -66,6 +96,10 @@ enum { IOV_BCMERROR, IOV_WDTICK, IOV_DUMP, +#ifdef DHD_DEBUG + IOV_CONS, + IOV_DCONSOLE_POLL, +#endif IOV_CLEARCOUNTS, IOV_LOGDUMP, IOV_LOGCAL, @@ -84,6 +118,10 @@ const bcm_iovar_t dhd_iovars[] = { {"bcmerror", IOV_BCMERROR, 0, IOVT_INT8, 0 }, {"wdtick", IOV_WDTICK, 0, IOVT_UINT32, 0 }, {"dump", IOV_DUMP, 0, IOVT_BUFFER, DHD_IOCTL_MAXLEN }, +#ifdef DHD_DEBUG + {"dconpoll", IOV_DCONSOLE_POLL, 0, IOVT_UINT32, 0 }, + {"cons", IOV_CONS, 0, IOVT_BUFFER, 0 }, +#endif {"clearcounts", IOV_CLEARCOUNTS, 0, IOVT_VOID, 0 }, {"gpioob", IOV_GPIOOB, 0, IOVT_UINT32, 0 }, {"ioctl_timeout", IOV_IOCTLTIMEOUT, 0, IOVT_UINT32, 0 }, @@ -223,6 +261,21 @@ dhd_doiovar(dhd_pub_t *dhd_pub, const bcm_iovar_t *vi, uint32 actionid, const ch bcmerror = dhd_dump(dhd_pub, arg, len); break; +#ifdef DHD_DEBUG + case IOV_GVAL(IOV_DCONSOLE_POLL): + int_val = (int32)dhd_console_ms; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_DCONSOLE_POLL): + dhd_console_ms = (uint)int_val; + break; + + case IOV_SVAL(IOV_CONS): + if (len > 0) + bcmerror = dhd_bus_console_in(dhd_pub, arg, len - 1); + break; +#endif case IOV_SVAL(IOV_CLEARCOUNTS): dhd_pub->tx_packets = dhd_pub->rx_packets = 0; @@ -459,9 +512,6 @@ dhd_ioctl(dhd_pub_t *dhd_pub, dhd_ioctl_t *ioc, void *buf, uint buflen) return bcmerror; } -#ifdef APSTA_PINGTEST -struct ether_addr guest_eas[MAX_GUEST]; -#endif #ifdef SHOW_EVENTS static void @@ -570,16 +620,6 @@ wl_show_host_event(wl_event_msg_t *event, void *event_data) case WLC_E_ASSOC_IND: case WLC_E_REASSOC_IND: -#ifdef APSTA_PINGTEST - { - int i; - for (i = 0; i < MAX_GUEST; ++i) - if (ETHER_ISNULLADDR(&guest_eas[i])) - break; - if (i < MAX_GUEST) - bcopy(event->addr.octet, guest_eas[i].octet, ETHER_ADDR_LEN); - } -#endif /* APSTA_PINGTEST */ DHD_EVENT(("MACEVENT: %s, MAC %s\n", event_name, eabuf)); break; @@ -600,18 +640,6 @@ wl_show_host_event(wl_event_msg_t *event, void *event_data) case WLC_E_DEAUTH_IND: case WLC_E_DISASSOC_IND: -#ifdef APSTA_PINGTEST - { - int i; - for (i = 0; i < MAX_GUEST; ++i) { - if (bcmp(guest_eas[i].octet, event->addr.octet, - ETHER_ADDR_LEN) == 0) { - bzero(guest_eas[i].octet, ETHER_ADDR_LEN); - break; - } - } - } -#endif /* APSTA_PINGTEST */ DHD_EVENT(("MACEVENT: %s, MAC %s, reason %d\n", event_name, eabuf, (int)reason)); break; @@ -892,3 +920,1264 @@ wl_event_to_host_order(wl_event_msg_t *evt) evt->datalen = ntoh32(evt->datalen); evt->version = ntoh16(evt->version); } + +void print_buf(void *pbuf, int len, int bytes_per_line) +{ + int i, j = 0; + unsigned char *buf = pbuf; + + if (bytes_per_line == 0) { + bytes_per_line = len; + } + + for (i = 0; i < len; i++) { + printf("%2.2x", *buf++); + j++; + if (j == bytes_per_line) { + printf("\n"); + j = 0; + } else { + printf(":"); + } + } + printf("\n"); +} + +#define strtoul(nptr, endptr, base) bcm_strtoul((nptr), (endptr), (base)) + +#ifdef PKT_FILTER_SUPPORT +/* Convert user's input in hex pattern to byte-size mask */ +static int +wl_pattern_atoh(char *src, char *dst) +{ + int i; + if (strncmp(src, "0x", 2) != 0 && + strncmp(src, "0X", 2) != 0) { + DHD_ERROR(("Mask invalid format. Needs to start with 0x\n")); + return -1; + } + src = src + 2; /* Skip past 0x */ + if (strlen(src) % 2 != 0) { + DHD_ERROR(("Mask invalid format. Needs to be of even length\n")); + return -1; + } + for (i = 0; *src != '\0'; i++) { + char num[3]; + strncpy(num, src, 2); + num[2] = '\0'; + dst[i] = (uint8)strtoul(num, NULL, 16); + src += 2; + } + return i; +} + +void +dhd_pktfilter_offload_enable(dhd_pub_t * dhd, char *arg, int enable, int master_mode) +{ + char *argv[8]; + int i = 0; + const char *str; + int buf_len; + int str_len; + char *arg_save = 0, *arg_org = 0; + int rc; + char buf[128]; + wl_pkt_filter_enable_t enable_parm; + wl_pkt_filter_enable_t * pkt_filterp; + + if (!(arg_save = MALLOC(dhd->osh, strlen(arg) + 1))) { + DHD_ERROR(("%s: kmalloc failed\n", __FUNCTION__)); + goto fail; + } + arg_org = arg_save; + memcpy(arg_save, arg, strlen(arg) + 1); + + argv[i] = bcmstrtok(&arg_save, " ", 0); + + i = 0; + if (NULL == argv[i]) { + DHD_ERROR(("No args provided\n")); + goto fail; + } + + str = "pkt_filter_enable"; + str_len = strlen(str); + strncpy(buf, str, str_len); + buf[str_len] = '\0'; + buf_len = str_len + 1; + + pkt_filterp = (wl_pkt_filter_enable_t *)(buf + str_len + 1); + + /* Parse packet filter id. */ + enable_parm.id = htod32(strtoul(argv[i], NULL, 0)); + + /* Parse enable/disable value. */ + enable_parm.enable = htod32(enable); + + buf_len += sizeof(enable_parm); + memcpy((char *)pkt_filterp, + &enable_parm, + sizeof(enable_parm)); + + /* Enable/disable the specified filter. */ + rc = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, buf, buf_len); + rc = rc >= 0 ? 0 : rc; + if (rc) + DHD_TRACE(("%s: failed to add pktfilter %s, retcode = %d\n", + __FUNCTION__, arg, rc)); + else + DHD_TRACE(("%s: successfully added pktfilter %s\n", + __FUNCTION__, arg)); + + /* Contorl the master mode */ + bcm_mkiovar("pkt_filter_mode", (char *)&master_mode, 4, buf, sizeof(buf)); + rc = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, buf, sizeof(buf)); + rc = rc >= 0 ? 0 : rc; + if (rc) + DHD_TRACE(("%s: failed to add pktfilter %s, retcode = %d\n", + __FUNCTION__, arg, rc)); + +fail: + if (arg_org) + MFREE(dhd->osh, arg_org, strlen(arg) + 1); +} + +void +dhd_pktfilter_offload_set(dhd_pub_t * dhd, char *arg) +{ + const char *str; + wl_pkt_filter_t pkt_filter; + wl_pkt_filter_t *pkt_filterp; + int buf_len; + int str_len; + int rc; + uint32 mask_size; + uint32 pattern_size; + char *argv[8], * buf = 0; + int i = 0; + char *arg_save = 0, *arg_org = 0; +#define BUF_SIZE 2048 + + if (!(arg_save = MALLOC(dhd->osh, strlen(arg) + 1))) { + DHD_ERROR(("%s: kmalloc failed\n", __FUNCTION__)); + goto fail; + } + + arg_org = arg_save; + + if (!(buf = MALLOC(dhd->osh, BUF_SIZE))) { + DHD_ERROR(("%s: kmalloc failed\n", __FUNCTION__)); + goto fail; + } + + memcpy(arg_save, arg, strlen(arg) + 1); + + if (strlen(arg) > BUF_SIZE) { + DHD_ERROR(("Not enough buffer %d < %d\n", (int)strlen(arg), (int)sizeof(buf))); + goto fail; + } + + argv[i] = bcmstrtok(&arg_save, " ", 0); + while (argv[i++]) + argv[i] = bcmstrtok(&arg_save, " ", 0); + + i = 0; + if (NULL == argv[i]) { + DHD_ERROR(("No args provided\n")); + goto fail; + } + + str = "pkt_filter_add"; + str_len = strlen(str); + strncpy(buf, str, str_len); + buf[ str_len ] = '\0'; + buf_len = str_len + 1; + + pkt_filterp = (wl_pkt_filter_t *) (buf + str_len + 1); + + /* Parse packet filter id. */ + pkt_filter.id = htod32(strtoul(argv[i], NULL, 0)); + + if (NULL == argv[++i]) { + DHD_ERROR(("Polarity not provided\n")); + goto fail; + } + + /* Parse filter polarity. */ + pkt_filter.negate_match = htod32(strtoul(argv[i], NULL, 0)); + + if (NULL == argv[++i]) { + DHD_ERROR(("Filter type not provided\n")); + goto fail; + } + + /* Parse filter type. */ + pkt_filter.type = htod32(strtoul(argv[i], NULL, 0)); + + if (NULL == argv[++i]) { + DHD_ERROR(("Offset not provided\n")); + goto fail; + } + + /* Parse pattern filter offset. */ + pkt_filter.u.pattern.offset = htod32(strtoul(argv[i], NULL, 0)); + + if (NULL == argv[++i]) { + DHD_ERROR(("Bitmask not provided\n")); + goto fail; + } + + /* Parse pattern filter mask. */ + mask_size = + htod32(wl_pattern_atoh(argv[i], (char *) pkt_filterp->u.pattern.mask_and_pattern)); + + if (NULL == argv[++i]) { + DHD_ERROR(("Pattern not provided\n")); + goto fail; + } + + /* Parse pattern filter pattern. */ + pattern_size = + htod32(wl_pattern_atoh(argv[i], + (char *) &pkt_filterp->u.pattern.mask_and_pattern[mask_size])); + + if (mask_size != pattern_size) { + DHD_ERROR(("Mask and pattern not the same size\n")); + goto fail; + } + + pkt_filter.u.pattern.size_bytes = mask_size; + buf_len += WL_PKT_FILTER_FIXED_LEN; + buf_len += (WL_PKT_FILTER_PATTERN_FIXED_LEN + 2 * mask_size); + + /* Keep-alive attributes are set in local variable (keep_alive_pkt), and + ** then memcpy'ed into buffer (keep_alive_pktp) since there is no + ** guarantee that the buffer is properly aligned. + */ + memcpy((char *)pkt_filterp, + &pkt_filter, + WL_PKT_FILTER_FIXED_LEN + WL_PKT_FILTER_PATTERN_FIXED_LEN); + + rc = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, buf, buf_len); + rc = rc >= 0 ? 0 : rc; + + if (rc) + DHD_TRACE(("%s: failed to add pktfilter %s, retcode = %d\n", + __FUNCTION__, arg, rc)); + else + DHD_TRACE(("%s: successfully added pktfilter %s\n", + __FUNCTION__, arg)); + +fail: + if (arg_org) + MFREE(dhd->osh, arg_org, strlen(arg) + 1); + + if (buf) + MFREE(dhd->osh, buf, BUF_SIZE); +} +#endif + +#ifdef ARP_OFFLOAD_SUPPORT +void +dhd_arp_offload_set(dhd_pub_t * dhd, int arp_mode) +{ + char iovbuf[32]; + int retcode; + + bcm_mkiovar("arp_ol", (char *)&arp_mode, 4, iovbuf, sizeof(iovbuf)); + retcode = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf)); + retcode = retcode >= 0 ? 0 : retcode; + if (retcode) + DHD_TRACE(("%s: failed to set ARP offload mode to 0x%x, retcode = %d\n", + __FUNCTION__, arp_mode, retcode)); + else + DHD_TRACE(("%s: successfully set ARP offload mode to 0x%x\n", + __FUNCTION__, arp_mode)); +} + +void +dhd_arp_offload_enable(dhd_pub_t * dhd, int arp_enable) +{ + char iovbuf[32]; + int retcode; + + bcm_mkiovar("arpoe", (char *)&arp_enable, 4, iovbuf, sizeof(iovbuf)); + retcode = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf)); + retcode = retcode >= 0 ? 0 : retcode; + if (retcode) + DHD_TRACE(("%s: failed to enabe ARP offload to %d, retcode = %d\n", + __FUNCTION__, arp_enable, retcode)); + else + DHD_TRACE(("%s: successfully enabed ARP offload to %d\n", + __FUNCTION__, arp_enable)); +} +#endif + +int +dhd_preinit_ioctls(dhd_pub_t *dhd) +{ + char iovbuf[WL_EVENTING_MASK_LEN + 12]; /* Room for "event_msgs" + '\0' + bitvec */ + uint up = 0; + char buf[128], *ptr; + uint power_mode = PM_FAST; + uint32 dongle_align = DHD_SDALIGN; + uint32 glom = 0; + uint bcn_timeout = 3; + int scan_assoc_time = 40; + int scan_unassoc_time = 40; +#ifdef GET_CUSTOM_MAC_ENABLE + int ret; + struct ether_addr ea_addr; +#endif /* GET_CUSTOM_MAC_ENABLE */ + + dhd_os_proto_block(dhd); + +#ifdef GET_CUSTOM_MAC_ENABLE + /* + ** Read MAC address from external customer place + ** NOTE that default mac address has to be present in otp or nvram file + ** to bring up firmware but unique per board mac address maybe provided + ** by customer code + */ + ret = dhd_custom_get_mac_address(ea_addr.octet); + if (!ret) { + bcm_mkiovar("cur_etheraddr", (void *)&ea_addr, ETHER_ADDR_LEN, buf, sizeof(buf)); + ret = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, buf, sizeof(buf)); + if (ret < 0) { + DHD_ERROR(("%s: can't set MAC address , error=%d\n", __FUNCTION__, ret)); + } else + memcpy(dhd->mac.octet, (void *)&ea_addr, ETHER_ADDR_LEN); + } +#endif /* GET_CUSTOM_MAC_ENABLE */ + +#ifdef SET_RANDOM_MAC_SOFTAP + if (strstr(fw_path, "apsta") != NULL) { + uint rand_mac; + int ret; + + srandom32((uint)jiffies); + rand_mac = random32(); + iovbuf[0] = 0x02; /* locally administered bit */ + iovbuf[1] = 0x1A; + iovbuf[2] = 0x11; + iovbuf[3] = (unsigned char)(rand_mac & 0x0F) | 0xF0; + iovbuf[4] = (unsigned char)(rand_mac >> 8); + iovbuf[5] = (unsigned char)(rand_mac >> 16); + + printk("Broadcom Dongle Host Driver mac=%02x:%02x:%02x:%02x:%02x:%02x\n", + iovbuf[0], iovbuf[1], iovbuf[2], iovbuf[3], iovbuf[4], iovbuf[5]); + + bcm_mkiovar("cur_etheraddr", (void *)iovbuf, ETHER_ADDR_LEN, buf, sizeof(buf)); + ret = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, buf, sizeof(buf)); + if (ret < 0) { + DHD_ERROR(("%s: can't set MAC address , error=%d\n", __FUNCTION__, ret)); + } else + memcpy(dhd->mac.octet, iovbuf, ETHER_ADDR_LEN); + } +#endif /* SET_RANDOM_MAC_SOFTAP */ + + /* Set Country code */ + if (dhd->country_code[0] != 0) { + if (dhdcdc_set_ioctl(dhd, 0, WLC_SET_COUNTRY, + dhd->country_code, sizeof(dhd->country_code)) < 0) { + DHD_ERROR(("%s: country code setting failed\n", __FUNCTION__)); + } + } + + /* query for 'ver' to get version info from firmware */ + memset(buf, 0, sizeof(buf)); + ptr = buf; + bcm_mkiovar("ver", 0, 0, buf, sizeof(buf)); + dhdcdc_query_ioctl(dhd, 0, WLC_GET_VAR, buf, sizeof(buf)); + bcmstrtok(&ptr, "\n", 0); + /* Print fw version info */ + DHD_ERROR(("Firmware version = %s\n", buf)); + + /* Set PowerSave mode */ + dhdcdc_set_ioctl(dhd, 0, WLC_SET_PM, (char *)&power_mode, sizeof(power_mode)); + + /* Match Host and Dongle rx alignment */ + bcm_mkiovar("bus:txglomalign", (char *)&dongle_align, 4, iovbuf, sizeof(iovbuf)); + dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf)); + + /* disable glom option per default */ + bcm_mkiovar("bus:txglom", (char *)&glom, 4, iovbuf, sizeof(iovbuf)); + dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf)); + + /* Setup timeout if Beacons are lost and roam is off to report link down */ + bcm_mkiovar("bcn_timeout", (char *)&bcn_timeout, 4, iovbuf, sizeof(iovbuf)); + dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf)); + + /* Enable/Disable build-in roaming to allowed ext supplicant to take of romaing */ + bcm_mkiovar("roam_off", (char *)&dhd_roam, 4, iovbuf, sizeof(iovbuf)); + dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf)); + + if (dhd_roam == 0) + { + /* set internal roaming roaming parameters */ + int roam_scan_period = 30; /* in sec */ + int roam_fullscan_period = 120; /* in sec */ + int roam_trigger = -85; + int roam_delta = 15; + int band; + int band_temp_set = WLC_BAND_2G; + + if (dhdcdc_set_ioctl(dhd, 0, WLC_SET_ROAM_SCAN_PERIOD, \ + (char *)&roam_scan_period, sizeof(roam_scan_period)) < 0) + DHD_ERROR(("%s: roam scan setup failed\n", __FUNCTION__)); + + bcm_mkiovar("fullroamperiod", (char *)&roam_fullscan_period, \ + 4, iovbuf, sizeof(iovbuf)); + if (dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, \ + iovbuf, sizeof(iovbuf)) < 0) + DHD_ERROR(("%s: roam fullscan setup failed\n", __FUNCTION__)); + + if (dhdcdc_query_ioctl(dhd, 0, WLC_GET_BAND, \ + (char *)&band, sizeof(band)) < 0) + DHD_ERROR(("%s: roam delta setting failed\n", __FUNCTION__)); + else { + if ((band == WLC_BAND_AUTO) || (band == WLC_BAND_ALL)) + { + /* temp set band to insert new roams values */ + if (dhdcdc_set_ioctl(dhd, 0, WLC_SET_BAND, \ + (char *)&band_temp_set, sizeof(band_temp_set)) < 0) + DHD_ERROR(("%s: local band seting failed\n", __FUNCTION__)); + } + if (dhdcdc_set_ioctl(dhd, 0, WLC_SET_ROAM_DELTA, \ + (char *)&roam_delta, sizeof(roam_delta)) < 0) + DHD_ERROR(("%s: roam delta setting failed\n", __FUNCTION__)); + + if (dhdcdc_set_ioctl(dhd, 0, WLC_SET_ROAM_TRIGGER, \ + (char *)&roam_trigger, sizeof(roam_trigger)) < 0) + DHD_ERROR(("%s: roam trigger setting failed\n", __FUNCTION__)); + + /* Restore original band settinngs */ + if (dhdcdc_set_ioctl(dhd, 0, WLC_SET_BAND, \ + (char *)&band, sizeof(band)) < 0) + DHD_ERROR(("%s: Original band restore failed\n", __FUNCTION__)); + } + } + + /* Force STA UP */ + if (dhd_radio_up) + dhdcdc_set_ioctl(dhd, 0, WLC_UP, (char *)&up, sizeof(up)); + + /* Setup event_msgs */ + bcm_mkiovar("event_msgs", dhd->eventmask, WL_EVENTING_MASK_LEN, iovbuf, sizeof(iovbuf)); + dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf)); + + dhdcdc_set_ioctl(dhd, 0, WLC_SET_SCAN_CHANNEL_TIME, (char *)&scan_assoc_time, + sizeof(scan_assoc_time)); + dhdcdc_set_ioctl(dhd, 0, WLC_SET_SCAN_UNASSOC_TIME, (char *)&scan_unassoc_time, + sizeof(scan_unassoc_time)); + +#ifdef ARP_OFFLOAD_SUPPORT + /* Set and enable ARP offload feature */ + if (dhd_arp_enable) + dhd_arp_offload_set(dhd, dhd_arp_mode); + dhd_arp_offload_enable(dhd, dhd_arp_enable); +#endif /* ARP_OFFLOAD_SUPPORT */ + +#ifdef PKT_FILTER_SUPPORT + { + int i; + /* Set up pkt filter */ + if (dhd_pkt_filter_enable) { + for (i = 0; i < dhd->pktfilter_count; i++) { + dhd_pktfilter_offload_set(dhd, dhd->pktfilter[i]); + dhd_pktfilter_offload_enable(dhd, dhd->pktfilter[i], + dhd_pkt_filter_init, dhd_master_mode); + } + } + } +#endif /* PKT_FILTER_SUPPORT */ + + dhd_os_proto_unblock(dhd); + + return 0; +} + +#ifdef SIMPLE_ISCAN + +uint iscan_thread_id; +iscan_buf_t * iscan_chain = 0; + +iscan_buf_t * +dhd_iscan_allocate_buf(dhd_pub_t *dhd, iscan_buf_t **iscanbuf) +{ + iscan_buf_t *iscanbuf_alloc = 0; + iscan_buf_t *iscanbuf_head; + + dhd_iscan_lock(); + + iscanbuf_alloc = (iscan_buf_t*)MALLOC(dhd->osh, sizeof(iscan_buf_t)); + if (iscanbuf_alloc == NULL) + goto fail; + + iscanbuf_alloc->next = NULL; + iscanbuf_head = *iscanbuf; + + DHD_ISCAN(("%s: addr of allocated node = 0x%X" + "addr of iscanbuf_head = 0x%X dhd = 0x%X\n", + __FUNCTION__, iscanbuf_alloc, iscanbuf_head, dhd)); + + if (iscanbuf_head == NULL) { + *iscanbuf = iscanbuf_alloc; + DHD_ISCAN(("%s: Head is allocated\n", __FUNCTION__)); + goto fail; + } + + while (iscanbuf_head->next) + iscanbuf_head = iscanbuf_head->next; + + iscanbuf_head->next = iscanbuf_alloc; + +fail: + dhd_iscan_unlock(); + return iscanbuf_alloc; +} + +void +dhd_iscan_free_buf(void *dhdp, iscan_buf_t *iscan_delete) +{ + iscan_buf_t *iscanbuf_free = 0; + iscan_buf_t *iscanbuf_prv = 0; + iscan_buf_t *iscanbuf_cur = iscan_chain; + dhd_pub_t *dhd = dhd_bus_pub(dhdp); + + dhd_iscan_lock(); + /* If iscan_delete is null then delete the entire + * chain or else delete specific one provided + */ + if (!iscan_delete) { + while (iscanbuf_cur) { + iscanbuf_free = iscanbuf_cur; + iscanbuf_cur = iscanbuf_cur->next; + iscanbuf_free->next = 0; + MFREE(dhd->osh, iscanbuf_free, sizeof(iscan_buf_t)); + } + iscan_chain = 0; + } else { + while (iscanbuf_cur) { + if (iscanbuf_cur == iscan_delete) + break; + iscanbuf_prv = iscanbuf_cur; + iscanbuf_cur = iscanbuf_cur->next; + } + if (iscanbuf_prv) + iscanbuf_prv->next = iscan_delete->next; + + iscan_delete->next = 0; + MFREE(dhd->osh, iscan_delete, sizeof(iscan_buf_t)); + + if (!iscanbuf_prv) + iscan_chain = 0; + } + dhd_iscan_unlock(); +} + +iscan_buf_t * +dhd_iscan_result_buf(void) +{ + return iscan_chain; +} + + + +/* +* print scan cache +* print partial iscan_skip list differently +*/ +int +dhd_iscan_print_cache(iscan_buf_t *iscan_skip) +{ + int i = 0, l = 0; + iscan_buf_t *iscan_cur; + wl_iscan_results_t *list; + wl_scan_results_t *results; + wl_bss_info_t UNALIGNED *bi; + + dhd_iscan_lock(); + + iscan_cur = dhd_iscan_result_buf(); + + while (iscan_cur) { + list = (wl_iscan_results_t *)iscan_cur->iscan_buf; + if (!list) + break; + + results = (wl_scan_results_t *)&list->results; + if (!results) + break; + + if (results->version != WL_BSS_INFO_VERSION) { + DHD_ISCAN(("%s: results->version %d != WL_BSS_INFO_VERSION\n", + __FUNCTION__, results->version)); + goto done; + } + + bi = results->bss_info; + for (i = 0; i < results->count; i++) { + if (!bi) + break; + + DHD_ISCAN(("%s[%2.2d:%2.2d] %X:%X:%X:%X:%X:%X\n", + iscan_cur != iscan_skip?"BSS":"bss", l, i, + bi->BSSID.octet[0], bi->BSSID.octet[1], bi->BSSID.octet[2], + bi->BSSID.octet[3], bi->BSSID.octet[4], bi->BSSID.octet[5])); + + bi = (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)); + } + iscan_cur = iscan_cur->next; + l++; + } + +done: + dhd_iscan_unlock(); + return 0; +} + +/* +* delete disappeared AP from specific scan cache but skip partial list in iscan_skip +*/ +int +dhd_iscan_delete_bss(void *dhdp, void *addr, iscan_buf_t *iscan_skip) +{ + int i = 0, j = 0, l = 0; + iscan_buf_t *iscan_cur; + wl_iscan_results_t *list; + wl_scan_results_t *results; + wl_bss_info_t UNALIGNED *bi, *bi_new, *bi_next; + + uchar *s_addr = addr; + + dhd_iscan_lock(); + DHD_ISCAN(("%s: BSS to remove %X:%X:%X:%X:%X:%X\n", + __FUNCTION__, s_addr[0], s_addr[1], s_addr[2], + s_addr[3], s_addr[4], s_addr[5])); + + iscan_cur = dhd_iscan_result_buf(); + + while (iscan_cur) { + if (iscan_cur != iscan_skip) { + list = (wl_iscan_results_t *)iscan_cur->iscan_buf; + if (!list) + break; + + results = (wl_scan_results_t *)&list->results; + if (!results) + break; + + if (results->version != WL_BSS_INFO_VERSION) { + DHD_ERROR(("%s: results->version %d != WL_BSS_INFO_VERSION\n", + __FUNCTION__, results->version)); + goto done; + } + + bi = results->bss_info; + for (i = 0; i < results->count; i++) { + if (!bi) + break; + + if (!memcmp(bi->BSSID.octet, addr, ETHER_ADDR_LEN)) { + DHD_ISCAN(("%s: Del BSS[%2.2d:%2.2d] %X:%X:%X:%X:%X:%X\n", + __FUNCTION__, l, i, bi->BSSID.octet[0], + bi->BSSID.octet[1], bi->BSSID.octet[2], + bi->BSSID.octet[3], bi->BSSID.octet[4], + bi->BSSID.octet[5])); + + bi_new = bi; + bi = (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)); +/* + if(bi && bi_new) { + bcopy(bi, bi_new, results->buflen - + dtoh32(bi_new->length)); + results->buflen -= dtoh32(bi_new->length); + } +*/ + results->buflen -= dtoh32(bi_new->length); + results->count--; + + for (j = i; j < results->count; j++) { + if (bi && bi_new) { + DHD_ISCAN(("%s: Moved up BSS[%2.2d:%2.2d]" + "%X:%X:%X:%X:%X:%X\n", + __FUNCTION__, l, j, bi->BSSID.octet[0], + bi->BSSID.octet[1], bi->BSSID.octet[2], + bi->BSSID.octet[3], bi->BSSID.octet[4], + bi->BSSID.octet[5])); + + bi_next = (wl_bss_info_t *)((uintptr)bi + + dtoh32(bi->length)); + bcopy(bi, bi_new, dtoh32(bi->length)); + bi_new = (wl_bss_info_t *)((uintptr)bi_new + + dtoh32(bi_new->length)); + bi = bi_next; + } + } + + if (results->count == 0) { + /* Prune now empty partial scan list */ + dhd_iscan_free_buf(dhdp, iscan_cur); + goto done; + } + break; + } + bi = (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)); + } + } + iscan_cur = iscan_cur->next; + l++; + } + +done: + dhd_iscan_unlock(); + return 0; +} + +int +dhd_iscan_remove_duplicates(void * dhdp, iscan_buf_t *iscan_cur) +{ + int i = 0; + wl_iscan_results_t *list; + wl_scan_results_t *results; + wl_bss_info_t UNALIGNED *bi, *bi_new, *bi_next; + + dhd_iscan_lock(); + + DHD_ISCAN(("%s: Scan cache before delete\n", + __FUNCTION__)); + dhd_iscan_print_cache(iscan_cur); + + if (!iscan_cur) + goto done; + + list = (wl_iscan_results_t *)iscan_cur->iscan_buf; + if (!list) + goto done; + + results = (wl_scan_results_t *)&list->results; + if (!results) + goto done; + + if (results->version != WL_BSS_INFO_VERSION) { + DHD_ERROR(("%s: results->version %d != WL_BSS_INFO_VERSION\n", + __FUNCTION__, results->version)); + goto done; + } + + bi = results->bss_info; + for (i = 0; i < results->count; i++) { + if (!bi) + break; + + DHD_ISCAN(("%s: Find dups for BSS[%2.2d] %X:%X:%X:%X:%X:%X\n", + __FUNCTION__, i, bi->BSSID.octet[0], bi->BSSID.octet[1], bi->BSSID.octet[2], + bi->BSSID.octet[3], bi->BSSID.octet[4], bi->BSSID.octet[5])); + + dhd_iscan_delete_bss(dhdp, bi->BSSID.octet, iscan_cur); + + bi = (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)); + } + +done: + DHD_ISCAN(("%s: Scan cache after delete\n", __FUNCTION__)); + dhd_iscan_print_cache(iscan_cur); + dhd_iscan_unlock(); + return 0; +} + +void +dhd_iscan_ind_scan_confirm(void *dhdp, bool status) +{ + + dhd_ind_scan_confirm(dhdp, status); +} + +int +dhd_iscan_request(void * dhdp, uint16 action) +{ + int rc; + wl_iscan_params_t params; + dhd_pub_t *dhd = dhd_bus_pub(dhdp); + char buf[WLC_IOCTL_SMLEN]; + + + memset(¶ms, 0, sizeof(wl_iscan_params_t)); + memcpy(¶ms.params.bssid, ðer_bcast, ETHER_ADDR_LEN); + + params.params.bss_type = DOT11_BSSTYPE_ANY; + params.params.scan_type = DOT11_SCANTYPE_ACTIVE; + + params.params.nprobes = htod32(-1); + params.params.active_time = htod32(-1); + params.params.passive_time = htod32(-1); + params.params.home_time = htod32(-1); + params.params.channel_num = htod32(0); + + params.version = htod32(ISCAN_REQ_VERSION); + params.action = htod16(action); + params.scan_duration = htod16(0); + + bcm_mkiovar("iscan", (char *)¶ms, sizeof(wl_iscan_params_t), buf, WLC_IOCTL_SMLEN); + rc = dhd_wl_ioctl(dhdp, WLC_SET_VAR, buf, WLC_IOCTL_SMLEN); + + return rc; +} + +static int +dhd_iscan_get_partial_result(void *dhdp, uint *scan_count) +{ + wl_iscan_results_t *list_buf; + wl_iscan_results_t list; + wl_scan_results_t *results; + iscan_buf_t *iscan_cur; + int status = -1; + dhd_pub_t *dhd = dhd_bus_pub(dhdp); + int rc; + + + iscan_cur = dhd_iscan_allocate_buf(dhd, &iscan_chain); + if (!iscan_cur) { + DHD_ERROR(("%s: Failed to allocate node\n", __FUNCTION__)); + dhd_iscan_free_buf(dhdp, 0); + dhd_iscan_request(dhdp, WL_SCAN_ACTION_ABORT); + goto fail; + } + + dhd_iscan_lock(); + + memset(iscan_cur->iscan_buf, 0, WLC_IW_ISCAN_MAXLEN); + list_buf = (wl_iscan_results_t*)iscan_cur->iscan_buf; + results = &list_buf->results; + results->buflen = WL_ISCAN_RESULTS_FIXED_SIZE; + results->version = 0; + results->count = 0; + + memset(&list, 0, sizeof(list)); + list.results.buflen = htod32(WLC_IW_ISCAN_MAXLEN); + bcm_mkiovar("iscanresults", (char *)&list, WL_ISCAN_RESULTS_FIXED_SIZE, + iscan_cur->iscan_buf, WLC_IW_ISCAN_MAXLEN); + rc = dhd_wl_ioctl(dhdp, WLC_GET_VAR, iscan_cur->iscan_buf, WLC_IW_ISCAN_MAXLEN); + + results->buflen = dtoh32(results->buflen); + results->version = dtoh32(results->version); + *scan_count = results->count = dtoh32(results->count); + status = dtoh32(list_buf->status); + + dhd_iscan_unlock(); + + if (!(*scan_count)) + dhd_iscan_free_buf(dhdp, iscan_cur); + else + dhd_iscan_remove_duplicates(dhdp, iscan_cur); + + +fail: + return status; +} + +#endif + +#ifdef PNO_SUPPORT +int dhd_pno_clean(dhd_pub_t *dhd) +{ + char iovbuf[128]; + int pfn_enabled = 0; + int iov_len = 0; + int ret; + + /* Disable pfn */ + iov_len = bcm_mkiovar("pfn", (char *)&pfn_enabled, 4, iovbuf, sizeof(iovbuf)); + if ((ret = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf))) >= 0) { + /* clear pfn */ + iov_len = bcm_mkiovar("pfnclear", 0, 0, iovbuf, sizeof(iovbuf)); + if (iov_len) { + if ((ret = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, iov_len)) < 0) { + DHD_ERROR(("%s failed code %d\n", __FUNCTION__, ret)); + } + } + else { + ret = -1; + DHD_ERROR(("%s failed code %d\n", __FUNCTION__, iov_len)); + } + } + else + DHD_ERROR(("%s failed code %d\n", __FUNCTION__, ret)); + + return ret; +} + +int dhd_pno_enable(dhd_pub_t *dhd, int pfn_enabled) +{ + char iovbuf[128]; + int ret = -1; + + if ((!dhd) && ((pfn_enabled != 0) || (pfn_enabled != 1))) { + DHD_ERROR(("%s error exit\n", __FUNCTION__)); + return ret; + } + + /* Enable/disable PNO */ + if ((ret = bcm_mkiovar("pfn", (char *)&pfn_enabled, 4, iovbuf, sizeof(iovbuf))) > 0) { + if ((ret = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf))) < 0) { + DHD_ERROR(("%s failed for error=%d\n", __FUNCTION__, ret)); + return ret; + } + else { + dhd->pno_enable = pfn_enabled; + DHD_TRACE(("%s set pno as %d\n", __FUNCTION__, dhd->pno_enable)); + } + } + else DHD_ERROR(("%s failed err=%d\n", __FUNCTION__, ret)); + + return ret; +} + +/* Function to execute combined scan */ +int +dhd_pno_set(dhd_pub_t *dhd, wlc_ssid_t* ssids_local, int nssid, ushort scan_fr) +{ + int err = -1; + char iovbuf[128]; + int k, i; + wl_pfn_param_t pfn_param; + wl_pfn_t pfn_element; + + DHD_TRACE(("%s nssid=%d nchan=%d\n", __FUNCTION__, nssid, scan_fr)); + + if ((!dhd) && (!ssids_local)) { + DHD_ERROR(("%s error exit\n", __FUNCTION__)); + err = -1; + } + + /* Check for broadcast ssid */ + for (k = 0; k < nssid; k++) { + if (!ssids_local[k].SSID_len) { + DHD_ERROR(("%d: Broadcast SSID is ilegal for PNO setting\n", k)); + return err; + } + } +/* #define PNO_DUMP 1 */ +#ifdef PNO_DUMP + { + int j; + for (j = 0; j < nssid; j++) { + DHD_ERROR(("%d: scan for %s size =%d\n", j, + ssids_local[j].SSID, ssids_local[j].SSID_len)); + } + } +#endif /* PNO_DUMP */ + + /* clean up everything */ + if ((err = dhd_pno_clean(dhd)) < 0) { + DHD_ERROR(("%s failed error=%d\n", __FUNCTION__, err)); + return err; + } + memset(&pfn_param, 0, sizeof(pfn_param)); + memset(&pfn_element, 0, sizeof(pfn_element)); + + /* set pfn parameters */ + pfn_param.version = htod32(PFN_VERSION); + pfn_param.flags = htod16((PFN_LIST_ORDER << SORT_CRITERIA_BIT)); + + /* set up pno scan fr */ + if (scan_fr != 0) + pfn_param.scan_freq = htod32(scan_fr); + + bcm_mkiovar("pfn_set", (char *)&pfn_param, sizeof(pfn_param), iovbuf, sizeof(iovbuf)); + dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf)); + + /* set all pfn ssid */ + for (i = 0; i < nssid; i++) { + + pfn_element.bss_type = htod32(DOT11_BSSTYPE_INFRASTRUCTURE); + pfn_element.auth = (DOT11_OPEN_SYSTEM); + pfn_element.wpa_auth = htod32(WPA_AUTH_PFN_ANY); + pfn_element.wsec = htod32(0); + pfn_element.infra = htod32(1); + + memcpy((char *)pfn_element.ssid.SSID, ssids_local[i].SSID, ssids_local[i].SSID_len); + pfn_element.ssid.SSID_len = ssids_local[i].SSID_len; + + if ((err = + bcm_mkiovar("pfn_add", (char *)&pfn_element, + sizeof(pfn_element), iovbuf, sizeof(iovbuf))) > 0) { + if ((err = + dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf))) < 0) { + DHD_ERROR(("%s failed for i=%d error=%d\n", + __FUNCTION__, i, err)); + return err; + } + } + else DHD_ERROR(("%s failed err=%d\n", __FUNCTION__, err)); + } + + /* Enable PNO */ + /* dhd_pno_enable(dhd, 1); */ + return err; +} + +int dhd_pno_get_status(dhd_pub_t *dhd) +{ + int ret = -1; + + if (!dhd) + return ret; + else + return (dhd->pno_enable); +} + +#endif /* PNO_SUPPORT */ + +#if defined(CSCAN) + +/* Androd ComboSCAN support */ +/* + * data parsing from ComboScan tlv list +*/ +int +wl_iw_parse_data_tlv(char** list_str, void *dst, int dst_size, const char token, + int input_size, int *bytes_left) +{ + char* str = *list_str; + uint16 short_temp; + uint32 int_temp; + + if ((list_str == NULL) || (*list_str == NULL) ||(bytes_left == NULL) || (*bytes_left < 0)) { + DHD_ERROR(("%s error paramters\n", __FUNCTION__)); + return -1; + } + + /* Clean all dest bytes */ + memset(dst, 0, dst_size); + while (*bytes_left > 0) { + + if (str[0] != token) { + DHD_TRACE(("%s NOT Type=%d get=%d left_parse=%d \n", + __FUNCTION__, token, str[0], *bytes_left)); + return -1; + } + + *bytes_left -= 1; + str += 1; + + if (input_size == 1) { + memcpy(dst, str, input_size); + } + else if (input_size == 2) { + memcpy(dst, (char *)htod16(memcpy(&short_temp, str, input_size)), + input_size); + } + else if (input_size == 4) { + memcpy(dst, (char *)htod32(memcpy(&int_temp, str, input_size)), + input_size); + } + + *bytes_left -= input_size; + str += input_size; + *list_str = str; + return 1; + } + return 1; +} + +/* + * channel list parsing from cscan tlv list +*/ +int +wl_iw_parse_channel_list_tlv(char** list_str, uint16* channel_list, + int channel_num, int *bytes_left) +{ + char* str = *list_str; + int idx = 0; + + if ((list_str == NULL) || (*list_str == NULL) ||(bytes_left == NULL) || (*bytes_left < 0)) { + DHD_ERROR(("%s error paramters\n", __FUNCTION__)); + return -1; + } + + while (*bytes_left > 0) { + + if (str[0] != CSCAN_TLV_TYPE_CHANNEL_IE) { + *list_str = str; + DHD_TRACE(("End channel=%d left_parse=%d %d\n", idx, *bytes_left, str[0])); + return idx; + } + /* Get proper CSCAN_TLV_TYPE_CHANNEL_IE */ + *bytes_left -= 1; + str += 1; + + if (str[0] == 0) { + /* All channels */ + channel_list[idx] = 0x0; + } + else { + channel_list[idx] = (uint16)str[0]; + DHD_TRACE(("%s channel=%d \n", __FUNCTION__, channel_list[idx])); + } + *bytes_left -= 1; + str += 1; + + if (idx++ > 255) { + DHD_ERROR(("%s Too many channels \n", __FUNCTION__)); + return -1; + } + } + + *list_str = str; + return idx; +} + +/* + * SSIDs list parsing from cscan tlv list + */ +int +wl_iw_parse_ssid_list_tlv(char** list_str, wlc_ssid_t* ssid, int max, int *bytes_left) +{ + char* str = *list_str; + int idx = 0; + + if ((list_str == NULL) || (*list_str == NULL) || (*bytes_left < 0)) { + DHD_ERROR(("%s error paramters\n", __FUNCTION__)); + return -1; + } + + while (*bytes_left > 0) { + + if (str[0] != CSCAN_TLV_TYPE_SSID_IE) { + *list_str = str; + DHD_TRACE(("nssid=%d left_parse=%d %d\n", idx, *bytes_left, str[0])); + return idx; + } + + /* Get proper CSCAN_TLV_TYPE_SSID_IE */ + *bytes_left -= 1; + str += 1; + + if (str[0] == 0) { + /* Broadcast SSID */ + ssid[idx].SSID_len = 0; + memset((char*)ssid[idx].SSID, 0x0, DOT11_MAX_SSID_LEN); + *bytes_left -= 1; + str += 1; + + DHD_TRACE(("BROADCAST SCAN left=%d\n", *bytes_left)); + } + else if (str[0] <= DOT11_MAX_SSID_LEN) { + /* Get proper SSID size */ + ssid[idx].SSID_len = str[0]; + *bytes_left -= 1; + str += 1; + + /* Get SSID */ + if (ssid[idx].SSID_len > *bytes_left) { + DHD_ERROR(("%s out of memory range len=%d but left=%d\n", + __FUNCTION__, ssid[idx].SSID_len, *bytes_left)); + return -1; + } + + memcpy((char*)ssid[idx].SSID, str, ssid[idx].SSID_len); + + *bytes_left -= ssid[idx].SSID_len; + str += ssid[idx].SSID_len; + + DHD_TRACE(("%s :size=%d left=%d\n", + (char*)ssid[idx].SSID, ssid[idx].SSID_len, *bytes_left)); + } + else { + DHD_ERROR(("### SSID size more that %d\n", str[0])); + return -1; + } + + if (idx++ > max) { + DHD_ERROR(("%s number of SSIDs more that %d\n", __FUNCTION__, idx)); + return -1; + } + } + + *list_str = str; + return idx; +} + +/* Parse a comma-separated list from list_str into ssid array, starting + * at index idx. Max specifies size of the ssid array. Parses ssids + * and returns updated idx; if idx >= max not all fit, the excess have + * not been copied. Returns -1 on empty string, or on ssid too long. + */ +int +wl_iw_parse_ssid_list(char** list_str, wlc_ssid_t* ssid, int idx, int max) +{ + char* str, *ptr; + + if ((list_str == NULL) || (*list_str == NULL)) + return -1; + + for (str = *list_str; str != NULL; str = ptr) { + + /* check for next TAG */ + if (!strncmp(str, GET_CHANNEL, strlen(GET_CHANNEL))) { + *list_str = str + strlen(GET_CHANNEL); + return idx; + } + + if ((ptr = strchr(str, ',')) != NULL) { + *ptr++ = '\0'; + } + + if (strlen(str) > DOT11_MAX_SSID_LEN) { + DHD_ERROR(("ssid <%s> exceeds %d\n", str, DOT11_MAX_SSID_LEN)); + return -1; + } + + if (strlen(str) == 0) + ssid[idx].SSID_len = 0; + + if (idx < max) { + strcpy((char*)ssid[idx].SSID, str); + ssid[idx].SSID_len = strlen(str); + } + idx++; + } + return idx; +} + +/* + * Parse channel list from iwpriv CSCAN + */ +int +wl_iw_parse_channel_list(char** list_str, uint16* channel_list, int channel_num) +{ + int num; + int val; + char* str; + char* endptr = NULL; + + if ((list_str == NULL)||(*list_str == NULL)) + return -1; + + str = *list_str; + num = 0; + while (strncmp(str, GET_NPROBE, strlen(GET_NPROBE))) { + val = (int)strtoul(str, &endptr, 0); + if (endptr == str) { + printf("could not parse channel number starting at" + " substring \"%s\" in list:\n%s\n", + str, *list_str); + return -1; + } + str = endptr + strspn(endptr, " ,"); + + if (num == channel_num) { + DHD_ERROR(("too many channels (more than %d) in channel list:\n%s\n", + channel_num, *list_str)); + return -1; + } + + channel_list[num++] = (uint16)val; + } + *list_str = str; + return num; +} + +#endif diff --git a/bcm4329/src/dhd/sys/dhd_custom_gpio.c b/bcm4329/src/dhd/sys/dhd_custom_gpio.c index 2dc6e42..8c6ec47 100644 --- a/bcm4329/src/dhd/sys/dhd_custom_gpio.c +++ b/bcm4329/src/dhd/sys/dhd_custom_gpio.c @@ -20,7 +20,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_custom_gpio.c,v 1.1.4.6 2010/02/19 22:56:49 Exp $ +* $Id: dhd_custom_gpio.c,v 1.1.4.8.4.1 2010/09/02 23:13:16 Exp $ */ @@ -42,11 +42,11 @@ extern void bcm_wlan_power_off(int); extern void bcm_wlan_power_on(int); #endif /* CUSTOMER_HW */ - #ifdef CUSTOMER_HW2 int wifi_set_carddetect(int on); int wifi_set_power(int on, unsigned long msec); int wifi_get_irq_number(unsigned long *irq_flags_ptr); +int wifi_get_mac_addr(unsigned char *buf); #endif #if defined(OOB_INTR_ONLY) @@ -55,6 +55,10 @@ int wifi_get_irq_number(unsigned long *irq_flags_ptr); extern int sdioh_mmc_irq(int irq); #endif /* (BCMLXSDMMC) */ +#ifdef CUSTOMER_HW3 +#include <mach/gpio.h> +#endif + /* Customer specific Host GPIO defintion */ static int dhd_oob_gpio_num = -1; /* GG 19 */ @@ -63,27 +67,36 @@ MODULE_PARM_DESC(dhd_oob_gpio_num, "DHD oob gpio number"); int dhd_customer_oob_irq_map(unsigned long *irq_flags_ptr) { - int host_oob_irq; + int host_oob_irq = 0; + #ifdef CUSTOMER_HW2 host_oob_irq = wifi_get_irq_number(irq_flags_ptr); -#else + +#else /* for NOT CUSTOMER_HW2 */ #if defined(CUSTOM_OOB_GPIO_NUM) if (dhd_oob_gpio_num < 0) { dhd_oob_gpio_num = CUSTOM_OOB_GPIO_NUM; } #endif - *irq_flags_ptr = IRQF_TRIGGER_FALLING; + if (dhd_oob_gpio_num < 0) { WL_ERROR(("%s: ERROR customer specific Host GPIO is NOT defined \n", - __FUNCTION__)); + __FUNCTION__)); return (dhd_oob_gpio_num); } WL_ERROR(("%s: customer specific Host GPIO number is (%d)\n", __FUNCTION__, dhd_oob_gpio_num)); - host_oob_irq = sdioh_mmc_irq(dhd_oob_gpio_num); -#endif +#if defined CUSTOMER_HW + host_oob_irq = MSM_GPIO_TO_INT(dhd_oob_gpio_num); +#elif defined CUSTOMER_HW3 + gpio_request(dhd_oob_gpio_num, "oob irq"); + host_oob_irq = gpio_to_irq(dhd_oob_gpio_num); + gpio_direction_input(dhd_oob_gpio_num); +#endif /* CUSTOMER_HW */ +#endif /* CUSTOMER_HW2 */ + return (host_oob_irq); } #endif /* defined(OOB_INTR_ONLY) */ @@ -130,9 +143,37 @@ dhd_customer_gpio_wlan_ctrl(int onoff) __FUNCTION__)); #ifdef CUSTOMER_HW bcm_wlan_power_on(1); -#endif /* CUSTOMER_HW */ /* Lets customer power to get stable */ - OSL_DELAY(500); + OSL_DELAY(50); +#endif /* CUSTOMER_HW */ break; } } + +#ifdef GET_CUSTOM_MAC_ENABLE +/* Function to get custom MAC address */ +int +dhd_custom_get_mac_address(unsigned char *buf) +{ + int ret = 0; + + WL_TRACE(("%s Enter\n", __FUNCTION__)); + if (!buf) + return -EINVAL; + + /* Customer access to MAC address stored outside of DHD driver */ +#ifdef CUSTOMER_HW2 + ret = wifi_get_mac_addr(buf); +#endif + +#ifdef EXAMPLE_GET_MAC + /* EXAMPLE code */ + { + struct ether_addr ea_example = {{0x00, 0x11, 0x22, 0x33, 0x44, 0xFF}}; + bcopy((char *)&ea_example, buf, sizeof(struct ether_addr)); + } +#endif /* EXAMPLE_GET_MAC */ + + return ret; +} +#endif /* GET_CUSTOM_MAC_ENABLE */ diff --git a/bcm4329/src/dhd/sys/dhd_dbg.h b/bcm4329/src/dhd/sys/dhd_dbg.h index e6d18f7..b48c1d7 100644 --- a/bcm4329/src/dhd/sys/dhd_dbg.h +++ b/bcm4329/src/dhd/sys/dhd_dbg.h @@ -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_dbg.h,v 1.5.6.2.4.2.14.4 2009/12/11 01:13:49 Exp $ + * $Id: dhd_dbg.h,v 1.5.6.2.4.2.14.10 2010/05/21 21:49:38 Exp $ */ #ifndef _dhd_dbg_ @@ -42,6 +42,7 @@ #define DHD_GLOM(args) do {if (dhd_msg_level & DHD_GLOM_VAL) printf args;} while (0) #define DHD_EVENT(args) do {if (dhd_msg_level & DHD_EVENT_VAL) printf args;} while (0) #define DHD_BTA(args) do {if (dhd_msg_level & DHD_BTA_VAL) printf args;} while (0) +#define DHD_ISCAN(args) do {if (dhd_msg_level & DHD_ISCAN_VAL) printf args;} while (0) #define DHD_ERROR_ON() (dhd_msg_level & DHD_ERROR_VAL) #define DHD_TRACE_ON() (dhd_msg_level & DHD_TRACE_VAL) @@ -55,6 +56,7 @@ #define DHD_GLOM_ON() (dhd_msg_level & DHD_GLOM_VAL) #define DHD_EVENT_ON() (dhd_msg_level & DHD_EVENT_VAL) #define DHD_BTA_ON() (dhd_msg_level & DHD_BTA_VAL) +#define DHD_ISCAN_ON() (dhd_msg_level & DHD_ISCAN_VAL) #else /* DHD_DEBUG */ @@ -70,6 +72,7 @@ #define DHD_GLOM(args) #define DHD_EVENT(args) #define DHD_BTA(args) +#define DHD_ISCAN(args) #define DHD_ERROR_ON() 0 #define DHD_TRACE_ON() 0 @@ -83,7 +86,7 @@ #define DHD_GLOM_ON() 0 #define DHD_EVENT_ON() 0 #define DHD_BTA_ON() 0 - +#define DHD_ISCAN_ON() 0 #endif /* DHD_DEBUG */ #define DHD_LOG(args) diff --git a/bcm4329/src/dhd/sys/dhd_linux.c b/bcm4329/src/dhd/sys/dhd_linux.c index c25a9b7..2eff469 100644 --- a/bcm4329/src/dhd/sys/dhd_linux.c +++ b/bcm4329/src/dhd/sys/dhd_linux.c @@ -22,7 +22,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_linux.c,v 1.65.4.9.2.12.2.66 2010/04/01 17:01:25 Exp $ + * $Id: dhd_linux.c,v 1.65.4.9.2.12.2.104 2010/08/20 19:15:40 Exp $ */ #ifdef CONFIG_WIFI_CONTROL_FUNC @@ -61,7 +61,6 @@ #ifdef CONFIG_HAS_WAKELOCK #include <linux/wakelock.h> #endif -#include <linux/freezer.h> #if defined(CUSTOMER_HW2) && defined(CONFIG_WIFI_CONTROL_FUNC) #include <linux/wlan_plat.h> @@ -116,6 +115,17 @@ int wifi_set_reset(int on, unsigned long msec) return 0; } +int wifi_get_mac_addr(unsigned char *buf) +{ + printk("%s\n", __FUNCTION__); + if (!buf) + return -EINVAL; + if (wifi_control_data && wifi_control_data->get_mac_addr) { + return wifi_control_data->get_mac_addr(buf); + } + return -EOPNOTSUPP; +} + static int wifi_probe(struct platform_device *pdev) { struct wifi_platform_data *wifi_ctrl = @@ -140,8 +150,8 @@ static int wifi_remove(struct platform_device *pdev) DHD_TRACE(("## %s\n", __FUNCTION__)); wifi_control_data = wifi_ctrl; - wifi_set_carddetect(0); /* CardDetect (1->0) */ wifi_set_power(0, 0); /* Power Off */ + wifi_set_carddetect(0); /* CardDetect (1->0) */ up(&wifi_control_sem); return 0; @@ -205,12 +215,18 @@ print_tainted() /* Linux wireless extension support */ #if defined(CONFIG_WIRELESS_EXT) #include <wl_iw.h> -#endif +#endif /* defined(CONFIG_WIRELESS_EXT) */ #if defined(CONFIG_HAS_EARLYSUSPEND) #include <linux/earlysuspend.h> +extern int dhdcdc_set_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len); #endif /* defined(CONFIG_HAS_EARLYSUSPEND) */ +#ifdef PKT_FILTER_SUPPORT +extern void dhd_pktfilter_offload_set(dhd_pub_t * dhd, char *arg); +extern void dhd_pktfilter_offload_enable(dhd_pub_t * dhd, char *arg, int enable, int master_mode); +#endif + /* Interface control information */ typedef struct dhd_if { struct dhd_info *info; /* back pointer to dhd_info */ @@ -230,7 +246,7 @@ typedef struct dhd_if { typedef struct dhd_info { #if defined(CONFIG_WIRELESS_EXT) wl_iw_t iw; /* wireless extensions state (must be first) */ -#endif +#endif /* defined(CONFIG_WIRELESS_EXT) */ dhd_pub_t pub; @@ -263,6 +279,8 @@ typedef struct dhd_info { int wl_count; int wl_packet; + int hang_was_sent; + /* Thread to issue ioctl for multicast */ long sysioc_pid; struct semaphore sysioc_sem; @@ -286,7 +304,8 @@ char nvram_path[MOD_PARAM_PATHLEN]; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) struct semaphore dhd_registration_sem; -#endif +#define DHD_REGISTRATION_TIMEOUT 8000 /* msec : allowed time to finished dhd registration */ +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ /* load firmware and/or nvram values from the filesystem */ module_param_string(firmware_path, firmware_path, MOD_PARAM_PATHLEN, 0); module_param_string(nvram_path, nvram_path, MOD_PARAM_PATHLEN, 0); @@ -302,6 +321,31 @@ module_param(dhd_sysioc, uint, 0); uint dhd_watchdog_ms = 10; module_param(dhd_watchdog_ms, uint, 0); +#ifdef DHD_DEBUG +/* Console poll interval */ +uint dhd_console_ms = 0; +module_param(dhd_console_ms, uint, 0); +#endif /* DHD_DEBUG */ + +/* ARP offload agent mode : Enable ARP Host Auto-Reply and ARP Peer Auto-Reply */ +uint dhd_arp_mode = 0xb; +module_param(dhd_arp_mode, uint, 0); + +/* ARP offload enable */ +uint dhd_arp_enable = TRUE; +module_param(dhd_arp_enable, uint, 0); + +/* Global Pkt filter enable control */ +uint dhd_pkt_filter_enable = TRUE; +module_param(dhd_pkt_filter_enable, uint, 0); + +/* Pkt filter init setup */ +uint dhd_pkt_filter_init = 0; +module_param(dhd_pkt_filter_init, uint, 0); + +/* Pkt filter mode control */ +uint dhd_master_mode = TRUE; +module_param(dhd_master_mode, uint, 1); /* Watchdog thread priority, -1 to use kernel timer */ int dhd_watchdog_prio = 97; @@ -315,6 +359,16 @@ module_param(dhd_dpc_prio, int, 0); extern int dhd_dongle_memsize; module_param(dhd_dongle_memsize, int, 0); +/* Control fw roaming */ +#ifdef CUSTOMER_HW2 +uint dhd_roam = 0; +#else +uint dhd_roam = 1; +#endif + +/* Control radio state */ +uint dhd_radio_up = 1; + /* Network inteface name */ char iface_name[IFNAMSIZ]; module_param_string(iface_name, iface_name, IFNAMSIZ, 0); @@ -383,6 +437,9 @@ module_param(dhd_pktgen_len, uint, 0); /* Version string to report */ #ifdef DHD_DEBUG +#ifndef SRCBASE +#define SRCBASE "drivers/net/wireless/bcm4329" +#endif #define DHD_COMPILED "\nCompiled in " SRCBASE #else #define DHD_COMPILED @@ -397,7 +454,7 @@ static char dhd_version[] = "Dongle Host Driver, version " EPI_VERSION_STR #if defined(CONFIG_WIRELESS_EXT) struct iw_statistics *dhd_get_wireless_stats(struct net_device *dev); -#endif +#endif /* defined(CONFIG_WIRELESS_EXT) */ static void dhd_dpc(ulong data); /* forward decl */ @@ -417,18 +474,22 @@ static int dhd_wl_host_event(dhd_info_t *dhd, int *ifidx, void *pktdata, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) static int dhd_sleep_pm_callback(struct notifier_block *nfb, unsigned long action, void *ignored) { - switch (action) - { - case PM_HIBERNATION_PREPARE: - case PM_SUSPEND_PREPARE: - dhd_mmc_suspend = TRUE; - return NOTIFY_OK; - case PM_POST_HIBERNATION: - case PM_POST_SUSPEND: - dhd_mmc_suspend = FALSE; - return NOTIFY_OK; + int ret = NOTIFY_DONE; + + switch (action) { + case PM_HIBERNATION_PREPARE: + case PM_SUSPEND_PREPARE: + dhd_mmc_suspend = TRUE; + ret = NOTIFY_OK; + break; + case PM_POST_HIBERNATION: + case PM_POST_SUSPEND: + dhd_mmc_suspend = FALSE; + ret = NOTIFY_OK; + break; } - return 0; + smp_mb(); + return ret; } static struct notifier_block dhd_sleep_pm_notifier = { @@ -439,28 +500,126 @@ extern int register_pm_notifier(struct notifier_block *nb); extern int unregister_pm_notifier(struct notifier_block *nb); #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) */ +static void dhd_set_packet_filter(int value, dhd_pub_t *dhd) +{ +#ifdef PKT_FILTER_SUPPORT + DHD_TRACE(("%s: %d\n", __FUNCTION__, value)); + /* 1 - Enable packet filter, only allow unicast packet to send up */ + /* 0 - Disable packet filter */ + if (dhd_pkt_filter_enable) { + int i; + + for (i = 0; i < dhd->pktfilter_count; i++) { + dhd_pktfilter_offload_set(dhd, dhd->pktfilter[i]); + dhd_pktfilter_offload_enable(dhd, dhd->pktfilter[i], + value, dhd_master_mode); + } + } +#endif +} #if defined(CONFIG_HAS_EARLYSUSPEND) -extern int dhd_set_suspend(int value, dhd_pub_t *dhd); +static int dhd_set_suspend(int value, dhd_pub_t *dhd) +{ + int power_mode = PM_MAX; + /* wl_pkt_filter_enable_t enable_parm; */ + char iovbuf[32]; + int bcn_li_dtim = 3; +#ifdef CUSTOMER_HW2 + uint roamvar = 1; +#endif /* CUSTOMER_HW2 */ + + DHD_TRACE(("%s: enter, value = %d in_suspend = %d\n", + __FUNCTION__, value, dhd->in_suspend)); + + if (dhd && dhd->up) { + if (value && dhd->in_suspend) { + + /* Kernel suspended */ + DHD_TRACE(("%s: force extra Suspend setting \n", __FUNCTION__)); + + dhdcdc_set_ioctl(dhd, 0, WLC_SET_PM, + (char *)&power_mode, sizeof(power_mode)); + + /* Enable packet filter, only allow unicast packet to send up */ + dhd_set_packet_filter(1, dhd); + + /* if dtim skip setup as default force it to wake each thrid dtim + * for better power saving. + * Note that side effect is chance to miss BC/MC packet + */ + if ((dhd->dtim_skip == 0) || (dhd->dtim_skip == 1)) + bcn_li_dtim = 3; + else + bcn_li_dtim = dhd->dtim_skip; + bcm_mkiovar("bcn_li_dtim", (char *)&bcn_li_dtim, + 4, iovbuf, sizeof(iovbuf)); + dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf)); +#ifdef CUSTOMER_HW2 + /* Disable build-in roaming to allowed ext supplicant to take of roaming */ + bcm_mkiovar("roam_off", (char *)&roamvar, 4, iovbuf, sizeof(iovbuf)); + dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf)); +#endif /* CUSTOMER_HW2 */ + } else { + + /* Kernel resumed */ + DHD_TRACE(("%s: Remove extra suspend setting \n", __FUNCTION__)); + + power_mode = PM_FAST; + dhdcdc_set_ioctl(dhd, 0, WLC_SET_PM, (char *)&power_mode, + sizeof(power_mode)); + + /* disable pkt filter */ + dhd_set_packet_filter(0, dhd); + + /* restore pre-suspend setting for dtim_skip */ + bcm_mkiovar("bcn_li_dtim", (char *)&dhd->dtim_skip, + 4, iovbuf, sizeof(iovbuf)); + + dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf)); +#ifdef CUSTOMER_HW2 + roamvar = dhd_roam; + bcm_mkiovar("roam_off", (char *)&roamvar, 4, iovbuf, sizeof(iovbuf)); + dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf)); +#endif /* CUSTOMER_HW2 */ + } + } + + return 0; +} + +static void dhd_suspend_resume_helper(struct dhd_info *dhd, int val) +{ + dhd_pub_t *dhdp = &dhd->pub; + + dhd_os_wake_lock(dhdp); + dhd_os_proto_block(dhdp); + /* Set flag when early suspend was called */ + dhdp->in_suspend = val; + if (!dhdp->suspend_disable_flag) + dhd_set_suspend(val, dhdp); + dhd_os_proto_unblock(dhdp); + dhd_os_wake_unlock(dhdp); +} static void dhd_early_suspend(struct early_suspend *h) { - struct dhd_info *dhdp; - dhdp = container_of(h, struct dhd_info, early_suspend); + struct dhd_info *dhd = container_of(h, struct dhd_info, early_suspend); DHD_TRACE(("%s: enter\n", __FUNCTION__)); - dhd_set_suspend(1, &dhdp->pub); + if (dhd) + dhd_suspend_resume_helper(dhd, 1); } static void dhd_late_resume(struct early_suspend *h) { - struct dhd_info *dhdp; - dhdp = container_of(h, struct dhd_info, early_suspend); + struct dhd_info *dhd = container_of(h, struct dhd_info, early_suspend); DHD_TRACE(("%s: enter\n", __FUNCTION__)); - dhd_set_suspend(0, &dhdp->pub); + if (dhd) + dhd_suspend_resume_helper(dhd, 0); } #endif /* defined(CONFIG_HAS_EARLYSUSPEND) */ @@ -584,7 +743,11 @@ static void _dhd_set_multicast_list(dhd_info_t *dhd, int ifidx) { struct net_device *dev; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35) + struct netdev_hw_addr *ha; +#else struct dev_mc_list *mclist; +#endif uint32 allmulti, cnt; wl_ioctl_t ioc; @@ -594,15 +757,19 @@ _dhd_set_multicast_list(dhd_info_t *dhd, int ifidx) ASSERT(dhd && dhd->iflist[ifidx]); dev = dhd->iflist[ifidx]->net; - mclist = dev->mc_list; + + netif_addr_lock_bh(dev); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35) + cnt = netdev_mc_count(dev); +#else cnt = dev->mc_count; +#endif + netif_addr_unlock_bh(dev); /* Determine initial value of allmulti flag */ allmulti = (dev->flags & IFF_ALLMULTI) ? TRUE : FALSE; /* Send down the multicast list first. */ - - buflen = sizeof("mcast_list") + sizeof(cnt) + (cnt * ETHER_ADDR_LEN); if (!(bufp = buf = MALLOC(dhd->pub.osh, buflen))) { DHD_ERROR(("%s: out of memory for mcast_list, cnt %d\n", @@ -617,10 +784,22 @@ _dhd_set_multicast_list(dhd_info_t *dhd, int ifidx) memcpy(bufp, &cnt, sizeof(cnt)); bufp += sizeof(cnt); - for (cnt = 0; mclist && (cnt < dev->mc_count); cnt++, mclist = mclist->next) { + netif_addr_lock_bh(dev); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35) + netdev_for_each_mc_addr(ha, dev) { + if (!cnt) + break; + memcpy(bufp, ha->addr, ETHER_ADDR_LEN); + bufp += ETHER_ADDR_LEN; + cnt--; + } +#else + for (mclist = dev->mc_list;(mclist && (cnt > 0)); cnt--, mclist = mclist->next) { memcpy(bufp, (void *)mclist->dmi_addr, ETHER_ADDR_LEN); bufp += ETHER_ADDR_LEN; } +#endif + netif_addr_unlock_bh(dev); memset(&ioc, 0, sizeof(ioc)); ioc.cmd = WLC_SET_VAR; @@ -810,8 +989,6 @@ _dhd_sysioc_thread(void *data) bool in_ap = FALSE; #endif - set_freezable(); - DAEMONIZE("dhd_sysioc"); while (down_interruptible(&dhd->sysioc_sem) == 0) { @@ -941,11 +1118,19 @@ dhd_start_xmit(struct sk_buff *skb, struct net_device *net) DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + dhd_os_wake_lock(&dhd->pub); + /* Reject if down */ if (!dhd->pub.up || (dhd->pub.busstate == DHD_BUS_DOWN)) { DHD_ERROR(("%s: xmit rejected pub.up=%d busstate=%d\n", __FUNCTION__, dhd->pub.up, dhd->pub.busstate)); netif_stop_queue(net); + /* Send Event when bus down detected during data session */ + if (dhd->pub.busstate == DHD_BUS_DOWN) { + DHD_ERROR(("%s: Event HANG send up\n", __FUNCTION__)); + net_os_send_hang_message(net); + } + dhd_os_wake_unlock(&dhd->pub); return -ENODEV; } @@ -953,6 +1138,7 @@ dhd_start_xmit(struct sk_buff *skb, struct net_device *net) if (ifidx == DHD_BAD_IF) { DHD_ERROR(("%s: bad ifidx %d\n", __FUNCTION__, ifidx)); netif_stop_queue(net); + dhd_os_wake_unlock(&dhd->pub); return -ENODEV; } @@ -984,13 +1170,14 @@ dhd_start_xmit(struct sk_buff *skb, struct net_device *net) ret = dhd_sendpkt(&dhd->pub, ifidx, pktbuf); - done: if (ret) dhd->pub.dstats.tx_dropped++; else dhd->pub.tx_packets++; + dhd_os_wake_unlock(&dhd->pub); + /* Return ok: we always eat the packet */ return 0; } @@ -1187,24 +1374,19 @@ dhd_watchdog_thread(void *data) } #endif /* DHD_SCHED */ - set_freezable(); - DAEMONIZE("dhd_watchdog"); /* Run until signal received */ while (1) { if (down_interruptible (&dhd->watchdog_sem) == 0) { dhd_os_wake_lock(&dhd->pub); - /* Call the bus module watchdog */ - dhd_bus_watchdog(&dhd->pub); + if (dhd->pub.dongle_reset == FALSE) { + /* Call the bus module watchdog */ + dhd_bus_watchdog(&dhd->pub); + } /* Count the tick for reference */ dhd->pub.tickcnt++; - - /* Reschedule the watchdog */ - if (dhd->wd_timer_valid) { - mod_timer(&dhd->timer, jiffies + dhd_watchdog_ms * HZ / 1000); - } dhd_os_wake_unlock(&dhd->pub); } else @@ -1219,8 +1401,15 @@ dhd_watchdog(ulong data) { dhd_info_t *dhd = (dhd_info_t *)data; + dhd_os_wake_lock(&dhd->pub); if (dhd->watchdog_pid >= 0) { up(&dhd->watchdog_sem); + + /* Reschedule the watchdog */ + if (dhd->wd_timer_valid) { + mod_timer(&dhd->timer, jiffies + dhd_watchdog_ms * HZ / 1000); + } + dhd_os_wake_unlock(&dhd->pub); return; } @@ -1231,12 +1420,9 @@ dhd_watchdog(ulong data) dhd->pub.tickcnt++; /* Reschedule the watchdog */ -#if defined(CONTINUOUS_WATCHDOG) - mod_timer(&dhd->timer, jiffies + dhd_watchdog_ms * HZ / 1000); -#else if (dhd->wd_timer_valid) mod_timer(&dhd->timer, jiffies + dhd_watchdog_ms * HZ / 1000); -#endif /* defined(CONTINUOUS_WATCHDOG) */ + dhd_os_wake_unlock(&dhd->pub); } static int @@ -1256,8 +1442,6 @@ dhd_dpc_thread(void *data) } #endif /* DHD_SCHED */ - set_freezable(); - DAEMONIZE("dhd_dpc"); /* Run until signal received */ @@ -1526,28 +1710,40 @@ dhd_ioctl_entry(struct net_device *net, struct ifreq *ifr, int cmd) uint driver = 0; int ifidx; bool is_set_key_cmd; + int ret; + + dhd_os_wake_lock(&dhd->pub); ifidx = dhd_net2idx(dhd, net); DHD_TRACE(("%s: ifidx %d, cmd 0x%04x\n", __FUNCTION__, ifidx, cmd)); - if (ifidx == DHD_BAD_IF) + if (ifidx == DHD_BAD_IF) { + dhd_os_wake_unlock(&dhd->pub); return -1; + } #if defined(CONFIG_WIRELESS_EXT) /* linux wireless extensions */ if ((cmd >= SIOCIWFIRST) && (cmd <= SIOCIWLAST)) { /* may recurse, do NOT lock */ - return wl_iw_ioctl(net, ifr, cmd); + ret = wl_iw_ioctl(net, ifr, cmd); + dhd_os_wake_unlock(&dhd->pub); + return ret; } -#endif +#endif /* defined(CONFIG_WIRELESS_EXT) */ #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 2) - if (cmd == SIOCETHTOOL) - return (dhd_ethtool(dhd, (void*)ifr->ifr_data)); + if (cmd == SIOCETHTOOL) { + ret = dhd_ethtool(dhd, (void*)ifr->ifr_data); + dhd_os_wake_unlock(&dhd->pub); + return ret; + } #endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 2) */ - if (cmd != SIOCDEVPRIVATE) + if (cmd != SIOCDEVPRIVATE) { + dhd_os_wake_unlock(&dhd->pub); return -EOPNOTSUPP; + } memset(&ioc, 0, sizeof(ioc)); @@ -1600,7 +1796,7 @@ dhd_ioctl_entry(struct net_device *net, struct ifreq *ifr, int cmd) /* send to dongle (must be up, and wl) */ if (dhd->pub.busstate != DHD_BUS_DATA) { - DHD_ERROR(("%s DONGLE_DOWN,__FUNCTION__\n", __FUNCTION__)); + DHD_ERROR(("%s DONGLE_DOWN\n", __FUNCTION__)); bcmerror = BCME_DONGLE_DOWN; goto done; } @@ -1625,6 +1821,12 @@ dhd_ioctl_entry(struct net_device *net, struct ifreq *ifr, int cmd) bcmerror = dhd_prot_ioctl(&dhd->pub, ifidx, (wl_ioctl_t *)&ioc, buf, buflen); done: + if ((bcmerror == -ETIMEDOUT) || ((dhd->pub.busstate == DHD_BUS_DOWN) && + (!dhd->pub.dongle_reset))) { + DHD_ERROR(("%s: Event HANG send up\n", __FUNCTION__)); + net_os_send_hang_message(net); + } + if (!bcmerror && buf && ioc.buf) { if (copy_to_user(ioc.buf, buf, buflen)) bcmerror = -EFAULT; @@ -1633,6 +1835,8 @@ done: if (buf) MFREE(dhd->pub.osh, buf, buflen); + dhd_os_wake_unlock(&dhd->pub); + return OSL_ERROR(bcmerror); } @@ -1643,7 +1847,6 @@ dhd_stop(struct net_device *net) dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net); DHD_TRACE(("%s: Enter\n", __FUNCTION__)); - if (dhd->pub.up == 0) { return 0; } @@ -1673,7 +1876,11 @@ dhd_open(struct net_device *net) ifidx = dhd_net2idx(dhd, net); DHD_TRACE(("%s: ifidx %d\n", __FUNCTION__, ifidx)); - /* ASSERT(ifidx == 0); */ + if ((dhd->iflist[ifidx]) && (dhd->iflist[ifidx]->state == WLC_E_IF_DEL)) { + DHD_ERROR(("%s: Error: called when IF already deleted\n", __FUNCTION__)); + return -1; + } + if (ifidx == 0) { /* do it only for primary eth0 */ @@ -1712,7 +1919,7 @@ dhd_osl_detach(osl_t *osh) osl_detach(osh); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && 1 up(&dhd_registration_sem); -#endif +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ } int @@ -1860,7 +2067,7 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) DHD_ERROR(("wl_iw_attach failed\n")); goto fail; } -#endif +#endif /* defined(CONFIG_WIRELESS_EXT) */ /* Set up the watchdog timer */ init_timer(&dhd->timer); @@ -1940,6 +2147,9 @@ dhd_bus_start(dhd_pub_t *dhdp) { int ret = -1; dhd_info_t *dhd = (dhd_info_t*)dhdp->info; +#ifdef EMBEDDED_PLATFORM + char iovbuf[WL_EVENTING_MASK_LEN + 12]; /* Room for "event_msgs" + '\0' + bitvec */ +#endif /* EMBEDDED_PLATFORM */ ASSERT(dhd); @@ -1985,6 +2195,41 @@ dhd_bus_start(dhd_pub_t *dhdp) return -ENODEV; } +#ifdef EMBEDDED_PLATFORM + bcm_mkiovar("event_msgs", dhdp->eventmask, WL_EVENTING_MASK_LEN, iovbuf, sizeof(iovbuf)); + dhdcdc_query_ioctl(dhdp, 0, WLC_GET_VAR, iovbuf, sizeof(iovbuf)); + bcopy(iovbuf, dhdp->eventmask, WL_EVENTING_MASK_LEN); + + setbit(dhdp->eventmask, WLC_E_SET_SSID); + setbit(dhdp->eventmask, WLC_E_PRUNE); + setbit(dhdp->eventmask, WLC_E_AUTH); + setbit(dhdp->eventmask, WLC_E_REASSOC); + setbit(dhdp->eventmask, WLC_E_REASSOC_IND); + setbit(dhdp->eventmask, WLC_E_DEAUTH_IND); + setbit(dhdp->eventmask, WLC_E_DISASSOC_IND); + setbit(dhdp->eventmask, WLC_E_DISASSOC); + setbit(dhdp->eventmask, WLC_E_JOIN); + setbit(dhdp->eventmask, WLC_E_ASSOC_IND); + setbit(dhdp->eventmask, WLC_E_PSK_SUP); + setbit(dhdp->eventmask, WLC_E_LINK); + setbit(dhdp->eventmask, WLC_E_NDIS_LINK); + setbit(dhdp->eventmask, WLC_E_MIC_ERROR); + setbit(dhdp->eventmask, WLC_E_PMKID_CACHE); + setbit(dhdp->eventmask, WLC_E_TXFAIL); + setbit(dhdp->eventmask, WLC_E_JOIN_START); + setbit(dhdp->eventmask, WLC_E_SCAN_COMPLETE); +#ifdef PNO_SUPPORT + setbit(dhdp->eventmask, WLC_E_PFN_NET_FOUND); +#endif /* PNO_SUPPORT */ + +/* enable dongle roaming event */ + setbit(dhdp->eventmask, WLC_E_ROAM); + + dhdp->pktfilter_count = 1; + /* Setup filter to allow only unicast */ + dhdp->pktfilter[0] = "100 0 0 0 0x01 0x00"; +#endif /* EMBEDDED_PLATFORM */ + /* Bus is ready, do any protocol initialization */ if ((ret = dhd_prot_init(&dhd->pub)) < 0) return ret; @@ -2080,21 +2325,21 @@ dhd_net_attach(dhd_pub_t *dhdp, int ifidx) if (ifidx == 1) { DHD_TRACE(("%s ACCESS POINT MAC: \n", __FUNCTION__)); /* ACCESSPOINT INTERFACE CASE */ - temp_addr[0] |= 0X02; /* set bit 2 , - Locally Administered address */ + temp_addr[0] |= 0x02; /* set bit 2 , - Locally Administered address */ } net->hard_header_len = ETH_HLEN + dhd->pub.hdrlen; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) net->ethtool_ops = &dhd_ethtool_ops; #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) */ -#ifdef CONFIG_WIRELESS_EXT +#if defined(CONFIG_WIRELESS_EXT) #if WIRELESS_EXT < 19 net->get_wireless_stats = dhd_get_wireless_stats; #endif /* WIRELESS_EXT < 19 */ #if WIRELESS_EXT > 12 net->wireless_handlers = (struct iw_handler_def *)&wl_iw_handler_def; #endif /* WIRELESS_EXT > 12 */ -#endif /* CONFIG_WIRELESS_EXT */ +#endif /* defined(CONFIG_WIRELESS_EXT) */ dhd->pub.rxsz = net->mtu + net->hard_header_len + dhd->pub.hdrlen; @@ -2108,6 +2353,8 @@ dhd_net_attach(dhd_pub_t *dhdp, int ifidx) printf("%s: Broadcom Dongle Host Driver mac=%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", net->name, dhd->pub.mac.octet[0], dhd->pub.mac.octet[1], dhd->pub.mac.octet[2], dhd->pub.mac.octet[3], dhd->pub.mac.octet[4], dhd->pub.mac.octet[5]); + +#if defined(CONFIG_WIRELESS_EXT) #ifdef SOFTAP if (ifidx == 0) /* Don't call for SOFTAP Interface in SOFTAP MODE */ @@ -2115,10 +2362,11 @@ dhd_net_attach(dhd_pub_t *dhdp, int ifidx) #else wl_iw_iscan_set_scan_broadcast_prep(net, 1); #endif /* SOFTAP */ +#endif /* CONFIG_WIRELESS_EXT */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) up(&dhd_registration_sem); -#endif +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ return 0; fail: @@ -2170,7 +2418,8 @@ dhd_detach(dhd_pub_t *dhdp) int i; #if defined(CONFIG_HAS_EARLYSUSPEND) - unregister_early_suspend(&dhd->early_suspend); + if (dhd->early_suspend.suspend) + unregister_early_suspend(&dhd->early_suspend); #endif /* defined(CONFIG_HAS_EARLYSUSPEND) */ #if defined(CONFIG_WIRELESS_EXT) /* Attach and link in the iw */ @@ -2230,6 +2479,19 @@ dhd_detach(dhd_pub_t *dhdp) } } +static void __exit +dhd_module_cleanup(void) +{ + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + dhd_bus_unregister(); +#if defined(CUSTOMER_HW2) && defined(CONFIG_WIFI_CONTROL_FUNC) + wifi_del_dev(); +#endif + /* Call customer gpio to turn off power with WL_REG_ON signal */ + dhd_customer_gpio_wlan_ctrl(WLAN_POWER_OFF); +} + static int __init dhd_module_init(void) { @@ -2289,7 +2551,7 @@ dhd_module_init(void) * It's needed to make sync up exit from dhd insmod and * Kernel MMC sdio device callback registration */ - if (down_timeout(&dhd_registration_sem, msecs_to_jiffies(10000)) != 0) { + if (down_timeout(&dhd_registration_sem, msecs_to_jiffies(DHD_REGISTRATION_TIMEOUT)) != 0) { error = -EINVAL; DHD_ERROR(("%s: sdio_register_driver timeout\n", __FUNCTION__)); goto fail_2; @@ -2312,20 +2574,6 @@ fail_0: return error; } -static void __exit -dhd_module_cleanup(void) -{ - DHD_TRACE(("%s: Enter\n", __FUNCTION__)); - - dhd_bus_unregister(); -#if defined(CUSTOMER_HW2) && defined(CONFIG_WIFI_CONTROL_FUNC) - wifi_del_dev(); -#endif - /* Call customer gpio to turn off power with WL_REG_ON signal */ - dhd_customer_gpio_wlan_ctrl(WLAN_POWER_OFF); -} - - module_init(dhd_module_init); module_exit(dhd_module_cleanup); @@ -2378,14 +2626,17 @@ dhd_os_ioctl_resp_wait(dhd_pub_t *pub, uint *condition, bool *pending) int timeout = dhd_ioctl_timeout_msec; /* Convert timeout in millsecond to jiffies */ - timeout = timeout * HZ / 1000; + /* timeout = timeout * HZ / 1000; */ + timeout = msecs_to_jiffies(timeout); /* Wait until control frame is available */ add_wait_queue(&dhd->ioctl_resp_wait, &wait); set_current_state(TASK_INTERRUPTIBLE); - - while (!(*condition) && (!signal_pending(current) && timeout)) + smp_mb(); + while (!(*condition) && (!signal_pending(current) && timeout)) { timeout = schedule_timeout(timeout); + smp_mb(); + } if (signal_pending(current)) *pending = TRUE; @@ -2412,8 +2663,12 @@ void dhd_os_wd_timer(void *bus, uint wdtick) { dhd_pub_t *pub = bus; - dhd_info_t *dhd = (dhd_info_t *)pub->info; static uint save_dhd_watchdog_ms = 0; + dhd_info_t *dhd = (dhd_info_t *)pub->info; + + /* don't start the wd until fw is loaded */ + if (pub->busstate == DHD_BUS_DOWN) + return; /* Totally stop the timer */ if (!wdtick && dhd->wd_timer_valid == TRUE) { @@ -2577,7 +2832,7 @@ dhd_get_wireless_stats(struct net_device *dev) else return NULL; } -#endif +#endif /* defined(CONFIG_WIRELESS_EXT) */ static int dhd_wl_host_event(dhd_info_t *dhd, int *ifidx, void *pktdata, @@ -2594,8 +2849,18 @@ dhd_wl_host_event(dhd_info_t *dhd, int *ifidx, void *pktdata, #if defined(CONFIG_WIRELESS_EXT) ASSERT(dhd->iflist[*ifidx] != NULL); - wl_iw_event(dhd->iflist[*ifidx]->net, event, *data); -#endif + if (ntoh32(event->event_type) == WLC_E_IF) { + DHD_INFO(("<0> interface:%d OP:%d don't pass to wext," + "net_device might not be created yet\n", + *ifidx, ntoh32(event->event_type))); + return bcmerror; + } + + ASSERT(dhd->iflist[*ifidx]->net != NULL); + + if (dhd->iflist[*ifidx]->net) + wl_iw_event(dhd->iflist[*ifidx]->net, event, *data); +#endif /* defined(CONFIG_WIRELESS_EXT) */ return (bcmerror); } @@ -2645,12 +2910,70 @@ dhd_dev_reset(struct net_device *dev, uint8 flag) /* Turning on watchdog back */ if (!flag) dhd_os_wd_timer(&dhd->pub, dhd_watchdog_ms); - DHD_ERROR(("%s: WLAN OFF DONE\n", __FUNCTION__)); return 1; } +int net_os_set_suspend_disable(struct net_device *dev, int val) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + int ret = 0; + + if (dhd) { + ret = dhd->pub.suspend_disable_flag; + dhd->pub.suspend_disable_flag = val; + } + return ret; +} + +int net_os_set_suspend(struct net_device *dev, int val) +{ + int ret = 0; +#if defined(CONFIG_HAS_EARLYSUSPEND) + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + + if (dhd) { + dhd_os_proto_block(&dhd->pub); + ret = dhd_set_suspend(val, &dhd->pub); + dhd_os_proto_unblock(&dhd->pub); + } +#endif /* defined(CONFIG_HAS_EARLYSUSPEND) */ + return ret; +} + +int net_os_set_dtim_skip(struct net_device *dev, int val) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + + if (dhd) + dhd->pub.dtim_skip = val; + + return 0; +} + +int net_os_set_packet_filter(struct net_device *dev, int val) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + int ret = 0; + + /* Packet filtering is set only if we still in early-suspend and + * we need either to turn it ON or turn it OFF + * We can always turn it OFF in case of early-suspend, but we turn it + * back ON only if suspend_disable_flag was not set + */ + if (dhd && dhd->pub.up) { + dhd_os_proto_block(&dhd->pub); + if (dhd->pub.in_suspend) { + if (!val || (val && !dhd->pub.suspend_disable_flag)) + dhd_set_packet_filter(val, &dhd->pub); + } + dhd_os_proto_unblock(&dhd->pub); + } + return ret; +} + + void dhd_dev_init_ioctl(struct net_device *dev) { @@ -2659,6 +2982,47 @@ dhd_dev_init_ioctl(struct net_device *dev) dhd_preinit_ioctls(&dhd->pub); } +#ifdef PNO_SUPPORT +/* Linux wrapper to call common dhd_pno_clean */ +int +dhd_dev_pno_reset(struct net_device *dev) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + + return (dhd_pno_clean(&dhd->pub)); +} + + +/* Linux wrapper to call common dhd_pno_enable */ +int +dhd_dev_pno_enable(struct net_device *dev, int pfn_enabled) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + + return (dhd_pno_enable(&dhd->pub, pfn_enabled)); +} + + +/* Linux wrapper to call common dhd_pno_set */ +int +dhd_dev_pno_set(struct net_device *dev, wlc_ssid_t* ssids_local, int nssid, ushort scan_fr) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + + return (dhd_pno_set(&dhd->pub, ssids_local, nssid, scan_fr)); +} + +/* Linux wrapper to get pno status */ +int +dhd_dev_get_pno_status(struct net_device *dev) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + + return (dhd_pno_get_status(&dhd->pub)); +} + +#endif /* PNO_SUPPORT */ + static int dhd_get_pend_8021x_cnt(dhd_info_t *dhd) { @@ -2687,6 +3051,43 @@ dhd_wait_pend8021x(struct net_device *dev) return pend; } +#ifdef DHD_DEBUG +int +write_to_file(dhd_pub_t *dhd, uint8 *buf, int size) +{ + int ret = 0; + struct file *fp; + mm_segment_t old_fs; + loff_t pos = 0; + + /* change to KERNEL_DS address limit */ + old_fs = get_fs(); + set_fs(KERNEL_DS); + + /* open file to write */ + fp = filp_open("/tmp/mem_dump", O_WRONLY|O_CREAT, 0640); + if (!fp) { + printf("%s: open file error\n", __FUNCTION__); + ret = -1; + goto exit; + } + + /* Write buf to file */ + fp->f_op->write(fp, buf, size, &pos); + +exit: + /* free buf before return */ + MFREE(dhd->osh, buf, size); + /* close file before return */ + if (fp) + filp_close(fp, current->files); + /* restore previous address limit */ + set_fs(old_fs); + + return ret; +} +#endif /* DHD_DEBUG */ + int dhd_os_wake_lock_timeout(dhd_pub_t *pub) { dhd_info_t *dhd = (dhd_info_t *)(pub->info); @@ -2803,3 +3204,17 @@ int net_os_wake_unlock(struct net_device *dev) ret = dhd_os_wake_unlock(&dhd->pub); return ret; } + +int net_os_send_hang_message(struct net_device *dev) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + int ret = 0; + + if (dhd) { + if (!dhd->hang_was_sent) { + dhd->hang_was_sent = 1; + ret = wl_iw_send_priv_event(dev, "HANG"); + } + } + return ret; +} diff --git a/bcm4329/src/dhd/sys/dhd_linux_sched.c b/bcm4329/src/dhd/sys/dhd_linux_sched.c index 3189123..480b416 100644 --- a/bcm4329/src/dhd/sys/dhd_linux_sched.c +++ b/bcm4329/src/dhd/sys/dhd_linux_sched.c @@ -1,7 +1,7 @@ /* * Expose some of the kernel scheduler routines * - * Copyright (C) 1999-2009, Broadcom Corporation + * Copyright (C) 1999-2010, Broadcom Corporation * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you diff --git a/bcm4329/src/dhd/sys/dhd_proto.h b/bcm4329/src/dhd/sys/dhd_proto.h index 1e2401a..7ef6929 100644 --- a/bcm4329/src/dhd/sys/dhd_proto.h +++ b/bcm4329/src/dhd/sys/dhd_proto.h @@ -4,7 +4,7 @@ * Provides type definitions and function prototypes used to link the * DHD OS, bus, and protocol modules. * - * Copyright (C) 1999-2009, Broadcom Corporation + * Copyright (C) 1999-2010, Broadcom Corporation * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you @@ -24,7 +24,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_proto.h,v 1.2.82.1.4.1.16.6 2009/06/17 01:01:55 Exp $ + * $Id: dhd_proto.h,v 1.2.82.1.4.1.16.7 2010/05/10 12:54:59 Exp $ */ #ifndef _dhd_proto_h_ @@ -34,7 +34,11 @@ #include <wlioctl.h> #ifndef IOCTL_RESP_TIMEOUT -#define IOCTL_RESP_TIMEOUT 2000 /* In milli second */ +#define IOCTL_RESP_TIMEOUT 3000 /* In milli second */ +#endif + +#ifndef IOCTL_CHIP_ACTIVE_TIMEOUT +#define IOCTL_CHIP_ACTIVE_TIMEOUT 10 /* In milli second */ #endif /* diff --git a/bcm4329/src/dhd/sys/dhd_sdio.c b/bcm4329/src/dhd/sys/dhd_sdio.c index 8cb567f..bd73556 100644 --- a/bcm4329/src/dhd/sys/dhd_sdio.c +++ b/bcm4329/src/dhd/sys/dhd_sdio.c @@ -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,v 1.157.2.27.2.33.2.109 2010/04/22 05:52:46 Exp $ + * $Id: dhd_sdio.c,v 1.157.2.27.2.33.2.129.4.1 2010/09/02 23:13:16 Exp $ */ #include <typedefs.h> @@ -36,13 +36,11 @@ #include <bcmutils.h> #include <bcmendian.h> #include <bcmdevs.h> - #include <siutils.h> #include <hndpmu.h> #include <hndsoc.h> #include <sbchipc.h> #include <sbhnddma.h> - #include <sdio.h> #include <sbsdio.h> #include <sbsdpcmdev.h> @@ -60,6 +58,13 @@ #include <dhdioctl.h> #include <sdiovar.h> +#ifdef DHD_DEBUG +#include <hndrte_cons.h> +#endif /* DHD_DEBUG */ +#ifdef DHD_DEBUG_TRAP +#include <hndrte_armtrap.h> +#endif /* DHD_DEBUG_TRAP */ + #define QLEN 256 /* bulk rx and tx queue lengths */ #define FCHI (QLEN - 10) #define FCLOW (FCHI / 2) @@ -119,11 +124,11 @@ /* Bump up limit on waiting for HT to account for first startup; * if the image is doing a CRC calculation before programming the PMU * for HT availability, it could take a couple hundred ms more, so - * max out at a half second (500000us). + * max out at a 1 second (1000000us). */ -#if (PMU_MAX_TRANSITION_DLY <= 500000) +#if (PMU_MAX_TRANSITION_DLY < 1000000) #undef PMU_MAX_TRANSITION_DLY -#define PMU_MAX_TRANSITION_DLY 500000 +#define PMU_MAX_TRANSITION_DLY 1000000 #endif /* Value for ChipClockCSR during initial setup */ @@ -141,6 +146,17 @@ DHD_SPINWAIT_SLEEP_INIT(sdioh_spinwait_sleep); extern int dhdcdc_set_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len); +#ifdef DHD_DEBUG +/* Device console log buffer state */ +typedef struct dhd_console { + uint count; /* Poll interval msec counter */ + uint log_addr; /* Log struct address (fixed) */ + hndrte_log_t log; /* Log struct (host copy) */ + uint bufsize; /* Size of log buffer */ + uint8 *buf; /* Log buffer (host copy) */ + uint last; /* Last buffer read index */ +} dhd_console_t; +#endif /* DHD_DEBUG */ /* Private data for SDIO bus interaction */ typedef struct dhd_bus { @@ -208,6 +224,10 @@ typedef struct dhd_bus { uint polltick; /* Tick counter */ uint pollcnt; /* Count of active polls */ +#ifdef DHD_DEBUG + dhd_console_t console; /* Console output polling support */ + uint console_addr; /* Console address from shared struct */ +#endif /* DHD_DEBUG */ uint regfails; /* Count of R_REG/W_REG failures */ @@ -404,6 +424,10 @@ static void dhdsdio_testrcv(dhd_bus_t *bus, void *pkt, uint seq); static void dhdsdio_sdtest_set(dhd_bus_t *bus, bool start); #endif +#ifdef DHD_DEBUG_TRAP +static int dhdsdio_checkdied(dhd_bus_t *bus, uint8 *data, uint size); +static int dhdsdio_mem_dump(dhd_bus_t *bus); +#endif /* DHD_DEBUG_TRAP */ static int dhdsdio_download_state(dhd_bus_t *bus, bool enter); static void dhdsdio_release(dhd_bus_t *bus, osl_t *osh); @@ -929,7 +953,6 @@ dhdsdio_txpkt(dhd_bus_t *bus, void *pkt, uint chan, bool free_pkt) (((pad + 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)); - bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP; #ifdef DHD_DEBUG tx_packets[PKTPRIO(pkt)]++; @@ -995,6 +1018,9 @@ dhdsdio_txpkt(dhd_bus_t *bus, void *pkt, uint chan, bool free_pkt) } } + if (ret == 0) { + bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP; + } } while ((ret < 0) && retrydata && retries++ < TXRETRIES); done: @@ -1046,7 +1072,7 @@ dhd_bus_txdata(struct dhd_bus *bus, void *pkt) /* 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_PENDING)) { + (bus->clkstate != CLK_AVAIL)) { DHD_TRACE(("%s: deferring pktq len %d\n", __FUNCTION__, pktq_len(&bus->txq))); bus->fcqueued++; @@ -1082,6 +1108,7 @@ dhd_bus_txdata(struct dhd_bus *bus, void *pkt) /* 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 @@ -1236,6 +1263,8 @@ dhd_bus_txctl(struct dhd_bus *bus, uchar *msg, uint msglen) htol32_ua_store(0, frame + SDPCM_FRAMETAG_LEN + sizeof(swheader)); if (!DATAOK(bus)) { + DHD_INFO(("%s: No bus credit bus->tx_max %d, bus->tx_seq %d\n", + __FUNCTION__, bus->tx_max, bus->tx_seq)); bus->ctrl_frame_stat = TRUE; /* Send from dpc */ bus->ctrl_frame_buf = frame; @@ -1243,15 +1272,16 @@ dhd_bus_txctl(struct dhd_bus *bus, uchar *msg, uint msglen) dhd_wait_for_event(bus->dhd, &bus->ctrl_frame_stat); - if (bus->ctrl_frame_stat == FALSE) + if (bus->ctrl_frame_stat == FALSE) { + DHD_INFO(("%s: ctrl_frame_stat == FALSE\n", __FUNCTION__)); ret = 0; - else + } else { + DHD_INFO(("%s: ctrl_frame_stat == TRUE\n", __FUNCTION__)); ret = -1; + } } if (ret == -1) { - bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP; - #ifdef DHD_DEBUG if (DHD_BYTES_ON() && DHD_CTL_ON()) { prhex("Tx Frame", frame, len); @@ -1261,6 +1291,7 @@ dhd_bus_txctl(struct dhd_bus *bus, uchar *msg, uint msglen) #endif do { + bus->ctrl_frame_stat = FALSE; ret = dhd_bcmsdh_send_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2, F2SYNC, frame, len, NULL, NULL, NULL); ASSERT(ret != BCME_PENDING); @@ -1289,6 +1320,9 @@ dhd_bus_txctl(struct dhd_bus *bus, uchar *msg, uint msglen) } } + if (ret == 0) { + bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP; + } } while ((ret < 0) && retries++ < TXRETRIES); } @@ -1333,11 +1367,21 @@ dhd_bus_rxctl(struct dhd_bus *bus, uchar *msg, uint msglen) __FUNCTION__, rxlen, msglen)); } else if (timeleft == 0) { DHD_ERROR(("%s: resumed on timeout\n", __FUNCTION__)); +#ifdef DHD_DEBUG_TRAP + dhd_os_sdlock(bus->dhd); + dhdsdio_checkdied(bus, NULL, 0); + dhd_os_sdunlock(bus->dhd); +#endif /* DHD_DEBUG_TRAP */ } else if (pending == TRUE) { DHD_CTL(("%s: cancelled\n", __FUNCTION__)); return -ERESTARTSYS; } else { DHD_CTL(("%s: resumed for unknown reason?\n", __FUNCTION__)); +#ifdef DHD_DEBUG_TRAP + dhd_os_sdlock(bus->dhd); + dhdsdio_checkdied(bus, NULL, 0); + dhd_os_sdunlock(bus->dhd); +#endif /* DHD_DEBUG_TRAP */ } if (rxlen) @@ -1357,6 +1401,9 @@ enum { IOV_SDCIS, IOV_MEMBYTES, IOV_MEMSIZE, +#ifdef DHD_DEBUG_TRAP + IOV_CHECKDIED, +#endif IOV_DOWNLOAD, IOV_FORCEEVEN, IOV_SDIOD_DRIVE, @@ -1408,6 +1455,9 @@ const bcm_iovar_t dhdsdio_iovars[] = { {"txminmax", IOV_TXMINMAX, 0, IOVT_UINT32, 0 }, {"cpu", IOV_CPU, 0, IOVT_BOOL, 0 }, #endif /* DHD_DEBUG */ +#ifdef DHD_DEBUG_TRAP + {"checkdied", IOV_CHECKDIED, 0, IOVT_BUFFER, 0 }, +#endif /* DHD_DEBUG_TRAP */ #ifdef SDTEST {"extloop", IOV_EXTLOOP, 0, IOVT_BOOL, 0 }, {"pktgen", IOV_PKTGEN, 0, IOVT_BUFFER, sizeof(dhd_pktgen_t) }, @@ -1636,6 +1686,301 @@ xfer_done: return bcmerror; } +#ifdef DHD_DEBUG_TRAP +static int +dhdsdio_readshared(dhd_bus_t *bus, sdpcm_shared_t *sh) +{ + uint32 addr; + int rv; + + /* Read last word in memory to determine address of sdpcm_shared structure */ + if ((rv = dhdsdio_membytes(bus, FALSE, bus->ramsize - 4, (uint8 *)&addr, 4)) < 0) + return rv; + + addr = ltoh32(addr); + + DHD_INFO(("sdpcm_shared address 0x%08X\n", addr)); + + /* + * Check if addr is valid. + * NVRAM length at the end of memory should have been overwritten. + */ + if (addr == 0 || ((~addr >> 16) & 0xffff) == (addr & 0xffff)) { + DHD_ERROR(("%s: address (0x%08x) of sdpcm_shared invalid\n", __FUNCTION__, addr)); + return BCME_ERROR; + } + + /* Read hndrte_shared structure */ + if ((rv = dhdsdio_membytes(bus, FALSE, addr, (uint8 *)sh, sizeof(sdpcm_shared_t))) < 0) + return rv; + + /* Endianness */ + sh->flags = ltoh32(sh->flags); + sh->trap_addr = ltoh32(sh->trap_addr); + sh->assert_exp_addr = ltoh32(sh->assert_exp_addr); + sh->assert_file_addr = ltoh32(sh->assert_file_addr); + sh->assert_line = ltoh32(sh->assert_line); + sh->console_addr = ltoh32(sh->console_addr); + sh->msgtrace_addr = ltoh32(sh->msgtrace_addr); + + if ((sh->flags & SDPCM_SHARED_VERSION_MASK) != SDPCM_SHARED_VERSION) { + DHD_ERROR(("%s: sdpcm_shared version %d in dhd " + "is different than sdpcm_shared version %d in dongle\n", + __FUNCTION__, SDPCM_SHARED_VERSION, + sh->flags & SDPCM_SHARED_VERSION_MASK)); + return BCME_ERROR; + } + + return BCME_OK; +} + +static int +dhdsdio_checkdied(dhd_bus_t *bus, uint8 *data, uint size) +{ + int bcmerror = 0; + uint msize = 512; + char *mbuffer = NULL; + uint maxstrlen = 256; + char *str = NULL; + trap_t tr; + sdpcm_shared_t sdpcm_shared; + struct bcmstrbuf strbuf; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + if (data == NULL) { + /* + * Called after a rx ctrl timeout. "data" is NULL. + * allocate memory to trace the trap or assert. + */ + size = msize; + mbuffer = data = MALLOC(bus->dhd->osh, msize); + if (mbuffer == NULL) { + DHD_ERROR(("%s: MALLOC(%d) failed \n", __FUNCTION__, msize)); + bcmerror = BCME_NOMEM; + goto done; + } + } + + if ((str = MALLOC(bus->dhd->osh, maxstrlen)) == NULL) { + DHD_ERROR(("%s: MALLOC(%d) failed \n", __FUNCTION__, maxstrlen)); + bcmerror = BCME_NOMEM; + goto done; + } + + if ((bcmerror = dhdsdio_readshared(bus, &sdpcm_shared)) < 0) + goto done; + + bcm_binit(&strbuf, data, size); + + bcm_bprintf(&strbuf, "msgtrace address : 0x%08X\nconsole address : 0x%08X\n", + sdpcm_shared.msgtrace_addr, sdpcm_shared.console_addr); + + if ((sdpcm_shared.flags & SDPCM_SHARED_ASSERT_BUILT) == 0) { + /* NOTE: Misspelled assert is intentional - DO NOT FIX. + * (Avoids conflict with real asserts for programmatic parsing of output.) + */ + bcm_bprintf(&strbuf, "Assrt not built in dongle\n"); + } + + if ((sdpcm_shared.flags & (SDPCM_SHARED_ASSERT|SDPCM_SHARED_TRAP)) == 0) { + /* NOTE: Misspelled assert is intentional - DO NOT FIX. + * (Avoids conflict with real asserts for programmatic parsing of output.) + */ + bcm_bprintf(&strbuf, "No trap%s in dongle", + (sdpcm_shared.flags & SDPCM_SHARED_ASSERT_BUILT) + ?"/assrt" :""); + } else { + if (sdpcm_shared.flags & SDPCM_SHARED_ASSERT) { + /* Download assert */ + bcm_bprintf(&strbuf, "Dongle assert"); + if (sdpcm_shared.assert_exp_addr != 0) { + str[0] = '\0'; + if ((bcmerror = dhdsdio_membytes(bus, FALSE, + sdpcm_shared.assert_exp_addr, + (uint8 *)str, maxstrlen)) < 0) + goto done; + + str[maxstrlen - 1] = '\0'; + bcm_bprintf(&strbuf, " expr \"%s\"", str); + } + + if (sdpcm_shared.assert_file_addr != 0) { + str[0] = '\0'; + if ((bcmerror = dhdsdio_membytes(bus, FALSE, + sdpcm_shared.assert_file_addr, + (uint8 *)str, maxstrlen)) < 0) + goto done; + + str[maxstrlen - 1] = '\0'; + bcm_bprintf(&strbuf, " file \"%s\"", str); + } + + bcm_bprintf(&strbuf, " line %d ", sdpcm_shared.assert_line); + } + + if (sdpcm_shared.flags & SDPCM_SHARED_TRAP) { + if ((bcmerror = dhdsdio_membytes(bus, FALSE, + sdpcm_shared.trap_addr, + (uint8*)&tr, sizeof(trap_t))) < 0) + goto done; + + bcm_bprintf(&strbuf, + "Dongle trap type 0x%x @ epc 0x%x, cpsr 0x%x, spsr 0x%x, sp 0x%x," + "lp 0x%x, rpc 0x%x Trap offset 0x%x, " + "r0 0x%x, r1 0x%x, r2 0x%x, r3 0x%x, r4 0x%x, r5 0x%x, r6 0x%x, r7 0x%x\n", + tr.type, tr.epc, tr.cpsr, tr.spsr, tr.r13, tr.r14, tr.pc, + sdpcm_shared.trap_addr, + tr.r0, tr.r1, tr.r2, tr.r3, tr.r4, tr.r5, tr.r6, tr.r7); + } + } + + if (sdpcm_shared.flags & (SDPCM_SHARED_ASSERT | SDPCM_SHARED_TRAP)) { + DHD_ERROR(("%s: %s\n", __FUNCTION__, strbuf.origbuf)); + } + + if (sdpcm_shared.flags & SDPCM_SHARED_TRAP) { + /* Mem dump to a file on device */ + dhdsdio_mem_dump(bus); + } + +done: + if (mbuffer) + MFREE(bus->dhd->osh, mbuffer, msize); + if (str) + MFREE(bus->dhd->osh, str, maxstrlen); + + return bcmerror; +} + +static int +dhdsdio_mem_dump(dhd_bus_t *bus) +{ + int ret = 0; + int size; /* Full mem size */ + int start = 0; /* Start address */ + int read_size = 0; /* Read size of each iteration */ + uint8 *buf = NULL, *databuf = NULL; + + /* Get full mem size */ + size = bus->ramsize; + buf = MALLOC(bus->dhd->osh, size); + if (!buf) { + printf("%s: Out of memory (%d bytes)\n", __FUNCTION__, size); + return -1; + } + + /* Read mem content */ + printf("Dump dongle memory"); + databuf = buf; + while (size) + { + read_size = MIN(MEMBLOCK, size); + if ((ret = dhdsdio_membytes(bus, FALSE, start, databuf, read_size))) + { + printf("%s: Error membytes %d\n", __FUNCTION__, ret); + if (buf) { + MFREE(bus->dhd->osh, buf, size); + } + return -1; + } + printf("."); + + /* Decrement size and increment start address */ + size -= read_size; + start += read_size; + databuf += read_size; + } + printf("Done\n"); + +#ifdef DHD_DEBUG + /* free buf before return !!! */ + if (write_to_file(bus->dhd, buf, bus->ramsize)) + { + printf("%s: Error writing to files\n", __FUNCTION__); + return -1; + } + /* buf free handled in write_to_file, not here */ +#else + MFREE(bus->dhd->osh, buf, size); +#endif + return 0; +} +#endif /* DHD_DEBUG_TRAP */ + +#ifdef DHD_DEBUG +#define CONSOLE_LINE_MAX 192 + +static int +dhdsdio_readconsole(dhd_bus_t *bus) +{ + dhd_console_t *c = &bus->console; + uint8 line[CONSOLE_LINE_MAX], ch; + uint32 n, idx, addr; + int rv; + + /* Don't do anything until FWREADY updates console address */ + if (bus->console_addr == 0) + return 0; + + /* Read console log struct */ + addr = bus->console_addr + OFFSETOF(hndrte_cons_t, log); + if ((rv = dhdsdio_membytes(bus, FALSE, addr, (uint8 *)&c->log, sizeof(c->log))) < 0) + return rv; + + /* Allocate console buffer (one time only) */ + if (c->buf == NULL) { + c->bufsize = ltoh32(c->log.buf_size); + if ((c->buf = MALLOC(bus->dhd->osh, c->bufsize)) == NULL) + return BCME_NOMEM; + } + + idx = ltoh32(c->log.idx); + + /* Protect against corrupt value */ + if (idx > c->bufsize) + return BCME_ERROR; + + /* Skip reading the console buffer if the index pointer has not moved */ + if (idx == c->last) + return BCME_OK; + + /* Read the console buffer */ + addr = ltoh32(c->log.buf); + if ((rv = dhdsdio_membytes(bus, FALSE, addr, c->buf, c->bufsize)) < 0) + return rv; + + while (c->last != idx) { + for (n = 0; n < CONSOLE_LINE_MAX - 2; n++) { + if (c->last == idx) { + /* This would output a partial line. Instead, back up + * the buffer pointer and output this line next time around. + */ + if (c->last >= n) + c->last -= n; + else + c->last = c->bufsize - n; + goto break2; + } + ch = c->buf[c->last]; + c->last = (c->last + 1) % c->bufsize; + if (ch == '\n') + break; + line[n] = ch; + } + + if (n > 0) { + if (line[n - 1] == '\r') + n--; + line[n] = 0; + printf("CONSOLE: %s\n", line); + } + } +break2: + + return BCME_OK; +} +#endif /* DHD_DEBUG */ int dhdsdio_downloadvars(dhd_bus_t *bus, void *arg, int len) @@ -3881,10 +4226,13 @@ clkwait: DHD_INTR(("%s: enable SDIO interrupts, rxdone %d framecnt %d\n", __FUNCTION__, rxdone, framecnt)); bus->intdis = FALSE; +#if defined(OOB_INTR_ONLY) + bcmsdh_oob_intr_set(1); +#endif /* (OOB_INTR_ONLY) */ bcmsdh_intr_enable(sdh); } - if (DATAOK(bus) && bus->ctrl_frame_stat) { + if (DATAOK(bus) && bus->ctrl_frame_stat && (bus->clkstate == CLK_AVAIL)) { int ret, i; ret = dhd_bcmsdh_send_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2, F2SYNC, @@ -3916,13 +4264,16 @@ clkwait: } } + if (ret == 0) { + bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP; + } + printf("Return_dpc value is : %d\n", ret); - bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP; bus->ctrl_frame_stat = FALSE; dhd_wait_event_wakeup(bus->dhd); } /* Send queued frames (limit 1 if rx may still be pending) */ - else if ((bus->clkstate != CLK_PENDING) && !bus->fcstate && + else if ((bus->clkstate == CLK_AVAIL) && !bus->fcstate && pktq_mlen(&bus->txq, ~bus->flowcontrol) && txlimit && DATAOK(bus)) { framecnt = rxdone ? txlimit : MIN(txlimit, dhd_txminmax); framecnt = dhdsdio_sendfromq(bus, framecnt); @@ -3937,7 +4288,9 @@ clkwait: bus->dhd->busstate = DHD_BUS_DOWN; bus->intstatus = 0; } else if (bus->clkstate == CLK_PENDING) { - /* Awaiting I_CHIPACTIVE; don't resched */ + DHD_INFO(("%s: rescheduled due to CLK_PENDING awaiting \ + I_CHIPACTIVE interrupt", __FUNCTION__)); + resched = TRUE; } else if (bus->intstatus || bus->ipend || (!bus->fcstate && pktq_mlen(&bus->txq, ~bus->flowcontrol) && DATAOK(bus)) || PKT_AVAILABLE()) { /* Read multiple frames */ @@ -3961,7 +4314,6 @@ clkwait: bool dhd_bus_dpc(struct dhd_bus *bus) { -#ifdef SDIO_ISR_THREAD bool resched; /* Call the DPC directly. */ @@ -3969,9 +4321,6 @@ dhd_bus_dpc(struct dhd_bus *bus) resched = dhdsdio_dpc(bus); return resched; -#else - return dhdsdio_dpc(bus); -#endif /* SDIO_ISR_THREAD */ } void @@ -3980,6 +4329,8 @@ dhdsdio_isr(void *arg) dhd_bus_t *bus = (dhd_bus_t*)arg; bcmsdh_info_t *sdh; + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + if (!bus) { DHD_ERROR(("%s : bus is null pointer , exit \n", __FUNCTION__)); return; @@ -3990,9 +4341,6 @@ dhdsdio_isr(void *arg) DHD_ERROR(("%s : bus is down. we have nothing to do\n", __FUNCTION__)); return; } - - DHD_TRACE(("%s: Enter\n", __FUNCTION__)); - /* Count the interrupt call */ bus->intrcount++; bus->ipend = TRUE; @@ -4319,6 +4667,19 @@ dhd_bus_watchdog(dhd_pub_t *dhdp) bus->lastintrs = bus->intrcount; } +#ifdef DHD_DEBUG + /* Poll for console output periodically */ + if (dhdp->busstate == DHD_BUS_DATA && dhd_console_ms != 0) { + bus->console.count += dhd_watchdog_ms; + if (bus->console.count >= dhd_console_ms) { + bus->console.count -= dhd_console_ms; + /* Make sure backplane clock is on */ + dhdsdio_clkctl(bus, CLK_AVAIL, FALSE); + if (dhdsdio_readconsole(bus) < 0) + dhd_console_ms = 0; /* On error, stop trying */ + } + } +#endif /* DHD_DEBUG */ #ifdef SDTEST /* Generate packets if configured */ @@ -4336,7 +4697,6 @@ dhd_bus_watchdog(dhd_pub_t *dhdp) bus->idlecount = 0; if (bus->activity) { bus->activity = FALSE; - } else { dhdsdio_clkctl(bus, CLK_NONE, FALSE); } } @@ -4347,6 +4707,68 @@ dhd_bus_watchdog(dhd_pub_t *dhdp) return bus->ipend; } +#ifdef DHD_DEBUG +extern int +dhd_bus_console_in(dhd_pub_t *dhdp, uchar *msg, uint msglen) +{ + dhd_bus_t *bus = dhdp->bus; + uint32 addr, val; + int rv; + void *pkt; + + /* Address could be zero if CONSOLE := 0 in dongle Makefile */ + if (bus->console_addr == 0) + return BCME_UNSUPPORTED; + + /* Exclusive bus access */ + dhd_os_sdlock(bus->dhd); + + /* Don't allow input if dongle is in reset */ + if (bus->dhd->dongle_reset) { + dhd_os_sdunlock(bus->dhd); + return BCME_NOTREADY; + } + + /* Request clock to allow SDIO accesses */ + BUS_WAKE(bus); + /* No pend allowed since txpkt is called later, ht clk has to be on */ + dhdsdio_clkctl(bus, CLK_AVAIL, FALSE); + + /* Zero cbuf_index */ + addr = bus->console_addr + OFFSETOF(hndrte_cons_t, cbuf_idx); + val = htol32(0); + if ((rv = dhdsdio_membytes(bus, TRUE, addr, (uint8 *)&val, sizeof(val))) < 0) + goto done; + + /* Write message into cbuf */ + addr = bus->console_addr + OFFSETOF(hndrte_cons_t, cbuf); + if ((rv = dhdsdio_membytes(bus, TRUE, addr, (uint8 *)msg, msglen)) < 0) + goto done; + + /* Write length into vcons_in */ + addr = bus->console_addr + OFFSETOF(hndrte_cons_t, vcons_in); + val = htol32(msglen); + if ((rv = dhdsdio_membytes(bus, TRUE, addr, (uint8 *)&val, sizeof(val))) < 0) + goto done; + + /* Bump dongle by sending an empty event pkt. + * sdpcm_sendup (RX) checks for virtual console input. + */ + if (((pkt = PKTGET(bus->dhd->osh, 4 + SDPCM_RESERVE, TRUE)) != NULL) && + bus->clkstate == CLK_AVAIL) + dhdsdio_txpkt(bus, pkt, SDPCM_EVENT_CHANNEL, TRUE); + +done: + if ((bus->idletime == DHD_IDLE_IMMEDIATE) && !bus->dpc_sched) { + bus->activity = FALSE; + dhdsdio_clkctl(bus, CLK_NONE, TRUE); + } + + dhd_os_sdunlock(bus->dhd); + + return rv; +} +#endif /* DHD_DEBUG */ #ifdef DHD_DEBUG static void diff --git a/bcm4329/src/dongle/dngl_stats.h b/bcm4329/src/dongle/dngl_stats.h index c1776a8..e5db54e 100644 --- a/bcm4329/src/dongle/dngl_stats.h +++ b/bcm4329/src/dongle/dngl_stats.h @@ -2,7 +2,7 @@ * Common stats definitions for clients of dongle * ports * - * Copyright (C) 1999-2009, Broadcom Corporation + * Copyright (C) 1999-2010, Broadcom Corporation * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you diff --git a/bcm4329/src/include/aidmp.h b/bcm4329/src/include/aidmp.h index 44b3de8..a927e5d 100644 --- a/bcm4329/src/include/aidmp.h +++ b/bcm4329/src/include/aidmp.h @@ -1,7 +1,7 @@ /* * Broadcom AMBA Interconnect definitions. * - * Copyright (C) 1999-2009, Broadcom Corporation + * Copyright (C) 1999-2010, Broadcom Corporation * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you diff --git a/bcm4329/src/include/bcmcdc.h b/bcm4329/src/include/bcmcdc.h index 641d755..c2a860b 100644 --- a/bcm4329/src/include/bcmcdc.h +++ b/bcm4329/src/include/bcmcdc.h @@ -4,7 +4,7 @@ * * Definitions subject to change without notice. * - * Copyright (C) 1999-2009, Broadcom Corporation + * Copyright (C) 1999-2010, Broadcom Corporation * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you diff --git a/bcm4329/src/include/bcmdevs.h b/bcm4329/src/include/bcmdevs.h index acfd414..14853f1 100644 --- a/bcm4329/src/include/bcmdevs.h +++ b/bcm4329/src/include/bcmdevs.h @@ -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: bcmdevs.h,v 13.172.4.5.4.10.2.28 2010/01/28 06:57:23 Exp $ + * $Id: bcmdevs.h,v 13.172.4.5.4.10.2.36 2010/05/25 08:33:44 Exp $ */ diff --git a/bcm4329/src/include/bcmendian.h b/bcm4329/src/include/bcmendian.h index 38887df..ae46838 100644 --- a/bcm4329/src/include/bcmendian.h +++ b/bcm4329/src/include/bcmendian.h @@ -1,7 +1,7 @@ /* * Byte order utilities * - * Copyright (C) 1999-2009, Broadcom Corporation + * Copyright (C) 1999-2010, Broadcom Corporation * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you diff --git a/bcm4329/src/include/bcmpcispi.h b/bcm4329/src/include/bcmpcispi.h index e3be826..7d98fb7 100644 --- a/bcm4329/src/include/bcmpcispi.h +++ b/bcm4329/src/include/bcmpcispi.h @@ -1,7 +1,7 @@ /* * Broadcom PCI-SPI Host Controller Register Definitions * - * Copyright (C) 1999-2009, Broadcom Corporation + * Copyright (C) 1999-2010, Broadcom Corporation * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you diff --git a/bcm4329/src/include/bcmperf.h b/bcm4329/src/include/bcmperf.h index dfc3f44..2a78784 100644 --- a/bcm4329/src/include/bcmperf.h +++ b/bcm4329/src/include/bcmperf.h @@ -1,7 +1,7 @@ /* * Performance counters software interface. * - * Copyright (C) 1999-2009, Broadcom Corporation + * Copyright (C) 1999-2010, Broadcom Corporation * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you diff --git a/bcm4329/src/include/bcmsdbus.h b/bcm4329/src/include/bcmsdbus.h index 0e629c0..b7b67bc 100644 --- a/bcm4329/src/include/bcmsdbus.h +++ b/bcm4329/src/include/bcmsdbus.h @@ -2,7 +2,7 @@ * Definitions for API from sdio common code (bcmsdh) to individual * host controller drivers. * - * Copyright (C) 1999-2009, Broadcom Corporation + * Copyright (C) 1999-2010, Broadcom Corporation * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you diff --git a/bcm4329/src/include/bcmsdh.h b/bcm4329/src/include/bcmsdh.h index f8ab8ab..f5dee5c 100644 --- a/bcm4329/src/include/bcmsdh.h +++ b/bcm4329/src/include/bcmsdh.h @@ -3,7 +3,7 @@ * export functions to client drivers * abstract OS and BUS specific details of SDIO * - * Copyright (C) 1999-2009, Broadcom Corporation + * Copyright (C) 1999-2010, Broadcom Corporation * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you diff --git a/bcm4329/src/include/bcmsdh_sdmmc.h b/bcm4329/src/include/bcmsdh_sdmmc.h index b572f34..4e6d1b5 100644 --- a/bcm4329/src/include/bcmsdh_sdmmc.h +++ b/bcm4329/src/include/bcmsdh_sdmmc.h @@ -1,7 +1,7 @@ /* * BCMSDH Function Driver for the native SDIO/MMC driver in the Linux Kernel * - * Copyright (C) 1999-2009, Broadcom Corporation + * Copyright (C) 1999-2010, Broadcom Corporation * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you diff --git a/bcm4329/src/include/bcmsdpcm.h b/bcm4329/src/include/bcmsdpcm.h index b63b1d3..77aca45 100644 --- a/bcm4329/src/include/bcmsdpcm.h +++ b/bcm4329/src/include/bcmsdpcm.h @@ -2,7 +2,7 @@ * Broadcom SDIO/PCMCIA * Software-specific definitions shared between device and host side * - * Copyright (C) 1999-2009, Broadcom Corporation + * Copyright (C) 1999-2010, Broadcom Corporation * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you @@ -22,7 +22,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: bcmsdpcm.h,v 1.1.2.3 2009/04/09 18:52:06 Exp $ + * $Id: bcmsdpcm.h,v 1.1.2.4 2010/07/02 01:15:46 Exp $ */ #ifndef _bcmsdpcm_h_ @@ -241,7 +241,7 @@ typedef volatile struct { * Shared structure between dongle and the host * The structure contains pointers to trap or assert information shared with the host */ -#define SDPCM_SHARED_VERSION 0x0001 +#define SDPCM_SHARED_VERSION 0x0002 #define SDPCM_SHARED_VERSION_MASK 0x00FF #define SDPCM_SHARED_ASSERT_BUILT 0x0100 #define SDPCM_SHARED_ASSERT 0x0200 @@ -255,6 +255,7 @@ typedef struct { uint32 assert_line; uint32 console_addr; /* Address of hndrte_cons_t */ uint32 msgtrace_addr; + uint8 tag[32]; } sdpcm_shared_t; extern sdpcm_shared_t sdpcm_shared; diff --git a/bcm4329/src/include/bcmsdspi.h b/bcm4329/src/include/bcmsdspi.h index b1e2be9..eaae10d 100644 --- a/bcm4329/src/include/bcmsdspi.h +++ b/bcm4329/src/include/bcmsdspi.h @@ -1,7 +1,7 @@ /* * SD-SPI Protocol Conversion - BCMSDH->SPI Translation Layer * - * Copyright (C) 1999-2009, Broadcom Corporation + * Copyright (C) 1999-2010, Broadcom Corporation * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you diff --git a/bcm4329/src/include/bcmspi.h b/bcm4329/src/include/bcmspi.h index 0c46538..2e2bc93 100644 --- a/bcm4329/src/include/bcmspi.h +++ b/bcm4329/src/include/bcmspi.h @@ -1,7 +1,7 @@ /* * Broadcom SPI Low-Level Hardware Driver API * - * Copyright (C) 1999-2009, Broadcom Corporation + * Copyright (C) 1999-2010, Broadcom Corporation * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you diff --git a/bcm4329/src/include/bcmutils.h b/bcm4329/src/include/bcmutils.h index 7010838..f85ed35 100644 --- a/bcm4329/src/include/bcmutils.h +++ b/bcm4329/src/include/bcmutils.h @@ -20,7 +20,7 @@ * Notwithstanding the above, under no circumstances may you combine this * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. - * $Id: bcmutils.h,v 13.184.4.6.2.1.18.24 2009/12/10 20:19:19 Exp $ + * $Id: bcmutils.h,v 13.184.4.6.2.1.18.25 2010/04/26 06:05:24 Exp $ */ diff --git a/bcm4329/src/include/bcmwifi.h b/bcm4329/src/include/bcmwifi.h index 52f17a6..038aedc 100644 --- a/bcm4329/src/include/bcmwifi.h +++ b/bcm4329/src/include/bcmwifi.h @@ -42,7 +42,7 @@ typedef uint16 chanspec_t; #define CH_5MHZ_APART 1 #define CH_MAX_2G_CHANNEL 14 #define WLC_MAX_2G_CHANNEL CH_MAX_2G_CHANNEL -#define MAXCHANNEL 224 +#define MAXCHANNEL 224 #define WL_CHANSPEC_CHAN_MASK 0x00ff #define WL_CHANSPEC_CHAN_SHIFT 0 @@ -118,7 +118,7 @@ typedef uint16 chanspec_t; (LOWER_20_SB(((chspec) & WL_CHANSPEC_CHAN_MASK))) : \ (UPPER_20_SB(((chspec) & WL_CHANSPEC_CHAN_MASK)))) -#define CHSPEC2WLC_BAND(chspec) (CHSPEC_IS5G((chspec)) ? WLC_BAND_5G : WLC_BAND_2G) +#define CHSPEC2WLC_BAND(chspec) (CHSPEC_IS5G((chspec))? WLC_BAND_5G: WLC_BAND_2G) #define CHANSPEC_STR_LEN 8 diff --git a/bcm4329/src/include/dhdioctl.h b/bcm4329/src/include/dhdioctl.h index ee78c3d..980a143 100644 --- a/bcm4329/src/include/dhdioctl.h +++ b/bcm4329/src/include/dhdioctl.h @@ -5,7 +5,7 @@ * * Definitions subject to change without notice. * - * Copyright (C) 1999-2009, Broadcom Corporation + * Copyright (C) 1999-2010, Broadcom Corporation * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you @@ -25,7 +25,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: dhdioctl.h,v 13.7.8.1.4.1.16.4 2009/09/05 16:50:35 Exp $ + * $Id: dhdioctl.h,v 13.7.8.1.4.1.16.5 2010/05/21 21:49:38 Exp $ */ #ifndef _dhdioctl_h_ @@ -79,6 +79,7 @@ typedef struct dhd_ioctl { #define DHD_GLOM_VAL 0x0400 #define DHD_EVENT_VAL 0x0800 #define DHD_BTA_VAL 0x1000 +#define DHD_ISCAN_VAL 0x2000 #ifdef SDTEST /* For pktgen iovar */ diff --git a/bcm4329/src/include/epivers.h b/bcm4329/src/include/epivers.h index 3ab7ad7..92dc326 100644 --- a/bcm4329/src/include/epivers.h +++ b/bcm4329/src/include/epivers.h @@ -31,18 +31,18 @@ #define EPI_MINOR_VERSION 218 -#define EPI_RC_NUMBER 223 +#define EPI_RC_NUMBER 248 -#define EPI_INCREMENTAL_NUMBER 0 +#define EPI_INCREMENTAL_NUMBER 6 #define EPI_BUILD_NUMBER 0 -#define EPI_VERSION 4, 218, 223, 0 +#define EPI_VERSION 4, 218, 248, 6 -#define EPI_VERSION_NUM 0x04dadf00 +#define EPI_VERSION_NUM 0x04daf806 -#define EPI_VERSION_STR "4.218.223.0" -#define EPI_ROUTER_VERSION_STR "4.219.223.0" +#define EPI_VERSION_STR "4.218.248.6" +#define EPI_ROUTER_VERSION_STR "4.219.248.6" #endif diff --git a/bcm4329/src/include/hndpmu.h b/bcm4329/src/include/hndpmu.h index afd78d7..e829b3d 100644 --- a/bcm4329/src/include/hndpmu.h +++ b/bcm4329/src/include/hndpmu.h @@ -1,7 +1,7 @@ /* * HND SiliconBackplane PMU support. * - * Copyright (C) 1999-2009, Broadcom Corporation + * Copyright (C) 1999-2010, 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: hndpmu.h,v 13.14.4.3.4.3.8.6 2009/09/14 09:21:45 Exp $ + * $Id: hndpmu.h,v 13.14.4.3.4.3.8.7 2010/04/09 13:20:51 Exp $ */ #ifndef _hndpmu_h_ diff --git a/bcm4329/src/include/hndrte_armtrap.h b/bcm4329/src/include/hndrte_armtrap.h new file mode 100644 index 0000000..ca3281b --- /dev/null +++ b/bcm4329/src/include/hndrte_armtrap.h @@ -0,0 +1,88 @@ +/* + * HNDRTE arm trap handling. + * + * Copyright (C) 1999-2010, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: hndrte_armtrap.h,v 13.3.196.2 2010/07/15 19:06:11 Exp $ + */ + +#ifndef _hndrte_armtrap_h +#define _hndrte_armtrap_h + + +/* ARM trap handling */ + +/* Trap types defined by ARM (see arminc.h) */ + +/* Trap locations in lo memory */ +#define TRAP_STRIDE 4 +#define FIRST_TRAP TR_RST +#define LAST_TRAP (TR_FIQ * TRAP_STRIDE) + +#if defined(__ARM_ARCH_4T__) +#define MAX_TRAP_TYPE (TR_FIQ + 1) +#elif defined(__ARM_ARCH_7M__) +#define MAX_TRAP_TYPE (TR_ISR + ARMCM3_NUMINTS) +#endif /* __ARM_ARCH_7M__ */ + +/* The trap structure is defined here as offsets for assembly */ +#define TR_TYPE 0x00 +#define TR_EPC 0x04 +#define TR_CPSR 0x08 +#define TR_SPSR 0x0c +#define TR_REGS 0x10 +#define TR_REG(n) (TR_REGS + (n) * 4) +#define TR_SP TR_REG(13) +#define TR_LR TR_REG(14) +#define TR_PC TR_REG(15) + +#define TRAP_T_SIZE 80 + +#ifndef _LANGUAGE_ASSEMBLY + +#include <typedefs.h> + +typedef struct _trap_struct { + uint32 type; + uint32 epc; + uint32 cpsr; + uint32 spsr; + uint32 r0; + uint32 r1; + uint32 r2; + uint32 r3; + uint32 r4; + uint32 r5; + uint32 r6; + uint32 r7; + uint32 r8; + uint32 r9; + uint32 r10; + uint32 r11; + uint32 r12; + uint32 r13; + uint32 r14; + uint32 pc; +} trap_t; + +#endif /* !_LANGUAGE_ASSEMBLY */ + +#endif /* _hndrte_armtrap_h */ diff --git a/bcm4329/src/include/hndrte_cons.h b/bcm4329/src/include/hndrte_cons.h new file mode 100644 index 0000000..a424174 --- /dev/null +++ b/bcm4329/src/include/hndrte_cons.h @@ -0,0 +1,63 @@ +/* + * Console support for hndrte. + * + * Copyright (C) 1999-2010, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: hndrte_cons.h,v 13.1.2.4 2010/07/15 19:06:11 Exp $ + */ + +#include <typedefs.h> + +#define CBUF_LEN (128) + +#define LOG_BUF_LEN 1024 + +typedef struct { + uint32 buf; /* Can't be pointer on (64-bit) hosts */ + uint buf_size; + uint idx; + char *_buf_compat; /* Redundant pointer for backward compat. */ +} hndrte_log_t; + +typedef struct { + /* Virtual UART + * When there is no UART (e.g. Quickturn), the host should write a complete + * input line directly into cbuf and then write the length into vcons_in. + * This may also be used when there is a real UART (at risk of conflicting with + * the real UART). vcons_out is currently unused. + */ + volatile uint vcons_in; + volatile uint vcons_out; + + /* Output (logging) buffer + * Console output is written to a ring buffer log_buf at index log_idx. + * The host may read the output when it sees log_idx advance. + * Output will be lost if the output wraps around faster than the host polls. + */ + hndrte_log_t log; + + /* Console input line buffer + * Characters are read one at a time into cbuf until <CR> is received, then + * the buffer is processed as a command line. Also used for virtual UART. + */ + uint cbuf_idx; + char cbuf[CBUF_LEN]; +} hndrte_cons_t; diff --git a/bcm4329/src/include/hndsoc.h b/bcm4329/src/include/hndsoc.h index dcbab43..3542417 100644 --- a/bcm4329/src/include/hndsoc.h +++ b/bcm4329/src/include/hndsoc.h @@ -1,7 +1,7 @@ /* * Broadcom HND chip & on-chip-interconnect-related definitions. * - * Copyright (C) 1999-2009, Broadcom Corporation + * Copyright (C) 1999-2010, Broadcom Corporation * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you diff --git a/bcm4329/src/include/linux_osl.h b/bcm4329/src/include/linux_osl.h index 98afe9c..b059c2a 100644 --- a/bcm4329/src/include/linux_osl.h +++ b/bcm4329/src/include/linux_osl.h @@ -1,7 +1,7 @@ /* * Linux OS Independent Layer * - * Copyright (C) 1999-2009, Broadcom Corporation + * Copyright (C) 1999-2010, 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: linux_osl.h,v 13.131.30.5 2009/10/27 04:42:45 Exp $ + * $Id: linux_osl.h,v 13.131.30.8 2010/04/26 05:42:18 Exp $ */ @@ -319,5 +319,4 @@ extern int osl_error(int bcmerror); #define OSL_SYSUPTIME() ((uint32)jiffies * (1000 / HZ)) - #endif diff --git a/bcm4329/src/include/linuxver.h b/bcm4329/src/include/linuxver.h index a36de95..6ed2265 100644 --- a/bcm4329/src/include/linuxver.h +++ b/bcm4329/src/include/linuxver.h @@ -2,7 +2,7 @@ * Linux-specific abstractions to gain some independence from linux kernel versions. * Pave over some 2.2 versus 2.4 versus 2.6 kernel differences. * - * Copyright (C) 1999-2009, Broadcom Corporation + * Copyright (C) 1999-2010, Broadcom Corporation * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you @@ -22,7 +22,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: linuxver.h,v 13.38.8.1.8.3 2009/06/19 04:42:45 Exp $ + * $Id: linuxver.h,v 13.38.8.1.8.6 2010/04/29 05:00:46 Exp $ */ @@ -32,7 +32,7 @@ #include <linux/version.h> #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)) #include <linux/config.h> -#else +#elif (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33)) #include <linux/autoconf.h> #endif #include <linux/module.h> @@ -66,6 +66,7 @@ #include <linux/pci.h> #include <linux/interrupt.h> #include <linux/netdevice.h> +#include <linux/semaphore.h> #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) #undef IP_TOS #endif diff --git a/bcm4329/src/include/miniopt.h b/bcm4329/src/include/miniopt.h index 91e6603..3667fb1 100644 --- a/bcm4329/src/include/miniopt.h +++ b/bcm4329/src/include/miniopt.h @@ -1,7 +1,7 @@ /* * Command line options parser. * - * Copyright (C) 1999-2009, Broadcom Corporation + * Copyright (C) 1999-2010, Broadcom Corporation * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you diff --git a/bcm4329/src/include/msgtrace.h b/bcm4329/src/include/msgtrace.h index d79a89e..1479086 100644 --- a/bcm4329/src/include/msgtrace.h +++ b/bcm4329/src/include/msgtrace.h @@ -1,7 +1,7 @@ /* * Trace messages sent over HBUS * - * Copyright (C) 1999-2009, Broadcom Corporation + * Copyright (C) 1999-2010, Broadcom Corporation * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you diff --git a/bcm4329/src/include/osl.h b/bcm4329/src/include/osl.h index 02b2b21..5599e53 100644 --- a/bcm4329/src/include/osl.h +++ b/bcm4329/src/include/osl.h @@ -1,7 +1,7 @@ /* * OS Abstraction Layer * - * Copyright (C) 1999-2009, Broadcom Corporation + * Copyright (C) 1999-2010, Broadcom Corporation * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you diff --git a/bcm4329/src/include/packed_section_end.h b/bcm4329/src/include/packed_section_end.h index e455d69..5b61c18 100644 --- a/bcm4329/src/include/packed_section_end.h +++ b/bcm4329/src/include/packed_section_end.h @@ -15,7 +15,7 @@ * #include <packed_section_end.h> * * - * Copyright (C) 1999-2009, Broadcom Corporation + * Copyright (C) 1999-2010, Broadcom Corporation * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you diff --git a/bcm4329/src/include/packed_section_start.h b/bcm4329/src/include/packed_section_start.h index a15562e..cb93aa6 100644 --- a/bcm4329/src/include/packed_section_start.h +++ b/bcm4329/src/include/packed_section_start.h @@ -15,7 +15,7 @@ * #include <packed_section_end.h> * * - * Copyright (C) 1999-2009, Broadcom Corporation + * Copyright (C) 1999-2010, Broadcom Corporation * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you diff --git a/bcm4329/src/include/pcicfg.h b/bcm4329/src/include/pcicfg.h index 61c2f4a..898962c 100644 --- a/bcm4329/src/include/pcicfg.h +++ b/bcm4329/src/include/pcicfg.h @@ -1,7 +1,7 @@ /* * pcicfg.h: PCI configuration constants and structures. * - * Copyright (C) 1999-2009, Broadcom Corporation + * Copyright (C) 1999-2010, Broadcom Corporation * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you diff --git a/bcm4329/src/include/proto/802.11.h b/bcm4329/src/include/proto/802.11.h index 3ef7de2..fd26317 100644 --- a/bcm4329/src/include/proto/802.11.h +++ b/bcm4329/src/include/proto/802.11.h @@ -976,6 +976,7 @@ BWL_PRE_PACKED_STRUCT struct dot11_management_notification { #define DOT11_MNG_EXT_CSA_ID 60 #define DOT11_MNG_HT_ADD 61 #define DOT11_MNG_EXT_CHANNEL_OFFSET 62 +#define DOT11_MNG_WAPI_ID 68 #define DOT11_MNG_HT_BSS_COEXINFO_ID 72 #define DOT11_MNG_HT_BSS_CHANNEL_REPORT_ID 73 #define DOT11_MNG_HT_OBSS_ID 74 @@ -1422,6 +1423,8 @@ typedef struct vndr_ie vndr_ie_t; #define AES_KEY_SIZE 16 #define AES_MIC_SIZE 8 +#define SMS4_KEY_LEN 16 +#define SMS4_WPI_CBC_MAC_LEN 16 #include <packed_section_end.h> diff --git a/bcm4329/src/include/proto/802.11e.h b/bcm4329/src/include/proto/802.11e.h index 0c53530..1dd6f45 100644 --- a/bcm4329/src/include/proto/802.11e.h +++ b/bcm4329/src/include/proto/802.11e.h @@ -1,7 +1,7 @@ /* * 802.11e protocol header file * - * Copyright (C) 1999-2009, Broadcom Corporation + * Copyright (C) 1999-2010, Broadcom Corporation * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you diff --git a/bcm4329/src/include/proto/802.1d.h b/bcm4329/src/include/proto/802.1d.h index efd7117..45c728b 100644 --- a/bcm4329/src/include/proto/802.1d.h +++ b/bcm4329/src/include/proto/802.1d.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999-2009, Broadcom Corporation + * Copyright (C) 1999-2010, Broadcom Corporation * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you diff --git a/bcm4329/src/include/proto/bcmeth.h b/bcm4329/src/include/proto/bcmeth.h index bd34a0b..fdb5a2a 100644 --- a/bcm4329/src/include/proto/bcmeth.h +++ b/bcm4329/src/include/proto/bcmeth.h @@ -1,7 +1,7 @@ /* * Broadcom Ethernettype protocol definitions * - * Copyright (C) 1999-2009, Broadcom Corporation + * Copyright (C) 1999-2010, Broadcom Corporation * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you diff --git a/bcm4329/src/include/proto/bcmevent.h b/bcm4329/src/include/proto/bcmevent.h index 1791478..46c04d3 100644 --- a/bcm4329/src/include/proto/bcmevent.h +++ b/bcm4329/src/include/proto/bcmevent.h @@ -1,7 +1,7 @@ /* * Broadcom Event protocol definitions * - * Copyright (C) 1999-2009, Broadcom Corporation + * Copyright (C) 1999-2010, Broadcom Corporation * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you diff --git a/bcm4329/src/include/proto/bcmip.h b/bcm4329/src/include/proto/bcmip.h index 9645016..9d2fd6f 100644 --- a/bcm4329/src/include/proto/bcmip.h +++ b/bcm4329/src/include/proto/bcmip.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999-2009, Broadcom Corporation + * Copyright (C) 1999-2010, Broadcom Corporation * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you diff --git a/bcm4329/src/include/proto/ethernet.h b/bcm4329/src/include/proto/ethernet.h index 05530d5..9ad2ea0 100644 --- a/bcm4329/src/include/proto/ethernet.h +++ b/bcm4329/src/include/proto/ethernet.h @@ -1,7 +1,7 @@ /* * From FreeBSD 2.2.7: Fundamental constants relating to ethernet. * - * Copyright (C) 1999-2009, Broadcom Corporation + * Copyright (C) 1999-2010, 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: ethernet.h,v 9.45.56.3 2009/08/15 00:51:27 Exp $ + * $Id: ethernet.h,v 9.45.56.5 2010/02/22 22:04:36 Exp $ */ @@ -67,6 +67,7 @@ #define ETHER_TYPE_8021Q 0x8100 #define ETHER_TYPE_BRCM 0x886c #define ETHER_TYPE_802_1X 0x888e +#define ETHER_TYPE_WAI 0x88b4 #ifdef BCMWPA2 #define ETHER_TYPE_802_1X_PREAUTH 0x88c7 #endif diff --git a/bcm4329/src/include/proto/sdspi.h b/bcm4329/src/include/proto/sdspi.h index 3c7bcf3..7739e68 100644 --- a/bcm4329/src/include/proto/sdspi.h +++ b/bcm4329/src/include/proto/sdspi.h @@ -1,7 +1,7 @@ /* * SD-SPI Protocol Standard * - * Copyright (C) 1999-2009, Broadcom Corporation + * Copyright (C) 1999-2010, Broadcom Corporation * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you diff --git a/bcm4329/src/include/proto/vlan.h b/bcm4329/src/include/proto/vlan.h index 2dda4d5..670bc44 100644 --- a/bcm4329/src/include/proto/vlan.h +++ b/bcm4329/src/include/proto/vlan.h @@ -1,7 +1,7 @@ /* * 802.1Q VLAN protocol definitions * - * Copyright (C) 1999-2009, Broadcom Corporation + * Copyright (C) 1999-2010, Broadcom Corporation * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you diff --git a/bcm4329/src/include/proto/wpa.h b/bcm4329/src/include/proto/wpa.h index 4c50125..f5d0cd5 100644 --- a/bcm4329/src/include/proto/wpa.h +++ b/bcm4329/src/include/proto/wpa.h @@ -1,7 +1,7 @@ /* * Fundamental types and constants relating to WPA * - * Copyright (C) 1999-2009, Broadcom Corporation + * Copyright (C) 1999-2010, Broadcom Corporation * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you diff --git a/bcm4329/src/include/sbconfig.h b/bcm4329/src/include/sbconfig.h index edf497c..da18ccb 100644 --- a/bcm4329/src/include/sbconfig.h +++ b/bcm4329/src/include/sbconfig.h @@ -1,7 +1,7 @@ /* * Broadcom SiliconBackplane hardware register definitions. * - * Copyright (C) 1999-2009, Broadcom Corporation + * Copyright (C) 1999-2010, Broadcom Corporation * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you diff --git a/bcm4329/src/include/sbhnddma.h b/bcm4329/src/include/sbhnddma.h index 2aeee9e..7681395 100644 --- a/bcm4329/src/include/sbhnddma.h +++ b/bcm4329/src/include/sbhnddma.h @@ -2,7 +2,7 @@ * Generic Broadcom Home Networking Division (HND) DMA engine HW interface * This supports the following chips: BCM42xx, 44xx, 47xx . * - * Copyright (C) 1999-2009, Broadcom Corporation + * Copyright (C) 1999-2010, Broadcom Corporation * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you diff --git a/bcm4329/src/include/sbpcmcia.h b/bcm4329/src/include/sbpcmcia.h index 121b1a9..d6d8033 100644 --- a/bcm4329/src/include/sbpcmcia.h +++ b/bcm4329/src/include/sbpcmcia.h @@ -1,7 +1,7 @@ /* * BCM43XX Sonics SiliconBackplane PCMCIA core hardware definitions. * - * Copyright (C) 1999-2009, Broadcom Corporation + * Copyright (C) 1999-2010, Broadcom Corporation * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you diff --git a/bcm4329/src/include/sbsdio.h b/bcm4329/src/include/sbsdio.h index 43ed6b5..75aaf4d 100644 --- a/bcm4329/src/include/sbsdio.h +++ b/bcm4329/src/include/sbsdio.h @@ -4,7 +4,7 @@ * * SDIO core support 1bit, 4 bit SDIO mode as well as SPI mode. * - * Copyright (C) 1999-2009, Broadcom Corporation + * Copyright (C) 1999-2010, Broadcom Corporation * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you diff --git a/bcm4329/src/include/sbsdpcmdev.h b/bcm4329/src/include/sbsdpcmdev.h index 57e832d..7c7c7e4 100644 --- a/bcm4329/src/include/sbsdpcmdev.h +++ b/bcm4329/src/include/sbsdpcmdev.h @@ -1,7 +1,7 @@ /* * Broadcom SiliconBackplane SDIO/PCMCIA hardware-specific device core support * - * Copyright (C) 1999-2009, Broadcom Corporation + * Copyright (C) 1999-2010, Broadcom Corporation * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you diff --git a/bcm4329/src/include/sbsocram.h b/bcm4329/src/include/sbsocram.h index 170e8f2..5ede0b6 100644 --- a/bcm4329/src/include/sbsocram.h +++ b/bcm4329/src/include/sbsocram.h @@ -1,7 +1,7 @@ /* * BCM47XX Sonics SiliconBackplane embedded ram core * - * Copyright (C) 1999-2009, Broadcom Corporation + * Copyright (C) 1999-2010, Broadcom Corporation * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you diff --git a/bcm4329/src/include/sdio.h b/bcm4329/src/include/sdio.h index d20d69c..280cb84 100644 --- a/bcm4329/src/include/sdio.h +++ b/bcm4329/src/include/sdio.h @@ -2,7 +2,7 @@ * SDIO spec header file * Protocol and standard (common) device definitions * - * Copyright (C) 1999-2009, Broadcom Corporation + * Copyright (C) 1999-2010, Broadcom Corporation * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you diff --git a/bcm4329/src/include/siutils.h b/bcm4329/src/include/siutils.h index c55293b..cb9f140 100644 --- a/bcm4329/src/include/siutils.h +++ b/bcm4329/src/include/siutils.h @@ -22,7 +22,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: siutils.h,v 13.197.4.2.4.3.8.14 2010/03/19 18:31:43 Exp $ + * $Id: siutils.h,v 13.197.4.2.4.3.8.16 2010/06/23 21:36:05 Exp $ */ diff --git a/bcm4329/src/include/trxhdr.h b/bcm4329/src/include/trxhdr.h index b79ac35..8f5eed9 100644 --- a/bcm4329/src/include/trxhdr.h +++ b/bcm4329/src/include/trxhdr.h @@ -1,7 +1,7 @@ /* * TRX image file header format. * - * Copyright (C) 1999-2009, Broadcom Corporation + * Copyright (C) 1999-2010, Broadcom Corporation * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you diff --git a/bcm4329/src/include/typedefs.h b/bcm4329/src/include/typedefs.h index e4daeee..4d9dd76 100644 --- a/bcm4329/src/include/typedefs.h +++ b/bcm4329/src/include/typedefs.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999-2009, Broadcom Corporation + * Copyright (C) 1999-2010, Broadcom Corporation * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you diff --git a/bcm4329/src/include/wlioctl.h b/bcm4329/src/include/wlioctl.h index 58a2f6c..345ba34 100644 --- a/bcm4329/src/include/wlioctl.h +++ b/bcm4329/src/include/wlioctl.h @@ -24,7 +24,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: wlioctl.h,v 1.601.4.15.2.14.2.60 2010/04/12 05:33:02 Exp $ + * $Id: wlioctl.h,v 1.601.4.15.2.14.2.62 2010/08/19 01:20:12 Exp $ */ @@ -288,6 +288,7 @@ typedef enum sup_auth_status { #define CRYPTO_ALGO_AES_OCB_MSDU 5 #define CRYPTO_ALGO_AES_OCB_MPDU 6 #define CRYPTO_ALGO_NALG 7 +#define CRYPTO_ALGO_SMS4 11 #define WSEC_GEN_MIC_ERROR 0x0001 #define WSEC_GEN_REPLAY 0x0002 @@ -338,6 +339,7 @@ typedef struct { #define AES_ENABLED 0x0004 #define WSEC_SWFLAG 0x0008 #define SES_OW_ENABLED 0x0040 +#define SMS4_ENABLED 0x0100 #define WPA_AUTH_DISABLED 0x0000 @@ -349,6 +351,7 @@ typedef struct { #define WPA2_AUTH_PSK 0x0080 #define BRCM_AUTH_PSK 0x0100 #define BRCM_AUTH_DPT 0x0200 +#define WPA_AUTH_WAPI 0x0400 #define WPA_AUTH_PFN_ANY 0xffffffff @@ -1314,6 +1317,8 @@ enum { #define PFN_VERSION 1 +#define MAX_PFN_LIST_COUNT 16 + typedef struct wl_pfn_param { int32 version; @@ -1324,12 +1329,12 @@ typedef struct wl_pfn_param { } wl_pfn_param_t; typedef struct wl_pfn { - wlc_ssid_t ssid; - int32 bss_type; - int32 infra; - int32 auth; - uint32 wpa_auth; - int32 wsec; + wlc_ssid_t ssid; + int32 bss_type; + int32 infra; + int32 auth; + uint32 wpa_auth; + int32 wsec; #ifdef WLPFN_AUTO_CONNECT union { wl_wsec_key_t sec_key; diff --git a/bcm4329/src/shared/aiutils.c b/bcm4329/src/shared/aiutils.c index 1962711..df48ac0 100644 --- a/bcm4329/src/shared/aiutils.c +++ b/bcm4329/src/shared/aiutils.c @@ -2,7 +2,7 @@ * Misc utility routines for accessing chip-specific features * of the SiliconBackplane-based Broadcom chips. * - * Copyright (C) 1999-2009, Broadcom Corporation + * Copyright (C) 1999-2010, Broadcom Corporation * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you @@ -22,7 +22,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: aiutils.c,v 1.6.4.7.4.5 2009/09/25 00:32:01 Exp $ + * $Id: aiutils.c,v 1.6.4.7.4.6 2010/04/21 20:43:47 Exp $ */ #include <typedefs.h> diff --git a/bcm4329/src/shared/bcmutils.c b/bcm4329/src/shared/bcmutils.c index c553d8e..43c04ee 100644 --- a/bcm4329/src/shared/bcmutils.c +++ b/bcm4329/src/shared/bcmutils.c @@ -20,7 +20,7 @@ * Notwithstanding the above, under no circumstances may you combine this * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. - * $Id: bcmutils.c,v 1.210.4.5.2.4.6.17 2009/11/17 02:20:12 Exp $ + * $Id: bcmutils.c,v 1.210.4.5.2.4.6.19 2010/04/26 06:05:25 Exp $ */ #include <typedefs.h> diff --git a/bcm4329/src/shared/bcmwifi.c b/bcm4329/src/shared/bcmwifi.c index 641a4fd..803acf8 100644 --- a/bcm4329/src/shared/bcmwifi.c +++ b/bcm4329/src/shared/bcmwifi.c @@ -3,7 +3,7 @@ * Contents are wifi-specific, used by any kernel or app-level * software that might want wifi things as it grows. * - * Copyright (C) 1999-2009, Broadcom Corporation + * Copyright (C) 1999-2010, Broadcom Corporation * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you diff --git a/bcm4329/src/shared/hndpmu.c b/bcm4329/src/shared/hndpmu.c index 25712ac..307347a 100644 --- a/bcm4329/src/shared/hndpmu.c +++ b/bcm4329/src/shared/hndpmu.c @@ -2,7 +2,7 @@ * Misc utility routines for accessing PMU corerev specific features * of the SiliconBackplane-based Broadcom chips. * - * Copyright (C) 1999-2009, Broadcom Corporation + * Copyright (C) 1999-2010, Broadcom Corporation * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you @@ -22,7 +22,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: hndpmu.c,v 1.95.2.17.4.11.2.50 2009/10/26 14:45:51 Exp $ + * $Id: hndpmu.c,v 1.95.2.17.4.11.2.63 2010/07/21 13:55:09 Exp $ */ #include <typedefs.h> diff --git a/bcm4329/src/shared/miniopt.c b/bcm4329/src/shared/miniopt.c index 44199ea..6a184a7 100644 --- a/bcm4329/src/shared/miniopt.c +++ b/bcm4329/src/shared/miniopt.c @@ -1,7 +1,7 @@ /* * Description. * - * Copyright (C) 1999-2009, Broadcom Corporation + * Copyright (C) 1999-2010, Broadcom Corporation * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you diff --git a/bcm4329/src/shared/sbutils.c b/bcm4329/src/shared/sbutils.c index 567e94e..46cd510 100644 --- a/bcm4329/src/shared/sbutils.c +++ b/bcm4329/src/shared/sbutils.c @@ -2,7 +2,7 @@ * Misc utility routines for accessing chip-specific features * of the SiliconBackplane-based Broadcom chips. * - * Copyright (C) 1999-2009, Broadcom Corporation + * Copyright (C) 1999-2010, Broadcom Corporation * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you @@ -22,7 +22,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: sbutils.c,v 1.662.4.10.2.7.4.1 2009/09/25 00:32:01 Exp $ + * $Id: sbutils.c,v 1.662.4.10.2.7.4.2 2010/04/19 05:48:48 Exp $ */ #include <typedefs.h> diff --git a/bcm4329/src/shared/siutils.c b/bcm4329/src/shared/siutils.c index 6472a78..1814db0 100644 --- a/bcm4329/src/shared/siutils.c +++ b/bcm4329/src/shared/siutils.c @@ -22,7 +22,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: siutils.c,v 1.662.4.4.4.16.4.26 2010/02/01 05:51:56 Exp $ + * $Id: siutils.c,v 1.662.4.4.4.16.4.28 2010/06/23 21:37:54 Exp $ */ #include <typedefs.h> diff --git a/bcm4329/src/shared/siutils_priv.h b/bcm4329/src/shared/siutils_priv.h index 1ec59aa..e8ad7e5 100644 --- a/bcm4329/src/shared/siutils_priv.h +++ b/bcm4329/src/shared/siutils_priv.h @@ -1,7 +1,7 @@ /* * Include file private to the SOC Interconnect support files. * - * Copyright (C) 1999-2009, Broadcom Corporation + * Copyright (C) 1999-2010, Broadcom Corporation * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you diff --git a/bcm4329/src/wl/sys/wl_cfg80211.c b/bcm4329/src/wl/sys/wl_cfg80211.c new file mode 100644 index 0000000..81491cb --- /dev/null +++ b/bcm4329/src/wl/sys/wl_cfg80211.c @@ -0,0 +1,4491 @@ +/* + * Linux Cfg80211 support + * + * Copyright (C) 1999-2010, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: wl_cfg80211.c,v 1.1.2.28 2010/05/04 21:43:38 Exp $ + */ + +#include <typedefs.h> +#include <linuxver.h> +#include <osl.h> + +#include <bcmutils.h> +#include <bcmendian.h> +#include <proto/ethernet.h> + +#include <linux/if_arp.h> +#include <asm/uaccess.h> + +#include <dngl_stats.h> +#include <dhd.h> +#include <dhdioctl.h> +#include <wlioctl.h> + +#include <proto/ethernet.h> +#include <dngl_stats.h> +#include <dhd.h> + +#include <linux/kernel.h> +#include <linux/netdevice.h> +#include <linux/sched.h> +#include <linux/etherdevice.h> +#include <linux/wireless.h> +#include <linux/ieee80211.h> +#include <net/cfg80211.h> + +#include <net/rtnetlink.h> +#include <linux/mmc/sdio_func.h> +#include <linux/firmware.h> +#include <wl_cfg80211.h> + +static struct sdio_func *cfg80211_sdio_func = NULL; +static struct wl_dev *wl_cfg80211_dev = NULL; + +#ifdef WL_CFG80211_BACKTRACE +uint32 wl_dbg_level = WL_DBG_ERR | WL_DBG_INFO | WL_DBG_DBG; +#else +uint32 wl_dbg_level = WL_DBG_ERR | WL_DBG_INFO; +#endif + +#define WL_4329_FW_FILE "brcm/fw_4329.bin" +#define WL_4329_NVRAM_FILE "brcm/nvram_4329.txt" + +/* +** cfg80211_ops api/callback list +*/ +static int32 wl_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev, + enum nl80211_iftype type, uint32 *flags, struct vif_params *params); +static int32 __wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, + struct cfg80211_scan_request *request, struct cfg80211_ssid *this_ssid); +static int32 wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, + struct cfg80211_scan_request *request); +static int32 wl_cfg80211_set_wiphy_params(struct wiphy *wiphy, uint32 changed); +static int32 wl_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_ibss_params *params); +static int32 wl_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev); +static int32 wl_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev, + uint8 *mac, struct station_info *sinfo); +static int32 wl_cfg80211_set_power_mgmt(struct wiphy *wiphy, + struct net_device *dev, bool enabled, int32 timeout); +static int32 wl_cfg80211_set_bitrate_mask(struct wiphy *wiphy, + struct net_device *dev, const uint8 *addr, + const struct cfg80211_bitrate_mask *mask); +static int wl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_connect_params *sme); +static int32 wl_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev, + uint16 reason_code); +static int32 wl_cfg80211_set_tx_power(struct wiphy *wiphy, enum tx_power_setting type, int32 dbm); +static int32 wl_cfg80211_get_tx_power(struct wiphy *wiphy, int32 *dbm); +static int32 wl_cfg80211_config_default_key(struct wiphy *wiphy, struct net_device *dev, + uint8 key_idx); +static int32 wl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *dev, + uint8 key_idx, const uint8 *mac_addr, struct key_params *params); +static int32 wl_cfg80211_del_key(struct wiphy *wiphy, struct net_device *dev, + uint8 key_idx, const uint8 *mac_addr); +static int32 wl_cfg80211_get_key(struct wiphy *wiphy, struct net_device *dev, + uint8 key_idx, const uint8 *mac_addr, void *cookie, + void (*callback)(void *cookie, struct key_params *params)); +static int32 wl_cfg80211_config_default_mgmt_key(struct wiphy *wiphy, + struct net_device *dev, uint8 key_idx); +static int32 wl_cfg80211_resume(struct wiphy *wiphy); +static int32 wl_cfg80211_suspend(struct wiphy *wiphy); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33)) || \ + defined(CHROMIUMOS_COMPAT_WIRELESS) +static int32 wl_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_pmksa *pmksa); +static int32 wl_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_pmksa *pmksa); +static int32 wl_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *dev); +#endif +/* +** event & event Q handlers for cfg80211 interfaces +*/ +static int32 wl_create_event_handler(struct wl_priv *wl); +static void wl_destroy_event_handler(struct wl_priv *wl); +static int32 wl_event_handler(void *data); +static void wl_init_eq(struct wl_priv *wl); +static void wl_flush_eq(struct wl_priv *wl); +static void wl_lock_eq(struct wl_priv *wl); +static void wl_unlock_eq(struct wl_priv *wl); +static void wl_init_eq_lock(struct wl_priv *wl); +static void wl_init_eloop_handler(struct wl_event_loop *el); +static struct wl_event_q *wl_deq_event(struct wl_priv *wl); +static int32 wl_enq_event(struct wl_priv *wl, uint32 type, const wl_event_msg_t *msg, void *data); +static void wl_put_event(struct wl_event_q *e); +static void wl_wakeup_event(struct wl_priv *wl); +static int32 wl_notify_connect_status(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void* data); +static int32 wl_notify_roaming_status(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void* data); +static int32 wl_notify_scan_status(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void* data); +static int32 wl_bss_connect_done(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void* data); +static int32 wl_bss_roaming_done(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void* data); +static int32 wl_notify_mic_status(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void* data); + +/* +** register/deregister sdio function +*/ +static struct sdio_func *wl_sdio_func(void); +static void wl_clear_sdio_func(void); + +/* +** ioctl utilites +*/ +static int32 wl_dev_bufvar_get(struct net_device *dev, int8 *name, int8 *buf, int32 buf_len); +static __used int32 wl_dev_bufvar_set(struct net_device *dev, int8 *name, int8 *buf, int32 len); +static int32 wl_dev_intvar_set(struct net_device *dev, int8 *name, int32 val); +static int32 wl_dev_intvar_get(struct net_device *dev, int8 *name, int32 *retval); +static int32 wl_dev_ioctl(struct net_device *dev, uint32 cmd, void *arg, uint32 len); + + +/* +** cfg80211 set_wiphy_params utilities +*/ +static int32 wl_set_frag(struct net_device *dev, uint32 frag_threshold); +static int32 wl_set_rts(struct net_device *dev, uint32 frag_threshold); +static int32 wl_set_retry(struct net_device *dev, uint32 retry, bool l); + +/* +** wl profile utilities +*/ +static int32 wl_update_prof(struct wl_priv *wl, const wl_event_msg_t *e, void *data, int32 item); +static void * wl_read_prof(struct wl_priv *wl, int32 item); +static void wl_init_prof(struct wl_profile *prof); + + +/* +** cfg80211 connect utilites +*/ +static int32 wl_set_wpa_version(struct net_device *dev, struct cfg80211_connect_params *sme); +static int32 wl_set_auth_type(struct net_device *dev, struct cfg80211_connect_params *sme); +static int32 wl_set_set_cipher(struct net_device *dev, struct cfg80211_connect_params *sme); +static int32 wl_set_key_mgmt(struct net_device *dev, struct cfg80211_connect_params *sme); +static int32 wl_set_set_sharedkey(struct net_device *dev, struct cfg80211_connect_params *sme); +static int32 wl_get_assoc_ies(struct wl_priv *wl); + + +/* +** information element utilities +*/ +static void wl_rst_ie(struct wl_priv *wl); +static int32 wl_add_ie(struct wl_priv *wl, uint8 t, uint8 l, uint8 *v); +static int32 wl_mrg_ie(struct wl_priv *wl, uint8 *ie_stream, uint16 ie_size); +static int32 wl_cp_ie(struct wl_priv *wl, uint8 *dst, uint16 dst_size); +static uint32 wl_get_ielen(struct wl_priv *wl); + + +static int32 wl_mode_to_nl80211_iftype(int32 mode); + +static struct wireless_dev *wl_alloc_wdev(int32 sizeof_iface, struct device *dev); +static void wl_free_wdev(struct wl_priv *wl); + +static int32 wl_inform_bss(struct wl_priv *wl); +static int32 wl_inform_single_bss(struct wl_priv *wl, struct wl_bss_info *bi); +static int32 wl_update_bss_info(struct wl_priv *wl); + +static int32 wl_add_keyext(struct wiphy *wiphy, struct net_device *dev, + uint8 key_idx, const uint8 *mac_addr, struct key_params *params); + + +/* +** key indianess swap utilities +*/ +static void swap_key_from_BE(struct wl_wsec_key *key); +static void swap_key_to_BE(struct wl_wsec_key *key); + + +/* +** wl_priv memory init/deinit utilities +*/ +static int32 wl_init_priv_mem(struct wl_priv *wl); +static void wl_deinit_priv_mem(struct wl_priv *wl); + +static void wl_delay(uint32 ms); + +/* +** store/restore cfg80211 instance data +*/ +static void wl_set_drvdata(struct wl_dev *dev, void *data); +static void *wl_get_drvdata(struct wl_dev *dev); + +/* +** ibss mode utilities +*/ +static bool wl_is_ibssmode(struct wl_priv *wl); +static bool wl_is_ibssstarter(struct wl_priv *wl); + +/* +** dongle up/down , default configuration utilities +*/ +static bool wl_is_linkdown(struct wl_priv *wl, const wl_event_msg_t *e); +static bool wl_is_linkup(struct wl_priv *wl, const wl_event_msg_t *e); +static void wl_link_up(struct wl_priv *wl); +static void wl_link_down(struct wl_priv *wl); +static int32 wl_dongle_mode(struct net_device *ndev, int32 iftype); +static int32 __wl_cfg80211_up(struct wl_priv *wl); +static int32 __wl_cfg80211_down(struct wl_priv *wl); +static int32 wl_dongle_probecap(struct wl_priv *wl); +static void wl_init_conf(struct wl_conf *conf); + +/* +** dongle configuration utilities +*/ +#ifndef EMBEDDED_PLATFORM +static int32 wl_dongle_mode(struct net_device *ndev, int32 iftype); +static int32 wl_dongle_country(struct net_device *ndev, uint8 ccode); +static int32 wl_dongle_up(struct net_device *ndev, uint32 up); +static int32 wl_dongle_power(struct net_device *ndev, uint32 power_mode); +static int32 wl_dongle_glom(struct net_device *ndev, uint32 glom, uint32 dongle_align); +static int32 wl_dongle_roam(struct net_device *ndev, uint32 roamvar, uint32 bcn_timeout); +static int32 wl_dongle_eventmsg(struct net_device *ndev); +static int32 wl_dongle_scantime(struct net_device *ndev, int32 scan_assoc_time, + int32 scan_unassoc_time); +static int32 wl_dongle_offload(struct net_device *ndev, int32 arpoe, int32 arp_ol); +static int32 wl_pattern_atoh(int8 *src, int8 *dst); +static int32 wl_dongle_filter(struct net_device *ndev, uint32 filter_mode); +static int32 wl_update_wiphybands(struct wl_priv *wl); +#endif /* !EMBEDDED_PLATFORM */ +static int32 wl_config_dongle(struct wl_priv *wl, bool need_lock); + +/* +** iscan handler +*/ +static void wl_iscan_timer(ulong data); +static void wl_term_iscan(struct wl_priv *wl); +static int32 wl_init_iscan(struct wl_priv *wl); +static int32 wl_iscan_thread(void *data); +static int32 wl_dev_iovar_setbuf(struct net_device *dev, int8 *iovar, void *param, + int32 paramlen, void *bufptr, int32 buflen); +static int32 wl_dev_iovar_getbuf(struct net_device *dev, int8 *iovar, void *param, + int32 paramlen, void *bufptr, int32 buflen); +static int32 wl_run_iscan(struct wl_iscan_ctrl *iscan, struct wlc_ssid *ssid, uint16 action); +static int32 wl_do_iscan(struct wl_priv *wl); +static int32 wl_wakeup_iscan(struct wl_iscan_ctrl *iscan); +static int32 wl_invoke_iscan(struct wl_priv *wl); +static int32 wl_get_iscan_results(struct wl_iscan_ctrl *iscan, uint32 *status, + struct wl_scan_results **bss_list); +static void wl_notify_iscan_complete(struct wl_iscan_ctrl *iscan, bool aborted); +static void wl_init_iscan_eloop(struct wl_iscan_eloop *el); +static int32 wl_iscan_done(struct wl_priv *wl); +static int32 wl_iscan_pending(struct wl_priv *wl); +static int32 wl_iscan_inprogress(struct wl_priv *wl); +static int32 wl_iscan_aborted(struct wl_priv *wl); + +/* +** fw/nvram downloading handler +*/ +static void wl_init_fw(struct wl_fw_ctrl *fw); + +/* +* find most significant bit set +*/ +static __used uint32 wl_find_msb(uint16 bit16); + +/* +* update pmklist to dongle +*/ +static __used int32 wl_update_pmklist(struct net_device *dev, struct wl_pmk_list *pmk_list, + int32 err); + + +#define WL_PRIV_GET() \ + ({ \ + struct wl_iface *ci; \ + if (unlikely(!(wl_cfg80211_dev && (ci = wl_get_drvdata(wl_cfg80211_dev))))) { \ + WL_ERR(("wl_cfg80211_dev is unavailable\n")); \ + BUG(); \ + } \ + ci_to_wl(ci); \ + }) + +#define CHECK_SYS_UP() \ + do { \ + struct wl_priv *wl = wiphy_to_wl(wiphy); \ + if (unlikely(!test_bit(WL_STATUS_READY, &wl->status))) { \ + WL_INFO(("device is not ready : status (%d)\n", (int)wl->status)); \ + return -EIO; \ + } \ + } while (0) + + +extern int dhd_wait_pend8021x(struct net_device *dev); + +#if (WL_DBG_LEVEL > 0) +#define WL_DBG_ESTR_MAX 32 +static int8 wl_dbg_estr[][WL_DBG_ESTR_MAX] = { +"SET_SSID", "JOIN", "START", "AUTH", "AUTH_IND", +"DEAUTH", "DEAUTH_IND", "ASSOC", "ASSOC_IND", "REASSOC", +"REASSOC_IND", "DISASSOC", "DISASSOC_IND", "QUIET_START", "QUIET_END", +"BEACON_RX", "LINK", "MIC_ERROR", "NDIS_LINK", "ROAM", +"TXFAIL", "PMKID_CACHE", "RETROGRADE_TSF", "PRUNE", "AUTOAUTH", +"EAPOL_MSG", "SCAN_COMPLETE", "ADDTS_IND", "DELTS_IND", "BCNSENT_IND", +"BCNRX_MSG", "BCNLOST_MSG", "ROAM_PREP", "PFN_NET_FOUND", "PFN_NET_LOST", +"RESET_COMPLETE", "JOIN_START", "ROAM_START", "ASSOC_START", "IBSS_ASSOC", +"RADIO", "PSM_WATCHDOG", +"PROBREQ_MSG", +"SCAN_CONFIRM_IND", "PSK_SUP", "COUNTRY_CODE_CHANGED", "EXCEEDED_MEDIUM_TIME", "ICV_ERROR", +"UNICAST_DECODE_ERROR", "MULTICAST_DECODE_ERROR", "TRACE", +"IF", +"RSSI", "PFN_SCAN_COMPLETE", "ACTION_FRAME", "ACTION_FRAME_COMPLETE", +}; +#endif /* WL_DBG_LEVEL */ + +#define CHAN2G(_channel, _freq, _flags) { \ + .band = IEEE80211_BAND_2GHZ, \ + .center_freq = (_freq), \ + .hw_value = (_channel), \ + .flags = (_flags), \ + .max_antenna_gain = 0, \ + .max_power = 30, \ +} + +#define CHAN5G(_channel, _flags) { \ + .band = IEEE80211_BAND_5GHZ, \ + .center_freq = 5000 + (5 * (_channel)), \ + .hw_value = (_channel), \ + .flags = (_flags), \ + .max_antenna_gain = 0, \ + .max_power = 30, \ +} + +#define RATE_TO_BASE100KBPS(rate) (((rate) * 10) / 2) +#define RATETAB_ENT(_rateid, _flags) \ + { \ + .bitrate = RATE_TO_BASE100KBPS(_rateid), \ + .hw_value = (_rateid), \ + .flags = (_flags), \ + } + +static struct ieee80211_rate __wl_rates[] = { + RATETAB_ENT(WLC_RATE_1M, 0), + RATETAB_ENT(WLC_RATE_2M, IEEE80211_RATE_SHORT_PREAMBLE), + RATETAB_ENT(WLC_RATE_5M5, IEEE80211_RATE_SHORT_PREAMBLE), + RATETAB_ENT(WLC_RATE_11M, IEEE80211_RATE_SHORT_PREAMBLE), + RATETAB_ENT(WLC_RATE_6M, 0), + RATETAB_ENT(WLC_RATE_9M, 0), + RATETAB_ENT(WLC_RATE_12M, 0), + RATETAB_ENT(WLC_RATE_18M, 0), + RATETAB_ENT(WLC_RATE_24M, 0), + RATETAB_ENT(WLC_RATE_36M, 0), + RATETAB_ENT(WLC_RATE_48M, 0), + RATETAB_ENT(WLC_RATE_54M, 0), +}; + +#define wl_a_rates (__wl_rates + 4) +#define wl_a_rates_size 8 +#define wl_g_rates (__wl_rates + 0) +#define wl_g_rates_size 12 + +static struct ieee80211_channel __wl_2ghz_channels[] = { + CHAN2G(1, 2412, 0), + CHAN2G(2, 2417, 0), + CHAN2G(3, 2422, 0), + CHAN2G(4, 2427, 0), + CHAN2G(5, 2432, 0), + CHAN2G(6, 2437, 0), + CHAN2G(7, 2442, 0), + CHAN2G(8, 2447, 0), + CHAN2G(9, 2452, 0), + CHAN2G(10, 2457, 0), + CHAN2G(11, 2462, 0), + CHAN2G(12, 2467, 0), + CHAN2G(13, 2472, 0), + CHAN2G(14, 2484, 0), +}; + +static struct ieee80211_channel __wl_5ghz_a_channels[] = { + CHAN5G(34, 0), CHAN5G(36, 0), + CHAN5G(38, 0), CHAN5G(40, 0), + CHAN5G(42, 0), CHAN5G(44, 0), + CHAN5G(46, 0), CHAN5G(48, 0), + CHAN5G(52, 0), CHAN5G(56, 0), + CHAN5G(60, 0), CHAN5G(64, 0), + CHAN5G(100, 0), CHAN5G(104, 0), + CHAN5G(108, 0), CHAN5G(112, 0), + CHAN5G(116, 0), CHAN5G(120, 0), + CHAN5G(124, 0), CHAN5G(128, 0), + CHAN5G(132, 0), CHAN5G(136, 0), + CHAN5G(140, 0), CHAN5G(149, 0), + CHAN5G(153, 0), CHAN5G(157, 0), + CHAN5G(161, 0), CHAN5G(165, 0), + CHAN5G(184, 0), CHAN5G(188, 0), + CHAN5G(192, 0), CHAN5G(196, 0), + CHAN5G(200, 0), CHAN5G(204, 0), + CHAN5G(208, 0), CHAN5G(212, 0), + CHAN5G(216, 0), +}; + +static struct ieee80211_channel __wl_5ghz_n_channels[] = { + CHAN5G(32, 0), CHAN5G(34, 0), + CHAN5G(36, 0), CHAN5G(38, 0), + CHAN5G(40, 0), CHAN5G(42, 0), + CHAN5G(44, 0), CHAN5G(46, 0), + CHAN5G(48, 0), CHAN5G(50, 0), + CHAN5G(52, 0), CHAN5G(54, 0), + CHAN5G(56, 0), CHAN5G(58, 0), + CHAN5G(60, 0), CHAN5G(62, 0), + CHAN5G(64, 0), CHAN5G(66, 0), + CHAN5G(68, 0), CHAN5G(70, 0), + CHAN5G(72, 0), CHAN5G(74, 0), + CHAN5G(76, 0), CHAN5G(78, 0), + CHAN5G(80, 0), CHAN5G(82, 0), + CHAN5G(84, 0), CHAN5G(86, 0), + CHAN5G(88, 0), CHAN5G(90, 0), + CHAN5G(92, 0), CHAN5G(94, 0), + CHAN5G(96, 0), CHAN5G(98, 0), + CHAN5G(100, 0), CHAN5G(102, 0), + CHAN5G(104, 0), CHAN5G(106, 0), + CHAN5G(108, 0), CHAN5G(110, 0), + CHAN5G(112, 0), CHAN5G(114, 0), + CHAN5G(116, 0), CHAN5G(118, 0), + CHAN5G(120, 0), CHAN5G(122, 0), + CHAN5G(124, 0), CHAN5G(126, 0), + CHAN5G(128, 0), CHAN5G(130, 0), + CHAN5G(132, 0), CHAN5G(134, 0), + CHAN5G(136, 0), CHAN5G(138, 0), + CHAN5G(140, 0), CHAN5G(142, 0), + CHAN5G(144, 0), CHAN5G(145, 0), + CHAN5G(146, 0), CHAN5G(147, 0), + CHAN5G(148, 0), CHAN5G(149, 0), + CHAN5G(150, 0), CHAN5G(151, 0), + CHAN5G(152, 0), CHAN5G(153, 0), + CHAN5G(154, 0), CHAN5G(155, 0), + CHAN5G(156, 0), CHAN5G(157, 0), + CHAN5G(158, 0), CHAN5G(159, 0), + CHAN5G(160, 0), CHAN5G(161, 0), + CHAN5G(162, 0), CHAN5G(163, 0), + CHAN5G(164, 0), CHAN5G(165, 0), + CHAN5G(166, 0), CHAN5G(168, 0), + CHAN5G(170, 0), CHAN5G(172, 0), + CHAN5G(174, 0), CHAN5G(176, 0), + CHAN5G(178, 0), CHAN5G(180, 0), + CHAN5G(182, 0), CHAN5G(184, 0), + CHAN5G(186, 0), CHAN5G(188, 0), + CHAN5G(190, 0), CHAN5G(192, 0), + CHAN5G(194, 0), CHAN5G(196, 0), + CHAN5G(198, 0), CHAN5G(200, 0), + CHAN5G(202, 0), CHAN5G(204, 0), + CHAN5G(206, 0), CHAN5G(208, 0), + CHAN5G(210, 0), CHAN5G(212, 0), + CHAN5G(214, 0), CHAN5G(216, 0), + CHAN5G(218, 0), CHAN5G(220, 0), + CHAN5G(222, 0), CHAN5G(224, 0), + CHAN5G(226, 0), CHAN5G(228, 0), +}; + +static struct ieee80211_supported_band __wl_band_2ghz = { + .band = IEEE80211_BAND_2GHZ, + .channels = __wl_2ghz_channels, + .n_channels = ARRAY_SIZE(__wl_2ghz_channels), + .bitrates = wl_g_rates, + .n_bitrates = wl_g_rates_size, +}; + +static struct ieee80211_supported_band __wl_band_5ghz_a = { + .band = IEEE80211_BAND_5GHZ, + .channels = __wl_5ghz_a_channels, + .n_channels = ARRAY_SIZE(__wl_5ghz_a_channels), + .bitrates = wl_a_rates, + .n_bitrates = wl_a_rates_size, +}; + +static struct ieee80211_supported_band __wl_band_5ghz_n = { + .band = IEEE80211_BAND_5GHZ, + .channels = __wl_5ghz_n_channels, + .n_channels = ARRAY_SIZE(__wl_5ghz_n_channels), + .bitrates = wl_a_rates, + .n_bitrates = wl_a_rates_size, +}; + +static const uint32 __wl_cipher_suites[] = { + WLAN_CIPHER_SUITE_WEP40, + WLAN_CIPHER_SUITE_WEP104, + WLAN_CIPHER_SUITE_TKIP, + WLAN_CIPHER_SUITE_CCMP, + WLAN_CIPHER_SUITE_AES_CMAC, +}; + +static void +swap_key_from_BE(struct wl_wsec_key *key) +{ + key->index = htod32(key->index); + key->len = htod32(key->len); + key->algo = htod32(key->algo); + key->flags = htod32(key->flags); + key->rxiv.hi = htod32(key->rxiv.hi); + key->rxiv.lo = htod16(key->rxiv.lo); + key->iv_initialized = htod32(key->iv_initialized); +} + +static void +swap_key_to_BE(struct wl_wsec_key *key) +{ + key->index = dtoh32(key->index); + key->len = dtoh32(key->len); + key->algo = dtoh32(key->algo); + key->flags = dtoh32(key->flags); + key->rxiv.hi = dtoh32(key->rxiv.hi); + key->rxiv.lo = dtoh16(key->rxiv.lo); + key->iv_initialized = dtoh32(key->iv_initialized); +} + +static int32 +wl_dev_ioctl(struct net_device *dev, uint32 cmd, void *arg, uint32 len) +{ + struct ifreq ifr; + struct wl_ioctl ioc; + mm_segment_t fs; + int32 err = 0; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In : cmd (%d)\n", cmd)); +#endif + memset(&ioc, 0, sizeof(ioc)); + ioc.cmd = cmd; + ioc.buf = arg; + ioc.len = len; + strcpy(ifr.ifr_name, dev->name); + ifr.ifr_data = (caddr_t) &ioc; + + + fs = get_fs(); + set_fs(get_ds()); + err = dev->netdev_ops->ndo_do_ioctl(dev, &ifr, SIOCDEVPRIVATE); + set_fs(fs); +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + + return err; +} + +static int32 +wl_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev, + enum nl80211_iftype type, uint32 *flags, struct vif_params *params) +{ + struct wl_priv *wl = wiphy_to_wl(wiphy); + struct wireless_dev *wdev; + int32 infra = 0; + int32 ap = 0; + int32 err = 0; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + CHECK_SYS_UP(); + switch (type) { + case NL80211_IFTYPE_MONITOR: + case NL80211_IFTYPE_WDS: + WL_ERR(("type (%d) : currently we do not support this type\n", type)); + return -EOPNOTSUPP; + case NL80211_IFTYPE_ADHOC: + wl->conf->mode = WL_MODE_IBSS; + break; + case NL80211_IFTYPE_STATION: + wl->conf->mode = WL_MODE_BSS; + infra = 1; + break; + default: + return -EINVAL; + } + infra = htod32(infra); + ap = htod32(ap); + wdev = ndev->ieee80211_ptr; + wdev->iftype = type; + WL_DBG(("%s : ap (%d), infra (%d)\n", ndev->name, ap, infra)); + if (unlikely((err = wl_dev_ioctl(ndev, WLC_SET_INFRA, &infra, sizeof(infra)))) || + unlikely((err = wl_dev_ioctl(ndev, WLC_SET_AP, &ap, sizeof(ap))))) { + WL_ERR(("Error (%d)\n", err)); + return err; + } +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + /* -EINPROGRESS: Call commit handler */ + return -EINPROGRESS; +} + +static void +wl_iscan_prep(struct wl_scan_params *params, struct wlc_ssid *ssid) +{ + memcpy(¶ms->bssid, ðer_bcast, ETHER_ADDR_LEN); + params->bss_type = DOT11_BSSTYPE_ANY; + params->scan_type = 0; + params->nprobes = -1; + params->active_time = -1; + params->passive_time = -1; + params->home_time = -1; + params->channel_num = 0; + + params->nprobes = htod32(params->nprobes); + params->active_time = htod32(params->active_time); + params->passive_time = htod32(params->passive_time); + params->home_time = htod32(params->home_time); + if (ssid && ssid->SSID_len) + memcpy(¶ms->ssid, ssid, sizeof(wlc_ssid_t)); + +} + +static int32 +wl_dev_iovar_setbuf(struct net_device *dev, int8 *iovar, void *param, + int32 paramlen, void *bufptr, int32 buflen) +{ + int32 iolen; + + iolen = bcm_mkiovar(iovar, param, paramlen, bufptr, buflen); + BUG_ON(unlikely(!iolen)); + + return wl_dev_ioctl(dev, WLC_SET_VAR, bufptr, iolen); +} + +static int32 +wl_dev_iovar_getbuf(struct net_device *dev, int8 *iovar, void *param, + int32 paramlen, void *bufptr, int32 buflen) +{ + int32 iolen; + + iolen = bcm_mkiovar(iovar, param, paramlen, bufptr, buflen); + BUG_ON(unlikely(!iolen)); + + return wl_dev_ioctl(dev, WLC_GET_VAR, bufptr, buflen); +} + +static int32 +wl_run_iscan(struct wl_iscan_ctrl *iscan, struct wlc_ssid *ssid, uint16 action) +{ + int32 params_size = (WL_SCAN_PARAMS_FIXED_SIZE + OFFSETOF(wl_iscan_params_t, params)); + struct wl_iscan_params *params; + int32 err = 0; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + if (ssid && ssid->SSID_len) + params_size += sizeof(struct wlc_ssid); + params = (struct wl_iscan_params *)kzalloc(params_size, GFP_KERNEL); + if (unlikely(!params)) + return -ENOMEM; + memset(params, 0, params_size); + BUG_ON(unlikely(params_size >= WLC_IOCTL_SMLEN)); + + wl_iscan_prep(¶ms->params, ssid); + + params->version = htod32(ISCAN_REQ_VERSION); + params->action = htod16(action); + params->scan_duration = htod16(0); + + /* params_size += OFFSETOF(wl_iscan_params_t, params); */ + if (unlikely((err = wl_dev_iovar_setbuf(iscan->dev, "iscan", params, params_size, + iscan->ioctl_buf, WLC_IOCTL_SMLEN)))) { + if (err == -EBUSY) { + WL_INFO(("system busy : iscan canceled\n")); + } else { + WL_ERR(("error (%d)\n", err)); + } + } + kfree(params); +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + return err; +} + + +static int32 +wl_do_iscan(struct wl_priv *wl) +{ + struct wl_iscan_ctrl *iscan = wl_to_iscan(wl); + struct wlc_ssid ssid; + int32 err = 0; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + /* Broadcast scan by default */ + memset(&ssid, 0, sizeof(ssid)); + + iscan->state = WL_ISCAN_STATE_SCANING; + + if (wl->active_scan) { + int32 passive_scan = 0; + /* make it active scan */ + if (unlikely((err = wl_dev_ioctl(wl_to_ndev(wl), WLC_SET_PASSIVE_SCAN, + &passive_scan, sizeof(passive_scan))))) { + WL_DBG(("error (%d)\n", err)); + return err; + } + } + wl->iscan_kickstart = TRUE; + wl_run_iscan(iscan, &ssid, WL_SCAN_ACTION_START); + mod_timer(&iscan->timer, jiffies + iscan->timer_ms*HZ/1000); + iscan->timer_on = 1; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + + return err; +} + + +static int32 +__wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, + struct cfg80211_scan_request *request, struct cfg80211_ssid *this_ssid) +{ + struct wl_priv *wl = ndev_to_wl(ndev); + struct cfg80211_ssid *ssids; + struct wl_scan_req *sr = wl_to_sr(wl); + uint32 n_ssids; + bool iscan_req; + bool spec_scan; + int32 err = 0; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + if (unlikely(test_bit(WL_STATUS_SCANNING, &wl->status))) { + WL_ERR(("Scanning already : status (%d)\n", (int)wl->status)); + return -EAGAIN; + } + if (unlikely(test_bit(WL_STATUS_SCAN_ABORTING, &wl->status))) { + WL_ERR(("Scanning being aborted : status (%d)\n", (int)wl->status)); + return -EAGAIN; + } + + iscan_req = FALSE; + spec_scan = FALSE; + if (request) { /* scan bss */ + ssids = request->ssids; + n_ssids = min(request->n_ssids, WL_NUM_SCAN_MAX); + if (wl->iscan_on && n_ssids && !ssids->ssid_len) { /* for specific scan, + * ssids->ssid_len has + * non-zero(ssid string) length. + * Otherwise this is 0. + * we do not iscan for + * specific scan request + */ + iscan_req = TRUE; + } + } else { /* scan in ibss */ + /* we don't do iscan in ibss */ + ssids = this_ssid; + n_ssids = 1; + } + wl->scan_request = request; + set_bit(WL_STATUS_SCANNING, &wl->status); + if (iscan_req) { + if (likely(!(err = wl_do_iscan(wl)))) + return err; + else + goto scan_out; + } else { + WL_DBG(("n_ssid (%d), ssid \"%s\", ssid_len (%d)\n", + n_ssids, ssids->ssid, ssids->ssid_len)); + memset(&sr->ssid, 0, sizeof(sr->ssid)); + if (n_ssids) { + sr->ssid.SSID_len = MIN(sizeof(sr->ssid.SSID), ssids->ssid_len); + if (sr->ssid.SSID_len) { + memcpy(sr->ssid.SSID, ssids->ssid, sr->ssid.SSID_len); + sr->ssid.SSID_len = htod32(sr->ssid.SSID_len); + WL_DBG(("Specific scan ssid=\"%s\" len=%d\n", sr->ssid.SSID, + sr->ssid.SSID_len)); + spec_scan = TRUE; + } else { + WL_DBG(("Broadcast scan\n")); + } + } else { + /* broadcast scan */ + WL_DBG(("Broadcast scan\n")); + } + WL_DBG(("sr->ssid.SSID_len (%d)\n", sr->ssid.SSID_len)); + if (wl->active_scan) { + int32 pssive_scan = 0; + /* make it active scan */ + if (unlikely((err = wl_dev_ioctl(ndev, WLC_SET_PASSIVE_SCAN, + &pssive_scan, sizeof(pssive_scan))))) { + WL_ERR(("WLC_SET_PASSIVE_SCAN error (%d)\n", err)); + goto scan_out; + } + } + if ((err = wl_dev_ioctl(ndev, WLC_SCAN, &sr->ssid, sizeof(sr->ssid)))) { + if (err == -EBUSY) { + WL_INFO(("system busy : scan for \"%s\" canceled\n", + sr->ssid.SSID)); + } else { + WL_ERR(("WLC_SCAN error (%d)\n", err)); + } + goto scan_out; + } + } +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + + return 0; + +scan_out: + clear_bit(WL_STATUS_SCANNING, &wl->status); + wl->scan_request = NULL; + return err; +} + + +static int32 +wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, + struct cfg80211_scan_request *request) +{ + int32 err = 0; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + CHECK_SYS_UP(); + if (unlikely((err = __wl_cfg80211_scan(wiphy, ndev, request, NULL)))) { + WL_DBG(("scan error (%d)\n", err)); + return err; + } +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + + return err; +} + +static int32 +wl_dev_intvar_set(struct net_device *dev, int8 *name, int32 val) +{ + int8 buf[WLC_IOCTL_SMLEN]; + uint32 len; + int32 err = 0; + + val = htod32(val); + len = bcm_mkiovar(name, (char *)(&val), sizeof(val), buf, sizeof(buf)); + BUG_ON(unlikely(!len)); + + if (unlikely((err = wl_dev_ioctl(dev, WLC_SET_VAR, buf, len)))) { + WL_ERR(("error (%d)\n", err)); + } + + return err; +} + +static int32 +wl_dev_intvar_get(struct net_device *dev, int8 *name, int32 *retval) +{ + union { + int8 buf[WLC_IOCTL_SMLEN]; + int32 val; + } var; + uint32 len; + uint32 data_null; + int32 err = 0; + + len = bcm_mkiovar(name, (char *)(&data_null), 0, (char *)(&var), sizeof(var.buf)); + BUG_ON(unlikely(!len)); + if (unlikely((err = wl_dev_ioctl(dev, WLC_GET_VAR, &var, len)))) { + WL_ERR(("error (%d)\n", err)); + } + *retval = dtoh32(var.val); + + return err; +} + +static int32 +wl_set_rts(struct net_device *dev, uint32 rts_threshold) +{ + int32 err = 0; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + if (unlikely((err = wl_dev_intvar_set(dev, "rtsthresh", rts_threshold)))) { + WL_ERR(("Error (%d)\n", err)); + return err; + } +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + return err; +} + +static int32 +wl_set_frag(struct net_device *dev, uint32 frag_threshold) +{ + int32 err = 0; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + if (unlikely((err = wl_dev_intvar_set(dev, "fragthresh", frag_threshold)))) { + WL_ERR(("Error (%d)\n", err)); + return err; + } +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + return err; +} + +static int32 +wl_set_retry(struct net_device *dev, uint32 retry, bool l) +{ + int32 err = 0; + uint32 cmd = (l ? WLC_SET_LRL : WLC_SET_SRL); + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + retry = htod32(retry); + if (unlikely((err = wl_dev_ioctl(dev, cmd, &retry, sizeof(retry))))) { + WL_ERR(("cmd (%d) , error (%d)\n", cmd, err)); + return err; + } +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + return err; +} + +static int32 +wl_cfg80211_set_wiphy_params(struct wiphy *wiphy, uint32 changed) +{ + struct wl_priv *wl = wiphy_to_wl(wiphy); + struct net_device *ndev = wl_to_ndev(wl); + int32 err = 0; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + CHECK_SYS_UP(); + if (changed & WIPHY_PARAM_RTS_THRESHOLD && + (wl->conf->rts_threshold != wiphy->rts_threshold)) { + wl->conf->rts_threshold = wiphy->rts_threshold; + if (!(err = wl_set_rts(ndev, wl->conf->rts_threshold))) { + return err; + } + } + if (changed & WIPHY_PARAM_FRAG_THRESHOLD && + (wl->conf->frag_threshold != wiphy->frag_threshold)) { + wl->conf->frag_threshold = wiphy->frag_threshold; + if (!(err = wl_set_frag(ndev, wl->conf->frag_threshold))) { + return err; + } + } + if (changed & WIPHY_PARAM_RETRY_LONG && (wl->conf->retry_long != wiphy->retry_long)) { + wl->conf->retry_long = wiphy->retry_long; + if (!(err = wl_set_retry(ndev, wl->conf->retry_long, TRUE))) { + return err; + } + } + if (changed & WIPHY_PARAM_RETRY_SHORT && (wl->conf->retry_short != wiphy->retry_short)) { + wl->conf->retry_short = wiphy->retry_short; + if (!(err = wl_set_retry(ndev, wl->conf->retry_short, FALSE))) { + return err; + } + } +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + + return err; +} + +static int32 +wl_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_ibss_params *params) +{ + struct wl_priv *wl = wiphy_to_wl(wiphy); + struct cfg80211_bss *bss; + struct ieee80211_channel *chan; + struct wl_join_params join_params; + struct cfg80211_ssid ssid; + int32 scan_retry = 0; + int32 err = 0; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In \n")); +#endif + CHECK_SYS_UP(); + if (params->bssid) { + WL_ERR(("Invalid bssid\n")); + return -EOPNOTSUPP; + } + bss = cfg80211_get_ibss(wiphy, NULL, params->ssid, params->ssid_len); + if (!bss) { + memcpy(ssid.ssid, params->ssid, params->ssid_len); + ssid.ssid_len = params->ssid_len; + do { + if (unlikely(__wl_cfg80211_scan(wiphy, dev, NULL, &ssid) == -EBUSY)) { + wl_delay(150); + } else { + break; + } + } while (++scan_retry < WL_SCAN_RETRY_MAX); + rtnl_unlock(); /* to allow scan_inform to paropagate to cfg80211 plane */ + schedule_timeout_interruptible(4 * HZ); /* wait 4 secons till scan done.... */ + rtnl_lock(); + bss = cfg80211_get_ibss(wiphy, NULL, + params->ssid, params->ssid_len); + } + if (bss) { + wl->ibss_starter = FALSE; + WL_DBG(("Found IBSS\n")); + } else { + wl->ibss_starter = TRUE; + } + if ((chan = params->channel)) { + wl->channel = ieee80211_frequency_to_channel(chan->center_freq); + } + /* + ** Join with specific BSSID and cached SSID + ** If SSID is zero join based on BSSID only + */ + memset(&join_params, 0, sizeof(join_params)); + memcpy((void *)join_params.ssid.SSID, (void *)params->ssid, params->ssid_len); + join_params.ssid.SSID_len = htod32(params->ssid_len); + if (params->bssid) + memcpy(&join_params.params.bssid, params->bssid, ETHER_ADDR_LEN); + else + memset(&join_params.params.bssid, 0, ETHER_ADDR_LEN); + + if (unlikely((err = wl_dev_ioctl(dev, WLC_SET_SSID, &join_params, sizeof(join_params))))) { + WL_ERR(("Error (%d)\n", err)); + return err; + } +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + return err; +} + +static int32 +wl_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev) +{ + struct wl_priv *wl = wiphy_to_wl(wiphy); + int32 err = 0; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + CHECK_SYS_UP(); + wl_link_down(wl); +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + + return err; +} + +static int32 +wl_set_wpa_version(struct net_device *dev, struct cfg80211_connect_params *sme) +{ + struct wl_priv *wl = ndev_to_wl(dev); + struct wl_security *sec; + int32 val = 0; + int32 err = 0; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1) + val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED; + else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2) + val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED; + else + val = WPA_AUTH_DISABLED; + WL_DBG(("setting wpa_auth to 0x%0x\n", val)); + if (unlikely((err = wl_dev_intvar_set(dev, "wpa_auth", val)))) { + WL_ERR(("set wpa_auth failed (%d)\n", err)); + return err; + } + sec = wl_read_prof(wl, WL_PROF_SEC); + sec->wpa_versions = sme->crypto.wpa_versions; +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + return err; +} + +static int32 +wl_set_auth_type(struct net_device *dev, struct cfg80211_connect_params *sme) +{ + struct wl_priv *wl = ndev_to_wl(dev); + struct wl_security *sec; + int32 val = 0; + int32 err = 0; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + switch (sme->auth_type) { + case NL80211_AUTHTYPE_OPEN_SYSTEM: + val = 0; + WL_DBG(("open system\n")); + break; + case NL80211_AUTHTYPE_SHARED_KEY: + val = 1; + WL_DBG(("shared key\n")); + break; + case NL80211_AUTHTYPE_AUTOMATIC: + val = 2; + WL_DBG(("automatic\n")); + break; + case NL80211_AUTHTYPE_NETWORK_EAP: + WL_DBG(("network eap\n")); + default : + val = 2; + WL_ERR(("invalid auth type (%d)\n", sme->auth_type)); + break; + } + + if (unlikely((err = wl_dev_intvar_set(dev, "auth", val)))) { + WL_ERR(("set auth failed (%d)\n", err)); + return err; + } + sec = wl_read_prof(wl, WL_PROF_SEC); + sec->auth_type = sme->auth_type; +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + return err; +} + + +static int32 +wl_set_set_cipher(struct net_device *dev, struct cfg80211_connect_params *sme) +{ + struct wl_priv *wl = ndev_to_wl(dev); + struct wl_security *sec; + int32 pval = 0; + int32 gval = 0; + int32 err = 0; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + if (sme->crypto.n_ciphers_pairwise) { + switch (sme->crypto.ciphers_pairwise[0]) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + pval = WEP_ENABLED; + break; + case WLAN_CIPHER_SUITE_TKIP: + pval = TKIP_ENABLED; + break; + case WLAN_CIPHER_SUITE_CCMP: + pval = AES_ENABLED; + break; + case WLAN_CIPHER_SUITE_AES_CMAC: + pval = AES_ENABLED; + break; + default: + WL_ERR(("invalid cipher pairwise (%d)\n", + sme->crypto.ciphers_pairwise[0])); + return -EINVAL; + } + } + if (sme->crypto.cipher_group) { + switch (sme->crypto.cipher_group) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + gval = WEP_ENABLED; + break; + case WLAN_CIPHER_SUITE_TKIP: + gval = TKIP_ENABLED; + break; + case WLAN_CIPHER_SUITE_CCMP: + gval = AES_ENABLED; + break; + case WLAN_CIPHER_SUITE_AES_CMAC: + gval = AES_ENABLED; + break; + default: + WL_ERR(("invalid cipher group (%d)\n", sme->crypto.cipher_group)); + return -EINVAL; + } + } + + WL_DBG(("pval (%d) gval (%d)\n", pval, gval)); + if (unlikely((err = wl_dev_intvar_set(dev, "wsec", pval|gval)))) { + WL_ERR(("error (%d)\n", err)); + return err; + } + + sec = wl_read_prof(wl, WL_PROF_SEC); + sec->cipher_pairwise = sme->crypto.ciphers_pairwise[0]; + sec->cipher_group = sme->crypto.cipher_group; +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + + return err; +} + +static int32 +wl_set_key_mgmt(struct net_device *dev, struct cfg80211_connect_params *sme) +{ + struct wl_priv *wl = ndev_to_wl(dev); + struct wl_security *sec; + int32 val = 0; + int32 err = 0; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + + if (sme->crypto.n_akm_suites) { + if (unlikely((err = wl_dev_intvar_get(dev, "wpa_auth", &val)))) { + WL_ERR(("could not get wpa_auth (%d)\n", err)); + return err; + } + if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) { + switch (sme->crypto.akm_suites[0]) { + case WLAN_AKM_SUITE_8021X: + val = WPA_AUTH_UNSPECIFIED; + break; + case WLAN_AKM_SUITE_PSK: + val = WPA_AUTH_PSK; + break; + default : + WL_ERR(("invalid cipher group (%d)\n", + sme->crypto.cipher_group)); + return -EINVAL; + } + } else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) { + switch (sme->crypto.akm_suites[0]) { + case WLAN_AKM_SUITE_8021X: + val = WPA2_AUTH_UNSPECIFIED; + break; + case WLAN_AKM_SUITE_PSK: + val = WPA2_AUTH_PSK; + break; + default : + WL_ERR(("invalid cipher group (%d)\n", + sme->crypto.cipher_group)); + return -EINVAL; + } + } + + WL_DBG(("setting wpa_auth to %d\n", val)); + if (unlikely((err = wl_dev_intvar_set(dev, "wpa_auth", val)))) { + WL_ERR(("could not set wpa_auth (%d)\n", err)); + return err; + } + } + sec = wl_read_prof(wl, WL_PROF_SEC); + sec->wpa_auth = sme->crypto.akm_suites[0]; +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + + return err; +} + +static int32 +wl_set_set_sharedkey(struct net_device *dev, struct cfg80211_connect_params *sme) +{ + struct wl_priv *wl = ndev_to_wl(dev); + struct wl_security *sec; + struct wl_wsec_key key; + int32 val; + int32 err = 0; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + WL_DBG(("key len (%d)\n", sme->key_len)); + if (sme->key_len) { + sec = wl_read_prof(wl, WL_PROF_SEC); + WL_DBG(("wpa_versions 0x%x cipher_pairwise 0x%x\n", sec->wpa_versions, + sec->cipher_pairwise)); + if (!(sec->wpa_versions & (NL80211_WPA_VERSION_1 | NL80211_WPA_VERSION_2)) && + (sec->cipher_pairwise & (WLAN_CIPHER_SUITE_WEP40 | + WLAN_CIPHER_SUITE_WEP104))) { + memset(&key, 0, sizeof(key)); + key.len = (uint32)sme->key_len; + key.index = (uint32)sme->key_idx; + if (unlikely(key.len > sizeof(key.data))) { + WL_ERR(("Too long key length (%u)\n", key.len)); + return -EINVAL; + } + memcpy(key.data, sme->key, key.len); + key.flags = WL_PRIMARY_KEY; + switch (sec->cipher_pairwise) { + case WLAN_CIPHER_SUITE_WEP40: + key.algo = CRYPTO_ALGO_WEP1; + break; + case WLAN_CIPHER_SUITE_WEP104: + key.algo = CRYPTO_ALGO_WEP128; + break; + default : + WL_ERR(("Invalid algorithm (%d)\n", + sme->crypto.ciphers_pairwise[0])); + return -EINVAL; + } + /* Set the new key/index */ + WL_DBG(("key length (%d) key index (%d) algo (%d)\n", key.len, + key.index, key.algo)); + WL_DBG(("key \"%s\"\n", key.data)); + swap_key_from_BE(&key); + if (unlikely((err = wl_dev_ioctl(dev, WLC_SET_KEY, &key, sizeof(key))))) { + WL_ERR(("WLC_SET_KEY error (%d)\n", err)); + return err; + } + if (sec->auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM) { + WL_DBG(("set auth_type to shared key\n")); + val = 1; /* shared key */ + if (unlikely((err = wl_dev_intvar_set(dev, "auth", val)))) { + WL_ERR(("set auth failed (%d)\n", err)); + return err; + } + } + } + } +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + return err; +} + +static int32 +wl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_connect_params *sme) +{ + struct wl_priv *wl = wiphy_to_wl(wiphy); + struct ieee80211_channel *chan = sme->channel; + struct wlc_ssid ssid; + int32 err = 0; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In \n")); +#endif + CHECK_SYS_UP(); + if (unlikely(!sme->ssid)) { + WL_ERR(("Invalid ssid\n")); + return -EOPNOTSUPP; + } + if (chan) { + wl->channel = ieee80211_frequency_to_channel(chan->center_freq); + WL_DBG(("channel (%d), center_req (%d) \n", wl->channel, chan->center_freq)); + } + WL_DBG(("ie (%p), ie_len (%d)\n", sme->ie, sme->ie_len)); + if (unlikely((err = wl_set_wpa_version(dev, sme)))) { + return err; + } + if (unlikely((err = wl_set_auth_type(dev, sme)))) { + return err; + } + if (unlikely((err = wl_set_set_cipher(dev, sme)))) { + return err; + } + if (unlikely((err = wl_set_key_mgmt(dev, sme)))) { + return err; + } + if (unlikely((err = wl_set_set_sharedkey(dev, sme)))) { + return err; + } + wl_update_prof(wl, NULL, sme->bssid, WL_PROF_BSSID); + /* + ** Join with specific BSSID and cached SSID + ** If SSID is zero join based on BSSID only + */ + memset(&ssid, 0, sizeof(ssid)); + ssid.SSID_len = MIN(sizeof(ssid.SSID), sme->ssid_len); + memcpy(ssid.SSID, sme->ssid, ssid.SSID_len); + ssid.SSID_len = htod32(ssid.SSID_len); + wl_update_prof(wl, NULL, &ssid, WL_PROF_SSID); + if (ssid.SSID_len < IEEE80211_MAX_SSID_LEN) { + WL_DBG(("ssid \"%s\", len (%d)\n", ssid.SSID, ssid.SSID_len)); + } + if (unlikely((err = wl_dev_ioctl(dev, WLC_SET_SSID, &ssid, sizeof(ssid))))) { + WL_ERR(("error (%d)\n", err)); + return err; + } + set_bit(WL_STATUS_CONNECTING, &wl->status); +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + + return err; +} + +static int32 +wl_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev, + uint16 reason_code) +{ + struct wl_priv *wl = wiphy_to_wl(wiphy); + scb_val_t scbval; + bool act = FALSE; + int32 err = 0; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + WL_DBG(("Reason %d\n", reason_code)); + CHECK_SYS_UP(); + if (likely((act = *(bool *)wl_read_prof(wl, WL_PROF_ACT)))) { + scbval.val = reason_code; + memcpy(&scbval.ea, &wl->bssid, ETHER_ADDR_LEN); + scbval.val = htod32(scbval.val); + if (unlikely((err = wl_dev_ioctl(dev, WLC_DISASSOC, &scbval, + sizeof(scb_val_t))))) { + WL_ERR(("error (%d)\n", err)); + return err; + } + } +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + + return err; +} + +static int32 +wl_cfg80211_set_tx_power(struct wiphy *wiphy, enum tx_power_setting type, int32 dbm) +{ + + struct wl_priv *wl = wiphy_to_wl(wiphy); + struct net_device *ndev = wl_to_ndev(wl); + uint16 txpwrmw; + int32 err = 0; + int32 disable = 0; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + CHECK_SYS_UP(); + switch (type) { + case TX_POWER_AUTOMATIC: + break; + case TX_POWER_LIMITED: + if (dbm < 0) { + WL_ERR(("TX_POWER_LIMITTED - dbm is negative\n")); + return -EINVAL; + } + break; + case TX_POWER_FIXED: + if (dbm < 0) { + WL_ERR(("TX_POWER_FIXED - dbm is negative..\n")); + return -EINVAL; + } + break; + } + /* Make sure radio is off or on as far as software is concerned */ + disable = WL_RADIO_SW_DISABLE << 16; + disable = htod32(disable); + if (unlikely((err = wl_dev_ioctl(ndev, WLC_SET_RADIO, &disable, sizeof(disable))))) { + WL_ERR(("WLC_SET_RADIO error (%d)\n", err)); + return err; + } + + if (dbm > 0xffff) + txpwrmw = 0xffff; + else + txpwrmw = (uint16)dbm; + if (unlikely((err = wl_dev_intvar_set(ndev, "qtxpower", + (int32)(bcm_mw_to_qdbm(txpwrmw)))))) { + WL_ERR(("qtxpower error (%d)\n", err)); + return err; + } + wl->conf->tx_power = dbm; +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + + return err; +} + +static int32 +wl_cfg80211_get_tx_power(struct wiphy *wiphy, int32 *dbm) +{ + struct wl_priv *wl = wiphy_to_wl(wiphy); + struct net_device *ndev = wl_to_ndev(wl); + int32 txpwrdbm; + uint8 result; + int32 err = 0; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + CHECK_SYS_UP(); + if (unlikely((err = wl_dev_intvar_get(ndev, "qtxpower", &txpwrdbm)))) { + WL_ERR(("error (%d)\n", err)); + return err; + } + result = (uint8)(txpwrdbm & ~WL_TXPWR_OVERRIDE); + *dbm = (int32)bcm_qdbm_to_mw(result); +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + + return err; +} + +static int32 +wl_cfg80211_config_default_key(struct wiphy *wiphy, struct net_device *dev, uint8 key_idx) +{ + uint32 index; + int32 wsec; + int32 err = 0; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + WL_DBG(("key index (%d)\n", key_idx)); + CHECK_SYS_UP(); + + if (unlikely(err = wl_dev_ioctl(dev, WLC_GET_WSEC, &wsec, sizeof(wsec)))) { + WL_ERR(("WLC_GET_WSEC error (%d)\n", err)); + return err; + } + wsec = dtoh32(wsec); + if (wsec & WEP_ENABLED) { + /* Just select a new current key */ + index = (uint32)key_idx; + index = htod32(index); + if (unlikely((err = wl_dev_ioctl(dev, WLC_SET_KEY_PRIMARY, + &index, sizeof(index))))) { + WL_ERR(("error (%d)\n", err)); + } + } +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + return err; +} + +static int32 +wl_add_keyext(struct wiphy *wiphy, struct net_device *dev, + uint8 key_idx, const uint8 *mac_addr, struct key_params *params) +{ + struct wl_wsec_key key; + int32 err = 0; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + memset(&key, 0, sizeof(key)); + key.index = (uint32)key_idx; + /* Instead of bcast for ea address for default wep keys, driver needs it to be Null */ + if (!ETHER_ISMULTI(mac_addr)) + memcpy((char *)&key.ea, (void *)mac_addr, ETHER_ADDR_LEN); + key.len = (uint32)params->key_len; + /* check for key index change */ + if (key.len == 0) { + /* key delete */ + swap_key_from_BE(&key); + if (unlikely((err = wl_dev_ioctl(dev, WLC_SET_KEY, &key, sizeof(key))))) { + WL_ERR(("key delete error (%d)\n", err)); + return err; + } + } else { + if (key.len > sizeof(key.data)) { + WL_ERR(("Invalid key length (%d)\n", key.len)); + return -EINVAL; + } + + WL_DBG(("Setting the key index %d\n", key.index)); + memcpy(key.data, params->key, key.len); + + if (params->cipher == WLAN_CIPHER_SUITE_TKIP) { + uint8 keybuf[8]; + memcpy(keybuf, &key.data[24], sizeof(keybuf)); + memcpy(&key.data[24], &key.data[16], sizeof(keybuf)); + memcpy(&key.data[16], keybuf, sizeof(keybuf)); + } + + /* if IW_ENCODE_EXT_RX_SEQ_VALID set */ + if (params->seq && params->seq_len == 6) { + /* rx iv */ + uint8 *ivptr; + ivptr = (uint8 *)params->seq; + key.rxiv.hi = (ivptr[5] << 24) | (ivptr[4] << 16) | + (ivptr[3] << 8) | ivptr[2]; + key.rxiv.lo = (ivptr[1] << 8) | ivptr[0]; + key.iv_initialized = TRUE; + } + + switch (params->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + key.algo = CRYPTO_ALGO_WEP1; + WL_DBG(("WLAN_CIPHER_SUITE_WEP40\n")); + break; + case WLAN_CIPHER_SUITE_WEP104: + key.algo = CRYPTO_ALGO_WEP128; + WL_DBG(("WLAN_CIPHER_SUITE_WEP104\n")); + break; + case WLAN_CIPHER_SUITE_TKIP: + key.algo = CRYPTO_ALGO_TKIP; + WL_DBG(("WLAN_CIPHER_SUITE_TKIP\n")); + break; + case WLAN_CIPHER_SUITE_AES_CMAC: + key.algo = CRYPTO_ALGO_AES_CCM; + WL_DBG(("WLAN_CIPHER_SUITE_AES_CMAC\n")); + break; + case WLAN_CIPHER_SUITE_CCMP: + key.algo = CRYPTO_ALGO_AES_CCM; + WL_DBG(("WLAN_CIPHER_SUITE_CCMP\n")); + break; + default: + WL_ERR(("Invalid cipher (0x%x)\n", params->cipher)); + return -EINVAL; + } + swap_key_from_BE(&key); + + dhd_wait_pend8021x(dev); + if (unlikely((err = wl_dev_ioctl(dev, WLC_SET_KEY, &key, sizeof(key))))) { + WL_ERR(("WLC_SET_KEY error (%d)\n", err)); + return err; + } + } +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + return err; +} + +static int32 +wl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *dev, + uint8 key_idx, const uint8 *mac_addr, struct key_params *params) +{ + struct wl_wsec_key key; + int32 val; + int32 wsec; + int32 err = 0; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + WL_DBG(("key index (%d)\n", key_idx)); + CHECK_SYS_UP(); + + if (mac_addr) + return wl_add_keyext(wiphy, dev, key_idx, mac_addr, params); + memset(&key, 0, sizeof(key)); + + key.len = (uint32)params->key_len; + key.index = (uint32)key_idx; + + if (unlikely(key.len > sizeof(key.data))) { + WL_ERR(("Too long key length (%u)\n", key.len)); + return -EINVAL; + } + memcpy(key.data, params->key, key.len); + + key.flags = WL_PRIMARY_KEY; + switch (params->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + key.algo = CRYPTO_ALGO_WEP1; + WL_DBG(("WLAN_CIPHER_SUITE_WEP40\n")); + break; + case WLAN_CIPHER_SUITE_WEP104: + key.algo = CRYPTO_ALGO_WEP128; + WL_DBG(("WLAN_CIPHER_SUITE_WEP104\n")); + break; + case WLAN_CIPHER_SUITE_TKIP: + key.algo = CRYPTO_ALGO_TKIP; + WL_DBG(("WLAN_CIPHER_SUITE_TKIP\n")); + break; + case WLAN_CIPHER_SUITE_AES_CMAC: + key.algo = CRYPTO_ALGO_AES_CCM; + WL_DBG(("WLAN_CIPHER_SUITE_AES_CMAC\n")); + break; + case WLAN_CIPHER_SUITE_CCMP: + key.algo = CRYPTO_ALGO_AES_CCM; + WL_DBG(("WLAN_CIPHER_SUITE_CCMP\n")); + break; + default: + WL_ERR(("Invalid cipher (0x%x)\n", params->cipher)); + return -EINVAL; + } + + /* Set the new key/index */ + swap_key_from_BE(&key); + if (unlikely((err = wl_dev_ioctl(dev, WLC_SET_KEY, &key, sizeof(key))))) { + WL_ERR(("WLC_SET_KEY error (%d)\n", err)); + return err; + } + + val = WEP_ENABLED; + if (unlikely((err = wl_dev_intvar_get(dev, "wsec", &wsec)))) { + WL_ERR(("get wsec error (%d)\n", err)); + return err; + } + wsec &= ~(WEP_ENABLED); + wsec |= val; + if (unlikely((err = wl_dev_intvar_set(dev, "wsec", wsec)))) { + WL_ERR(("set wsec error (%d)\n", err)); + return err; + } + + val = 1; /* assume shared key. otherwise 0 */ + val = htod32(val); + if (unlikely((err = wl_dev_ioctl(dev, WLC_SET_AUTH, &val, sizeof(val))))) { + WL_ERR(("WLC_SET_AUTH error (%d)\n", err)); + return err; + } +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + return err; +} + +static int32 +wl_cfg80211_del_key(struct wiphy *wiphy, struct net_device *dev, + uint8 key_idx, const uint8 *mac_addr) +{ + struct wl_wsec_key key; + int32 err = 0; + int32 val; + int32 wsec; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + CHECK_SYS_UP(); + memset(&key, 0, sizeof(key)); + + key.index = (uint32)key_idx; + key.flags = WL_PRIMARY_KEY; + key.algo = CRYPTO_ALGO_OFF; + + WL_DBG(("key index (%d)\n", key_idx)); + /* Set the new key/index */ + swap_key_from_BE(&key); + if (unlikely((err = wl_dev_ioctl(dev, WLC_SET_KEY, &key, sizeof(key))))) { + if (err == -EINVAL) { + if (key.index >= DOT11_MAX_DEFAULT_KEYS) { + /* we ignore this key index in this case */ + WL_DBG(("invalid key index (%d)\n", key_idx)); + } + } else { + WL_ERR(("WLC_SET_KEY error (%d)\n", err)); + } + return err; + } + + val = 0; + if (unlikely((err = wl_dev_intvar_get(dev, "wsec", &wsec)))) { + WL_ERR(("get wsec error (%d)\n", err)); + return err; + } + wsec &= ~(WEP_ENABLED); + wsec |= val; + if (unlikely((err = wl_dev_intvar_set(dev, "wsec", wsec)))) { + WL_ERR(("set wsec error (%d)\n", err)); + return err; + } + + val = 0; /* assume open key. otherwise 1 */ + val = htod32(val); + if (unlikely((err = wl_dev_ioctl(dev, WLC_SET_AUTH, &val, sizeof(val))))) { + WL_ERR(("WLC_SET_AUTH error (%d)\n", err)); + return err; + } +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + return err; +} + +static int32 +wl_cfg80211_get_key(struct wiphy *wiphy, struct net_device *dev, + uint8 key_idx, const uint8 *mac_addr, void *cookie, + void (*callback)(void *cookie, struct key_params *params)) +{ + struct key_params params; + struct wl_wsec_key key; + struct wl_priv *wl = wiphy_to_wl(wiphy); + struct wl_security *sec; + int32 wsec; + int32 err = 0; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + WL_DBG(("key index (%d)\n", key_idx)); + CHECK_SYS_UP(); + + memset(&key, 0, sizeof(key)); + key.index = key_idx; + swap_key_to_BE(&key); + memset(¶ms, 0, sizeof(params)); + params.key_len = (uint8)MIN(DOT11_MAX_KEY_SIZE, key.len); + memcpy(params.key, key.data, params.key_len); + + if (unlikely(err = wl_dev_ioctl(dev, WLC_GET_WSEC, &wsec, sizeof(wsec)))) { + WL_ERR(("WLC_GET_WSEC error (%d)\n", err)); + return err; + } + wsec = dtoh32(wsec); + switch (wsec) { + case WEP_ENABLED: + sec = wl_read_prof(wl, WL_PROF_SEC); + if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP40) { + params.cipher = WLAN_CIPHER_SUITE_WEP40; + WL_DBG(("WLAN_CIPHER_SUITE_WEP40\n")); + } else if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP104) { + params.cipher = WLAN_CIPHER_SUITE_WEP104; + WL_DBG(("WLAN_CIPHER_SUITE_WEP104\n")); + } + break; + case TKIP_ENABLED: + params.cipher = WLAN_CIPHER_SUITE_TKIP; + WL_DBG(("WLAN_CIPHER_SUITE_TKIP\n")); + break; + case AES_ENABLED: + params.cipher = WLAN_CIPHER_SUITE_AES_CMAC; + WL_DBG(("WLAN_CIPHER_SUITE_AES_CMAC\n")); + break; + default: + WL_ERR(("Invalid algo (0x%x)\n", wsec)); + return -EINVAL; + } + + callback(cookie, ¶ms); +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + return err; +} + +static int32 +wl_cfg80211_config_default_mgmt_key(struct wiphy *wiphy, + struct net_device *dev, uint8 key_idx) +{ + WL_INFO(("Not supported\n")); + CHECK_SYS_UP(); + return -EOPNOTSUPP; +} + +static int32 +wl_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev, + uint8 *mac, struct station_info *sinfo) + +{ + struct wl_priv *wl = wiphy_to_wl(wiphy); + scb_val_t scb_val; + int rssi; + int32 rate; + int32 err = 0; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + CHECK_SYS_UP(); + if (unlikely(memcmp(mac, wl_read_prof(wl, WL_PROF_BSSID), ETHER_ADDR_LEN))) { + WL_ERR(("Wrong Mac address\n")); + return -ENOENT; + } + + /* Report the current tx rate */ + if ((err = wl_dev_ioctl(dev, WLC_GET_RATE, &rate, sizeof(rate)))) { + WL_ERR(("Could not get rate (%d)\n", err)); + } else { + rate = dtoh32(rate); + sinfo->filled |= STATION_INFO_TX_BITRATE; + sinfo->txrate.legacy = rate * 5; + WL_DBG(("Rate %d Mbps\n", (rate/2))); + } + + if (test_bit(WL_STATUS_CONNECTED, &wl->status)) { + scb_val.val = 0; + if (unlikely(err = wl_dev_ioctl(dev, WLC_GET_RSSI, &scb_val, sizeof(scb_val_t)))) { + WL_ERR(("Could not get rssi (%d)\n", err)); + return err; + } + rssi = dtoh32(scb_val.val); + sinfo->filled |= STATION_INFO_SIGNAL; + sinfo->signal = rssi; + WL_DBG(("RSSI %d dBm\n", rssi)); + } +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + + return err; +} + +static int32 +wl_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, + bool enabled, int32 timeout) +{ + int32 pm; + int32 err = 0; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + CHECK_SYS_UP(); + pm = enabled ? PM_FAST : PM_OFF; + pm = htod32(pm); + WL_DBG(("power save %s\n", (pm ? "enabled" : "disabled"))); + if (unlikely((err = wl_dev_ioctl(dev, WLC_SET_PM, &pm, sizeof(pm))))) { + if (err == -ENODEV) { + WL_DBG(("net_device is not ready yet\n")); + } else { + WL_ERR(("error (%d)\n", err)); + } + return err; + } +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + return err; +} + +static __used uint32 +wl_find_msb(uint16 bit16) +{ + uint32 ret = 0; + + if (bit16 & 0xff00) { + ret += 8; + bit16 >>= 8; + } + + if (bit16 & 0xf0) { + ret += 4; + bit16 >>= 4; + } + + if (bit16 & 0xc) { + ret += 2; + bit16 >>= 2; + } + + if (bit16 & 2) + ret += bit16 & 2; + else if (bit16) + ret += bit16; + + return ret; +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33)) || \ + defined(CHROMIUMOS_COMPAT_WIRELESS) +static int32 +wl_cfg80211_set_bitrate_mask(struct wiphy *wiphy, struct net_device *dev, + const uint8 *addr, const struct cfg80211_bitrate_mask *mask) +{ + struct wl_rateset rateset; + int32 rate; + int32 val; + int32 err_bg; + int32 err_a; + uint32 legacy; + int32 err = 0; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + CHECK_SYS_UP(); + /* addr param is always NULL. ignore it */ + /* Get current rateset */ + if (unlikely((err = wl_dev_ioctl(dev, WLC_GET_CURR_RATESET, &rateset, + sizeof(rateset))))) { + WL_ERR(("could not get current rateset (%d)\n", err)); + return err; + } + + rateset.count = dtoh32(rateset.count); + + if (!(legacy = wl_find_msb(mask->control[IEEE80211_BAND_2GHZ].legacy))) + legacy = wl_find_msb(mask->control[IEEE80211_BAND_5GHZ].legacy); + + val = wl_g_rates[legacy-1].bitrate * 100000; + + if (val < rateset.count) { + /* Select rate by rateset index */ + rate = rateset.rates[val] & 0x7f; + } else { + /* Specified rate in bps */ + rate = val / 500000; + } + + WL_DBG(("rate %d mbps\n", (rate/2))); + + /* + * + * Set rate override, + * Since the is a/b/g-blind, both a/bg_rate are enforced. + */ + err_bg = wl_dev_intvar_set(dev, "bg_rate", rate); + err_a = wl_dev_intvar_set(dev, "a_rate", rate); + if (unlikely(err_bg && err_a)) { + WL_ERR(("could not set fixed rate (%d) (%d)\n", err_bg, err_a)); + return (err_bg | err_a); + } + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + + return err; +} +#else +static int32 +wl_cfg80211_set_bitrate_mask(struct wiphy *wiphy, struct net_device *dev, + const uint8 *addr, const struct cfg80211_bitrate_mask *mask) +{ + struct wl_rateset rateset; + int32 rate; + int32 val; + int32 err_bg; + int32 err_a; + int32 err = 0; + int i; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + CHECK_SYS_UP(); + /* addr param is always NULL. ignore it */ + /* Get current rateset */ + if (unlikely((err = wl_dev_ioctl(dev, WLC_GET_CURR_RATESET, &rateset, + sizeof(rateset))))) { + WL_ERR(("could not get current rateset (%d)\n", err)); + return err; + } + + rateset.count = dtoh32(rateset.count); + + if (mask->fixed || mask->maxrate) { + val = mask->fixed ? (mask->fixed * 1000) : (mask->maxrate * 1000); + if (val < rateset.count) { + /* Select rate by rateset index */ + rate = rateset.rates[val] & 0x7f; + } else { + /* Specified rate in bps */ + rate = val / 500000; + } + } else { + /* Select maximum rate */ + rate = rateset.rates[rateset.count - 1] & 0x7f; + } + + if (mask->fixed) { + /* + Set rate override, + Since the is a/b/g-blind, both a/bg_rate are enforced. + */ + err_bg = wl_dev_intvar_set(dev, "bg_rate", rate); + err_a = wl_dev_intvar_set(dev, "a_rate", rate); + if (unlikely(err_bg && err_a)) { + WL_ERR(("could not set fixed rate (%d) (%d)\n", err_bg, err_a)); + return (err_bg | err_a); + } + } else { + /* + clear rate override + Since the is a/b/g-blind, both a/bg_rate are enforced. + */ + /* 0 is for clearing rate override */ + err_bg = wl_dev_intvar_set(dev, "bg_rate", 0); + /* 0 is for clearing rate override */ + err_a = wl_dev_intvar_set(dev, "a_rate", 0); + + if (unlikely(err_bg && err_a)) { + WL_ERR(("could not set max rate (%d) (%d)\n", err_bg, err_a)); + return (err_bg | err_a); + } + + /* Remove rates above selected rate */ + for (i = 0; i < rateset.count; i++) + if ((rateset.rates[i] & 0x7f) > rate) + break; + rateset.count = htod32(i); + + /* Set current rateset */ + if (unlikely((err = wl_dev_ioctl(dev, WLC_SET_RATESET, &rateset, + sizeof(rateset))))) { + WL_ERR(("error (%d)\n", err)); + return err; + } + } +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + + return err; +} +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33)) */ + +static int32 +wl_cfg80211_resume(struct wiphy *wiphy) +{ + int32 err = 0; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + CHECK_SYS_UP(); + wl_invoke_iscan(wiphy_to_wl(wiphy)); +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + + return err; +} + +static int32 +wl_cfg80211_suspend(struct wiphy *wiphy) +{ + struct wl_priv *wl = wiphy_to_wl(wiphy); + int32 err = 0; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + CHECK_SYS_UP(); + + set_bit(WL_STATUS_SCAN_ABORTING, &wl->status); + wl_term_iscan(wl); + if (wl->scan_request) { + cfg80211_scan_done(wl->scan_request, TRUE); /* TRUE means abort */ + wl->scan_request = NULL; + } + clear_bit(WL_STATUS_SCANNING, &wl->status); + clear_bit(WL_STATUS_SCAN_ABORTING, &wl->status); + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + + return err; +} + +static __used int32 +wl_update_pmklist(struct net_device *dev, struct wl_pmk_list *pmk_list, int32 err) +{ + int8 eabuf[ETHER_ADDR_STR_LEN]; + int i, j; + + memset(eabuf, 0, ETHER_ADDR_STR_LEN); + + WL_DBG(("No of elements %d\n", pmk_list->pmkids.npmkid)); + for (i = 0; i < pmk_list->pmkids.npmkid; i++) { + WL_DBG(("PMKID[%d]: %s =\n", i, + bcm_ether_ntoa(&pmk_list->pmkids.pmkid[i].BSSID, + eabuf))); + for (j = 0; j < WPA2_PMKID_LEN; j++) { + WL_DBG(("%02x\n", pmk_list->pmkids.pmkid[i].PMKID[j])); + } + } + if (likely(!err)) { + err = wl_dev_bufvar_set(dev, "pmkid_info", (char *)pmk_list, + sizeof(*pmk_list)); + } + + return err; +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33)) || \ + defined(CHROMIUMOS_COMPAT_WIRELESS) +static int32 +wl_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_pmksa *pmksa) +{ + struct wl_priv *wl = wiphy_to_wl(wiphy); + int8 eabuf[ETHER_ADDR_STR_LEN]; + int32 err = 0; + int i; +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + + CHECK_SYS_UP(); + memset(eabuf, 0, ETHER_ADDR_STR_LEN); + for (i = 0; i < wl->pmk_list->pmkids.npmkid; i++) + if (!memcmp(pmksa->bssid, &wl->pmk_list->pmkids.pmkid[i].BSSID, + ETHER_ADDR_LEN)) + break; + if (i < WL_NUM_PMKIDS_MAX) { + memcpy(&wl->pmk_list->pmkids.pmkid[i].BSSID, pmksa->bssid, ETHER_ADDR_LEN); + memcpy(&wl->pmk_list->pmkids.pmkid[i].PMKID, pmksa->pmkid, WPA2_PMKID_LEN); + if (i == wl->pmk_list->pmkids.npmkid) + wl->pmk_list->pmkids.npmkid++; + } else { + err = -EINVAL; + } + WL_DBG(("set_pmksa,IW_PMKSA_ADD - PMKID: %s =\n", + bcm_ether_ntoa(&wl->pmk_list->pmkids.pmkid[wl->pmk_list->pmkids.npmkid].BSSID, + eabuf))); + for (i = 0; i < WPA2_PMKID_LEN; i++) { + WL_DBG(("%02x\n", + wl->pmk_list->pmkids.pmkid[wl->pmk_list->pmkids.npmkid].PMKID[i])); + } + + err = wl_update_pmklist(dev, wl->pmk_list, err); + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + + return err; +} + +static int32 +wl_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_pmksa *pmksa) +{ + struct wl_priv *wl = wiphy_to_wl(wiphy); + int8 eabuf[ETHER_ADDR_STR_LEN]; + struct _pmkid_list pmkid; + int32 err = 0; + int i; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + + CHECK_SYS_UP(); + memset(eabuf, 0, ETHER_ADDR_STR_LEN); + memcpy(&pmkid.pmkid[0].BSSID, pmksa->bssid, ETHER_ADDR_LEN); + memcpy(&pmkid.pmkid[0].PMKID, pmksa->pmkid, WPA2_PMKID_LEN); + + WL_DBG(("del_pmksa,IW_PMKSA_REMOVE - PMKID: %s =\n", + bcm_ether_ntoa(&pmkid.pmkid[0].BSSID, eabuf))); + for (i = 0; i < WPA2_PMKID_LEN; i++) { + WL_DBG(("%02x\n", pmkid.pmkid[0].PMKID[i])); + } + + for (i = 0; i < wl->pmk_list->pmkids.npmkid; i++) + if (!memcmp(pmksa->bssid, &wl->pmk_list->pmkids.pmkid[i].BSSID, ETHER_ADDR_LEN)) + break; + + if ((wl->pmk_list->pmkids.npmkid > 0) && (i < wl->pmk_list->pmkids.npmkid)) { + memset(&wl->pmk_list->pmkids.pmkid[i], 0, sizeof(pmkid_t)); + for (; i < (wl->pmk_list->pmkids.npmkid - 1); i++) { + memcpy(&wl->pmk_list->pmkids.pmkid[i].BSSID, + &wl->pmk_list->pmkids.pmkid[i+1].BSSID, ETHER_ADDR_LEN); + memcpy(&wl->pmk_list->pmkids.pmkid[i].PMKID, + &wl->pmk_list->pmkids.pmkid[i+1].PMKID, WPA2_PMKID_LEN); + } + wl->pmk_list->pmkids.npmkid--; + } + else { + err = -EINVAL; + } + + err = wl_update_pmklist(dev, wl->pmk_list, err); + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + return err; + +} + +static int32 +wl_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *dev) +{ + struct wl_priv *wl = wiphy_to_wl(wiphy); + int32 err = 0; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + CHECK_SYS_UP(); + memset(wl->pmk_list, 0, sizeof(*wl->pmk_list)); + err = wl_update_pmklist(dev, wl->pmk_list, err); +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + return err; + +} +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33)) */ + +static struct cfg80211_ops wl_cfg80211_ops = { + .change_virtual_intf = wl_cfg80211_change_iface, + .scan = wl_cfg80211_scan, + .set_wiphy_params = wl_cfg80211_set_wiphy_params, + .join_ibss = wl_cfg80211_join_ibss, + .leave_ibss = wl_cfg80211_leave_ibss, + .get_station = wl_cfg80211_get_station, + .set_tx_power = wl_cfg80211_set_tx_power, + .get_tx_power = wl_cfg80211_get_tx_power, + .add_key = wl_cfg80211_add_key, + .del_key = wl_cfg80211_del_key, + .get_key = wl_cfg80211_get_key, + .set_default_key = wl_cfg80211_config_default_key, + .set_default_mgmt_key = wl_cfg80211_config_default_mgmt_key, + .set_power_mgmt = wl_cfg80211_set_power_mgmt, + .set_bitrate_mask = wl_cfg80211_set_bitrate_mask, + .connect = wl_cfg80211_connect, + .disconnect = wl_cfg80211_disconnect, + .suspend = wl_cfg80211_suspend, + .resume = wl_cfg80211_resume, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33)) || \ + defined(CHROMIUMOS_COMPAT_WIRELESS) + .set_pmksa = wl_cfg80211_set_pmksa, + .del_pmksa = wl_cfg80211_del_pmksa, + .flush_pmksa = wl_cfg80211_flush_pmksa +#endif +}; + +static int32 +wl_mode_to_nl80211_iftype(int32 mode) +{ + int32 err = 0; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + switch (mode) { + case WL_MODE_BSS: + return NL80211_IFTYPE_STATION; + case WL_MODE_IBSS: + return NL80211_IFTYPE_ADHOC; + default: + return NL80211_IFTYPE_UNSPECIFIED; + } +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + + return err; +} + +static struct wireless_dev * +wl_alloc_wdev(int32 sizeof_iface, struct device *dev) +{ + struct wireless_dev *wdev; + int32 err = 0; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + wdev = kzalloc(sizeof(*wdev), GFP_KERNEL); + if (unlikely(!wdev)) { + WL_ERR(("Could not allocate wireless device\n")); + return ERR_PTR(-ENOMEM); + } + wdev->wiphy = wiphy_new(&wl_cfg80211_ops, sizeof(struct wl_priv) + sizeof_iface); + if (unlikely(!wdev->wiphy)) { + WL_ERR(("Couldn not allocate wiphy device\n")); + err = -ENOMEM; + goto wiphy_new_out; + } + set_wiphy_dev(wdev->wiphy, dev); + wdev->wiphy->max_scan_ssids = WL_NUM_SCAN_MAX; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33)) || \ + defined(CHROMIUMOS_COMPAT_WIRELESS) + wdev->wiphy->max_num_pmkids = WL_NUM_PMKIDS_MAX; +#endif + wdev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION)| BIT(NL80211_IFTYPE_ADHOC); + wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &__wl_band_2ghz; + wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = &__wl_band_5ghz_a; /* Set it as 11a by default. + ** This will be updated with + ** 11n phy tables in "ifconfig up" + ** if phy has 11n capability + */ + wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; + wdev->wiphy->cipher_suites = __wl_cipher_suites; + wdev->wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites); +#ifndef WL_POWERSAVE_DISABLED +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33)) || \ + defined(CHROMIUMOS_COMPAT_WIRELESS) + wdev->wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT; /* enable power + * save mode by default + */ +#else + wdev->wiphy->ps_default = TRUE; /* enable power save mode by default */ +#endif +#else +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33)) || \ + defined(CHROMIUMOS_COMPAT_WIRELESS) + wdev->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; +#else + wdev->wiphy->ps_default = FALSE; +#endif +#endif /* !WL_POWERSAVE_DISABLED */ + if (unlikely(((err = wiphy_register(wdev->wiphy)) < 0))) { + WL_ERR(("Couldn not register wiphy device (%d)\n", err)); + goto wiphy_register_out; + } + return wdev; + +wiphy_register_out: + wiphy_free(wdev->wiphy); + +wiphy_new_out: + kfree(wdev); + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + + return ERR_PTR(err); +} + +static void +wl_free_wdev(struct wl_priv *wl) +{ + struct wireless_dev *wdev = wl_to_wdev(wl); + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + if (unlikely(!wdev)) { + WL_ERR(("wdev is invalid\n")); + return; + } + wiphy_unregister(wdev->wiphy); + wiphy_free(wdev->wiphy); + kfree(wdev); + wl_to_wdev(wl) = NULL; +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif +} + +static int32 +wl_inform_bss(struct wl_priv *wl) +{ + struct wl_scan_results *bss_list; + struct wl_bss_info *bi = NULL; /* must be initialized */ + int32 err = 0; + int i; + + + bss_list = wl->bss_list; + if (unlikely(bss_list->version != WL_BSS_INFO_VERSION)) { + WL_ERR(("Version %d != WL_BSS_INFO_VERSION\n", bss_list->version)); + return -EOPNOTSUPP; + } + WL_DBG(("scanned AP count (%d)\n", bss_list->count)); + bi = next_bss(bss_list, bi); + for_each_bss(bss_list, bi, i) { + if (unlikely(err = wl_inform_single_bss(wl, bi))) + break; + } + return err; +} + +static int32 +wl_inform_single_bss(struct wl_priv *wl, struct wl_bss_info *bi) +{ + struct wiphy *wiphy = wl_to_wiphy(wl); + struct ieee80211_mgmt *mgmt; + struct ieee80211_channel *channel; + struct ieee80211_supported_band *band; + struct wl_cfg80211_bss_info *notif_bss_info; + struct wl_scan_req *sr = wl_to_sr(wl); + uint32 signal; + uint32 freq; + int32 err = 0; + + if (unlikely(dtoh32(bi->length) > WL_BSS_INFO_MAX)) { + WL_DBG(("Beacon size is larger than allocated buffer. Discard it!!\n")); + return err; + } + notif_bss_info = kzalloc(sizeof(*notif_bss_info) + sizeof(*mgmt) - sizeof(uint8) + + WL_BSS_INFO_MAX, GFP_KERNEL); + if (unlikely(!notif_bss_info)) { + WL_ERR(("notif_bss_info alloc failed\n")); + return -ENOMEM; + } + mgmt = (struct ieee80211_mgmt *)notif_bss_info->frame_buf; + notif_bss_info->channel = CHSPEC_CHANNEL(bi->chanspec); + if (notif_bss_info->channel <= CH_MAX_2G_CHANNEL) + band = wiphy->bands[IEEE80211_BAND_2GHZ]; + else + band = wiphy->bands[IEEE80211_BAND_5GHZ]; + notif_bss_info->rssi = bi->RSSI; + memcpy(mgmt->bssid, &bi->BSSID, ETHER_ADDR_LEN); + if (!memcmp(bi->SSID, sr->ssid.SSID, bi->SSID_len)) { + mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_PROBE_RESP); + } + mgmt->u.probe_resp.timestamp = 0; + mgmt->u.probe_resp.beacon_int = cpu_to_le16(bi->beacon_period); + mgmt->u.probe_resp.capab_info = cpu_to_le16(bi->capability); + wl_rst_ie(wl); + wl_add_ie(wl, WLAN_EID_SSID, bi->SSID_len, bi->SSID); + wl_add_ie(wl, WLAN_EID_SUPP_RATES, bi->rateset.count, bi->rateset.rates); + wl_mrg_ie(wl, ((uint8 *)bi) + bi->ie_offset, bi->ie_length); + wl_cp_ie(wl, mgmt->u.probe_resp.variable, WL_BSS_INFO_MAX - + offsetof(struct wl_cfg80211_bss_info, frame_buf)); + notif_bss_info->frame_len = offsetof(struct ieee80211_mgmt, u.probe_resp.variable) + + wl_get_ielen(wl); + freq = ieee80211_channel_to_frequency(notif_bss_info->channel); + channel = ieee80211_get_channel(wiphy, freq); + + WL_DBG(("SSID : \"%s\", rssi (%d), capability : 0x04%x\n", bi->SSID, notif_bss_info->rssi, + mgmt->u.probe_resp.capab_info)); + + signal = notif_bss_info->rssi * 100; + if (unlikely(!cfg80211_inform_bss_frame(wiphy, channel, mgmt, + le16_to_cpu(notif_bss_info->frame_len), signal, GFP_KERNEL))) { + WL_ERR(("cfg80211_inform_bss_frame error\n")); + kfree(notif_bss_info); + return -EINVAL; + } + kfree(notif_bss_info); + + return err; +} + +static bool +wl_is_linkup(struct wl_priv *wl, const wl_event_msg_t *e) +{ + uint32 event = ntoh32(e->event_type); + uint16 flags = ntoh16(e->flags); + + if (event == WLC_E_JOIN || event == WLC_E_ASSOC_IND || event == WLC_E_REASSOC_IND) { + return TRUE; + } else if (event == WLC_E_LINK) { + if (flags & WLC_EVENT_MSG_LINK) { + if (wl_is_ibssmode(wl)) { + if (wl_is_ibssstarter(wl)) { + } + } else { + + } + } + } + + return FALSE; +} + +static bool +wl_is_linkdown(struct wl_priv *wl, const wl_event_msg_t *e) +{ + uint32 event = ntoh32(e->event_type); + uint16 flags = ntoh16(e->flags); + + if (event == WLC_E_DEAUTH_IND || event == WLC_E_DISASSOC_IND) { + return TRUE; + } else if (event == WLC_E_LINK) { + if (!(flags & WLC_EVENT_MSG_LINK)) { + return TRUE; + } + } + + return FALSE; +} + +static int32 +wl_notify_connect_status(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void* data) +{ + bool act; + int32 err = 0; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + if (wl_is_linkup(wl, e)) { + wl_link_up(wl); + if (wl_is_ibssmode(wl)) { + cfg80211_ibss_joined(ndev, (int8 *)&e->addr, GFP_KERNEL); + WL_DBG(("joined in IBSS network\n")); + } else { + wl_bss_connect_done(wl, ndev, e, data); + WL_DBG(("joined in BSS network \"%s\"\n", + ((struct wlc_ssid *)wl_read_prof(wl, WL_PROF_SSID))->SSID)); + } + act = TRUE; + wl_update_prof(wl, e, &act, WL_PROF_ACT); + } else if (wl_is_linkdown(wl, e)) { + cfg80211_disconnected(ndev, 0, NULL, 0, GFP_KERNEL); + clear_bit(WL_STATUS_CONNECTED, &wl->status); + wl_link_down(wl); + wl_init_prof(wl->profile); + } +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + + return err; +} + +static int32 +wl_notify_roaming_status(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void* data) +{ + bool act; + int32 err = 0; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + wl_bss_roaming_done(wl, ndev, e, data); + act = TRUE; + wl_update_prof(wl, e, &act, WL_PROF_ACT); +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + + return err; +} + +static __used int32 +wl_dev_bufvar_set(struct net_device *dev, int8 *name, int8 *buf, int32 len) +{ + struct wl_priv *wl = ndev_to_wl(dev); + uint32 buflen; + + buflen = bcm_mkiovar(name, buf, len, wl->ioctl_buf, WL_IOCTL_LEN_MAX); + BUG_ON(unlikely(!buflen)); + + return (wl_dev_ioctl(dev, WLC_SET_VAR, wl->ioctl_buf, buflen)); +} + +static int32 +wl_dev_bufvar_get(struct net_device *dev, int8 *name, int8 *buf, int32 buf_len) +{ + struct wl_priv *wl = ndev_to_wl(dev); + uint32 len; + int32 err = 0; + + len = bcm_mkiovar(name, NULL, 0, wl->ioctl_buf, WL_IOCTL_LEN_MAX); + BUG_ON(unlikely(!len)); + if (unlikely((err = wl_dev_ioctl(dev, WLC_GET_VAR, (void *)wl->ioctl_buf, + WL_IOCTL_LEN_MAX)))) { + WL_ERR(("error (%d)\n", err)); + return err; + } + memcpy(buf, wl->ioctl_buf, buf_len); + + return err; +} + +static int32 +wl_get_assoc_ies(struct wl_priv *wl) +{ + struct net_device *ndev = wl_to_ndev(wl); + struct wl_assoc_ielen *assoc_info; + struct wl_connect_info *conn_info = wl_to_conn(wl); + uint32 req_len; + uint32 resp_len; + int32 err = 0; + + if (unlikely(err = wl_dev_bufvar_get(ndev, "assoc_info", wl->extra_buf, + WL_ASSOC_INFO_MAX))) { + WL_ERR(("could not get assoc info (%d)\n", err)); + return err; + } + assoc_info = (struct wl_assoc_ielen *)wl->extra_buf; + req_len = assoc_info->req_len; + resp_len = assoc_info->resp_len; + if (req_len) { + if (unlikely(err = wl_dev_bufvar_get(ndev, "assoc_req_ies", wl->extra_buf, + WL_ASSOC_INFO_MAX))) { + WL_ERR(("could not get assoc req (%d)\n", err)); + return err; + } + conn_info->req_ie_len = req_len; + conn_info->req_ie = kmemdup(wl->extra_buf, conn_info->req_ie_len, GFP_KERNEL); + } else { + conn_info->req_ie_len = 0; + conn_info->req_ie = NULL; + } + if (resp_len) { + if (unlikely(err = wl_dev_bufvar_get(ndev, "assoc_resp_ies", wl->extra_buf, + WL_ASSOC_INFO_MAX))) { + WL_ERR(("could not get assoc resp (%d)\n", err)); + return err; + } + conn_info->resp_ie_len = resp_len; + conn_info->resp_ie = kmemdup(wl->extra_buf, conn_info->resp_ie_len, GFP_KERNEL); + } else { + conn_info->resp_ie_len = 0; + conn_info->resp_ie = NULL; + } + WL_DBG(("req len (%d) resp len (%d)\n", conn_info->req_ie_len, conn_info->resp_ie_len)); + + return err; +} + +static int32 +wl_update_bss_info(struct wl_priv *wl) +{ + struct cfg80211_bss *bss; + struct wl_bss_info *bi; + struct wlc_ssid *ssid; + int32 err = 0; + + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + if (wl_is_ibssmode(wl)) + return err; + + ssid = (struct wlc_ssid *)wl_read_prof(wl, WL_PROF_SSID); + bss = cfg80211_get_bss(wl_to_wiphy(wl), NULL, (int8 *)&wl->bssid, ssid->SSID, + ssid->SSID_len, WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); + + rtnl_lock(); + if (unlikely(!bss)) { + WL_DBG(("Could not find the AP\n")); + *(uint32*)wl->extra_buf = htod32(WL_EXTRA_BUF_MAX); + if (unlikely(err = wl_dev_ioctl(wl_to_ndev(wl), WLC_GET_BSS_INFO, wl->extra_buf, + WL_EXTRA_BUF_MAX))) { + WL_ERR(("Could not get bss info %d\n", err)); + goto update_bss_info_out; + } + bi = (struct wl_bss_info *)(wl->extra_buf + 4); + if (unlikely(memcmp(&bi->BSSID, &wl->bssid, ETHER_ADDR_LEN))) { + err = -EIO; + goto update_bss_info_out; + } + if (unlikely((err = wl_inform_single_bss(wl, bi)))) + goto update_bss_info_out; + } else { + WL_DBG(("Found the AP in the list - BSSID %02x:%02x:%02x:%02x:%02x:%02x\n", + bss->bssid[0], bss->bssid[1], bss->bssid[2], bss->bssid[3], + bss->bssid[4], bss->bssid[5])); + cfg80211_put_bss(bss); + } +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + +update_bss_info_out: + rtnl_unlock(); + return err; +} + +static int32 +wl_bss_roaming_done(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void* data) +{ + struct wl_connect_info *conn_info = wl_to_conn(wl); + int32 err = 0; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + wl_get_assoc_ies(wl); + memcpy(&wl->bssid, &e->addr, ETHER_ADDR_LEN); + wl_update_bss_info(wl); + cfg80211_roamed(ndev, + (uint8 *)&wl->bssid, + conn_info->req_ie, conn_info->req_ie_len, + conn_info->resp_ie, conn_info->resp_ie_len, + GFP_KERNEL); + WL_DBG(("Report roaming result\n")); + + set_bit(WL_STATUS_CONNECTED, &wl->status); +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + + return err; +} + +static int32 +wl_bss_connect_done(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void* data) +{ + struct wl_connect_info *conn_info = wl_to_conn(wl); + int32 err = 0; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + wl_get_assoc_ies(wl); + memcpy(&wl->bssid, &e->addr, ETHER_ADDR_LEN); + wl_update_bss_info(wl); + if (test_and_clear_bit(WL_STATUS_CONNECTING, &wl->status)) { + cfg80211_connect_result(ndev, + (uint8 *)&wl->bssid, + conn_info->req_ie, conn_info->req_ie_len, + conn_info->resp_ie, conn_info->resp_ie_len, + WLAN_STATUS_SUCCESS, + GFP_KERNEL); + WL_DBG(("Report connect result\n")); + } else { + cfg80211_roamed(ndev, + (uint8 *)&wl->bssid, + conn_info->req_ie, conn_info->req_ie_len, + conn_info->resp_ie, conn_info->resp_ie_len, + GFP_KERNEL); + WL_DBG(("Report roaming result\n")); + } + set_bit(WL_STATUS_CONNECTED, &wl->status); +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + + return err; +} + +static int32 +wl_notify_mic_status(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void* data) +{ + uint16 flags = ntoh16(e->flags); + enum nl80211_key_type key_type; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + + rtnl_lock(); + if (flags & WLC_EVENT_MSG_GROUP) + key_type = NL80211_KEYTYPE_GROUP; + else + key_type = NL80211_KEYTYPE_PAIRWISE; + + cfg80211_michael_mic_failure(ndev, (uint8 *)&e->addr, key_type, -1, NULL, GFP_KERNEL); + rtnl_unlock(); + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + return 0; +} + +static int32 +wl_notify_scan_status(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void* data) +{ + struct channel_info channel_inform; + struct wl_scan_results *bss_list; + uint32 len = WL_SCAN_BUF_MAX; + int32 err = 0; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + + if (wl->iscan_on && wl->iscan_kickstart) + return wl_wakeup_iscan(wl_to_iscan(wl)); + + if (unlikely(!test_and_clear_bit(WL_STATUS_SCANNING, &wl->status))) { + WL_ERR(("Scan complete while device not scanning\n")); + return -EINVAL; + } + if (unlikely(!wl->scan_request)) { + } + rtnl_lock(); + if (unlikely((err = wl_dev_ioctl(ndev, WLC_GET_CHANNEL, &channel_inform, + sizeof(channel_inform))))) { + WL_ERR(("scan busy (%d)\n", err)); + goto scan_done_out; + } + channel_inform.scan_channel = dtoh32(channel_inform.scan_channel); + if (unlikely(channel_inform.scan_channel)) { + + WL_DBG(("channel_inform.scan_channel (%d)\n", channel_inform.scan_channel)); + } + wl->bss_list = wl->scan_results; + bss_list = wl->bss_list; + memset(bss_list, 0, len); + bss_list->buflen = htod32(len); + if (unlikely((err = wl_dev_ioctl(ndev, WLC_SCAN_RESULTS, bss_list, len)))) { + WL_ERR(("%s Scan_results error (%d)\n", ndev->name, err)); + err = -EINVAL; + goto scan_done_out; + } + bss_list->buflen = dtoh32(bss_list->buflen); + bss_list->version = dtoh32(bss_list->version); + bss_list->count = dtoh32(bss_list->count); + + if ((err = wl_inform_bss(wl))) { + goto scan_done_out; + } + +scan_done_out : + if (wl->scan_request) { + cfg80211_scan_done(wl->scan_request, FALSE); + wl->scan_request = NULL; + } +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + rtnl_unlock(); + return err; +} + +static void +wl_init_conf(struct wl_conf *conf) +{ + conf->mode = (uint32)-1; + conf->frag_threshold = (uint32)-1; + conf->rts_threshold = (uint32)-1; + conf->retry_short = (uint32)-1; + conf->retry_long = (uint32)-1; + conf->tx_power = -1; +} + +static void +wl_init_prof(struct wl_profile *prof) +{ + memset(prof, 0, sizeof(*prof)); +} + +static void +wl_init_eloop_handler(struct wl_event_loop *el) +{ + memset(el, 0, sizeof(*el)); + el->handler[WLC_E_SCAN_COMPLETE] = wl_notify_scan_status; + el->handler[WLC_E_JOIN] = wl_notify_connect_status; + el->handler[WLC_E_LINK] = wl_notify_connect_status; + el->handler[WLC_E_DEAUTH_IND] = wl_notify_connect_status; + el->handler[WLC_E_DISASSOC_IND] = wl_notify_connect_status; + el->handler[WLC_E_ASSOC_IND] = wl_notify_connect_status; + el->handler[WLC_E_REASSOC_IND] = wl_notify_connect_status; + el->handler[WLC_E_ROAM] = wl_notify_roaming_status; + el->handler[WLC_E_MIC_ERROR] = wl_notify_mic_status; +} + +static int32 +wl_init_priv_mem(struct wl_priv *wl) +{ + wl->scan_results = (void *)kzalloc(WL_SCAN_BUF_MAX, GFP_KERNEL); + if (unlikely(!wl->scan_results)) { + WL_ERR(("Scan results alloc failed\n")); + goto init_priv_mem_out; + } + wl->conf = (void *)kzalloc(sizeof(*wl->conf), GFP_KERNEL); + if (unlikely(!wl->conf)) { + WL_ERR(("wl_conf alloc failed\n")); + goto init_priv_mem_out; + } + wl->profile = (void *)kzalloc(sizeof(*wl->profile), GFP_KERNEL); + if (unlikely(!wl->profile)) { + WL_ERR(("wl_profile alloc failed\n")); + goto init_priv_mem_out; + } + wl->bss_info = (void *)kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL); + if (unlikely(!wl->bss_info)) { + WL_ERR(("Bss information alloc failed\n")); + goto init_priv_mem_out; + } + wl->scan_req_int = (void *)kzalloc(sizeof(*wl->scan_req_int), GFP_KERNEL); + if (unlikely(!wl->scan_req_int)) { + WL_ERR(("Scan req alloc failed\n")); + goto init_priv_mem_out; + } + wl->ioctl_buf = (void *)kzalloc(WL_IOCTL_LEN_MAX, GFP_KERNEL); + if (unlikely(!wl->ioctl_buf)) { + WL_ERR(("Ioctl buf alloc failed\n")); + goto init_priv_mem_out; + } + wl->extra_buf = (void *)kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL); + if (unlikely(!wl->extra_buf)) { + WL_ERR(("Extra buf alloc failed\n")); + goto init_priv_mem_out; + } + wl->iscan = (void *)kzalloc(sizeof(*wl->iscan), GFP_KERNEL); + if (unlikely(!wl->iscan)) { + WL_ERR(("Iscan buf alloc failed\n")); + goto init_priv_mem_out; + } + wl->fw = (void *)kzalloc(sizeof(*wl->fw), GFP_KERNEL); + if (unlikely(!wl->fw)) { + WL_ERR(("fw object alloc failed\n")); + goto init_priv_mem_out; + } + wl->pmk_list = (void *)kzalloc(sizeof(*wl->pmk_list), GFP_KERNEL); + if (unlikely(!wl->pmk_list)) { + WL_ERR(("pmk list alloc failed\n")); + goto init_priv_mem_out; + } + + return 0; + +init_priv_mem_out: + wl_deinit_priv_mem(wl); + + return -ENOMEM; +} + +static void +wl_deinit_priv_mem(struct wl_priv *wl) +{ + if (wl->scan_results) { + kfree(wl->scan_results); + wl->scan_results = NULL; + } + if (wl->bss_info) { + kfree(wl->bss_info); + wl->bss_info = NULL; + } + if (wl->conf) { + kfree(wl->conf); + wl->conf = NULL; + } + if (wl->profile) { + kfree(wl->profile); + wl->profile = NULL; + } + if (wl->scan_req_int) { + kfree(wl->scan_req_int); + wl->scan_req_int = NULL; + } + if (wl->ioctl_buf) { + kfree(wl->ioctl_buf); + wl->ioctl_buf = NULL; + } + if (wl->extra_buf) { + kfree(wl->extra_buf); + wl->extra_buf = NULL; + } + if (wl->iscan) { + kfree(wl->iscan); + wl->iscan = NULL; + } + if (wl->fw) { + kfree(wl->fw); + wl->fw = NULL; + } + if (wl->pmk_list) { + kfree(wl->pmk_list); + wl->pmk_list = NULL; + } +} + + +static int32 +wl_create_event_handler(struct wl_priv *wl) +{ + sema_init(&wl->event_sync, 0); + init_completion(&wl->event_exit); + if (unlikely(((wl->event_pid = kernel_thread(wl_event_handler, wl, 0)) < 0))) { + WL_ERR(("failed to create event thread\n")); + return -ENOMEM; + } + WL_DBG(("pid %d\n", wl->event_pid)); + return 0; +} + +static void +wl_destroy_event_handler(struct wl_priv *wl) +{ + if (wl->event_pid >= 0) { + KILL_PROC(wl->event_pid, SIGTERM); + wait_for_completion(&wl->event_exit); + } +} + +static void +wl_term_iscan(struct wl_priv *wl) +{ + struct wl_iscan_ctrl *iscan = wl_to_iscan(wl); + + if (wl->iscan_on && iscan->pid >= 0) { + iscan->state = WL_ISCAN_STATE_IDLE; + KILL_PROC(iscan->pid, SIGTERM); + wait_for_completion(&iscan->exited); + iscan->pid = -1; + } +} + +static void +wl_notify_iscan_complete(struct wl_iscan_ctrl *iscan, bool aborted) +{ + struct wl_priv *wl = iscan_to_wl(iscan); + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + if (unlikely(!test_and_clear_bit(WL_STATUS_SCANNING, &wl->status))) { + WL_ERR(("Scan complete while device not scanning\n")); + return; + } + if (likely(wl->scan_request)) { + cfg80211_scan_done(wl->scan_request, aborted); + wl->scan_request = NULL; + } + wl->iscan_kickstart = FALSE; +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif +} + +static int32 +wl_wakeup_iscan(struct wl_iscan_ctrl *iscan) +{ + if (likely(iscan->state != WL_ISCAN_STATE_IDLE)) { + WL_DBG(("wake up iscan\n")); + up(&iscan->sync); + return 0; + } + + return -EIO; +} + +static int32 +wl_get_iscan_results(struct wl_iscan_ctrl *iscan, uint32 *status, struct wl_scan_results **bss_list) +{ + struct wl_iscan_results list; + struct wl_scan_results *results; + struct wl_iscan_results *list_buf; + int32 err = 0; + + memset(iscan->scan_buf, 0, WL_ISCAN_BUF_MAX); + list_buf = (struct wl_iscan_results *)iscan->scan_buf; + results = &list_buf->results; + results->buflen = WL_ISCAN_RESULTS_FIXED_SIZE; + results->version = 0; + results->count = 0; + + memset(&list, 0, sizeof(list)); + list.results.buflen = htod32(WL_ISCAN_BUF_MAX); + if (unlikely((err = wl_dev_iovar_getbuf( + iscan->dev, + "iscanresults", + &list, + WL_ISCAN_RESULTS_FIXED_SIZE, + iscan->scan_buf, + WL_ISCAN_BUF_MAX)))) { + WL_ERR(("error (%d)\n", err)); + return err; + } + results->buflen = dtoh32(results->buflen); + results->version = dtoh32(results->version); + results->count = dtoh32(results->count); + WL_DBG(("results->count = %d\n", results->count)); + WL_DBG(("results->buflen = %d\n", results->buflen)); + *status = dtoh32(list_buf->status); + *bss_list = results; + + return err; +} + +static int32 +wl_iscan_done(struct wl_priv *wl) +{ + struct wl_iscan_ctrl *iscan = wl->iscan; + int32 err = 0; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + iscan->state = WL_ISCAN_STATE_IDLE; + rtnl_lock(); + wl_inform_bss(wl); + wl_notify_iscan_complete(iscan, FALSE); + rtnl_unlock(); +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + + return err; +} + +static int32 +wl_iscan_pending(struct wl_priv *wl) +{ + struct wl_iscan_ctrl *iscan = wl->iscan; + int32 err = 0; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + /* Reschedule the timer */ + mod_timer(&iscan->timer, jiffies + iscan->timer_ms*HZ/1000); + iscan->timer_on = 1; +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + + return err; +} + +static int32 +wl_iscan_inprogress(struct wl_priv *wl) +{ + struct wl_iscan_ctrl *iscan = wl->iscan; + int32 err = 0; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + rtnl_lock(); + wl_inform_bss(wl); + wl_run_iscan(iscan, NULL, WL_SCAN_ACTION_CONTINUE); + rtnl_unlock(); + /* Reschedule the timer */ + mod_timer(&iscan->timer, jiffies + iscan->timer_ms*HZ/1000); + iscan->timer_on = 1; +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + + return err; +} + +static int32 +wl_iscan_aborted(struct wl_priv *wl) +{ + struct wl_iscan_ctrl *iscan = wl->iscan; + int32 err = 0; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + iscan->state = WL_ISCAN_STATE_IDLE; + rtnl_lock(); + wl_notify_iscan_complete(iscan, TRUE); + rtnl_unlock(); +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + + return err; +} + +static int32 +wl_iscan_thread(void *data) +{ + struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 }; + struct wl_iscan_ctrl *iscan = (struct wl_iscan_ctrl *)data; + struct wl_priv *wl = iscan_to_wl(iscan); + struct wl_iscan_eloop *el = &iscan->el; + uint32 status; + int err = 0; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + sched_setscheduler(current, SCHED_FIFO, ¶m); + status = WL_SCAN_RESULTS_PARTIAL; + while (likely(!down_interruptible(&iscan->sync))) { + if (iscan->timer_on) { + del_timer_sync(&iscan->timer); + iscan->timer_on = 0; + } + rtnl_lock(); + if (unlikely((err = wl_get_iscan_results(iscan, &status, &wl->bss_list)))) { + status = WL_SCAN_RESULTS_ABORTED; + WL_ERR(("Abort iscan\n")); + } + rtnl_unlock(); + el->handler[status](wl); + } + if (iscan->timer_on) { + del_timer_sync(&iscan->timer); + iscan->timer_on = 0; + } + complete_and_exit(&iscan->exited, 0); +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + + return 0; +} + +static void +wl_iscan_timer(ulong data) +{ + struct wl_iscan_ctrl *iscan = (struct wl_iscan_ctrl *)data; + + if (iscan) { + iscan->timer_on = 0; + WL_DBG(("timer expired\n")); + wl_wakeup_iscan(iscan); + } +} + +static int32 +wl_invoke_iscan(struct wl_priv *wl) +{ + struct wl_iscan_ctrl *iscan = wl_to_iscan(wl); + int err = 0; + + if (wl->iscan_on && iscan->pid < 0) { + iscan->state = WL_ISCAN_STATE_IDLE; + sema_init(&iscan->sync, 0); + init_completion(&iscan->exited); + iscan->pid = kernel_thread(wl_iscan_thread, iscan, 0); + if (unlikely(iscan->pid < 0)) { + WL_ERR(("Could not create iscan thread\n")); + return -ENOMEM; + } + } + + return err; +} + +static void +wl_init_iscan_eloop(struct wl_iscan_eloop *el) +{ + memset(el, 0, sizeof(*el)); + el->handler[WL_SCAN_RESULTS_SUCCESS] = wl_iscan_done; + el->handler[WL_SCAN_RESULTS_PARTIAL] = wl_iscan_inprogress; + el->handler[WL_SCAN_RESULTS_PENDING] = wl_iscan_pending; + el->handler[WL_SCAN_RESULTS_ABORTED] = wl_iscan_aborted; + el->handler[WL_SCAN_RESULTS_NO_MEM] = wl_iscan_aborted; +} + +static int32 +wl_init_iscan(struct wl_priv *wl) +{ + struct wl_iscan_ctrl *iscan = wl_to_iscan(wl); + int err = 0; + + if (wl->iscan_on) { + iscan->dev = wl_to_ndev(wl); + iscan->state = WL_ISCAN_STATE_IDLE; + wl_init_iscan_eloop(&iscan->el); + iscan->timer_ms = WL_ISCAN_TIMER_INTERVAL_MS; + init_timer(&iscan->timer); + iscan->timer.data = (ulong)iscan; + iscan->timer.function = wl_iscan_timer; + sema_init(&iscan->sync, 0); + init_completion(&iscan->exited); + iscan->pid = kernel_thread(wl_iscan_thread, iscan, 0); + if (unlikely(iscan->pid < 0)) { + WL_ERR(("Could not create iscan thread\n")); + return -ENOMEM; + } + iscan->data = wl; + } + + return err; +} + +static void +wl_init_fw(struct wl_fw_ctrl *fw) +{ + fw->status = 0; /* init fw loading status. 0 means nothing was loaded yet */ +} + +static int32 +wl_init_priv(struct wl_priv *wl) +{ + struct wiphy *wiphy = wl_to_wiphy(wl); + int32 err = 0; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + wl->scan_request = NULL; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33)) || \ + defined(CHROMIUMOS_COMPAT_WIRELESS) + wl->pwr_save = !!(wiphy->flags & WIPHY_FLAG_PS_ON_BY_DEFAULT); +#else + wl->pwr_save = wiphy->ps_default; +#endif +#ifndef WL_ISCAN_DISABLED + wl->iscan_on = TRUE; /* iscan on & off switch. we enable iscan per default */ +#else + wl->iscan_on = FALSE; +#endif /* WL_ISCAN_DISABLED */ +#ifndef WL_ROAM_DISABLED + wl->roam_on = TRUE; /* roam on & off switch. we enable roam per default */ +#else + wl->roam_on = FALSE; +#endif /* WL_ROAM_DISABLED */ + + wl->iscan_kickstart = FALSE; + wl->active_scan = TRUE; /* we do active scan for specific scan per default */ + wl->dongle_up = FALSE; /* dongle is not up yet */ + wl_init_eq(wl); + if (unlikely((err = wl_init_priv_mem(wl)))) + return err; + if (unlikely(wl_create_event_handler(wl))) + return -ENOMEM; + wl_init_eloop_handler(&wl->el); + mutex_init(&wl->usr_sync); + if (unlikely((err = wl_init_iscan(wl)))) + return err; + wl_init_fw(wl->fw); + wl_init_conf(wl->conf); + wl_init_prof(wl->profile); + wl_link_down(wl); +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + + return err; +} + +static void +wl_deinit_priv(struct wl_priv *wl) +{ +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + wl_destroy_event_handler(wl); + wl->dongle_up = FALSE; /* dongle down */ + wl_flush_eq(wl); + wl_link_down(wl); + wl_term_iscan(wl); + wl_deinit_priv_mem(wl); +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif +} + +int32 +wl_cfg80211_attach(struct net_device *ndev, void *data) +{ + struct wireless_dev *wdev; + struct wl_priv *wl; + struct wl_iface *ci; + int32 err = 0; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + if (unlikely(!ndev)) { + WL_ERR(("ndev is invaild\n")); + return -ENODEV; + } + wl_cfg80211_dev = kzalloc(sizeof(struct wl_dev), GFP_KERNEL); + if (unlikely(!wl_cfg80211_dev)) { + WL_ERR(("wl_cfg80211_dev is invalid\n")); + return -ENOMEM; + } + WL_DBG(("func %p\n", wl_sdio_func())); +#ifndef WL_CFG80211_LOCALTEST + wdev = wl_alloc_wdev(sizeof(struct wl_iface), &wl_sdio_func()->dev); +#else + wdev = wl_alloc_wdev(sizeof(struct wl_iface), NULL); +#endif + if (unlikely(IS_ERR(wdev))) + return -ENOMEM; + + wdev->iftype = wl_mode_to_nl80211_iftype(WL_MODE_BSS); + wl = wdev_to_wl(wdev); + wl->wdev = wdev; + wl->pub = data; + ci = (struct wl_iface *)wl_to_ci(wl); + ci->wl = wl; + ndev->ieee80211_ptr = wdev; + SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy)); + wdev->netdev = ndev; + if (unlikely((err = wl_init_priv(wl)))) { + WL_ERR(("Failed to init iwm_priv (%d)\n", err)); + goto cfg80211_attach_out; + } + wl_set_drvdata(wl_cfg80211_dev, ci); + set_bit(WL_STATUS_READY, &wl->status); +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + + return err; + +cfg80211_attach_out: + wl_free_wdev(wl); + return err; +} + +void +wl_cfg80211_detach(void) +{ + struct wl_priv *wl; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + wl = WL_PRIV_GET(); + + wl_deinit_priv(wl); + wl_free_wdev(wl); + wl_set_drvdata(wl_cfg80211_dev, NULL); + kfree(wl_cfg80211_dev); + wl_cfg80211_dev = NULL; + wl_clear_sdio_func(); +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif +} + +static void +wl_wakeup_event(struct wl_priv *wl) +{ + up(&wl->event_sync); +} + +static int32 +wl_event_handler(void *data) +{ + struct wl_priv *wl = (struct wl_priv *)data; + struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 }; + struct wl_event_q *e; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + sched_setscheduler(current, SCHED_FIFO, ¶m); + while (likely(!down_interruptible(&wl->event_sync))) { + if (unlikely(!(e = wl_deq_event(wl)))) { + WL_ERR(("eqeue empty..\n")); + BUG(); + } + WL_DBG(("event type (%d)\n", e->etype)); + if (wl->el.handler[e->etype]) { + wl->el.handler[e->etype](wl, wl_to_ndev(wl), &e->emsg, e->edata); + } else { + WL_DBG(("Unknown Event (%d): ignoring\n", e->etype)); + } + wl_put_event(e); + } + complete_and_exit(&wl->event_exit, 0); +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif +} + +void +wl_cfg80211_event(struct net_device *ndev, const wl_event_msg_t *e, void* data) +{ + uint32 event_type = ntoh32(e->event_type); + struct wl_priv *wl = ndev_to_wl(ndev); +#if (WL_DBG_LEVEL > 0) + int8 *estr = (event_type <= sizeof(wl_dbg_estr)/WL_DBG_ESTR_MAX-1) ? + wl_dbg_estr[event_type] : (int8 *)"Unknown"; +#endif /* (WL_DBG_LEVEL > 0) */ + WL_DBG(("event_type (%d):""WLC_E_""%s\n", event_type, estr)); + if (likely(!wl_enq_event(wl, event_type, e, data))) + wl_wakeup_event(wl); +} + +static void +wl_init_eq(struct wl_priv *wl) +{ + wl_init_eq_lock(wl); + INIT_LIST_HEAD(&wl->eq_list); +} + +static void +wl_flush_eq(struct wl_priv *wl) +{ + struct wl_event_q *e; + + wl_lock_eq(wl); + while (!list_empty(&wl->eq_list)) { + e = list_first_entry(&wl->eq_list, struct wl_event_q, eq_list); + list_del(&e->eq_list); + kfree(e); + } + wl_unlock_eq(wl); +} + +/* +* retrieve first queued event from head +*/ + +static struct wl_event_q * +wl_deq_event(struct wl_priv *wl) +{ + struct wl_event_q *e = NULL; + + wl_lock_eq(wl); + if (likely(!list_empty(&wl->eq_list))) { + e = list_first_entry(&wl->eq_list, struct wl_event_q, eq_list); + list_del(&e->eq_list); + } + wl_unlock_eq(wl); + + return e; +} + +/* +** push event to tail of the queue +*/ + +static int32 +wl_enq_event(struct wl_priv *wl, uint32 event, const wl_event_msg_t *msg, void *data) +{ + struct wl_event_q *e; + int32 err = 0; + + if (unlikely(!(e = kzalloc(sizeof(struct wl_event_q), GFP_KERNEL)))) { + WL_ERR(("event alloc failed\n")); + return -ENOMEM; + } + + e->etype = event; + memcpy(&e->emsg, msg, sizeof(wl_event_msg_t)); + if (data) { + } + wl_lock_eq(wl); + list_add_tail(&e->eq_list, &wl->eq_list); + wl_unlock_eq(wl); + + return err; +} + +static void +wl_put_event(struct wl_event_q *e) +{ + kfree(e); +} + +void +wl_cfg80211_sdio_func(void *func) +{ + cfg80211_sdio_func = (struct sdio_func *)func; +} + +static void +wl_clear_sdio_func(void) +{ + cfg80211_sdio_func = NULL; +} + + +static struct sdio_func * +wl_sdio_func(void) +{ + return cfg80211_sdio_func; +} + +static int32 +wl_dongle_mode(struct net_device *ndev, int32 iftype) +{ + int32 infra = 0; + int32 ap = 0; + int32 err = 0; + + switch (iftype) { + case NL80211_IFTYPE_MONITOR: + case NL80211_IFTYPE_WDS: + WL_ERR(("type (%d) : currently we do not support this mode\n", iftype)); + err = -EINVAL; + return err; + case NL80211_IFTYPE_ADHOC: + break; + case NL80211_IFTYPE_STATION: + infra = 1; + break; + default: + err = -EINVAL; + WL_ERR(("invalid type (%d)\n", iftype)); + return err; + } + infra = htod32(infra); + ap = htod32(ap); + WL_DBG(("%s ap (%d), infra (%d)\n", ndev->name, ap, infra)); + if (unlikely(err = wl_dev_ioctl(ndev, WLC_SET_INFRA, &infra, sizeof(infra))) || + unlikely(err = wl_dev_ioctl(ndev, WLC_SET_AP, &ap, sizeof(ap)))) { + WL_ERR(("WLC_SET_INFRA error (%d)\n", err)); + return err; + } + + return -EINPROGRESS; +} + +#ifndef EMBEDDED_PLATFORM +static int32 +wl_dongle_country(struct net_device *ndev, uint8 ccode) +{ + + int32 err = 0; + + + return err; +} + +static int32 +wl_dongle_up(struct net_device *ndev, uint32 up) +{ + int32 err = 0; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + if (unlikely(err = wl_dev_ioctl(ndev, WLC_UP, &up, sizeof(up)))) { + WL_ERR(("WLC_UP error (%d)\n", err)); + } +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + return err; +} + +static int32 +wl_dongle_power(struct net_device *ndev, uint32 power_mode) +{ + int32 err = 0; + + if (unlikely(err = wl_dev_ioctl(ndev, WLC_SET_PM, &power_mode, sizeof(power_mode)))) { + WL_ERR(("WLC_SET_PM error (%d)\n", err)); + } + return err; +} + +static int32 +wl_dongle_glom(struct net_device *ndev, uint32 glom, uint32 dongle_align) +{ + int8 iovbuf[WL_EVENTING_MASK_LEN + 12]; /* Room for "event_msgs" + '\0' + bitvec */ + int32 err = 0; + + /* Match Host and Dongle rx alignment */ + bcm_mkiovar("bus:txglomalign", (char *)&dongle_align, 4, iovbuf, sizeof(iovbuf)); + if (unlikely(err = wl_dev_ioctl(ndev, WLC_SET_VAR, iovbuf, sizeof(iovbuf)))) { + WL_ERR(("txglomalign error (%d)\n", err)); + goto dongle_glom_out; + } + /* disable glom option per default */ + bcm_mkiovar("bus:txglom", (char *)&glom, 4, iovbuf, sizeof(iovbuf)); + if (unlikely(err = wl_dev_ioctl(ndev, WLC_SET_VAR, iovbuf, sizeof(iovbuf)))) { + WL_ERR(("txglom error (%d)\n", err)); + goto dongle_glom_out; + } +dongle_glom_out : + return err; +} + +static int32 +wl_dongle_roam(struct net_device *ndev, uint32 roamvar, uint32 bcn_timeout) +{ + int8 iovbuf[WL_EVENTING_MASK_LEN + 12]; /* Room for "event_msgs" + '\0' + bitvec */ + int32 err = 0; + + /* Setup timeout if Beacons are lost and roam is off to report link down */ + if (roamvar) { + bcm_mkiovar("bcn_timeout", (char *)&bcn_timeout, 4, iovbuf, sizeof(iovbuf)); + if (unlikely(err = wl_dev_ioctl(ndev, WLC_SET_VAR, iovbuf, sizeof(iovbuf)))) { + WL_ERR(("bcn_timeout error (%d)\n", err)); + goto dongle_rom_out; + } + } + /* Enable/Disable built-in roaming to allow supplicant to take care of roaming */ + bcm_mkiovar("roam_off", (char *)&roamvar, 4, iovbuf, sizeof(iovbuf)); + if (unlikely(err = wl_dev_ioctl(ndev, WLC_SET_VAR, iovbuf, sizeof(iovbuf)))) { + WL_ERR(("roam_off error (%d)\n", err)); + goto dongle_rom_out; + } +dongle_rom_out : + return err; +} + +static int32 +wl_dongle_eventmsg(struct net_device *ndev) +{ + + int8 iovbuf[WL_EVENTING_MASK_LEN + 12]; /* Room for "event_msgs" + '\0' + bitvec */ + int8 eventmask[WL_EVENTING_MASK_LEN]; + int32 err = 0; + + /* Setup event_msgs */ + bcm_mkiovar("event_msgs", eventmask, WL_EVENTING_MASK_LEN, iovbuf, sizeof(iovbuf)); + if (unlikely(err = wl_dev_ioctl(ndev, WLC_GET_VAR, iovbuf, sizeof(iovbuf)))) { + WL_ERR(("Get event_msgs error (%d)\n", err)); + goto dongle_eventmsg_out; + } + memcpy(eventmask, iovbuf, WL_EVENTING_MASK_LEN); + + setbit(eventmask, WLC_E_SET_SSID); + setbit(eventmask, WLC_E_PRUNE); + setbit(eventmask, WLC_E_AUTH); + setbit(eventmask, WLC_E_REASSOC); + setbit(eventmask, WLC_E_REASSOC_IND); + setbit(eventmask, WLC_E_DEAUTH_IND); + setbit(eventmask, WLC_E_DISASSOC_IND); + setbit(eventmask, WLC_E_DISASSOC); + setbit(eventmask, WLC_E_JOIN); + setbit(eventmask, WLC_E_ASSOC_IND); + setbit(eventmask, WLC_E_PSK_SUP); + setbit(eventmask, WLC_E_LINK); + setbit(eventmask, WLC_E_NDIS_LINK); + setbit(eventmask, WLC_E_MIC_ERROR); + setbit(eventmask, WLC_E_PMKID_CACHE); + setbit(eventmask, WLC_E_TXFAIL); + setbit(eventmask, WLC_E_JOIN_START); + setbit(eventmask, WLC_E_SCAN_COMPLETE); + + bcm_mkiovar("event_msgs", eventmask, WL_EVENTING_MASK_LEN, iovbuf, sizeof(iovbuf)); + if (unlikely(err = wl_dev_ioctl(ndev, WLC_SET_VAR, iovbuf, sizeof(iovbuf)))) { + WL_ERR(("Set event_msgs error (%d)\n", err)); + goto dongle_eventmsg_out; + } + +dongle_eventmsg_out : + return err; +} + +static int32 +wl_dongle_scantime(struct net_device *ndev, int32 scan_assoc_time, int32 scan_unassoc_time) +{ + int32 err = 0; + + if ((err = wl_dev_ioctl(ndev, WLC_SET_SCAN_CHANNEL_TIME, &scan_assoc_time, + sizeof(scan_assoc_time)))) { + if (err == -EOPNOTSUPP) { + WL_INFO(("Scan assoc time is not supported\n")); + } else { + WL_ERR(("Scan assoc time error (%d)\n", err)); + } + goto dongle_scantime_out; + } + if ((err = wl_dev_ioctl(ndev, WLC_SET_SCAN_UNASSOC_TIME, &scan_unassoc_time, + sizeof(scan_unassoc_time)))) { + if (err == -EOPNOTSUPP) { + WL_INFO(("Scan unassoc time is not supported\n")); + } else { + WL_ERR(("Scan unassoc time error (%d)\n", err)); + } + goto dongle_scantime_out; + } + +dongle_scantime_out : + return err; +} + +static int32 +wl_dongle_offload(struct net_device *ndev, int32 arpoe, int32 arp_ol) +{ + int8 iovbuf[WL_EVENTING_MASK_LEN + 12]; /* Room for "event_msgs" + '\0' + bitvec */ + int32 err = 0; + + /* Set ARP offload */ + bcm_mkiovar("arpoe", (char *)&arpoe, 4, iovbuf, sizeof(iovbuf)); + if ((err = wl_dev_ioctl(ndev, WLC_SET_VAR, iovbuf, sizeof(iovbuf)))) { + if (err == -EOPNOTSUPP) { + WL_INFO(("arpoe is not supported\n")); + } else { + WL_ERR(("arpoe error (%d)\n", err)); + } + goto dongle_offload_out; + } + bcm_mkiovar("arp_ol", (char *)&arp_ol, 4, iovbuf, sizeof(iovbuf)); + if ((err = wl_dev_ioctl(ndev, WLC_SET_VAR, iovbuf, sizeof(iovbuf)))) { + if (err == -EOPNOTSUPP) { + WL_INFO(("arp_ol is not supported\n")); + } else { + WL_ERR(("arp_ol error (%d)\n", err)); + } + goto dongle_offload_out; + } + +dongle_offload_out : + return err; +} + +static int32 +wl_pattern_atoh(int8 *src, int8 *dst) +{ +#define strtoul(nptr, endptr, base) bcm_strtoul((nptr), (endptr), (base)) + int i; + if (strncmp(src, "0x", 2) != 0 && + strncmp(src, "0X", 2) != 0) { + WL_ERR(("Mask invalid format. Needs to start with 0x\n")); + return -1; + } + src = src + 2; /* Skip past 0x */ + if (strlen(src) % 2 != 0) { + WL_ERR(("Mask invalid format. Needs to be of even length\n")); + return -1; + } + for (i = 0; *src != '\0'; i++) { + char num[3]; + strncpy(num, src, 2); + num[2] = '\0'; + dst[i] = (uint8)strtoul(num, NULL, 16); + src += 2; + } + return i; +} + +static int32 +wl_dongle_filter(struct net_device *ndev, uint32 filter_mode) +{ + int8 iovbuf[WL_EVENTING_MASK_LEN + 12]; /* Room for "event_msgs" + '\0' + bitvec */ + const int8 *str; + struct wl_pkt_filter pkt_filter; + struct wl_pkt_filter *pkt_filterp; + int32 buf_len; + int32 str_len; + uint32 mask_size; + uint32 pattern_size; + int8 buf[256]; + int32 err = 0; + +/* add a default packet filter pattern */ + str = "pkt_filter_add"; + str_len = strlen(str); + strncpy(buf, str, str_len); + buf[ str_len ] = '\0'; + buf_len = str_len + 1; + + pkt_filterp = (struct wl_pkt_filter *) (buf + str_len + 1); + + /* Parse packet filter id. */ + pkt_filter.id = htod32(100); + + /* Parse filter polarity. */ + pkt_filter.negate_match = htod32(0); + + /* Parse filter type. */ + pkt_filter.type = htod32(0); + + /* Parse pattern filter offset. */ + pkt_filter.u.pattern.offset = htod32(0); + + /* Parse pattern filter mask. */ + mask_size = htod32(wl_pattern_atoh("0xff", + (char *) pkt_filterp->u.pattern.mask_and_pattern)); + + /* Parse pattern filter pattern. */ + pattern_size = htod32(wl_pattern_atoh("0x00", + (char *) &pkt_filterp->u.pattern.mask_and_pattern[mask_size])); + + if (mask_size != pattern_size) { + WL_ERR(("Mask and pattern not the same size\n")); + err = -EINVAL; + goto dongle_filter_out; + } + + pkt_filter.u.pattern.size_bytes = mask_size; + buf_len += WL_PKT_FILTER_FIXED_LEN; + buf_len += (WL_PKT_FILTER_PATTERN_FIXED_LEN + 2 * mask_size); + + /* Keep-alive attributes are set in local variable (keep_alive_pkt), and + ** then memcpy'ed into buffer (keep_alive_pktp) since there is no + ** guarantee that the buffer is properly aligned. + */ + memcpy((char *)pkt_filterp, &pkt_filter, + WL_PKT_FILTER_FIXED_LEN + WL_PKT_FILTER_PATTERN_FIXED_LEN); + + if ((err = wl_dev_ioctl(ndev, WLC_SET_VAR, buf, buf_len))) { + if (err == -EOPNOTSUPP) { + WL_INFO(("filter not supported\n")); + } else { + WL_ERR(("filter (%d)\n", err)); + } + goto dongle_filter_out; + } + + /* set mode to allow pattern */ + bcm_mkiovar("pkt_filter_mode", (char *)&filter_mode, 4, iovbuf, sizeof(iovbuf)); + if ((err = wl_dev_ioctl(ndev, WLC_SET_VAR, iovbuf, sizeof(iovbuf)))) { + if (err == -EOPNOTSUPP) { + WL_INFO(("filter_mode not supported\n")); + } else { + WL_ERR(("filter_mode (%d)\n", err)); + } + goto dongle_filter_out; + } + +dongle_filter_out : + return err; +} +#endif /* !EMBEDDED_PLATFORM */ + +int32 +wl_config_dongle(struct wl_priv *wl, bool need_lock) +{ +#ifndef DHD_SDALIGN +#define DHD_SDALIGN 32 +#endif + struct net_device *ndev; + struct wireless_dev *wdev; + int32 err = 0; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + if (wl->dongle_up) + return err; + + + ndev = wl_to_ndev(wl); + wdev = ndev->ieee80211_ptr; + if (need_lock) + rtnl_lock(); + +#ifndef EMBEDDED_PLATFORM + if (unlikely((err = wl_dongle_up(ndev, 0)))) + goto default_conf_out; + if (unlikely((err = wl_dongle_country(ndev, 0)))) + goto default_conf_out; + if (unlikely((err = wl_dongle_power(ndev, PM_FAST)))) + goto default_conf_out; + if (unlikely((err = wl_dongle_glom(ndev, 0, DHD_SDALIGN)))) + goto default_conf_out; + if (unlikely((err = wl_dongle_roam(ndev, (wl->roam_on ? 0 : 1), 3)))) + goto default_conf_out; + if (unlikely((err = wl_dongle_eventmsg(ndev)))) + goto default_conf_out; + + wl_dongle_scantime(ndev, 40, 80); + wl_dongle_offload(ndev, 1, 0xf); + wl_dongle_filter(ndev, 1); +#endif /* !EMBEDDED_PLATFORM */ + + err = wl_dongle_mode(ndev, wdev->iftype); + if (unlikely(err && err != -EINPROGRESS)) + goto default_conf_out; + if (unlikely((err = wl_dongle_probecap(wl)))) + goto default_conf_out; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + /* -EINPROGRESS: Call commit handler */ + +default_conf_out : + if (need_lock) + rtnl_unlock(); + + wl->dongle_up = TRUE; + + return err; + +} + +static int32 +wl_update_wiphybands(struct wl_priv *wl) +{ + struct wiphy *wiphy; + int32 phy_list; + int8 phy; + int32 err = 0; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + if (unlikely(err = wl_dev_ioctl(wl_to_ndev(wl), WLC_GET_PHYLIST, &phy_list, + sizeof(phy_list)))) { + WL_ERR(("error (%d)\n", err)); + return err; + } + + phy = ((char *)&phy_list)[1]; + WL_DBG(("%c phy\n", phy)); + if (phy == 'n' || phy == 'a') { + wiphy = wl_to_wiphy(wl); + wiphy->bands[IEEE80211_BAND_5GHZ] = &__wl_band_5ghz_n; + } +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + + return err; +} + +static int32 +__wl_cfg80211_up(struct wl_priv *wl) +{ + int32 err = 0; + + if (unlikely(err = wl_config_dongle(wl, FALSE))) + return err; + + wl_invoke_iscan(wl); + set_bit(WL_STATUS_READY, &wl->status); + return err; +} + +static int32 +__wl_cfg80211_down(struct wl_priv *wl) +{ + int32 err = 0; + + /* Check if cfg80211 interface is already down */ + if (!test_bit(WL_STATUS_READY, &wl->status)) + return err; /* it is even not ready */ + + set_bit(WL_STATUS_SCAN_ABORTING, &wl->status); + wl_term_iscan(wl); + if (wl->scan_request) { + cfg80211_scan_done(wl->scan_request, TRUE); /* TRUE means abort */ + wl->scan_request = NULL; + } + clear_bit(WL_STATUS_READY, &wl->status); + clear_bit(WL_STATUS_SCANNING, &wl->status); + clear_bit(WL_STATUS_SCAN_ABORTING, &wl->status); + clear_bit(WL_STATUS_CONNECTED, &wl->status); + + return err; +} + +int32 +wl_cfg80211_up(void) +{ + struct wl_priv *wl; + int32 err = 0; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + wl = WL_PRIV_GET(); + mutex_lock(&wl->usr_sync); + err = __wl_cfg80211_up(wl); + mutex_unlock(&wl->usr_sync); +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + + return err; +} + +int32 +wl_cfg80211_down(void) +{ + struct wl_priv *wl; + int32 err = 0; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + wl = WL_PRIV_GET(); + mutex_lock(&wl->usr_sync); + err = __wl_cfg80211_down(wl); + mutex_unlock(&wl->usr_sync); +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + + return err; +} + +static int32 +wl_dongle_probecap(struct wl_priv *wl) +{ + int32 err = 0; + + if (unlikely((err = wl_update_wiphybands(wl)))) + return err; + + return err; +} + +static void * +wl_read_prof(struct wl_priv *wl, int32 item) +{ + switch (item) { + case WL_PROF_SEC: + return &wl->profile->sec; + case WL_PROF_ACT: + return &wl->profile->active; + case WL_PROF_BSSID: + return &wl->profile->bssid; + case WL_PROF_SSID: + return &wl->profile->ssid; + } + WL_ERR(("invalid item (%d)\n", item)); + return NULL; +} + +static int32 +wl_update_prof(struct wl_priv *wl, const wl_event_msg_t *e, void *data, int32 item) +{ + int32 err = 0; + struct wlc_ssid *ssid; + + switch (item) { + case WL_PROF_SSID: + ssid = (wlc_ssid_t *)data; + memset(wl->profile->ssid.SSID, 0, sizeof(wl->profile->ssid.SSID)); + memcpy(wl->profile->ssid.SSID, ssid->SSID, ssid->SSID_len); + wl->profile->ssid.SSID_len = ssid->SSID_len; + break; + case WL_PROF_BSSID: + if (data) + memcpy(wl->profile->bssid, data, ETHER_ADDR_LEN); + else + memset(wl->profile->bssid, 0, ETHER_ADDR_LEN); + break; + case WL_PROF_SEC: + memcpy(&wl->profile->sec, data, sizeof(wl->profile->sec)); + break; + case WL_PROF_ACT: + wl->profile->active = *(bool *)data; + break; + default : + WL_ERR(("unsupported item (%d)\n", item)); + err = -EOPNOTSUPP; + break; + } + + return err; +} + +void +wl_cfg80211_dbg_level(uint32 level) +{ + wl_dbg_level = level; +} + +static bool +wl_is_ibssmode(struct wl_priv *wl) +{ + return (wl->conf->mode == WL_MODE_IBSS); +} + +static bool +wl_is_ibssstarter(struct wl_priv *wl) +{ + return wl->ibss_starter; +} + +static void +wl_rst_ie(struct wl_priv *wl) +{ + struct wl_ie *ie = wl_to_ie(wl); + + ie->offset = 0; +} + +static int32 +wl_add_ie(struct wl_priv *wl, uint8 t, uint8 l, uint8 *v) +{ + struct wl_ie *ie = wl_to_ie(wl); + int32 err = 0; + + if (unlikely(ie->offset + l + 2 > WL_TLV_INFO_MAX)) { + WL_ERR(("ei crosses buffer boundary\n")); + return -ENOSPC; + } + ie->buf[ie->offset] = t; + ie->buf[ie->offset+1] = l; + memcpy(&ie->buf[ie->offset+2], v, l); + ie->offset += l+2; + + return err; +} + +static int32 +wl_mrg_ie(struct wl_priv *wl, uint8 *ie_stream, uint16 ie_size) +{ + struct wl_ie *ie = wl_to_ie(wl); + int32 err = 0; + + if (unlikely(ie->offset + ie_size > WL_TLV_INFO_MAX)) { + WL_ERR(("ei_stream crosses buffer boundary\n")); + return -ENOSPC; + } + memcpy(&ie->buf[ie->offset], ie_stream, ie_size); + ie->offset += ie_size; + + return err; +} + +static int32 +wl_cp_ie(struct wl_priv *wl, uint8 *dst, uint16 dst_size) +{ + struct wl_ie *ie = wl_to_ie(wl); + int32 err = 0; + + if (unlikely(ie->offset > dst_size)) { + WL_ERR(("dst_size is not enough\n")); + return -ENOSPC; + } + memcpy(dst, &ie->buf[0], ie->offset); + + return err; +} + +static uint32 +wl_get_ielen(struct wl_priv *wl) +{ + struct wl_ie *ie = wl_to_ie(wl); + + return ie->offset; +} + +static void +wl_link_up(struct wl_priv *wl) +{ + wl->link_up = TRUE; +} + +static void +wl_link_down(struct wl_priv *wl) +{ + struct wl_connect_info *conn_info = wl_to_conn(wl); + + wl->link_up = FALSE; + if (conn_info->req_ie) { + kfree(conn_info->req_ie); + conn_info->req_ie = NULL; + } + conn_info->req_ie_len = 0; + if (conn_info->resp_ie) { + kfree(conn_info->resp_ie); + conn_info->resp_ie = NULL; + } + conn_info->resp_ie_len = 0; +} + +static void +wl_lock_eq(struct wl_priv *wl) +{ + spin_lock_irq(&wl->eq_lock); +} + +static void +wl_unlock_eq(struct wl_priv *wl) +{ + spin_unlock_irq(&wl->eq_lock); +} + +static void +wl_init_eq_lock(struct wl_priv *wl) +{ + spin_lock_init(&wl->eq_lock); +} + +static void +wl_delay(uint32 ms) +{ + if (ms < 1000 / HZ) { + cond_resched(); + mdelay(ms); + } else { + msleep(ms); + } +} + +static void +wl_set_drvdata(struct wl_dev *dev, void *data) +{ + dev->driver_data = data; +} + +static void * +wl_get_drvdata(struct wl_dev *dev) +{ + return dev->driver_data; +} + +int32 +wl_cfg80211_read_fw(int8 *buf, uint32 size) +{ + const struct firmware *fw_entry; + struct wl_priv *wl; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In : size (%d)\n", size)); +#endif + wl = WL_PRIV_GET(); + + fw_entry = wl->fw->fw_entry; + + if (fw_entry->size < wl->fw->ptr + size) { + size = fw_entry->size - wl->fw->ptr; + } + memcpy(buf, &fw_entry->data[wl->fw->ptr], size); + wl->fw->ptr += size; +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out : size (%d)\n", size)); +#endif + return size; +} + +void +wl_cfg80211_release_fw(void) +{ + struct wl_priv *wl; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + wl = WL_PRIV_GET(); + release_firmware(wl->fw->fw_entry); + wl->fw->ptr = 0; +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif +} + +void * +wl_cfg80211_request_fw(int8 *file_name) +{ + struct wl_priv *wl; + const struct firmware *fw_entry = NULL; + int32 err = 0; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + WL_DBG(("file name : \"%s\"\n", file_name)); + wl = WL_PRIV_GET(); + + if (!test_bit(WL_FW_LOADING_DONE, &wl->fw->status)) { + if (unlikely(err = request_firmware(&wl->fw->fw_entry, file_name, + &wl_sdio_func()->dev))) { + WL_ERR(("Could not download fw (%d)\n", err)); + goto req_fw_out; + } + set_bit(WL_FW_LOADING_DONE, &wl->fw->status); + fw_entry = wl->fw->fw_entry; + if (fw_entry) { + WL_DBG(("fw size (%d), data (%p)\n", fw_entry->size, fw_entry->data)); + } + } else if (!test_bit(WL_NVRAM_LOADING_DONE, &wl->fw->status)) { + if (unlikely(err = request_firmware(&wl->fw->fw_entry, file_name, + &wl_sdio_func()->dev))) { + WL_ERR(("Could not download nvram (%d)\n", err)); + goto req_fw_out; + } + set_bit(WL_NVRAM_LOADING_DONE, &wl->fw->status); + fw_entry = wl->fw->fw_entry; + if (fw_entry) { + WL_DBG(("nvram size (%d), data (%p)\n", fw_entry->size, fw_entry->data)); + } + } else { + WL_DBG(("Downloading already done. Nothing to do more\n")); + err = -EPERM; + } +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + +req_fw_out: + if (unlikely(err)) { + return NULL; + } + wl->fw->ptr = 0; + return (void *)fw_entry->data; +} + +int8 * +wl_cfg80211_get_fwname(void) +{ + struct wl_priv *wl; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + wl = WL_PRIV_GET(); + strcpy(wl->fw->fw_name, WL_4329_FW_FILE); +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + return wl->fw->fw_name; +} + +int8 * +wl_cfg80211_get_nvramname(void) +{ + struct wl_priv *wl; + +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("In\n")); +#endif + wl = WL_PRIV_GET(); + strcpy(wl->fw->nvram_name, WL_4329_NVRAM_FILE); +#ifdef WL_CFG80211_BACKTRACE + WL_DBG(("Out\n")); +#endif + return wl->fw->nvram_name; +} diff --git a/bcm4329/src/wl/sys/wl_cfg80211.h b/bcm4329/src/wl/sys/wl_cfg80211.h new file mode 100644 index 0000000..2722699 --- /dev/null +++ b/bcm4329/src/wl/sys/wl_cfg80211.h @@ -0,0 +1,372 @@ +/* + * Linux Cfg80211 support + * + * Copyright (C) 1999-2010, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: wl_cfg80211.h,v 1.1.2.19 2010/05/04 21:21:00 Exp $ + */ + +#ifndef _wl_cfg80211_h_ +#define _wl_cfg80211_h_ + +#include <linux/wireless.h> +#include <typedefs.h> +#include <proto/ethernet.h> +#include <wlioctl.h> +#include <linux/wireless.h> +#include <net/cfg80211.h> + +struct wl_conf; +struct wl_iface; +struct wl_priv; +struct wl_security; +struct wl_ibss; + +#if defined(IL_BIGENDIAN) +#include <bcmendian.h> +#define htod32(i) (bcmswap32(i)) +#define htod16(i) (bcmswap16(i)) +#define dtoh32(i) (bcmswap32(i)) +#define dtoh16(i) (bcmswap16(i)) +#define htodchanspec(i) htod16(i) +#define dtohchanspec(i) dtoh16(i) +#else +#define htod32(i) i +#define htod16(i) i +#define dtoh32(i) i +#define dtoh16(i) i +#define htodchanspec(i) i +#define dtohchanspec(i) i +#endif + +#define WL_DBG_NONE 0 +#define WL_DBG_DBG (1 << 2) +#define WL_DBG_INFO (1 << 1) +#define WL_DBG_ERR (1 << 0) +#define WL_DBG_MASK ((WL_DBG_DBG | WL_DBG_INFO | WL_DBG_ERR) << 1) + +#define WL_DBG_LEVEL 1 /* 0 invalidates all debug messages. default is 1 */ +#define WL_ERR(args) \ + do { \ + if (wl_dbg_level & WL_DBG_ERR) { \ + if (net_ratelimit()) { \ + printk("ERROR @%s : ", __FUNCTION__); \ + printk args; \ + } \ + } \ + } while (0) +#define WL_INFO(args) \ + do { \ + if (wl_dbg_level & WL_DBG_INFO) { \ + if (net_ratelimit()) { \ + printk("INFO @%s : ", __FUNCTION__); \ + printk args; \ + } \ + } \ + } while (0) +#if (WL_DBG_LEVEL > 0) +#define WL_DBG(args) \ + do { \ + if (wl_dbg_level & WL_DBG_DBG) { \ + printk("DEBUG @%s :", __FUNCTION__); \ + printk args; \ + } \ + } while (0) +#else /* !(WL_DBG_LEVEL > 0) */ +#define WL_DBG(args) +#endif /* (WL_DBG_LEVEL > 0) */ + +#define WL_SCAN_RETRY_MAX 3 /* used for ibss scan */ +#define WL_NUM_SCAN_MAX 1 +#define WL_NUM_PMKIDS_MAX MAXPMKID /* will be used for 2.6.33 kernel + * or later + */ +#define WL_SCAN_BUF_MAX (1024 * 8) +#define WL_TLV_INFO_MAX 1024 +#define WL_BSS_INFO_MAX 2048 +#define WL_ASSOC_INFO_MAX 512 /* + * needs to grab assoc info from dongle to + * report it to cfg80211 through "connect" + * event + */ +#define WL_IOCTL_LEN_MAX 1024 +#define WL_EXTRA_BUF_MAX 2048 +#define WL_ISCAN_BUF_MAX 2048 /* the buf lengh can be WLC_IOCTL_MAXLEN (8K) + * to reduce iteration + */ +#define WL_ISCAN_TIMER_INTERVAL_MS 3000 +#define WL_SCAN_ERSULTS_LAST (WL_SCAN_RESULTS_NO_MEM+1) +#define WL_AP_MAX 256 /* virtually unlimitted as long + * as kernel memory allows + */ +#define WL_FILE_NAME_MAX 256 + +/* dongle status */ +enum wl_status { + WL_STATUS_READY, + WL_STATUS_SCANNING, + WL_STATUS_SCAN_ABORTING, + WL_STATUS_CONNECTING, + WL_STATUS_CONNECTED +}; + +/* wi-fi mode */ +enum wl_mode { + WL_MODE_BSS, + WL_MODE_IBSS, + WL_MODE_AP +}; + +/* dongle profile list */ +enum wl_prof_list { + WL_PROF_MODE, + WL_PROF_SSID, + WL_PROF_SEC, + WL_PROF_IBSS, + WL_PROF_BAND, + WL_PROF_BSSID, + WL_PROF_ACT +}; + +/* dongle iscan state */ +enum wl_iscan_state { + WL_ISCAN_STATE_IDLE, + WL_ISCAN_STATE_SCANING +}; + +/* fw downloading status */ +enum wl_fw_status { + WL_FW_LOADING_DONE, + WL_NVRAM_LOADING_DONE +}; + +/* dongle configuration */ +struct wl_conf { + uint32 mode; /* adhoc , infrastructure or ap */ + uint32 frag_threshold; + uint32 rts_threshold; + uint32 retry_short; + uint32 retry_long; + int32 tx_power; + struct ieee80211_channel channel; +}; + +/* cfg80211 main event loop */ +struct wl_event_loop { + int32 (*handler[WLC_E_LAST])(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data); +}; + +/* representing interface of cfg80211 plane */ +struct wl_iface { + struct wl_priv *wl; +}; + +struct wl_dev { + void *driver_data; /* to store cfg80211 object information */ +}; + +/* bss inform structure for cfg80211 interface */ +struct wl_cfg80211_bss_info { + uint16 band; + uint16 channel; + int16 rssi; + uint16 frame_len; + uint8 frame_buf[1]; +}; + +/* basic structure of scan request */ +struct wl_scan_req { + struct wlc_ssid ssid; +}; + +/* basic structure of information element */ +struct wl_ie { + uint16 offset; + uint8 buf[WL_TLV_INFO_MAX]; +}; + +/* event queue for cfg80211 main event */ +struct wl_event_q { + struct list_head eq_list; + uint32 etype; + wl_event_msg_t emsg; + int8 edata[1]; +}; + +/* security information with currently associated ap */ +struct wl_security { + uint32 wpa_versions; + uint32 auth_type; + uint32 cipher_pairwise; + uint32 cipher_group; + uint32 wpa_auth; +}; + +/* ibss information for currently joined ibss network */ +struct wl_ibss { + uint8 beacon_interval; /* in millisecond */ + uint8 atim; /* in millisecond */ + int8 join_only; + uint8 band; + uint8 channel; +}; + +/* dongle profile */ +struct wl_profile { + uint32 mode; + struct wlc_ssid ssid; + uint8 bssid[ETHER_ADDR_LEN]; + struct wl_security sec; + struct wl_ibss ibss; + int32 band; + bool active; +}; + +/* dongle iscan event loop */ +struct wl_iscan_eloop { + int32 (*handler[WL_SCAN_ERSULTS_LAST])(struct wl_priv *wl); +}; + +/* dongle iscan controller */ +struct wl_iscan_ctrl { + struct net_device *dev; + struct timer_list timer; + uint32 timer_ms; + uint32 timer_on; + int32 state; + int32 pid; + struct semaphore sync; + struct completion exited; + struct wl_iscan_eloop el; + void *data; + int8 ioctl_buf[WLC_IOCTL_SMLEN]; + int8 scan_buf[WL_ISCAN_BUF_MAX]; +}; + +/* association inform */ +struct wl_connect_info { + uint8 *req_ie; + int32 req_ie_len; + uint8 *resp_ie; + int32 resp_ie_len; +}; + +/* firmware /nvram downloading controller */ +struct wl_fw_ctrl { + const struct firmware *fw_entry; + ulong status; + uint32 ptr; + int8 fw_name[WL_FILE_NAME_MAX]; + int8 nvram_name[WL_FILE_NAME_MAX]; +}; + +/* assoc ie length */ +struct wl_assoc_ielen { + uint32 req_len; + uint32 resp_len; +}; + +/* wpa2 pmk list */ +struct wl_pmk_list { + pmkid_list_t pmkids; + pmkid_t foo[MAXPMKID-1]; +}; + + +/* dongle private data of cfg80211 interface */ +struct wl_priv { + struct wireless_dev *wdev; /* representing wl cfg80211 device */ + struct wl_conf *conf; /* dongle configuration */ + struct cfg80211_scan_request *scan_request; /* scan request object */ + struct wl_event_loop el; /* main event loop */ + struct list_head eq_list; /* used for event queue */ + spinlock_t eq_lock; /* for event queue synchronization */ + struct mutex usr_sync; /* maily for dongle up/down synchronization */ + struct wl_scan_results *bss_list; /* bss_list holding scanned ap information */ + struct wl_scan_results *scan_results; + struct wl_scan_req *scan_req_int; /* scan request object for internal purpose */ + struct wl_cfg80211_bss_info *bss_info; /* bss information for cfg80211 layer */ + struct wl_ie ie; /* information element object for internal purpose */ + struct ether_addr bssid; /* bssid of currently engaged network */ + struct semaphore event_sync; /* for synchronization of main event thread */ + struct completion event_exit; + struct wl_profile *profile; /* holding dongle profile */ + struct wl_iscan_ctrl *iscan; /* iscan controller */ + struct wl_connect_info conn_info; /* association information container */ + struct wl_fw_ctrl *fw; /* control firwmare / nvram paramter downloading */ + struct wl_pmk_list *pmk_list; /* wpa2 pmk list */ + int32 event_pid; /* pid of main event handler thread */ + ulong status; /* current dongle status */ + void * pub; + uint32 channel; /* current channel */ + bool iscan_on; /* iscan on/off switch */ + bool iscan_kickstart; /* indicate iscan already started */ + bool active_scan; /* current scan mode */ + bool ibss_starter; /* indicates this sta is ibss starter */ + bool link_up; /* link/connection up flag */ + bool pwr_save; /* indicate whether dongle to support power save mode */ + bool dongle_up; /* indicate whether dongle up or not */ + bool roam_on; /* on/off switch for dongle self-roaming */ + bool scan_tried; /* indicates if first scan attempted */ + uint8 *ioctl_buf; /* ioctl buffer */ + uint8 *extra_buf; /* maily to grab assoc information */ + uint8 ci[0] __attribute__((__aligned__(NETDEV_ALIGN))); +}; + +#define wl_to_dev(w) (wiphy_dev(wl->wdev->wiphy)) +#define wl_to_wiphy(w) (w->wdev->wiphy) +#define wiphy_to_wl(w) ((struct wl_priv *)(wiphy_priv(w))) +#define wl_to_wdev(w) (w->wdev) +#define wdev_to_wl(w) ((struct wl_priv *)(wdev_priv(w))) +#define wl_to_ndev(w) (w->wdev->netdev) +#define ndev_to_wl(n) (wdev_to_wl(n->ieee80211_ptr)) +#define ci_to_wl(c) (ci->wl) +#define wl_to_ci(w) (&w->ci) +#define wl_to_sr(w) (w->scan_req_int) +#define wl_to_ie(w) (&w->ie) +#define iscan_to_wl(i) ((struct wl_priv *)(i->data)) +#define wl_to_iscan(w) (w->iscan) +#define wl_to_conn(w) (&w->conn_info) + +inline static struct wl_bss_info * next_bss(struct wl_scan_results *list, + struct wl_bss_info *bss) { + return (bss = bss ? + (struct wl_bss_info *)((uintptr)bss + dtoh32(bss->length)) : list->bss_info); +} +#define for_each_bss(list, bss, __i) \ + for (__i = 0; __i < list->count && __i < WL_AP_MAX; __i++, bss = next_bss(list, bss)) + +extern int32 wl_cfg80211_attach(struct net_device *ndev, void *data); +extern void wl_cfg80211_detach(void); +/* event handler from dongle */ +extern void wl_cfg80211_event(struct net_device *ndev, const wl_event_msg_t *e, void* data); +extern void wl_cfg80211_sdio_func(void *func); /* set sdio function info */ +extern int32 wl_cfg80211_up(void); /* dongle up */ +extern int32 wl_cfg80211_down(void); /* dongle down */ +extern void wl_cfg80211_dbg_level(uint32 level); /* set dongle debugging level */ +extern void * wl_cfg80211_request_fw(int8 *file_name); /* request fw /nvram downloading */ +extern int32 wl_cfg80211_read_fw(int8 *buf, uint32 size); /* read fw image */ +extern void wl_cfg80211_release_fw(void); /* release fw */ +extern int8 * wl_cfg80211_get_fwname(void); /* get firmware name for the dongle */ +extern int8 * wl_cfg80211_get_nvramname(void); /* get nvram name for the dongle */ + +#endif /* _wl_cfg80211_h_ */ diff --git a/bcm4329/src/wl/sys/wl_iw.c b/bcm4329/src/wl/sys/wl_iw.c index de32e94..2cd629c 100644 --- a/bcm4329/src/wl/sys/wl_iw.c +++ b/bcm4329/src/wl/sys/wl_iw.c @@ -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: wl_iw.c,v 1.51.4.9.2.6.4.104 2010/04/21 23:21:00 Exp $ + * $Id: wl_iw.c,v 1.51.4.9.2.6.4.142.4.13 2010/09/15 03:34:56 Exp $ */ @@ -53,11 +53,38 @@ typedef const struct si_pub si_t; #define WL_ASSOC(x) #define WL_INFORM(x) #define WL_WSEC(x) +#define WL_SCAN(x) #include <wl_iw.h> -#define IW_WSEC_ENABLED(wsec) ((wsec) & (WEP_ENABLED | TKIP_ENABLED | AES_ENABLED)) + +#ifndef IW_ENCODE_ALG_SM4 +#define IW_ENCODE_ALG_SM4 0x20 +#endif + +#ifndef IW_AUTH_WAPI_ENABLED +#define IW_AUTH_WAPI_ENABLED 0x20 +#endif + +#ifndef IW_AUTH_WAPI_VERSION_1 +#define IW_AUTH_WAPI_VERSION_1 0x00000008 +#endif + +#ifndef IW_AUTH_CIPHER_SMS4 +#define IW_AUTH_CIPHER_SMS4 0x00000020 +#endif + +#ifndef IW_AUTH_KEY_MGMT_WAPI_PSK +#define IW_AUTH_KEY_MGMT_WAPI_PSK 4 +#endif + +#ifndef IW_AUTH_KEY_MGMT_WAPI_CERT +#define IW_AUTH_KEY_MGMT_WAPI_CERT 8 +#endif + + +#define IW_WSEC_ENABLED(wsec) ((wsec) & (WEP_ENABLED | TKIP_ENABLED | AES_ENABLED | SMS4_ENABLED)) #include <linux/rtnetlink.h> #include <linux/mutex.h> @@ -82,6 +109,7 @@ static int wl_iw_softap_deassoc_stations(struct net_device *dev); } while (0) static int g_onoff = G_WLAN_SET_ON; +wl_iw_extra_params_t g_wl_iw_params; static struct mutex wl_start_lock; static struct mutex wl_cache_lock; @@ -133,7 +161,9 @@ static wlc_ssid_t g_specific_ssid; static wlc_ssid_t g_ssid; static wl_iw_ss_cache_ctrl_t g_ss_cache_ctrl; -static volatile uint g_first_broadcast_scan; +static volatile uint g_first_broadcast_scan; +static volatile uint g_first_counter_scans; +#define MAX_ALLOWED_BLOCK_SCAN_FROM_FIRST_SCAN 3 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) @@ -150,8 +180,10 @@ static volatile uint g_first_broadcast_scan; #endif #if defined(WL_IW_USE_ISCAN) +#if !defined(CSCAN) static void wl_iw_free_ss_cache(void); static int wl_iw_run_ss_cache_timer(int kick_off); +#endif int wl_iw_iscan_set_scan_broadcast_prep(struct net_device *dev, uint flag); static int dev_wlc_bufvar_set(struct net_device *dev, char *name, char *buf, int len); #define ISCAN_STATE_IDLE 0 @@ -178,8 +210,13 @@ typedef struct iscan_info { struct completion sysioc_exited; uint32 scan_flag; - +#if defined CSCAN + char ioctlbuf[WLC_IOCTL_MEDLEN]; +#else char ioctlbuf[WLC_IOCTL_SMLEN]; +#endif + wl_iscan_params_t *iscan_ex_params_p; + int iscan_ex_param_size; } iscan_info_t; #define COEX_DHCP 1 static void wl_iw_bt_flag_set(struct net_device *dev, bool set); @@ -222,6 +259,8 @@ wl_iw_set_scan( union iwreq_data *wrqu, char *extra ); + +#ifndef CSCAN static int wl_iw_get_scan( struct net_device *dev, @@ -237,7 +276,7 @@ wl_iw_get_scan_prep( char *extra, short max_size ); - +#endif static void swap_key_from_BE( wl_wsec_key_t *key @@ -283,7 +322,9 @@ dev_wlc_ioctl( return ret; } - WL_TRACE(("%s, PID:%x: send Local IOCTL -> dhd: cmd:0x%x, buf:%p, len:%d ,\n", + net_os_wake_lock(dev); + + WL_INFORM(("\n%s, PID:%x: send Local IOCTL -> dhd: cmd:0x%x, buf:%p, len:%d ,\n", __FUNCTION__, current->pid, cmd, arg, len)); if (g_onoff == G_WLAN_SET_ON) { @@ -298,6 +339,7 @@ dev_wlc_ioctl( ret = dev_open(dev); if (ret) { WL_ERROR(("%s: Error dev_open: %d\n", __func__, ret)); + net_os_wake_unlock(dev); return ret; } @@ -311,8 +353,11 @@ dev_wlc_ioctl( set_fs(fs); } else { - WL_TRACE(("%s: call after driver stop\n", __FUNCTION__)); + WL_TRACE(("%s: call after driver stop : ignored\n", __FUNCTION__)); } + + net_os_wake_unlock(dev); + return ret; } @@ -388,6 +433,9 @@ dev_iw_iovar_setbuf( iolen = bcm_mkiovar(iovar, param, paramlen, bufptr, buflen); ASSERT(iolen); + if (iolen == 0) + return 0; + return (dev_wlc_ioctl(dev, WLC_SET_VAR, bufptr, iolen)); } @@ -624,15 +672,21 @@ wl_iw_set_power_mode( dev_wlc_ioctl(dev, WLC_GET_PM, &pm, sizeof(pm)); dev_wlc_ioctl(dev, WLC_SET_PM, &pm_local, sizeof(pm_local)); - } - else if (strnicmp((char *)&powermode_val, "0", strlen("0")) == 0) { + + /* Disable packet filtering if necessary */ + net_os_set_packet_filter(dev, 0); + + } else if (strnicmp((char *)&powermode_val, "0", strlen("0")) == 0) { WL_TRACE(("%s: DHCP session done\n", __FUNCTION__)); dev_wlc_ioctl(dev, WLC_SET_PM, &pm, sizeof(pm)); - } - else { - WL_TRACE(("Unkwown yet power setting, ignored\n")); + + /* Enable packet filtering if was turned off */ + net_os_set_packet_filter(dev, 1); + + } else { + WL_ERROR(("Unkwown yet power setting, ignored\n")); } p += snprintf(p, MAX_WX_STRING, "OK"); @@ -644,6 +698,35 @@ wl_iw_set_power_mode( #endif static int +wl_iw_get_power_mode( + struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra +) +{ + int error; + char *p = extra; + int pm_local = PM_FAST; + + error = dev_wlc_ioctl(dev, WLC_GET_PM, &pm_local, sizeof(pm_local)); + if (!error) { + WL_TRACE(("%s: Powermode = %d\n", __func__, pm_local)); + if (pm_local == PM_OFF) + pm_local = 1; /* Active */ + else + pm_local = 0; /* Auto */ + p += snprintf(p, MAX_WX_STRING, "powermode = %d", pm_local); + } + else { + WL_TRACE(("%s: Error = %d\n", __func__, error)); + p += snprintf(p, MAX_WX_STRING, "FAIL"); + } + wrqu->data.length = p - extra + 1; + return error; +} + +static int wl_iw_set_btcoex_dhcp( struct net_device *dev, struct iw_request_info *info, @@ -768,6 +851,36 @@ wl_iw_set_btcoex_dhcp( return error; } +static int +wl_iw_set_suspend( + struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra +) +{ + int suspend_flag; + int ret_now; + int ret = 0; + + suspend_flag = *(extra + strlen(SETSUSPEND_CMD) + 1) - '0'; + + if (suspend_flag != 0) + suspend_flag = 1; + + ret_now = net_os_set_suspend_disable(dev, suspend_flag); + + if (ret_now != suspend_flag) { + if (!(ret = net_os_set_suspend(dev, ret_now))) + WL_ERROR(("%s: Suspend Flag %d -> %d\n", \ + __FUNCTION__, ret_now, suspend_flag)); + else + WL_ERROR(("%s: failed %d\n", __FUNCTION__, ret)); + } + + return ret; +} + int wl_format_ssid(char* ssid_buf, uint8* ssid, int ssid_len) { @@ -804,7 +917,7 @@ wl_iw_get_link_speed( char *p = extra; static int link_speed; - + net_os_wake_lock(dev); if (g_onoff == G_WLAN_SET_ON) { error = dev_wlc_ioctl(dev, WLC_GET_RATE, &link_speed, sizeof(link_speed)); link_speed *= 500000; @@ -814,9 +927,356 @@ wl_iw_get_link_speed( wrqu->data.length = p - extra + 1; + net_os_wake_unlock(dev); + return error; +} + + +static int +wl_iw_get_dtim_skip( + struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra +) +{ + int error = -1; + char *p = extra; + char iovbuf[32]; + + net_os_wake_lock(dev); + if (g_onoff == G_WLAN_SET_ON) { + + memset(iovbuf, 0, sizeof(iovbuf)); + strcpy(iovbuf, "bcn_li_dtim"); + + if ((error = dev_wlc_ioctl(dev, WLC_GET_VAR, + &iovbuf, sizeof(iovbuf))) >= 0) { + + p += snprintf(p, MAX_WX_STRING, "Dtim_skip %d", iovbuf[0]); + WL_TRACE(("%s: get dtim_skip = %d\n", __FUNCTION__, iovbuf[0])); + wrqu->data.length = p - extra + 1; + } + else + WL_ERROR(("%s: get dtim_skip failed code %d\n", \ + __FUNCTION__, error)); + } + net_os_wake_unlock(dev); + return error; +} + + +static int +wl_iw_set_dtim_skip( + struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra +) +{ + int error = -1; + char *p = extra; + int bcn_li_dtim; + char iovbuf[32]; + + net_os_wake_lock(dev); + if (g_onoff == G_WLAN_SET_ON) { + + bcn_li_dtim = htod32((uint)*(extra + strlen(DTIM_SKIP_SET_CMD) + 1) - '0'); + + if ((bcn_li_dtim >= 0) || ((bcn_li_dtim <= 5))) { + + memset(iovbuf, 0, sizeof(iovbuf)); + bcm_mkiovar("bcn_li_dtim", (char *)&bcn_li_dtim, + 4, iovbuf, sizeof(iovbuf)); + + if ((error = dev_wlc_ioctl(dev, WLC_SET_VAR, + &iovbuf, sizeof(iovbuf))) >= 0) { + p += snprintf(p, MAX_WX_STRING, "OK"); + + net_os_set_dtim_skip(dev, bcn_li_dtim); + + WL_TRACE(("%s: set dtim_skip %d OK\n", __FUNCTION__, \ + bcn_li_dtim)); + goto exit; + } + else WL_ERROR(("%s: set dtim_skip %d failed code %d\n", \ + __FUNCTION__, bcn_li_dtim, error)); + } + else WL_ERROR(("%s Incorrect dtim_skip setting %d, ignored\n", \ + __FUNCTION__, bcn_li_dtim)); + } + + p += snprintf(p, MAX_WX_STRING, "FAIL"); + +exit: + wrqu->data.length = p - extra + 1; + net_os_wake_unlock(dev); + return error; +} + + +static int +wl_iw_get_band( + struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra +) +{ + int error = -1; + char *p = extra; + static int band; + + net_os_wake_lock(dev); + + if (g_onoff == G_WLAN_SET_ON) { + error = dev_wlc_ioctl(dev, WLC_GET_BAND, &band, sizeof(band)); + + p += snprintf(p, MAX_WX_STRING, "Band %d", band); + + wrqu->data.length = p - extra + 1; + } + + net_os_wake_unlock(dev); + return error; +} + + +static int +wl_iw_set_band( + struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra +) +{ + int error = -1; + char *p = extra; + uint band; + + net_os_wake_lock(dev); + + if (g_onoff == G_WLAN_SET_ON) { + + band = htod32((uint)*(extra + strlen(BAND_SET_CMD) + 1) - '0'); + + if ((band == WLC_BAND_AUTO) || (band == WLC_BAND_5G) || (band == WLC_BAND_2G)) { + + + if ((error = dev_wlc_ioctl(dev, WLC_SET_BAND, + &band, sizeof(band))) >= 0) { + p += snprintf(p, MAX_WX_STRING, "OK"); + WL_TRACE(("%s: set band %d OK\n", __FUNCTION__, band)); + goto exit; + } + else WL_ERROR(("%s: set band %d failed code %d\n", __FUNCTION__, \ + band, error)); + } + else WL_ERROR(("%s Incorrect band setting %d, ignored\n", __FUNCTION__, band)); + } + + p += snprintf(p, MAX_WX_STRING, "FAIL"); + +exit: + wrqu->data.length = p - extra + 1; + net_os_wake_unlock(dev); + return error; +} + +#ifdef PNO_SUPPORT + +static int +wl_iw_set_pno_reset( + struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra +) +{ + int error = -1; + char *p = extra; + + net_os_wake_lock(dev); + if ((g_onoff == G_WLAN_SET_ON) && (dev != NULL)) { + + if ((error = dhd_dev_pno_reset(dev)) >= 0) { + p += snprintf(p, MAX_WX_STRING, "OK"); + WL_TRACE(("%s: set OK\n", __FUNCTION__)); + goto exit; + } + else WL_ERROR(("%s: failed code %d\n", __FUNCTION__, error)); + } + + p += snprintf(p, MAX_WX_STRING, "FAIL"); + +exit: + wrqu->data.length = p - extra + 1; + net_os_wake_unlock(dev); + return error; +} + + + +static int +wl_iw_set_pno_enable( + struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra +) +{ + int error = -1; + char *p = extra; + int pfn_enabled; + + net_os_wake_lock(dev); + pfn_enabled = htod32((uint)*(extra + strlen(PNOENABLE_SET_CMD) + 1) - '0'); + + if ((g_onoff == G_WLAN_SET_ON) && (dev != NULL)) { + + if ((error = dhd_dev_pno_enable(dev, pfn_enabled)) >= 0) { + p += snprintf(p, MAX_WX_STRING, "OK"); + WL_TRACE(("%s: set OK\n", __FUNCTION__)); + goto exit; + } + else WL_ERROR(("%s: failed code %d\n", __FUNCTION__, error)); + } + + p += snprintf(p, MAX_WX_STRING, "FAIL"); + +exit: + wrqu->data.length = p - extra + 1; + net_os_wake_unlock(dev); return error; } + + +static int +wl_iw_set_pno_set( + struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra +) +{ + int res = -1; + wlc_ssid_t ssids_local[MAX_PFN_LIST_COUNT]; + int nssid = 0; + cmd_tlv_t *cmd_tlv_temp; + char type; + char *str_ptr; + int tlv_size_left; + int pno_time; + +#ifdef PNO_SET_DEBUG + int i; + char pno_in_example[] = {'P', 'N', 'O', 'S', 'E', 'T', 'U', 'P', ' ', \ + 'S', 0x01, 0x01, 0x00, + 'S', + 0x04, + 'B', 'R', 'C', 'M', + 'S', + 0x04, + 'G', 'O', 'O', 'G', + 'T', + 0x00, + 0x0A + }; +#endif + + net_os_wake_lock(dev); + WL_ERROR(("\n### %s: info->cmd:%x, info->flags:%x, u.data=0x%p, u.len=%d\n", + __FUNCTION__, info->cmd, info->flags, + wrqu->data.pointer, wrqu->data.length)); + + if (g_onoff == G_WLAN_SET_OFF) { + WL_TRACE(("%s: driver is not up yet after START\n", __FUNCTION__)); + goto exit_proc; + } + + if (wrqu->data.length < (strlen(PNOSETUP_SET_CMD) + sizeof(cmd_tlv_t))) { + WL_ERROR(("%s aggument=%d less %d\n", __FUNCTION__, \ + wrqu->data.length, strlen(PNOSETUP_SET_CMD) + sizeof(cmd_tlv_t))); + goto exit_proc; + } + +#ifdef PNO_SET_DEBUG + if (!(extra = kmalloc(sizeof(pno_in_example) +100, GFP_KERNEL))) { + res = -ENOMEM; + goto exit_proc; + } + memcpy(extra, pno_in_example, sizeof(pno_in_example)); + wrqu->data.length = sizeof(pno_in_example); + for (i = 0; i < wrqu->data.length; i++) + printf("%02X ", extra[i]); + printf("\n"); +#endif + + str_ptr = extra; +#ifdef PNO_SET_DEBUG + str_ptr += strlen("PNOSETUP "); + tlv_size_left = wrqu->data.length - strlen("PNOSETUP "); +#else + str_ptr += strlen(PNOSETUP_SET_CMD); + tlv_size_left = wrqu->data.length - strlen(PNOSETUP_SET_CMD); +#endif + + cmd_tlv_temp = (cmd_tlv_t *)str_ptr; + memset(ssids_local, 0, sizeof(ssids_local)); + + if ((cmd_tlv_temp->prefix == PNO_TLV_PREFIX) && \ + (cmd_tlv_temp->version == PNO_TLV_VERSION) && \ + (cmd_tlv_temp->subver == PNO_TLV_SUBVERSION)) + { + str_ptr += sizeof(cmd_tlv_t); + tlv_size_left -= sizeof(cmd_tlv_t); + + if ((nssid = wl_iw_parse_ssid_list_tlv(&str_ptr, ssids_local, \ + MAX_PFN_LIST_COUNT, &tlv_size_left)) <= 0) { + WL_ERROR(("SSID is not presented or corrupted ret=%d\n", nssid)); + goto exit_proc; + } + else { + while (tlv_size_left > 0) + { + type = str_ptr[0]; + switch (type) { + case PNO_TLV_TYPE_TIME: + + if ((res = wl_iw_parse_data_tlv(&str_ptr, \ + &pno_time, \ + sizeof(pno_time), \ + type, sizeof(short), &tlv_size_left)) == -1) { + WL_ERROR(("%s return %d\n", \ + __FUNCTION__, res)); + goto exit_proc; + } + break; + + default: + WL_ERROR(("%s get unkwown type %X\n", \ + __FUNCTION__, type)); + goto exit_proc; + break; + } + } + } + } + else { + WL_ERROR(("%s get wrong TLV command\n", __FUNCTION__)); + goto exit_proc; + } + + res = dhd_dev_pno_set(dev, ssids_local, nssid, pno_time); + +exit_proc: + net_os_wake_unlock(dev); + return res; +} +#endif + static int wl_iw_get_rssi( struct net_device *dev, @@ -832,12 +1292,15 @@ wl_iw_get_rssi( static char ssidbuf[SSID_FMT_BUF_LEN]; scb_val_t scb_val; + net_os_wake_lock(dev); + bzero(&scb_val, sizeof(scb_val_t)); if (g_onoff == G_WLAN_SET_ON) { error = dev_wlc_ioctl(dev, WLC_GET_RSSI, &scb_val, sizeof(scb_val_t)); if (error) { WL_ERROR(("%s: Fails %d\n", __FUNCTION__, error)); + net_os_wake_unlock(dev); return error; } rssi = dtoh32(scb_val.val); @@ -852,10 +1315,11 @@ wl_iw_get_rssi( p += snprintf(p, MAX_WX_STRING, "%s rssi %d ", ssidbuf, rssi); wrqu->data.length = p - extra + 1; + net_os_wake_unlock(dev); return error; } -static int +int wl_iw_send_priv_event( struct net_device *dev, char *flag @@ -873,6 +1337,7 @@ wl_iw_send_priv_event( strcpy(extra, flag); wrqu.data.length = strlen(extra); wireless_send_event(dev, cmd, &wrqu, extra); + net_os_wake_lock_timeout_enable(dev); WL_TRACE(("Send IWEVCUSTOM Event as %s\n", extra)); return 0; @@ -946,25 +1411,28 @@ wl_iw_control_wl_off( dhd_dev_reset(dev, 1); #if defined(WL_IW_USE_ISCAN) +#if !defined(CSCAN) wl_iw_free_ss_cache(); wl_iw_run_ss_cache_timer(0); - memset(g_scan, 0, G_SCAN_RESULTS); g_ss_cache_ctrl.m_link_down = 1; +#endif + memset(g_scan, 0, G_SCAN_RESULTS); g_scan_specified_ssid = 0; g_first_broadcast_scan = BROADCAST_SCAN_FIRST_IDLE; + g_first_counter_scans = 0; #endif #if defined(BCMLXSDMMC) sdioh_stop(NULL); #endif + net_os_set_dtim_skip(dev, 0); + dhd_customer_gpio_wlan_ctrl(WLAN_RESET_OFF); wl_iw_send_priv_event(dev, "STOP"); - - net_os_wake_lock_timeout_enable(dev); } mutex_unlock(&wl_start_lock); @@ -996,8 +1464,6 @@ wl_iw_control_wl_on( wl_iw_iscan_set_scan_broadcast_prep(dev, 0); #endif - net_os_wake_lock_timeout_enable(dev); - WL_TRACE(("Exited %s \n", __FUNCTION__)); return ret; @@ -1153,28 +1619,6 @@ static int iwpriv_set_ap_config(struct net_device *dev, #ifdef SOFTAP -void print_buf(void *pbuf, int len, int bytes_per_line) -{ - int i, j = 0; - unsigned char *buf = pbuf; - - if (bytes_per_line == 0) { - bytes_per_line = len; - } - - for (i = 0; i < len; i++) { - WL_SOFTAP(("%2.2x", *buf++)); - j++; - if (j == bytes_per_line) { - WL_SOFTAP(("\n")); - j = 0; - } else { - WL_SOFTAP((":")); - } - } - WL_SOFTAP(("\n")); -} - static int iwpriv_get_assoc_list(struct net_device *dev, struct iw_request_info *info, union iwreq_data *p_iwrq, @@ -1431,6 +1875,7 @@ wl_iw_set_freq( if ((error = dev_wlc_ioctl(dev, WLC_SET_CHANNEL, &chan, sizeof(chan)))) return error; + g_wl_iw_params.target_channel = chan; return -EINPROGRESS; } @@ -1451,7 +1896,6 @@ wl_iw_get_freq( if ((error = dev_wlc_ioctl(dev, WLC_GET_CHANNEL, &ci, sizeof(ci)))) return error; - fwrq->m = dtoh32(ci.hw_channel); fwrq->e = dtoh32(0); return 0; @@ -1525,9 +1969,9 @@ wl_iw_get_range( ) { struct iw_range *range = (struct iw_range *) extra; - int channels[MAXCHANNEL+1]; - wl_uint32_list_t *list = (wl_uint32_list_t *) channels; + wl_uint32_list_t *list; wl_rateset_t rateset; + int8 *channels; int error, i, k; uint sf, ch; @@ -1545,14 +1989,23 @@ wl_iw_get_range( if (!extra) return -EINVAL; + channels = kmalloc((MAXCHANNEL+1)*4, GFP_KERNEL); + if (!channels) { + WL_ERROR(("Could not alloc channels\n")); + return -ENOMEM; + } + list = (wl_uint32_list_t *)channels; + dwrq->length = sizeof(struct iw_range); memset(range, 0, sizeof(range)); range->min_nwid = range->max_nwid = 0; list->count = htod32(MAXCHANNEL); - if ((error = dev_wlc_ioctl(dev, WLC_GET_VALID_CHANNELS, channels, sizeof(channels)))) + if ((error = dev_wlc_ioctl(dev, WLC_GET_VALID_CHANNELS, channels, (MAXCHANNEL+1)*4))) { + kfree(channels); return error; + } for (i = 0; i < dtoh32(list->count) && i < IW_MAX_FREQUENCIES; i++) { range->freq[i].i = dtoh32(list->element[i]); @@ -1584,8 +2037,10 @@ wl_iw_get_range( range->avg_qual.noise = 0x100 - 75; #endif - if ((error = dev_wlc_ioctl(dev, WLC_GET_CURR_RATESET, &rateset, sizeof(rateset)))) + if ((error = dev_wlc_ioctl(dev, WLC_GET_CURR_RATESET, &rateset, sizeof(rateset)))) { + kfree(channels); return error; + } rateset.count = dtoh32(rateset.count); range->num_bitrates = rateset.count; for (i = 0; i < rateset.count && i < IW_MAX_BITRATES; i++) @@ -1620,8 +2075,10 @@ wl_iw_get_range( } } - if ((error = dev_wlc_ioctl(dev, WLC_GET_PHYTYPE, &i, sizeof(i)))) + if ((error = dev_wlc_ioctl(dev, WLC_GET_PHYTYPE, &i, sizeof(i)))) { + kfree(channels); return error; + } i = dtoh32(i); if (i == WLC_PHY_TYPE_A) range->throughput = 24000000; @@ -1690,6 +2147,8 @@ wl_iw_get_range( #endif #endif + kfree(channels); + return 0; } @@ -1764,6 +2223,41 @@ wl_iw_get_spy( return 0; } + +static int +wl_iw_ch_to_chanspec(int ch, wl_join_params_t *join_params, int *join_params_size) +{ + chanspec_t chanspec = 0; + + if (ch != 0) { + + join_params->params.chanspec_num = 1; + join_params->params.chanspec_list[0] = ch; + + if (join_params->params.chanspec_list[0]) + chanspec |= WL_CHANSPEC_BAND_2G; + else + chanspec |= WL_CHANSPEC_BAND_5G; + + chanspec |= WL_CHANSPEC_BW_20; + chanspec |= WL_CHANSPEC_CTL_SB_NONE; + + *join_params_size += WL_ASSOC_PARAMS_FIXED_SIZE + + join_params->params.chanspec_num * sizeof(chanspec_t); + + join_params->params.chanspec_list[0] &= WL_CHANSPEC_CHAN_MASK; + join_params->params.chanspec_list[0] |= chanspec; + join_params->params.chanspec_list[0] = + htodchanspec(join_params->params.chanspec_list[0]); + + join_params->params.chanspec_num = htod32(join_params->params.chanspec_num); + + WL_TRACE(("%s join_params->params.chanspec_list[0]= %X\n", \ + __FUNCTION__, join_params->params.chanspec_list[0])); + } + return 1; +} + static int wl_iw_set_wap( struct net_device *dev, @@ -1774,6 +2268,7 @@ wl_iw_set_wap( { int error = -EINVAL; wl_join_params_t join_params; + int join_params_size; WL_TRACE(("%s: SIOCSIWAP\n", dev->name)); @@ -1795,16 +2290,26 @@ wl_iw_set_wap( memset(&join_params, 0, sizeof(join_params)); + join_params_size = sizeof(join_params.ssid); memcpy(join_params.ssid.SSID, g_ssid.SSID, g_ssid.SSID_len); join_params.ssid.SSID_len = htod32(g_ssid.SSID_len); memcpy(&join_params.params.bssid, awrq->sa_data, ETHER_ADDR_LEN); - if ((error = dev_wlc_ioctl(dev, WLC_SET_SSID, &join_params, sizeof(join_params)))) { - WL_ERROR(("Invalid ioctl data.\n")); + WL_TRACE(("%s target_channel=%d\n", __FUNCTION__, g_wl_iw_params.target_channel)); + wl_iw_ch_to_chanspec(g_wl_iw_params.target_channel, &join_params, &join_params_size); + + if ((error = dev_wlc_ioctl(dev, WLC_SET_SSID, &join_params, join_params_size))) { + WL_ERROR(("%s Invalid ioctl data=%d\n", __FUNCTION__, error)); return error; } + if (g_ssid.SSID_len) { + WL_TRACE(("%s: join SSID=%s BSSID="MACSTR" ch=%d\n", __FUNCTION__, \ + g_ssid.SSID, MAC2STR((u8 *)awrq->sa_data), \ + g_wl_iw_params.target_channel)); + } + memset(&g_ssid, 0, sizeof(g_ssid)); return 0; @@ -1871,6 +2376,7 @@ wl_iw_mlme( } #endif +#ifndef WL_IW_USE_ISCAN static int wl_iw_get_aplist( struct net_device *dev, @@ -1946,6 +2452,7 @@ wl_iw_get_aplist( } return 0; } +#endif #ifdef WL_IW_USE_ISCAN static int @@ -1971,7 +2478,8 @@ wl_iw_iscan_get_aplist( return -EINVAL; if ((!iscan) || (iscan->sysioc_pid < 0)) { - return wl_iw_get_aplist(dev, info, dwrq, extra); + WL_ERROR(("%s error\n", __FUNCTION__)); + return 0; } buf = iscan->list_hdr; @@ -2048,35 +2556,26 @@ wl_iw_iscan_prep(wl_scan_params_t *params, wlc_ssid_t *ssid) static int wl_iw_iscan(iscan_info_t *iscan, wlc_ssid_t *ssid, uint16 action) { - int params_size = (WL_SCAN_PARAMS_FIXED_SIZE + OFFSETOF(wl_iscan_params_t, params)); - wl_iscan_params_t *params; int err = 0; - WL_TRACE(("%s: start\n", __func__)); - - if (ssid && ssid->SSID_len) { - params_size += sizeof(wlc_ssid_t); - } - params = (wl_iscan_params_t*)kmalloc(params_size, GFP_KERNEL); - if (params == NULL) { - return -ENOMEM; - } - memset(params, 0, params_size); - ASSERT(params_size < WLC_IOCTL_SMLEN); + iscan->iscan_ex_params_p->version = htod32(ISCAN_REQ_VERSION); + iscan->iscan_ex_params_p->action = htod16(action); + iscan->iscan_ex_params_p->scan_duration = htod16(0); - err = wl_iw_iscan_prep(¶ms->params, ssid); + WL_SCAN(("%s : nprobes=%d\n", __FUNCTION__, iscan->iscan_ex_params_p->params.nprobes)); + WL_SCAN(("active_time=%d\n", iscan->iscan_ex_params_p->params.active_time)); + WL_SCAN(("passive_time=%d\n", iscan->iscan_ex_params_p->params.passive_time)); + WL_SCAN(("home_time=%d\n", iscan->iscan_ex_params_p->params.home_time)); + WL_SCAN(("scan_type=%d\n", iscan->iscan_ex_params_p->params.scan_type)); + WL_SCAN(("bss_type=%d\n", iscan->iscan_ex_params_p->params.bss_type)); - if (!err) { - params->version = htod32(ISCAN_REQ_VERSION); - params->action = htod16(action); - params->scan_duration = htod16(0); - - - (void) dev_iw_iovar_setbuf(iscan->dev, "iscan", params, params_size, - iscan->ioctlbuf, WLC_IOCTL_SMLEN); + + if ((err = dev_iw_iovar_setbuf(iscan->dev, "iscan", iscan->iscan_ex_params_p, \ + iscan->iscan_ex_param_size, iscan->ioctlbuf, sizeof(iscan->ioctlbuf)))) { + WL_ERROR(("Set ISCAN for %s failed with %d\n", __FUNCTION__, err)); + err = -1; } - kfree(params); return err; } @@ -2087,7 +2586,7 @@ wl_iw_timerfunc(ulong data) if (iscan) { iscan->timer_on = 0; if (iscan->iscan_state != ISCAN_STATE_IDLE) { - WL_TRACE(("timer trigger\n")); + WL_SCAN(("timer trigger\n")); up(&iscan->sysioc_sem); } } @@ -2113,6 +2612,7 @@ wl_iw_iscan_get(iscan_info_t *iscan) wl_iscan_results_t list; wl_scan_results_t *results; uint32 status; + int res; mutex_lock(&wl_cache_lock); if (iscan->list_cur) { @@ -2147,20 +2647,25 @@ wl_iw_iscan_get(iscan_info_t *iscan) memset(&list, 0, sizeof(list)); list.results.buflen = htod32(WLC_IW_ISCAN_MAXLEN); - (void) dev_iw_iovar_getbuf( + res = dev_iw_iovar_getbuf( iscan->dev, "iscanresults", &list, WL_ISCAN_RESULTS_FIXED_SIZE, buf->iscan_buf, WLC_IW_ISCAN_MAXLEN); - results->buflen = dtoh32(results->buflen); - results->version = dtoh32(results->version); - results->count = dtoh32(results->count); - WL_TRACE(("results->count = %d\n", results->count)); - - WL_TRACE(("results->buflen = %d\n", results->buflen)); - status = dtoh32(list_buf->status); + if (res == 0) { + results->buflen = dtoh32(results->buflen); + results->version = dtoh32(results->version); + results->count = dtoh32(results->count); + WL_SCAN(("results->count = %d\n", results->count)); + + WL_SCAN(("results->buflen = %d\n", results->buflen)); + status = dtoh32(list_buf->status); + } else { + WL_ERROR(("%s returns error %d\n", __FUNCTION__, res)); + status = WL_SCAN_RESULTS_NO_MEM; + } mutex_unlock(&wl_cache_lock); return status; } @@ -2203,13 +2708,15 @@ _iscan_sysioc_thread(void *data) status = WL_SCAN_RESULTS_PARTIAL; while (down_interruptible(&iscan->sysioc_sem) == 0) { + net_os_wake_lock(iscan->dev); + #if defined(SOFTAP) if (ap_cfg_running) { - WL_TRACE(("%s skipping SCAN ops in AP mode !!!\n", __FUNCTION__)); + WL_SCAN(("%s skipping SCAN ops in AP mode !!!\n", __FUNCTION__)); + net_os_wake_unlock(iscan->dev); continue; } #endif - net_os_wake_lock(iscan->dev); if (iscan->timer_on) { iscan->timer_on = 0; @@ -2225,7 +2732,7 @@ _iscan_sysioc_thread(void *data) #endif if (g_scan_specified_ssid && (iscan_pass_abort == TRUE)) { - WL_TRACE(("%s Get results from specific scan status=%d\n", __FUNCTION__, status)); + WL_SCAN(("%s Get results from specific scan status=%d\n", __FUNCTION__, status)); wl_iw_send_scan_complete(iscan); iscan_pass_abort = FALSE; status = -1; @@ -2233,7 +2740,7 @@ _iscan_sysioc_thread(void *data) switch (status) { case WL_SCAN_RESULTS_PARTIAL: - WL_TRACE(("iscanresults incomplete\n")); + WL_SCAN(("iscanresults incomplete\n")); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) rtnl_lock(); #endif @@ -2247,18 +2754,18 @@ _iscan_sysioc_thread(void *data) iscan->timer_on = 1; break; case WL_SCAN_RESULTS_SUCCESS: - WL_TRACE(("iscanresults complete\n")); + WL_SCAN(("iscanresults complete\n")); iscan->iscan_state = ISCAN_STATE_IDLE; wl_iw_send_scan_complete(iscan); break; case WL_SCAN_RESULTS_PENDING: - WL_TRACE(("iscanresults pending\n")); + WL_SCAN(("iscanresults pending\n")); mod_timer(&iscan->timer, jiffies + iscan->timer_ms*HZ/1000); iscan->timer_on = 1; break; case WL_SCAN_RESULTS_ABORTED: - WL_TRACE(("iscanresults aborted\n")); + WL_SCAN(("iscanresults aborted\n")); iscan->iscan_state = ISCAN_STATE_IDLE; if (g_scan_specified_ssid == 0) wl_iw_send_scan_complete(iscan); @@ -2268,11 +2775,11 @@ _iscan_sysioc_thread(void *data) } break; case WL_SCAN_RESULTS_NO_MEM: - WL_TRACE(("iscanresults can't alloc memory: skip\n")); + WL_SCAN(("iscanresults can't alloc memory: skip\n")); iscan->iscan_state = ISCAN_STATE_IDLE; break; default: - WL_TRACE(("iscanresults returned unknown status %d\n", status)); + WL_SCAN(("iscanresults returned unknown status %d\n", status)); break; } @@ -2288,6 +2795,7 @@ _iscan_sysioc_thread(void *data) } #endif +#if !defined(CSCAN) static void wl_iw_set_ss_cache_timer_flag(void) @@ -2453,18 +2961,14 @@ wl_iw_add_bss_to_ss_cache(wl_scan_results_t *ss_list) if (node) { continue; } - leaf = kmalloc(WLC_IW_SS_CACHE_MAXLEN, GFP_KERNEL); + leaf = kmalloc(bi->length + WLC_IW_SS_CACHE_CTRL_FIELD_MAXLEN, GFP_KERNEL); if (!leaf) { + WL_ERROR(("Memory alloc failure %d\n", \ + bi->length + WLC_IW_SS_CACHE_CTRL_FIELD_MAXLEN)); mutex_unlock(&wl_cache_lock); return -ENOMEM; } - if (bi->length > WLC_IW_BSS_INFO_MAXLEN) { - WL_TRACE(("bss info length is too long : %d\n", bi->length)); - kfree(leaf); - continue; - } - memcpy(leaf->bss_info, bi, bi->length); leaf->next = NULL; leaf->dirty = 1; @@ -2544,6 +3048,7 @@ wl_iw_delete_bss_from_ss_cache(void *addr) return 0; } +#endif static int @@ -2557,6 +3062,11 @@ wl_iw_set_scan( int error; WL_TRACE(("%s dev:%s: SIOCSIWSCAN : SCAN\n", __FUNCTION__, dev->name)); +#if defined(CSCAN) + WL_ERROR(("%s: Scan from SIOCGIWSCAN not supported\n", __FUNCTION__)); + return -EINVAL; +#endif + #if defined(SOFTAP) if (ap_cfg_running) { WL_TRACE(("\n>%s: Not executed, reason -'SOFTAP is active'\n", __FUNCTION__)); @@ -2603,7 +3113,7 @@ wl_iw_set_scan( if ((error = dev_wlc_ioctl(dev, WLC_SCAN, &g_specific_ssid, sizeof(g_specific_ssid)))) { WL_TRACE(("#### Set SCAN for %s failed with %d\n", g_specific_ssid.SSID, error)); - g_scan_specified_ssid = 0; /* Clean to allow future ISCAN */ + g_scan_specified_ssid = 0; return -EBUSY; } @@ -2626,11 +3136,6 @@ wl_iw_iscan_set_scan_broadcast_prep(struct net_device *dev, uint flag) return 0; } - memset(&ssid, 0, sizeof(ssid)); - - iscan->list_cur = iscan->list_hdr; - iscan->iscan_state = ISCAN_STATE_SCANING; - #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) if (flag) rtnl_lock(); @@ -2640,7 +3145,14 @@ wl_iw_iscan_set_scan_broadcast_prep(struct net_device *dev, uint flag) wl_iw_set_event_mask(dev); WL_TRACE(("+++: Set Broadcast ISCAN\n")); + + memset(&ssid, 0, sizeof(ssid)); + + iscan->list_cur = iscan->list_hdr; + iscan->iscan_state = ISCAN_STATE_SCANING; + memset(&iscan->iscan_ex_params_p->params, 0, iscan->iscan_ex_param_size); + wl_iw_iscan_prep(&iscan->iscan_ex_params_p->params, &ssid); wl_iw_iscan(iscan, &ssid, WL_SCAN_ACTION_START); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) @@ -2654,6 +3166,7 @@ wl_iw_iscan_set_scan_broadcast_prep(struct net_device *dev, uint flag) return 0; } + static int wl_iw_iscan_set_scan( struct net_device *dev, @@ -2664,31 +3177,45 @@ wl_iw_iscan_set_scan( { wlc_ssid_t ssid; iscan_info_t *iscan = g_iscan; + int ret = 0; WL_TRACE(("%s: SIOCSIWSCAN : ISCAN\n", dev->name)); +#if defined(CSCAN) + WL_ERROR(("%s: Scan from SIOCGIWSCAN not supported\n", __FUNCTION__)); + return -EINVAL; +#endif + + net_os_wake_lock(dev); + #if defined(SOFTAP) if (ap_cfg_running) { WL_TRACE(("\n>%s: Not executed, reason -'SOFTAP is active'\n", __FUNCTION__)); - return 0; + goto set_scan_end; } #endif if (g_onoff == G_WLAN_SET_OFF) { WL_TRACE(("%s: driver is not up yet after START\n", __FUNCTION__)); - return 0; + goto set_scan_end; + } + +#ifdef PNO_SUPPORT + if (dhd_dev_get_pno_status(dev)) { + WL_ERROR(("%s: Scan called when PNO is active\n", __FUNCTION__)); } +#endif if ((!iscan) || (iscan->sysioc_pid < 0)) { - WL_TRACE(("%s use backup if iscan thread is not successful\n", \ - __FUNCTION__)); - return wl_iw_set_scan(dev, info, wrqu, extra); + WL_ERROR(("%s error\n", __FUNCTION__)); + goto set_scan_end; } if (g_scan_specified_ssid) { WL_TRACE(("%s Specific SCAN already running ignoring BC scan\n", \ __FUNCTION__)); - return EBUSY; + ret = EBUSY; + goto set_scan_end; } memset(&ssid, 0, sizeof(ssid)); @@ -2699,32 +3226,47 @@ wl_iw_iscan_set_scan( if (wrqu->data.flags & IW_SCAN_THIS_ESSID) { int as = 0; struct iw_scan_req *req = (struct iw_scan_req *)extra; - if (g_first_broadcast_scan < BROADCAST_SCAN_FIRST_RESULT_CONSUMED) { - WL_TRACE(("%s First ISCAN in progress : ignoring SC = %s\n", \ - __FUNCTION__, req->essid)); - return -EBUSY; - } ssid.SSID_len = MIN(sizeof(ssid.SSID), req->essid_len); memcpy(ssid.SSID, req->essid, ssid.SSID_len); ssid.SSID_len = htod32(ssid.SSID_len); dev_wlc_ioctl(dev, WLC_SET_PASSIVE_SCAN, &as, sizeof(as)); wl_iw_set_event_mask(dev); - return wl_iw_set_scan(dev, info, wrqu, extra); + ret = wl_iw_set_scan(dev, info, wrqu, extra); + goto set_scan_end; } else { g_scan_specified_ssid = 0; if (iscan->iscan_state == ISCAN_STATE_SCANING) { WL_TRACE(("%s ISCAN already in progress \n", __FUNCTION__)); - return 0; + goto set_scan_end; } } } #endif +#if !defined(CSCAN) + if (g_first_broadcast_scan < BROADCAST_SCAN_FIRST_RESULT_CONSUMED) { + if (++g_first_counter_scans == MAX_ALLOWED_BLOCK_SCAN_FROM_FIRST_SCAN) { + + WL_ERROR(("%s Clean up First scan flag which is %d\n", \ + __FUNCTION__, g_first_broadcast_scan)); + g_first_broadcast_scan = BROADCAST_SCAN_FIRST_RESULT_CONSUMED; + } + else { + WL_ERROR(("%s Ignoring Broadcast Scan:First Scan is not done yet %d\n", \ + __FUNCTION__, g_first_counter_scans)); + ret = -EBUSY; + goto set_scan_end; + } + } +#endif + wl_iw_iscan_set_scan_broadcast_prep(dev, 0); - return 0; +set_scan_end: + net_os_wake_unlock(dev); + return ret; } #endif @@ -2766,6 +3308,32 @@ ie_is_wps_ie(uint8 **wpsie, uint8 **tlvs, int *tlvs_len) } #endif +static inline int _wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data, + size_t len, int uppercase) +{ + size_t i; + char *pos = buf, *end = buf + buf_size; + int ret; + if (buf_size == 0) + return 0; + for (i = 0; i < len; i++) { + ret = snprintf(pos, end - pos, uppercase ? "%02X" : "%02x", + data[i]); + if (ret < 0 || ret >= end - pos) { + end[-1] = '\0'; + return pos - buf; + } + pos += ret; + } + end[-1] = '\0'; + return pos - buf; +} + + +int wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data, size_t len) +{ + return _wpa_snprintf_hex(buf, buf_size, data, len, 0); +} static int wl_iw_handle_scanresults_ies(char **event_p, char *end, @@ -2774,6 +3342,8 @@ wl_iw_handle_scanresults_ies(char **event_p, char *end, #if WIRELESS_EXT > 17 struct iw_event iwe; char *event; + char *buf; + int custom_event_len; event = *event_p; if (bi->ie_length) { @@ -2812,6 +3382,35 @@ wl_iw_handle_scanresults_ies(char **event_p, char *end, } } + ptr = ((uint8 *)bi) + sizeof(wl_bss_info_t); + ptr_len = bi->ie_length; + + while ((ie = bcm_parse_tlvs(ptr, ptr_len, DOT11_MNG_WAPI_ID))) { + WL_TRACE(("%s: found a WAPI IE...\n", __FUNCTION__)); +#ifdef WAPI_IE_USE_GENIE + iwe.cmd = IWEVGENIE; + iwe.u.data.length = ie->len + 2; + event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)ie); +#else + iwe.cmd = IWEVCUSTOM; + custom_event_len = strlen("wapi_ie=") + 2*(ie->len + 2); + iwe.u.data.length = custom_event_len; + + buf = kmalloc(custom_event_len+1, GFP_KERNEL); + if (buf == NULL) + { + WL_ERROR(("malloc(%d) returned NULL...\n", custom_event_len)); + break; + } + + memcpy(buf, "wapi_ie=", 8); + wpa_snprintf_hex(buf + 8, 2+1, &(ie->id), 1); + wpa_snprintf_hex(buf + 10, 2+1, &(ie->len), 1); + wpa_snprintf_hex(buf + 12, 2*ie->len+1, ie->data, ie->len); + event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, buf); +#endif + break; + } *event_p = event; } #endif @@ -2819,6 +3418,7 @@ wl_iw_handle_scanresults_ies(char **event_p, char *end, return 0; } +#ifndef CSCAN static uint wl_iw_get_scan_prep( wl_scan_results_t *list, @@ -2931,12 +3531,16 @@ wl_iw_get_scan( uint buflen_from_user = dwrq->length; uint len = G_SCAN_RESULTS; __u16 len_ret = 0; +#if !defined(CSCAN) __u16 merged_len = 0; +#endif #if defined(WL_IW_USE_ISCAN) iscan_info_t *iscan = g_iscan; iscan_buf_t * p_buf; +#if !defined(CSCAN) uint32 counter = 0; #endif +#endif WL_TRACE(("%s: buflen_from_user %d: \n", dev->name, buflen_from_user)); if (!extra) { @@ -2951,6 +3555,7 @@ wl_iw_get_scan( if (ci.scan_channel) return -EAGAIN; +#if !defined(CSCAN) if (g_ss_cache_ctrl.m_timer_expired) { wl_iw_free_ss_cache(); g_ss_cache_ctrl.m_timer_expired ^= 1; @@ -2968,7 +3573,7 @@ wl_iw_get_scan( else { g_ss_cache_ctrl.m_cons_br_scan_cnt++; } - +#endif if (g_scan_specified_ssid) { @@ -2983,10 +3588,12 @@ wl_iw_get_scan( memset(list, 0, len); list->buflen = htod32(len); if ((error = dev_wlc_ioctl(dev, WLC_SCAN_RESULTS, list, len))) { - WL_TRACE(("%s: %s : Scan_results ERROR %d\n", dev->name, __FUNCTION__, len)); + WL_ERROR(("%s: %s : Scan_results ERROR %d\n", dev->name, __FUNCTION__, error)); dwrq->length = len; - if (g_scan_specified_ssid) + if (g_scan_specified_ssid) { + g_scan_specified_ssid = 0; kfree(list); + } return 0; } list->buflen = dtoh32(list->buflen); @@ -3003,6 +3610,7 @@ wl_iw_get_scan( return -EINVAL; } +#if !defined(CSCAN) if (g_scan_specified_ssid) { wl_iw_add_bss_to_ss_cache(list); @@ -3038,6 +3646,37 @@ wl_iw_get_scan( len_ret += merged_len; wl_iw_run_ss_cache_timer(0); wl_iw_run_ss_cache_timer(1); +#else + + if (g_scan_specified_ssid) { + WL_TRACE(("%s: Specified scan APs in the list =%d\n", __FUNCTION__, list->count)); + len_ret = (__u16) wl_iw_get_scan_prep(list, info, extra, buflen_from_user); + kfree(list); + +#if defined(WL_IW_USE_ISCAN) + p_buf = iscan->list_hdr; + + while (p_buf != iscan->list_cur) { + list_merge = &((wl_iscan_results_t*)p_buf->iscan_buf)->results; + WL_TRACE(("%s: Bcast APs list=%d\n", __FUNCTION__, list_merge->count)); + if (list_merge->count > 0) + len_ret += (__u16) wl_iw_get_scan_prep(list_merge, info, + extra+len_ret, buflen_from_user -len_ret); + p_buf = p_buf->next; + } +#else + list_merge = (wl_scan_results_t *) g_scan; + WL_TRACE(("%s: Bcast APs list=%d\n", __FUNCTION__, list_merge->count)); + if (list_merge->count > 0) + len_ret += (__u16) wl_iw_get_scan_prep(list_merge, info, extra+len_ret, + buflen_from_user -len_ret); +#endif + } + else { + list = (wl_scan_results_t *) g_scan; + len_ret = (__u16) wl_iw_get_scan_prep(list, info, extra, buflen_from_user); + } +#endif #if defined(WL_IW_USE_ISCAN) @@ -3053,6 +3692,7 @@ wl_iw_get_scan( WL_TRACE(("%s return to WE %d bytes APs=%d\n", __FUNCTION__, dwrq->length, list->count)); return 0; } +#endif #if defined(WL_IW_USE_ISCAN) static int @@ -3072,8 +3712,11 @@ wl_iw_iscan_get_scan( iscan_info_t *iscan = g_iscan; iscan_buf_t * p_buf; uint32 counter = 0; + uint8 channel; +#if !defined(CSCAN) __u16 merged_len = 0; uint buflen_from_user = dwrq->length; +#endif WL_TRACE(("%s %s buflen_from_user %d:\n", dev->name, __FUNCTION__, dwrq->length)); @@ -3094,12 +3737,13 @@ wl_iw_iscan_get_scan( dev->name, __FUNCTION__)); return -EAGAIN; } - + if ((!iscan) || (iscan->sysioc_pid < 0)) { - WL_TRACE(("%ssysioc_pid\n", __FUNCTION__)); - return wl_iw_get_scan(dev, info, dwrq, extra); + WL_ERROR(("%ssysioc_pid\n", __FUNCTION__)); + return -EAGAIN; } +#if !defined(CSCAN) if (g_ss_cache_ctrl.m_timer_expired) { wl_iw_free_ss_cache(); g_ss_cache_ctrl.m_timer_expired ^= 1; @@ -3119,6 +3763,7 @@ wl_iw_iscan_get_scan( g_ss_cache_ctrl.m_prev_scan_mode = g_scan_specified_ssid; g_ss_cache_ctrl.m_cons_br_scan_cnt++; } +#endif WL_TRACE(("%s: SIOCGIWSCAN GET broadcast results\n", dev->name)); apcnt = 0; @@ -3169,8 +3814,9 @@ wl_iw_iscan_get_scan( iwe.cmd = SIOCGIWFREQ; - iwe.u.freq.m = wf_channel2mhz(CHSPEC_CHANNEL(bi->chanspec), - CHSPEC_CHANNEL(bi->chanspec) <= CH_MAX_2G_CHANNEL ? + channel = (bi->ctl_ch == 0) ? CHSPEC_CHANNEL(bi->chanspec) : bi->ctl_ch; + iwe.u.freq.m = wf_channel2mhz(channel, + channel <= CH_MAX_2G_CHANNEL ? WF_CHAN_FACTOR_2_4_G : WF_CHAN_FACTOR_5_G); iwe.u.freq.e = 6; event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_FREQ_LEN); @@ -3217,18 +3863,16 @@ wl_iw_iscan_get_scan( dwrq->length = event - extra; dwrq->flags = 0; +#if !defined(CSCAN) wl_iw_merge_scan_cache(info, event, buflen_from_user - dwrq->length, &merged_len); dwrq->length += merged_len; wl_iw_run_ss_cache_timer(0); wl_iw_run_ss_cache_timer(1); - +#endif /* CSCAN */ g_first_broadcast_scan = BROADCAST_SCAN_FIRST_RESULT_CONSUMED; WL_TRACE(("%s return to WE %d bytes APs=%d\n", __FUNCTION__, dwrq->length, counter)); - if (!dwrq->length) - return -EAGAIN; - return 0; } #endif @@ -3242,6 +3886,8 @@ wl_iw_set_essid( ) { int error; + wl_join_params_t join_params; + int join_params_size; WL_TRACE(("%s: SIOCSIWESSID\n", dev->name)); @@ -3262,11 +3908,24 @@ wl_iw_set_essid( g_ssid.SSID_len = 0; } g_ssid.SSID_len = htod32(g_ssid.SSID_len); - if ((error = dev_wlc_ioctl(dev, WLC_SET_SSID, &g_ssid, sizeof(g_ssid)))) + + memset(&join_params, 0, sizeof(join_params)); + join_params_size = sizeof(join_params.ssid); + + memcpy(&join_params.ssid.SSID, g_ssid.SSID, g_ssid.SSID_len); + join_params.ssid.SSID_len = htod32(g_ssid.SSID_len); + memcpy(&join_params.params.bssid, ðer_bcast, ETHER_ADDR_LEN); + + wl_iw_ch_to_chanspec(g_wl_iw_params.target_channel, &join_params, &join_params_size); + + if ((error = dev_wlc_ioctl(dev, WLC_SET_SSID, &join_params, join_params_size))) { + WL_ERROR(("Invalid ioctl data=%d\n", error)); return error; + } if (g_ssid.SSID_len) { - WL_TRACE(("%s: join SSID=%s\n", __FUNCTION__, g_ssid.SSID)); + WL_TRACE(("%s: join SSID=%s ch=%d\n", __FUNCTION__, \ + g_ssid.SSID, g_wl_iw_params.target_channel)); } return 0; } @@ -3294,7 +3953,6 @@ wl_iw_get_essid( ssid.SSID_len = dtoh32(ssid.SSID_len); - memcpy(extra, ssid.SSID, ssid.SSID_len); dwrq->length = ssid.SSID_len; @@ -3319,7 +3977,6 @@ wl_iw_set_nick( if (!extra) return -EINVAL; - if (dwrq->length > sizeof(iw->nickname)) return -E2BIG; @@ -3889,11 +4546,21 @@ wl_iw_set_wpaie( char *extra ) { + uchar buf[WLC_IOCTL_SMLEN] = {0}; + uchar *p = buf; + int wapi_ie_size; WL_TRACE(("%s: SIOCSIWGENIE\n", dev->name)); CHECK_EXTRA_FOR_NULL(extra); + if (extra[0] == DOT11_MNG_WAPI_ID) + { + wapi_ie_size = iwp->length; + memcpy(p, extra, iwp->length); + dev_wlc_bufvar_set(dev, "wapiie", buf, wapi_ie_size); + } + else dev_wlc_bufvar_set(dev, "wpaie", extra, iwp->length); return 0; @@ -4010,6 +4677,12 @@ wl_iw_set_encodeext( case IW_ENCODE_ALG_CCMP: key.algo = CRYPTO_ALGO_AES_CCM; break; + case IW_ENCODE_ALG_SM4: + key.algo = CRYPTO_ALGO_SMS4; + if (iwe->ext_flags & IW_ENCODE_EXT_GROUP_KEY) { + key.flags &= ~WL_PRIMARY_KEY; + } + break; default: break; } @@ -4193,6 +4866,8 @@ wl_iw_set_wpaauth( else if (paramval & IW_AUTH_WPA_VERSION_WPA2) val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED; #endif + else if (paramval & IW_AUTH_WAPI_VERSION_1) + val = WPA_AUTH_WAPI; WL_INFORM(("%s: %d: setting wpa_auth to 0x%0x\n", __FUNCTION__, __LINE__, val)); if ((error = dev_wlc_intvar_set(dev, "wpa_auth", val))) return error; @@ -4207,6 +4882,8 @@ wl_iw_set_wpaauth( val = TKIP_ENABLED; if (paramval & IW_AUTH_CIPHER_CCMP) val = AES_ENABLED; + if (paramval & IW_AUTH_CIPHER_SMS4) + val = SMS4_ENABLED; if (paramid == IW_AUTH_CIPHER_PAIRWISE) { iw->pwsec = val; @@ -4254,9 +4931,12 @@ wl_iw_set_wpaauth( val = WPA2_AUTH_UNSPECIFIED; } #endif + if (paramval & (IW_AUTH_KEY_MGMT_WAPI_PSK | IW_AUTH_KEY_MGMT_WAPI_CERT)) + val = WPA_AUTH_WAPI; WL_INFORM(("%s: %d: setting wpa_auth to %d\n", __FUNCTION__, __LINE__, val)); if ((error = dev_wlc_intvar_set(dev, "wpa_auth", val))) return error; + break; case IW_AUTH_TKIP_COUNTERMEASURES: dev_wlc_bufvar_set(dev, "tkip_countermeasures", (char *)¶mval, 1); @@ -4339,6 +5019,24 @@ wl_iw_set_wpaauth( break; } #endif + case IW_AUTH_WAPI_ENABLED: + if ((error = dev_wlc_intvar_get(dev, "wsec", &val))) + return error; + if (paramval) { + val |= SMS4_ENABLED; + if ((error = dev_wlc_intvar_set(dev, "wsec", val))) { + WL_ERROR(("%s: setting wsec to 0x%0x returned error %d\n", + __FUNCTION__, val, error)); + return error; + } + if ((error = dev_wlc_intvar_set(dev, "wpa_auth", WPA_AUTH_WAPI))) { + WL_ERROR(("%s: setting wpa_auth(WPA_AUTH_WAPI) returned %d\n", + __FUNCTION__, error)); + return error; + } + } + + break; default: break; } @@ -4574,6 +5272,7 @@ int dev_iw_read_cfg1_bss_var(struct net_device *dev, int *val) } +#ifndef AP_ONLY static int wl_bssiovar_mkbuf( const char *iovar, int bssidx, @@ -4616,6 +5315,7 @@ static int wl_bssiovar_mkbuf( *perr = 0; return iolen; } +#endif int get_user_params(char *user_params, struct iw_point *dwrq) @@ -4634,7 +5334,414 @@ int get_user_params(char *user_params, struct iw_point *dwrq) } +#define strtoul(nptr, endptr, base) bcm_strtoul((nptr), (endptr), (base)) + +#if defined(CSCAN) + +static int +wl_iw_combined_scan_set(struct net_device *dev, wlc_ssid_t* ssids_local, int nssid, int nchan) +{ + int params_size = WL_SCAN_PARAMS_FIXED_SIZE + WL_NUMCHANNELS * sizeof(uint16); + int err = 0; + char *p; + int i; + iscan_info_t *iscan = g_iscan; + + WL_TRACE(("%s nssid=%d nchan=%d\n", __FUNCTION__, nssid, nchan)); + + if ((!dev) && (!g_iscan) && (!iscan->iscan_ex_params_p)) { + WL_ERROR(("%s error exit\n", __FUNCTION__)); + err = -1; + goto exit; + } + +#ifdef PNO_SUPPORT + if (dhd_dev_get_pno_status(dev)) { + WL_ERROR(("%s: Scan called when PNO is active\n", __FUNCTION__)); + } +#endif + + params_size += WL_SCAN_PARAMS_SSID_MAX * sizeof(wlc_ssid_t); + + if (nssid > 0) { + i = OFFSETOF(wl_scan_params_t, channel_list) + nchan * sizeof(uint16); + i = ROUNDUP(i, sizeof(uint32)); + if (i + nssid * sizeof(wlc_ssid_t) > params_size) { + printf("additional ssids exceed params_size\n"); + err = -1; + goto exit; + } + + p = ((char*)&iscan->iscan_ex_params_p->params) + i; + memcpy(p, ssids_local, nssid * sizeof(wlc_ssid_t)); + p += nssid * sizeof(wlc_ssid_t); + } else { + p = (char*)iscan->iscan_ex_params_p->params.channel_list + nchan * sizeof(uint16); + } + + + iscan->iscan_ex_params_p->params.channel_num = \ + htod32((nssid << WL_SCAN_PARAMS_NSSID_SHIFT) | \ + (nchan & WL_SCAN_PARAMS_COUNT_MASK)); + + nssid = \ + (uint)((iscan->iscan_ex_params_p->params.channel_num >> WL_SCAN_PARAMS_NSSID_SHIFT) & \ + WL_SCAN_PARAMS_COUNT_MASK); + + + params_size = (int) (p - (char*)iscan->iscan_ex_params_p + nssid * sizeof(wlc_ssid_t)); + iscan->iscan_ex_param_size = params_size; + + iscan->list_cur = iscan->list_hdr; + iscan->iscan_state = ISCAN_STATE_SCANING; + wl_iw_set_event_mask(dev); + mod_timer(&iscan->timer, jiffies + iscan->timer_ms*HZ/1000); + + iscan->timer_on = 1; + +#ifdef SCAN_DUMP + { + int i; + WL_SCAN(("\n### List of SSIDs to scan ###\n")); + for (i = 0; i < nssid; i++) { + if (!ssids_local[i].SSID_len) + WL_SCAN(("%d: Broadcast scan\n", i)); + else + WL_SCAN(("%d: scan for %s size =%d\n", i, \ + ssids_local[i].SSID, ssids_local[i].SSID_len)); + } + WL_SCAN(("### List of channels to scan ###\n")); + for (i = 0; i < nchan; i++) + { + WL_SCAN(("%d ", iscan->iscan_ex_params_p->params.channel_list[i])); + } + WL_SCAN(("\nnprobes=%d\n", iscan->iscan_ex_params_p->params.nprobes)); + WL_SCAN(("active_time=%d\n", iscan->iscan_ex_params_p->params.active_time)); + WL_SCAN(("passive_time=%d\n", iscan->iscan_ex_params_p->params.passive_time)); + WL_SCAN(("home_time=%d\n", iscan->iscan_ex_params_p->params.home_time)); + WL_SCAN(("scan_type=%d\n", iscan->iscan_ex_params_p->params.scan_type)); + WL_SCAN(("\n###################\n")); + } +#endif + + if (params_size > WLC_IOCTL_MEDLEN) { + WL_ERROR(("Set ISCAN for %s due to params_size=%d \n", \ + __FUNCTION__, params_size)); + err = -1; + } + + if ((err = dev_iw_iovar_setbuf(dev, "iscan", iscan->iscan_ex_params_p, \ + iscan->iscan_ex_param_size, \ + iscan->ioctlbuf, sizeof(iscan->ioctlbuf)))) { + WL_ERROR(("Set ISCAN for %s failed with %d\n", __FUNCTION__, err)); + err = -1; + } + +exit: + + return err; +} + + +static int iwpriv_set_cscan(struct net_device *dev, struct iw_request_info *info, \ + union iwreq_data *wrqu, char *ext) +{ + int res = 0; + char *extra = NULL; + iscan_info_t *iscan = g_iscan; + wlc_ssid_t ssids_local[WL_SCAN_PARAMS_SSID_MAX]; + int nssid = 0; + int nchan = 0; + + WL_TRACE(("\%s: info->cmd:%x, info->flags:%x, u.data=0x%p, u.len=%d\n", + __FUNCTION__, info->cmd, info->flags, + wrqu->data.pointer, wrqu->data.length)); + + if (g_onoff == G_WLAN_SET_OFF) { + WL_TRACE(("%s: driver is not up yet after START\n", __FUNCTION__)); + return -1; + } + + if (wrqu->data.length != 0) { + + char *str_ptr; + + if (!iscan->iscan_ex_params_p) { + return -EFAULT; + } + + if (!(extra = kmalloc(wrqu->data.length+1, GFP_KERNEL))) + return -ENOMEM; + + if (copy_from_user(extra, wrqu->data.pointer, wrqu->data.length)) { + kfree(extra); + return -EFAULT; + } + + extra[wrqu->data.length] = 0; + WL_ERROR(("Got str param in iw_point:\n %s\n", extra)); + + str_ptr = extra; + + if (strncmp(str_ptr, GET_SSID, strlen(GET_SSID))) { + WL_ERROR(("%s Error: extracting SSID='' string\n", __FUNCTION__)); + goto exit_proc; + } + str_ptr += strlen(GET_SSID); + nssid = wl_iw_parse_ssid_list(&str_ptr, ssids_local, nssid, \ + WL_SCAN_PARAMS_SSID_MAX); + if (nssid == -1) { + WL_ERROR(("%s wrong ssid list", __FUNCTION__)); + return -1; + } + + memset(iscan->iscan_ex_params_p, 0, iscan->iscan_ex_param_size); + ASSERT(iscan->iscan_ex_param_size < WLC_IOCTL_MAXLEN); + + + wl_iw_iscan_prep(&iscan->iscan_ex_params_p->params, NULL); + iscan->iscan_ex_params_p->version = htod32(ISCAN_REQ_VERSION); + iscan->iscan_ex_params_p->action = htod16(WL_SCAN_ACTION_START); + iscan->iscan_ex_params_p->scan_duration = htod16(0); + + + if ((nchan = wl_iw_parse_channel_list(&str_ptr, \ + &iscan->iscan_ex_params_p->params.channel_list[0], \ + WL_NUMCHANNELS)) == -1) { + WL_ERROR(("%s missing channel list\n", __FUNCTION__)); + return -1; + } + + + get_parmeter_from_string(&str_ptr, \ + GET_NPROBE, PTYPE_INTDEC, \ + &iscan->iscan_ex_params_p->params.nprobes, 2); + + get_parmeter_from_string(&str_ptr, GET_ACTIVE_ASSOC_DWELL, PTYPE_INTDEC, \ + &iscan->iscan_ex_params_p->params.active_time, 4); + + get_parmeter_from_string(&str_ptr, GET_PASSIVE_ASSOC_DWELL, PTYPE_INTDEC, \ + &iscan->iscan_ex_params_p->params.passive_time, 4); + + get_parmeter_from_string(&str_ptr, GET_HOME_DWELL, PTYPE_INTDEC, \ + &iscan->iscan_ex_params_p->params.home_time, 4); + + get_parmeter_from_string(&str_ptr, GET_SCAN_TYPE, PTYPE_INTDEC, \ + &iscan->iscan_ex_params_p->params.scan_type, 1); + + res = wl_iw_combined_scan_set(dev, ssids_local, nssid, nchan); + + } else { + WL_ERROR(("IWPRIV argument len = 0 \n")); + return -1; + } + +exit_proc: + + kfree(extra); + + return res; +} + + +static int +wl_iw_set_cscan( + struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra +) +{ + int res = -1; + iscan_info_t *iscan = g_iscan; + wlc_ssid_t ssids_local[WL_SCAN_PARAMS_SSID_MAX]; + int nssid = 0; + int nchan = 0; + cscan_tlv_t *cscan_tlv_temp; + char type; + char *str_ptr; + int tlv_size_left; +#ifdef TLV_DEBUG + int i; + char tlv_in_example[] = { 'C', 'S', 'C', 'A', 'N', ' ', \ + 0x53, 0x01, 0x00, 0x00, + 'S', + 0x00, + 'S', + 0x04, + 'B', 'R', 'C', 'M', + 'C', + 0x06, + 'P', + 0x94, + 0x11, + 'T', + 0x01 + }; +#endif + + WL_TRACE(("\n### %s: info->cmd:%x, info->flags:%x, u.data=0x%p, u.len=%d\n", + __FUNCTION__, info->cmd, info->flags, + wrqu->data.pointer, wrqu->data.length)); + + net_os_wake_lock(dev); + + if (g_onoff == G_WLAN_SET_OFF) { + WL_TRACE(("%s: driver is not up yet after START\n", __FUNCTION__)); + goto exit_proc; + } + + + if (wrqu->data.length < (strlen(CSCAN_COMMAND) + sizeof(cscan_tlv_t))) { + WL_ERROR(("%s aggument=%d less %d\n", __FUNCTION__, \ + wrqu->data.length, strlen(CSCAN_COMMAND) + sizeof(cscan_tlv_t))); + goto exit_proc; + } + +#ifdef TLV_DEBUG + memcpy(extra, tlv_in_example, sizeof(tlv_in_example)); + wrqu->data.length = sizeof(tlv_in_example); + for (i = 0; i < wrqu->data.length; i++) + printf("%02X ", extra[i]); + printf("\n"); +#endif + + str_ptr = extra; + str_ptr += strlen(CSCAN_COMMAND); + tlv_size_left = wrqu->data.length - strlen(CSCAN_COMMAND); + + cscan_tlv_temp = (cscan_tlv_t *)str_ptr; + memset(ssids_local, 0, sizeof(ssids_local)); + + if ((cscan_tlv_temp->prefix == CSCAN_TLV_PREFIX) && \ + (cscan_tlv_temp->version == CSCAN_TLV_VERSION) && \ + (cscan_tlv_temp->subver == CSCAN_TLV_SUBVERSION)) + { + str_ptr += sizeof(cscan_tlv_t); + tlv_size_left -= sizeof(cscan_tlv_t); + + + if ((nssid = wl_iw_parse_ssid_list_tlv(&str_ptr, ssids_local, \ + WL_SCAN_PARAMS_SSID_MAX, &tlv_size_left)) <= 0) { + WL_ERROR(("SSID is not presented or corrupted ret=%d\n", nssid)); + goto exit_proc; + } + else { + + memset(iscan->iscan_ex_params_p, 0, iscan->iscan_ex_param_size); + + + wl_iw_iscan_prep(&iscan->iscan_ex_params_p->params, NULL); + iscan->iscan_ex_params_p->version = htod32(ISCAN_REQ_VERSION); + iscan->iscan_ex_params_p->action = htod16(WL_SCAN_ACTION_START); + iscan->iscan_ex_params_p->scan_duration = htod16(0); + + + while (tlv_size_left > 0) + { + type = str_ptr[0]; + switch (type) { + case CSCAN_TLV_TYPE_CHANNEL_IE: + + if ((nchan = wl_iw_parse_channel_list_tlv(&str_ptr, \ + &iscan->iscan_ex_params_p->params.channel_list[0], \ + WL_NUMCHANNELS, &tlv_size_left)) == -1) { + WL_ERROR(("%s missing channel list\n", \ + __FUNCTION__)); + goto exit_proc; + } + break; + case CSCAN_TLV_TYPE_NPROBE_IE: + if ((res = wl_iw_parse_data_tlv(&str_ptr, \ + &iscan->iscan_ex_params_p->params.nprobes, \ + sizeof(iscan->iscan_ex_params_p->params.nprobes), \ + type, sizeof(char), &tlv_size_left)) == -1) { + WL_ERROR(("%s return %d\n", \ + __FUNCTION__, res)); + goto exit_proc; + } + break; + case CSCAN_TLV_TYPE_ACTIVE_IE: + if ((res = wl_iw_parse_data_tlv(&str_ptr, \ + &iscan->iscan_ex_params_p->params.active_time, \ + sizeof(iscan->iscan_ex_params_p->params.active_time), \ + type, sizeof(short), &tlv_size_left)) == -1) { + WL_ERROR(("%s return %d\n", \ + __FUNCTION__, res)); + goto exit_proc; + } + break; + case CSCAN_TLV_TYPE_PASSIVE_IE: + if ((res = wl_iw_parse_data_tlv(&str_ptr, \ + &iscan->iscan_ex_params_p->params.passive_time, \ + sizeof(iscan->iscan_ex_params_p->params.passive_time), \ + type, sizeof(short), &tlv_size_left)) == -1) { + WL_ERROR(("%s return %d\n", \ + __FUNCTION__, res)); + goto exit_proc; + } + break; + case CSCAN_TLV_TYPE_HOME_IE: + if ((res = wl_iw_parse_data_tlv(&str_ptr, \ + &iscan->iscan_ex_params_p->params.home_time, \ + sizeof(iscan->iscan_ex_params_p->params.home_time), \ + type, sizeof(short), &tlv_size_left)) == -1) { + WL_ERROR(("%s return %d\n", \ + __FUNCTION__, res)); + goto exit_proc; + } + break; + case CSCAN_TLV_TYPE_STYPE_IE: + if ((res = wl_iw_parse_data_tlv(&str_ptr, \ + &iscan->iscan_ex_params_p->params.scan_type, \ + sizeof(iscan->iscan_ex_params_p->params.scan_type), \ + type, sizeof(char), &tlv_size_left)) == -1) { + WL_ERROR(("%s return %d\n", \ + __FUNCTION__, res)); + goto exit_proc; + } + break; + + default : + WL_ERROR(("%s get unkwown type %X\n", \ + __FUNCTION__, type)); + goto exit_proc; + break; + } + } + } + } + else { + WL_ERROR(("%s get wrong TLV command\n", __FUNCTION__)); + goto exit_proc; + } + + if (g_first_broadcast_scan < BROADCAST_SCAN_FIRST_RESULT_CONSUMED) { + if (++g_first_counter_scans == MAX_ALLOWED_BLOCK_SCAN_FROM_FIRST_SCAN) { + + WL_ERROR(("%s Clean up First scan flag which is %d\n", \ + __FUNCTION__, g_first_broadcast_scan)); + g_first_broadcast_scan = BROADCAST_SCAN_FIRST_RESULT_CONSUMED; + } + else { + WL_ERROR(("%s Ignoring CSCAN : First Scan is not done yet %d\n", \ + __FUNCTION__, g_first_counter_scans)); + res = -EBUSY; + goto exit_proc; + } + } + + res = wl_iw_combined_scan_set(dev, ssids_local, nssid, nchan); + +exit_proc: + net_os_wake_unlock(dev); + return res; +} + +#endif + #ifdef SOFTAP +#ifndef AP_ONLY static int thr_wait_for_2nd_eth_dev(void *data) { @@ -4670,7 +5777,10 @@ fail: return ret; } - +#endif +#ifndef AP_ONLY +static int last_auto_channel = 6; +#endif static int get_softap_auto_channel(struct net_device *dev, struct ap_profile *ap) { int chosen = 0; @@ -4681,12 +5791,30 @@ static int get_softap_auto_channel(struct net_device *dev, struct ap_profile *ap int ret = 0; wlc_ssid_t null_ssid; int res = 0; - +#ifndef AP_ONLY + int iolen = 0; + int mkvar_err = 0; + int bsscfg_index = 1; + char buf[WLC_IOCTL_SMLEN]; +#endif WL_SOFTAP(("Enter %s\n", __FUNCTION__)); + +#ifndef AP_ONLY + if (ap_cfg_running) { + ap->channel = last_auto_channel; + return res; + } +#endif memset(&null_ssid, 0, sizeof(wlc_ssid_t)); res |= dev_wlc_ioctl(dev, WLC_UP, &updown, sizeof(updown)); +#ifdef AP_ONLY res |= dev_wlc_ioctl(dev, WLC_SET_SSID, &null_ssid, sizeof(null_ssid)); - +#else + iolen = wl_bssiovar_mkbuf("ssid", bsscfg_index, (char *)(&null_ssid), \ + null_ssid.SSID_len+4, buf, sizeof(buf), &mkvar_err); + ASSERT(iolen); + res |= dev_wlc_ioctl(dev, WLC_SET_VAR, buf, iolen); +#endif auto_channel_retry: request.count = htod32(0); ret = dev_wlc_ioctl(dev, WLC_START_CHANNEL_SEL, &request, sizeof(request)); @@ -4716,6 +5844,11 @@ static int get_softap_auto_channel(struct net_device *dev, struct ap_profile *ap WL_ERROR(("%s fail to set up err =%d\n", __FUNCTION__, res)); goto fail; } +#ifndef AP_ONLY + if (!res) + last_auto_channel = ap->channel; +#endif + fail : return res; } @@ -4728,21 +5861,23 @@ static int set_ap_cfg(struct net_device *dev, struct ap_profile *ap) wlc_ssid_t ap_ssid; int max_assoc = 8; - int mpc = 0; int res = 0; int apsta_var = 0; +#ifndef AP_ONLY + int mpc = 0; int iolen = 0; int mkvar_err = 0; int bsscfg_index = 1; char buf[WLC_IOCTL_SMLEN]; +#endif if (!dev) { WL_ERROR(("%s: dev is null\n", __FUNCTION__)); return -1; } - net_os_wake_lock(dev); + net_os_wake_lock(dev); WL_SOFTAP(("wl_iw: set ap profile:\n")); WL_SOFTAP((" ssid = '%s'\n", ap->ssid)); @@ -4752,8 +5887,17 @@ static int set_ap_cfg(struct net_device *dev, struct ap_profile *ap) WL_SOFTAP((" channel = %d\n", ap->channel)); WL_SOFTAP((" max scb = %d\n", ap->max_scb)); +#ifdef AP_ONLY + if (ap_cfg_running) { + wl_iw_softap_deassoc_stations(dev); + ap_cfg_running = FALSE; + } +#endif + if (ap_cfg_running == FALSE) { +#ifndef AP_ONLY + sema_init(&ap_eth_sema, 0); mpc = 0; @@ -4761,6 +5905,7 @@ static int set_ap_cfg(struct net_device *dev, struct ap_profile *ap) WL_ERROR(("%s fail to set mpc\n", __FUNCTION__)); goto fail; } +#endif updown = 0; if ((res = dev_wlc_ioctl(dev, WLC_DOWN, &updown, sizeof(updown)))) { @@ -4845,6 +5990,15 @@ static int set_ap_cfg(struct net_device *dev, struct ap_profile *ap) ap_ssid.SSID_len = strlen(ap->ssid); strncpy(ap_ssid.SSID, ap->ssid, ap_ssid.SSID_len); +#ifdef AP_ONLY + if ((res = wl_iw_set_ap_security(dev, &my_ap)) != 0) { + WL_ERROR(("ERROR:%d in:%s, wl_iw_set_ap_security is skipped\n", \ + res, __FUNCTION__)); + goto fail; + } + wl_iw_send_priv_event(dev, "ASCII_CMD=AP_BSS_START"); + ap_cfg_running = TRUE; +#else iolen = wl_bssiovar_mkbuf("ssid", bsscfg_index, (char *)(&ap_ssid), ap_ssid.SSID_len+4, buf, sizeof(buf), &mkvar_err); ASSERT(iolen); @@ -4874,12 +6028,13 @@ static int set_ap_cfg(struct net_device *dev, struct ap_profile *ap) goto fail; } } +#endif fail: WL_SOFTAP(("%s exit with %d\n", __FUNCTION__, res)); - net_os_wake_unlock(dev); + net_os_wake_unlock(dev); - return res; + return res; } @@ -4890,6 +6045,11 @@ static int wl_iw_set_ap_security(struct net_device *dev, struct ap_profile *ap) int res = 0; int i; char *ptr; +#ifdef AP_ONLY + int mpc = 0; + wlc_ssid_t ap_ssid; +#endif + wl_wsec_key_t key; WL_SOFTAP(("\nsetting SOFTAP security mode:\n")); WL_SOFTAP(("wl_iw: set ap profile:\n")); @@ -4912,7 +6072,6 @@ static int wl_iw_set_ap_security(struct net_device *dev, struct ap_profile *ap) } else if (strnicmp(ap->sec, "wep", strlen("wep")) == 0) { - wl_wsec_key_t key; memset(&key, 0, sizeof(key)); wsec = WEP_ENABLED; @@ -5030,6 +6189,16 @@ static int wl_iw_set_ap_security(struct net_device *dev, struct ap_profile *ap) WL_SOFTAP((" wsec & auth set 'wpa-psk' (TKIP), result:&d %d\n", res)); } +#ifdef AP_ONLY + ap_ssid.SSID_len = strlen(ap->ssid); + strncpy(ap_ssid.SSID, ap->ssid, ap_ssid.SSID_len); + res |= dev_wlc_ioctl(dev, WLC_SET_SSID, &ap_ssid, sizeof(ap_ssid)); + mpc = 0; + res |= dev_wlc_intvar_set(dev, "mpc", mpc); + if (strnicmp(ap->sec, "wep", strlen("wep")) == 0) { + res |= dev_wlc_ioctl(dev, WLC_SET_KEY, &key, sizeof(key)); + } +#endif return res; } @@ -5045,7 +6214,7 @@ int get_parmeter_from_string( char *param_str_end; char *orig_str = *str_ptr; - if (!strncmp(*str_ptr, token, strlen(token))) { + if ((*str_ptr) && !strncmp(*str_ptr, token, strlen(token))) { strsep(str_ptr, "=,"); param_str_begin = *str_ptr; @@ -5163,13 +6332,17 @@ static int iwpriv_softap_stop(struct net_device *dev, return res; } - net_os_wake_lock(dev); + net_os_wake_lock(dev); if ((ap_cfg_running == TRUE)) { - wl_iw_softap_deassoc_stations(ap_net_dev); +#ifdef AP_ONLY + wl_iw_softap_deassoc_stations(dev); +#else + wl_iw_softap_deassoc_stations(ap_net_dev); if ((res = dev_iw_write_cfg1_bss_var(dev, 2)) < 0) WL_ERROR(("%s failed to del BSS err = %d", __FUNCTION__, res)); +#endif bcm_mdelay(100); @@ -5181,9 +6354,9 @@ static int iwpriv_softap_stop(struct net_device *dev, WL_SOFTAP(("%s Done with %d\n", __FUNCTION__, res)); - net_os_wake_unlock(dev); + net_os_wake_unlock(dev); - return res; + return res; } @@ -5293,10 +6466,11 @@ iwpriv_en_ap_bss( return -1; } - net_os_wake_lock(dev); + net_os_wake_lock(dev); WL_TRACE(("%s: rcvd IWPRIV IOCTL: for dev:%s\n", __FUNCTION__, dev->name)); +#ifndef AP_ONLY if ((res = wl_iw_set_ap_security(dev, &my_ap)) != 0) { WL_ERROR((" %s ERROR setting SOFTAP security in :%d\n", __FUNCTION__, res)); } @@ -5307,9 +6481,10 @@ iwpriv_en_ap_bss( bcm_mdelay(100); } +#endif WL_SOFTAP(("%s done with res %d \n", __FUNCTION__, res)); - net_os_wake_unlock(dev); + net_os_wake_unlock(dev); return res; } @@ -5440,6 +6615,7 @@ int wl_iw_process_private_ascii_cmd( WL_SOFTAP(("\n!!! got 'WL_AP_EN_BSS' from WPA supplicant, dev:%s\n", dev->name)); +#ifndef AP_ONLY if (ap_net_dev == NULL) { printf("\n ERROR: SOFTAP net_dev* is NULL !!!\n"); } else { @@ -5447,15 +6623,21 @@ int wl_iw_process_private_ascii_cmd( WL_ERROR(("%s line %d fail to set bss up\n", \ __FUNCTION__, __LINE__)); } - +#else + if ((ret = iwpriv_en_ap_bss(dev, info, dwrq, cmd_str)) < 0) + WL_ERROR(("%s line %d fail to set bss up\n", \ + __FUNCTION__, __LINE__)); +#endif } else if (strnicmp(sub_cmd, "ASSOC_LST", strlen("ASSOC_LST")) == 0) { /* no code yet */ } else if (strnicmp(sub_cmd, "AP_BSS_STOP", strlen("AP_BSS_STOP")) == 0) { WL_SOFTAP((" \n temp DOWN SOFTAP\n")); +#ifndef AP_ONLY if ((ret = dev_iw_write_cfg1_bss_var(dev, 0)) < 0) { WL_ERROR(("%s line %d fail to set bss down\n", \ __FUNCTION__, __LINE__)); } +#endif } return ret; @@ -5520,6 +6702,28 @@ static int wl_iw_set_priv( ret = wl_iw_set_country(dev, info, (union iwreq_data *)dwrq, extra); else if (strnicmp(extra, "STOP", strlen("STOP")) == 0) ret = wl_iw_control_wl_off(dev, info); + else if (strnicmp(extra, BAND_GET_CMD, strlen(BAND_GET_CMD)) == 0) + ret = wl_iw_get_band(dev, info, (union iwreq_data *)dwrq, extra); + else if (strnicmp(extra, BAND_SET_CMD, strlen(BAND_SET_CMD)) == 0) + ret = wl_iw_set_band(dev, info, (union iwreq_data *)dwrq, extra); + else if (strnicmp(extra, DTIM_SKIP_GET_CMD, strlen(DTIM_SKIP_GET_CMD)) == 0) + ret = wl_iw_get_dtim_skip(dev, info, (union iwreq_data *)dwrq, extra); + else if (strnicmp(extra, DTIM_SKIP_SET_CMD, strlen(DTIM_SKIP_SET_CMD)) == 0) + ret = wl_iw_set_dtim_skip(dev, info, (union iwreq_data *)dwrq, extra); + else if (strnicmp(extra, SETSUSPEND_CMD, strlen(SETSUSPEND_CMD)) == 0) + ret = wl_iw_set_suspend(dev, info, (union iwreq_data *)dwrq, extra); +#if defined(PNO_SUPPORT) + else if (strnicmp(extra, PNOSSIDCLR_SET_CMD, strlen(PNOSSIDCLR_SET_CMD)) == 0) + ret = wl_iw_set_pno_reset(dev, info, (union iwreq_data *)dwrq, extra); + else if (strnicmp(extra, PNOSETUP_SET_CMD, strlen(PNOSETUP_SET_CMD)) == 0) + ret = wl_iw_set_pno_set(dev, info, (union iwreq_data *)dwrq, extra); + else if (strnicmp(extra, PNOENABLE_SET_CMD, strlen(PNOENABLE_SET_CMD)) == 0) + ret = wl_iw_set_pno_enable(dev, info, (union iwreq_data *)dwrq, extra); +#endif +#if defined(CSCAN) + else if (strnicmp(extra, CSCAN_COMMAND, strlen(CSCAN_COMMAND)) == 0) + ret = wl_iw_set_cscan(dev, info, (union iwreq_data *)dwrq, extra); +#endif #ifdef CUSTOMER_HW2 else if (strnicmp(extra, "POWERMODE", strlen("POWERMODE")) == 0) ret = wl_iw_set_power_mode(dev, info, (union iwreq_data *)dwrq, extra); @@ -5529,6 +6733,8 @@ static int wl_iw_set_priv( else if (strnicmp(extra, "POWERMODE", strlen("POWERMODE")) == 0) ret = wl_iw_set_btcoex_dhcp(dev, info, (union iwreq_data *)dwrq, extra); #endif + else if (strnicmp(extra, "GETPOWER", strlen("GETPOWER")) == 0) + ret = wl_iw_get_power_mode(dev, info, (union iwreq_data *)dwrq, extra); #ifdef SOFTAP else if (strnicmp(extra, "ASCII_CMD", strlen("ASCII_CMD")) == 0) { wl_iw_process_private_ascii_cmd(dev, info, (union iwreq_data *)dwrq, extra); @@ -5677,8 +6883,13 @@ static const iw_handler wl_iw_priv_handler[] = { (iw_handler)iwpriv_softap_stop, NULL, - (iw_handler)iwpriv_fw_reload + (iw_handler)iwpriv_fw_reload, #endif +#if defined(CSCAN) + + NULL, + (iw_handler)iwpriv_set_cscan +#endif }; static const struct iw_priv_args wl_iw_priv_args[] = { @@ -5775,6 +6986,14 @@ static const struct iw_priv_args wl_iw_priv_args[] = { "WL_FW_RELOAD" }, #endif +#if defined(CSCAN) + { + WL_COMBO_SCAN, + IW_PRIV_TYPE_CHAR | 1024, + 0, + "CSCAN" + }, +#endif }; const struct iw_handler_def wl_iw_handler_def = @@ -5805,11 +7024,14 @@ int wl_iw_ioctl( char *extra = NULL; int token_size = 1, max_tokens = 0, ret = 0; + net_os_wake_lock(dev); + WL_TRACE(("%s: cmd:%x alled via dhd->do_ioctl()entry point\n", __FUNCTION__, cmd)); if (cmd < SIOCIWFIRST || IW_IOCTL_IDX(cmd) >= ARRAYSIZE(wl_iw_handler) || !(handler = wl_iw_handler[IW_IOCTL_IDX(cmd)])) { WL_ERROR(("%s: error in cmd=%x : not supported\n", __FUNCTION__, cmd)); + net_os_wake_unlock(dev); return -EOPNOTSUPP; } @@ -5874,14 +7096,18 @@ int wl_iw_ioctl( if (wrq->u.data.length > max_tokens) { WL_ERROR(("%s: error in cmd=%x wrq->u.data.length=%d > max_tokens=%d\n", \ __FUNCTION__, cmd, wrq->u.data.length, max_tokens)); - return -E2BIG; + ret = -E2BIG; + goto wl_iw_ioctl_done; + } + if (!(extra = kmalloc(max_tokens * token_size, GFP_KERNEL))) { + ret = -ENOMEM; + goto wl_iw_ioctl_done; } - if (!(extra = kmalloc(max_tokens * token_size, GFP_KERNEL))) - return -ENOMEM; if (copy_from_user(extra, wrq->u.data.pointer, wrq->u.data.length * token_size)) { kfree(extra); - return -EFAULT; + ret = -EFAULT; + goto wl_iw_ioctl_done; } } @@ -5893,12 +7119,17 @@ int wl_iw_ioctl( if (extra) { if (copy_to_user(wrq->u.data.pointer, extra, wrq->u.data.length * token_size)) { kfree(extra); - return -EFAULT; + ret = -EFAULT; + goto wl_iw_ioctl_done; } kfree(extra); } +wl_iw_ioctl_done: + + net_os_wake_unlock(dev); + return ret; } @@ -6015,6 +7246,8 @@ wl_iw_event(struct net_device *dev, wl_event_msg_t *e, void* data) uint32 datalen = ntoh32(e->datalen); uint32 status = ntoh32(e->status); uint32 toto; + static uint32 roam_no_success = 0; + static bool roam_no_success_send = FALSE; memset(&wrqu, 0, sizeof(wrqu)); memset(extra, 0, sizeof(extra)); @@ -6024,7 +7257,7 @@ wl_iw_event(struct net_device *dev, wl_event_msg_t *e, void* data) return; } - net_os_wake_lock(dev); + net_os_wake_lock(dev); WL_TRACE(("%s: dev=%s event=%d \n", __FUNCTION__, dev->name, event_type)); @@ -6071,20 +7304,41 @@ wl_iw_event(struct net_device *dev, wl_event_msg_t *e, void* data) WL_SOFTAP(("STA connect received %d\n", event_type)); if (ap_cfg_running) { wl_iw_send_priv_event(priv_dev, "STA_JOIN"); - goto wl_iw_event_end; + goto wl_iw_event_end; } #endif memcpy(wrqu.addr.sa_data, &e->addr, ETHER_ADDR_LEN); wrqu.addr.sa_family = ARPHRD_ETHER; cmd = IWEVREGISTERED; break; + case WLC_E_ROAM: + if (status != WLC_E_STATUS_SUCCESS) { + roam_no_success++; + if ((roam_no_success == 3) && (roam_no_success_send == FALSE)) { + + roam_no_success_send = TRUE; + bzero(wrqu.addr.sa_data, ETHER_ADDR_LEN); + bzero(&extra, ETHER_ADDR_LEN); + cmd = SIOCGIWAP; + WL_ERROR(("%s ROAMING did not succeeded , send Link Down\n", \ + __FUNCTION__)); + } else { + WL_TRACE(("##### ROAMING did not succeeded %d\n", roam_no_success)); + goto wl_iw_event_end; + } + } else { + memcpy(wrqu.addr.sa_data, &e->addr.octet, ETHER_ADDR_LEN); + wrqu.addr.sa_family = ARPHRD_ETHER; + cmd = SIOCGIWAP; + } + break; case WLC_E_DEAUTH_IND: case WLC_E_DISASSOC_IND: #if defined(SOFTAP) WL_SOFTAP(("STA disconnect received %d\n", event_type)); if (ap_cfg_running) { wl_iw_send_priv_event(priv_dev, "STA_LEAVE"); - goto wl_iw_event_end; + goto wl_iw_event_end; } #endif cmd = SIOCGIWAP; @@ -6097,7 +7351,11 @@ wl_iw_event(struct net_device *dev, wl_event_msg_t *e, void* data) cmd = SIOCGIWAP; if (!(flags & WLC_EVENT_MSG_LINK)) { #ifdef SOFTAP +#ifdef AP_ONLY + if (ap_cfg_running) { +#else if (ap_cfg_running && !strncmp(dev->name, "wl0.1", 5)) { +#endif WL_SOFTAP(("AP DOWN %d\n", event_type)); wl_iw_send_priv_event(priv_dev, "AP_DOWN"); } else { @@ -6111,7 +7369,6 @@ wl_iw_event(struct net_device *dev, wl_event_msg_t *e, void* data) bzero(wrqu.addr.sa_data, ETHER_ADDR_LEN); bzero(&extra, ETHER_ADDR_LEN); - net_os_wake_lock_timeout_enable(dev); } else { memcpy(wrqu.addr.sa_data, &e->addr, ETHER_ADDR_LEN); @@ -6120,16 +7377,23 @@ wl_iw_event(struct net_device *dev, wl_event_msg_t *e, void* data) memcpy(g_ss_cache_ctrl.m_active_bssid, &e->addr, ETHER_ADDR_LEN); #ifdef SOFTAP +#ifdef AP_ONLY + if (ap_cfg_running) { +#else if (ap_cfg_running && !strncmp(dev->name, "wl0.1", 5)) { +#endif WL_SOFTAP(("AP UP %d\n", event_type)); wl_iw_send_priv_event(priv_dev, "AP_UP"); } else { WL_TRACE(("STA_LINK_UP\n")); + roam_no_success_send = FALSE; + roam_no_success = 0; } #endif WL_TRACE(("Link UP\n")); } + net_os_wake_lock_timeout_enable(dev); wrqu.addr.sa_family = ARPHRD_ETHER; break; case WLC_E_ACTION_FRAME: @@ -6210,15 +7474,30 @@ wl_iw_event(struct net_device *dev, wl_event_msg_t *e, void* data) } else { cmd = SIOCGIWSCAN; wrqu.data.length = strlen(extra); - WL_TRACE(("Event WLC_E_SCAN_COMPLETE from specific scan\n")); + WL_TRACE(("Event WLC_E_SCAN_COMPLETE from specific scan %d\n", \ + g_iscan->iscan_state)); } #else cmd = SIOCGIWSCAN; wrqu.data.length = strlen(extra); WL_TRACE(("Event WLC_E_SCAN_COMPLETE\n")); -#endif +#endif break; + case WLC_E_PFN_NET_FOUND: + { + wlc_ssid_t * ssid; + ssid = (wlc_ssid_t *)data; + WL_TRACE(("%s Event WLC_E_PFN_NET_FOUND, send %s up : find %s len=%d\n", \ + __FUNCTION__, PNO_EVENT_UP, ssid->SSID, ssid->SSID_len)); + net_os_wake_lock_timeout_enable(dev); + cmd = IWEVCUSTOM; + memset(&wrqu, 0, sizeof(wrqu)); + strcpy(extra, PNO_EVENT_UP); + wrqu.data.length = strlen(extra); + } + break; + default: WL_TRACE(("Unknown Event %d: ignoring\n", event_type)); @@ -6247,8 +7526,8 @@ wl_iw_event(struct net_device *dev, wl_event_msg_t *e, void* data) } #endif wl_iw_event_end: - net_os_wake_unlock(dev); -#endif + net_os_wake_unlock(dev); +#endif } int wl_iw_get_wireless_stats(struct net_device *dev, struct iw_statistics *wstats) @@ -6416,7 +7695,7 @@ _bt_dhcp_sysioc_thread(void *data) g_bt->bt_state = BT_DHCP_IDLE; g_bt->timer_on = 0; break; - } + } net_os_wake_unlock(g_bt->dev); } @@ -6480,6 +7759,7 @@ wl_iw_bt_init(struct net_device *dev) int wl_iw_attach(struct net_device *dev, void * dhdp) { + int params_size; wl_iw_t *iw; #if defined(WL_IW_USE_ISCAN) iscan_info_t *iscan = NULL; @@ -6492,19 +7772,33 @@ int wl_iw_attach(struct net_device *dev, void * dhdp) if (!dev) return 0; + memset(&g_wl_iw_params, 0, sizeof(wl_iw_extra_params_t)); + +#ifdef CSCAN + params_size = (WL_SCAN_PARAMS_FIXED_SIZE + OFFSETOF(wl_iscan_params_t, params)) + + (WL_NUMCHANNELS * sizeof(uint16)) + WL_SCAN_PARAMS_SSID_MAX * sizeof(wlc_ssid_t); +#else + params_size = (WL_SCAN_PARAMS_FIXED_SIZE + OFFSETOF(wl_iscan_params_t, params)); +#endif iscan = kmalloc(sizeof(iscan_info_t), GFP_KERNEL); if (!iscan) return -ENOMEM; memset(iscan, 0, sizeof(iscan_info_t)); + + iscan->iscan_ex_params_p = (wl_iscan_params_t*)kmalloc(params_size, GFP_KERNEL); + if (!iscan->iscan_ex_params_p) + return -ENOMEM; + iscan->iscan_ex_param_size = params_size; iscan->sysioc_pid = -1; g_iscan = iscan; iscan->dev = dev; iscan->iscan_state = ISCAN_STATE_IDLE; g_first_broadcast_scan = BROADCAST_SCAN_FIRST_IDLE; + g_first_counter_scans = 0; g_iscan->scan_flag = 0; - iscan->timer_ms = 3000; + iscan->timer_ms = 8000; init_timer(&iscan->timer); iscan->timer.data = (ulong)iscan; iscan->timer.function = wl_iw_timerfunc; @@ -6530,11 +7824,12 @@ int wl_iw_attach(struct net_device *dev, void * dhdp) memset(g_scan, 0, G_SCAN_RESULTS); g_scan_specified_ssid = 0; +#if !defined(CSCAN) wl_iw_init_ss_cache_ctrl(); +#endif wl_iw_bt_init(dev); - return 0; } @@ -6556,6 +7851,7 @@ void wl_iw_detach(void) kfree(iscan->list_hdr); iscan->list_hdr = buf; } + kfree(iscan->iscan_ex_params_p); kfree(iscan); g_iscan = NULL; mutex_unlock(&wl_cache_lock); @@ -6565,7 +7861,9 @@ void wl_iw_detach(void) kfree(g_scan); g_scan = NULL; +#if !defined(CSCAN) wl_iw_release_ss_cache_ctrl(); +#endif wl_iw_bt_release(); #ifdef SOFTAP if (ap_cfg_running) { diff --git a/bcm4329/src/wl/sys/wl_iw.h b/bcm4329/src/wl/sys/wl_iw.h index aea656b..43088cf 100644 --- a/bcm4329/src/wl/sys/wl_iw.h +++ b/bcm4329/src/wl/sys/wl_iw.h @@ -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: wl_iw.h,v 1.5.34.1.6.16 2010/04/19 21:32:10 Exp $ + * $Id: wl_iw.h,v 1.5.34.1.6.36.4.1 2010/09/10 19:24:30 Exp $ */ @@ -34,6 +34,32 @@ #include <proto/ethernet.h> #include <wlioctl.h> +#define WL_SCAN_PARAMS_SSID_MAX 10 +#define GET_SSID "SSID=" +#define GET_CHANNEL "CH=" +#define GET_NPROBE "NPROBE=" +#define GET_ACTIVE_ASSOC_DWELL "ACTIVE=" +#define GET_PASSIVE_ASSOC_DWELL "PASSIVE=" +#define GET_HOME_DWELL "HOME=" +#define GET_SCAN_TYPE "TYPE=" + +#define BAND_GET_CMD "GETBAND" +#define BAND_SET_CMD "SETBAND" +#define DTIM_SKIP_GET_CMD "DTIMSKIPGET" +#define DTIM_SKIP_SET_CMD "DTIMSKIPSET" +#define SETSUSPEND_CMD "SETSUSPENDOPT" +#define PNOSSIDCLR_SET_CMD "PNOSSIDCLR" +#define PNOSETUP_SET_CMD "PNOSETUP " +#define PNOENABLE_SET_CMD "PNOFORCE" +#define PNODEBUG_SET_CMD "PNODEBUG" + +#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] +#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" + + +typedef struct wl_iw_extra_params { + int target_channel; +} wl_iw_extra_params_t; #define WL_IW_RSSI_MINVAL -200 #define WL_IW_RSSI_NO_SIGNAL -91 @@ -61,7 +87,7 @@ #define AP_LPB_CMD (SIOCIWFIRSTPRIV+23) #define WL_AP_STOP (SIOCIWFIRSTPRIV+25) #define WL_FW_RELOAD (SIOCIWFIRSTPRIV+27) -#define WL_AP_SPARE2 (SIOCIWFIRSTPRIV+29) +#define WL_COMBO_SCAN (SIOCIWFIRSTPRIV+29) #define WL_AP_SPARE3 (SIOCIWFIRSTPRIV+31) #define G_SCAN_RESULTS (8*1024) #define WE_ADD_EVENT_FIX 0x80 @@ -95,7 +121,7 @@ typedef struct wl_iw { #define WLC_IW_BSS_INFO_MAXLEN \ (WLC_IW_SS_CACHE_MAXLEN - WLC_IW_SS_CACHE_CTRL_FIELD_MAXLEN) -typedef struct wl_iw_ss_cache{ +typedef struct wl_iw_ss_cache { uint32 buflen; uint32 version; uint32 count; @@ -166,6 +192,11 @@ extern int net_os_wake_lock(struct net_device *dev); extern int net_os_wake_unlock(struct net_device *dev); extern int net_os_wake_lock_timeout(struct net_device *dev); extern int net_os_wake_lock_timeout_enable(struct net_device *dev); +extern int net_os_set_suspend_disable(struct net_device *dev, int val); +extern int net_os_set_suspend(struct net_device *dev, int val); +extern int net_os_set_dtim_skip(struct net_device *dev, int val); +extern int net_os_set_packet_filter(struct net_device *dev, int val); +extern int net_os_send_hang_message(struct net_device *dev); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) #define IWE_STREAM_ADD_EVENT(info, stream, ends, iwe, extra) \ @@ -183,4 +214,65 @@ extern int net_os_wake_lock_timeout_enable(struct net_device *dev); iwe_stream_add_point(stream, ends, iwe, extra) #endif -#endif +extern int dhd_pno_enable(dhd_pub_t *dhd, int pfn_enabled); +extern int dhd_pno_clean(dhd_pub_t *dhd); +extern int dhd_pno_set(dhd_pub_t *dhd, wlc_ssid_t* ssids_local, int nssid, ushort scan_fr); +extern int dhd_pno_get_status(dhd_pub_t *dhd); +extern int dhd_dev_pno_reset(struct net_device *dev); +extern int dhd_dev_pno_set(struct net_device *dev, wlc_ssid_t* ssids_local, \ + int nssid, ushort scan_fr); +extern int dhd_dev_pno_enable(struct net_device *dev, int pfn_enabled); +extern int dhd_dev_get_pno_status(struct net_device *dev); + +#define PNO_TLV_PREFIX 'S' +#define PNO_TLV_VERSION 1 +#define PNO_TLV_SUBVERSION 1 +#define PNO_TLV_RESERVED 0 +#define PNO_TLV_TYPE_SSID_IE 'S' +#define PNO_TLV_TYPE_TIME 'T' +#define PNO_EVENT_UP "PNO_EVENT" + +typedef struct cmd_tlv { + char prefix; + char version; + char subver; + char reserved; +} cmd_tlv_t; + +#if defined(CSCAN) + +typedef struct cscan_tlv { + char prefix; + char version; + char subver; + char reserved; +} cscan_tlv_t; + +#define CSCAN_COMMAND "CSCAN " +#define CSCAN_TLV_PREFIX 'S' +#define CSCAN_TLV_VERSION 1 +#define CSCAN_TLV_SUBVERSION 0 +#define CSCAN_TLV_TYPE_SSID_IE 'S' +#define CSCAN_TLV_TYPE_CHANNEL_IE 'C' +#define CSCAN_TLV_TYPE_NPROBE_IE 'N' +#define CSCAN_TLV_TYPE_ACTIVE_IE 'A' +#define CSCAN_TLV_TYPE_PASSIVE_IE 'P' +#define CSCAN_TLV_TYPE_HOME_IE 'H' +#define CSCAN_TLV_TYPE_STYPE_IE 'T' + +extern int wl_iw_parse_channel_list_tlv(char** list_str, uint16* channel_list, \ + int channel_num, int *bytes_left); + +extern int wl_iw_parse_data_tlv(char** list_str, void *dst, int dst_size, \ + const char token, int input_size, int *bytes_left); + +extern int wl_iw_parse_ssid_list_tlv(char** list_str, wlc_ssid_t* ssid, \ + int max, int *bytes_left); + +extern int wl_iw_parse_ssid_list(char** list_str, wlc_ssid_t* ssid, int idx, int max); + +extern int wl_iw_parse_channel_list(char** list_str, uint16* channel_list, int channel_num); + +#endif + +#endif |