From 765f3a9d95e48d6f522bdbc0236ddaa02f981cde Mon Sep 17 00:00:00 2001 From: tytung Date: Wed, 23 Feb 2011 01:21:11 +0800 Subject: [PATCH] wifi: updated WiFi driver to AOSP version to support WiFi with EAP authentication on Gingerbread. Need Gingerbread wpa_supplicant. --- arch/arm/mach-msm/board-htcleo-wifi.c | 33 +- drivers/net/wireless/Makefile | 2 +- drivers/net/wireless/bcm4329/Kconfig | 4 +- drivers/net/wireless/bcm4329/Makefile | 6 +- drivers/net/wireless/bcm4329/aiutils.c | 4 +- drivers/net/wireless/bcm4329/bcmpcispi.c | 13 +- drivers/net/wireless/bcm4329/bcmsdh.c | 2 - drivers/net/wireless/bcm4329/bcmsdh_linux.c | 96 +- drivers/net/wireless/bcm4329/bcmsdh_sdmmc.c | 9 +- .../net/wireless/bcm4329/bcmsdh_sdmmc_linux.c | 20 +- drivers/net/wireless/bcm4329/bcmsdspi_linux.c | 2 +- drivers/net/wireless/bcm4329/bcmsdstd.c | 21 +- drivers/net/wireless/bcm4329/bcmsdstd_linux.c | 8 +- drivers/net/wireless/bcm4329/bcmspibrcm.c | 1726 +++++++++++ drivers/net/wireless/bcm4329/bcmutils.c | 2 +- drivers/net/wireless/bcm4329/bcmwifi.c | 2 +- drivers/net/wireless/bcm4329/dhd.h | 87 +- drivers/net/wireless/bcm4329/dhd_bus.h | 8 +- drivers/net/wireless/bcm4329/dhd_cdc.c | 338 +-- drivers/net/wireless/bcm4329/dhd_common.c | 1483 +++++++++- .../net/wireless/bcm4329/dhd_custom_gpio.c | 61 +- drivers/net/wireless/bcm4329/dhd_dbg.h | 7 +- drivers/net/wireless/bcm4329/dhd_linux.c | 779 ++++- .../net/wireless/bcm4329/dhd_linux_sched.c | 2 +- drivers/net/wireless/bcm4329/dhd_proto.h | 10 +- drivers/net/wireless/bcm4329/dhd_sdio.c | 339 ++- drivers/net/wireless/bcm4329/dngl_stats.h | 2 +- drivers/net/wireless/bcm4329/hndpmu.c | 4 +- drivers/net/wireless/bcm4329/include/aidmp.h | 2 +- drivers/net/wireless/bcm4329/include/bcmcdc.h | 2 +- .../net/wireless/bcm4329/include/bcmdevs.h | 2 +- .../net/wireless/bcm4329/include/bcmendian.h | 2 +- .../net/wireless/bcm4329/include/bcmpcispi.h | 2 +- .../net/wireless/bcm4329/include/bcmperf.h | 2 +- .../net/wireless/bcm4329/include/bcmsdbus.h | 2 +- drivers/net/wireless/bcm4329/include/bcmsdh.h | 2 +- .../wireless/bcm4329/include/bcmsdh_sdmmc.h | 2 +- .../net/wireless/bcm4329/include/bcmsdpcm.h | 7 +- .../net/wireless/bcm4329/include/bcmsdspi.h | 2 +- drivers/net/wireless/bcm4329/include/bcmspi.h | 2 +- .../net/wireless/bcm4329/include/bcmspibrcm.h | 134 + .../net/wireless/bcm4329/include/bcmutils.h | 2 +- .../net/wireless/bcm4329/include/bcmwifi.h | 4 +- .../net/wireless/bcm4329/include/dhdioctl.h | 5 +- .../net/wireless/bcm4329/include/epivers.h | 12 +- drivers/net/wireless/bcm4329/include/hndpmu.h | 4 +- .../wireless/bcm4329/include/hndrte_armtrap.h | 8 +- .../wireless/bcm4329/include/hndrte_cons.h | 63 + drivers/net/wireless/bcm4329/include/hndsoc.h | 2 +- .../net/wireless/bcm4329/include/linux_osl.h | 5 +- .../net/wireless/bcm4329/include/linuxver.h | 7 +- .../net/wireless/bcm4329/include/miniopt.h | 2 +- .../net/wireless/bcm4329/include/msgtrace.h | 2 +- drivers/net/wireless/bcm4329/include/osl.h | 2 +- .../bcm4329/include/packed_section_end.h | 2 +- .../bcm4329/include/packed_section_start.h | 2 +- drivers/net/wireless/bcm4329/include/pcicfg.h | 2 +- .../wireless/bcm4329/include/proto/802.11.h | 3 + .../wireless/bcm4329/include/proto/802.11e.h | 2 +- .../wireless/bcm4329/include/proto/802.1d.h | 2 +- .../wireless/bcm4329/include/proto/bcmeth.h | 2 +- .../wireless/bcm4329/include/proto/bcmevent.h | 13 +- .../wireless/bcm4329/include/proto/bcmip.h | 2 +- .../wireless/bcm4329/include/proto/ethernet.h | 5 +- .../wireless/bcm4329/include/proto/sdspi.h | 2 +- .../net/wireless/bcm4329/include/proto/vlan.h | 2 +- .../net/wireless/bcm4329/include/proto/wpa.h | 2 +- .../net/wireless/bcm4329/include/sbconfig.h | 2 +- .../net/wireless/bcm4329/include/sbhnddma.h | 2 +- .../net/wireless/bcm4329/include/sbpcmcia.h | 2 +- drivers/net/wireless/bcm4329/include/sbsdio.h | 2 +- .../net/wireless/bcm4329/include/sbsdpcmdev.h | 2 +- .../net/wireless/bcm4329/include/sbsocram.h | 2 +- drivers/net/wireless/bcm4329/include/sdio.h | 2 +- .../net/wireless/bcm4329/include/siutils.h | 2 +- drivers/net/wireless/bcm4329/include/spid.h | 153 + drivers/net/wireless/bcm4329/include/trxhdr.h | 2 +- .../net/wireless/bcm4329/include/typedefs.h | 2 +- .../net/wireless/bcm4329/include/wlioctl.h | 20 +- drivers/net/wireless/bcm4329/linux_osl.c | 38 +- drivers/net/wireless/bcm4329/miniopt.c | 2 +- drivers/net/wireless/bcm4329/sbutils.c | 4 +- drivers/net/wireless/bcm4329/siutils.c | 2 +- drivers/net/wireless/bcm4329/siutils_priv.h | 2 +- drivers/net/wireless/bcm4329/wl_iw.c | 2573 ++++++++++++++--- drivers/net/wireless/bcm4329/wl_iw.h | 124 +- include/linux/netfilter/xt_CONNMARK.h | 29 +- include/linux/netfilter/xt_DSCP.h | 26 + include/linux/netfilter/xt_MARK.h | 10 + include/linux/netfilter/xt_RATEEST.h | 38 +- include/linux/netfilter/xt_TCPMSS.h | 13 +- include/linux/netfilter/xt_connmark.h | 20 + include/linux/netfilter/xt_dscp.h | 29 +- include/linux/netfilter/xt_mark.h | 9 +- include/linux/netfilter/xt_rateest.h | 37 + include/linux/netfilter/xt_tcpmss.h | 11 + include/linux/netfilter_ipv4/ipt_ECN.h | 32 +- include/linux/netfilter_ipv4/ipt_TTL.h | 14 +- include/linux/netfilter_ipv4/ipt_ecn.h | 33 + include/linux/netfilter_ipv4/ipt_ttl.h | 21 + include/linux/netfilter_ipv6/ip6t_HL.h | 14 +- include/linux/netfilter_ipv6/ip6t_hl.h | 22 + include/linux/string.h | 3 + include/linux/wlan_plat.h | 1 + net/ethernet/eth.c | 6 + net/ipv4/netfilter/ipt_ECN.c | 141 + net/ipv4/netfilter/ipt_ecn.c | 256 +- net/netfilter/Makefile | 6 +- net/netfilter/xt_CONNMARK.c | 113 + net/netfilter/xt_DSCP.c | 164 ++ net/netfilter/xt_HL.c | 171 ++ net/netfilter/xt_MARK.c | 56 + net/netfilter/xt_RATEEST.c | 183 ++ net/netfilter/xt_TCPMSS.c | 336 ++- net/netfilter/xt_connmark.c | 82 +- net/netfilter/xt_dscp.c | 147 +- net/netfilter/xt_hl.c | 4 +- net/netfilter/xt_mark.c | 45 +- net/netfilter/xt_rateest.c | 237 +- net/netfilter/xt_tcpmss.c | 110 + 120 files changed, 8937 insertions(+), 1812 deletions(-) create mode 100644 drivers/net/wireless/bcm4329/bcmspibrcm.c create mode 100644 drivers/net/wireless/bcm4329/include/bcmspibrcm.h create mode 100644 drivers/net/wireless/bcm4329/include/hndrte_cons.h create mode 100644 drivers/net/wireless/bcm4329/include/spid.h create mode 100644 include/linux/netfilter/xt_DSCP.h create mode 100644 include/linux/netfilter/xt_MARK.h create mode 100644 include/linux/netfilter/xt_connmark.h create mode 100644 include/linux/netfilter/xt_rateest.h create mode 100644 include/linux/netfilter/xt_tcpmss.h create mode 100644 include/linux/netfilter_ipv4/ipt_ecn.h create mode 100644 include/linux/netfilter_ipv4/ipt_ttl.h create mode 100644 include/linux/netfilter_ipv6/ip6t_hl.h create mode 100644 net/ipv4/netfilter/ipt_ECN.c create mode 100644 net/netfilter/xt_CONNMARK.c create mode 100644 net/netfilter/xt_DSCP.c create mode 100644 net/netfilter/xt_HL.c create mode 100644 net/netfilter/xt_MARK.c create mode 100644 net/netfilter/xt_RATEEST.c create mode 100644 net/netfilter/xt_tcpmss.c diff --git a/arch/arm/mach-msm/board-htcleo-wifi.c b/arch/arm/mach-msm/board-htcleo-wifi.c index bd856f50..8f2fb3f4 100644 --- a/arch/arm/mach-msm/board-htcleo-wifi.c +++ b/arch/arm/mach-msm/board-htcleo-wifi.c @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include "board-htcleo.h" @@ -87,7 +87,6 @@ static struct wifi_platform_data htcleo_wifi_control = { .set_reset = htcleo_wifi_reset, .set_carddetect = htcleo_wifi_set_carddetect, .mem_prealloc = htcleo_wifi_mem_prealloc, - .dot11n_enable = 1, }; static struct platform_device htcleo_wifi_device = { @@ -100,6 +99,36 @@ static struct platform_device htcleo_wifi_device = { }, }; +extern unsigned char *get_wifi_nvs_ram(void); +extern int wifi_calibration_size_set(void); + +static unsigned htcleo_wifi_update_nvs(char *str, int add_flag) +{ +#define NVS_LEN_OFFSET 0x0C +#define NVS_DATA_OFFSET 0x40 + unsigned char *ptr; + unsigned len; + + if (!str) + return -EINVAL; + ptr = get_wifi_nvs_ram(); + /* Size in format LE assumed */ + memcpy(&len, ptr + NVS_LEN_OFFSET, sizeof(len)); + /* if the last byte in NVRAM is 0, trim it */ + if (ptr[NVS_DATA_OFFSET + len - 1] == 0) + len -= 1; + if (add_flag) { + strcpy(ptr + NVS_DATA_OFFSET + len, str); + len += strlen(str); + } else { + if (strnstr(ptr + NVS_DATA_OFFSET, str, len)) + len -= strlen(str); + } + memcpy(ptr + NVS_LEN_OFFSET, &len, sizeof(len)); + wifi_calibration_size_set(); + return 0; +} + static int __init htcleo_wifi_init(void) { diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 380d6d13..6dbba408 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -63,4 +63,4 @@ obj-$(CONFIG_WL12XX) += wl12xx/ obj-$(CONFIG_IWM) += iwmc3200wifi/ -obj-$(CONFIG_BCM4329) += bcm4329_204/ +obj-$(CONFIG_BCM4329) += bcm4329/ diff --git a/drivers/net/wireless/bcm4329/Kconfig b/drivers/net/wireless/bcm4329/Kconfig index 260085f8..ca5760d3 100644 --- a/drivers/net/wireless/bcm4329/Kconfig +++ b/drivers/net/wireless/bcm4329/Kconfig @@ -1,6 +1,8 @@ config BCM4329 tristate "Broadcom 4329 wireless cards support" - depends on WIRELESS_EXT && MMC + depends on MMC + select WIRELESS_EXT + select WEXT_PRIV ---help--- This module adds support for wireless adapters based on Broadcom 4329 chipset. diff --git a/drivers/net/wireless/bcm4329/Makefile b/drivers/net/wireless/bcm4329/Makefile index 064124a1..3f49a643 100644 --- a/drivers/net/wireless/bcm4329/Makefile +++ b/drivers/net/wireless/bcm4329/Makefile @@ -4,8 +4,10 @@ DHDCFLAGS = -DLINUX -DBCMDRIVER -DBCMDONGLEHOST -DDHDTHREAD -DBCMWPA2 \ -DDHD_FIRSTREAD=64 -DDHD_GPL -DDHD_SCHED -DBDC -DTOE -DDHD_BCMEVENTS \ -DSHOW_EVENTS -DBCMSDIO -DDHD_GPL -DBCMLXSDMMC -DBCMPLATFORM_BUS \ -Wall -Wstrict-prototypes -Werror -DOOB_INTR_ONLY -DCUSTOMER_HW2 \ - -DDHD_USE_STATIC_BUF -DMMC_SDIO_ABORT -DSOFTAP \ - -DSET_RANDOM_MAC_SOFTAP \ + -DDHD_USE_STATIC_BUF -DMMC_SDIO_ABORT -DDHD_DEBUG_TRAP -DSOFTAP \ + -DEMBEDDED_PLATFORM -DARP_OFFLOAD_SUPPORT -DPKT_FILTER_SUPPORT \ + -DGET_CUSTOM_MAC_ENABLE -DSET_RANDOM_MAC_SOFTAP -DCSCAN -DHW_OOB \ + -DKEEP_ALIVE \ -Idrivers/net/wireless/bcm4329 -Idrivers/net/wireless/bcm4329/include DHDOFILES = dhd_linux.o linux_osl.o bcmutils.o dhd_common.o dhd_custom_gpio.o \ diff --git a/drivers/net/wireless/bcm4329/aiutils.c b/drivers/net/wireless/bcm4329/aiutils.c index 19627111..df48ac0d 100644 --- a/drivers/net/wireless/bcm4329/aiutils.c +++ b/drivers/net/wireless/bcm4329/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 diff --git a/drivers/net/wireless/bcm4329/bcmpcispi.c b/drivers/net/wireless/bcm4329/bcmpcispi.c index ba3e0c51..1a8b6717 100644 --- a/drivers/net/wireless/bcm4329/bcmpcispi.c +++ b/drivers/net/wireless/bcm4329/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 @@ -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/drivers/net/wireless/bcm4329/bcmsdh.c b/drivers/net/wireless/bcm4329/bcmsdh.c index aed6859e..4bf5889e 100644 --- a/drivers/net/wireless/bcm4329/bcmsdh.c +++ b/drivers/net/wireless/bcm4329/bcmsdh.c @@ -40,9 +40,7 @@ #include /* 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/drivers/net/wireless/bcm4329/bcmsdh_linux.c b/drivers/net/wireless/bcm4329/bcmsdh_linux.c index 0555f87f..3b7da426 100644 --- a/drivers/net/wireless/bcm4329/bcmsdh_linux.c +++ b/drivers/net/wireless/bcm4329/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), @@ -567,14 +582,31 @@ bcmsdh_unregister(void) } #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; } @@ -590,16 +622,23 @@ int bcmsdh_register_oob_intr(void * dhdp) SDLX_MSG(("%s Enter\n", __FUNCTION__)); +/* Example of HW_OOB for HW2: please refer to your host specifiction */ +/* sdhcinfo->oob_flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL | IORESOURCE_IRQ_SHAREABLE; */ + 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 (!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; - 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 +650,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/drivers/net/wireless/bcm4329/bcmsdh_sdmmc.c b/drivers/net/wireless/bcm4329/bcmsdh_sdmmc.c index 48d3f377..031367b8 100644 --- a/drivers/net/wireless/bcm4329/bcmsdh_sdmmc.c +++ b/drivers/net/wireless/bcm4329/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 @@ -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); @@ -675,6 +675,7 @@ sdioh_enable_hw_oob_intr(sdioh_info_t *sd, bool enable) data = 3; /* enable hw oob interrupt */ 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 +1065,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/drivers/net/wireless/bcm4329/bcmsdh_sdmmc_linux.c b/drivers/net/wireless/bcm4329/bcmsdh_sdmmc_linux.c index 196ad4fc..8992a426 100644 --- a/drivers/net/wireless/bcm4329/bcmsdh_sdmmc_linux.c +++ b/drivers/net/wireless/bcm4329/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 @@ -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 @@ -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/drivers/net/wireless/bcm4329/bcmsdspi_linux.c b/drivers/net/wireless/bcm4329/bcmsdspi_linux.c index 1046a17a..e2e0ca6a 100644 --- a/drivers/net/wireless/bcm4329/bcmsdspi_linux.c +++ b/drivers/net/wireless/bcm4329/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/drivers/net/wireless/bcm4329/bcmsdstd.c b/drivers/net/wireless/bcm4329/bcmsdstd.c index 0b1b575d..0ca1f8ff 100644 --- a/drivers/net/wireless/bcm4329/bcmsdstd.c +++ b/drivers/net/wireless/bcm4329/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 @@ -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/drivers/net/wireless/bcm4329/bcmsdstd_linux.c b/drivers/net/wireless/bcm4329/bcmsdstd_linux.c index 5746b084..a8b98e20 100644 --- a/drivers/net/wireless/bcm4329/bcmsdstd_linux.c +++ b/drivers/net/wireless/bcm4329/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 @@ -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/drivers/net/wireless/bcm4329/bcmspibrcm.c b/drivers/net/wireless/bcm4329/bcmspibrcm.c new file mode 100644 index 00000000..0f131a40 --- /dev/null +++ b/drivers/net/wireless/bcm4329/bcmspibrcm.c @@ -0,0 +1,1726 @@ +/* + * Broadcom BCMSDH to gSPI Protocol Conversion Layer + * + * Copyright (C) 2010, Broadcom Corporation + * All Rights Reserved. + * + * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Broadcom Corporation; + * the contents of this file may not be disclosed to third parties, copied + * or duplicated in any form, in whole or in part, without the prior + * written permission of Broadcom Corporation. + * + * $Id: bcmspibrcm.c,v 1.11.2.10.2.9.6.11 2009/05/21 13:21:57 Exp $ + */ + +#define HSMODE + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include /* bcmsdh to/from specific controller APIs */ +#include /* ioctl/iovars */ +#include + +#include + + +#include +#include + +#define F0_RESPONSE_DELAY 16 +#define F1_RESPONSE_DELAY 16 +#define F2_RESPONSE_DELAY F0_RESPONSE_DELAY + +#define CMDLEN 4 + +#define DWORDMODE_ON (sd->chip == BCM4329_CHIP_ID) && (sd->chiprev == 2) && (sd->dwordmode == TRUE) + +/* Globals */ +uint sd_msglevel = 0; + +uint sd_hiok = FALSE; /* Use hi-speed mode if available? */ +uint sd_sdmode = SDIOH_MODE_SPI; /* Use SD4 mode by default */ +uint sd_f2_blocksize = 64; /* Default blocksize */ + + +uint sd_divisor = 2; +uint sd_power = 1; /* Default to SD Slot powered ON */ +uint sd_clock = 1; /* Default to SD Clock turned ON */ +uint sd_crc = 0; /* Default to SPI CRC Check turned OFF */ + +uint8 spi_outbuf[SPI_MAX_PKT_LEN]; +uint8 spi_inbuf[SPI_MAX_PKT_LEN]; + +/* 128bytes buffer is enough to clear data-not-available and program response-delay F0 bits + * assuming we will not exceed F0 response delay > 100 bytes at 48MHz. + */ +#define BUF2_PKT_LEN 128 +uint8 spi_outbuf2[BUF2_PKT_LEN]; +uint8 spi_inbuf2[BUF2_PKT_LEN]; + +/* Prototypes */ +static bool bcmspi_test_card(sdioh_info_t *sd); +static bool bcmspi_host_device_init_adapt(sdioh_info_t *sd); +static int bcmspi_set_highspeed_mode(sdioh_info_t *sd, bool hsmode); +static int bcmspi_cmd_issue(sdioh_info_t *sd, bool use_dma, uint32 cmd_arg, + uint32 *data, uint32 datalen); +static int bcmspi_card_regread(sdioh_info_t *sd, int func, uint32 regaddr, + int regsize, uint32 *data); +static int bcmspi_card_regwrite(sdioh_info_t *sd, int func, uint32 regaddr, + int regsize, uint32 data); +static int bcmspi_card_bytewrite(sdioh_info_t *sd, int func, uint32 regaddr, + uint8 *data); +static int bcmspi_driver_init(sdioh_info_t *sd); +static int bcmspi_card_buf(sdioh_info_t *sd, int rw, int func, bool fifo, + uint32 addr, int nbytes, uint32 *data); +static int bcmspi_card_regread_fixedaddr(sdioh_info_t *sd, int func, uint32 regaddr, int regsize, + uint32 *data); +static void bcmspi_cmd_getdstatus(sdioh_info_t *sd, uint32 *dstatus_buffer); +static int bcmspi_update_stats(sdioh_info_t *sd, uint32 cmd_arg); + +/* + * Public entry points & extern's + */ +extern sdioh_info_t * +sdioh_attach(osl_t *osh, void *bar0, uint irq) +{ + sdioh_info_t *sd; + + sd_trace(("%s\n", __FUNCTION__)); + if ((sd = (sdioh_info_t *)MALLOC(osh, sizeof(sdioh_info_t))) == NULL) { + sd_err(("%s: out of memory, malloced %d bytes\n", __FUNCTION__, MALLOCED(osh))); + return NULL; + } + bzero((char *)sd, sizeof(sdioh_info_t)); + sd->osh = osh; + if (spi_osinit(sd) != 0) { + sd_err(("%s: spi_osinit() failed\n", __FUNCTION__)); + MFREE(sd->osh, sd, sizeof(sdioh_info_t)); + return NULL; + } + + sd->bar0 = bar0; + sd->irq = irq; + sd->intr_handler = NULL; + sd->intr_handler_arg = NULL; + sd->intr_handler_valid = FALSE; + + /* Set defaults */ + sd->use_client_ints = TRUE; + sd->sd_use_dma = FALSE; /* DMA Not supported */ + + /* Spi device default is 16bit mode, change to 4 when device is changed to 32bit + * mode + */ + sd->wordlen = 2; + + if (!spi_hw_attach(sd)) { + sd_err(("%s: spi_hw_attach() failed\n", __FUNCTION__)); + spi_osfree(sd); + MFREE(sd->osh, sd, sizeof(sdioh_info_t)); + return (NULL); + } + + if (bcmspi_driver_init(sd) != SUCCESS) { + sd_err(("%s: bcmspi_driver_init() failed()\n", __FUNCTION__)); + spi_hw_detach(sd); + spi_osfree(sd); + MFREE(sd->osh, sd, sizeof(sdioh_info_t)); + return (NULL); + } + + if (spi_register_irq(sd, irq) != SUCCESS) { + sd_err(("%s: spi_register_irq() failed for irq = %d\n", __FUNCTION__, irq)); + spi_hw_detach(sd); + spi_osfree(sd); + MFREE(sd->osh, sd, sizeof(sdioh_info_t)); + return (NULL); + } + + sd_trace(("%s: Done\n", __FUNCTION__)); + + return sd; +} + +extern SDIOH_API_RC +sdioh_detach(osl_t *osh, sdioh_info_t *sd) +{ + sd_trace(("%s\n", __FUNCTION__)); + if (sd) { + sd_err(("%s: detaching from hardware\n", __FUNCTION__)); + spi_free_irq(sd->irq, sd); + spi_hw_detach(sd); + spi_osfree(sd); + MFREE(sd->osh, sd, sizeof(sdioh_info_t)); + } + return SDIOH_API_RC_SUCCESS; +} + +/* Configure callback to client when we recieve client interrupt */ +extern SDIOH_API_RC +sdioh_interrupt_register(sdioh_info_t *sd, sdioh_cb_fn_t fn, void *argh) +{ + sd_trace(("%s: Entering\n", __FUNCTION__)); + sd->intr_handler = fn; + sd->intr_handler_arg = argh; + sd->intr_handler_valid = TRUE; + return SDIOH_API_RC_SUCCESS; +} + +extern SDIOH_API_RC +sdioh_interrupt_deregister(sdioh_info_t *sd) +{ + sd_trace(("%s: Entering\n", __FUNCTION__)); + sd->intr_handler_valid = FALSE; + sd->intr_handler = NULL; + sd->intr_handler_arg = NULL; + return SDIOH_API_RC_SUCCESS; +} + +extern SDIOH_API_RC +sdioh_interrupt_query(sdioh_info_t *sd, bool *onoff) +{ + sd_trace(("%s: Entering\n", __FUNCTION__)); + *onoff = sd->client_intr_enabled; + return SDIOH_API_RC_SUCCESS; +} + +#if defined(DHD_DEBUG) +extern bool +sdioh_interrupt_pending(sdioh_info_t *sd) +{ + return 0; +} +#endif + +extern SDIOH_API_RC +sdioh_query_device(sdioh_info_t *sd) +{ + /* Return a BRCM ID appropriate to the dongle class */ + return (sd->num_funcs > 1) ? BCM4329_D11NDUAL_ID : BCM4318_D11G_ID; +} + +/* Provide dstatus bits of spi-transaction for dhd layers. */ +extern uint32 +sdioh_get_dstatus(sdioh_info_t *sd) +{ + return sd->card_dstatus; +} + +extern void +sdioh_chipinfo(sdioh_info_t *sd, uint32 chip, uint32 chiprev) +{ + sd->chip = chip; + sd->chiprev = chiprev; +} + +extern void +sdioh_dwordmode(sdioh_info_t *sd, bool set) +{ + uint8 reg = 0; + int status; + + if ((status = sdioh_request_byte(sd, SDIOH_READ, SPI_FUNC_0, SPID_STATUS_ENABLE, ®)) != + SUCCESS) { + sd_err(("%s: Failed to set dwordmode in gSPI\n", __FUNCTION__)); + return; + } + + if (set) { + reg |= DWORD_PKT_LEN_EN; + sd->dwordmode = TRUE; + sd->client_block_size[SPI_FUNC_2] = 4096; /* h2spi's limit is 4KB, we support 8KB */ + } else { + reg &= ~DWORD_PKT_LEN_EN; + sd->dwordmode = FALSE; + sd->client_block_size[SPI_FUNC_2] = 2048; + } + + if ((status = sdioh_request_byte(sd, SDIOH_WRITE, SPI_FUNC_0, SPID_STATUS_ENABLE, ®)) != + SUCCESS) { + sd_err(("%s: Failed to set dwordmode in gSPI\n", __FUNCTION__)); + return; + } +} + + +uint +sdioh_query_iofnum(sdioh_info_t *sd) +{ + return sd->num_funcs; +} + +/* IOVar table */ +enum { + IOV_MSGLEVEL = 1, + IOV_BLOCKMODE, + IOV_BLOCKSIZE, + IOV_DMA, + IOV_USEINTS, + IOV_NUMINTS, + IOV_NUMLOCALINTS, + IOV_HOSTREG, + IOV_DEVREG, + IOV_DIVISOR, + IOV_SDMODE, + IOV_HISPEED, + IOV_HCIREGS, + IOV_POWER, + IOV_CLOCK, + IOV_SPIERRSTATS, + IOV_RESP_DELAY_ALL +}; + +const bcm_iovar_t sdioh_iovars[] = { + {"sd_msglevel", IOV_MSGLEVEL, 0, IOVT_UINT32, 0 }, + {"sd_blocksize", IOV_BLOCKSIZE, 0, IOVT_UINT32, 0 }, /* ((fn << 16) | size) */ + {"sd_dma", IOV_DMA, 0, IOVT_BOOL, 0 }, + {"sd_ints", IOV_USEINTS, 0, IOVT_BOOL, 0 }, + {"sd_numints", IOV_NUMINTS, 0, IOVT_UINT32, 0 }, + {"sd_numlocalints", IOV_NUMLOCALINTS, 0, IOVT_UINT32, 0 }, + {"sd_hostreg", IOV_HOSTREG, 0, IOVT_BUFFER, sizeof(sdreg_t) }, + {"sd_devreg", IOV_DEVREG, 0, IOVT_BUFFER, sizeof(sdreg_t) }, + {"sd_divisor", IOV_DIVISOR, 0, IOVT_UINT32, 0 }, + {"sd_power", IOV_POWER, 0, IOVT_UINT32, 0 }, + {"sd_clock", IOV_CLOCK, 0, IOVT_UINT32, 0 }, + {"sd_mode", IOV_SDMODE, 0, IOVT_UINT32, 100}, + {"sd_highspeed", IOV_HISPEED, 0, IOVT_UINT32, 0}, + {"spi_errstats", IOV_SPIERRSTATS, 0, IOVT_BUFFER, sizeof(struct spierrstats_t) }, + {"spi_respdelay", IOV_RESP_DELAY_ALL, 0, IOVT_BOOL, 0 }, + {NULL, 0, 0, 0, 0 } +}; + +int +sdioh_iovar_op(sdioh_info_t *si, const char *name, + void *params, int plen, void *arg, int len, bool set) +{ + const bcm_iovar_t *vi = NULL; + int bcmerror = 0; + int val_size; + int32 int_val = 0; + bool bool_val; + uint32 actionid; +/* + sdioh_regs_t *regs; +*/ + + ASSERT(name); + ASSERT(len >= 0); + + /* Get must have return space; Set does not take qualifiers */ + ASSERT(set || (arg && len)); + ASSERT(!set || (!params && !plen)); + + sd_trace(("%s: Enter (%s %s)\n", __FUNCTION__, (set ? "set" : "get"), name)); + + if ((vi = bcm_iovar_lookup(sdioh_iovars, name)) == NULL) { + bcmerror = BCME_UNSUPPORTED; + goto exit; + } + + if ((bcmerror = bcm_iovar_lencheck(vi, arg, len, set)) != 0) + goto exit; + + /* Set up params so get and set can share the convenience variables */ + if (params == NULL) { + params = arg; + plen = len; + } + + if (vi->type == IOVT_VOID) + val_size = 0; + else if (vi->type == IOVT_BUFFER) + val_size = len; + else + val_size = sizeof(int); + + if (plen >= (int)sizeof(int_val)) + bcopy(params, &int_val, sizeof(int_val)); + + bool_val = (int_val != 0) ? TRUE : FALSE; + + actionid = set ? IOV_SVAL(vi->varid) : IOV_GVAL(vi->varid); + switch (actionid) { + case IOV_GVAL(IOV_MSGLEVEL): + int_val = (int32)sd_msglevel; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_MSGLEVEL): + sd_msglevel = int_val; + break; + + case IOV_GVAL(IOV_BLOCKSIZE): + if ((uint32)int_val > si->num_funcs) { + bcmerror = BCME_BADARG; + break; + } + int_val = (int32)si->client_block_size[int_val]; + bcopy(&int_val, arg, val_size); + break; + + case IOV_GVAL(IOV_DMA): + int_val = (int32)si->sd_use_dma; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_DMA): + si->sd_use_dma = (bool)int_val; + break; + + case IOV_GVAL(IOV_USEINTS): + int_val = (int32)si->use_client_ints; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_USEINTS): + break; + + case IOV_GVAL(IOV_DIVISOR): + int_val = (uint32)sd_divisor; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_DIVISOR): + sd_divisor = int_val; + if (!spi_start_clock(si, (uint16)sd_divisor)) { + sd_err(("%s: set clock failed\n", __FUNCTION__)); + bcmerror = BCME_ERROR; + } + break; + + case IOV_GVAL(IOV_POWER): + int_val = (uint32)sd_power; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_POWER): + sd_power = int_val; + break; + + case IOV_GVAL(IOV_CLOCK): + int_val = (uint32)sd_clock; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_CLOCK): + sd_clock = int_val; + break; + + case IOV_GVAL(IOV_SDMODE): + int_val = (uint32)sd_sdmode; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_SDMODE): + sd_sdmode = int_val; + break; + + case IOV_GVAL(IOV_HISPEED): + int_val = (uint32)sd_hiok; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_HISPEED): + sd_hiok = int_val; + + if (!bcmspi_set_highspeed_mode(si, (bool)sd_hiok)) { + sd_err(("%s: Failed changing highspeed mode to %d.\n", + __FUNCTION__, sd_hiok)); + bcmerror = BCME_ERROR; + return ERROR; + } + break; + + case IOV_GVAL(IOV_NUMINTS): + int_val = (int32)si->intrcount; + bcopy(&int_val, arg, val_size); + break; + + case IOV_GVAL(IOV_NUMLOCALINTS): + int_val = (int32)si->local_intrcount; + bcopy(&int_val, arg, val_size); + break; + case IOV_GVAL(IOV_DEVREG): + { + sdreg_t *sd_ptr = (sdreg_t *)params; + uint8 data; + + if (sdioh_cfg_read(si, sd_ptr->func, sd_ptr->offset, &data)) { + bcmerror = BCME_SDIO_ERROR; + break; + } + + int_val = (int)data; + bcopy(&int_val, arg, sizeof(int_val)); + break; + } + + case IOV_SVAL(IOV_DEVREG): + { + sdreg_t *sd_ptr = (sdreg_t *)params; + uint8 data = (uint8)sd_ptr->value; + + if (sdioh_cfg_write(si, sd_ptr->func, sd_ptr->offset, &data)) { + bcmerror = BCME_SDIO_ERROR; + break; + } + break; + } + + + case IOV_GVAL(IOV_SPIERRSTATS): + { + bcopy(&si->spierrstats, arg, sizeof(struct spierrstats_t)); + break; + } + + case IOV_SVAL(IOV_SPIERRSTATS): + { + bzero(&si->spierrstats, sizeof(struct spierrstats_t)); + break; + } + + case IOV_GVAL(IOV_RESP_DELAY_ALL): + int_val = (int32)si->resp_delay_all; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_RESP_DELAY_ALL): + si->resp_delay_all = (bool)int_val; + int_val = STATUS_ENABLE|INTR_WITH_STATUS; + if (si->resp_delay_all) + int_val |= RESP_DELAY_ALL; + else { + if (bcmspi_card_regwrite(si, SPI_FUNC_0, SPID_RESPONSE_DELAY, 1, + F1_RESPONSE_DELAY) != SUCCESS) { + sd_err(("%s: Unable to set response delay.\n", __FUNCTION__)); + bcmerror = BCME_SDIO_ERROR; + break; + } + } + + if (bcmspi_card_regwrite(si, SPI_FUNC_0, SPID_STATUS_ENABLE, 1, int_val) + != SUCCESS) { + sd_err(("%s: Unable to set response delay.\n", __FUNCTION__)); + bcmerror = BCME_SDIO_ERROR; + break; + } + break; + + default: + bcmerror = BCME_UNSUPPORTED; + break; + } +exit: + + return bcmerror; +} + +extern SDIOH_API_RC +sdioh_cfg_read(sdioh_info_t *sd, uint fnc_num, uint32 addr, uint8 *data) +{ + SDIOH_API_RC status; + /* No lock needed since sdioh_request_byte does locking */ + status = sdioh_request_byte(sd, SDIOH_READ, fnc_num, addr, data); + return status; +} + +extern SDIOH_API_RC +sdioh_cfg_write(sdioh_info_t *sd, uint fnc_num, uint32 addr, uint8 *data) +{ + /* No lock needed since sdioh_request_byte does locking */ + SDIOH_API_RC status; + + if ((fnc_num == SPI_FUNC_1) && (addr == SBSDIO_FUNC1_FRAMECTRL)) { + uint8 dummy_data; + status = sdioh_cfg_read(sd, fnc_num, addr, &dummy_data); + if (status) { + sd_err(("sdioh_cfg_read() failed.\n")); + return status; + } + } + + status = sdioh_request_byte(sd, SDIOH_WRITE, fnc_num, addr, data); + return status; +} + +extern SDIOH_API_RC +sdioh_cis_read(sdioh_info_t *sd, uint func, uint8 *cisd, uint32 length) +{ + uint32 count; + int offset; + uint32 cis_byte; + uint16 *cis = (uint16 *)cisd; + uint bar0 = SI_ENUM_BASE; + int status; + uint8 data; + + sd_trace(("%s: Func %d\n", __FUNCTION__, func)); + + spi_lock(sd); + + /* Set sb window address to 0x18000000 */ + data = (bar0 >> 8) & SBSDIO_SBADDRLOW_MASK; + status = bcmspi_card_bytewrite(sd, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRLOW, &data); + if (status == SUCCESS) { + data = (bar0 >> 16) & SBSDIO_SBADDRMID_MASK; + status = bcmspi_card_bytewrite(sd, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRMID, &data); + } else { + sd_err(("%s: Unable to set sb-addr-windows\n", __FUNCTION__)); + spi_unlock(sd); + return (BCME_ERROR); + } + if (status == SUCCESS) { + data = (bar0 >> 24) & SBSDIO_SBADDRHIGH_MASK; + status = bcmspi_card_bytewrite(sd, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRHIGH, &data); + } else { + sd_err(("%s: Unable to set sb-addr-windows\n", __FUNCTION__)); + spi_unlock(sd); + return (BCME_ERROR); + } + + offset = CC_OTP; /* OTP offset in chipcommon. */ + for (count = 0; count < length/2; count++) { + if (bcmspi_card_regread (sd, SDIO_FUNC_1, offset, 2, &cis_byte) < 0) { + sd_err(("%s: regread failed: Can't read CIS\n", __FUNCTION__)); + spi_unlock(sd); + return (BCME_ERROR); + } + + *cis = (uint16)cis_byte; + cis++; + offset += 2; + } + + spi_unlock(sd); + + return (BCME_OK); +} + +extern SDIOH_API_RC +sdioh_request_byte(sdioh_info_t *sd, uint rw, uint func, uint regaddr, uint8 *byte) +{ + int status; + uint32 cmd_arg; + uint32 dstatus; + uint32 data = (uint32)(*byte); + + spi_lock(sd); + + cmd_arg = 0; + cmd_arg = SFIELD(cmd_arg, SPI_FUNCTION, func); + cmd_arg = SFIELD(cmd_arg, SPI_ACCESS, 1); /* Incremental access */ + cmd_arg = SFIELD(cmd_arg, SPI_REG_ADDR, regaddr); + cmd_arg = SFIELD(cmd_arg, SPI_RW_FLAG, rw == SDIOH_READ ? 0 : 1); + cmd_arg = SFIELD(cmd_arg, SPI_LEN, 1); + + sd_trace(("%s cmd_arg = 0x%x\n", __FUNCTION__, cmd_arg)); + sd_trace(("%s: rw=%d, func=%d, regaddr=0x%08x, data=0x%x\n", __FUNCTION__, rw, func, + regaddr, data)); + + if ((status = bcmspi_cmd_issue(sd, sd->sd_use_dma, + cmd_arg, &data, 1)) != SUCCESS) { + spi_unlock(sd); + return status; + } + + if (rw == SDIOH_READ) + *byte = (uint8)data; + + bcmspi_cmd_getdstatus(sd, &dstatus); + if (dstatus) + sd_trace(("dstatus =0x%x\n", dstatus)); + + spi_unlock(sd); + return SDIOH_API_RC_SUCCESS; +} + +extern SDIOH_API_RC +sdioh_request_word(sdioh_info_t *sd, uint cmd_type, uint rw, uint func, uint addr, + uint32 *word, uint nbytes) +{ + int status; + + spi_lock(sd); + + if (rw == SDIOH_READ) + status = bcmspi_card_regread(sd, func, addr, nbytes, word); + else + status = bcmspi_card_regwrite(sd, func, addr, nbytes, *word); + + spi_unlock(sd); + return (status == SUCCESS ? SDIOH_API_RC_SUCCESS : SDIOH_API_RC_FAIL); +} + +extern SDIOH_API_RC +sdioh_request_buffer(sdioh_info_t *sd, uint pio_dma, uint fix_inc, uint rw, uint func, + uint addr, uint reg_width, uint buflen_u, uint8 *buffer, void *pkt) +{ + int len; + int buflen = (int)buflen_u; + bool fifo = (fix_inc == SDIOH_DATA_FIX); + + spi_lock(sd); + + ASSERT(reg_width == 4); + ASSERT(buflen_u < (1 << 30)); + ASSERT(sd->client_block_size[func]); + + sd_data(("%s: %c len %d r_cnt %d t_cnt %d, pkt @0x%p\n", + __FUNCTION__, rw == SDIOH_READ ? 'R' : 'W', + buflen_u, sd->r_cnt, sd->t_cnt, pkt)); + + /* Break buffer down into blocksize chunks. */ + while (buflen > 0) { + len = MIN(sd->client_block_size[func], buflen); + if (bcmspi_card_buf(sd, rw, func, fifo, addr, len, (uint32 *)buffer) != SUCCESS) { + sd_err(("%s: bcmspi_card_buf %s failed\n", + __FUNCTION__, rw == SDIOH_READ ? "Read" : "Write")); + spi_unlock(sd); + return SDIOH_API_RC_FAIL; + } + buffer += len; + buflen -= len; + if (!fifo) + addr += len; + } + spi_unlock(sd); + return SDIOH_API_RC_SUCCESS; +} + +/* This function allows write to gspi bus when another rd/wr function is deep down the call stack. + * Its main aim is to have simpler spi writes rather than recursive writes. + * e.g. When there is a need to program response delay on the fly after detecting the SPI-func + * this call will allow to program the response delay. + */ +static int +bcmspi_card_byterewrite(sdioh_info_t *sd, int func, uint32 regaddr, uint8 byte) +{ + uint32 cmd_arg; + uint32 datalen = 1; + uint32 hostlen; + + cmd_arg = 0; + + cmd_arg = SFIELD(cmd_arg, SPI_RW_FLAG, 1); + cmd_arg = SFIELD(cmd_arg, SPI_ACCESS, 1); /* Incremental access */ + cmd_arg = SFIELD(cmd_arg, SPI_FUNCTION, func); + cmd_arg = SFIELD(cmd_arg, SPI_REG_ADDR, regaddr); + cmd_arg = SFIELD(cmd_arg, SPI_LEN, datalen); + + sd_trace(("%s cmd_arg = 0x%x\n", __FUNCTION__, cmd_arg)); + + + /* Set up and issue the SPI command. MSByte goes out on bus first. Increase datalen + * according to the wordlen mode(16/32bit) the device is in. + */ + ASSERT(sd->wordlen == 4 || sd->wordlen == 2); + datalen = ROUNDUP(datalen, sd->wordlen); + + /* Start by copying command in the spi-outbuffer */ + if (sd->wordlen == 4) { /* 32bit spid */ + *(uint32 *)spi_outbuf2 = bcmswap32(cmd_arg); + if (datalen & 0x3) + datalen += (4 - (datalen & 0x3)); + } else if (sd->wordlen == 2) { /* 16bit spid */ + *(uint16 *)spi_outbuf2 = bcmswap16(cmd_arg & 0xffff); + *(uint16 *)&spi_outbuf2[2] = bcmswap16((cmd_arg & 0xffff0000) >> 16); + if (datalen & 0x1) + datalen++; + } else { + sd_err(("%s: Host is %d bit spid, could not create SPI command.\n", + __FUNCTION__, 8 * sd->wordlen)); + return ERROR; + } + + /* for Write, put the data into the output buffer */ + if (datalen != 0) { + if (sd->wordlen == 4) { /* 32bit spid */ + *(uint32 *)&spi_outbuf2[CMDLEN] = bcmswap32(byte); + } else if (sd->wordlen == 2) { /* 16bit spid */ + *(uint16 *)&spi_outbuf2[CMDLEN] = bcmswap16(byte & 0xffff); + *(uint16 *)&spi_outbuf2[CMDLEN + 2] = + bcmswap16((byte & 0xffff0000) >> 16); + } + } + + /* +4 for cmd, +4 for dstatus */ + hostlen = datalen + 8; + hostlen += (4 - (hostlen & 0x3)); + spi_sendrecv(sd, spi_outbuf2, spi_inbuf2, hostlen); + + /* Last 4bytes are dstatus. Device is configured to return status bits. */ + if (sd->wordlen == 4) { /* 32bit spid */ + sd->card_dstatus = bcmswap32(*(uint32 *)&spi_inbuf2[datalen + CMDLEN ]); + } else if (sd->wordlen == 2) { /* 16bit spid */ + sd->card_dstatus = (bcmswap16(*(uint16 *)&spi_inbuf2[datalen + CMDLEN ]) | + (bcmswap16(*(uint16 *)&spi_inbuf2[datalen + CMDLEN + 2]) << 16)); + } else { + sd_err(("%s: Host is %d bit machine, could not read SPI dstatus.\n", + __FUNCTION__, 8 * sd->wordlen)); + return ERROR; + } + + if (sd->card_dstatus) + sd_trace(("dstatus after byte rewrite = 0x%x\n", sd->card_dstatus)); + + return (BCME_OK); +} + +/* Program the response delay corresponding to the spi function */ +static int +bcmspi_prog_resp_delay(sdioh_info_t *sd, int func, uint8 resp_delay) +{ + if (sd->resp_delay_all == FALSE) + return (BCME_OK); + + if (sd->prev_fun == func) + return (BCME_OK); + + if (F0_RESPONSE_DELAY == F1_RESPONSE_DELAY) + return (BCME_OK); + + bcmspi_card_byterewrite(sd, SPI_FUNC_0, SPID_RESPONSE_DELAY, resp_delay); + + /* Remember function for which to avoid reprogramming resp-delay in next iteration */ + sd->prev_fun = func; + + return (BCME_OK); + +} + +#define GSPI_RESYNC_PATTERN 0x0 + +/* A resync pattern is a 32bit MOSI line with all zeros. Its a special command in gSPI. + * It resets the spi-bkplane logic so that all F1 related ping-pong buffer logic is + * synchronised and all queued resuests are cancelled. + */ +static int +bcmspi_resync_f1(sdioh_info_t *sd) +{ + uint32 cmd_arg = GSPI_RESYNC_PATTERN, data = 0, datalen = 0; + + + /* Set up and issue the SPI command. MSByte goes out on bus first. Increase datalen + * according to the wordlen mode(16/32bit) the device is in. + */ + ASSERT(sd->wordlen == 4 || sd->wordlen == 2); + datalen = ROUNDUP(datalen, sd->wordlen); + + /* Start by copying command in the spi-outbuffer */ + *(uint32 *)spi_outbuf2 = cmd_arg; + + /* for Write, put the data into the output buffer */ + *(uint32 *)&spi_outbuf2[CMDLEN] = data; + + /* +4 for cmd, +4 for dstatus */ + spi_sendrecv(sd, spi_outbuf2, spi_inbuf2, datalen + 8); + + /* Last 4bytes are dstatus. Device is configured to return status bits. */ + if (sd->wordlen == 4) { /* 32bit spid */ + sd->card_dstatus = bcmswap32(*(uint32 *)&spi_inbuf2[datalen + CMDLEN ]); + } else if (sd->wordlen == 2) { /* 16bit spid */ + sd->card_dstatus = (bcmswap16(*(uint16 *)&spi_inbuf2[datalen + CMDLEN ]) | + (bcmswap16(*(uint16 *)&spi_inbuf2[datalen + CMDLEN + 2]) << 16)); + } else { + sd_err(("%s: Host is %d bit machine, could not read SPI dstatus.\n", + __FUNCTION__, 8 * sd->wordlen)); + return ERROR; + } + + if (sd->card_dstatus) + sd_trace(("dstatus after resync pattern write = 0x%x\n", sd->card_dstatus)); + + return (BCME_OK); +} + +uint32 dstatus_count = 0; + +static int +bcmspi_update_stats(sdioh_info_t *sd, uint32 cmd_arg) +{ + uint32 dstatus = sd->card_dstatus; + struct spierrstats_t *spierrstats = &sd->spierrstats; + int err = SUCCESS; + + sd_trace(("cmd = 0x%x, dstatus = 0x%x\n", cmd_arg, dstatus)); + + /* Store dstatus of last few gSPI transactions */ + spierrstats->dstatus[dstatus_count % NUM_PREV_TRANSACTIONS] = dstatus; + spierrstats->spicmd[dstatus_count % NUM_PREV_TRANSACTIONS] = cmd_arg; + dstatus_count++; + + if (sd->card_init_done == FALSE) + return err; + + if (dstatus & STATUS_DATA_NOT_AVAILABLE) { + spierrstats->dna++; + sd_trace(("Read data not available on F1 addr = 0x%x\n", + GFIELD(cmd_arg, SPI_REG_ADDR))); + /* Clear dna bit */ + bcmspi_card_byterewrite(sd, SPI_FUNC_0, SPID_INTR_REG, DATA_UNAVAILABLE); + } + + if (dstatus & STATUS_UNDERFLOW) { + spierrstats->rdunderflow++; + sd_err(("FIFO underflow happened due to current F2 read command.\n")); + } + + if (dstatus & STATUS_OVERFLOW) { + spierrstats->wroverflow++; + sd_err(("FIFO overflow happened due to current (F1/F2) write command.\n")); + if ((sd->chip == BCM4329_CHIP_ID) && (sd->chiprev == 0)) { + bcmspi_card_byterewrite(sd, SPI_FUNC_0, SPID_INTR_REG, F1_OVERFLOW); + bcmspi_resync_f1(sd); + sd_err(("Recovering from F1 FIFO overflow.\n")); + } else { + err = ERROR_OF; + } + } + + if (dstatus & STATUS_F2_INTR) { + spierrstats->f2interrupt++; + sd_trace(("Interrupt from F2. SW should clear corresponding IntStatus bits\n")); + } + + if (dstatus & STATUS_F3_INTR) { + spierrstats->f3interrupt++; + sd_err(("Interrupt from F3. SW should clear corresponding IntStatus bits\n")); + } + + if (dstatus & STATUS_HOST_CMD_DATA_ERR) { + spierrstats->hostcmddataerr++; + sd_err(("Error in CMD or Host data, detected by CRC/Checksum (optional)\n")); + } + + if (dstatus & STATUS_F2_PKT_AVAILABLE) { + spierrstats->f2pktavailable++; + sd_trace(("Packet is available/ready in F2 TX FIFO\n")); + sd_trace(("Packet length = %d\n", sd->dwordmode ? + ((dstatus & STATUS_F2_PKT_LEN_MASK) >> (STATUS_F2_PKT_LEN_SHIFT - 2)) : + ((dstatus & STATUS_F2_PKT_LEN_MASK) >> STATUS_F2_PKT_LEN_SHIFT))); + } + + if (dstatus & STATUS_F3_PKT_AVAILABLE) { + spierrstats->f3pktavailable++; + sd_err(("Packet is available/ready in F3 TX FIFO\n")); + sd_err(("Packet length = %d\n", + (dstatus & STATUS_F3_PKT_LEN_MASK) >> STATUS_F3_PKT_LEN_SHIFT)); + } + + return err; +} + +extern int +sdioh_abort(sdioh_info_t *sd, uint func) +{ + return 0; +} + +int +sdioh_start(sdioh_info_t *sd, int stage) +{ + return SUCCESS; +} + +int +sdioh_stop(sdioh_info_t *sd) +{ + return SUCCESS; +} + + + +/* + * Private/Static work routines + */ +static int +bcmspi_host_init(sdioh_info_t *sd) +{ + + /* Default power on mode */ + sd->sd_mode = SDIOH_MODE_SPI; + sd->polled_mode = TRUE; + sd->host_init_done = TRUE; + sd->card_init_done = FALSE; + sd->adapter_slot = 1; + + return (SUCCESS); +} + +static int +get_client_blocksize(sdioh_info_t *sd) +{ + uint32 regdata[2]; + int status; + + /* Find F1/F2/F3 max packet size */ + if ((status = bcmspi_card_regread(sd, 0, SPID_F1_INFO_REG, + 8, regdata)) != SUCCESS) { + return status; + } + + sd_trace(("pkt_size regdata[0] = 0x%x, regdata[1] = 0x%x\n", + regdata[0], regdata[1])); + + sd->client_block_size[1] = (regdata[0] & F1_MAX_PKT_SIZE) >> 2; + sd_trace(("Func1 blocksize = %d\n", sd->client_block_size[1])); + ASSERT(sd->client_block_size[1] == BLOCK_SIZE_F1); + + sd->client_block_size[2] = ((regdata[0] >> 16) & F2_MAX_PKT_SIZE) >> 2; + sd_trace(("Func2 blocksize = %d\n", sd->client_block_size[2])); + ASSERT(sd->client_block_size[2] == BLOCK_SIZE_F2); + + sd->client_block_size[3] = (regdata[1] & F3_MAX_PKT_SIZE) >> 2; + sd_trace(("Func3 blocksize = %d\n", sd->client_block_size[3])); + ASSERT(sd->client_block_size[3] == BLOCK_SIZE_F3); + + return 0; +} + +static int +bcmspi_client_init(sdioh_info_t *sd) +{ + uint32 status_en_reg = 0; + sd_trace(("%s: Powering up slot %d\n", __FUNCTION__, sd->adapter_slot)); + +#ifdef HSMODE + if (!spi_start_clock(sd, (uint16)sd_divisor)) { + sd_err(("spi_start_clock failed\n")); + return ERROR; + } +#else + /* Start at ~400KHz clock rate for initialization */ + if (!spi_start_clock(sd, 128)) { + sd_err(("spi_start_clock failed\n")); + return ERROR; + } +#endif /* HSMODE */ + + if (!bcmspi_host_device_init_adapt(sd)) { + sd_err(("bcmspi_host_device_init_adapt failed\n")); + return ERROR; + } + + if (!bcmspi_test_card(sd)) { + sd_err(("bcmspi_test_card failed\n")); + return ERROR; + } + + sd->num_funcs = SPI_MAX_IOFUNCS; + + get_client_blocksize(sd); + + /* Apply resync pattern cmd with all zeros to reset spi-bkplane F1 logic */ + bcmspi_resync_f1(sd); + + sd->dwordmode = FALSE; + + bcmspi_card_regread(sd, 0, SPID_STATUS_ENABLE, 1, &status_en_reg); + + sd_trace(("%s: Enabling interrupt with dstatus \n", __FUNCTION__)); + status_en_reg |= INTR_WITH_STATUS; + + + if (bcmspi_card_regwrite(sd, SPI_FUNC_0, SPID_STATUS_ENABLE, 1, + status_en_reg & 0xff) != SUCCESS) { + sd_err(("%s: Unable to set response delay for all fun's.\n", __FUNCTION__)); + return ERROR; + } + + +#ifndef HSMODE + /* After configuring for High-Speed mode, set the desired clock rate. */ + if (!spi_start_clock(sd, 4)) { + sd_err(("spi_start_clock failed\n")); + return ERROR; + } +#endif /* HSMODE */ + + sd->card_init_done = TRUE; + + + return SUCCESS; +} + +static int +bcmspi_set_highspeed_mode(sdioh_info_t *sd, bool hsmode) +{ + uint32 regdata; + int status; + + if ((status = bcmspi_card_regread(sd, 0, SPID_CONFIG, + 4, ®data)) != SUCCESS) + return status; + + sd_trace(("In %s spih-ctrl = 0x%x \n", __FUNCTION__, regdata)); + + + if (hsmode == TRUE) { + sd_trace(("Attempting to enable High-Speed mode.\n")); + + if (regdata & HIGH_SPEED_MODE) { + sd_trace(("Device is already in High-Speed mode.\n")); + return status; + } else { + regdata |= HIGH_SPEED_MODE; + sd_trace(("Writing %08x to device at %08x\n", regdata, SPID_CONFIG)); + if ((status = bcmspi_card_regwrite(sd, 0, SPID_CONFIG, + 4, regdata)) != SUCCESS) { + return status; + } + } + } else { + sd_trace(("Attempting to disable High-Speed mode.\n")); + + if (regdata & HIGH_SPEED_MODE) { + regdata &= ~HIGH_SPEED_MODE; + sd_trace(("Writing %08x to device at %08x\n", regdata, SPID_CONFIG)); + if ((status = bcmspi_card_regwrite(sd, 0, SPID_CONFIG, + 4, regdata)) != SUCCESS) + return status; + } + else { + sd_trace(("Device is already in Low-Speed mode.\n")); + return status; + } + } + + spi_controller_highspeed_mode(sd, hsmode); + + return TRUE; +} + +#define bcmspi_find_curr_mode(sd) { \ + sd->wordlen = 2; \ + status = bcmspi_card_regread_fixedaddr(sd, 0, SPID_TEST_READ, 4, ®data); \ + regdata &= 0xff; \ + if ((regdata == 0xad) || (regdata == 0x5b) || \ + (regdata == 0x5d) || (regdata == 0x5a)) \ + break; \ + sd->wordlen = 4; \ + status = bcmspi_card_regread_fixedaddr(sd, 0, SPID_TEST_READ, 4, ®data); \ + regdata &= 0xff; \ + if ((regdata == 0xad) || (regdata == 0x5b) || \ + (regdata == 0x5d) || (regdata == 0x5a)) \ + break; \ + sd_trace(("Silicon testability issue: regdata = 0x%x." \ + " Expected 0xad, 0x5a, 0x5b or 0x5d.\n", regdata)); \ + OSL_DELAY(100000); \ +} + +#define INIT_ADAPT_LOOP 100 + +/* Adapt clock-phase-speed-bitwidth between host and device */ +static bool +bcmspi_host_device_init_adapt(sdioh_info_t *sd) +{ + uint32 wrregdata, regdata = 0; + int status; + int i; + + /* Due to a silicon testability issue, the first command from the Host + * to the device will get corrupted (first bit will be lost). So the + * Host should poll the device with a safe read request. ie: The Host + * should try to read F0 addr 0x14 using the Fixed address mode + * (This will prevent a unintended write command to be detected by device) + */ + for (i = 0; i < INIT_ADAPT_LOOP; i++) { + /* If device was not power-cycled it will stay in 32bit mode with + * response-delay-all bit set. Alternate the iteration so that + * read either with or without response-delay for F0 to succeed. + */ + bcmspi_find_curr_mode(sd); + sd->resp_delay_all = (i & 0x1) ? TRUE : FALSE; + + bcmspi_find_curr_mode(sd); + sd->dwordmode = TRUE; + + bcmspi_find_curr_mode(sd); + sd->dwordmode = FALSE; + } + + /* Bail out, device not detected */ + if (i == INIT_ADAPT_LOOP) + return FALSE; + + /* Softreset the spid logic */ + if ((sd->dwordmode) || (sd->wordlen == 4)) { + bcmspi_card_regwrite(sd, 0, SPID_RESET_BP, 1, RESET_ON_WLAN_BP_RESET|RESET_SPI); + bcmspi_card_regread(sd, 0, SPID_RESET_BP, 1, ®data); + sd_trace(("reset reg read = 0x%x\n", regdata)); + sd_trace(("dwordmode = %d, wordlen = %d, resp_delay_all = %d\n", sd->dwordmode, + sd->wordlen, sd->resp_delay_all)); + /* Restore default state after softreset */ + sd->wordlen = 2; + sd->dwordmode = FALSE; + } + + if (sd->wordlen == 4) { + if ((status = bcmspi_card_regread(sd, 0, SPID_TEST_READ, 4, ®data)) != + SUCCESS) + return FALSE; + if (regdata == TEST_RO_DATA_32BIT_LE) { + sd_trace(("Spid is already in 32bit LE mode. Value read = 0x%x\n", + regdata)); + sd_trace(("Spid power was left on.\n")); + } else { + sd_err(("Spid power was left on but signature read failed." + " Value read = 0x%x\n", regdata)); + return FALSE; + } + } else { + sd->wordlen = 2; + +#define CTRL_REG_DEFAULT 0x00010430 /* according to the host m/c */ + + wrregdata = (CTRL_REG_DEFAULT); + sd->resp_delay_all = TRUE; + if (sd->resp_delay_all == TRUE) { + /* Enable response delay for all */ + wrregdata |= (RESP_DELAY_ALL << 16); + /* Program response delay value */ + wrregdata &= 0xffff00ff; + wrregdata |= (F1_RESPONSE_DELAY << 8); + sd->prev_fun = SPI_FUNC_1; + bcmspi_card_regwrite(sd, 0, SPID_CONFIG, 4, wrregdata); + } + + if ((status = bcmspi_card_regread(sd, 0, SPID_TEST_READ, 4, ®data)) != SUCCESS) + return FALSE; + sd_trace(("(we are still in 16bit mode) 32bit READ LE regdata = 0x%x\n", regdata)); + +#ifndef HSMODE + wrregdata |= (CLOCK_PHASE | CLOCK_POLARITY); + wrregdata &= ~HIGH_SPEED_MODE; + bcmspi_card_regwrite(sd, 0, SPID_CONFIG, 4, wrregdata); +#endif /* HSMODE */ + + for (i = 0; i < INIT_ADAPT_LOOP; i++) { + if ((regdata == 0xfdda7d5b) || (regdata == 0xfdda7d5a)) { + sd_trace(("0xfeedbead was leftshifted by 1-bit.\n")); + if ((status = bcmspi_card_regread(sd, 0, SPID_TEST_READ, 4, + ®data)) != SUCCESS) + return FALSE; + } + OSL_DELAY(1000); + } + + + /* Change to host controller intr-polarity of active-low */ + wrregdata &= ~INTR_POLARITY; + sd_trace(("(we are still in 16bit mode) 32bit Write LE reg-ctrl-data = 0x%x\n", + wrregdata)); + /* Change to 32bit mode */ + wrregdata |= WORD_LENGTH_32; + bcmspi_card_regwrite(sd, 0, SPID_CONFIG, 4, wrregdata); + + /* Change command/data packaging in 32bit LE mode */ + sd->wordlen = 4; + + if ((status = bcmspi_card_regread(sd, 0, SPID_TEST_READ, 4, ®data)) != SUCCESS) + return FALSE; + + if (regdata == TEST_RO_DATA_32BIT_LE) { + sd_trace(("Read spid passed. Value read = 0x%x\n", regdata)); + sd_trace(("Spid had power-on cycle OR spi was soft-resetted \n")); + } else { + sd_err(("Stale spid reg values read as it was kept powered. Value read =" + "0x%x\n", regdata)); + return FALSE; + } + } + + + return TRUE; +} + +static bool +bcmspi_test_card(sdioh_info_t *sd) +{ + uint32 regdata; + int status; + + if ((status = bcmspi_card_regread(sd, 0, SPID_TEST_READ, 4, ®data)) != SUCCESS) + return FALSE; + + if (regdata == (TEST_RO_DATA_32BIT_LE)) + sd_trace(("32bit LE regdata = 0x%x\n", regdata)); + else { + sd_trace(("Incorrect 32bit LE regdata = 0x%x\n", regdata)); + return FALSE; + } + + +#define RW_PATTERN1 0xA0A1A2A3 +#define RW_PATTERN2 0x4B5B6B7B + + regdata = RW_PATTERN1; + if ((status = bcmspi_card_regwrite(sd, 0, SPID_TEST_RW, 4, regdata)) != SUCCESS) + return FALSE; + regdata = 0; + if ((status = bcmspi_card_regread(sd, 0, SPID_TEST_RW, 4, ®data)) != SUCCESS) + return FALSE; + if (regdata != RW_PATTERN1) { + sd_err(("Write-Read spid failed. Value wrote = 0x%x, Value read = 0x%x\n", + RW_PATTERN1, regdata)); + return FALSE; + } else + sd_trace(("R/W spid passed. Value read = 0x%x\n", regdata)); + + regdata = RW_PATTERN2; + if ((status = bcmspi_card_regwrite(sd, 0, SPID_TEST_RW, 4, regdata)) != SUCCESS) + return FALSE; + regdata = 0; + if ((status = bcmspi_card_regread(sd, 0, SPID_TEST_RW, 4, ®data)) != SUCCESS) + return FALSE; + if (regdata != RW_PATTERN2) { + sd_err(("Write-Read spid failed. Value wrote = 0x%x, Value read = 0x%x\n", + RW_PATTERN2, regdata)); + return FALSE; + } else + sd_trace(("R/W spid passed. Value read = 0x%x\n", regdata)); + + return TRUE; +} + +static int +bcmspi_driver_init(sdioh_info_t *sd) +{ + sd_trace(("%s\n", __FUNCTION__)); + if ((bcmspi_host_init(sd)) != SUCCESS) { + return ERROR; + } + + if (bcmspi_client_init(sd) != SUCCESS) { + return ERROR; + } + + return SUCCESS; +} + +/* Read device reg */ +static int +bcmspi_card_regread(sdioh_info_t *sd, int func, uint32 regaddr, int regsize, uint32 *data) +{ + int status; + uint32 cmd_arg, dstatus; + + ASSERT(regsize); + + if (func == 2) + sd_trace(("Reg access on F2 will generate error indication in dstatus bits.\n")); + + cmd_arg = 0; + cmd_arg = SFIELD(cmd_arg, SPI_RW_FLAG, 0); + cmd_arg = SFIELD(cmd_arg, SPI_ACCESS, 1); /* Incremental access */ + cmd_arg = SFIELD(cmd_arg, SPI_FUNCTION, func); + cmd_arg = SFIELD(cmd_arg, SPI_REG_ADDR, regaddr); + cmd_arg = SFIELD(cmd_arg, SPI_LEN, regsize == BLOCK_SIZE_F2 ? 0 : regsize); + + sd_trace(("%s cmd_arg = 0x%x\n", __FUNCTION__, cmd_arg)); + sd_trace(("%s: rw=%d, func=%d, regaddr=0x%08x, data=0x%x\n", __FUNCTION__, 0, func, + regaddr, *data)); + + if ((status = bcmspi_cmd_issue(sd, sd->sd_use_dma, cmd_arg, data, regsize)) + != SUCCESS) + return status; + + bcmspi_cmd_getdstatus(sd, &dstatus); + if (dstatus) + sd_trace(("dstatus =0x%x\n", dstatus)); + + return SUCCESS; +} + +static int +bcmspi_card_regread_fixedaddr(sdioh_info_t *sd, int func, uint32 regaddr, int regsize, uint32 *data) +{ + + int status; + uint32 cmd_arg; + uint32 dstatus; + + ASSERT(regsize); + + if (func == 2) + sd_trace(("Reg access on F2 will generate error indication in dstatus bits.\n")); + + cmd_arg = 0; + cmd_arg = SFIELD(cmd_arg, SPI_RW_FLAG, 0); + cmd_arg = SFIELD(cmd_arg, SPI_ACCESS, 0); /* Fixed access */ + cmd_arg = SFIELD(cmd_arg, SPI_FUNCTION, func); + cmd_arg = SFIELD(cmd_arg, SPI_REG_ADDR, regaddr); + cmd_arg = SFIELD(cmd_arg, SPI_LEN, regsize); + + sd_trace(("%s cmd_arg = 0x%x\n", __FUNCTION__, cmd_arg)); + + if ((status = bcmspi_cmd_issue(sd, sd->sd_use_dma, cmd_arg, data, regsize)) + != SUCCESS) + return status; + + sd_trace(("%s: rw=%d, func=%d, regaddr=0x%08x, data=0x%x\n", __FUNCTION__, 0, func, + regaddr, *data)); + + bcmspi_cmd_getdstatus(sd, &dstatus); + sd_trace(("dstatus =0x%x\n", dstatus)); + return SUCCESS; +} + +/* write a device register */ +static int +bcmspi_card_regwrite(sdioh_info_t *sd, int func, uint32 regaddr, int regsize, uint32 data) +{ + int status; + uint32 cmd_arg, dstatus; + + ASSERT(regsize); + + cmd_arg = 0; + + cmd_arg = SFIELD(cmd_arg, SPI_RW_FLAG, 1); + cmd_arg = SFIELD(cmd_arg, SPI_ACCESS, 1); /* Incremental access */ + cmd_arg = SFIELD(cmd_arg, SPI_FUNCTION, func); + cmd_arg = SFIELD(cmd_arg, SPI_REG_ADDR, regaddr); + cmd_arg = SFIELD(cmd_arg, SPI_LEN, regsize == BLOCK_SIZE_F2 ? 0 : regsize); + + sd_trace(("%s cmd_arg = 0x%x\n", __FUNCTION__, cmd_arg)); + sd_trace(("%s: rw=%d, func=%d, regaddr=0x%08x, data=0x%x\n", __FUNCTION__, 1, func, + regaddr, data)); + + + if ((status = bcmspi_cmd_issue(sd, sd->sd_use_dma, cmd_arg, &data, regsize)) + != SUCCESS) + return status; + + bcmspi_cmd_getdstatus(sd, &dstatus); + if (dstatus) + sd_trace(("dstatus =0x%x\n", dstatus)); + + return SUCCESS; +} + +/* write a device register - 1 byte */ +static int +bcmspi_card_bytewrite(sdioh_info_t *sd, int func, uint32 regaddr, uint8 *byte) +{ + int status; + uint32 cmd_arg; + uint32 dstatus; + uint32 data = (uint32)(*byte); + + cmd_arg = 0; + cmd_arg = SFIELD(cmd_arg, SPI_FUNCTION, func); + cmd_arg = SFIELD(cmd_arg, SPI_ACCESS, 1); /* Incremental access */ + cmd_arg = SFIELD(cmd_arg, SPI_REG_ADDR, regaddr); + cmd_arg = SFIELD(cmd_arg, SPI_RW_FLAG, 1); + cmd_arg = SFIELD(cmd_arg, SPI_LEN, 1); + + sd_trace(("%s cmd_arg = 0x%x\n", __FUNCTION__, cmd_arg)); + sd_trace(("%s: func=%d, regaddr=0x%08x, data=0x%x\n", __FUNCTION__, func, + regaddr, data)); + + if ((status = bcmspi_cmd_issue(sd, sd->sd_use_dma, + cmd_arg, &data, 1)) != SUCCESS) { + return status; + } + + bcmspi_cmd_getdstatus(sd, &dstatus); + if (dstatus) + sd_trace(("dstatus =0x%x\n", dstatus)); + + return SUCCESS; +} + +void +bcmspi_cmd_getdstatus(sdioh_info_t *sd, uint32 *dstatus_buffer) +{ + *dstatus_buffer = sd->card_dstatus; +} + +/* 'data' is of type uint32 whereas other buffers are of type uint8 */ +static int +bcmspi_cmd_issue(sdioh_info_t *sd, bool use_dma, uint32 cmd_arg, + uint32 *data, uint32 datalen) +{ + uint32 i, j; + uint8 resp_delay = 0; + int err = SUCCESS; + uint32 hostlen; + uint32 spilen = 0; + uint32 dstatus_idx = 0; + uint16 templen, buslen, len, *ptr = NULL; + + sd_trace(("spi cmd = 0x%x\n", cmd_arg)); + + if (DWORDMODE_ON) { + spilen = GFIELD(cmd_arg, SPI_LEN); + if ((GFIELD(cmd_arg, SPI_FUNCTION) == SPI_FUNC_0) || + (GFIELD(cmd_arg, SPI_FUNCTION) == SPI_FUNC_1)) + dstatus_idx = spilen * 3; + + if ((GFIELD(cmd_arg, SPI_FUNCTION) == SPI_FUNC_2) && + (GFIELD(cmd_arg, SPI_RW_FLAG) == 1)) { + spilen = spilen << 2; + dstatus_idx = (spilen % 16) ? (16 - (spilen % 16)) : 0; + /* convert len to mod16 size */ + spilen = ROUNDUP(spilen, 16); + cmd_arg = SFIELD(cmd_arg, SPI_LEN, (spilen >> 2)); + } + } + + /* Set up and issue the SPI command. MSByte goes out on bus first. Increase datalen + * according to the wordlen mode(16/32bit) the device is in. + */ + if (sd->wordlen == 4) { /* 32bit spid */ + *(uint32 *)spi_outbuf = bcmswap32(cmd_arg); + if (datalen & 0x3) + datalen += (4 - (datalen & 0x3)); + } else if (sd->wordlen == 2) { /* 16bit spid */ + *(uint16 *)spi_outbuf = bcmswap16(cmd_arg & 0xffff); + *(uint16 *)&spi_outbuf[2] = bcmswap16((cmd_arg & 0xffff0000) >> 16); + if (datalen & 0x1) + datalen++; + if (datalen < 4) + datalen = ROUNDUP(datalen, 4); + } else { + sd_err(("Host is %d bit spid, could not create SPI command.\n", + 8 * sd->wordlen)); + return ERROR; + } + + /* for Write, put the data into the output buffer */ + if (GFIELD(cmd_arg, SPI_RW_FLAG) == 1) { + /* We send len field of hw-header always a mod16 size, both from host and dongle */ + if (DWORDMODE_ON) { + if (GFIELD(cmd_arg, SPI_FUNCTION) == SPI_FUNC_2) { + ptr = (uint16 *)&data[0]; + templen = *ptr; + /* ASSERT(*ptr == ~*(ptr + 1)); */ + templen = ROUNDUP(templen, 16); + *ptr = templen; + sd_trace(("actual tx len = %d\n", (uint16)(~*(ptr+1)))); + } + } + + if (datalen != 0) { + for (i = 0; i < datalen/4; i++) { + if (sd->wordlen == 4) { /* 32bit spid */ + *(uint32 *)&spi_outbuf[i * 4 + CMDLEN] = + bcmswap32(data[i]); + } else if (sd->wordlen == 2) { /* 16bit spid */ + *(uint16 *)&spi_outbuf[i * 4 + CMDLEN] = + bcmswap16(data[i] & 0xffff); + *(uint16 *)&spi_outbuf[i * 4 + CMDLEN + 2] = + bcmswap16((data[i] & 0xffff0000) >> 16); + } + } + } + } + + /* Append resp-delay number of bytes and clock them out for F0/1/2 reads. */ + if (GFIELD(cmd_arg, SPI_RW_FLAG) == 0) { + int func = GFIELD(cmd_arg, SPI_FUNCTION); + switch (func) { + case 0: + resp_delay = sd->resp_delay_all ? F0_RESPONSE_DELAY : 0; + break; + case 1: + resp_delay = F1_RESPONSE_DELAY; + break; + case 2: + resp_delay = sd->resp_delay_all ? F2_RESPONSE_DELAY : 0; + break; + default: + ASSERT(0); + break; + } + /* Program response delay */ + bcmspi_prog_resp_delay(sd, func, resp_delay); + } + + /* +4 for cmd and +4 for dstatus */ + hostlen = datalen + 8 + resp_delay; + hostlen += dstatus_idx; + hostlen += (4 - (hostlen & 0x3)); + spi_sendrecv(sd, spi_outbuf, spi_inbuf, hostlen); + + /* for Read, get the data into the input buffer */ + if (datalen != 0) { + if (GFIELD(cmd_arg, SPI_RW_FLAG) == 0) { /* if read cmd */ + for (j = 0; j < datalen/4; j++) { + if (sd->wordlen == 4) { /* 32bit spid */ + data[j] = bcmswap32(*(uint32 *)&spi_inbuf[j * 4 + + CMDLEN + resp_delay]); + } else if (sd->wordlen == 2) { /* 16bit spid */ + data[j] = (bcmswap16(*(uint16 *)&spi_inbuf[j * 4 + + CMDLEN + resp_delay])) | + ((bcmswap16(*(uint16 *)&spi_inbuf[j * 4 + + CMDLEN + resp_delay + 2])) << 16); + } + } + + if ((DWORDMODE_ON) && (GFIELD(cmd_arg, SPI_FUNCTION) == SPI_FUNC_2)) { + ptr = (uint16 *)&data[0]; + templen = *ptr; + buslen = len = ~(*(ptr + 1)); + buslen = ROUNDUP(buslen, 16); + /* populate actual len in hw-header */ + if (templen == buslen) + *ptr = len; + } + } + } + + /* Restore back the len field of the hw header */ + if (DWORDMODE_ON) { + if ((GFIELD(cmd_arg, SPI_FUNCTION) == SPI_FUNC_2) && + (GFIELD(cmd_arg, SPI_RW_FLAG) == 1)) { + ptr = (uint16 *)&data[0]; + *ptr = (uint16)(~*(ptr+1)); + } + } + + dstatus_idx += (datalen + CMDLEN + resp_delay); + /* Last 4bytes are dstatus. Device is configured to return status bits. */ + if (sd->wordlen == 4) { /* 32bit spid */ + sd->card_dstatus = bcmswap32(*(uint32 *)&spi_inbuf[dstatus_idx]); + } else if (sd->wordlen == 2) { /* 16bit spid */ + sd->card_dstatus = (bcmswap16(*(uint16 *)&spi_inbuf[dstatus_idx]) | + (bcmswap16(*(uint16 *)&spi_inbuf[dstatus_idx + 2]) << 16)); + } else { + sd_err(("Host is %d bit machine, could not read SPI dstatus.\n", + 8 * sd->wordlen)); + return ERROR; + } + if (sd->card_dstatus == 0xffffffff) { + sd_err(("looks like not a GSPI device or device is not powered.\n")); + } + + err = bcmspi_update_stats(sd, cmd_arg); + + return err; + +} + +static int +bcmspi_card_buf(sdioh_info_t *sd, int rw, int func, bool fifo, + uint32 addr, int nbytes, uint32 *data) +{ + int status; + uint32 cmd_arg; + bool write = rw == SDIOH_READ ? 0 : 1; + uint retries = 0; + + bool enable; + uint32 spilen; + + cmd_arg = 0; + + ASSERT(nbytes); + ASSERT(nbytes <= sd->client_block_size[func]); + + if (write) sd->t_cnt++; else sd->r_cnt++; + + if (func == 2) { + /* Frame len check limited by gSPI. */ + if ((nbytes > 2000) && write) { + sd_trace((">2KB write: F2 wr of %d bytes\n", nbytes)); + } + /* ASSERT(nbytes <= 2048); Fix bigger len gspi issue and uncomment. */ + /* If F2 fifo on device is not ready to receive data, don't do F2 transfer */ + if (write) { + uint32 dstatus; + /* check F2 ready with cached one */ + bcmspi_cmd_getdstatus(sd, &dstatus); + if ((dstatus & STATUS_F2_RX_READY) == 0) { + retries = WAIT_F2RXFIFORDY; + enable = 0; + while (retries-- && !enable) { + OSL_DELAY(WAIT_F2RXFIFORDY_DELAY * 1000); + bcmspi_card_regread(sd, SPI_FUNC_0, SPID_STATUS_REG, 4, + &dstatus); + if (dstatus & STATUS_F2_RX_READY) + enable = TRUE; + } + if (!enable) { + struct spierrstats_t *spierrstats = &sd->spierrstats; + spierrstats->f2rxnotready++; + sd_err(("F2 FIFO is not ready to receive data.\n")); + return ERROR; + } + sd_trace(("No of retries on F2 ready %d\n", + (WAIT_F2RXFIFORDY - retries))); + } + } + } + + /* F2 transfers happen on 0 addr */ + addr = (func == 2) ? 0 : addr; + + /* In pio mode buffer is read using fixed address fifo in func 1 */ + if ((func == 1) && (fifo)) + cmd_arg = SFIELD(cmd_arg, SPI_ACCESS, 0); + else + cmd_arg = SFIELD(cmd_arg, SPI_ACCESS, 1); + + cmd_arg = SFIELD(cmd_arg, SPI_FUNCTION, func); + cmd_arg = SFIELD(cmd_arg, SPI_REG_ADDR, addr); + cmd_arg = SFIELD(cmd_arg, SPI_RW_FLAG, write); + spilen = sd->data_xfer_count = MIN(sd->client_block_size[func], nbytes); + if ((sd->dwordmode == TRUE) && (GFIELD(cmd_arg, SPI_FUNCTION) == SPI_FUNC_2)) { + /* convert len to mod4 size */ + spilen = spilen + ((spilen & 0x3) ? (4 - (spilen & 0x3)): 0); + cmd_arg = SFIELD(cmd_arg, SPI_LEN, (spilen >> 2)); + } else + cmd_arg = SFIELD(cmd_arg, SPI_LEN, spilen); + + if ((func == 2) && (fifo == 1)) { + sd_data(("%s: %s func %d, %s, addr 0x%x, len %d bytes, r_cnt %d t_cnt %d\n", + __FUNCTION__, write ? "Wr" : "Rd", func, "INCR", + addr, nbytes, sd->r_cnt, sd->t_cnt)); + } + + sd_trace(("%s cmd_arg = 0x%x\n", __FUNCTION__, cmd_arg)); + sd_data(("%s: %s func %d, %s, addr 0x%x, len %d bytes, r_cnt %d t_cnt %d\n", + __FUNCTION__, write ? "Wd" : "Rd", func, "INCR", + addr, nbytes, sd->r_cnt, sd->t_cnt)); + + + if ((status = bcmspi_cmd_issue(sd, sd->sd_use_dma, cmd_arg, + data, nbytes)) != SUCCESS) { + sd_err(("%s: cmd_issue failed for %s\n", __FUNCTION__, + (write ? "write" : "read"))); + return status; + } + + /* gSPI expects that hw-header-len is equal to spi-command-len */ + if ((func == 2) && (rw == SDIOH_WRITE) && (sd->dwordmode == FALSE)) { + ASSERT((uint16)sd->data_xfer_count == (uint16)(*data & 0xffff)); + ASSERT((uint16)sd->data_xfer_count == (uint16)(~((*data & 0xffff0000) >> 16))); + } + + if ((nbytes > 2000) && !write) { + sd_trace((">2KB read: F2 rd of %d bytes\n", nbytes)); + } + + return SUCCESS; +} + +/* Reset and re-initialize the device */ +int +sdioh_sdio_reset(sdioh_info_t *si) +{ + si->card_init_done = FALSE; + return bcmspi_client_init(si); +} diff --git a/drivers/net/wireless/bcm4329/bcmutils.c b/drivers/net/wireless/bcm4329/bcmutils.c index c553d8ec..43c04ee9 100644 --- a/drivers/net/wireless/bcm4329/bcmutils.c +++ b/drivers/net/wireless/bcm4329/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 diff --git a/drivers/net/wireless/bcm4329/bcmwifi.c b/drivers/net/wireless/bcm4329/bcmwifi.c index 641a4fd3..803acf84 100644 --- a/drivers/net/wireless/bcm4329/bcmwifi.c +++ b/drivers/net/wireless/bcm4329/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/drivers/net/wireless/bcm4329/dhd.h b/drivers/net/wireless/bcm4329/dhd.h index 3b22eb73..1ddf1ff6 100644 --- a/drivers/net/wireless/bcm4329/dhd.h +++ b/drivers/net/wireless/bcm4329/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.4.7 2010/11/12 22:48:36 Exp $ */ /**************** @@ -59,6 +59,11 @@ #include +#ifdef DHD_DEBUG +#ifndef DHD_DEBUG_TRAP +#define DHD_DEBUG_TRAP +#endif +#endif /* Forward decls */ struct dhd_bus; @@ -81,9 +86,11 @@ enum dhd_bus_wake_state { WAKE_LOCK_TMOUT, WAKE_LOCK_WATCHDOG, WAKE_LOCK_LINK_DOWN_TMOUT, + WAKE_LOCK_PNO_FIND_TMOUT, WAKE_LOCK_SOFTAP_SET, WAKE_LOCK_SOFTAP_STOP, WAKE_LOCK_SOFTAP_START, + WAKE_LOCK_SOFTAP_THREAD, WAKE_LOCK_MAX }; enum dhd_prealloc_index { @@ -145,33 +152,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 +210,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 */ @@ -197,6 +220,11 @@ extern int dhd_os_wake_unlock(dhd_pub_t *pub); extern int dhd_os_wake_lock_timeout(dhd_pub_t *pub); extern int dhd_os_wake_lock_timeout_enable(dhd_pub_t *pub); +extern void dhd_os_start_lock(dhd_pub_t *pub); +extern void dhd_os_start_unlock(dhd_pub_t *pub); +extern unsigned long dhd_os_spin_lock(dhd_pub_t *pub); +extern void dhd_os_spin_unlock(dhd_pub_t *pub, unsigned long flags); + typedef struct dhd_if_event { uint8 ifidx; uint8 action; @@ -262,9 +290,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 +343,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 { @@ -319,7 +352,11 @@ typedef enum cust_gpio_modes { WLAN_POWER_ON, 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); +extern int net_os_send_hang_message(struct net_device *dev); + /* * Insmod parameters for debug/test */ @@ -327,6 +364,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 +375,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 @@ -344,6 +406,10 @@ extern uint dhd_sdiod_drive_strength; /* Override to force tx queueing all the time */ extern uint dhd_force_tx_queueing; +/* Default KEEP_ALIVE Period is 55 sec to prevent AP from sending Keep Alive probe frame */ +#define KEEP_ALIVE_PERIOD 55000 +#define NULL_PKT_STR "null_pkt" + #ifdef SDTEST /* Echo packet generator (SDIO), pkts/s */ extern uint dhd_pktgen; @@ -364,9 +430,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/drivers/net/wireless/bcm4329/dhd_bus.h b/drivers/net/wireless/bcm4329/dhd_bus.h index 93392f98..97af41b3 100644 --- a/drivers/net/wireless/bcm4329/dhd_bus.h +++ b/drivers/net/wireless/bcm4329/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.7 2010/08/13 01:35:24 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 /* DHD_DEBUG */ /* Deferred processing for the bus, return TRUE requests reschedule */ extern bool dhd_bus_dpc(struct dhd_bus *bus); diff --git a/drivers/net/wireless/bcm4329/dhd_cdc.c b/drivers/net/wireless/bcm4329/dhd_cdc.c index e5c5a708..61f6a6f3 100644 --- a/drivers/net/wireless/bcm4329/dhd_cdc.c +++ b/drivers/net/wireless/bcm4329/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,10 +41,7 @@ #include #include -#ifdef SET_RANDOM_MAC_SOFTAP -#include -#include -#endif +extern int dhd_preinit_ioctls(dhd_pub_t *dhd); /* Packet alignment for most efficient SDIO (can change based on platform) */ #ifndef DHD_SDALIGN @@ -197,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; @@ -214,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); @@ -326,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__)); @@ -350,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); @@ -365,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); @@ -514,298 +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) -{ - 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; - char mac_buf[16]; -#ifdef SET_RANDOM_MAC_SOFTAP - uint rand_mac; -#endif - 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); - -#ifdef SET_RANDOM_MAC_SOFTAP - if (strstr(fw_path, "apsta") != NULL) { - srandom32((uint)jiffies); - rand_mac = random32(); - iovbuf[0] |= 0x02; /* locally administered bit */ - iovbuf[3] = (unsigned char)rand_mac; - 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__)); - } - } - - /* 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 */ - if (roamvar) { - 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 *)&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("0xffffffffffff", - (char *) pkt_filterp->u.pattern.mask_and_pattern)); - - /* Parse pattern filter pattern. */ - sprintf( mac_buf, "0x%02x%02x%02x%02x%02x%02x", - dhd->mac.octet[0], dhd->mac.octet[1], dhd->mac.octet[2], - dhd->mac.octet[3], dhd->mac.octet[4], dhd->mac.octet[5] - ); - - pattern_size = htod32(wl_pattern_atoh(mac_buf, - (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")); - dhd_os_proto_unblock(dhd); - return -EINVAL; - } - - 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)); - - dhd_os_proto_unblock(dhd); - return 0; -} - int dhd_prot_init(dhd_pub_t *dhd) { int ret = 0; + char buf[128]; + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + dhd_os_proto_block(dhd); + /* 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 ret; + } + memcpy(dhd->mac.octet, buf, ETHER_ADDR_LEN); + + dhd_os_proto_unblock(dhd); + +#ifdef EMBEDDED_PLATFORM ret = dhd_preinit_ioctls(dhd); +#endif /* EMBEDDED_PLATFORM */ /* Always assumes wl for now */ dhd->iswl = TRUE; diff --git a/drivers/net/wireless/bcm4329/dhd_common.c b/drivers/net/wireless/bcm4329/dhd_common.c index a0e6a737..4331d39a 100644 --- a/drivers/net/wireless/bcm4329/dhd_common.c +++ b/drivers/net/wireless/bcm4329/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.20 2010/12/20 23:37:28 Exp $ */ #include #include @@ -37,9 +37,21 @@ #include #include +#include + +#ifdef SET_RANDOM_MAC_SOFTAP +#include +#include +#endif + +#ifdef GET_CUSTOM_MAC_ENABLE +int wifi_get_mac_addr(unsigned char *buf); +#endif /* GET_CUSTOM_MAC_ENABLE */ int dhd_msg_level; +#include + char fw_path[MOD_PARAM_PATHLEN]; char nv_path[MOD_PARAM_PATHLEN]; @@ -48,6 +60,32 @@ 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); + +#if defined(SOFTAP) +extern bool ap_fw_loaded; +#endif +#if defined(KEEP_ALIVE) +int dhd_keep_alive_onoff(dhd_pub_t *dhd, int ka_on); +#endif /* KEEP_ALIVE */ + +/* 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 +95,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 +103,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 +125,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 +268,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 +519,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 +627,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 +647,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 +927,1397 @@ 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 = 4; + int scan_assoc_time = 40; + int scan_unassoc_time = 40; + uint32 listen_interval = LISTEN_INTERVAL; /* Default Listen Interval in Beacons */ +#if defined(SOFTAP) + uint dtim = 1; +#endif + int ret = 0; +#ifdef GET_CUSTOM_MAC_ENABLE + 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; + + 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__)); + } + } + + /* Set Listen Interval */ + bcm_mkiovar("assoc_listen", (char *)&listen_interval, 4, iovbuf, sizeof(iovbuf)); + if ((ret = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf))) < 0) + DHD_ERROR(("%s assoc_listen failed %d\n", __FUNCTION__, ret)); + + /* 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 defined(SOFTAP) + if (ap_fw_loaded == TRUE) { + dhdcdc_set_ioctl(dhd, 0, WLC_SET_DTIMPRD, (char *)&dtim, sizeof(dtim)); + } +#endif + + 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 */ + +#if defined(KEEP_ALIVE) + { + /* Set Keep Alive : be sure to use FW with -keepalive */ + int res; + + if (ap_fw_loaded == FALSE) { + if ((res = dhd_keep_alive_onoff(dhd, 1)) < 0) + DHD_ERROR(("%s set keeplive failed %d\n", \ + __FUNCTION__, res)); + } + } +#endif + + 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 + +/* Function to estimate possible DTIM_SKIP value */ +int dhd_get_dtim_skip(dhd_pub_t *dhd) +{ + int bcn_li_dtim; + char buf[128]; + int ret; + int dtim_assoc = 0; + + if ((dhd->dtim_skip == 0) || (dhd->dtim_skip == 1)) + bcn_li_dtim = 3; + else + bcn_li_dtim = dhd->dtim_skip; + + /* Read DTIM value if associated */ + memset(buf, 0, sizeof(buf)); + bcm_mkiovar("dtim_assoc", 0, 0, buf, sizeof(buf)); + if ((ret = dhdcdc_query_ioctl(dhd, 0, WLC_GET_VAR, buf, sizeof(buf))) < 0) { + DHD_ERROR(("%s failed code %d\n", __FUNCTION__, ret)); + bcn_li_dtim = 1; + goto exit; + } + else + dtim_assoc = dtoh32(*(int *)buf); + + DHD_ERROR(("%s bcn_li_dtim=%d DTIM=%d Listen=%d\n", \ + __FUNCTION__, bcn_li_dtim, dtim_assoc, LISTEN_INTERVAL)); + + /* if not assocated just eixt */ + if (dtim_assoc == 0) { + goto exit; + } + + /* check if sta listen interval fits into AP dtim */ + if (dtim_assoc > LISTEN_INTERVAL) { + /* AP DTIM to big for our Listen Interval : no dtim skiping */ + bcn_li_dtim = 1; + DHD_ERROR(("%s DTIM=%d > Listen=%d : too big ...\n", \ + __FUNCTION__, dtim_assoc, LISTEN_INTERVAL)); + goto exit; + } + + if ((bcn_li_dtim * dtim_assoc) > LISTEN_INTERVAL) { + /* Round up dtim_skip to fit into STAs Listen Interval */ + bcn_li_dtim = (int)(LISTEN_INTERVAL / dtim_assoc); + DHD_TRACE(("%s agjust dtim_skip as %d\n", __FUNCTION__, bcn_li_dtim)); + } + +exit: + return bcn_li_dtim; +} + +#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); + + if (pfn_param.scan_freq > PNO_SCAN_MAX_FW) { + DHD_ERROR(("%s pno freq above %d sec\n", __FUNCTION__, PNO_SCAN_MAX_FW)); + return err; + } + + 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 set OK with PNO time=%d\n", __FUNCTION__, \ + pfn_param.scan_freq)); + } + 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(KEEP_ALIVE) +int dhd_keep_alive_onoff(dhd_pub_t *dhd, int ka_on) +{ + char buf[256]; + char *buf_ptr = buf; + wl_keep_alive_pkt_t keep_alive_pkt; + char * str; + int str_len, buf_len; + int res = 0; + int keep_alive_period = KEEP_ALIVE_PERIOD; /* in ms */ + + DHD_TRACE(("%s: ka:%d\n", __FUNCTION__, ka_on)); + + if (ka_on) { /* on suspend */ + keep_alive_pkt.period_msec = keep_alive_period; + + } else { + /* on resume, turn off keep_alive packets */ + keep_alive_pkt.period_msec = 0; + } + + /* IOC var name */ + str = "keep_alive"; + str_len = strlen(str); + strncpy(buf, str, str_len); + buf[str_len] = '\0'; + buf_len = str_len + 1; + + /* set ptr to IOCTL payload after the var name */ + buf_ptr += buf_len; /* include term Z */ + + /* copy Keep-alive attributes from local var keep_alive_pkt */ + str = NULL_PKT_STR; + keep_alive_pkt.len_bytes = strlen(str); + + memcpy(buf_ptr, &keep_alive_pkt, WL_KEEP_ALIVE_FIXED_LEN); + buf_ptr += WL_KEEP_ALIVE_FIXED_LEN; + + /* copy packet data */ + memcpy(buf_ptr, str, keep_alive_pkt.len_bytes); + buf_len += (WL_KEEP_ALIVE_FIXED_LEN + keep_alive_pkt.len_bytes); + + res = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, buf, buf_len); + return res; +} +#endif /* defined(KEEP_ALIVE) */ + +#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/drivers/net/wireless/bcm4329/dhd_custom_gpio.c b/drivers/net/wireless/bcm4329/dhd_custom_gpio.c index 2dc6e42a..8c6ec470 100644 --- a/drivers/net/wireless/bcm4329/dhd_custom_gpio.c +++ b/drivers/net/wireless/bcm4329/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 +#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/drivers/net/wireless/bcm4329/dhd_dbg.h b/drivers/net/wireless/bcm4329/dhd_dbg.h index e6d18f7a..b48c1d70 100644 --- a/drivers/net/wireless/bcm4329/dhd_dbg.h +++ b/drivers/net/wireless/bcm4329/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/drivers/net/wireless/bcm4329/dhd_linux.c b/drivers/net/wireless/bcm4329/dhd_linux.c index 2661d8f2..f14f5e81 100644 --- a/drivers/net/wireless/bcm4329/dhd_linux.c +++ b/drivers/net/wireless/bcm4329/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.4.35 2010/11/17 03:13:21 Exp $ */ #ifdef CONFIG_WIFI_CONTROL_FUNC @@ -43,6 +43,7 @@ #include #include #include +#include #include #include @@ -61,7 +62,6 @@ #ifdef CONFIG_HAS_WAKELOCK #include #endif -#include #if defined(CUSTOMER_HW2) && defined(CONFIG_WIFI_CONTROL_FUNC) #include @@ -107,7 +107,7 @@ int wifi_set_power(int on, unsigned long msec) int wifi_set_reset(int on, unsigned long msec) { - printk("%s = %d\n", __FUNCTION__, on); + DHD_TRACE(("%s = %d\n", __FUNCTION__, on)); if (wifi_control_data && wifi_control_data->set_reset) { wifi_control_data->set_reset(on); } @@ -116,6 +116,17 @@ int wifi_set_reset(int on, unsigned long msec) return 0; } +int wifi_get_mac_addr(unsigned char *buf) +{ + DHD_TRACE(("%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 +151,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 +216,19 @@ print_tainted() /* Linux wireless extension support */ #if defined(CONFIG_WIRELESS_EXT) #include -#endif +#endif /* defined(CONFIG_WIRELESS_EXT) */ + +extern int dhdcdc_set_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len); #if defined(CONFIG_HAS_EARLYSUSPEND) #include #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,23 +248,25 @@ 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; /* OS/stack specifics */ dhd_if_t *iflist[DHD_MAX_IFS]; - struct semaphore proto_sem; + struct mutex proto_sem; wait_queue_head_t ioctl_resp_wait; struct timer_list timer; bool wd_timer_valid; struct tasklet_struct tasklet; spinlock_t sdlock; spinlock_t txqlock; + spinlock_t dhd_lock; + /* Thread based operation */ bool threads_only; - struct semaphore sdsem; + struct mutex sdsem; long watchdog_pid; struct semaphore watchdog_sem; struct completion watchdog_exited; @@ -263,6 +283,10 @@ typedef struct dhd_info { int wl_count; int wl_packet; + int hang_was_sent; /* flag that message was send at least once */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) + struct mutex wl_start_lock; /* mutex when START called to prevent any other Linux calls */ +#endif /* Thread to issue ioctl for multicast */ long sysioc_pid; struct semaphore sysioc_sem; @@ -286,7 +310,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 12000 /* 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 +327,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 +365,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); @@ -400,7 +460,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 */ @@ -420,18 +480,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 = { @@ -442,28 +506,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 + */ + bcn_li_dtim = dhd_get_dtim_skip(dhd); + 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 during suspend */ + 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) */ @@ -587,7 +749,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; @@ -597,15 +763,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", @@ -620,10 +790,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; @@ -722,13 +904,18 @@ _dhd_set_mac_address(dhd_info_t *dhd, int ifidx, struct ether_addr *addr) #ifdef SOFTAP extern struct net_device *ap_net_dev; +/* semaphore that the soft AP CODE waits on */ +extern struct semaphore ap_eth_sema; #endif static void dhd_op_if(dhd_if_t *ifp) { - dhd_info_t *dhd; - int ret = 0, err = 0; + dhd_info_t *dhd; + int ret = 0, err = 0; +#ifdef SOFTAP + unsigned long flags; +#endif ASSERT(ifp && ifp->info && ifp->idx); /* Virtual interfaces only */ @@ -763,13 +950,12 @@ dhd_op_if(dhd_if_t *ifp) ret = -EOPNOTSUPP; } else { #ifdef SOFTAP - /* semaphore that the soft AP CODE waits on */ - extern struct semaphore ap_eth_sema; - + flags = dhd_os_spin_lock(&dhd->pub); /* save ptr to wl0.1 netdev for use in wl_iw.c */ ap_net_dev = ifp->net; /* signal to the SOFTAP 'sleeper' thread, wl0.1 is ready */ up(&ap_eth_sema); + dhd_os_spin_unlock(&dhd->pub, flags); #endif DHD_TRACE(("\n ==== pid:%x, net_device for if:%s created ===\n\n", current->pid, ifp->net->name)); @@ -798,8 +984,10 @@ dhd_op_if(dhd_if_t *ifp) dhd->iflist[ifp->idx] = NULL; MFREE(dhd->pub.osh, ifp, sizeof(*ifp)); #ifdef SOFTAP + flags = dhd_os_spin_lock(&dhd->pub); if (ifp->net == ap_net_dev) ap_net_dev = NULL; /* NULL SOFTAP global as well */ + dhd_os_spin_unlock(&dhd->pub, flags); #endif /* SOFTAP */ } } @@ -811,18 +999,21 @@ _dhd_sysioc_thread(void *data) int i; #ifdef SOFTAP bool in_ap = FALSE; + unsigned long flags; #endif - set_freezable(); - DAEMONIZE("dhd_sysioc"); while (down_interruptible(&dhd->sysioc_sem) == 0) { + dhd_os_start_lock(&dhd->pub); dhd_os_wake_lock(&dhd->pub); for (i = 0; i < DHD_MAX_IFS; i++) { if (dhd->iflist[i]) { + DHD_TRACE(("%s: interface %d\n",__FUNCTION__, i)); #ifdef SOFTAP + flags = dhd_os_spin_lock(&dhd->pub); in_ap = (ap_net_dev != NULL); + dhd_os_spin_unlock(&dhd->pub, flags); #endif /* SOFTAP */ if (dhd->iflist[i]->state) dhd_op_if(dhd->iflist[i]); @@ -855,7 +1046,9 @@ _dhd_sysioc_thread(void *data) } } dhd_os_wake_unlock(&dhd->pub); + dhd_os_start_unlock(&dhd->pub); } + DHD_TRACE(("%s: stopped\n",__FUNCTION__)); complete_and_exit(&dhd->sysioc_exited, 0); } @@ -868,6 +1061,7 @@ dhd_set_mac_address(struct net_device *dev, void *addr) struct sockaddr *sa = (struct sockaddr *)addr; int ifidx; + DHD_TRACE(("%s: Enter\n",__FUNCTION__)); ifidx = dhd_net2idx(dhd, dev); if (ifidx == DHD_BAD_IF) return -1; @@ -886,6 +1080,7 @@ dhd_set_multicast_list(struct net_device *dev) dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); int ifidx; + DHD_TRACE(("%s: Enter\n",__FUNCTION__)); ifidx = dhd_net2idx(dhd, dev); if (ifidx == DHD_BAD_IF) return; @@ -944,11 +1139,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; } @@ -956,6 +1159,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; } @@ -987,13 +1191,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; } @@ -1190,28 +1395,29 @@ 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); + dhd_os_sdlock(&dhd->pub); + if (dhd->pub.dongle_reset == FALSE) { + DHD_TIMER(("%s:\n", __FUNCTION__)); + /* Call the bus module watchdog */ + dhd_bus_watchdog(&dhd->pub); - /* Count the tick for reference */ - dhd->pub.tickcnt++; + /* 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); + /* Reschedule the watchdog */ + if (dhd->wd_timer_valid) + mod_timer(&dhd->timer, jiffies + dhd_watchdog_ms * HZ / 1000); } + dhd_os_sdunlock(&dhd->pub); dhd_os_wake_unlock(&dhd->pub); - } - else + } else { break; + } } complete_and_exit(&dhd->watchdog_exited, 0); @@ -1222,11 +1428,18 @@ dhd_watchdog(ulong data) { dhd_info_t *dhd = (dhd_info_t *)data; + dhd_os_wake_lock(&dhd->pub); + if (dhd->pub.dongle_reset) { + dhd_os_wake_unlock(&dhd->pub); + return; + } + if (dhd->watchdog_pid >= 0) { up(&dhd->watchdog_sem); return; } + dhd_os_sdlock(&dhd->pub); /* Call the bus module watchdog */ dhd_bus_watchdog(&dhd->pub); @@ -1234,12 +1447,10 @@ 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_sdunlock(&dhd->pub); + dhd_os_wake_unlock(&dhd->pub); } static int @@ -1259,8 +1470,6 @@ dhd_dpc_thread(void *data) } #endif /* DHD_SCHED */ - set_freezable(); - DAEMONIZE("dhd_dpc"); /* Run until signal received */ @@ -1529,28 +1738,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)); @@ -1628,6 +1849,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; @@ -1636,6 +1863,8 @@ done: if (buf) MFREE(dhd->pub.osh, buf, buflen); + dhd_os_wake_unlock(&dhd->pub); + return OSL_ERROR(bcmerror); } @@ -1645,8 +1874,7 @@ dhd_stop(struct net_device *net) #if !defined(IGNORE_ETH0_DOWN) dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net); - DHD_TRACE(("%s: Enter\n", __FUNCTION__)); - + DHD_TRACE(("%s: Enter %s\n", __FUNCTION__, net->name)); if (dhd->pub.up == 0) { return 0; } @@ -1671,12 +1899,16 @@ dhd_open(struct net_device *net) #endif int ifidx; - wl_control_wl_start(net); /* start if needed */ + /* Force start if ifconfig_up gets called before START command */ + wl_control_wl_start(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 */ @@ -1715,7 +1947,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 @@ -1827,7 +2059,7 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) net->netdev_ops = NULL; #endif - init_MUTEX(&dhd->proto_sem); + mutex_init(&dhd->proto_sem); /* Initialize other structure content */ init_waitqueue_head(&dhd->ioctl_resp_wait); init_waitqueue_head(&dhd->ctrl_wait); @@ -1835,6 +2067,7 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) /* Initialize the spinlocks */ spin_lock_init(&dhd->sdlock); spin_lock_init(&dhd->txqlock); + spin_lock_init(&dhd->dhd_lock); /* Initialize Wakelock stuff */ spin_lock_init(&dhd->wl_lock); @@ -1844,7 +2077,9 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) wake_lock_init(&dhd->wl_wifi, WAKE_LOCK_SUSPEND, "wlan_wake"); wake_lock_init(&dhd->wl_rxwake, WAKE_LOCK_SUSPEND, "wlan_rx_wake"); #endif - +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) + mutex_init(&dhd->wl_start_lock); +#endif /* Link to info module */ dhd->pub.info = dhd; @@ -1863,7 +2098,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); @@ -1871,7 +2106,7 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) dhd->timer.function = dhd_watchdog; /* Initialize thread based operation and lock */ - init_MUTEX(&dhd->sdsem); + mutex_init(&dhd->sdsem); if ((dhd_watchdog_prio >= 0) && (dhd_dpc_prio >= 0)) { dhd->threads_only = TRUE; } @@ -1943,6 +2178,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); @@ -1970,8 +2208,8 @@ dhd_bus_start(dhd_pub_t *dhdp) #if defined(OOB_INTR_ONLY) /* Host registration for OOB interrupt */ if (bcmsdh_register_oob_intr(dhdp)) { - del_timer_sync(&dhd->timer); dhd->wd_timer_valid = FALSE; + del_timer_sync(&dhd->timer); DHD_ERROR(("%s Host failed to resgister for OOB\n", __FUNCTION__)); return -ENODEV; } @@ -1982,12 +2220,48 @@ dhd_bus_start(dhd_pub_t *dhdp) /* If bus is not ready, can't come up */ if (dhd->pub.busstate != DHD_BUS_DATA) { - del_timer_sync(&dhd->timer); dhd->wd_timer_valid = FALSE; + del_timer_sync(&dhd->timer); DHD_ERROR(("%s failed bus is not ready\n", __FUNCTION__)); 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); + setbit(dhdp->eventmask, WLC_E_RELOAD); +#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; @@ -2090,14 +2364,14 @@ dhd_net_attach(dhd_pub_t *dhdp, int ifidx) 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; @@ -2111,6 +2385,9 @@ 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) +#if defined(CONFIG_FIRST_SCAN) #ifdef SOFTAP if (ifidx == 0) /* Don't call for SOFTAP Interface in SOFTAP MODE */ @@ -2118,10 +2395,12 @@ dhd_net_attach(dhd_pub_t *dhdp, int ifidx) #else wl_iw_iscan_set_scan_broadcast_prep(net, 1); #endif /* SOFTAP */ +#endif /* CONFIG_FIRST_SCAN */ +#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: @@ -2153,8 +2432,8 @@ dhd_bus_detach(dhd_pub_t *dhdp) #endif /* defined(OOB_INTR_ONLY) */ /* Clear the watchdog timer */ - del_timer_sync(&dhd->timer); dhd->wd_timer_valid = FALSE; + del_timer_sync(&dhd->timer); } } } @@ -2173,22 +2452,25 @@ 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 */ wl_iw_detach(); #endif - - for (i = 1; i < DHD_MAX_IFS; i++) - if (dhd->iflist[i]) - dhd_del_if(dhd, i); - if (dhd->sysioc_pid >= 0) { KILL_PROC(dhd->sysioc_pid, SIGTERM); wait_for_completion(&dhd->sysioc_exited); } + for (i = 1; i < DHD_MAX_IFS; i++) + if (dhd->iflist[i]) { + dhd->iflist[i]->state = WLC_E_IF_DEL; + dhd->iflist[i]->idx = i; + dhd_op_if(dhd->iflist[i]); + } + ifp = dhd->iflist[0]; ASSERT(ifp); #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 31)) @@ -2233,6 +2515,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) { @@ -2292,7 +2587,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; @@ -2315,20 +2610,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); @@ -2341,7 +2622,7 @@ dhd_os_proto_block(dhd_pub_t *pub) dhd_info_t *dhd = (dhd_info_t *)(pub->info); if (dhd) { - down(&dhd->proto_sem); + mutex_lock(&dhd->proto_sem); return 1; } @@ -2354,7 +2635,7 @@ dhd_os_proto_unblock(dhd_pub_t *pub) dhd_info_t *dhd = (dhd_info_t *)(pub->info); if (dhd) { - up(&dhd->proto_sem); + mutex_unlock(&dhd->proto_sem); return 1; } @@ -2381,14 +2662,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; @@ -2416,28 +2700,27 @@ 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; + unsigned long flags; + int del_timer_flag = FALSE; - if (pub->busstate == DHD_BUS_DOWN) { - return; + flags = dhd_os_spin_lock(pub); + + /* don't start the wd until fw is loaded */ + if (pub->busstate != DHD_BUS_DOWN) { + if (wdtick) { + dhd_watchdog_ms = (uint)wdtick; + dhd->wd_timer_valid = TRUE; + /* Re arm the timer, at last watchdog period */ + mod_timer(&dhd->timer, jiffies + dhd_watchdog_ms * HZ / 1000); + } else if (dhd->wd_timer_valid == TRUE) { + /* Totally stop the timer */ + dhd->wd_timer_valid = FALSE; + del_timer_flag = TRUE; + } } - - /* Totally stop the timer */ - if (!wdtick && dhd->wd_timer_valid == TRUE) { + dhd_os_spin_unlock(pub, flags); + if (del_timer_flag) { del_timer_sync(&dhd->timer); - dhd->wd_timer_valid = FALSE; - save_dhd_watchdog_ms = wdtick; - return; - } - - if (wdtick) { - dhd_watchdog_ms = (uint)wdtick; - - /* Re arm the timer, at last watchdog period */ - mod_timer(&dhd->timer, jiffies + dhd_watchdog_ms * HZ / 1000); - - dhd->wd_timer_valid = TRUE; - save_dhd_watchdog_ms = wdtick; } } @@ -2491,7 +2774,7 @@ dhd_os_sdlock(dhd_pub_t *pub) dhd = (dhd_info_t *)(pub->info); if (dhd->threads_only) - down(&dhd->sdsem); + mutex_lock(&dhd->sdsem); else spin_lock_bh(&dhd->sdlock); } @@ -2504,7 +2787,7 @@ dhd_os_sdunlock(dhd_pub_t *pub) dhd = (dhd_info_t *)(pub->info); if (dhd->threads_only) - up(&dhd->sdsem); + mutex_unlock(&dhd->sdsem); else spin_unlock_bh(&dhd->sdlock); } @@ -2584,7 +2867,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, @@ -2601,8 +2884,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); } @@ -2641,23 +2934,79 @@ void dhd_wait_event_wakeup(dhd_pub_t *dhd) int dhd_dev_reset(struct net_device *dev, uint8 flag) { + int ret; + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); - /* Turning off watchdog */ - if (flag) - dhd_os_wd_timer(&dhd->pub, 0); + ret = dhd_bus_devreset(&dhd->pub, flag); + if (ret) { + DHD_ERROR(("%s: dhd_bus_devreset: %d\n", __FUNCTION__, ret)); + return ret; + } + DHD_ERROR(("%s: WLAN %s DONE\n", __FUNCTION__, flag ? "OFF" : "ON")); - dhd_bus_devreset(&dhd->pub, 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; + return ret; } +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) { @@ -2666,6 +3015,98 @@ 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 */ + +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; +} + +void dhd_bus_country_set(struct net_device *dev, char *country_code) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + + if (dhd && dhd->pub.up) + strncpy(dhd->pub.country_code, country_code, WLC_CNTRY_BUF_SZ); +} + +char *dhd_bus_country_get(struct net_device *dev) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + + if (dhd && (dhd->pub.country_code[0] != 0)) + return dhd->pub.country_code; + return NULL; +} + +void dhd_os_start_lock(dhd_pub_t *pub) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) + dhd_info_t *dhd = (dhd_info_t *)(pub->info); + + if (dhd) + mutex_lock(&dhd->wl_start_lock); +#endif +} + +void dhd_os_start_unlock(dhd_pub_t *pub) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) + dhd_info_t *dhd = (dhd_info_t *)(pub->info); + + if (dhd) + mutex_unlock(&dhd->wl_start_lock); +#endif +} + static int dhd_get_pend_8021x_cnt(dhd_info_t *dhd) { @@ -2694,6 +3135,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); @@ -2810,3 +3288,22 @@ int net_os_wake_unlock(struct net_device *dev) ret = dhd_os_wake_unlock(&dhd->pub); return ret; } + +unsigned long dhd_os_spin_lock(dhd_pub_t *pub) +{ + dhd_info_t *dhd = (dhd_info_t *)(pub->info); + unsigned long flags = 0; + + if (dhd) + spin_lock_irqsave(&dhd->dhd_lock, flags); + + return flags; +} + +void dhd_os_spin_unlock(dhd_pub_t *pub, unsigned long flags) +{ + dhd_info_t *dhd = (dhd_info_t *)(pub->info); + + if (dhd) + spin_unlock_irqrestore(&dhd->dhd_lock, flags); +} diff --git a/drivers/net/wireless/bcm4329/dhd_linux_sched.c b/drivers/net/wireless/bcm4329/dhd_linux_sched.c index 31891236..480b4166 100644 --- a/drivers/net/wireless/bcm4329/dhd_linux_sched.c +++ b/drivers/net/wireless/bcm4329/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/drivers/net/wireless/bcm4329/dhd_proto.h b/drivers/net/wireless/bcm4329/dhd_proto.h index 1e2401ac..7ef6929a 100644 --- a/drivers/net/wireless/bcm4329/dhd_proto.h +++ b/drivers/net/wireless/bcm4329/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 #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/drivers/net/wireless/bcm4329/dhd_sdio.c b/drivers/net/wireless/bcm4329/dhd_sdio.c index 5859c4a7..f9b9eceb 100644 --- a/drivers/net/wireless/bcm4329/dhd_sdio.c +++ b/drivers/net/wireless/bcm4329/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 @@ -36,14 +36,11 @@ #include #include #include - #include #include #include -#include #include #include - #include #include #include @@ -61,6 +58,13 @@ #include #include +#ifdef DHD_DEBUG +#include +#endif /* DHD_DEBUG */ +#ifdef DHD_DEBUG_TRAP +#include +#endif /* DHD_DEBUG_TRAP */ + #define QLEN 256 /* bulk rx and tx queue lengths */ #define FCHI (QLEN - 10) #define FCLOW (FCHI / 2) @@ -120,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 */ @@ -142,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 { @@ -209,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 */ @@ -405,7 +424,9 @@ 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); +#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); @@ -416,7 +437,7 @@ static bool dhdsdio_probe_attach(dhd_bus_t *bus, osl_t *osh, void *sdh, void * regsva, uint16 devid); static bool dhdsdio_probe_malloc(dhd_bus_t *bus, osl_t *osh, void *sdh); static bool dhdsdio_probe_init(dhd_bus_t *bus, osl_t *osh, void *sdh); -static void dhdsdio_release_dongle(dhd_bus_t *bus, osl_t *osh); +static void dhdsdio_release_dongle(dhd_bus_t *bus, osl_t *osh, int reset_flag); static uint process_nvram_vars(char *varbuf, uint len); @@ -683,6 +704,7 @@ dhdsdio_sdclk(dhd_bus_t *bus, bool on) static int dhdsdio_clkctl(dhd_bus_t *bus, uint target, bool pendok) { + int ret = BCME_OK; #ifdef DHD_DEBUG uint oldstate = bus->clkstate; #endif /* DHD_DEBUG */ @@ -695,7 +717,7 @@ dhdsdio_clkctl(dhd_bus_t *bus, uint target, bool pendok) dhd_os_wd_timer(bus->dhd, dhd_watchdog_ms); bus->activity = TRUE; } - return BCME_OK; + return ret; } switch (target) { @@ -704,29 +726,32 @@ dhdsdio_clkctl(dhd_bus_t *bus, uint target, bool pendok) if (bus->clkstate == CLK_NONE) dhdsdio_sdclk(bus, TRUE); /* Now request HT Avail on the backplane */ - dhdsdio_htclk(bus, TRUE, pendok); - dhd_os_wd_timer(bus->dhd, dhd_watchdog_ms); - bus->activity = TRUE; + ret = dhdsdio_htclk(bus, TRUE, pendok); + if (ret == BCME_OK) { + dhd_os_wd_timer(bus->dhd, dhd_watchdog_ms); + bus->activity = TRUE; + } break; case CLK_SDONLY: /* Remove HT request, or bring up SD clock */ if (bus->clkstate == CLK_NONE) - dhdsdio_sdclk(bus, TRUE); + ret = dhdsdio_sdclk(bus, TRUE); else if (bus->clkstate == CLK_AVAIL) - dhdsdio_htclk(bus, FALSE, FALSE); + ret = dhdsdio_htclk(bus, FALSE, FALSE); else DHD_ERROR(("dhdsdio_clkctl: request for %d -> %d\n", bus->clkstate, target)); - dhd_os_wd_timer(bus->dhd, dhd_watchdog_ms); + if (ret == BCME_OK) + dhd_os_wd_timer(bus->dhd, dhd_watchdog_ms); break; case CLK_NONE: /* Make sure to remove HT request */ if (bus->clkstate == CLK_AVAIL) - dhdsdio_htclk(bus, FALSE, FALSE); + ret = dhdsdio_htclk(bus, FALSE, FALSE); /* Now remove the SD clock */ - dhdsdio_sdclk(bus, FALSE); + ret = dhdsdio_sdclk(bus, FALSE); dhd_os_wd_timer(bus->dhd, 0); break; } @@ -734,7 +759,7 @@ dhdsdio_clkctl(dhd_bus_t *bus, uint target, bool pendok) DHD_INFO(("dhdsdio_clkctl: %d -> %d\n", oldstate, bus->clkstate)); #endif /* DHD_DEBUG */ - return BCME_OK; + return ret; } int @@ -931,7 +956,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)]++; @@ -997,6 +1021,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: @@ -1048,7 +1075,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++; @@ -1084,6 +1111,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 @@ -1238,6 +1266,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; @@ -1245,15 +1275,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); @@ -1263,6 +1294,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); @@ -1291,6 +1323,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); } @@ -1335,17 +1370,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) @@ -1365,7 +1404,9 @@ enum { IOV_SDCIS, IOV_MEMBYTES, IOV_MEMSIZE, +#ifdef DHD_DEBUG_TRAP IOV_CHECKDIED, +#endif IOV_DOWNLOAD, IOV_FORCEEVEN, IOV_SDIOD_DRIVE, @@ -1416,8 +1457,10 @@ const bcm_iovar_t dhdsdio_iovars[] = { {"rxbound", IOV_RXBOUND, 0, IOVT_UINT32, 0 }, {"txminmax", IOV_TXMINMAX, 0, IOVT_UINT32, 0 }, {"cpu", IOV_CPU, 0, IOVT_BOOL, 0 }, - {"checkdied", IOV_CHECKDIED, 0, IOVT_BUFFER, 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) }, @@ -1646,6 +1689,7 @@ xfer_done: return bcmerror; } +#ifdef DHD_DEBUG_TRAP static int dhdsdio_readshared(dhd_bus_t *bus, sdpcm_shared_t *sh) { @@ -1806,6 +1850,81 @@ done: return bcmerror; } +#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) @@ -2544,6 +2663,9 @@ dhd_bus_stop(struct dhd_bus *bus, bool enforce_mutex) BUS_WAKE(bus); + /* Change our idea of bus state */ + bus->dhd->busstate = DHD_BUS_DOWN; + /* Enable clock for device interrupts */ dhdsdio_clkctl(bus, CLK_AVAIL, FALSE); @@ -2552,9 +2674,6 @@ dhd_bus_stop(struct dhd_bus *bus, bool enforce_mutex) local_hostintmask = bus->hostintmask; bus->hostintmask = 0; - /* Change our idea of bus state */ - bus->dhd->busstate = DHD_BUS_DOWN; - /* Force clocks on backplane to be sure F2 interrupt propagates */ saveclk = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, &err); if (!err) { @@ -2607,23 +2726,24 @@ dhd_bus_init(dhd_pub_t *dhdp, bool enforce_mutex) dhd_timeout_t tmo; uint retries = 0; uint8 ready, enable; - int err, ret = 0; + int err, ret = BCME_ERROR; uint8 saveclk; DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ASSERT(bus->dhd); if (!bus->dhd) - return 0; + return BCME_OK; if (enforce_mutex) dhd_os_sdlock(bus->dhd); /* Make sure backplane clock is on, needed to generate F2 interrupt */ - dhdsdio_clkctl(bus, CLK_AVAIL, FALSE); - if (bus->clkstate != CLK_AVAIL) + err = dhdsdio_clkctl(bus, CLK_AVAIL, FALSE); + if ((err != BCME_OK) || (bus->clkstate != CLK_AVAIL)) { + DHD_ERROR(("%s: Failed to set backplane clock: err %d\n", __FUNCTION__, err)); goto exit; - + } /* Force clocks on backplane to be sure F2 interrupt propagates */ saveclk = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, &err); @@ -2698,6 +2818,7 @@ dhd_bus_init(dhd_pub_t *dhdp, bool enforce_mutex) if (dhdp->busstate != DHD_BUS_DATA) dhdsdio_clkctl(bus, CLK_NONE, FALSE); + ret = BCME_OK; exit: if (enforce_mutex) dhd_os_sdunlock(bus->dhd); @@ -4051,10 +4172,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, @@ -4086,13 +4210,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); @@ -4107,7 +4234,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 */ @@ -4131,7 +4260,6 @@ clkwait: bool dhd_bus_dpc(struct dhd_bus *bus) { -#ifdef SDIO_ISR_THREAD bool resched; /* Call the DPC directly. */ @@ -4139,9 +4267,6 @@ dhd_bus_dpc(struct dhd_bus *bus) resched = dhdsdio_dpc(bus); return resched; -#else - return dhdsdio_dpc(bus); -#endif /* SDIO_ISR_THREAD */ } void @@ -4150,6 +4275,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; @@ -4160,9 +4287,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; @@ -4453,8 +4577,6 @@ dhd_bus_watchdog(dhd_pub_t *dhdp) if (bus->sleeping) return FALSE; - dhd_os_sdlock(bus->dhd); - /* Poll period: check device if appropriate. */ if (bus->poll && (++bus->polltick >= bus->pollrate)) { uint32 intstatus = 0; @@ -4489,6 +4611,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 */ @@ -4506,17 +4641,76 @@ dhd_bus_watchdog(dhd_pub_t *dhdp) bus->idlecount = 0; if (bus->activity) { bus->activity = FALSE; - } else { dhdsdio_clkctl(bus, CLK_NONE, FALSE); } } } - dhd_os_sdunlock(bus->dhd); - 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 @@ -5047,7 +5241,7 @@ dhdsdio_release(dhd_bus_t *bus, osl_t *osh) if (bus->dhd) { - dhdsdio_release_dongle(bus, osh); + dhdsdio_release_dongle(bus, osh, TRUE); dhd_detach(bus->dhd); bus->dhd = NULL; @@ -5091,11 +5285,11 @@ dhdsdio_release_malloc(dhd_bus_t *bus, osl_t *osh) static void -dhdsdio_release_dongle(dhd_bus_t *bus, osl_t *osh) +dhdsdio_release_dongle(dhd_bus_t *bus, osl_t *osh, int reset_flag) { DHD_TRACE(("%s: Enter\n", __FUNCTION__)); - if (bus->dhd && bus->dhd->dongle_reset) + if ((bus->dhd && bus->dhd->dongle_reset) && reset_flag) return; if (bus->sih) { @@ -5545,24 +5739,23 @@ dhd_bus_devreset(dhd_pub_t *dhdp, uint8 flag) if (flag == TRUE) { if (!bus->dhd->dongle_reset) { + dhd_os_sdlock(dhdp); + /* Turning off watchdog */ + dhd_os_wd_timer(dhdp, 0); #if !defined(IGNORE_ETH0_DOWN) /* Force flow control as protection when stop come before ifconfig_down */ dhd_txflowcontrol(bus->dhd, 0, ON); #endif /* !defined(IGNORE_ETH0_DOWN) */ - /* save country settinng if was pre-setup with priv ioctl */ - dhd_os_proto_block(dhdp); - dhdcdc_query_ioctl(bus->dhd, 0, WLC_GET_COUNTRY, - bus->dhd->country_code, sizeof(bus->dhd->country_code)); - dhd_os_proto_unblock(dhdp); /* Expect app to have torn down any connection before calling */ /* Stop the bus, disable F2 */ dhd_bus_stop(bus, FALSE); /* Clean tx/rx buffer pointers, detach from the dongle */ - dhdsdio_release_dongle(bus, bus->dhd->osh); + dhdsdio_release_dongle(bus, bus->dhd->osh, TRUE); bus->dhd->dongle_reset = TRUE; bus->dhd->up = FALSE; + dhd_os_sdunlock(dhdp); DHD_TRACE(("%s: WLAN OFF DONE\n", __FUNCTION__)); /* App can now remove power from device */ @@ -5575,6 +5768,8 @@ dhd_bus_devreset(dhd_pub_t *dhdp, uint8 flag) if (bus->dhd->dongle_reset) { /* Turn on WLAN */ + dhd_os_sdlock(dhdp); + /* Reset SD client */ bcmsdh_reset(bus->sdh); @@ -5587,25 +5782,31 @@ dhd_bus_devreset(dhd_pub_t *dhdp, uint8 flag) dhdsdio_download_firmware(bus, bus->dhd->osh, bus->sdh)) { /* Re-init bus, enable F2 transfer */ - dhd_bus_init((dhd_pub_t *) bus->dhd, FALSE); - + bcmerror = dhd_bus_init((dhd_pub_t *) bus->dhd, FALSE); + if (bcmerror == BCME_OK) { #if defined(OOB_INTR_ONLY) - dhd_enable_oob_intr(bus, TRUE); + dhd_enable_oob_intr(bus, TRUE); #endif /* defined(OOB_INTR_ONLY) */ - - bus->dhd->dongle_reset = FALSE; - bus->dhd->up = TRUE; - + bus->dhd->dongle_reset = FALSE; + bus->dhd->up = TRUE; #if !defined(IGNORE_ETH0_DOWN) - /* Restore flow control */ - dhd_txflowcontrol(bus->dhd, 0, OFF); -#endif + /* Restore flow control */ + dhd_txflowcontrol(bus->dhd, 0, OFF); +#endif + /* Turning on watchdog back */ + dhd_os_wd_timer(dhdp, dhd_watchdog_ms); - DHD_TRACE(("%s: WLAN ON DONE\n", __FUNCTION__)); + DHD_TRACE(("%s: WLAN ON DONE\n", __FUNCTION__)); + } else { + dhd_bus_stop(bus, FALSE); + dhdsdio_release_dongle(bus, bus->dhd->osh, FALSE); + } } else bcmerror = BCME_SDIO_ERROR; } else bcmerror = BCME_SDIO_ERROR; + + dhd_os_sdunlock(dhdp); } else { bcmerror = BCME_NOTDOWN; DHD_ERROR(("%s: Set DEVRESET=FALSE invoked when device is on\n", diff --git a/drivers/net/wireless/bcm4329/dngl_stats.h b/drivers/net/wireless/bcm4329/dngl_stats.h index c1776a82..e5db54e7 100644 --- a/drivers/net/wireless/bcm4329/dngl_stats.h +++ b/drivers/net/wireless/bcm4329/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/drivers/net/wireless/bcm4329/hndpmu.c b/drivers/net/wireless/bcm4329/hndpmu.c index 25712ac8..307347a4 100644 --- a/drivers/net/wireless/bcm4329/hndpmu.c +++ b/drivers/net/wireless/bcm4329/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 diff --git a/drivers/net/wireless/bcm4329/include/aidmp.h b/drivers/net/wireless/bcm4329/include/aidmp.h index 44b3de86..a927e5da 100644 --- a/drivers/net/wireless/bcm4329/include/aidmp.h +++ b/drivers/net/wireless/bcm4329/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/drivers/net/wireless/bcm4329/include/bcmcdc.h b/drivers/net/wireless/bcm4329/include/bcmcdc.h index 641d7558..c2a860be 100644 --- a/drivers/net/wireless/bcm4329/include/bcmcdc.h +++ b/drivers/net/wireless/bcm4329/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/drivers/net/wireless/bcm4329/include/bcmdevs.h b/drivers/net/wireless/bcm4329/include/bcmdevs.h index acfd4147..14853f17 100644 --- a/drivers/net/wireless/bcm4329/include/bcmdevs.h +++ b/drivers/net/wireless/bcm4329/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/drivers/net/wireless/bcm4329/include/bcmendian.h b/drivers/net/wireless/bcm4329/include/bcmendian.h index 38887df5..ae468383 100644 --- a/drivers/net/wireless/bcm4329/include/bcmendian.h +++ b/drivers/net/wireless/bcm4329/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/drivers/net/wireless/bcm4329/include/bcmpcispi.h b/drivers/net/wireless/bcm4329/include/bcmpcispi.h index e3be8261..7d98fb7c 100644 --- a/drivers/net/wireless/bcm4329/include/bcmpcispi.h +++ b/drivers/net/wireless/bcm4329/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/drivers/net/wireless/bcm4329/include/bcmperf.h b/drivers/net/wireless/bcm4329/include/bcmperf.h index dfc3f441..2a78784e 100644 --- a/drivers/net/wireless/bcm4329/include/bcmperf.h +++ b/drivers/net/wireless/bcm4329/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/drivers/net/wireless/bcm4329/include/bcmsdbus.h b/drivers/net/wireless/bcm4329/include/bcmsdbus.h index 0e629c04..b7b67bc6 100644 --- a/drivers/net/wireless/bcm4329/include/bcmsdbus.h +++ b/drivers/net/wireless/bcm4329/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/drivers/net/wireless/bcm4329/include/bcmsdh.h b/drivers/net/wireless/bcm4329/include/bcmsdh.h index f8ab8ab3..f5dee5c5 100644 --- a/drivers/net/wireless/bcm4329/include/bcmsdh.h +++ b/drivers/net/wireless/bcm4329/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/drivers/net/wireless/bcm4329/include/bcmsdh_sdmmc.h b/drivers/net/wireless/bcm4329/include/bcmsdh_sdmmc.h index b572f340..4e6d1b5b 100644 --- a/drivers/net/wireless/bcm4329/include/bcmsdh_sdmmc.h +++ b/drivers/net/wireless/bcm4329/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/drivers/net/wireless/bcm4329/include/bcmsdpcm.h b/drivers/net/wireless/bcm4329/include/bcmsdpcm.h index b63b1d39..77aca450 100644 --- a/drivers/net/wireless/bcm4329/include/bcmsdpcm.h +++ b/drivers/net/wireless/bcm4329/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/drivers/net/wireless/bcm4329/include/bcmsdspi.h b/drivers/net/wireless/bcm4329/include/bcmsdspi.h index b1e2be94..eaae10d8 100644 --- a/drivers/net/wireless/bcm4329/include/bcmsdspi.h +++ b/drivers/net/wireless/bcm4329/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/drivers/net/wireless/bcm4329/include/bcmspi.h b/drivers/net/wireless/bcm4329/include/bcmspi.h index 0c46538e..2e2bc935 100644 --- a/drivers/net/wireless/bcm4329/include/bcmspi.h +++ b/drivers/net/wireless/bcm4329/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/drivers/net/wireless/bcm4329/include/bcmspibrcm.h b/drivers/net/wireless/bcm4329/include/bcmspibrcm.h new file mode 100644 index 00000000..9dce878d --- /dev/null +++ b/drivers/net/wireless/bcm4329/include/bcmspibrcm.h @@ -0,0 +1,134 @@ +/* + * SD-SPI Protocol Conversion - BCMSDH->gSPI Translation Layer + * + * Copyright (C) 2010, Broadcom Corporation + * All Rights Reserved. + * + * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Broadcom Corporation; + * the contents of this file may not be disclosed to third parties, copied + * or duplicated in any form, in whole or in part, without the prior + * written permission of Broadcom Corporation. + * + * $Id: bcmspibrcm.h,v 1.4.4.1.4.3.6.1 2008/09/27 17:03:25 Exp $ + */ + +/* global msglevel for debug messages - bitvals come from sdiovar.h */ + +#define sd_err(x) +#define sd_trace(x) +#define sd_info(x) +#define sd_debug(x) +#define sd_data(x) +#define sd_ctrl(x) + +#define sd_log(x) + +#define SDIOH_ASSERT(exp) \ + do { if (!(exp)) \ + printf("!!!ASSERT fail: file %s lines %d", __FILE__, __LINE__); \ + } while (0) + +#define BLOCK_SIZE_F1 64 +#define BLOCK_SIZE_F2 2048 +#define BLOCK_SIZE_F3 2048 + +/* internal return code */ +#define SUCCESS 0 +#undef ERROR +#define ERROR 1 +#define ERROR_UF 2 +#define ERROR_OF 3 + +/* private bus modes */ +#define SDIOH_MODE_SPI 0 + +#define USE_BLOCKMODE 0x2 /* Block mode can be single block or multi */ +#define USE_MULTIBLOCK 0x4 + +struct sdioh_info { + uint cfg_bar; /* pci cfg address for bar */ + uint32 caps; /* cached value of capabilities reg */ + void *bar0; /* BAR0 for PCI Device */ + osl_t *osh; /* osh handler */ + void *controller; /* Pointer to SPI Controller's private data struct */ + + uint lockcount; /* nest count of spi_lock() calls */ + bool client_intr_enabled; /* interrupt connnected flag */ + bool intr_handler_valid; /* client driver interrupt handler valid */ + sdioh_cb_fn_t intr_handler; /* registered interrupt handler */ + void *intr_handler_arg; /* argument to call interrupt handler */ + bool initialized; /* card initialized */ + uint32 target_dev; /* Target device ID */ + uint32 intmask; /* Current active interrupts */ + void *sdos_info; /* Pointer to per-OS private data */ + + uint32 controller_type; /* Host controller type */ + uint8 version; /* Host Controller Spec Compliance Version */ + uint irq; /* Client irq */ + uint32 intrcount; /* Client interrupts */ + uint32 local_intrcount; /* Controller interrupts */ + bool host_init_done; /* Controller initted */ + bool card_init_done; /* Client SDIO interface initted */ + bool polled_mode; /* polling for command completion */ + + bool sd_use_dma; /* DMA on CMD53 */ + bool sd_blockmode; /* sd_blockmode == FALSE => 64 Byte Cmd 53s. */ + /* Must be on for sd_multiblock to be effective */ + bool use_client_ints; /* If this is false, make sure to restore */ + /* polling hack in wl_linux.c:wl_timer() */ + int adapter_slot; /* Maybe dealing with multiple slots/controllers */ + int sd_mode; /* SD1/SD4/SPI */ + int client_block_size[SPI_MAX_IOFUNCS]; /* Blocksize */ + uint32 data_xfer_count; /* Current transfer */ + uint16 card_rca; /* Current Address */ + uint8 num_funcs; /* Supported funcs on client */ + uint32 card_dstatus; /* 32bit device status */ + uint32 com_cis_ptr; + uint32 func_cis_ptr[SPI_MAX_IOFUNCS]; + void *dma_buf; + ulong dma_phys; + int r_cnt; /* rx count */ + int t_cnt; /* tx_count */ + uint32 wordlen; /* host processor 16/32bits */ + uint32 prev_fun; + uint32 chip; + uint32 chiprev; + bool resp_delay_all; + bool dwordmode; + + struct spierrstats_t spierrstats; +}; + +/************************************************************ + * Internal interfaces: per-port references into bcmspibrcm.c + */ + +/* Global message bits */ +extern uint sd_msglevel; + +/************************************************************** + * Internal interfaces: bcmspibrcm.c references to per-port code + */ + +/* Interrupt (de)registration routines */ +extern int spi_register_irq(sdioh_info_t *sd, uint irq); +extern void spi_free_irq(uint irq, sdioh_info_t *sd); + +/* OS-specific interrupt wrappers (atomic interrupt enable/disable) */ +extern void spi_lock(sdioh_info_t *sd); +extern void spi_unlock(sdioh_info_t *sd); + +/* Allocate/init/free per-OS private data */ +extern int spi_osinit(sdioh_info_t *sd); +extern void spi_osfree(sdioh_info_t *sd); + +#define SPI_RW_FLAG_M BITFIELD_MASK(1) /* Bit [31] - R/W Command Bit */ +#define SPI_RW_FLAG_S 31 +#define SPI_ACCESS_M BITFIELD_MASK(1) /* Bit [30] - Fixed/Incr Access */ +#define SPI_ACCESS_S 30 +#define SPI_FUNCTION_M BITFIELD_MASK(2) /* Bit [29:28] - Function Number */ +#define SPI_FUNCTION_S 28 +#define SPI_REG_ADDR_M BITFIELD_MASK(17) /* Bit [27:11] - Address */ +#define SPI_REG_ADDR_S 11 +#define SPI_LEN_M BITFIELD_MASK(11) /* Bit [10:0] - Packet length */ +#define SPI_LEN_S 0 diff --git a/drivers/net/wireless/bcm4329/include/bcmutils.h b/drivers/net/wireless/bcm4329/include/bcmutils.h index 70108382..f85ed351 100644 --- a/drivers/net/wireless/bcm4329/include/bcmutils.h +++ b/drivers/net/wireless/bcm4329/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/drivers/net/wireless/bcm4329/include/bcmwifi.h b/drivers/net/wireless/bcm4329/include/bcmwifi.h index 52f17a65..038aedcd 100644 --- a/drivers/net/wireless/bcm4329/include/bcmwifi.h +++ b/drivers/net/wireless/bcm4329/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/drivers/net/wireless/bcm4329/include/dhdioctl.h b/drivers/net/wireless/bcm4329/include/dhdioctl.h index ee78c3d1..980a1430 100644 --- a/drivers/net/wireless/bcm4329/include/dhdioctl.h +++ b/drivers/net/wireless/bcm4329/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/drivers/net/wireless/bcm4329/include/epivers.h b/drivers/net/wireless/bcm4329/include/epivers.h index 3ab7ad74..00e3cac1 100644 --- a/drivers/net/wireless/bcm4329/include/epivers.h +++ b/drivers/net/wireless/bcm4329/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 20 #define EPI_BUILD_NUMBER 0 -#define EPI_VERSION 4, 218, 223, 0 +#define EPI_VERSION 4, 218, 248, 20 -#define EPI_VERSION_NUM 0x04dadf00 +#define EPI_VERSION_NUM 0x04daf814 -#define EPI_VERSION_STR "4.218.223.0" -#define EPI_ROUTER_VERSION_STR "4.219.223.0" +#define EPI_VERSION_STR "4.218.248.20" +#define EPI_ROUTER_VERSION_STR "4.219.248.20" #endif diff --git a/drivers/net/wireless/bcm4329/include/hndpmu.h b/drivers/net/wireless/bcm4329/include/hndpmu.h index afd78d7c..e829b3df 100644 --- a/drivers/net/wireless/bcm4329/include/hndpmu.h +++ b/drivers/net/wireless/bcm4329/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/drivers/net/wireless/bcm4329/include/hndrte_armtrap.h b/drivers/net/wireless/bcm4329/include/hndrte_armtrap.h index 51409018..ca3281b6 100644 --- a/drivers/net/wireless/bcm4329/include/hndrte_armtrap.h +++ b/drivers/net/wireless/bcm4329/include/hndrte_armtrap.h @@ -2,13 +2,13 @@ * 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 @@ -16,12 +16,12 @@ * the license of that module. An independent module is a module which is not * derived from this software. The special exception does not apply to any * modifications of the software. - * + * * Notwithstanding the above, under no circumstances may you combine this * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: hndrte_armtrap.h,v 13.3.196.1 2009/07/13 23:35:44 Exp $ + * $Id: hndrte_armtrap.h,v 13.3.196.2 2010/07/15 19:06:11 Exp $ */ #ifndef _hndrte_armtrap_h diff --git a/drivers/net/wireless/bcm4329/include/hndrte_cons.h b/drivers/net/wireless/bcm4329/include/hndrte_cons.h new file mode 100644 index 00000000..a4241747 --- /dev/null +++ b/drivers/net/wireless/bcm4329/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 + +#define CBUF_LEN (128) + +#define LOG_BUF_LEN 1024 + +typedef struct { + uint32 buf; /* Can't be pointer on (64-bit) hosts */ + uint buf_size; + uint idx; + char *_buf_compat; /* Redundant pointer for backward compat. */ +} hndrte_log_t; + +typedef struct { + /* Virtual UART + * When there is no UART (e.g. Quickturn), the host should write a complete + * input line directly into cbuf and then write the length into vcons_in. + * This may also be used when there is a real UART (at risk of conflicting with + * the real UART). vcons_out is currently unused. + */ + volatile uint vcons_in; + volatile uint vcons_out; + + /* Output (logging) buffer + * Console output is written to a ring buffer log_buf at index log_idx. + * The host may read the output when it sees log_idx advance. + * Output will be lost if the output wraps around faster than the host polls. + */ + hndrte_log_t log; + + /* Console input line buffer + * Characters are read one at a time into cbuf until is received, then + * the buffer is processed as a command line. Also used for virtual UART. + */ + uint cbuf_idx; + char cbuf[CBUF_LEN]; +} hndrte_cons_t; diff --git a/drivers/net/wireless/bcm4329/include/hndsoc.h b/drivers/net/wireless/bcm4329/include/hndsoc.h index dcbab431..35424175 100644 --- a/drivers/net/wireless/bcm4329/include/hndsoc.h +++ b/drivers/net/wireless/bcm4329/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/drivers/net/wireless/bcm4329/include/linux_osl.h b/drivers/net/wireless/bcm4329/include/linux_osl.h index 98afe9c0..b059c2ad 100644 --- a/drivers/net/wireless/bcm4329/include/linux_osl.h +++ b/drivers/net/wireless/bcm4329/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/drivers/net/wireless/bcm4329/include/linuxver.h b/drivers/net/wireless/bcm4329/include/linuxver.h index a36de951..6ed22658 100644 --- a/drivers/net/wireless/bcm4329/include/linuxver.h +++ b/drivers/net/wireless/bcm4329/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 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)) #include -#else +#elif (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33)) #include #endif #include @@ -66,6 +66,7 @@ #include #include #include +#include #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) #undef IP_TOS #endif diff --git a/drivers/net/wireless/bcm4329/include/miniopt.h b/drivers/net/wireless/bcm4329/include/miniopt.h index 91e66030..3667fb1e 100644 --- a/drivers/net/wireless/bcm4329/include/miniopt.h +++ b/drivers/net/wireless/bcm4329/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/drivers/net/wireless/bcm4329/include/msgtrace.h b/drivers/net/wireless/bcm4329/include/msgtrace.h index d79a89ec..1479086d 100644 --- a/drivers/net/wireless/bcm4329/include/msgtrace.h +++ b/drivers/net/wireless/bcm4329/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/drivers/net/wireless/bcm4329/include/osl.h b/drivers/net/wireless/bcm4329/include/osl.h index 02b2b21d..5599e536 100644 --- a/drivers/net/wireless/bcm4329/include/osl.h +++ b/drivers/net/wireless/bcm4329/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/drivers/net/wireless/bcm4329/include/packed_section_end.h b/drivers/net/wireless/bcm4329/include/packed_section_end.h index e455d691..5b61c18f 100644 --- a/drivers/net/wireless/bcm4329/include/packed_section_end.h +++ b/drivers/net/wireless/bcm4329/include/packed_section_end.h @@ -15,7 +15,7 @@ * #include * * - * 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/drivers/net/wireless/bcm4329/include/packed_section_start.h b/drivers/net/wireless/bcm4329/include/packed_section_start.h index a15562e3..cb93aa64 100644 --- a/drivers/net/wireless/bcm4329/include/packed_section_start.h +++ b/drivers/net/wireless/bcm4329/include/packed_section_start.h @@ -15,7 +15,7 @@ * #include * * - * 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/drivers/net/wireless/bcm4329/include/pcicfg.h b/drivers/net/wireless/bcm4329/include/pcicfg.h index 61c2f4a6..898962c9 100644 --- a/drivers/net/wireless/bcm4329/include/pcicfg.h +++ b/drivers/net/wireless/bcm4329/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/drivers/net/wireless/bcm4329/include/proto/802.11.h b/drivers/net/wireless/bcm4329/include/proto/802.11.h index 3ef7de2f..fd263173 100644 --- a/drivers/net/wireless/bcm4329/include/proto/802.11.h +++ b/drivers/net/wireless/bcm4329/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 diff --git a/drivers/net/wireless/bcm4329/include/proto/802.11e.h b/drivers/net/wireless/bcm4329/include/proto/802.11e.h index 0c535300..1dd6f45b 100644 --- a/drivers/net/wireless/bcm4329/include/proto/802.11e.h +++ b/drivers/net/wireless/bcm4329/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/drivers/net/wireless/bcm4329/include/proto/802.1d.h b/drivers/net/wireless/bcm4329/include/proto/802.1d.h index efd71173..45c728bc 100644 --- a/drivers/net/wireless/bcm4329/include/proto/802.1d.h +++ b/drivers/net/wireless/bcm4329/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/drivers/net/wireless/bcm4329/include/proto/bcmeth.h b/drivers/net/wireless/bcm4329/include/proto/bcmeth.h index bd34a0ba..fdb5a2a5 100644 --- a/drivers/net/wireless/bcm4329/include/proto/bcmeth.h +++ b/drivers/net/wireless/bcm4329/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/drivers/net/wireless/bcm4329/include/proto/bcmevent.h b/drivers/net/wireless/bcm4329/include/proto/bcmevent.h index 1791478a..1f8ecb14 100644 --- a/drivers/net/wireless/bcm4329/include/proto/bcmevent.h +++ b/drivers/net/wireless/bcm4329/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 @@ -24,7 +24,7 @@ * * Dependencies: proto/bcmeth.h * - * $Id: bcmevent.h,v 9.34.4.1.20.16 2009/09/25 23:52:38 Exp $ + * $Id: bcmevent.h,v 9.34.4.1.20.16.64.1 2010/11/08 21:57:03 Exp $ * */ @@ -131,10 +131,10 @@ typedef BWL_PRE_PACKED_STRUCT struct bcm_event { #define WLC_E_ACTION_FRAME 58 #define WLC_E_ACTION_FRAME_COMPLETE 59 -#define WLC_E_ESCAN_RESULT 69 -#define WLC_E_WAKE_EVENT 70 -#define WLC_E_LAST 71 - +#define WLC_E_ESCAN_RESULT 69 +#define WLC_E_WAKE_EVENT 70 +#define WLC_E_RELOAD 71 +#define WLC_E_LAST 72 @@ -205,6 +205,7 @@ typedef BWL_PRE_PACKED_STRUCT struct bcm_event { #define WLC_E_IF_ADD 1 #define WLC_E_IF_DEL 2 +#define WLC_E_RELOAD_STATUS1 1 #include diff --git a/drivers/net/wireless/bcm4329/include/proto/bcmip.h b/drivers/net/wireless/bcm4329/include/proto/bcmip.h index 9645016c..9d2fd6fb 100644 --- a/drivers/net/wireless/bcm4329/include/proto/bcmip.h +++ b/drivers/net/wireless/bcm4329/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/drivers/net/wireless/bcm4329/include/proto/ethernet.h b/drivers/net/wireless/bcm4329/include/proto/ethernet.h index 05530d51..9ad2ea0c 100644 --- a/drivers/net/wireless/bcm4329/include/proto/ethernet.h +++ b/drivers/net/wireless/bcm4329/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/drivers/net/wireless/bcm4329/include/proto/sdspi.h b/drivers/net/wireless/bcm4329/include/proto/sdspi.h index 3c7bcf33..7739e68a 100644 --- a/drivers/net/wireless/bcm4329/include/proto/sdspi.h +++ b/drivers/net/wireless/bcm4329/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/drivers/net/wireless/bcm4329/include/proto/vlan.h b/drivers/net/wireless/bcm4329/include/proto/vlan.h index 2dda4d5e..670bc44c 100644 --- a/drivers/net/wireless/bcm4329/include/proto/vlan.h +++ b/drivers/net/wireless/bcm4329/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/drivers/net/wireless/bcm4329/include/proto/wpa.h b/drivers/net/wireless/bcm4329/include/proto/wpa.h index 4c50125d..f5d0cd53 100644 --- a/drivers/net/wireless/bcm4329/include/proto/wpa.h +++ b/drivers/net/wireless/bcm4329/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/drivers/net/wireless/bcm4329/include/sbconfig.h b/drivers/net/wireless/bcm4329/include/sbconfig.h index edf497cb..da18ccbe 100644 --- a/drivers/net/wireless/bcm4329/include/sbconfig.h +++ b/drivers/net/wireless/bcm4329/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/drivers/net/wireless/bcm4329/include/sbhnddma.h b/drivers/net/wireless/bcm4329/include/sbhnddma.h index 2aeee9e7..7681395f 100644 --- a/drivers/net/wireless/bcm4329/include/sbhnddma.h +++ b/drivers/net/wireless/bcm4329/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/drivers/net/wireless/bcm4329/include/sbpcmcia.h b/drivers/net/wireless/bcm4329/include/sbpcmcia.h index 121b1a95..d6d80334 100644 --- a/drivers/net/wireless/bcm4329/include/sbpcmcia.h +++ b/drivers/net/wireless/bcm4329/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/drivers/net/wireless/bcm4329/include/sbsdio.h b/drivers/net/wireless/bcm4329/include/sbsdio.h index 43ed6b55..75aaf4d8 100644 --- a/drivers/net/wireless/bcm4329/include/sbsdio.h +++ b/drivers/net/wireless/bcm4329/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/drivers/net/wireless/bcm4329/include/sbsdpcmdev.h b/drivers/net/wireless/bcm4329/include/sbsdpcmdev.h index 57e832de..7c7c7e4d 100644 --- a/drivers/net/wireless/bcm4329/include/sbsdpcmdev.h +++ b/drivers/net/wireless/bcm4329/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/drivers/net/wireless/bcm4329/include/sbsocram.h b/drivers/net/wireless/bcm4329/include/sbsocram.h index 170e8f29..5ede0b66 100644 --- a/drivers/net/wireless/bcm4329/include/sbsocram.h +++ b/drivers/net/wireless/bcm4329/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/drivers/net/wireless/bcm4329/include/sdio.h b/drivers/net/wireless/bcm4329/include/sdio.h index d20d69ca..280cb845 100644 --- a/drivers/net/wireless/bcm4329/include/sdio.h +++ b/drivers/net/wireless/bcm4329/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/drivers/net/wireless/bcm4329/include/siutils.h b/drivers/net/wireless/bcm4329/include/siutils.h index c55293b8..cb9f1407 100644 --- a/drivers/net/wireless/bcm4329/include/siutils.h +++ b/drivers/net/wireless/bcm4329/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/drivers/net/wireless/bcm4329/include/spid.h b/drivers/net/wireless/bcm4329/include/spid.h new file mode 100644 index 00000000..c740296d --- /dev/null +++ b/drivers/net/wireless/bcm4329/include/spid.h @@ -0,0 +1,153 @@ +/* + * SPI device spec header file + * + * Copyright (C) 2010, Broadcom Corporation + * All Rights Reserved. + * + * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Broadcom Corporation; + * the contents of this file may not be disclosed to third parties, copied + * or duplicated in any form, in whole or in part, without the prior + * written permission of Broadcom Corporation. + * + * $Id: spid.h,v 1.7.10.1.16.3 2009/04/09 19:23:14 Exp $ + */ + +#ifndef _SPI_H +#define _SPI_H + +/* + * Brcm SPI Device Register Map. + * + */ + +typedef volatile struct { + uint8 config; /* 0x00, len, endian, clock, speed, polarity, wakeup */ + uint8 response_delay; /* 0x01, read response delay in bytes (corerev < 3) */ + uint8 status_enable; /* 0x02, status-enable, intr with status, response_delay + * function selection, command/data error check + */ + uint8 reset_bp; /* 0x03, reset on wlan/bt backplane reset (corerev >= 1) */ + uint16 intr_reg; /* 0x04, Intr status register */ + uint16 intr_en_reg; /* 0x06, Intr mask register */ + uint32 status_reg; /* 0x08, RO, Status bits of last spi transfer */ + uint16 f1_info_reg; /* 0x0c, RO, enabled, ready for data transfer, blocksize */ + uint16 f2_info_reg; /* 0x0e, RO, enabled, ready for data transfer, blocksize */ + uint16 f3_info_reg; /* 0x10, RO, enabled, ready for data transfer, blocksize */ + uint32 test_read; /* 0x14, RO 0xfeedbead signature */ + uint32 test_rw; /* 0x18, RW */ + uint8 resp_delay_f0; /* 0x1c, read resp delay bytes for F0 (corerev >= 3) */ + uint8 resp_delay_f1; /* 0x1d, read resp delay bytes for F1 (corerev >= 3) */ + uint8 resp_delay_f2; /* 0x1e, read resp delay bytes for F2 (corerev >= 3) */ + uint8 resp_delay_f3; /* 0x1f, read resp delay bytes for F3 (corerev >= 3) */ +} spi_regs_t; + +/* SPI device register offsets */ +#define SPID_CONFIG 0x00 +#define SPID_RESPONSE_DELAY 0x01 +#define SPID_STATUS_ENABLE 0x02 +#define SPID_RESET_BP 0x03 /* (corerev >= 1) */ +#define SPID_INTR_REG 0x04 /* 16 bits - Interrupt status */ +#define SPID_INTR_EN_REG 0x06 /* 16 bits - Interrupt mask */ +#define SPID_STATUS_REG 0x08 /* 32 bits */ +#define SPID_F1_INFO_REG 0x0C /* 16 bits */ +#define SPID_F2_INFO_REG 0x0E /* 16 bits */ +#define SPID_F3_INFO_REG 0x10 /* 16 bits */ +#define SPID_TEST_READ 0x14 /* 32 bits */ +#define SPID_TEST_RW 0x18 /* 32 bits */ +#define SPID_RESP_DELAY_F0 0x1c /* 8 bits (corerev >= 3) */ +#define SPID_RESP_DELAY_F1 0x1d /* 8 bits (corerev >= 3) */ +#define SPID_RESP_DELAY_F2 0x1e /* 8 bits (corerev >= 3) */ +#define SPID_RESP_DELAY_F3 0x1f /* 8 bits (corerev >= 3) */ + +/* Bit masks for SPID_CONFIG device register */ +#define WORD_LENGTH_32 0x1 /* 0/1 16/32 bit word length */ +#define ENDIAN_BIG 0x2 /* 0/1 Little/Big Endian */ +#define CLOCK_PHASE 0x4 /* 0/1 clock phase delay */ +#define CLOCK_POLARITY 0x8 /* 0/1 Idle state clock polarity is low/high */ +#define HIGH_SPEED_MODE 0x10 /* 1/0 High Speed mode / Normal mode */ +#define INTR_POLARITY 0x20 /* 1/0 Interrupt active polarity is high/low */ +#define WAKE_UP 0x80 /* 0/1 Wake-up command from Host to WLAN */ + +/* Bit mask for SPID_RESPONSE_DELAY device register */ +#define RESPONSE_DELAY_MASK 0xFF /* Configurable rd response delay in multiples of 8 bits */ + +/* Bit mask for SPID_STATUS_ENABLE device register */ +#define STATUS_ENABLE 0x1 /* 1/0 Status sent/not sent to host after read/write */ +#define INTR_WITH_STATUS 0x2 /* 0/1 Do-not / do-interrupt if status is sent */ +#define RESP_DELAY_ALL 0x4 /* Applicability of resp delay to F1 or all func's read */ +#define DWORD_PKT_LEN_EN 0x8 /* Packet len denoted in dwords instead of bytes */ +#define CMD_ERR_CHK_EN 0x20 /* Command error check enable */ +#define DATA_ERR_CHK_EN 0x40 /* Data error check enable */ + +/* Bit mask for SPID_RESET_BP device register */ +#define RESET_ON_WLAN_BP_RESET 0x4 /* enable reset for WLAN backplane */ +#define RESET_ON_BT_BP_RESET 0x8 /* enable reset for BT backplane */ +#define RESET_SPI 0x80 /* reset the above enabled logic */ + +/* Bit mask for SPID_INTR_REG device register */ +#define DATA_UNAVAILABLE 0x0001 /* Requested data not available; Clear by writing a "1" */ +#define F2_F3_FIFO_RD_UNDERFLOW 0x0002 +#define F2_F3_FIFO_WR_OVERFLOW 0x0004 +#define COMMAND_ERROR 0x0008 /* Cleared by writing 1 */ +#define DATA_ERROR 0x0010 /* Cleared by writing 1 */ +#define F2_PACKET_AVAILABLE 0x0020 +#define F3_PACKET_AVAILABLE 0x0040 +#define F1_OVERFLOW 0x0080 /* Due to last write. Bkplane has pending write requests */ +#define MISC_INTR0 0x0100 +#define MISC_INTR1 0x0200 +#define MISC_INTR2 0x0400 +#define MISC_INTR3 0x0800 +#define MISC_INTR4 0x1000 +#define F1_INTR 0x2000 +#define F2_INTR 0x4000 +#define F3_INTR 0x8000 + +/* Bit mask for 32bit SPID_STATUS_REG device register */ +#define STATUS_DATA_NOT_AVAILABLE 0x00000001 +#define STATUS_UNDERFLOW 0x00000002 +#define STATUS_OVERFLOW 0x00000004 +#define STATUS_F2_INTR 0x00000008 +#define STATUS_F3_INTR 0x00000010 +#define STATUS_F2_RX_READY 0x00000020 +#define STATUS_F3_RX_READY 0x00000040 +#define STATUS_HOST_CMD_DATA_ERR 0x00000080 +#define STATUS_F2_PKT_AVAILABLE 0x00000100 +#define STATUS_F2_PKT_LEN_MASK 0x000FFE00 +#define STATUS_F2_PKT_LEN_SHIFT 9 +#define STATUS_F3_PKT_AVAILABLE 0x00100000 +#define STATUS_F3_PKT_LEN_MASK 0xFFE00000 +#define STATUS_F3_PKT_LEN_SHIFT 21 + +/* Bit mask for 16 bits SPID_F1_INFO_REG device register */ +#define F1_ENABLED 0x0001 +#define F1_RDY_FOR_DATA_TRANSFER 0x0002 +#define F1_MAX_PKT_SIZE 0x01FC + +/* Bit mask for 16 bits SPID_F2_INFO_REG device register */ +#define F2_ENABLED 0x0001 +#define F2_RDY_FOR_DATA_TRANSFER 0x0002 +#define F2_MAX_PKT_SIZE 0x3FFC + +/* Bit mask for 16 bits SPID_F3_INFO_REG device register */ +#define F3_ENABLED 0x0001 +#define F3_RDY_FOR_DATA_TRANSFER 0x0002 +#define F3_MAX_PKT_SIZE 0x3FFC + +/* Bit mask for 32 bits SPID_TEST_READ device register read in 16bit LE mode */ +#define TEST_RO_DATA_32BIT_LE 0xFEEDBEAD + +/* Maximum number of I/O funcs */ +#define SPI_MAX_IOFUNCS 4 + +#define SPI_MAX_PKT_LEN (2048*4) + +/* Misc defines */ +#define SPI_FUNC_0 0 +#define SPI_FUNC_1 1 +#define SPI_FUNC_2 2 +#define SPI_FUNC_3 3 + +#define WAIT_F2RXFIFORDY 100 +#define WAIT_F2RXFIFORDY_DELAY 20 + +#endif /* _SPI_H */ diff --git a/drivers/net/wireless/bcm4329/include/trxhdr.h b/drivers/net/wireless/bcm4329/include/trxhdr.h index b79ac355..8f5eed94 100644 --- a/drivers/net/wireless/bcm4329/include/trxhdr.h +++ b/drivers/net/wireless/bcm4329/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/drivers/net/wireless/bcm4329/include/typedefs.h b/drivers/net/wireless/bcm4329/include/typedefs.h index e4daeeef..4d9dd761 100644 --- a/drivers/net/wireless/bcm4329/include/typedefs.h +++ b/drivers/net/wireless/bcm4329/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/drivers/net/wireless/bcm4329/include/wlioctl.h b/drivers/net/wireless/bcm4329/include/wlioctl.h index 58a2f6c0..cd7725a7 100644 --- a/drivers/net/wireless/bcm4329/include/wlioctl.h +++ b/drivers/net/wireless/bcm4329/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.4.1 2010/11/17 03:09:28 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 @@ -854,6 +857,7 @@ typedef struct wl_ioctl { #define PM_MAX 1 #define PM_FAST 2 +#define LISTEN_INTERVAL 20 #define INTERFERE_NONE 0 #define NON_WLAN 1 @@ -1314,6 +1318,8 @@ enum { #define PFN_VERSION 1 +#define MAX_PFN_LIST_COUNT 16 + typedef struct wl_pfn_param { int32 version; @@ -1324,12 +1330,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/drivers/net/wireless/bcm4329/linux_osl.c b/drivers/net/wireless/bcm4329/linux_osl.c index bb4d817a..980416ba 100644 --- a/drivers/net/wireless/bcm4329/linux_osl.c +++ b/drivers/net/wireless/bcm4329/linux_osl.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: linux_osl.c,v 1.125.12.3.8.6 2009/12/09 01:29:03 Exp $ + * $Id: linux_osl.c,v 1.125.12.3.8.7 2010/05/04 21:10:04 Exp $ */ @@ -35,6 +35,7 @@ #include #include #include +#include #define PCI_CFG_RETRY 10 @@ -46,7 +47,7 @@ #define STATIC_BUF_SIZE (PAGE_SIZE*2) #define STATIC_BUF_TOTAL_LEN (MAX_STATIC_BUF_NUM*STATIC_BUF_SIZE) typedef struct bcm_static_buf { - struct semaphore static_sem; + struct mutex static_sem; unsigned char *buf_ptr; unsigned char buf_use[MAX_STATIC_BUF_NUM]; } bcm_static_buf_t; @@ -57,7 +58,7 @@ static bcm_static_buf_t *bcm_static_buf = 0; typedef struct bcm_static_pkt { struct sk_buff *skb_4k[MAX_STATIC_PKT_NUM]; struct sk_buff *skb_8k[MAX_STATIC_PKT_NUM]; - struct semaphore osl_pkt_sem; + struct mutex osl_pkt_sem; unsigned char pkt_use[MAX_STATIC_PKT_NUM*2]; } bcm_static_pkt_t; static bcm_static_pkt_t *bcm_static_skb = 0; @@ -199,7 +200,7 @@ osl_attach(void *pdev, uint bustype, bool pkttag) /* printk("alloc static buf at %x!\n", (unsigned int)bcm_static_buf); */ } - init_MUTEX(&bcm_static_buf->static_sem); + mutex_init(&bcm_static_buf->static_sem); bcm_static_buf->buf_ptr = (unsigned char *)bcm_static_buf + STATIC_BUF_SIZE; @@ -217,7 +218,7 @@ osl_attach(void *pdev, uint bustype, bool pkttag) for (i = 0; i < MAX_STATIC_PKT_NUM*2; i++) bcm_static_skb->pkt_use[i] = 0; - init_MUTEX(&bcm_static_skb->osl_pkt_sem); + mutex_init(&bcm_static_skb->osl_pkt_sem); } #endif return osh; @@ -304,7 +305,7 @@ osl_pktget_static(osl_t *osh, uint len) } - down(&bcm_static_skb->osl_pkt_sem); + mutex_lock(&bcm_static_skb->osl_pkt_sem); if (len <= PAGE_SIZE) { @@ -317,7 +318,7 @@ osl_pktget_static(osl_t *osh, uint len) if (i != MAX_STATIC_PKT_NUM) { bcm_static_skb->pkt_use[i] = 1; - up(&bcm_static_skb->osl_pkt_sem); + mutex_unlock(&bcm_static_skb->osl_pkt_sem); skb = bcm_static_skb->skb_4k[i]; skb->tail = skb->data + len; @@ -337,7 +338,7 @@ osl_pktget_static(osl_t *osh, uint len) if (i != MAX_STATIC_PKT_NUM) { bcm_static_skb->pkt_use[i+MAX_STATIC_PKT_NUM] = 1; - up(&bcm_static_skb->osl_pkt_sem); + mutex_unlock(&bcm_static_skb->osl_pkt_sem); skb = bcm_static_skb->skb_8k[i]; skb->tail = skb->data + len; skb->len = len; @@ -347,7 +348,7 @@ osl_pktget_static(osl_t *osh, uint len) - up(&bcm_static_skb->osl_pkt_sem); + mutex_unlock(&bcm_static_skb->osl_pkt_sem); printk("all static pkt in use!\n"); return osl_pktget(osh, len); } @@ -360,12 +361,11 @@ osl_pktfree_static(osl_t *osh, void *p, bool send) for (i = 0; i < MAX_STATIC_PKT_NUM*2; i++) { - if ( (i < MAX_STATIC_PKT_NUM && p == bcm_static_skb->skb_4k[i]) || - (i >= MAX_STATIC_PKT_NUM && p == bcm_static_skb->skb_8k[i-MAX_STATIC_PKT_NUM]) ) + if (p == bcm_static_skb->skb_4k[i]) { - down(&bcm_static_skb->osl_pkt_sem); + mutex_lock(&bcm_static_skb->osl_pkt_sem); bcm_static_skb->pkt_use[i] = 0; - up(&bcm_static_skb->osl_pkt_sem); + mutex_unlock(&bcm_static_skb->osl_pkt_sem); return; @@ -467,7 +467,7 @@ osl_malloc(osl_t *osh, uint size) int i = 0; if ((size >= PAGE_SIZE)&&(size <= STATIC_BUF_SIZE)) { - down(&bcm_static_buf->static_sem); + mutex_lock(&bcm_static_buf->static_sem); for (i = 0; i < MAX_STATIC_BUF_NUM; i++) { @@ -477,13 +477,13 @@ osl_malloc(osl_t *osh, uint size) if (i == MAX_STATIC_BUF_NUM) { - up(&bcm_static_buf->static_sem); + mutex_unlock(&bcm_static_buf->static_sem); printk("all static buff in use!\n"); goto original; } bcm_static_buf->buf_use[i] = 1; - up(&bcm_static_buf->static_sem); + mutex_unlock(&bcm_static_buf->static_sem); bzero(bcm_static_buf->buf_ptr+STATIC_BUF_SIZE*i, size); if (osh) @@ -512,16 +512,16 @@ osl_mfree(osl_t *osh, void *addr, uint size) #ifdef DHD_USE_STATIC_BUF if (bcm_static_buf) { - if ((addr > (void *)bcm_static_buf) && ((unsigned char *)addr \ + if ((addr > (void *)bcm_static_buf) && ((unsigned char *)addr <= ((unsigned char *)bcm_static_buf + STATIC_BUF_TOTAL_LEN))) { int buf_idx = 0; buf_idx = ((unsigned char *)addr - bcm_static_buf->buf_ptr)/STATIC_BUF_SIZE; - down(&bcm_static_buf->static_sem); + mutex_lock(&bcm_static_buf->static_sem); bcm_static_buf->buf_use[buf_idx] = 0; - up(&bcm_static_buf->static_sem); + mutex_unlock(&bcm_static_buf->static_sem); if (osh) { ASSERT(osh->magic == OS_HANDLE_MAGIC); diff --git a/drivers/net/wireless/bcm4329/miniopt.c b/drivers/net/wireless/bcm4329/miniopt.c index 44199eab..6a184a75 100644 --- a/drivers/net/wireless/bcm4329/miniopt.c +++ b/drivers/net/wireless/bcm4329/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/drivers/net/wireless/bcm4329/sbutils.c b/drivers/net/wireless/bcm4329/sbutils.c index 567e94e0..46cd5101 100644 --- a/drivers/net/wireless/bcm4329/sbutils.c +++ b/drivers/net/wireless/bcm4329/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 diff --git a/drivers/net/wireless/bcm4329/siutils.c b/drivers/net/wireless/bcm4329/siutils.c index 6472a785..1814db0f 100644 --- a/drivers/net/wireless/bcm4329/siutils.c +++ b/drivers/net/wireless/bcm4329/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 diff --git a/drivers/net/wireless/bcm4329/siutils_priv.h b/drivers/net/wireless/bcm4329/siutils_priv.h index 1ec59aa3..e8ad7e50 100644 --- a/drivers/net/wireless/bcm4329/siutils_priv.h +++ b/drivers/net/wireless/bcm4329/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/drivers/net/wireless/bcm4329/wl_iw.c b/drivers/net/wireless/bcm4329/wl_iw.c index 381397b6..8460804c 100644 --- a/drivers/net/wireless/bcm4329/wl_iw.c +++ b/drivers/net/wireless/bcm4329/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.69 2010/12/21 03:00:08 Exp $ */ @@ -53,11 +53,39 @@ typedef const struct si_pub si_t; #define WL_ASSOC(x) #define WL_INFORM(x) #define WL_WSEC(x) +#define WL_SCAN(x) +#define WL_TRACE_COEX(x) #include -#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 #include @@ -69,11 +97,13 @@ typedef const struct si_pub si_t; #define WL_SOFTAP(x) printk x static struct net_device *priv_dev; static bool ap_cfg_running = FALSE; -static bool ap_fw_loaded = FALSE; +bool ap_fw_loaded = FALSE; +static long ap_cfg_pid = -1; struct net_device *ap_net_dev = NULL; struct semaphore ap_eth_sema; +static struct completion ap_cfg_exited; static int wl_iw_set_ap_security(struct net_device *dev, struct ap_profile *ap); -static int wl_iw_softap_deassoc_stations(struct net_device *dev); +static int wl_iw_softap_deassoc_stations(struct net_device *dev, u8 *mac); #endif #define WL_IW_IOCTL_CALL(func_call) \ @@ -82,9 +112,13 @@ static int wl_iw_softap_deassoc_stations(struct net_device *dev); } while (0) static int g_onoff = G_WLAN_SET_ON; -static struct mutex wl_start_lock; +wl_iw_extra_params_t g_wl_iw_params; static struct mutex wl_cache_lock; +#ifdef CONFIG_US_NON_DFS_CHANNELS_ONLY +static bool use_non_dfs_channels = true; +#endif + extern bool wl_iw_conn_status_str(uint32 event_type, uint32 status, uint32 reason, char* stringBuf, uint buflen); #include @@ -133,7 +167,11 @@ 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; +#if defined(CONFIG_FIRST_SCAN) +static volatile uint g_first_broadcast_scan; +static volatile uint g_first_counter_scans; +#define MAX_ALLOWED_BLOCK_SCAN_FROM_FIRST_SCAN 3 +#endif #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) @@ -150,9 +188,13 @@ 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 +#if defined(CONFIG_FIRST_SCAN) int wl_iw_iscan_set_scan_broadcast_prep(struct net_device *dev, uint flag); +#endif static int dev_wlc_bufvar_set(struct net_device *dev, char *name, char *buf, int len); #define ISCAN_STATE_IDLE 0 #define ISCAN_STATE_SCANING 1 @@ -178,10 +220,20 @@ 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 +#define COEX_DHCP 1 + +#define BT_DHCP_eSCO_FIX +#define BT_DHCP_USE_FLAGS +#define BT_DHCP_OPPORTUNITY_WINDOW_TIME 2500 +#define BT_DHCP_FLAG_FORCE_TIME 5500 static void wl_iw_bt_flag_set(struct net_device *dev, bool set); static void wl_iw_bt_release(void); @@ -191,18 +243,16 @@ typedef enum bt_coex_status { BT_DHCP_OPPORTUNITY_WINDOW, BT_DHCP_FLAG_FORCE_TIMEOUT } coex_status_t; -#define BT_DHCP_OPPORTUNITY_WINDOW_TIEM 2500 -#define BT_DHCP_FLAG_FORCE_TIME 5500 typedef struct bt_info { struct net_device *dev; struct timer_list timer; uint32 timer_ms; uint32 timer_on; - int bt_state; + bool dhcp_done; + int bt_state; - - long bt_pid; + long bt_pid; struct semaphore bt_sem; struct completion bt_exited; } bt_info_t; @@ -222,6 +272,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 +289,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 +335,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 +352,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 +366,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 +446,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)); } @@ -558,6 +619,31 @@ wl_iw_get_macaddr( return error; } +static int +wl_iw_set_country_code(struct net_device *dev, char *ccode) +{ + char country_code[WLC_CNTRY_BUF_SZ]; + int ret = -1; + + WL_TRACE(("%s\n", __FUNCTION__)); + if (!ccode) + ccode = dhd_bus_country_get(dev); + strncpy(country_code, ccode, sizeof(country_code)); + if (ccode && (country_code[0] != 0)) { +#ifdef CONFIG_US_NON_DFS_CHANNELS_ONLY + if (use_non_dfs_channels && !strncmp(country_code, "US", 2)) + strncpy(country_code, "Q2", WLC_CNTRY_BUF_SZ); + if (!use_non_dfs_channels && !strncmp(country_code, "Q2", 2)) + strncpy(country_code, "US", WLC_CNTRY_BUF_SZ); +#endif + ret = dev_wlc_ioctl(dev, WLC_SET_COUNTRY, &country_code, sizeof(country_code)); + if (ret >= 0) { + WL_TRACE(("%s: set country %s OK\n", __FUNCTION__, country_code)); + dhd_bus_country_set(dev, &country_code[0]); + } + } + return ret; +} static int wl_iw_set_country( @@ -579,14 +665,11 @@ wl_iw_set_country( country_offset = strcspn(extra, " "); country_code_size = strlen(extra) - country_offset; - if (country_offset != 0) { strncpy(country_code, extra + country_offset + 1, MIN(country_code_size, sizeof(country_code))); - - - if ((error = dev_wlc_ioctl(dev, WLC_SET_COUNTRY, - &country_code, sizeof(country_code))) >= 0) { + error = wl_iw_set_country_code(dev, country_code); + if (error >= 0) { p += snprintf(p, MAX_WX_STRING, "OK"); WL_TRACE(("%s: set country %s OK\n", __FUNCTION__, country_code)); goto exit; @@ -616,23 +699,34 @@ wl_iw_set_power_mode( int pm_local = PM_OFF; char powermode_val = 0; + WL_TRACE_COEX(("%s: DHCP session cmd:%s\n", __FUNCTION__, extra)); + strncpy((char *)&powermode_val, extra + strlen("POWERMODE") + 1, 1); if (strnicmp((char *)&powermode_val, "1", strlen("1")) == 0) { - WL_TRACE(("%s: DHCP session starts\n", __FUNCTION__)); - 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) { - WL_TRACE(("%s: DHCP session done\n", __FUNCTION__)); + /* Disable packet filtering if necessary */ + net_os_set_packet_filter(dev, 0); + + g_bt->dhcp_done = false; + WL_TRACE_COEX(("%s: DHCP start, pm:%d changed to pm:%d\n", + __FUNCTION__, pm, pm_local)); + + } else if (strnicmp((char *)&powermode_val, "0", strlen("0")) == 0) { 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); + + g_bt->dhcp_done = true; + + } else { + WL_ERROR(("%s Unkwown yet power setting, ignored\n", + __FUNCTION__)); } p += snprintf(p, MAX_WX_STRING, "OK"); @@ -643,6 +737,122 @@ wl_iw_set_power_mode( } #endif + +static bool btcoex_is_sco_active(struct net_device *dev) +{ + int ioc_res = 0; + bool res = false; + int temp = 0; + + ioc_res = dev_wlc_intvar_get_reg(dev, "btc_params", 4, &temp); + + if (ioc_res == 0) { + WL_TRACE_COEX(("%s: read btc_params[4] = %x\n", __FUNCTION__, temp)); + + if ((temp > 0xea0) && (temp < 0xed8)) { + WL_TRACE_COEX(("%s: BT SCO/eSCO is ACTIVE\n", __FUNCTION__)); + res = true; + } else { + WL_TRACE_COEX(("%s: BT SCO/eSCO is NOT detected\n", __FUNCTION__)); + } + } else { + WL_ERROR(("%s ioc read btc params error\n", __FUNCTION__)); + } + return res; +} + +#if defined(BT_DHCP_eSCO_FIX) + +static int set_btc_esco_params(struct net_device *dev, bool trump_sco) +{ + static bool saved_status = false; + + char buf_reg50va_dhcp_on[8] = { 50, 00, 00, 00, 0x22, 0x80, 0x00, 0x00 }; + char buf_reg51va_dhcp_on[8] = { 51, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 }; + char buf_reg64va_dhcp_on[8] = { 64, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 }; + char buf_reg65va_dhcp_on[8] = { 65, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 }; + char buf_reg71va_dhcp_on[8] = { 71, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 }; + + uint32 regaddr; + static uint32 saved_reg50; + static uint32 saved_reg51; + static uint32 saved_reg64; + static uint32 saved_reg65; + static uint32 saved_reg71; + + if (trump_sco) { + + WL_TRACE_COEX(("Do new SCO/eSCO coex algo {save & override} \n")); + + if ((!dev_wlc_intvar_get_reg(dev, "btc_params", 50, &saved_reg50)) && + (!dev_wlc_intvar_get_reg(dev, "btc_params", 51, &saved_reg51)) && + (!dev_wlc_intvar_get_reg(dev, "btc_params", 64, &saved_reg64)) && + (!dev_wlc_intvar_get_reg(dev, "btc_params", 65, &saved_reg65)) && + (!dev_wlc_intvar_get_reg(dev, "btc_params", 71, &saved_reg71))) { + + saved_status = TRUE; + WL_TRACE_COEX(("%s saved bt_params[50,51,64,65,71]:" + " 0x%x 0x%x 0x%x 0x%x 0x%x\n", + __FUNCTION__, saved_reg50, saved_reg51, + saved_reg64, saved_reg65, saved_reg71)); + + } else { + WL_ERROR((":%s: save btc_params failed\n", + __FUNCTION__)); + saved_status = false; + return -1; + } + + WL_TRACE_COEX(("override with [50,51,64,65,71]:" + " 0x%x 0x%x 0x%x 0x%x 0x%x\n", + *(u32 *)(buf_reg50va_dhcp_on+4), + *(u32 *)(buf_reg51va_dhcp_on+4), + *(u32 *)(buf_reg64va_dhcp_on+4), + *(u32 *)(buf_reg65va_dhcp_on+4), + *(u32 *)(buf_reg71va_dhcp_on+4))); + + dev_wlc_bufvar_set(dev, "btc_params", (char *)&buf_reg50va_dhcp_on[0], 8); + dev_wlc_bufvar_set(dev, "btc_params", (char *)&buf_reg51va_dhcp_on[0], 8); + dev_wlc_bufvar_set(dev, "btc_params", (char *)&buf_reg64va_dhcp_on[0], 8); + dev_wlc_bufvar_set(dev, "btc_params", (char *)&buf_reg65va_dhcp_on[0], 8); + dev_wlc_bufvar_set(dev, "btc_params", (char *)&buf_reg71va_dhcp_on[0], 8); + + saved_status = true; + + } else if (saved_status) { + + WL_TRACE_COEX(("Do new SCO/eSCO coex algo {save & override} \n")); + + regaddr = 50; + dev_wlc_intvar_set_reg(dev, "btc_params", + (char *)®addr, (char *)&saved_reg50); + regaddr = 51; + dev_wlc_intvar_set_reg(dev, "btc_params", + (char *)®addr, (char *)&saved_reg51); + regaddr = 64; + dev_wlc_intvar_set_reg(dev, "btc_params", + (char *)®addr, (char *)&saved_reg64); + regaddr = 65; + dev_wlc_intvar_set_reg(dev, "btc_params", + (char *)®addr, (char *)&saved_reg65); + regaddr = 71; + dev_wlc_intvar_set_reg(dev, "btc_params", + (char *)®addr, (char *)&saved_reg71); + + WL_TRACE_COEX(("restore bt_params[50,51,64,65,71]: 0x%x 0x%x 0x%x 0x%x 0x%x\n", + saved_reg50, saved_reg51, saved_reg64, + saved_reg65, saved_reg71)); + + saved_status = false; + } else { + WL_ERROR((":%s att to restore not saved BTCOEX params\n", + __FUNCTION__)); + return -1; + } + return 0; +} +#endif + static int wl_iw_get_power_mode( struct net_device *dev, @@ -698,9 +908,6 @@ wl_iw_set_btcoex_dhcp( static bool saved_status = FALSE; char buf_flag7_default[8] = { 7, 00, 00, 00, 0x0, 0x00, 0x00, 0x00}; -#ifndef CUSTOMER_HW2 - uint32 temp1, temp2; -#endif #ifdef CUSTOMER_HW2 strncpy((char *)&powermode_val, extra + strlen("BTCOEXMODE") + 1, 1); @@ -710,44 +917,43 @@ wl_iw_set_btcoex_dhcp( if (strnicmp((char *)&powermode_val, "1", strlen("1")) == 0) { - WL_TRACE(("%s: DHCP session starts\n", __FUNCTION__)); + WL_TRACE_COEX(("%s: DHCP session start, cmd:%s\n", __FUNCTION__, extra)); if ((saved_status == FALSE) && #ifndef CUSTOMER_HW2 - (!dev_wlc_ioctl(dev, WLC_GET_PM, &pm, sizeof(pm))) && + (!dev_wlc_ioctl(dev, WLC_GET_PM, &pm, sizeof(pm))) && #endif - (!dev_wlc_intvar_get_reg(dev, "btc_params", 66, &saved_reg66)) && - (!dev_wlc_intvar_get_reg(dev, "btc_params", 41, &saved_reg41)) && - (!dev_wlc_intvar_get_reg(dev, "btc_params", 68, &saved_reg68))) { - saved_status = TRUE; - WL_TRACE(("Saved 0x%x 0x%x 0x%x\n", \ - saved_reg66, saved_reg41, saved_reg68)); + (!dev_wlc_intvar_get_reg(dev, "btc_params", 66, &saved_reg66)) && + (!dev_wlc_intvar_get_reg(dev, "btc_params", 41, &saved_reg41)) && + (!dev_wlc_intvar_get_reg(dev, "btc_params", 68, &saved_reg68))) { + WL_TRACE_COEX(("save regs {66,41,68} ->: 0x%x 0x%x 0x%x\n", \ + saved_reg66, saved_reg41, saved_reg68)); #ifndef CUSTOMER_HW2 - dev_wlc_ioctl(dev, WLC_SET_PM, &pm_local, sizeof(pm_local)); + dev_wlc_ioctl(dev, WLC_SET_PM, &pm_local, sizeof(pm_local)); #endif - dev_wlc_bufvar_set(dev, "btc_params", \ - (char *)&buf_reg66va_dhcp_on[0], sizeof(buf_reg66va_dhcp_on)); - dev_wlc_bufvar_set(dev, "btc_params", \ - (char *)&buf_reg41va_dhcp_on[0], sizeof(buf_reg41va_dhcp_on)); - dev_wlc_bufvar_set(dev, "btc_params", \ - (char *)&buf_reg68va_dhcp_on[0], sizeof(buf_reg68va_dhcp_on)); -#ifndef CUSTOMER_HW2 - if ((!dev_wlc_intvar_get_reg(dev, "btc_params", 12, &temp1)) && - (!dev_wlc_intvar_get_reg(dev, "btc_params", 13, &temp2))) - { - if ((temp1 != 0) && (temp2 != 0)) { -#endif - g_bt->bt_state = BT_DHCP_START; - g_bt->timer_on = 1; - mod_timer(&g_bt->timer, g_bt->timer.expires); - WL_TRACE(("%s enable BT DHCP Timer\n", \ - __FUNCTION__)); -#ifndef CUSTOMER_HW2 - } - } -#endif + if (btcoex_is_sco_active(dev)) { + + dev_wlc_bufvar_set(dev, "btc_params", \ + (char *)&buf_reg66va_dhcp_on[0], \ + sizeof(buf_reg66va_dhcp_on)); + + dev_wlc_bufvar_set(dev, "btc_params", \ + (char *)&buf_reg41va_dhcp_on[0], \ + sizeof(buf_reg41va_dhcp_on)); + + dev_wlc_bufvar_set(dev, "btc_params", \ + (char *)&buf_reg68va_dhcp_on[0], \ + sizeof(buf_reg68va_dhcp_on)); + saved_status = TRUE; + + g_bt->bt_state = BT_DHCP_START; + g_bt->timer_on = 1; + mod_timer(&g_bt->timer, g_bt->timer.expires); + WL_TRACE_COEX(("%s enable BT DHCP Timer\n", \ + __FUNCTION__)); + } } else if (saved_status == TRUE) { WL_ERROR(("%s was called w/o DHCP OFF. Continue\n", __FUNCTION__)); @@ -758,22 +964,28 @@ wl_iw_set_btcoex_dhcp( #else else if (strnicmp((char *)&powermode_val, "0", strlen("0")) == 0) { #endif - WL_TRACE(("%s: DHCP session done\n", __FUNCTION__)); #ifndef CUSTOMER_HW2 dev_wlc_ioctl(dev, WLC_SET_PM, &pm, sizeof(pm)); #endif - WL_TRACE(("%s disable BT DHCP Timer\n", __FUNCTION__)); + WL_TRACE_COEX(("%s disable BT DHCP Timer\n", __FUNCTION__)); if (g_bt->timer_on) { g_bt->timer_on = 0; del_timer_sync(&g_bt->timer); + + if (g_bt->bt_state != BT_DHCP_IDLE) { + WL_TRACE_COEX(("%s bt->bt_state:%d\n", + __FUNCTION__, g_bt->bt_state)); + + up(&g_bt->bt_sem); + } } - dev_wlc_bufvar_set(dev, "btc_flags", \ + if (saved_status == TRUE) { + dev_wlc_bufvar_set(dev, "btc_flags", \ (char *)&buf_flag7_default[0], sizeof(buf_flag7_default)); - if (saved_status) { regaddr = 66; dev_wlc_intvar_set_reg(dev, "btc_params", \ (char *)®addr, (char *)&saved_reg66); @@ -783,11 +995,15 @@ wl_iw_set_btcoex_dhcp( regaddr = 68; dev_wlc_intvar_set_reg(dev, "btc_params", \ (char *)®addr, (char *)&saved_reg68); + + WL_TRACE_COEX(("restore regs {66,41,68} <- 0x%x 0x%x 0x%x\n", \ + saved_reg66, saved_reg41, saved_reg68)); } saved_status = FALSE; } else { - WL_ERROR(("Unkwown yet power setting, ignored\n")); + WL_ERROR(("%s Unkwown yet power setting, ignored\n", + __FUNCTION__)); } p += snprintf(p, MAX_WX_STRING, "OK"); @@ -797,6 +1013,52 @@ 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; +} + +#ifdef CONFIG_US_NON_DFS_CHANNELS_ONLY +static int +wl_iw_set_dfs_channels( + struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra +) +{ + use_non_dfs_channels = *(extra + strlen(SETDFSCHANNELS_CMD) + 1) - '0'; + use_non_dfs_channels = (use_non_dfs_channels != 0) ? false : true; + wl_iw_set_country_code(dev, NULL); + return 0; +} +#endif + int wl_format_ssid(char* ssid_buf, uint8* ssid, int ssid_len) { @@ -833,7 +1095,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; @@ -843,9 +1105,341 @@ 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 *str_ptr; + char *str_ptr_end; + 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', '1', '2', '0', + 'S', + 0x04, + 'B', 'R', 'C', 'M', + 'S', + 0x04, + 'G', 'O', 'O', 'G', + 'T', + '1','E', + 0x00 + }; +#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 { + if ((str_ptr[0] != PNO_TLV_TYPE_TIME) || (tlv_size_left <= 1)) { + WL_ERROR(("%s scan duration corrupted field size %d\n", \ + __FUNCTION__, tlv_size_left)); + goto exit_proc; + } + str_ptr++; + pno_time = simple_strtoul(str_ptr, &str_ptr_end, 16); + WL_ERROR((" got %d bytes left pno_time %d or %#x\n", \ + tlv_size_left, pno_time, pno_time)); + } + } + 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, @@ -861,30 +1455,39 @@ 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)); - return error; - } - rssi = dtoh32(scb_val.val); + } else { + rssi = dtoh32(scb_val.val); - error = dev_wlc_ioctl(dev, WLC_GET_SSID, &ssid, sizeof(ssid)); - if (!error) { - ssid.SSID_len = dtoh32(ssid.SSID_len); - wl_format_ssid(ssidbuf, ssid.SSID, dtoh32(ssid.SSID_len)); + error = dev_wlc_ioctl(dev, WLC_GET_SSID, &ssid, sizeof(ssid)); + if (!error) { + ssid.SSID_len = dtoh32(ssid.SSID_len); + wl_format_ssid(ssidbuf, ssid.SSID, dtoh32(ssid.SSID_len)); + } } } - p += snprintf(p, MAX_WX_STRING, "%s rssi %d ", ssidbuf, rssi); + WL_ASSOC(("%s ssid_len:%d, rssi:%d\n", __FUNCTION__, ssid.SSID_len, rssi)); + + if (error || (ssid.SSID_len == 0)) { + p += snprintf(p, MAX_WX_STRING, "FAIL"); + } else { + 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 @@ -902,6 +1505,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; @@ -912,6 +1516,7 @@ int wl_control_wl_start(struct net_device *dev) { int ret = 0; + wl_iw_t *iw; WL_TRACE(("Enter %s \n", __FUNCTION__)); @@ -920,7 +1525,13 @@ wl_control_wl_start(struct net_device *dev) return -1; } - mutex_lock(&wl_start_lock); + iw = *(wl_iw_t **)netdev_priv(dev); + + if (!iw) { + WL_ERROR(("%s: wl is null\n", __FUNCTION__)); + return -1; + } + dhd_os_start_lock(iw->pub); if (g_onoff == G_WLAN_SET_OFF) { dhd_customer_gpio_wlan_ctrl(WLAN_RESET_ON); @@ -929,19 +1540,19 @@ wl_control_wl_start(struct net_device *dev) sdioh_start(NULL, 0); #endif - dhd_dev_reset(dev, 0); + ret = dhd_dev_reset(dev, 0); + if (ret == BCME_OK) { #if defined(BCMLXSDMMC) - sdioh_start(NULL, 1); + sdioh_start(NULL, 1); #endif - - dhd_dev_init_ioctl(dev); - - g_onoff = G_WLAN_SET_ON; + dhd_dev_init_ioctl(dev); + g_onoff = G_WLAN_SET_ON; + } } WL_TRACE(("Exited %s \n", __FUNCTION__)); - mutex_unlock(&wl_start_lock); + dhd_os_start_unlock(iw->pub); return ret; } @@ -953,6 +1564,8 @@ wl_iw_control_wl_off( ) { int ret = 0; + wl_iw_t *iw; + WL_TRACE(("Enter %s\n", __FUNCTION__)); if (!dev) { @@ -960,7 +1573,12 @@ wl_iw_control_wl_off( return -1; } - mutex_lock(&wl_start_lock); + iw = *(wl_iw_t **)netdev_priv(dev); + if (!iw) { + WL_ERROR(("%s: dev is null\n", __FUNCTION__)); + return -1; + } + dhd_os_start_lock(iw->pub); #ifdef SOFTAP ap_cfg_running = FALSE; @@ -975,28 +1593,32 @@ 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; - +#if defined(CONFIG_FIRST_SCAN) g_first_broadcast_scan = BROADCAST_SCAN_FIRST_IDLE; + g_first_counter_scans = 0; +#endif #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); + dhd_os_start_unlock(iw->pub); WL_TRACE(("Exited %s\n", __FUNCTION__)); @@ -1013,7 +1635,15 @@ wl_iw_control_wl_on( WL_TRACE(("Enter %s \n", __FUNCTION__)); - ret = wl_control_wl_start(dev); + if ((ret = wl_control_wl_start(dev)) != BCME_OK) { + WL_ERROR(("%s failed first attemp\n", __FUNCTION__)); + dhd_customer_gpio_wlan_ctrl(WLAN_RESET_OFF); + if ((ret = wl_control_wl_start(dev)) != BCME_OK) { + WL_ERROR(("%s failed second attemp\n", __FUNCTION__)); + net_os_send_hang_message(dev); + return ret; + } + } wl_iw_send_priv_event(dev, "START"); @@ -1025,8 +1655,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; @@ -1036,7 +1664,7 @@ wl_iw_control_wl_on( static struct ap_profile my_ap; static int set_ap_cfg(struct net_device *dev, struct ap_profile *ap); static int get_assoc_sta_list(struct net_device *dev, char *buf, int len); -static int set_ap_mac_list(struct net_device *dev, char *buf); +static int set_ap_mac_list(struct net_device *dev, void *buf); #define PTYPE_STRING 0 #define PTYPE_INTDEC 1 @@ -1118,9 +1746,13 @@ int init_ap_profile_from_string(char *param_str, struct ap_profile *ap_cfg) ret |= get_parmeter_from_string(&str_ptr, "CHANNEL=", PTYPE_INTDEC, &ap_cfg->channel, 5); - ret |= get_parmeter_from_string(&str_ptr, "PREAMBLE=", PTYPE_INTDEC, &ap_cfg->preamble, 5); + get_parmeter_from_string(&str_ptr, "PREAMBLE=", PTYPE_INTDEC, &ap_cfg->preamble, 5); - ret |= get_parmeter_from_string(&str_ptr, "MAX_SCB=", PTYPE_INTDEC, &ap_cfg->max_scb, 5); + get_parmeter_from_string(&str_ptr, "MAX_SCB=", PTYPE_INTDEC, &ap_cfg->max_scb, 5); + + get_parmeter_from_string(&str_ptr, "HIDDEN=", PTYPE_INTDEC, &ap_cfg->closednet, 5); + + get_parmeter_from_string(&str_ptr, "COUNTRY=", PTYPE_STRING, &ap_cfg->country_code, 3); return ret; } @@ -1137,7 +1769,8 @@ static int iwpriv_set_ap_config(struct net_device *dev, char *extra = NULL; struct ap_profile *ap_cfg = &my_ap; - WL_TRACE(("> Got IWPRIV SET_AP IOCTL: info->cmd:%x, info->flags:%x, u.data:%p, u.len:%d\n", + WL_TRACE(("%s: info->cmd:%x, info->flags:%x, u.data:%p, u.len:%d\n", + __FUNCTION__, info->cmd, info->flags, wrqu->data.pointer, wrqu->data.length)); @@ -1182,28 +1815,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, @@ -1213,52 +1824,86 @@ static int iwpriv_get_assoc_list(struct net_device *dev, char mac_buf[256]; struct maclist *sta_maclist = (struct maclist *)mac_buf; - char mac_lst[256]; + char mac_lst[384]; char *p_mac_str; + char *p_mac_str_end; + + if ((!dev) || (!extra)) { + return -EINVAL; + } + + net_os_wake_lock(dev); WL_TRACE(("\n %s: IWPRIV IOCTL: cmd:%hx, flags:%hx, extra:%p, iwp.len:%d, \ iwp.len:%p, iwp.flags:%x \n", __FUNCTION__, info->cmd, info->flags, \ extra, p_iwrq->data.length, p_iwrq->data.pointer, p_iwrq->data.flags)); - WL_SOFTAP(("extra:%s\n", extra)); - print_buf((u8 *)p_iwrq, 16, 0); - memset(sta_maclist, 0, sizeof(mac_buf)); sta_maclist->count = 8; - WL_TRACE((" net device:%s, buf_sz:%d\n", dev->name, sizeof(mac_buf))); - get_assoc_sta_list(dev, mac_buf, 256); - WL_TRACE((" got %d stations\n", sta_maclist->count)); + WL_SOFTAP(("%s: net device:%s, buf_sz:%d\n", + __FUNCTION__, dev->name, sizeof(mac_buf))); + + if ((ret = get_assoc_sta_list(dev, mac_buf, sizeof(mac_buf))) < 0) { + WL_ERROR(("%s: sta list ioctl error:%d\n", + __FUNCTION__, ret)); + goto func_exit; + } + + WL_SOFTAP(("%s: got %d stations\n", __FUNCTION__, + sta_maclist->count)); memset(mac_lst, 0, sizeof(mac_lst)); p_mac_str = mac_lst; + p_mac_str_end = &mac_lst[sizeof(mac_lst)-1]; for (i = 0; i < 8; i++) { struct ether_addr *id = &sta_maclist->ea[i]; + if (!ETHER_ISNULLADDR(id->octet)) { + scb_val_t scb_val; + int rssi = 0; - WL_SOFTAP(("dhd_drv>> sta_mac[%d] :", i)); - print_buf((unsigned char *)&sta_maclist->ea[i], 6, 0); + bzero(&scb_val, sizeof(scb_val_t)); - p_mac_str += snprintf(p_mac_str, MAX_WX_STRING, - "Mac[%d]=%02X:%02X:%02X:%02X:%02X:%02X\n", i, + if ((p_mac_str_end - p_mac_str) <= 36) { + WL_ERROR(("%s: mac list buf is < 36 for item[%i] item\n", + __FUNCTION__, i)); + break; + } + + p_mac_str += snprintf(p_mac_str, MAX_WX_STRING, + "\nMac[%d]=%02X:%02X:%02X:%02X:%02X:%02X,", i, id->octet[0], id->octet[1], id->octet[2], id->octet[3], id->octet[4], id->octet[5]); - } + bcopy(id->octet, &scb_val.ea, 6); + ret = dev_wlc_ioctl(dev, WLC_GET_RSSI, &scb_val, sizeof(scb_val_t)); + if (ret < 0) { + snprintf(p_mac_str, MAX_WX_STRING, "RSSI:ERR"); + WL_ERROR(("%s: RSSI ioctl error:%d\n", + __FUNCTION__, ret)); + break; + } - p_iwrq->data.length = strlen(mac_lst); - - WL_TRACE(("u.pointer:%p\n", p_iwrq->data.pointer)); - WL_TRACE(("resulting str:\n%s \n len:%d\n\n", mac_lst, p_iwrq->data.length)); - - if (p_iwrq->data.length) { - if (copy_to_user(p_iwrq->data.pointer, mac_lst, p_iwrq->data.length)) { - WL_ERROR(("%s: Can't copy to user\n", __FUNCTION__)); - return -EFAULT; + rssi = dtoh32(scb_val.val); + p_mac_str += snprintf(p_mac_str, MAX_WX_STRING, + "RSSI:%d", rssi); } } + p_iwrq->data.length = strlen(mac_lst) + 1; + + WL_SOFTAP(("%s: data to user:\n%s\n usr_ptr:%p\n", __FUNCTION__, + mac_lst, p_iwrq->data.pointer)); + + if (p_iwrq->data.length) { + bcopy(mac_lst, extra, p_iwrq->data.length); + } + +func_exit: + net_os_wake_unlock(dev); + WL_TRACE(("Exited %s \n", __FUNCTION__)); return ret; } @@ -1266,19 +1911,20 @@ static int iwpriv_get_assoc_list(struct net_device *dev, #ifdef SOFTAP +#define MAC_FILT_MAX 8 static int iwpriv_set_mac_filters(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *ext) { - int i, ret = -1; - char *extra = NULL; - u8 macfilt[8][6]; + char * extra = NULL; int mac_cnt = 0; - char sub_cmd[16]; + int mac_mode = 0; + struct ether_addr *p_ea; + struct mac_list_set mflist_set; - WL_TRACE((">>> Got IWPRIV SET_MAC_FILTER IOCTL: info->cmd:%x, \ + WL_SOFTAP((">>> Got IWPRIV SET_MAC_FILTER IOCTL: info->cmd:%x, \ info->flags:%x, u.data:%p, u.len:%d\n", info->cmd, info->flags, wrqu->data.pointer, wrqu->data.length)); @@ -1298,25 +1944,21 @@ static int iwpriv_set_mac_filters(struct net_device *dev, extra[wrqu->data.length] = 0; WL_SOFTAP((" Got parameter string in iw_point:\n %s \n", extra)); - memset(macfilt, 0, sizeof(macfilt)); - memset(sub_cmd, 0, sizeof(sub_cmd)); + memset(&mflist_set, 0, sizeof(mflist_set)); str_ptr = extra; - if (get_parmeter_from_string(&str_ptr, "ASCII_CMD=", PTYPE_STRING, sub_cmd, 15) != 0) { + if (get_parmeter_from_string(&str_ptr, "MAC_MODE=", + PTYPE_INTDEC, &mac_mode, 4) != 0) { + WL_ERROR(("ERROR: 'MAC_MODE=' token is missing\n")); goto exit_proc; } -#define MAC_FILT_MAX 8 - - if (strncmp(sub_cmd, "MAC_FLT_W", strlen("MAC_FLT_W"))) { - WL_ERROR(("ERROR: sub_cmd:%s != 'MAC_FLT_W'!\n", sub_cmd)); - goto exit_proc; - } + p_ea = &mflist_set.mac_list.ea[0]; if (get_parmeter_from_string(&str_ptr, "MAC_CNT=", PTYPE_INTDEC, &mac_cnt, 4) != 0) { - WL_ERROR(("ERROR: MAC_CNT param is missing \n")); + WL_ERROR(("ERROR: 'MAC_CNT=' token param is missing \n")); goto exit_proc; } @@ -1325,18 +1967,22 @@ static int iwpriv_set_mac_filters(struct net_device *dev, goto exit_proc; } - for (i = 0; i < mac_cnt; i++) { + for (i=0; i < mac_cnt; i++) if (get_parmeter_from_string(&str_ptr, "MAC=", - PTYPE_STR_HEX, macfilt[i], 12) != 0) { + PTYPE_STR_HEX, &p_ea[i], 12) != 0) { WL_ERROR(("ERROR: MAC_filter[%d] is missing !\n", i)); goto exit_proc; } + + WL_SOFTAP(("MAC_MODE=:%d, MAC_CNT=%d, MACs:..\n", mac_mode, mac_cnt)); + for (i = 0; i < mac_cnt; i++) { + WL_SOFTAP(("mac_filt[%d]:", i)); + print_buf(&p_ea[i], 6, 0); } - for (i = 0; i < mac_cnt; i++) { - WL_SOFTAP(("mac_filt[%d]:", i)); - print_buf(macfilt[i], 6, 0); - } + mflist_set.mode = mac_mode; + mflist_set.mac_list.count = mac_cnt; + set_ap_mac_list(dev, &mflist_set); wrqu->data.pointer = NULL; wrqu->data.length = 0; @@ -1353,8 +1999,44 @@ static int iwpriv_set_mac_filters(struct net_device *dev, } #endif + +#ifdef SOFTAP +static int iwpriv_set_ap_sta_disassoc(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *ext) +{ + int res = 0; + char sta_mac[6] = {0, 0, 0, 0, 0, 0}; + char cmd_buf[256]; + char *str_ptr = cmd_buf; + + WL_SOFTAP((">>%s called\n args: info->cmd:%x," + " info->flags:%x, u.data.p:%p, u.data.len:%d\n", + __FUNCTION__, info->cmd, info->flags, + wrqu->data.pointer, wrqu->data.length)); + + if (wrqu->data.length != 0) { + + if (copy_from_user(cmd_buf, wrqu->data.pointer, wrqu->data.length)) { + return -EFAULT; + } + + if (get_parmeter_from_string(&str_ptr, + "MAC=", PTYPE_STR_HEX, sta_mac, 12) == 0) { + res = wl_iw_softap_deassoc_stations(dev, sta_mac); + } else { + WL_ERROR(("ERROR: STA_MAC= token not found\n")); + } + } + + return res; +} #endif +#endif + + #if WIRELESS_EXT < 13 struct iw_request_info { @@ -1460,6 +2142,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; } @@ -1480,7 +2163,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; @@ -1554,9 +2236,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; @@ -1574,14 +2256,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]); @@ -1613,8 +2304,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++) @@ -1649,8 +2342,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; @@ -1719,6 +2414,8 @@ wl_iw_get_range( #endif #endif + kfree(channels); + return 0; } @@ -1793,6 +2490,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, @@ -1803,6 +2535,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)); @@ -1824,16 +2557,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_ASSOC(("%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_ASSOC(("%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; @@ -1900,6 +2643,7 @@ wl_iw_mlme( } #endif +#ifndef WL_IW_USE_ISCAN static int wl_iw_get_aplist( struct net_device *dev, @@ -1934,7 +2678,7 @@ wl_iw_get_aplist( list->version = dtoh32(list->version); list->count = dtoh32(list->count); if (list->version != WL_BSS_INFO_VERSION) { - WL_ERROR(("%s : list->version %d != WL_BSS_INFO_VERSION\n", \ + WL_ERROR(("%s: list->version %d != WL_BSS_INFO_VERSION\n", \ __FUNCTION__, list->version)); kfree(list); return -EINVAL; @@ -1942,21 +2686,23 @@ wl_iw_get_aplist( for (i = 0, dwrq->length = 0; i < list->count && dwrq->length < IW_MAX_AP; i++) { bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : list->bss_info; - ASSERT(((uintptr)bi + dtoh32(bi->length)) <= ((uintptr)list + - buflen)); - + if ((dtoh32(bi->length) > buflen) || + (((uintptr)bi + dtoh32(bi->length)) > ((uintptr)list + buflen))) { + WL_ERROR(("%s: Scan results out of bounds: %u\n",__FUNCTION__,dtoh32(bi->length))); + kfree(list); + return -E2BIG; + } + if (!(dtoh16(bi->capability) & DOT11_CAP_ESS)) continue; - memcpy(addr[dwrq->length].sa_data, &bi->BSSID, ETHER_ADDR_LEN); addr[dwrq->length].sa_family = ARPHRD_ETHER; qual[dwrq->length].qual = rssi_to_qual(dtoh16(bi->RSSI)); qual[dwrq->length].level = 0x100 + dtoh16(bi->RSSI); qual[dwrq->length].noise = 0x100 + bi->phy_noise; - #if WIRELESS_EXT > 18 qual[dwrq->length].updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM; #else @@ -1975,6 +2721,7 @@ wl_iw_get_aplist( } return 0; } +#endif #ifdef WL_IW_USE_ISCAN static int @@ -2000,7 +2747,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; @@ -2017,21 +2765,22 @@ wl_iw_iscan_get_aplist( for (i = 0, dwrq->length = 0; i < list->count && dwrq->length < IW_MAX_AP; i++) { bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : list->bss_info; - ASSERT(((uintptr)bi + dtoh32(bi->length)) <= ((uintptr)list + - WLC_IW_ISCAN_MAXLEN)); - + if ((dtoh32(bi->length) > WLC_IW_ISCAN_MAXLEN) || + (((uintptr)bi + dtoh32(bi->length)) > ((uintptr)list + WLC_IW_ISCAN_MAXLEN))) { + WL_ERROR(("%s: Scan results out of bounds: %u\n",__FUNCTION__,dtoh32(bi->length))); + return -E2BIG; + } + if (!(dtoh16(bi->capability) & DOT11_CAP_ESS)) continue; - memcpy(addr[dwrq->length].sa_data, &bi->BSSID, ETHER_ADDR_LEN); addr[dwrq->length].sa_family = ARPHRD_ETHER; qual[dwrq->length].qual = rssi_to_qual(dtoh16(bi->RSSI)); qual[dwrq->length].level = 0x100 + dtoh16(bi->RSSI); qual[dwrq->length].noise = 0x100 + bi->phy_noise; - #if WIRELESS_EXT > 18 qual[dwrq->length].updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM; #else @@ -2063,7 +2812,10 @@ wl_iw_iscan_prep(wl_scan_params_t *params, wlc_ssid_t *ssid) params->passive_time = -1; params->home_time = -1; params->channel_num = 0; - +#if defined(CONFIG_FIRST_SCAN) + if (g_first_broadcast_scan == BROADCAST_SCAN_FIRST_STARTED) + params->passive_time = 30; +#endif params->nprobes = htod32(params->nprobes); params->active_time = htod32(params->active_time); params->passive_time = htod32(params->passive_time); @@ -2077,35 +2829,25 @@ 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__)); + 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); - 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); + 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)); - err = wl_iw_iscan_prep(¶ms->params, ssid); - - 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; } @@ -2116,7 +2858,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); } } @@ -2142,6 +2884,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) { @@ -2176,27 +2919,32 @@ 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)); + 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_TRACE(("results->buflen = %d\n", results->buflen)); - status = dtoh32(list_buf->status); + 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; } static void wl_iw_force_specific_scan(iscan_info_t *iscan) { - WL_TRACE(("%s force Specific SCAN for %s\n", __FUNCTION__, g_specific_ssid.SSID)); + WL_SCAN(("%s force Specific SCAN for %s\n", __FUNCTION__, g_specific_ssid.SSID)); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) rtnl_lock(); #endif @@ -2214,9 +2962,11 @@ static void wl_iw_send_scan_complete(iscan_info_t *iscan) memset(&wrqu, 0, sizeof(wrqu)); wireless_send_event(iscan->dev, SIOCGIWSCAN, &wrqu, NULL); +#if defined(CONFIG_FIRST_SCAN) if (g_first_broadcast_scan == BROADCAST_SCAN_FIRST_STARTED) g_first_broadcast_scan = BROADCAST_SCAN_FIRST_RESULT_READY; - WL_TRACE(("Send Event ISCAN complete\n")); +#endif + WL_SCAN(("Send Event ISCAN complete\n")); #endif } @@ -2232,13 +2982,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; @@ -2254,7 +3006,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; @@ -2262,7 +3014,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 @@ -2276,18 +3028,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); @@ -2297,11 +3049,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; } @@ -2317,6 +3069,7 @@ _iscan_sysioc_thread(void *data) } #endif +#if !defined(CSCAN) static void wl_iw_set_ss_cache_timer_flag(void) @@ -2482,18 +3235,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; @@ -2522,7 +3271,7 @@ __u16 *merged_len) mutex_lock(&wl_cache_lock); node = g_ss_cache_ctrl.m_cache_head; for (;node;) { - list_merge = (wl_scan_results_t *)node; + list_merge = (wl_scan_results_t *)&node->buflen; WL_TRACE(("%s: Cached Specific APs list=%d\n", __FUNCTION__, list_merge->count)); if (buflen_from_user - *merged_len > 0) { *merged_len += (__u16) wl_iw_get_scan_prep(list_merge, info, @@ -2573,6 +3322,7 @@ wl_iw_delete_bss_from_ss_cache(void *addr) return 0; } +#endif static int @@ -2586,6 +3336,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__)); @@ -2606,14 +3361,16 @@ wl_iw_set_scan( if (wrqu->data.length == sizeof(struct iw_scan_req)) { if (wrqu->data.flags & IW_SCAN_THIS_ESSID) { struct iw_scan_req *req = (struct iw_scan_req *)extra; +#if defined(CONFIG_FIRST_SCAN) if (g_first_broadcast_scan != BROADCAST_SCAN_FIRST_RESULT_CONSUMED) { - WL_TRACE(("%s Ignoring SC %s first BC is not done = %d\n", \ + WL_ERROR(("%s Ignoring SC %s first BC is not done = %d\n", \ __FUNCTION__, req->essid, \ g_first_broadcast_scan)); return -EBUSY; } +#endif if (g_scan_specified_ssid) { - WL_TRACE(("%s Specific SCAN is not done ignore scan for = %s \n", \ + WL_SCAN(("%s Specific SCAN is not done ignore scan for = %s \n", \ __FUNCTION__, req->essid)); return -EBUSY; } @@ -2631,8 +3388,8 @@ wl_iw_set_scan( #endif 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 */ + WL_SCAN(("Set SCAN for %s failed with %d\n", g_specific_ssid.SSID, error)); + g_scan_specified_ssid = 0; return -EBUSY; } @@ -2646,19 +3403,16 @@ wl_iw_iscan_set_scan_broadcast_prep(struct net_device *dev, uint flag) wlc_ssid_t ssid; iscan_info_t *iscan = g_iscan; +#if defined(CONFIG_FIRST_SCAN) if (g_first_broadcast_scan == BROADCAST_SCAN_FIRST_IDLE) { g_first_broadcast_scan = BROADCAST_SCAN_FIRST_STARTED; - WL_TRACE(("%s: First Brodcast scan was forced\n", __FUNCTION__)); + WL_SCAN(("%s: First Brodcast scan was forced\n", __FUNCTION__)); } else if (g_first_broadcast_scan == BROADCAST_SCAN_FIRST_STARTED) { - WL_TRACE(("%s: ignore ISCAN request first BS is not done yet\n", __FUNCTION__)); + WL_SCAN(("%s: ignore ISCAN request first BS is not done yet\n", __FUNCTION__)); return 0; } - - memset(&ssid, 0, sizeof(ssid)); - - iscan->list_cur = iscan->list_hdr; - iscan->iscan_state = ISCAN_STATE_SCANING; +#endif #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) if (flag) @@ -2668,8 +3422,15 @@ wl_iw_iscan_set_scan_broadcast_prep(struct net_device *dev, uint flag) dev_wlc_ioctl(dev, WLC_SET_PASSIVE_SCAN, &iscan->scan_flag, sizeof(iscan->scan_flag)); wl_iw_set_event_mask(dev); - WL_TRACE(("+++: Set Broadcast ISCAN\n")); + WL_SCAN(("+++: 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)) @@ -2683,6 +3444,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, @@ -2693,31 +3455,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)); + WL_SCAN(("%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; + WL_SCAN(("\n>%s: Not executed, reason -'SOFTAP is active'\n", __FUNCTION__)); + 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; + WL_SCAN(("%s: driver is not up yet after START\n", __FUNCTION__)); + goto set_scan_end; } +#ifdef PNO_SUPPORT + if (dhd_dev_get_pno_status(dev)) { + WL_SCAN(("%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", \ + WL_SCAN(("%s Specific SCAN already running ignoring BC scan\n", \ __FUNCTION__)); - return EBUSY; + ret = EBUSY; + goto set_scan_end; } memset(&ssid, 0, sizeof(ssid)); @@ -2728,32 +3504,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; + WL_SCAN(("%s ISCAN already in progress \n", __FUNCTION__)); + goto set_scan_end; } } } #endif +#if defined(CONFIG_FIRST_SCAN) && !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 @@ -2795,6 +3586,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, @@ -2803,6 +3620,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) { @@ -2841,6 +3660,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 @@ -2848,6 +3696,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, @@ -2860,8 +3709,12 @@ wl_iw_get_scan_prep( wl_bss_info_t *bi = NULL; char *event = extra, *end = extra + max_size - WE_ADD_EVENT_FIX, *value; int ret = 0; + int channel; - ASSERT(list); + if (!list) { + WL_ERROR(("%s: Null list pointer",__FUNCTION__)); + return ret; + } for (i = 0; i < list->count && i < IW_MAX_AP; i++) { @@ -2869,7 +3722,7 @@ wl_iw_get_scan_prep( WL_ERROR(("%s : list->version %d != WL_BSS_INFO_VERSION\n", \ __FUNCTION__, list->version)); return ret; - } + } bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : list->bss_info; @@ -2885,7 +3738,6 @@ wl_iw_get_scan_prep( iwe.u.data.flags = 1; event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, bi->SSID); - if (dtoh16(bi->capability) & (DOT11_CAP_ESS | DOT11_CAP_IBSS)) { iwe.cmd = SIOCGIWMODE; if (dtoh16(bi->capability) & DOT11_CAP_ESS) @@ -2895,15 +3747,14 @@ wl_iw_get_scan_prep( event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_UINT_LEN); } - 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); - iwe.cmd = IWEVQUAL; iwe.u.qual.qual = rssi_to_qual(dtoh16(bi->RSSI)); iwe.u.qual.level = 0x100 + dtoh16(bi->RSSI); @@ -2920,7 +3771,6 @@ wl_iw_get_scan_prep( iwe.u.data.length = 0; event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)event); - if (bi->rateset.count) { if (((event -extra) + IW_EV_LCP_LEN) <= (uintptr)end) { value = event + IW_EV_LCP_LEN; @@ -2960,11 +3810,15 @@ 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)); @@ -2973,13 +3827,13 @@ wl_iw_get_scan( return -EINVAL; } - if ((error = dev_wlc_ioctl(dev, WLC_GET_CHANNEL, &ci, sizeof(ci)))) return error; ci.scan_channel = dtoh32(ci.scan_channel); 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; @@ -2997,7 +3851,7 @@ wl_iw_get_scan( else { g_ss_cache_ctrl.m_cons_br_scan_cnt++; } - +#endif if (g_scan_specified_ssid) { @@ -3012,10 +3866,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); @@ -3032,6 +3888,7 @@ wl_iw_get_scan( return -EINVAL; } +#if !defined(CSCAN) if (g_scan_specified_ssid) { wl_iw_add_bss_to_ss_cache(list); @@ -3067,6 +3924,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) @@ -3082,6 +3970,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 @@ -3101,10 +3990,13 @@ 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)); + WL_SCAN(("%s %s buflen_from_user %d:\n", dev->name, __FUNCTION__, dwrq->length)); #if defined(SOFTAP) if (ap_cfg_running) { @@ -3118,17 +4010,20 @@ wl_iw_iscan_get_scan( return -EINVAL; } +#if defined(CONFIG_FIRST_SCAN) if (g_first_broadcast_scan < BROADCAST_SCAN_FIRST_RESULT_READY) { WL_TRACE(("%s %s: first ISCAN results are NOT ready yet \n", \ dev->name, __FUNCTION__)); return -EAGAIN; } - +#endif + 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; @@ -3148,6 +4043,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; @@ -3167,26 +4063,27 @@ wl_iw_iscan_get_scan( bi = NULL; for (ii = 0; ii < list->count && apcnt < IW_MAX_AP; apcnt++, ii++) { bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : list->bss_info; - ASSERT(((uintptr)bi + dtoh32(bi->length)) <= ((uintptr)list + - WLC_IW_ISCAN_MAXLEN)); - + if ((dtoh32(bi->length) > WLC_IW_ISCAN_MAXLEN) || + (((uintptr)bi + dtoh32(bi->length)) > ((uintptr)list + WLC_IW_ISCAN_MAXLEN))) { + WL_ERROR(("%s: Scan results out of bounds: %u\n",__FUNCTION__,dtoh32(bi->length))); + return -E2BIG; + } + if (event + ETHER_ADDR_LEN + bi->SSID_len + IW_EV_UINT_LEN + IW_EV_FREQ_LEN + IW_EV_QUAL_LEN >= end) return -E2BIG; - + iwe.cmd = SIOCGIWAP; iwe.u.ap_addr.sa_family = ARPHRD_ETHER; memcpy(iwe.u.ap_addr.sa_data, &bi->BSSID, ETHER_ADDR_LEN); event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_ADDR_LEN); - iwe.u.data.length = dtoh32(bi->SSID_len); iwe.cmd = SIOCGIWESSID; iwe.u.data.flags = 1; event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, bi->SSID); - if (dtoh16(bi->capability) & (DOT11_CAP_ESS | DOT11_CAP_IBSS)) { iwe.cmd = SIOCGIWMODE; if (dtoh16(bi->capability) & DOT11_CAP_ESS) @@ -3196,25 +4093,22 @@ wl_iw_iscan_get_scan( event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_UINT_LEN); } - 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); - iwe.cmd = IWEVQUAL; iwe.u.qual.qual = rssi_to_qual(dtoh16(bi->RSSI)); iwe.u.qual.level = 0x100 + dtoh16(bi->RSSI); iwe.u.qual.noise = 0x100 + bi->phy_noise; event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_QUAL_LEN); - wl_iw_handle_scanresults_ies(&event, end, info, bi); - iwe.cmd = SIOCGIWENCODE; if (dtoh16(bi->capability) & DOT11_CAP_PRIVACY) iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; @@ -3223,7 +4117,6 @@ wl_iw_iscan_get_scan( iwe.u.data.length = 0; event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)event); - if (bi->rateset.count) { if (event + IW_MAX_BITRATES*IW_EV_PARAM_LEN >= end) return -E2BIG; @@ -3246,18 +4139,18 @@ 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 */ +#if defined(CONFIG_FIRST_SCAN) g_first_broadcast_scan = BROADCAST_SCAN_FIRST_RESULT_CONSUMED; +#endif WL_TRACE(("%s return to WE %d bytes APs=%d\n", __FUNCTION__, dwrq->length, counter)); - if (!dwrq->length) - return -EAGAIN; - return 0; } #endif @@ -3271,6 +4164,8 @@ wl_iw_set_essid( ) { int error; + wl_join_params_t join_params; + int join_params_size; WL_TRACE(("%s: SIOCSIWESSID\n", dev->name)); @@ -3291,11 +4186,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; } @@ -3323,7 +4231,6 @@ wl_iw_get_essid( ssid.SSID_len = dtoh32(ssid.SSID_len); - memcpy(extra, ssid.SSID, ssid.SSID_len); dwrq->length = ssid.SSID_len; @@ -3348,7 +4255,6 @@ wl_iw_set_nick( if (!extra) return -EINVAL; - if (dwrq->length > sizeof(iw->nickname)) return -E2BIG; @@ -3918,11 +4824,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; @@ -3954,7 +4870,7 @@ wl_iw_set_encodeext( int error; struct iw_encode_ext *iwe; - WL_TRACE(("%s: SIOCSIWENCODEEXT\n", dev->name)); + WL_WSEC(("%s: SIOCSIWENCODEEXT\n", dev->name)); CHECK_EXTRA_FOR_NULL(extra); @@ -4039,6 +4955,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; } @@ -4178,7 +5100,7 @@ wl_iw_get_encodeext( char *extra ) { - WL_TRACE(("%s: SIOCGIWENCODEEXT\n", dev->name)); + WL_WSEC(("%s: SIOCGIWENCODEEXT\n", dev->name)); return 0; } @@ -4196,7 +5118,7 @@ wl_iw_set_wpaauth( int val = 0; wl_iw_t *iw = *(wl_iw_t **)netdev_priv(dev); - WL_TRACE(("%s: SIOCSIWAUTH\n", dev->name)); + WL_WSEC(("%s: SIOCSIWAUTH\n", dev->name)); #if defined(SOFTAP) if (ap_cfg_running) { @@ -4208,7 +5130,7 @@ wl_iw_set_wpaauth( paramid = vwrq->flags & IW_AUTH_INDEX; paramval = vwrq->value; - WL_TRACE(("%s: SIOCSIWAUTH, paramid = 0x%0x, paramval = 0x%0x\n", + WL_WSEC(("%s: SIOCSIWAUTH, paramid = 0x%0x, paramval = 0x%0x\n", dev->name, paramid, paramval)); switch (paramid) { @@ -4222,7 +5144,9 @@ wl_iw_set_wpaauth( else if (paramval & IW_AUTH_WPA_VERSION_WPA2) val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED; #endif - WL_INFORM(("%s: %d: setting wpa_auth to 0x%0x\n", __FUNCTION__, __LINE__, val)); + else if (paramval & IW_AUTH_WAPI_VERSION_1) + val = WPA_AUTH_WAPI; + WL_WSEC(("%s: %d: setting wpa_auth to 0x%0x\n", __FUNCTION__, __LINE__, val)); if ((error = dev_wlc_intvar_set(dev, "wpa_auth", val))) return error; break; @@ -4236,6 +5160,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; @@ -4250,24 +5176,28 @@ wl_iw_set_wpaauth( WL_WSEC(("%s: %s: 'Privacy invoked' TRUE but clearing wsec, assuming " "we're a WPS enrollee\n", dev->name, __FUNCTION__)); if ((error = dev_wlc_intvar_set(dev, "is_WPS_enrollee", TRUE))) { - WL_WSEC(("Failed to set iovar is_WPS_enrollee\n")); + WL_ERROR(("Failed to set iovar is_WPS_enrollee\n")); return error; } } else if (val) { if ((error = dev_wlc_intvar_set(dev, "is_WPS_enrollee", FALSE))) { - WL_WSEC(("Failed to clear iovar is_WPS_enrollee\n")); + WL_ERROR(("Failed to clear iovar is_WPS_enrollee\n")); return error; } } - if ((error = dev_wlc_intvar_set(dev, "wsec", val))) + if ((error = dev_wlc_intvar_set(dev, "wsec", val))) { + WL_ERROR(("Failed to set 'wsec'iovar\n")); return error; + } break; case IW_AUTH_KEY_MGMT: - if ((error = dev_wlc_intvar_get(dev, "wpa_auth", &val))) + if ((error = dev_wlc_intvar_get(dev, "wpa_auth", &val))) { + WL_ERROR(("Failed to get 'wpa_auth'iovar\n")); return error; + } if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) { if (paramval & IW_AUTH_KEY_MGMT_PSK) @@ -4283,17 +5213,24 @@ wl_iw_set_wpaauth( val = WPA2_AUTH_UNSPECIFIED; } #endif - WL_INFORM(("%s: %d: setting wpa_auth to %d\n", __FUNCTION__, __LINE__, val)); - if ((error = dev_wlc_intvar_set(dev, "wpa_auth", val))) + if (paramval & (IW_AUTH_KEY_MGMT_WAPI_PSK | IW_AUTH_KEY_MGMT_WAPI_CERT)) + val = WPA_AUTH_WAPI; + WL_WSEC(("%s: %d: setting wpa_auth to %d\n", __FUNCTION__, __LINE__, val)); + if ((error = dev_wlc_intvar_set(dev, "wpa_auth", val))) { + WL_ERROR(("Failed to set 'wpa_auth'iovar\n")); return error; + } + break; case IW_AUTH_TKIP_COUNTERMEASURES: - dev_wlc_bufvar_set(dev, "tkip_countermeasures", (char *)¶mval, 1); + if ((error = dev_wlc_bufvar_set(dev, "tkip_countermeasures", \ + (char *)¶mval, sizeof(paramval)))) + WL_WSEC(("%s: tkip_countermeasures failed %d\n", __FUNCTION__, error)); break; case IW_AUTH_80211_AUTH_ALG: - WL_INFORM(("Setting the D11auth %d\n", paramval)); + WL_WSEC(("Setting the D11auth %d\n", paramval)); if (paramval == IW_AUTH_ALG_OPEN_SYSTEM) val = 0; else if (paramval == IW_AUTH_ALG_SHARED_KEY) @@ -4310,15 +5247,21 @@ wl_iw_set_wpaauth( if (paramval == 0) { iw->pwsec = 0; iw->gwsec = 0; - if ((error = dev_wlc_intvar_get(dev, "wsec", &val))) + if ((error = dev_wlc_intvar_get(dev, "wsec", &val))) { + WL_ERROR(("Failed to get 'wsec'iovar\n")); return error; + } if (val & (TKIP_ENABLED | AES_ENABLED)) { val &= ~(TKIP_ENABLED | AES_ENABLED); dev_wlc_intvar_set(dev, "wsec", val); } val = 0; - WL_INFORM(("%s: %d: setting wpa_auth to %d\n", __FUNCTION__, __LINE__, val)); - dev_wlc_intvar_set(dev, "wpa_auth", 0); + + WL_INFORM(("%s: %d: setting wpa_auth to %d\n", + __FUNCTION__, __LINE__, val)); + error = dev_wlc_intvar_set(dev, "wpa_auth", 0); + if (error) + WL_ERROR(("Failed to set 'wpa_auth'iovar\n")); return error; } @@ -4326,11 +5269,17 @@ wl_iw_set_wpaauth( break; case IW_AUTH_DROP_UNENCRYPTED: - dev_wlc_bufvar_set(dev, "wsec_restrict", (char *)¶mval, 1); + error = dev_wlc_bufvar_set(dev, "wsec_restrict", \ + (char *)¶mval, sizeof(paramval)); + if (error) + WL_ERROR(("%s: wsec_restrict %d\n", __FUNCTION__, error)); break; case IW_AUTH_RX_UNENCRYPTED_EAPOL: - dev_wlc_bufvar_set(dev, "rx_unencrypted_eapol", (char *)¶mval, 1); + error = dev_wlc_bufvar_set(dev, "rx_unencrypted_eapol", \ + (char *)¶mval, sizeof(paramval)); + if (error) + WL_WSEC(("%s: rx_unencrypted_eapol %d\n", __FUNCTION__, error)); break; #if WIRELESS_EXT > 17 @@ -4368,6 +5317,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; } @@ -4441,15 +5408,24 @@ wl_iw_get_wpaauth( break; case IW_AUTH_TKIP_COUNTERMEASURES: - dev_wlc_bufvar_get(dev, "tkip_countermeasures", (char *)¶mval, 1); + error = dev_wlc_bufvar_get(dev, "tkip_countermeasures", \ + (char *)¶mval, sizeof(paramval)); + if (error) + WL_ERROR(("%s get tkip_countermeasures %d\n", __FUNCTION__, error)); break; case IW_AUTH_DROP_UNENCRYPTED: - dev_wlc_bufvar_get(dev, "wsec_restrict", (char *)¶mval, 1); + error = dev_wlc_bufvar_get(dev, "wsec_restrict", \ + (char *)¶mval, sizeof(paramval)); + if (error) + WL_ERROR(("%s get wsec_restrict %d\n", __FUNCTION__, error)); break; case IW_AUTH_RX_UNENCRYPTED_EAPOL: - dev_wlc_bufvar_get(dev, "rx_unencrypted_eapol", (char *)¶mval, 1); + error = dev_wlc_bufvar_get(dev, "rx_unencrypted_eapol", \ + (char *)¶mval, sizeof(paramval)); + if (error) + WL_ERROR(("%s get rx_unencrypted_eapol %d\n", __FUNCTION__, error)); break; case IW_AUTH_80211_AUTH_ALG: @@ -4603,6 +5579,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, @@ -4645,6 +5622,7 @@ static int wl_bssiovar_mkbuf( *perr = 0; return iolen; } +#endif int get_user_params(char *user_params, struct iw_point *dwrq) @@ -4663,25 +5641,451 @@ 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_SCAN(("%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; + } + + if (iscan->iscan_ex_param_size > WLC_IOCTL_MAXLEN) { + WL_ERROR(("%s wrong ex_param_size %d", \ + __FUNCTION__, iscan->iscan_ex_param_size)); + return -1; + } + 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); + + + 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 defined(CONFIG_FIRST_SCAN) + 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; + } + } +#endif + + 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) { + struct net_device *dev = (struct net_device *)data; + wl_iw_t *iw; int ret = 0; + unsigned long flags; + + net_os_wake_lock(dev); DAEMONIZE("wl0_eth_wthread"); - WL_TRACE(("\n>%s threda started:, PID:%x\n", __FUNCTION__, current->pid)); + WL_TRACE(("\n>%s thread started:, PID:%x\n", __FUNCTION__, current->pid)); + iw = *(wl_iw_t **)netdev_priv(dev); + if (!iw) { + WL_ERROR(("%s: dev is null\n", __FUNCTION__)); + ret = -1; + goto fail; + } +#ifndef BCMSDIOH_STD if (down_timeout(&ap_eth_sema, msecs_to_jiffies(5000)) != 0) { WL_ERROR(("\n%s: sap_eth_sema timeout \n", __FUNCTION__)); ret = -1; goto fail; } +#endif + flags = dhd_os_spin_lock(iw->pub); if (!ap_net_dev) { WL_ERROR((" ap_net_dev is null !!!")); ret = -1; + dhd_os_spin_unlock(iw->pub, flags); goto fail; } @@ -4690,6 +6094,8 @@ static int thr_wait_for_2nd_eth_dev(void *data) ap_cfg_running = TRUE; + dhd_os_spin_unlock(iw->pub, flags); + bcm_mdelay(500); wl_iw_send_priv_event(priv_dev, "AP_SET_CFG_OK"); @@ -4697,9 +6103,15 @@ static int thr_wait_for_2nd_eth_dev(void *data) fail: WL_TRACE(("\n>%s, thread completed\n", __FUNCTION__)); + net_os_wake_unlock(dev); + + complete_and_exit(&ap_cfg_exited, 0); 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; @@ -4710,12 +6122,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)); @@ -4745,6 +6175,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; } @@ -4757,21 +6192,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)); @@ -4781,8 +6218,16 @@ 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, NULL); + ap_cfg_running = FALSE; + } +#endif + if (ap_cfg_running == FALSE) { +#ifndef AP_ONLY sema_init(&ap_eth_sema, 0); mpc = 0; @@ -4790,6 +6235,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)))) { @@ -4814,7 +6260,10 @@ static int set_ap_cfg(struct net_device *dev, struct ap_profile *ap) iolen = wl_bssiovar_mkbuf("apsta", bsscfg_index, &apsta_var, sizeof(apsta_var)+4, buf, sizeof(buf), &mkvar_err); - ASSERT(iolen); + + if (iolen <= 0) + goto fail; + if ((res = dev_wlc_ioctl(dev, WLC_SET_VAR, buf, iolen)) < 0) { WL_ERROR(("%s fail to set apsta \n", __FUNCTION__)); goto fail; @@ -4835,7 +6284,7 @@ static int set_ap_cfg(struct net_device *dev, struct ap_profile *ap) goto fail; } - res = wl_iw_softap_deassoc_stations(ap_net_dev); + res = wl_iw_softap_deassoc_stations(ap_net_dev, NULL); if ((res = dev_iw_write_cfg1_bss_var(dev, 0)) < 0) { @@ -4844,6 +6293,32 @@ static int set_ap_cfg(struct net_device *dev, struct ap_profile *ap) } } + if (strlen(ap->country_code)) { + int error = 0; + if ((error = dev_wlc_ioctl(dev, WLC_SET_COUNTRY, + ap->country_code, sizeof(ap->country_code))) >= 0) { + WL_SOFTAP(("%s: set country %s OK\n", + __FUNCTION__, ap->country_code)); + dhd_bus_country_set(dev, &ap->country_code[0]); + } else { + WL_ERROR(("%s: ERROR:%d setting country %s\n", + __FUNCTION__, error, ap->country_code)); + } + } else { + WL_SOFTAP(("%s: Country code is not specified," + " will use Radio's default\n", + __FUNCTION__)); + } + + iolen = wl_bssiovar_mkbuf("closednet", + bsscfg_index, &ap->closednet, sizeof(ap->closednet)+4, + buf, sizeof(buf), &mkvar_err); + ASSERT(iolen); + if ((res = dev_wlc_ioctl(dev, WLC_SET_VAR, buf, iolen)) < 0) { + WL_ERROR(("%s failed to set 'closednet'for apsta \n", __FUNCTION__)); + goto fail; + } + if ((ap->channel == 0) && (get_softap_auto_channel(dev, ap) < 0)) { ap->channel = 1; @@ -4874,6 +6349,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); @@ -4883,8 +6367,10 @@ static int set_ap_cfg(struct net_device *dev, struct ap_profile *ap) goto fail; } if (ap_cfg_running == FALSE) { - kernel_thread(thr_wait_for_2nd_eth_dev, 0, 0); + init_completion(&ap_cfg_exited); + ap_cfg_pid = kernel_thread(thr_wait_for_2nd_eth_dev, dev, 0); } else { + ap_cfg_pid = -1; if (ap_net_dev == NULL) { WL_ERROR(("%s ERROR: ap_net_dev is NULL !!!\n", __FUNCTION__)); goto fail; @@ -4903,12 +6389,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; } @@ -4919,13 +6406,19 @@ 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")); WL_SOFTAP((" ssid = '%s'\n", ap->ssid)); WL_SOFTAP((" security = '%s'\n", ap->sec)); - if (ap->key[0] != '\0') + if (ap->key[0] != '\0') { WL_SOFTAP((" key = '%s'\n", ap->key)); + } WL_SOFTAP((" channel = %d\n", ap->channel)); WL_SOFTAP((" max scb = %d\n", ap->max_scb)); @@ -4941,7 +6434,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; @@ -5027,6 +6519,7 @@ static int wl_iw_set_ap_security(struct net_device *dev, struct ap_profile *ap) if (key_len < WSEC_MAX_PSK_LEN) { unsigned char output[2*SHA1HashSize]; char key_str_buf[WSEC_MAX_PSK_LEN+1]; + bzero(output, 2*SHA1HashSize); WL_SOFTAP(("%s: do passhash...\n", __FUNCTION__)); @@ -5059,6 +6552,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; } @@ -5072,9 +6575,8 @@ int get_parmeter_from_string( int parm_str_len; char *param_str_begin; 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; @@ -5129,27 +6631,36 @@ int get_parmeter_from_string( return 0; } else { - WL_ERROR(("\n %s: ERROR: can't find token:%s in str:%s \n", - __FUNCTION__, token, orig_str)); + WL_ERROR(("\n %s: No token:%s in str:%s\n", + __FUNCTION__, token, *str_ptr)); return -1; } } - -static int wl_iw_softap_deassoc_stations(struct net_device *dev) +static int wl_iw_softap_deassoc_stations(struct net_device *dev, u8 *mac) { int i; int res = 0; char mac_buf[128] = {0}; - struct maclist *assoc_maclist = (struct maclist *)mac_buf; + char z_mac[6] = {0, 0, 0, 0, 0, 0}; + char *sta_mac; + struct maclist *assoc_maclist = (struct maclist *) mac_buf; + bool deauth_all = false; + + if (mac == NULL) { + deauth_all = true; + sta_mac = z_mac; + } else { + sta_mac = mac; + } memset(assoc_maclist, 0, sizeof(mac_buf)); assoc_maclist->count = 8; res = dev_wlc_ioctl(dev, WLC_GET_ASSOCLIST, assoc_maclist, 128); if (res != 0) { - WL_SOFTAP((" Error:%d in :%s, Couldn't get ASSOC List\n", res, __FUNCTION__)); + WL_SOFTAP(("%s: Error:%d Couldn't get ASSOC List\n", __FUNCTION__, res)); return res; } @@ -5160,18 +6671,19 @@ static int wl_iw_softap_deassoc_stations(struct net_device *dev) scbval.val = htod32(1); bcopy(&assoc_maclist->ea[i], &scbval.ea, ETHER_ADDR_LEN); - WL_SOFTAP(("deauth STA:%d \n", i)); - res |= dev_wlc_ioctl(dev, WLC_SCB_DEAUTHENTICATE_FOR_REASON, + if (deauth_all || (memcmp(&scbval.ea, sta_mac, ETHER_ADDR_LEN) == 0)) { + WL_SOFTAP(("%s, deauth STA:%d \n", __FUNCTION__, i)); + res |= dev_wlc_ioctl(dev, WLC_SCB_DEAUTHENTICATE_FOR_REASON, &scbval, sizeof(scb_val_t)); + } } } else { WL_SOFTAP((" STA ASSOC list is empty\n")); } - if (res != 0) - WL_SOFTAP((" Error:%d in :%s\n", res, __FUNCTION__)); - else if (assoc_maclist->count) { - + if (res != 0) { + WL_ERROR(("%s: Error:%d\n", __FUNCTION__, res)); + } else if (assoc_maclist->count) { bcm_mdelay(200); } return res; @@ -5192,13 +6704,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, NULL); +#else + wl_iw_softap_deassoc_stations(ap_net_dev, NULL); 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); @@ -5210,9 +6726,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; } @@ -5322,9 +6838,15 @@ 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)); + WL_SOFTAP(("%s: rcvd IWPRIV IOCTL: for dev:%s\n", __FUNCTION__, dev->name)); + +#ifndef AP_ONLY + if (ap_cfg_pid >= 0) { + wait_for_completion(&ap_cfg_exited); + ap_cfg_pid = -1; + } if ((res = wl_iw_set_ap_security(dev, &my_ap)) != 0) { WL_ERROR((" %s ERROR setting SOFTAP security in :%d\n", __FUNCTION__, res)); @@ -5336,9 +6858,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; } @@ -5346,84 +6869,108 @@ iwpriv_en_ap_bss( static int get_assoc_sta_list(struct net_device *dev, char *buf, int len) { - WL_TRACE(("calling dev_wlc_ioctl(dev:%p, cmd:%d, buf:%p, len:%d)\n", - dev, WLC_GET_ASSOCLIST, buf, len)); + WL_TRACE(("%s: dev_wlc_ioctl(dev:%p, cmd:%d, buf:%p, len:%d)\n", + __FUNCTION__, dev, WLC_GET_ASSOCLIST, buf, len)); - dev_wlc_ioctl(dev, WLC_GET_ASSOCLIST, buf, len); + return dev_wlc_ioctl(dev, WLC_GET_ASSOCLIST, buf, len); - return 0; } +void check_error(int res, const char *msg, const char *func, int line) +{ + if (res != 0) + WL_ERROR(("%s, %d function:%s, line:%d\n", msg, res, func, line)); +} + static int -set_ap_mac_list(struct net_device *dev, char *buf) +set_ap_mac_list(struct net_device *dev, void *buf) { struct mac_list_set *mac_list_set = (struct mac_list_set *)buf; - struct maclist *white_maclist = (struct maclist *)&mac_list_set->white_list; - struct maclist *black_maclist = (struct maclist *)&mac_list_set->black_list; - int mac_mode = mac_list_set->mode; + struct maclist *maclist = (struct maclist *)&mac_list_set->mac_list; int length; int i; + int mac_mode = mac_list_set->mode; + int ioc_res = 0; + ap_macmode = mac_list_set->mode; + + bzero(&ap_black_list, sizeof(struct mflist)); - ap_macmode = mac_mode; if (mac_mode == MACLIST_MODE_DISABLED) { - bzero(&ap_black_list, sizeof(struct mflist)); - - dev_wlc_ioctl(dev, WLC_SET_MACMODE, &mac_mode, sizeof(mac_mode)); + ioc_res = dev_wlc_ioctl(dev, WLC_SET_MACMODE, &mac_mode, sizeof(mac_mode)); + check_error(ioc_res, "ioctl ERROR:", __FUNCTION__, __LINE__); + WL_SOFTAP(("%s: MAC filtering disabled\n", __FUNCTION__)); } else { + scb_val_t scbval; char mac_buf[256] = {0}; struct maclist *assoc_maclist = (struct maclist *) mac_buf; - mac_mode = MACLIST_MODE_ALLOW; + bcopy(maclist, &ap_black_list, sizeof(ap_black_list)); - dev_wlc_ioctl(dev, WLC_SET_MACMODE, &mac_mode, sizeof(mac_mode)); + ioc_res = dev_wlc_ioctl(dev, WLC_SET_MACMODE, &mac_mode, sizeof(mac_mode)); + check_error(ioc_res, "ioctl ERROR:", __FUNCTION__, __LINE__); - length = sizeof(white_maclist->count)+white_maclist->count*ETHER_ADDR_LEN; - dev_wlc_ioctl(dev, WLC_SET_MACLIST, white_maclist, length); - WL_SOFTAP(("White List, length %d:\n", length)); - for (i = 0; i < white_maclist->count; i++) + length = sizeof(maclist->count) + maclist->count*ETHER_ADDR_LEN; + dev_wlc_ioctl(dev, WLC_SET_MACLIST, maclist, length); + + WL_SOFTAP(("%s: applied MAC List, mode:%d, length %d:\n", + __FUNCTION__, mac_mode, length)); + for (i = 0; i < maclist->count; i++) WL_SOFTAP(("mac %d: %02X:%02X:%02X:%02X:%02X:%02X\n", - i, white_maclist->ea[i].octet[0], white_maclist->ea[i].octet[1], - white_maclist->ea[i].octet[2], - white_maclist->ea[i].octet[3], white_maclist->ea[i].octet[4], - white_maclist->ea[i].octet[5])); + i, maclist->ea[i].octet[0], maclist->ea[i].octet[1], \ + maclist->ea[i].octet[2], \ + maclist->ea[i].octet[3], maclist->ea[i].octet[4], \ + maclist->ea[i].octet[5])); - bcopy(black_maclist, &ap_black_list, sizeof(ap_black_list)); + assoc_maclist->count = 8; + ioc_res = dev_wlc_ioctl(dev, WLC_GET_ASSOCLIST, assoc_maclist, 256); + check_error(ioc_res, "ioctl ERROR:", __FUNCTION__, __LINE__); + WL_SOFTAP((" Cur assoc clients:%d\n", assoc_maclist->count)); - WL_SOFTAP(("Black List, size %d:\n", sizeof(ap_black_list))); - for (i = 0; i < ap_black_list.count; i++) - WL_SOFTAP(("mac %d: %02X:%02X:%02X:%02X:%02X:%02X\n", - i, ap_black_list.ea[i].octet[0], ap_black_list.ea[i].octet[1], - ap_black_list.ea[i].octet[2], - ap_black_list.ea[i].octet[3], - ap_black_list.ea[i].octet[4], ap_black_list.ea[i].octet[5])); - - dev_wlc_ioctl(dev, WLC_GET_ASSOCLIST, assoc_maclist, 256); - if (assoc_maclist->count) { - int j; + if (assoc_maclist->count) for (i = 0; i < assoc_maclist->count; i++) { - for (j = 0; j < white_maclist->count; j++) { - if (!bcmp(&assoc_maclist->ea[i], &white_maclist->ea[j], + int j; + bool assoc_mac_matched = false; + + WL_SOFTAP(("\n Cheking assoc STA: ")); + print_buf(&assoc_maclist->ea[i], 6, 7); + WL_SOFTAP(("with the b/w list:")); + + for (j = 0; j < maclist->count; j++) + if (!bcmp(&assoc_maclist->ea[i], &maclist->ea[j], ETHER_ADDR_LEN)) { - WL_SOFTAP(("match allow, let it be\n")); + + assoc_mac_matched = true; break; } - } - if (j == white_maclist->count) { - WL_SOFTAP(("match black, deauth it\n")); - scbval.val = htod32(1); - bcopy(&assoc_maclist->ea[i], &scbval.ea, + + if (((mac_mode == MACLIST_MODE_ALLOW) && !assoc_mac_matched) || + ((mac_mode == MACLIST_MODE_DENY) && assoc_mac_matched)) { + + WL_SOFTAP(("b-match or w-mismatch," + " do deauth/disassoc \n")); + scbval.val = htod32(1); + bcopy(&assoc_maclist->ea[i], &scbval.ea, \ ETHER_ADDR_LEN); - dev_wlc_ioctl(dev, - WLC_SCB_DEAUTHENTICATE_FOR_REASON, &scbval, - sizeof(scb_val_t)); + ioc_res = dev_wlc_ioctl(dev, + WLC_SCB_DEAUTHENTICATE_FOR_REASON, + &scbval, sizeof(scb_val_t)); + check_error(ioc_res, + "ioctl ERROR:", + __FUNCTION__, __LINE__); + + } else { + WL_SOFTAP((" no b/w list hits, let it be\n")); } - } + } else { + WL_SOFTAP(("No ASSOC CLIENTS\n")); } - } - return 0; + } + + WL_SOFTAP(("%s iocres:%d\n", __FUNCTION__, ioc_res)); + return ioc_res; } #endif @@ -5469,6 +7016,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 { @@ -5476,15 +7024,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; @@ -5549,6 +7103,32 @@ 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); +#ifdef CONFIG_US_NON_DFS_CHANNELS_ONLY + else if (strnicmp(extra, SETDFSCHANNELS_CMD, strlen(SETDFSCHANNELS_CMD)) == 0) + ret = wl_iw_set_dfs_channels(dev, info, (union iwreq_data *)dwrq, extra); +#endif +#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); @@ -5708,8 +7288,16 @@ static const iw_handler wl_iw_priv_handler[] = { (iw_handler)iwpriv_softap_stop, NULL, - (iw_handler)iwpriv_fw_reload + (iw_handler)iwpriv_fw_reload, + + NULL, + (iw_handler)iwpriv_set_ap_sta_disassoc, #endif +#if defined(CSCAN) + + NULL, + (iw_handler)iwpriv_set_cscan +#endif }; static const struct iw_priv_args wl_iw_priv_args[] = { @@ -5766,8 +7354,8 @@ static const struct iw_priv_args wl_iw_priv_args[] = { { WL_AP_STA_LIST, - 0, IW_PRIV_TYPE_CHAR | 0, + IW_PRIV_TYPE_CHAR | 1024, "AP_GET_STA_LIST" }, @@ -5805,6 +7393,21 @@ static const struct iw_priv_args wl_iw_priv_args[] = { IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 0, "WL_FW_RELOAD" }, + + { + WL_AP_STA_DISASSOC, + IW_PRIV_TYPE_CHAR | 256, + IW_PRIV_TYPE_CHAR | 0, + "AP_STA_DISASSOC" + }, +#endif +#if defined(CSCAN) + { + WL_COMBO_SCAN, + IW_PRIV_TYPE_CHAR | 1024, + 0, + "CSCAN" + }, #endif }; @@ -5836,11 +7439,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; } @@ -5905,14 +7511,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; } } @@ -5924,12 +7534,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; } @@ -6046,6 +7661,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)); @@ -6055,11 +7672,17 @@ 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)); switch (event_type) { + + case WLC_E_RELOAD: + WL_ERROR(("%s: Firmware ERROR %d\n", __FUNCTION__, status)); + net_os_send_hang_message(dev); + goto wl_iw_event_end; + #if defined(SOFTAP) case WLC_E_PRUNE: if (ap_cfg_running) { @@ -6102,20 +7725,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) { + memcpy(wrqu.addr.sa_data, &e->addr.octet, ETHER_ADDR_LEN); + wrqu.addr.sa_family = ARPHRD_ETHER; + cmd = SIOCGIWAP; + } + else if (status == WLC_E_STATUS_NO_NETWORKS) { + roam_no_success++; + if ((roam_no_success == 5) && (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; + } + } + 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; @@ -6128,7 +7772,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 { @@ -6142,7 +7790,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); @@ -6151,16 +7798,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: @@ -6241,15 +7895,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)); @@ -6278,8 +7947,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) @@ -6370,13 +8039,21 @@ wl_iw_bt_flag_set( struct net_device *dev, bool set) { +#if defined(BT_DHCP_USE_FLAGS) char buf_flag7_dhcp_on[8] = { 7, 00, 00, 00, 0x1, 0x0, 0x00, 0x00 }; char buf_flag7_default[8] = { 7, 00, 00, 00, 0x0, 0x00, 0x00, 0x00}; +#endif #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) rtnl_lock(); #endif +#if defined(BT_DHCP_eSCO_FIX) + set_btc_esco_params(dev, set); +#endif + +#if defined(BT_DHCP_USE_FLAGS) + WL_TRACE_COEX(("WI-FI priority boost via bt flags, set:%d\n", set)); if (set == TRUE) { dev_wlc_bufvar_set(dev, "btc_flags", (char *)&buf_flag7_dhcp_on[0], sizeof(buf_flag7_dhcp_on)); @@ -6385,6 +8062,7 @@ wl_iw_bt_flag_set( dev_wlc_bufvar_set(dev, "btc_flags", (char *)&buf_flag7_default[0], sizeof(buf_flag7_default)); } +#endif #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) rtnl_unlock(); @@ -6417,14 +8095,23 @@ _bt_dhcp_sysioc_thread(void *data) switch (g_bt->bt_state) { case BT_DHCP_START: + WL_TRACE_COEX(("%s bt_dhcp stm: started \n", __FUNCTION__)); g_bt->bt_state = BT_DHCP_OPPORTUNITY_WINDOW; - mod_timer(&g_bt->timer, jiffies + BT_DHCP_OPPORTUNITY_WINDOW_TIEM*HZ/1000); + mod_timer(&g_bt->timer, jiffies + BT_DHCP_OPPORTUNITY_WINDOW_TIME*HZ/1000); g_bt->timer_on = 1; break; case BT_DHCP_OPPORTUNITY_WINDOW: - WL_TRACE(("%s waiting for %d msec expired, force bt flag\n", \ - __FUNCTION__, BT_DHCP_OPPORTUNITY_WINDOW_TIEM)); + if (g_bt->dhcp_done) { + WL_TRACE_COEX(("%s DHCP Done before T1 expiration\n", \ + __FUNCTION__)); + g_bt->bt_state = BT_DHCP_IDLE; + g_bt->timer_on = 0; + break; + } + + WL_TRACE_COEX(("%s DHCP T1:%d expired\n", \ + __FUNCTION__, BT_DHCP_OPPORTUNITY_WINDOW_TIME)); if (g_bt->dev) wl_iw_bt_flag_set(g_bt->dev, TRUE); g_bt->bt_state = BT_DHCP_FLAG_FORCE_TIMEOUT; mod_timer(&g_bt->timer, jiffies + BT_DHCP_FLAG_FORCE_TIME*HZ/1000); @@ -6432,9 +8119,14 @@ _bt_dhcp_sysioc_thread(void *data) break; case BT_DHCP_FLAG_FORCE_TIMEOUT: - WL_TRACE(("%s waiting for %d msec expired remove bt flag\n", \ + if (g_bt->dhcp_done) { + WL_TRACE_COEX(("%s DHCP Done before T2 expiration\n", \ + __FUNCTION__)); + } else { + WL_TRACE_COEX(("%s DHCP wait interval T2:%d msec expired\n", __FUNCTION__, BT_DHCP_FLAG_FORCE_TIME)); - + } + if (g_bt->dev) wl_iw_bt_flag_set(g_bt->dev, FALSE); g_bt->bt_state = BT_DHCP_IDLE; g_bt->timer_on = 0; @@ -6447,7 +8139,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); } @@ -6509,33 +8201,49 @@ wl_iw_bt_init(struct net_device *dev) return 0; } -int wl_iw_attach(struct net_device *dev, void * dhdp) +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; #endif mutex_init(&wl_cache_lock); - mutex_init(&wl_start_lock); #if defined(WL_IW_USE_ISCAN) 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; +#if defined(CONFIG_FIRST_SCAN) g_first_broadcast_scan = BROADCAST_SCAN_FIRST_IDLE; + g_first_counter_scans = 0; g_iscan->scan_flag = 0; +#endif - iscan->timer_ms = 3000; + iscan->timer_ms = 8000; init_timer(&iscan->timer); iscan->timer.data = (ulong)iscan; iscan->timer.function = wl_iw_timerfunc; @@ -6561,11 +8269,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; } @@ -6587,6 +8296,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); @@ -6596,7 +8306,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) { @@ -6604,5 +8316,4 @@ void wl_iw_detach(void) wl_iw_send_priv_event(priv_dev, "AP_DOWN"); } #endif - } diff --git a/drivers/net/wireless/bcm4329/wl_iw.h b/drivers/net/wireless/bcm4329/wl_iw.h index aea656b4..928291fe 100644 --- a/drivers/net/wireless/bcm4329/wl_iw.h +++ b/drivers/net/wireless/bcm4329/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.15 2010/11/17 03:13:51 Exp $ */ @@ -34,6 +34,33 @@ #include #include +#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 SETDFSCHANNELS_CMD "SETDFSCHANNELS" + +#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,8 +88,9 @@ #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_AP_SPARE3 (SIOCIWFIRSTPRIV+31) +#define WL_AP_STA_DISASSOC (SIOCIWFIRSTPRIV+29) +#define WL_COMBO_SCAN (SIOCIWFIRSTPRIV+31) + #define G_SCAN_RESULTS (8*1024) #define WE_ADD_EVENT_FIX 0x80 #define G_WLAN_SET_ON 0 @@ -90,19 +118,18 @@ typedef struct wl_iw { dhd_pub_t * pub; } wl_iw_t; -#define WLC_IW_SS_CACHE_MAXLEN 512 +#define WLC_IW_SS_CACHE_MAXLEN 2048 #define WLC_IW_SS_CACHE_CTRL_FIELD_MAXLEN 32 #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 { + struct wl_iw_ss_cache *next; + int dirty; uint32 buflen; uint32 version; uint32 count; wl_bss_info_t bss_info[1]; - char dummy[WLC_IW_BSS_INFO_MAXLEN - sizeof(wl_bss_info_t)]; - int dirty; - struct wl_iw_ss_cache *next; } wl_iw_ss_cache_t; typedef struct wl_iw_ss_cache_ctrl { @@ -114,6 +141,7 @@ typedef struct wl_iw_ss_cache_ctrl { uint m_cons_br_scan_cnt; struct timer_list *m_timer; } wl_iw_ss_cache_ctrl_t; + typedef enum broadcast_first_scan { BROADCAST_SCAN_FIRST_IDLE = 0, BROADCAST_SCAN_FIRST_STARTED, @@ -132,12 +160,14 @@ struct ap_profile { uint32 channel; uint32 preamble; uint32 max_scb; + uint32 closednet; + char country_code[WLC_CNTRY_BUF_SZ]; }; #define MACLIST_MODE_DISABLED 0 -#define MACLIST_MODE_ENABLED 1 -#define MACLIST_MODE_ALLOW 2 +#define MACLIST_MODE_DENY 1 +#define MACLIST_MODE_ALLOW 2 struct mflist { uint count; struct ether_addr ea[16]; @@ -145,8 +175,7 @@ struct mflist { struct mac_list_set { uint32 mode; - struct mflist white_list; - struct mflist black_list; + struct mflist mac_list; }; #endif @@ -166,6 +195,13 @@ 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 void dhd_bus_country_set(struct net_device *dev, char *country_code); +extern char *dhd_bus_country_get(struct net_device *dev); +extern int dhd_get_dtim_skip(dhd_pub_t *dhd); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) #define IWE_STREAM_ADD_EVENT(info, stream, ends, iwe, extra) \ @@ -183,4 +219,66 @@ 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 '2' +#define PNO_TLV_RESERVED '0' +#define PNO_TLV_TYPE_SSID_IE 'S' +#define PNO_TLV_TYPE_TIME 'T' +#define PNO_EVENT_UP "PNO_EVENT" +#define PNO_SCAN_MAX_FW 508 + +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 diff --git a/include/linux/netfilter/xt_CONNMARK.h b/include/linux/netfilter/xt_CONNMARK.h index a772c066..0a854586 100644 --- a/include/linux/netfilter/xt_CONNMARK.h +++ b/include/linux/netfilter/xt_CONNMARK.h @@ -1,5 +1,5 @@ -#ifndef _XT_CONNMARK_H -#define _XT_CONNMARK_H +#ifndef _XT_CONNMARK_H_target +#define _XT_CONNMARK_H_target #include @@ -12,20 +12,15 @@ * (at your option) any later version. */ - enum { - XT_CONNMARK_SET = 0, - XT_CONNMARK_SAVE, - XT_CONNMARK_RESTORE - }; - - struct xt_connmark_tginfo1 { - __u32 ctmark, ctmask, nfmask; - __u8 mode; - }; - -struct xt_connmark_mtinfo1 { - __u32 mark, mask; - __u8 invert; +enum { + XT_CONNMARK_SET = 0, + XT_CONNMARK_SAVE, + XT_CONNMARK_RESTORE }; -#endif /*_XT_CONNMARK_H*/ +struct xt_connmark_tginfo1 { + __u32 ctmark, ctmask, nfmask; + __u8 mode; +}; + +#endif /*_XT_CONNMARK_H_target*/ diff --git a/include/linux/netfilter/xt_DSCP.h b/include/linux/netfilter/xt_DSCP.h new file mode 100644 index 00000000..648e0b3b --- /dev/null +++ b/include/linux/netfilter/xt_DSCP.h @@ -0,0 +1,26 @@ +/* x_tables module for setting the IPv4/IPv6 DSCP field + * + * (C) 2002 Harald Welte + * based on ipt_FTOS.c (C) 2000 by Matthew G. Marsh + * This software is distributed under GNU GPL v2, 1991 + * + * See RFC2474 for a description of the DSCP field within the IP Header. + * + * xt_DSCP.h,v 1.7 2002/03/14 12:03:13 laforge Exp +*/ +#ifndef _XT_DSCP_TARGET_H +#define _XT_DSCP_TARGET_H +#include +#include + +/* target info */ +struct xt_DSCP_info { + __u8 dscp; +}; + +struct xt_tos_target_info { + __u8 tos_value; + __u8 tos_mask; +}; + +#endif /* _XT_DSCP_TARGET_H */ diff --git a/include/linux/netfilter/xt_MARK.h b/include/linux/netfilter/xt_MARK.h new file mode 100644 index 00000000..bc9561bd --- /dev/null +++ b/include/linux/netfilter/xt_MARK.h @@ -0,0 +1,10 @@ +#ifndef _XT_MARK_H_target +#define _XT_MARK_H_target + +#include + +struct xt_mark_tginfo2 { + __u32 mark, mask; +}; + +#endif /*_XT_MARK_H_target */ diff --git a/include/linux/netfilter/xt_RATEEST.h b/include/linux/netfilter/xt_RATEEST.h index d40a6196..6605e20a 100644 --- a/include/linux/netfilter/xt_RATEEST.h +++ b/include/linux/netfilter/xt_RATEEST.h @@ -1,37 +1,15 @@ -#ifndef _XT_RATEEST_MATCH_H -#define _XT_RATEEST_MATCH_H +#ifndef _XT_RATEEST_TARGET_H +#define _XT_RATEEST_TARGET_H #include -enum xt_rateest_match_flags { - XT_RATEEST_MATCH_INVERT = 1<<0, - XT_RATEEST_MATCH_ABS = 1<<1, - XT_RATEEST_MATCH_REL = 1<<2, - XT_RATEEST_MATCH_DELTA = 1<<3, - XT_RATEEST_MATCH_BPS = 1<<4, - XT_RATEEST_MATCH_PPS = 1<<5, -}; - -enum xt_rateest_match_mode { - XT_RATEEST_MATCH_NONE, - XT_RATEEST_MATCH_EQ, - XT_RATEEST_MATCH_LT, - XT_RATEEST_MATCH_GT, -}; - -struct xt_rateest_match_info { - char name1[IFNAMSIZ]; - char name2[IFNAMSIZ]; - __u16 flags; - __u16 mode; - __u32 bps1; - __u32 pps1; - __u32 bps2; - __u32 pps2; +struct xt_rateest_target_info { + char name[IFNAMSIZ]; + __s8 interval; + __u8 ewma_log; /* Used internally by the kernel */ - struct xt_rateest *est1 __attribute__((aligned(8))); - struct xt_rateest *est2 __attribute__((aligned(8))); + struct xt_rateest *est __attribute__((aligned(8))); }; -#endif /* _XT_RATEEST_MATCH_H */ +#endif /* _XT_RATEEST_TARGET_H */ diff --git a/include/linux/netfilter/xt_TCPMSS.h b/include/linux/netfilter/xt_TCPMSS.h index fbac56b9..9a6960af 100644 --- a/include/linux/netfilter/xt_TCPMSS.h +++ b/include/linux/netfilter/xt_TCPMSS.h @@ -1,11 +1,12 @@ -#ifndef _XT_TCPMSS_MATCH_H -#define _XT_TCPMSS_MATCH_H +#ifndef _XT_TCPMSS_H +#define _XT_TCPMSS_H #include -struct xt_tcpmss_match_info { - __u16 mss_min, mss_max; - __u8 invert; +struct xt_tcpmss_info { + __u16 mss; }; -#endif /*_XT_TCPMSS_MATCH_H*/ +#define XT_TCPMSS_CLAMP_PMTU 0xffff + +#endif /* _XT_TCPMSS_H */ diff --git a/include/linux/netfilter/xt_connmark.h b/include/linux/netfilter/xt_connmark.h new file mode 100644 index 00000000..619e47cd --- /dev/null +++ b/include/linux/netfilter/xt_connmark.h @@ -0,0 +1,20 @@ +#ifndef _XT_CONNMARK_H +#define _XT_CONNMARK_H + +#include + +/* Copyright (C) 2002,2004 MARA Systems AB + * by Henrik Nordstrom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +struct xt_connmark_mtinfo1 { + __u32 mark, mask; + __u8 invert; +}; + +#endif /*_XT_CONNMARK_H*/ diff --git a/include/linux/netfilter/xt_dscp.h b/include/linux/netfilter/xt_dscp.h index 61199930..15f8932a 100644 --- a/include/linux/netfilter/xt_dscp.h +++ b/include/linux/netfilter/xt_dscp.h @@ -1,30 +1,31 @@ -/* x_tables module for setting the IPv4/IPv6 DSCP field +/* x_tables module for matching the IPv4/IPv6 DSCP field * * (C) 2002 Harald Welte - * based on ipt_FTOS.c (C) 2000 by Matthew G. Marsh * This software is distributed under GNU GPL v2, 1991 * * See RFC2474 for a description of the DSCP field within the IP Header. * - * xt_DSCP.h,v 1.7 2002/03/14 12:03:13 laforge Exp + * xt_dscp.h,v 1.3 2002/08/05 19:00:21 laforge Exp */ -#ifndef _XT_DSCP_TARGET_H -#define _XT_DSCP_TARGET_H -#include +#ifndef _XT_DSCP_H +#define _XT_DSCP_H + #include -#define XT_DSCP_MASK 0xfc /* 11111100 */ -#define XT_DSCP_SHIFT 2 -#define XT_DSCP_MAX 0x3f /* 00111111 */ +#define XT_DSCP_MASK 0xfc /* 11111100 */ +#define XT_DSCP_SHIFT 2 +#define XT_DSCP_MAX 0x3f /* 00111111 */ -/* target info */ -struct xt_DSCP_info { +/* match info */ +struct xt_dscp_info { __u8 dscp; + __u8 invert; }; -struct xt_tos_target_info { - __u8 tos_value; +struct xt_tos_match_info { __u8 tos_mask; + __u8 tos_value; + __u8 invert; }; -#endif /* _XT_DSCP_TARGET_H */ +#endif /* _XT_DSCP_H */ diff --git a/include/linux/netfilter/xt_mark.h b/include/linux/netfilter/xt_mark.h index bc9561bd..6607c8f3 100644 --- a/include/linux/netfilter/xt_mark.h +++ b/include/linux/netfilter/xt_mark.h @@ -1,10 +1,11 @@ -#ifndef _XT_MARK_H_target -#define _XT_MARK_H_target +#ifndef _XT_MARK_H +#define _XT_MARK_H #include -struct xt_mark_tginfo2 { +struct xt_mark_mtinfo1 { __u32 mark, mask; + __u8 invert; }; -#endif /*_XT_MARK_H_target */ +#endif /*_XT_MARK_H*/ diff --git a/include/linux/netfilter/xt_rateest.h b/include/linux/netfilter/xt_rateest.h new file mode 100644 index 00000000..d40a6196 --- /dev/null +++ b/include/linux/netfilter/xt_rateest.h @@ -0,0 +1,37 @@ +#ifndef _XT_RATEEST_MATCH_H +#define _XT_RATEEST_MATCH_H + +#include + +enum xt_rateest_match_flags { + XT_RATEEST_MATCH_INVERT = 1<<0, + XT_RATEEST_MATCH_ABS = 1<<1, + XT_RATEEST_MATCH_REL = 1<<2, + XT_RATEEST_MATCH_DELTA = 1<<3, + XT_RATEEST_MATCH_BPS = 1<<4, + XT_RATEEST_MATCH_PPS = 1<<5, +}; + +enum xt_rateest_match_mode { + XT_RATEEST_MATCH_NONE, + XT_RATEEST_MATCH_EQ, + XT_RATEEST_MATCH_LT, + XT_RATEEST_MATCH_GT, +}; + +struct xt_rateest_match_info { + char name1[IFNAMSIZ]; + char name2[IFNAMSIZ]; + __u16 flags; + __u16 mode; + __u32 bps1; + __u32 pps1; + __u32 bps2; + __u32 pps2; + + /* Used internally by the kernel */ + struct xt_rateest *est1 __attribute__((aligned(8))); + struct xt_rateest *est2 __attribute__((aligned(8))); +}; + +#endif /* _XT_RATEEST_MATCH_H */ diff --git a/include/linux/netfilter/xt_tcpmss.h b/include/linux/netfilter/xt_tcpmss.h new file mode 100644 index 00000000..fbac56b9 --- /dev/null +++ b/include/linux/netfilter/xt_tcpmss.h @@ -0,0 +1,11 @@ +#ifndef _XT_TCPMSS_MATCH_H +#define _XT_TCPMSS_MATCH_H + +#include + +struct xt_tcpmss_match_info { + __u16 mss_min, mss_max; + __u8 invert; +}; + +#endif /*_XT_TCPMSS_MATCH_H*/ diff --git a/include/linux/netfilter_ipv4/ipt_ECN.h b/include/linux/netfilter_ipv4/ipt_ECN.h index 9945baa4..7ca45918 100644 --- a/include/linux/netfilter_ipv4/ipt_ECN.h +++ b/include/linux/netfilter_ipv4/ipt_ECN.h @@ -1,33 +1,31 @@ -/* iptables module for matching the ECN header in IPv4 and TCP header +/* Header file for iptables ipt_ECN target * - * (C) 2002 Harald Welte + * (C) 2002 by Harald Welte * * This software is distributed under GNU GPL v2, 1991 * - * ipt_ecn.h,v 1.4 2002/08/05 19:39:00 laforge Exp + * ipt_ECN.h,v 1.3 2002/05/29 12:17:40 laforge Exp */ -#ifndef _IPT_ECN_H -#define _IPT_ECN_H -#include +#ifndef _IPT_ECN_TARGET_H +#define _IPT_ECN_TARGET_H +#include #define IPT_ECN_IP_MASK (~XT_DSCP_MASK) -#define IPT_ECN_OP_MATCH_IP 0x01 -#define IPT_ECN_OP_MATCH_ECE 0x10 -#define IPT_ECN_OP_MATCH_CWR 0x20 +#define IPT_ECN_OP_SET_IP 0x01 /* set ECN bits of IPv4 header */ +#define IPT_ECN_OP_SET_ECE 0x10 /* set ECE bit of TCP header */ +#define IPT_ECN_OP_SET_CWR 0x20 /* set CWR bit of TCP header */ -#define IPT_ECN_OP_MATCH_MASK 0xce +#define IPT_ECN_OP_MASK 0xce -/* match info */ -struct ipt_ecn_info { - u_int8_t operation; - u_int8_t invert; - u_int8_t ip_ect; +struct ipt_ECN_info { + u_int8_t operation; /* bitset of operations */ + u_int8_t ip_ect; /* ECT codepoint of IPv4 header, pre-shifted */ union { struct { - u_int8_t ect; + u_int8_t ece:1, cwr:1; /* TCP ECT bits */ } tcp; } proto; }; -#endif /* _IPT_ECN_H */ +#endif /* _IPT_ECN_TARGET_H */ diff --git a/include/linux/netfilter_ipv4/ipt_TTL.h b/include/linux/netfilter_ipv4/ipt_TTL.h index ee24fd86..ee6611ed 100644 --- a/include/linux/netfilter_ipv4/ipt_TTL.h +++ b/include/linux/netfilter_ipv4/ipt_TTL.h @@ -1,18 +1,18 @@ -/* IP tables module for matching the value of the TTL - * (C) 2000 by Harald Welte */ +/* TTL modification module for IP tables + * (C) 2000 by Harald Welte */ #ifndef _IPT_TTL_H #define _IPT_TTL_H enum { - IPT_TTL_EQ = 0, /* equals */ - IPT_TTL_NE, /* not equals */ - IPT_TTL_LT, /* less than */ - IPT_TTL_GT, /* greater than */ + IPT_TTL_SET = 0, + IPT_TTL_INC, + IPT_TTL_DEC }; +#define IPT_TTL_MAXMODE IPT_TTL_DEC -struct ipt_ttl_info { +struct ipt_TTL_info { u_int8_t mode; u_int8_t ttl; }; diff --git a/include/linux/netfilter_ipv4/ipt_ecn.h b/include/linux/netfilter_ipv4/ipt_ecn.h new file mode 100644 index 00000000..9945baa4 --- /dev/null +++ b/include/linux/netfilter_ipv4/ipt_ecn.h @@ -0,0 +1,33 @@ +/* iptables module for matching the ECN header in IPv4 and TCP header + * + * (C) 2002 Harald Welte + * + * This software is distributed under GNU GPL v2, 1991 + * + * ipt_ecn.h,v 1.4 2002/08/05 19:39:00 laforge Exp +*/ +#ifndef _IPT_ECN_H +#define _IPT_ECN_H +#include + +#define IPT_ECN_IP_MASK (~XT_DSCP_MASK) + +#define IPT_ECN_OP_MATCH_IP 0x01 +#define IPT_ECN_OP_MATCH_ECE 0x10 +#define IPT_ECN_OP_MATCH_CWR 0x20 + +#define IPT_ECN_OP_MATCH_MASK 0xce + +/* match info */ +struct ipt_ecn_info { + u_int8_t operation; + u_int8_t invert; + u_int8_t ip_ect; + union { + struct { + u_int8_t ect; + } tcp; + } proto; +}; + +#endif /* _IPT_ECN_H */ diff --git a/include/linux/netfilter_ipv4/ipt_ttl.h b/include/linux/netfilter_ipv4/ipt_ttl.h new file mode 100644 index 00000000..ee24fd86 --- /dev/null +++ b/include/linux/netfilter_ipv4/ipt_ttl.h @@ -0,0 +1,21 @@ +/* IP tables module for matching the value of the TTL + * (C) 2000 by Harald Welte */ + +#ifndef _IPT_TTL_H +#define _IPT_TTL_H + +enum { + IPT_TTL_EQ = 0, /* equals */ + IPT_TTL_NE, /* not equals */ + IPT_TTL_LT, /* less than */ + IPT_TTL_GT, /* greater than */ +}; + + +struct ipt_ttl_info { + u_int8_t mode; + u_int8_t ttl; +}; + + +#endif diff --git a/include/linux/netfilter_ipv6/ip6t_HL.h b/include/linux/netfilter_ipv6/ip6t_HL.h index 5ef91b83..afb7813d 100644 --- a/include/linux/netfilter_ipv6/ip6t_HL.h +++ b/include/linux/netfilter_ipv6/ip6t_HL.h @@ -1,19 +1,19 @@ -/* ip6tables module for matching the Hop Limit value +/* Hop Limit modification module for ip6tables * Maciej Soltysiak - * Based on HW's ttl module */ + * Based on HW's TTL module */ #ifndef _IP6T_HL_H #define _IP6T_HL_H enum { - IP6T_HL_EQ = 0, /* equals */ - IP6T_HL_NE, /* not equals */ - IP6T_HL_LT, /* less than */ - IP6T_HL_GT, /* greater than */ + IP6T_HL_SET = 0, + IP6T_HL_INC, + IP6T_HL_DEC }; +#define IP6T_HL_MAXMODE IP6T_HL_DEC -struct ip6t_hl_info { +struct ip6t_HL_info { u_int8_t mode; u_int8_t hop_limit; }; diff --git a/include/linux/netfilter_ipv6/ip6t_hl.h b/include/linux/netfilter_ipv6/ip6t_hl.h new file mode 100644 index 00000000..5ef91b83 --- /dev/null +++ b/include/linux/netfilter_ipv6/ip6t_hl.h @@ -0,0 +1,22 @@ +/* ip6tables module for matching the Hop Limit value + * Maciej Soltysiak + * Based on HW's ttl module */ + +#ifndef _IP6T_HL_H +#define _IP6T_HL_H + +enum { + IP6T_HL_EQ = 0, /* equals */ + IP6T_HL_NE, /* not equals */ + IP6T_HL_LT, /* less than */ + IP6T_HL_GT, /* greater than */ +}; + + +struct ip6t_hl_info { + u_int8_t mode; + u_int8_t hop_limit; +}; + + +#endif diff --git a/include/linux/string.h b/include/linux/string.h index b8508868..0fa4bf9e 100644 --- a/include/linux/string.h +++ b/include/linux/string.h @@ -66,6 +66,9 @@ extern char * __must_check strstrip(char *); #ifndef __HAVE_ARCH_STRSTR extern char * strstr(const char *,const char *); #endif +#ifndef __HAVE_ARCH_STRNSTR +extern char * strnstr(const char *, const char *, size_t); +#endif #ifndef __HAVE_ARCH_STRLEN extern __kernel_size_t strlen(const char *); #endif diff --git a/include/linux/wlan_plat.h b/include/linux/wlan_plat.h index 70ee63b4..3b1e2e05 100644 --- a/include/linux/wlan_plat.h +++ b/include/linux/wlan_plat.h @@ -20,6 +20,7 @@ struct wifi_platform_data { int (*set_reset)(int val); int (*set_carddetect)(int val); void *(*mem_prealloc)(int section, unsigned long size); + int (*get_mac_addr)(unsigned char *buf); }; #endif diff --git a/net/ethernet/eth.c b/net/ethernet/eth.c index 5890b4e1..5a883aff 100644 --- a/net/ethernet/eth.c +++ b/net/ethernet/eth.c @@ -394,3 +394,9 @@ ssize_t sysfs_format_mac(char *buf, const unsigned char *addr, int len) } EXPORT_SYMBOL(sysfs_format_mac); +char *print_mac(char *buf, const unsigned char *addr) +{ + _format_mac_addr(buf, MAC_BUF_SIZE, addr, ETH_ALEN); + return buf; +} +EXPORT_SYMBOL(print_mac); diff --git a/net/ipv4/netfilter/ipt_ECN.c b/net/ipv4/netfilter/ipt_ECN.c new file mode 100644 index 00000000..f7e2fa09 --- /dev/null +++ b/net/ipv4/netfilter/ipt_ECN.c @@ -0,0 +1,141 @@ +/* iptables module for the IPv4 and TCP ECN bits, Version 1.5 + * + * (C) 2002 by Harald Welte + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Harald Welte "); +MODULE_DESCRIPTION("Xtables: Explicit Congestion Notification (ECN) flag modification"); + +/* set ECT codepoint from IP header. + * return false if there was an error. */ +static inline bool +set_ect_ip(struct sk_buff *skb, const struct ipt_ECN_info *einfo) +{ + struct iphdr *iph = ip_hdr(skb); + + if ((iph->tos & IPT_ECN_IP_MASK) != (einfo->ip_ect & IPT_ECN_IP_MASK)) { + __u8 oldtos; + if (!skb_make_writable(skb, sizeof(struct iphdr))) + return false; + iph = ip_hdr(skb); + oldtos = iph->tos; + iph->tos &= ~IPT_ECN_IP_MASK; + iph->tos |= (einfo->ip_ect & IPT_ECN_IP_MASK); + csum_replace2(&iph->check, htons(oldtos), htons(iph->tos)); + } + return true; +} + +/* Return false if there was an error. */ +static inline bool +set_ect_tcp(struct sk_buff *skb, const struct ipt_ECN_info *einfo) +{ + struct tcphdr _tcph, *tcph; + __be16 oldval; + + /* Not enought header? */ + tcph = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_tcph), &_tcph); + if (!tcph) + return false; + + if ((!(einfo->operation & IPT_ECN_OP_SET_ECE) || + tcph->ece == einfo->proto.tcp.ece) && + (!(einfo->operation & IPT_ECN_OP_SET_CWR) || + tcph->cwr == einfo->proto.tcp.cwr)) + return true; + + if (!skb_make_writable(skb, ip_hdrlen(skb) + sizeof(*tcph))) + return false; + tcph = (void *)ip_hdr(skb) + ip_hdrlen(skb); + + oldval = ((__be16 *)tcph)[6]; + if (einfo->operation & IPT_ECN_OP_SET_ECE) + tcph->ece = einfo->proto.tcp.ece; + if (einfo->operation & IPT_ECN_OP_SET_CWR) + tcph->cwr = einfo->proto.tcp.cwr; + + inet_proto_csum_replace2(&tcph->check, skb, + oldval, ((__be16 *)tcph)[6], 0); + return true; +} + +static unsigned int +ecn_tg(struct sk_buff *skb, const struct xt_target_param *par) +{ + const struct ipt_ECN_info *einfo = par->targinfo; + + if (einfo->operation & IPT_ECN_OP_SET_IP) + if (!set_ect_ip(skb, einfo)) + return NF_DROP; + + if (einfo->operation & (IPT_ECN_OP_SET_ECE | IPT_ECN_OP_SET_CWR) + && ip_hdr(skb)->protocol == IPPROTO_TCP) + if (!set_ect_tcp(skb, einfo)) + return NF_DROP; + + return XT_CONTINUE; +} + +static bool ecn_tg_check(const struct xt_tgchk_param *par) +{ + const struct ipt_ECN_info *einfo = par->targinfo; + const struct ipt_entry *e = par->entryinfo; + + if (einfo->operation & IPT_ECN_OP_MASK) { + printk(KERN_WARNING "ECN: unsupported ECN operation %x\n", + einfo->operation); + return false; + } + if (einfo->ip_ect & ~IPT_ECN_IP_MASK) { + printk(KERN_WARNING "ECN: new ECT codepoint %x out of mask\n", + einfo->ip_ect); + return false; + } + if ((einfo->operation & (IPT_ECN_OP_SET_ECE|IPT_ECN_OP_SET_CWR)) + && (e->ip.proto != IPPROTO_TCP || (e->ip.invflags & XT_INV_PROTO))) { + printk(KERN_WARNING "ECN: cannot use TCP operations on a " + "non-tcp rule\n"); + return false; + } + return true; +} + +static struct xt_target ecn_tg_reg __read_mostly = { + .name = "ECN", + .family = NFPROTO_IPV4, + .target = ecn_tg, + .targetsize = sizeof(struct ipt_ECN_info), + .table = "mangle", + .checkentry = ecn_tg_check, + .me = THIS_MODULE, +}; + +static int __init ecn_tg_init(void) +{ + return xt_register_target(&ecn_tg_reg); +} + +static void __exit ecn_tg_exit(void) +{ + xt_unregister_target(&ecn_tg_reg); +} + +module_init(ecn_tg_init); +module_exit(ecn_tg_exit); diff --git a/net/ipv4/netfilter/ipt_ecn.c b/net/ipv4/netfilter/ipt_ecn.c index bed80136..6289b641 100644 --- a/net/ipv4/netfilter/ipt_ecn.c +++ b/net/ipv4/netfilter/ipt_ecn.c @@ -1,129 +1,129 @@ - /* IP tables module for matching the value of the IPv4 and TCP ECN bits - * - * (C) 2002 by Harald Welte - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - - #include - #include - #include - #include - #include - #include - - #include - #include - #include - - MODULE_AUTHOR("Harald Welte "); - MODULE_DESCRIPTION("Xtables: Explicit Congestion Notification (ECN) flag match for IPv4"); - MODULE_LICENSE("GPL"); - - static inline bool match_ip(const struct sk_buff *skb, - const struct ipt_ecn_info *einfo) - { - return (ip_hdr(skb)->tos & IPT_ECN_IP_MASK) == einfo->ip_ect; - } - - static inline bool match_tcp(const struct sk_buff *skb, - const struct ipt_ecn_info *einfo, - bool *hotdrop) - { - struct tcphdr _tcph; - const struct tcphdr *th; +/* IP tables module for matching the value of the IPv4 and TCP ECN bits + * + * (C) 2002 by Harald Welte + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ - /* In practice, TCP match does this, so can't fail. But let's - * be good citizens. - */ - th = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_tcph), &_tcph); - if (th == NULL) { - *hotdrop = false; - return false; - } - - if (einfo->operation & IPT_ECN_OP_MATCH_ECE) { - if (einfo->invert & IPT_ECN_OP_MATCH_ECE) { - if (th->ece == 1) - return false; - } else { - if (th->ece == 0) - return false; - } - } - - if (einfo->operation & IPT_ECN_OP_MATCH_CWR) { - if (einfo->invert & IPT_ECN_OP_MATCH_CWR) { - if (th->cwr == 1) - return false; - } else { - if (th->cwr == 0) - return false; - } - } - - return true; - } - - static bool ecn_mt(const struct sk_buff *skb, const struct xt_match_param *par) - { - const struct ipt_ecn_info *info = par->matchinfo; - - if (info->operation & IPT_ECN_OP_MATCH_IP) - if (!match_ip(skb, info)) - return false; - - if (info->operation & (IPT_ECN_OP_MATCH_ECE|IPT_ECN_OP_MATCH_CWR)) { - if (ip_hdr(skb)->protocol != IPPROTO_TCP) - return false; - if (!match_tcp(skb, info, par->hotdrop)) - return false; - } - - return true; - } - - static bool ecn_mt_check(const struct xt_mtchk_param *par) - { - const struct ipt_ecn_info *info = par->matchinfo; - const struct ipt_ip *ip = par->entryinfo; - - if (info->operation & IPT_ECN_OP_MATCH_MASK) - return false; - - if (info->invert & IPT_ECN_OP_MATCH_MASK) - return false; - - if (info->operation & (IPT_ECN_OP_MATCH_ECE|IPT_ECN_OP_MATCH_CWR) - && ip->proto != IPPROTO_TCP) { - printk(KERN_WARNING "ipt_ecn: can't match TCP bits in rule for" - " non-tcp packets\n"); - return false; - } - - return true; - } - - static struct xt_match ecn_mt_reg __read_mostly = { - .name = "ecn", - .family = NFPROTO_IPV4, - .match = ecn_mt, - .matchsize = sizeof(struct ipt_ecn_info), - .checkentry = ecn_mt_check, - .me = THIS_MODULE, - }; - - static int __init ecn_mt_init(void) - { - return xt_register_match(&ecn_mt_reg); - } - - static void __exit ecn_mt_exit(void) - { - xt_unregister_match(&ecn_mt_reg); - } - - module_init(ecn_mt_init); - module_exit(ecn_mt_exit); +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +MODULE_AUTHOR("Harald Welte "); +MODULE_DESCRIPTION("Xtables: Explicit Congestion Notification (ECN) flag match for IPv4"); +MODULE_LICENSE("GPL"); + +static inline bool match_ip(const struct sk_buff *skb, + const struct ipt_ecn_info *einfo) +{ + return (ip_hdr(skb)->tos & IPT_ECN_IP_MASK) == einfo->ip_ect; +} + +static inline bool match_tcp(const struct sk_buff *skb, + const struct ipt_ecn_info *einfo, + bool *hotdrop) +{ + struct tcphdr _tcph; + const struct tcphdr *th; + + /* In practice, TCP match does this, so can't fail. But let's + * be good citizens. + */ + th = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_tcph), &_tcph); + if (th == NULL) { + *hotdrop = false; + return false; + } + + if (einfo->operation & IPT_ECN_OP_MATCH_ECE) { + if (einfo->invert & IPT_ECN_OP_MATCH_ECE) { + if (th->ece == 1) + return false; + } else { + if (th->ece == 0) + return false; + } + } + + if (einfo->operation & IPT_ECN_OP_MATCH_CWR) { + if (einfo->invert & IPT_ECN_OP_MATCH_CWR) { + if (th->cwr == 1) + return false; + } else { + if (th->cwr == 0) + return false; + } + } + + return true; +} + +static bool ecn_mt(const struct sk_buff *skb, const struct xt_match_param *par) +{ + const struct ipt_ecn_info *info = par->matchinfo; + + if (info->operation & IPT_ECN_OP_MATCH_IP) + if (!match_ip(skb, info)) + return false; + + if (info->operation & (IPT_ECN_OP_MATCH_ECE|IPT_ECN_OP_MATCH_CWR)) { + if (ip_hdr(skb)->protocol != IPPROTO_TCP) + return false; + if (!match_tcp(skb, info, par->hotdrop)) + return false; + } + + return true; +} + +static bool ecn_mt_check(const struct xt_mtchk_param *par) +{ + const struct ipt_ecn_info *info = par->matchinfo; + const struct ipt_ip *ip = par->entryinfo; + + if (info->operation & IPT_ECN_OP_MATCH_MASK) + return false; + + if (info->invert & IPT_ECN_OP_MATCH_MASK) + return false; + + if (info->operation & (IPT_ECN_OP_MATCH_ECE|IPT_ECN_OP_MATCH_CWR) + && ip->proto != IPPROTO_TCP) { + printk(KERN_WARNING "ipt_ecn: can't match TCP bits in rule for" + " non-tcp packets\n"); + return false; + } + + return true; +} + +static struct xt_match ecn_mt_reg __read_mostly = { + .name = "ecn", + .family = NFPROTO_IPV4, + .match = ecn_mt, + .matchsize = sizeof(struct ipt_ecn_info), + .checkentry = ecn_mt_check, + .me = THIS_MODULE, +}; + +static int __init ecn_mt_init(void) +{ + return xt_register_match(&ecn_mt_reg); +} + +static void __exit ecn_mt_exit(void) +{ + xt_unregister_match(&ecn_mt_reg); +} + +module_init(ecn_mt_init); +module_exit(ecn_mt_exit); diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 4886df8a..49f62ee4 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -42,12 +42,12 @@ obj-$(CONFIG_NETFILTER_XTABLES) += x_tables.o xt_tcpudp.o # targets obj-$(CONFIG_NETFILTER_XT_TARGET_CLASSIFY) += xt_CLASSIFY.o -obj-$(CONFIG_NETFILTER_XT_TARGET_CONNMARK) += xt_connmark.o +obj-$(CONFIG_NETFILTER_XT_TARGET_CONNMARK) += xt_CONNMARK.o obj-$(CONFIG_NETFILTER_XT_TARGET_CONNSECMARK) += xt_CONNSECMARK.o obj-$(CONFIG_NETFILTER_XT_TARGET_DSCP) += xt_DSCP.o -obj-$(CONFIG_NETFILTER_XT_TARGET_HL) += xt_hl.o +obj-$(CONFIG_NETFILTER_XT_TARGET_HL) += xt_HL.o obj-$(CONFIG_NETFILTER_XT_TARGET_LED) += xt_LED.o -obj-$(CONFIG_NETFILTER_XT_TARGET_MARK) += xt_mark.o +obj-$(CONFIG_NETFILTER_XT_TARGET_MARK) += xt_MARK.o obj-$(CONFIG_NETFILTER_XT_TARGET_NFLOG) += xt_NFLOG.o obj-$(CONFIG_NETFILTER_XT_TARGET_NFQUEUE) += xt_NFQUEUE.o obj-$(CONFIG_NETFILTER_XT_TARGET_NOTRACK) += xt_NOTRACK.o diff --git a/net/netfilter/xt_CONNMARK.c b/net/netfilter/xt_CONNMARK.c new file mode 100644 index 00000000..59345706 --- /dev/null +++ b/net/netfilter/xt_CONNMARK.c @@ -0,0 +1,113 @@ +/* + * xt_CONNMARK - Netfilter module to modify the connection mark values + * + * Copyright (C) 2002,2004 MARA Systems AB + * by Henrik Nordstrom + * Copyright © CC Computer Consultants GmbH, 2007 - 2008 + * Jan Engelhardt + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include + +MODULE_AUTHOR("Henrik Nordstrom "); +MODULE_DESCRIPTION("Xtables: connection mark modification"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("ipt_CONNMARK"); +MODULE_ALIAS("ip6t_CONNMARK"); + +#include +#include +#include + +static unsigned int +connmark_tg(struct sk_buff *skb, const struct xt_target_param *par) +{ + const struct xt_connmark_tginfo1 *info = par->targinfo; + enum ip_conntrack_info ctinfo; + struct nf_conn *ct; + u_int32_t newmark; + + ct = nf_ct_get(skb, &ctinfo); + if (ct == NULL) + return XT_CONTINUE; + + switch (info->mode) { + case XT_CONNMARK_SET: + newmark = (ct->mark & ~info->ctmask) ^ info->ctmark; + if (ct->mark != newmark) { + ct->mark = newmark; + nf_conntrack_event_cache(IPCT_MARK, ct); + } + break; + case XT_CONNMARK_SAVE: + newmark = (ct->mark & ~info->ctmask) ^ + (skb->mark & info->nfmask); + if (ct->mark != newmark) { + ct->mark = newmark; + nf_conntrack_event_cache(IPCT_MARK, ct); + } + break; + case XT_CONNMARK_RESTORE: + newmark = (skb->mark & ~info->nfmask) ^ + (ct->mark & info->ctmask); + skb->mark = newmark; + break; + } + + return XT_CONTINUE; +} + +static bool connmark_tg_check(const struct xt_tgchk_param *par) +{ + if (nf_ct_l3proto_try_module_get(par->family) < 0) { + printk(KERN_WARNING "cannot load conntrack support for " + "proto=%u\n", par->family); + return false; + } + return true; +} + +static void connmark_tg_destroy(const struct xt_tgdtor_param *par) +{ + nf_ct_l3proto_module_put(par->family); +} + +static struct xt_target connmark_tg_reg __read_mostly = { + .name = "CONNMARK", + .revision = 1, + .family = NFPROTO_UNSPEC, + .checkentry = connmark_tg_check, + .target = connmark_tg, + .targetsize = sizeof(struct xt_connmark_tginfo1), + .destroy = connmark_tg_destroy, + .me = THIS_MODULE, +}; + +static int __init connmark_tg_init(void) +{ + return xt_register_target(&connmark_tg_reg); +} + +static void __exit connmark_tg_exit(void) +{ + xt_unregister_target(&connmark_tg_reg); +} + +module_init(connmark_tg_init); +module_exit(connmark_tg_exit); diff --git a/net/netfilter/xt_DSCP.c b/net/netfilter/xt_DSCP.c new file mode 100644 index 00000000..74ce8926 --- /dev/null +++ b/net/netfilter/xt_DSCP.c @@ -0,0 +1,164 @@ +/* x_tables module for setting the IPv4/IPv6 DSCP field, Version 1.8 + * + * (C) 2002 by Harald Welte + * based on ipt_FTOS.c (C) 2000 by Matthew G. Marsh + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * See RFC2474 for a description of the DSCP field within the IP Header. +*/ + +#include +#include +#include +#include +#include + +#include +#include + +MODULE_AUTHOR("Harald Welte "); +MODULE_DESCRIPTION("Xtables: DSCP/TOS field modification"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("ipt_DSCP"); +MODULE_ALIAS("ip6t_DSCP"); +MODULE_ALIAS("ipt_TOS"); +MODULE_ALIAS("ip6t_TOS"); + +static unsigned int +dscp_tg(struct sk_buff *skb, const struct xt_target_param *par) +{ + const struct xt_DSCP_info *dinfo = par->targinfo; + u_int8_t dscp = ipv4_get_dsfield(ip_hdr(skb)) >> XT_DSCP_SHIFT; + + if (dscp != dinfo->dscp) { + if (!skb_make_writable(skb, sizeof(struct iphdr))) + return NF_DROP; + + ipv4_change_dsfield(ip_hdr(skb), (__u8)(~XT_DSCP_MASK), + dinfo->dscp << XT_DSCP_SHIFT); + + } + return XT_CONTINUE; +} + +static unsigned int +dscp_tg6(struct sk_buff *skb, const struct xt_target_param *par) +{ + const struct xt_DSCP_info *dinfo = par->targinfo; + u_int8_t dscp = ipv6_get_dsfield(ipv6_hdr(skb)) >> XT_DSCP_SHIFT; + + if (dscp != dinfo->dscp) { + if (!skb_make_writable(skb, sizeof(struct ipv6hdr))) + return NF_DROP; + + ipv6_change_dsfield(ipv6_hdr(skb), (__u8)(~XT_DSCP_MASK), + dinfo->dscp << XT_DSCP_SHIFT); + } + return XT_CONTINUE; +} + +static bool dscp_tg_check(const struct xt_tgchk_param *par) +{ + const struct xt_DSCP_info *info = par->targinfo; + + if (info->dscp > XT_DSCP_MAX) { + printk(KERN_WARNING "DSCP: dscp %x out of range\n", info->dscp); + return false; + } + return true; +} + +static unsigned int +tos_tg(struct sk_buff *skb, const struct xt_target_param *par) +{ + const struct xt_tos_target_info *info = par->targinfo; + struct iphdr *iph = ip_hdr(skb); + u_int8_t orig, nv; + + orig = ipv4_get_dsfield(iph); + nv = (orig & ~info->tos_mask) ^ info->tos_value; + + if (orig != nv) { + if (!skb_make_writable(skb, sizeof(struct iphdr))) + return NF_DROP; + iph = ip_hdr(skb); + ipv4_change_dsfield(iph, 0, nv); + } + + return XT_CONTINUE; +} + +static unsigned int +tos_tg6(struct sk_buff *skb, const struct xt_target_param *par) +{ + const struct xt_tos_target_info *info = par->targinfo; + struct ipv6hdr *iph = ipv6_hdr(skb); + u_int8_t orig, nv; + + orig = ipv6_get_dsfield(iph); + nv = (orig & info->tos_mask) ^ info->tos_value; + + if (orig != nv) { + if (!skb_make_writable(skb, sizeof(struct iphdr))) + return NF_DROP; + iph = ipv6_hdr(skb); + ipv6_change_dsfield(iph, 0, nv); + } + + return XT_CONTINUE; +} + +static struct xt_target dscp_tg_reg[] __read_mostly = { + { + .name = "DSCP", + .family = NFPROTO_IPV4, + .checkentry = dscp_tg_check, + .target = dscp_tg, + .targetsize = sizeof(struct xt_DSCP_info), + .table = "mangle", + .me = THIS_MODULE, + }, + { + .name = "DSCP", + .family = NFPROTO_IPV6, + .checkentry = dscp_tg_check, + .target = dscp_tg6, + .targetsize = sizeof(struct xt_DSCP_info), + .table = "mangle", + .me = THIS_MODULE, + }, + { + .name = "TOS", + .revision = 1, + .family = NFPROTO_IPV4, + .table = "mangle", + .target = tos_tg, + .targetsize = sizeof(struct xt_tos_target_info), + .me = THIS_MODULE, + }, + { + .name = "TOS", + .revision = 1, + .family = NFPROTO_IPV6, + .table = "mangle", + .target = tos_tg6, + .targetsize = sizeof(struct xt_tos_target_info), + .me = THIS_MODULE, + }, +}; + +static int __init dscp_tg_init(void) +{ + return xt_register_targets(dscp_tg_reg, ARRAY_SIZE(dscp_tg_reg)); +} + +static void __exit dscp_tg_exit(void) +{ + xt_unregister_targets(dscp_tg_reg, ARRAY_SIZE(dscp_tg_reg)); +} + +module_init(dscp_tg_init); +module_exit(dscp_tg_exit); diff --git a/net/netfilter/xt_HL.c b/net/netfilter/xt_HL.c new file mode 100644 index 00000000..10e789e2 --- /dev/null +++ b/net/netfilter/xt_HL.c @@ -0,0 +1,171 @@ +/* + * TTL modification target for IP tables + * (C) 2000,2005 by Harald Welte + * + * Hop Limit modification target for ip6tables + * Maciej Soltysiak + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +MODULE_AUTHOR("Harald Welte "); +MODULE_AUTHOR("Maciej Soltysiak "); +MODULE_DESCRIPTION("Xtables: Hoplimit/TTL Limit field modification target"); +MODULE_LICENSE("GPL"); + +static unsigned int +ttl_tg(struct sk_buff *skb, const struct xt_target_param *par) +{ + struct iphdr *iph; + const struct ipt_TTL_info *info = par->targinfo; + int new_ttl; + + if (!skb_make_writable(skb, skb->len)) + return NF_DROP; + + iph = ip_hdr(skb); + + switch (info->mode) { + case IPT_TTL_SET: + new_ttl = info->ttl; + break; + case IPT_TTL_INC: + new_ttl = iph->ttl + info->ttl; + if (new_ttl > 255) + new_ttl = 255; + break; + case IPT_TTL_DEC: + new_ttl = iph->ttl - info->ttl; + if (new_ttl < 0) + new_ttl = 0; + break; + default: + new_ttl = iph->ttl; + break; + } + + if (new_ttl != iph->ttl) { + csum_replace2(&iph->check, htons(iph->ttl << 8), + htons(new_ttl << 8)); + iph->ttl = new_ttl; + } + + return XT_CONTINUE; +} + +static unsigned int +hl_tg6(struct sk_buff *skb, const struct xt_target_param *par) +{ + struct ipv6hdr *ip6h; + const struct ip6t_HL_info *info = par->targinfo; + int new_hl; + + if (!skb_make_writable(skb, skb->len)) + return NF_DROP; + + ip6h = ipv6_hdr(skb); + + switch (info->mode) { + case IP6T_HL_SET: + new_hl = info->hop_limit; + break; + case IP6T_HL_INC: + new_hl = ip6h->hop_limit + info->hop_limit; + if (new_hl > 255) + new_hl = 255; + break; + case IP6T_HL_DEC: + new_hl = ip6h->hop_limit - info->hop_limit; + if (new_hl < 0) + new_hl = 0; + break; + default: + new_hl = ip6h->hop_limit; + break; + } + + ip6h->hop_limit = new_hl; + + return XT_CONTINUE; +} + +static bool ttl_tg_check(const struct xt_tgchk_param *par) +{ + const struct ipt_TTL_info *info = par->targinfo; + + if (info->mode > IPT_TTL_MAXMODE) { + printk(KERN_WARNING "ipt_TTL: invalid or unknown Mode %u\n", + info->mode); + return false; + } + if (info->mode != IPT_TTL_SET && info->ttl == 0) + return false; + return true; +} + +static bool hl_tg6_check(const struct xt_tgchk_param *par) +{ + const struct ip6t_HL_info *info = par->targinfo; + + if (info->mode > IP6T_HL_MAXMODE) { + printk(KERN_WARNING "ip6t_HL: invalid or unknown Mode %u\n", + info->mode); + return false; + } + if (info->mode != IP6T_HL_SET && info->hop_limit == 0) { + printk(KERN_WARNING "ip6t_HL: increment/decrement doesn't " + "make sense with value 0\n"); + return false; + } + return true; +} + +static struct xt_target hl_tg_reg[] __read_mostly = { + { + .name = "TTL", + .revision = 0, + .family = NFPROTO_IPV4, + .target = ttl_tg, + .targetsize = sizeof(struct ipt_TTL_info), + .table = "mangle", + .checkentry = ttl_tg_check, + .me = THIS_MODULE, + }, + { + .name = "HL", + .revision = 0, + .family = NFPROTO_IPV6, + .target = hl_tg6, + .targetsize = sizeof(struct ip6t_HL_info), + .table = "mangle", + .checkentry = hl_tg6_check, + .me = THIS_MODULE, + }, +}; + +static int __init hl_tg_init(void) +{ + return xt_register_targets(hl_tg_reg, ARRAY_SIZE(hl_tg_reg)); +} + +static void __exit hl_tg_exit(void) +{ + xt_unregister_targets(hl_tg_reg, ARRAY_SIZE(hl_tg_reg)); +} + +module_init(hl_tg_init); +module_exit(hl_tg_exit); +MODULE_ALIAS("ipt_TTL"); +MODULE_ALIAS("ip6t_HL"); diff --git a/net/netfilter/xt_MARK.c b/net/netfilter/xt_MARK.c new file mode 100644 index 00000000..225f8d11 --- /dev/null +++ b/net/netfilter/xt_MARK.c @@ -0,0 +1,56 @@ +/* + * xt_MARK - Netfilter module to modify the NFMARK field of an skb + * + * (C) 1999-2001 Marc Boucher + * Copyright © CC Computer Consultants GmbH, 2007 - 2008 + * Jan Engelhardt + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include + +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Marc Boucher "); +MODULE_DESCRIPTION("Xtables: packet mark modification"); +MODULE_ALIAS("ipt_MARK"); +MODULE_ALIAS("ip6t_MARK"); + +static unsigned int +mark_tg(struct sk_buff *skb, const struct xt_target_param *par) +{ + const struct xt_mark_tginfo2 *info = par->targinfo; + + skb->mark = (skb->mark & ~info->mask) ^ info->mark; + return XT_CONTINUE; +} + +static struct xt_target mark_tg_reg __read_mostly = { + .name = "MARK", + .revision = 2, + .family = NFPROTO_UNSPEC, + .target = mark_tg, + .targetsize = sizeof(struct xt_mark_tginfo2), + .me = THIS_MODULE, +}; + +static int __init mark_tg_init(void) +{ + return xt_register_target(&mark_tg_reg); +} + +static void __exit mark_tg_exit(void) +{ + xt_unregister_target(&mark_tg_reg); +} + +module_init(mark_tg_init); +module_exit(mark_tg_exit); diff --git a/net/netfilter/xt_RATEEST.c b/net/netfilter/xt_RATEEST.c new file mode 100644 index 00000000..d80b8192 --- /dev/null +++ b/net/netfilter/xt_RATEEST.c @@ -0,0 +1,183 @@ +/* + * (C) 2007 Patrick McHardy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +static DEFINE_MUTEX(xt_rateest_mutex); + +#define RATEEST_HSIZE 16 +static struct hlist_head rateest_hash[RATEEST_HSIZE] __read_mostly; +static unsigned int jhash_rnd __read_mostly; + +static unsigned int xt_rateest_hash(const char *name) +{ + return jhash(name, FIELD_SIZEOF(struct xt_rateest, name), jhash_rnd) & + (RATEEST_HSIZE - 1); +} + +static void xt_rateest_hash_insert(struct xt_rateest *est) +{ + unsigned int h; + + h = xt_rateest_hash(est->name); + hlist_add_head(&est->list, &rateest_hash[h]); +} + +struct xt_rateest *xt_rateest_lookup(const char *name) +{ + struct xt_rateest *est; + struct hlist_node *n; + unsigned int h; + + h = xt_rateest_hash(name); + mutex_lock(&xt_rateest_mutex); + hlist_for_each_entry(est, n, &rateest_hash[h], list) { + if (strcmp(est->name, name) == 0) { + est->refcnt++; + mutex_unlock(&xt_rateest_mutex); + return est; + } + } + mutex_unlock(&xt_rateest_mutex); + return NULL; +} +EXPORT_SYMBOL_GPL(xt_rateest_lookup); + +void xt_rateest_put(struct xt_rateest *est) +{ + mutex_lock(&xt_rateest_mutex); + if (--est->refcnt == 0) { + hlist_del(&est->list); + gen_kill_estimator(&est->bstats, &est->rstats); + kfree(est); + } + mutex_unlock(&xt_rateest_mutex); +} +EXPORT_SYMBOL_GPL(xt_rateest_put); + +static unsigned int +xt_rateest_tg(struct sk_buff *skb, const struct xt_target_param *par) +{ + const struct xt_rateest_target_info *info = par->targinfo; + struct gnet_stats_basic_packed *stats = &info->est->bstats; + + spin_lock_bh(&info->est->lock); + stats->bytes += skb->len; + stats->packets++; + spin_unlock_bh(&info->est->lock); + + return XT_CONTINUE; +} + +static bool xt_rateest_tg_checkentry(const struct xt_tgchk_param *par) +{ + struct xt_rateest_target_info *info = par->targinfo; + struct xt_rateest *est; + struct { + struct nlattr opt; + struct gnet_estimator est; + } cfg; + + est = xt_rateest_lookup(info->name); + if (est) { + /* + * If estimator parameters are specified, they must match the + * existing estimator. + */ + if ((!info->interval && !info->ewma_log) || + (info->interval != est->params.interval || + info->ewma_log != est->params.ewma_log)) { + xt_rateest_put(est); + return false; + } + info->est = est; + return true; + } + + est = kzalloc(sizeof(*est), GFP_KERNEL); + if (!est) + goto err1; + + strlcpy(est->name, info->name, sizeof(est->name)); + spin_lock_init(&est->lock); + est->refcnt = 1; + est->params.interval = info->interval; + est->params.ewma_log = info->ewma_log; + + cfg.opt.nla_len = nla_attr_size(sizeof(cfg.est)); + cfg.opt.nla_type = TCA_STATS_RATE_EST; + cfg.est.interval = info->interval; + cfg.est.ewma_log = info->ewma_log; + + if (gen_new_estimator(&est->bstats, &est->rstats, &est->lock, + &cfg.opt) < 0) + goto err2; + + info->est = est; + xt_rateest_hash_insert(est); + + return true; + +err2: + kfree(est); +err1: + return false; +} + +static void xt_rateest_tg_destroy(const struct xt_tgdtor_param *par) +{ + struct xt_rateest_target_info *info = par->targinfo; + + xt_rateest_put(info->est); +} + +static struct xt_target xt_rateest_tg_reg __read_mostly = { + .name = "RATEEST", + .revision = 0, + .family = NFPROTO_UNSPEC, + .target = xt_rateest_tg, + .checkentry = xt_rateest_tg_checkentry, + .destroy = xt_rateest_tg_destroy, + .targetsize = sizeof(struct xt_rateest_target_info), + .me = THIS_MODULE, +}; + +static int __init xt_rateest_tg_init(void) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(rateest_hash); i++) + INIT_HLIST_HEAD(&rateest_hash[i]); + + get_random_bytes(&jhash_rnd, sizeof(jhash_rnd)); + return xt_register_target(&xt_rateest_tg_reg); +} + +static void __exit xt_rateest_tg_fini(void) +{ + xt_unregister_target(&xt_rateest_tg_reg); +} + + +MODULE_AUTHOR("Patrick McHardy "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Xtables: packet rate estimator"); +MODULE_ALIAS("ipt_RATEEST"); +MODULE_ALIAS("ip6t_RATEEST"); +module_init(xt_rateest_tg_init); +module_exit(xt_rateest_tg_fini); diff --git a/net/netfilter/xt_TCPMSS.c b/net/netfilter/xt_TCPMSS.c index 4809b34b..eda64c1c 100644 --- a/net/netfilter/xt_TCPMSS.c +++ b/net/netfilter/xt_TCPMSS.c @@ -1,7 +1,7 @@ -/* Kernel module to match TCP MSS values. */ - -/* Copyright (C) 2000 Marc Boucher - * Portions (C) 2005 by Harald Welte +/* + * This is a module which is used for setting the MSS option in TCP packets. + * + * Copyright (C) 2000 Marc Boucher * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -10,101 +10,305 @@ #include #include +#include +#include +#include +#include +#include +#include +#include #include -#include -#include - #include #include +#include +#include +#include MODULE_LICENSE("GPL"); MODULE_AUTHOR("Marc Boucher "); -MODULE_DESCRIPTION("Xtables: TCP MSS match"); -MODULE_ALIAS("ipt_tcpmss"); -MODULE_ALIAS("ip6t_tcpmss"); +MODULE_DESCRIPTION("Xtables: TCP Maximum Segment Size (MSS) adjustment"); +MODULE_ALIAS("ipt_TCPMSS"); +MODULE_ALIAS("ip6t_TCPMSS"); -static bool -tcpmss_mt(const struct sk_buff *skb, const struct xt_match_param *par) +static inline unsigned int +optlen(const u_int8_t *opt, unsigned int offset) { - const struct xt_tcpmss_match_info *info = par->matchinfo; - const struct tcphdr *th; - struct tcphdr _tcph; - /* tcp.doff is only 4 bits, ie. max 15 * 4 bytes */ - const u_int8_t *op; - u8 _opt[15 * 4 - sizeof(_tcph)]; - unsigned int i, optlen; + /* Beware zero-length options: make finite progress */ + if (opt[offset] <= TCPOPT_NOP || opt[offset+1] == 0) + return 1; + else + return opt[offset+1]; +} - /* If we don't have the whole header, drop packet. */ - th = skb_header_pointer(skb, par->thoff, sizeof(_tcph), &_tcph); - if (th == NULL) - goto dropit; +static int +tcpmss_mangle_packet(struct sk_buff *skb, + const struct xt_tcpmss_info *info, + unsigned int in_mtu, + unsigned int tcphoff, + unsigned int minlen) +{ + struct tcphdr *tcph; + unsigned int tcplen, i; + __be16 oldval; + u16 newmss; + u8 *opt; - /* Malformed. */ - if (th->doff*4 < sizeof(*th)) - goto dropit; + if (!skb_make_writable(skb, skb->len)) + return -1; - optlen = th->doff*4 - sizeof(*th); - if (!optlen) - goto out; + tcplen = skb->len - tcphoff; + tcph = (struct tcphdr *)(skb_network_header(skb) + tcphoff); - /* Truncated options. */ - op = skb_header_pointer(skb, par->thoff + sizeof(*th), optlen, _opt); - if (op == NULL) - goto dropit; - - for (i = 0; i < optlen; ) { - if (op[i] == TCPOPT_MSS - && (optlen - i) >= TCPOLEN_MSS - && op[i+1] == TCPOLEN_MSS) { - u_int16_t mssval; - - mssval = (op[i+2] << 8) | op[i+3]; - - return (mssval >= info->mss_min && - mssval <= info->mss_max) ^ info->invert; - } - if (op[i] < 2) - i++; - else - i += op[i+1] ? : 1; + /* Since it passed flags test in tcp match, we know it is is + not a fragment, and has data >= tcp header length. SYN + packets should not contain data: if they did, then we risk + running over MTU, sending Frag Needed and breaking things + badly. --RR */ + if (tcplen != tcph->doff*4) { + if (net_ratelimit()) + printk(KERN_ERR "xt_TCPMSS: bad length (%u bytes)\n", + skb->len); + return -1; } -out: - return info->invert; -dropit: - *par->hotdrop = true; + if (info->mss == XT_TCPMSS_CLAMP_PMTU) { + if (dst_mtu(skb_dst(skb)) <= minlen) { + if (net_ratelimit()) + printk(KERN_ERR "xt_TCPMSS: " + "unknown or invalid path-MTU (%u)\n", + dst_mtu(skb_dst(skb))); + return -1; + } + if (in_mtu <= minlen) { + if (net_ratelimit()) + printk(KERN_ERR "xt_TCPMSS: unknown or " + "invalid path-MTU (%u)\n", in_mtu); + return -1; + } + newmss = min(dst_mtu(skb_dst(skb)), in_mtu) - minlen; + } else + newmss = info->mss; + + opt = (u_int8_t *)tcph; + for (i = sizeof(struct tcphdr); i < tcph->doff*4; i += optlen(opt, i)) { + if (opt[i] == TCPOPT_MSS && tcph->doff*4 - i >= TCPOLEN_MSS && + opt[i+1] == TCPOLEN_MSS) { + u_int16_t oldmss; + + oldmss = (opt[i+2] << 8) | opt[i+3]; + + /* Never increase MSS, even when setting it, as + * doing so results in problems for hosts that rely + * on MSS being set correctly. + */ + if (oldmss <= newmss) + return 0; + + opt[i+2] = (newmss & 0xff00) >> 8; + opt[i+3] = newmss & 0x00ff; + + inet_proto_csum_replace2(&tcph->check, skb, + htons(oldmss), htons(newmss), + 0); + return 0; + } + } + + /* + * MSS Option not found ?! add it.. + */ + if (skb_tailroom(skb) < TCPOLEN_MSS) { + if (pskb_expand_head(skb, 0, + TCPOLEN_MSS - skb_tailroom(skb), + GFP_ATOMIC)) + return -1; + tcph = (struct tcphdr *)(skb_network_header(skb) + tcphoff); + } + + skb_put(skb, TCPOLEN_MSS); + + opt = (u_int8_t *)tcph + sizeof(struct tcphdr); + memmove(opt + TCPOLEN_MSS, opt, tcplen - sizeof(struct tcphdr)); + + inet_proto_csum_replace2(&tcph->check, skb, + htons(tcplen), htons(tcplen + TCPOLEN_MSS), 1); + opt[0] = TCPOPT_MSS; + opt[1] = TCPOLEN_MSS; + opt[2] = (newmss & 0xff00) >> 8; + opt[3] = newmss & 0x00ff; + + inet_proto_csum_replace4(&tcph->check, skb, 0, *((__be32 *)opt), 0); + + oldval = ((__be16 *)tcph)[6]; + tcph->doff += TCPOLEN_MSS/4; + inet_proto_csum_replace2(&tcph->check, skb, + oldval, ((__be16 *)tcph)[6], 0); + return TCPOLEN_MSS; +} + +static u_int32_t tcpmss_reverse_mtu(const struct sk_buff *skb, + unsigned int family) +{ + struct flowi fl = {}; + const struct nf_afinfo *ai; + struct rtable *rt = NULL; + u_int32_t mtu = ~0U; + + if (family == PF_INET) + fl.fl4_dst = ip_hdr(skb)->saddr; + else + fl.fl6_dst = ipv6_hdr(skb)->saddr; + + rcu_read_lock(); + ai = nf_get_afinfo(family); + if (ai != NULL) + ai->route((struct dst_entry **)&rt, &fl); + rcu_read_unlock(); + + if (rt != NULL) { + mtu = dst_mtu(&rt->u.dst); + dst_release(&rt->u.dst); + } + return mtu; +} + +static unsigned int +tcpmss_tg4(struct sk_buff *skb, const struct xt_target_param *par) +{ + struct iphdr *iph = ip_hdr(skb); + __be16 newlen; + int ret; + + ret = tcpmss_mangle_packet(skb, par->targinfo, + tcpmss_reverse_mtu(skb, PF_INET), + iph->ihl * 4, + sizeof(*iph) + sizeof(struct tcphdr)); + if (ret < 0) + return NF_DROP; + if (ret > 0) { + iph = ip_hdr(skb); + newlen = htons(ntohs(iph->tot_len) + ret); + csum_replace2(&iph->check, iph->tot_len, newlen); + iph->tot_len = newlen; + } + return XT_CONTINUE; +} + +#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE) +static unsigned int +tcpmss_tg6(struct sk_buff *skb, const struct xt_target_param *par) +{ + struct ipv6hdr *ipv6h = ipv6_hdr(skb); + u8 nexthdr; + int tcphoff; + int ret; + + nexthdr = ipv6h->nexthdr; + tcphoff = ipv6_skip_exthdr(skb, sizeof(*ipv6h), &nexthdr); + if (tcphoff < 0) + return NF_DROP; + ret = tcpmss_mangle_packet(skb, par->targinfo, + tcpmss_reverse_mtu(skb, PF_INET6), + tcphoff, + sizeof(*ipv6h) + sizeof(struct tcphdr)); + if (ret < 0) + return NF_DROP; + if (ret > 0) { + ipv6h = ipv6_hdr(skb); + ipv6h->payload_len = htons(ntohs(ipv6h->payload_len) + ret); + } + return XT_CONTINUE; +} +#endif + +#define TH_SYN 0x02 + +/* Must specify -p tcp --syn */ +static inline bool find_syn_match(const struct xt_entry_match *m) +{ + const struct xt_tcp *tcpinfo = (const struct xt_tcp *)m->data; + + if (strcmp(m->u.kernel.match->name, "tcp") == 0 && + tcpinfo->flg_cmp & TH_SYN && + !(tcpinfo->invflags & XT_TCP_INV_FLAGS)) + return true; + return false; } -static struct xt_match tcpmss_mt_reg[] __read_mostly = { +static bool tcpmss_tg4_check(const struct xt_tgchk_param *par) +{ + const struct xt_tcpmss_info *info = par->targinfo; + const struct ipt_entry *e = par->entryinfo; + + if (info->mss == XT_TCPMSS_CLAMP_PMTU && + (par->hook_mask & ~((1 << NF_INET_FORWARD) | + (1 << NF_INET_LOCAL_OUT) | + (1 << NF_INET_POST_ROUTING))) != 0) { + printk("xt_TCPMSS: path-MTU clamping only supported in " + "FORWARD, OUTPUT and POSTROUTING hooks\n"); + return false; + } + if (IPT_MATCH_ITERATE(e, find_syn_match)) + return true; + printk("xt_TCPMSS: Only works on TCP SYN packets\n"); + return false; +} + +#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE) +static bool tcpmss_tg6_check(const struct xt_tgchk_param *par) +{ + const struct xt_tcpmss_info *info = par->targinfo; + const struct ip6t_entry *e = par->entryinfo; + + if (info->mss == XT_TCPMSS_CLAMP_PMTU && + (par->hook_mask & ~((1 << NF_INET_FORWARD) | + (1 << NF_INET_LOCAL_OUT) | + (1 << NF_INET_POST_ROUTING))) != 0) { + printk("xt_TCPMSS: path-MTU clamping only supported in " + "FORWARD, OUTPUT and POSTROUTING hooks\n"); + return false; + } + if (IP6T_MATCH_ITERATE(e, find_syn_match)) + return true; + printk("xt_TCPMSS: Only works on TCP SYN packets\n"); + return false; +} +#endif + +static struct xt_target tcpmss_tg_reg[] __read_mostly = { { - .name = "tcpmss", .family = NFPROTO_IPV4, - .match = tcpmss_mt, - .matchsize = sizeof(struct xt_tcpmss_match_info), + .name = "TCPMSS", + .checkentry = tcpmss_tg4_check, + .target = tcpmss_tg4, + .targetsize = sizeof(struct xt_tcpmss_info), .proto = IPPROTO_TCP, .me = THIS_MODULE, }, +#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE) { - .name = "tcpmss", .family = NFPROTO_IPV6, - .match = tcpmss_mt, - .matchsize = sizeof(struct xt_tcpmss_match_info), + .name = "TCPMSS", + .checkentry = tcpmss_tg6_check, + .target = tcpmss_tg6, + .targetsize = sizeof(struct xt_tcpmss_info), .proto = IPPROTO_TCP, .me = THIS_MODULE, }, +#endif }; -static int __init tcpmss_mt_init(void) +static int __init tcpmss_tg_init(void) { - return xt_register_matches(tcpmss_mt_reg, ARRAY_SIZE(tcpmss_mt_reg)); + return xt_register_targets(tcpmss_tg_reg, ARRAY_SIZE(tcpmss_tg_reg)); } -static void __exit tcpmss_mt_exit(void) +static void __exit tcpmss_tg_exit(void) { - xt_unregister_matches(tcpmss_mt_reg, ARRAY_SIZE(tcpmss_mt_reg)); + xt_unregister_targets(tcpmss_tg_reg, ARRAY_SIZE(tcpmss_tg_reg)); } -module_init(tcpmss_mt_init); -module_exit(tcpmss_mt_exit); +module_init(tcpmss_tg_init); +module_exit(tcpmss_tg_exit); diff --git a/net/netfilter/xt_connmark.c b/net/netfilter/xt_connmark.c index 59345706..122aa8b0 100644 --- a/net/netfilter/xt_connmark.c +++ b/net/netfilter/xt_connmark.c @@ -1,5 +1,5 @@ /* - * xt_CONNMARK - Netfilter module to modify the connection mark values + * xt_connmark - Netfilter module to match connection mark values * * Copyright (C) 2002,2004 MARA Systems AB * by Henrik Nordstrom @@ -20,60 +20,34 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #include #include -#include -#include +#include +#include +#include MODULE_AUTHOR("Henrik Nordstrom "); -MODULE_DESCRIPTION("Xtables: connection mark modification"); +MODULE_DESCRIPTION("Xtables: connection mark match"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("ipt_CONNMARK"); -MODULE_ALIAS("ip6t_CONNMARK"); +MODULE_ALIAS("ipt_connmark"); +MODULE_ALIAS("ip6t_connmark"); -#include -#include -#include - -static unsigned int -connmark_tg(struct sk_buff *skb, const struct xt_target_param *par) +static bool +connmark_mt(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct xt_connmark_tginfo1 *info = par->targinfo; + const struct xt_connmark_mtinfo1 *info = par->matchinfo; enum ip_conntrack_info ctinfo; - struct nf_conn *ct; - u_int32_t newmark; + const struct nf_conn *ct; ct = nf_ct_get(skb, &ctinfo); if (ct == NULL) - return XT_CONTINUE; + return false; - switch (info->mode) { - case XT_CONNMARK_SET: - newmark = (ct->mark & ~info->ctmask) ^ info->ctmark; - if (ct->mark != newmark) { - ct->mark = newmark; - nf_conntrack_event_cache(IPCT_MARK, ct); - } - break; - case XT_CONNMARK_SAVE: - newmark = (ct->mark & ~info->ctmask) ^ - (skb->mark & info->nfmask); - if (ct->mark != newmark) { - ct->mark = newmark; - nf_conntrack_event_cache(IPCT_MARK, ct); - } - break; - case XT_CONNMARK_RESTORE: - newmark = (skb->mark & ~info->nfmask) ^ - (ct->mark & info->ctmask); - skb->mark = newmark; - break; - } - - return XT_CONTINUE; + return ((ct->mark & info->mask) == info->mark) ^ info->invert; } -static bool connmark_tg_check(const struct xt_tgchk_param *par) +static bool connmark_mt_check(const struct xt_mtchk_param *par) { if (nf_ct_l3proto_try_module_get(par->family) < 0) { printk(KERN_WARNING "cannot load conntrack support for " @@ -83,31 +57,31 @@ static bool connmark_tg_check(const struct xt_tgchk_param *par) return true; } -static void connmark_tg_destroy(const struct xt_tgdtor_param *par) +static void connmark_mt_destroy(const struct xt_mtdtor_param *par) { nf_ct_l3proto_module_put(par->family); } -static struct xt_target connmark_tg_reg __read_mostly = { - .name = "CONNMARK", +static struct xt_match connmark_mt_reg __read_mostly = { + .name = "connmark", .revision = 1, .family = NFPROTO_UNSPEC, - .checkentry = connmark_tg_check, - .target = connmark_tg, - .targetsize = sizeof(struct xt_connmark_tginfo1), - .destroy = connmark_tg_destroy, + .checkentry = connmark_mt_check, + .match = connmark_mt, + .matchsize = sizeof(struct xt_connmark_mtinfo1), + .destroy = connmark_mt_destroy, .me = THIS_MODULE, }; -static int __init connmark_tg_init(void) +static int __init connmark_mt_init(void) { - return xt_register_target(&connmark_tg_reg); + return xt_register_match(&connmark_mt_reg); } -static void __exit connmark_tg_exit(void) +static void __exit connmark_mt_exit(void) { - xt_unregister_target(&connmark_tg_reg); + xt_unregister_match(&connmark_mt_reg); } -module_init(connmark_tg_init); -module_exit(connmark_tg_exit); +module_init(connmark_mt_init); +module_exit(connmark_mt_exit); diff --git a/net/netfilter/xt_dscp.c b/net/netfilter/xt_dscp.c index 74ce8926..0280d3a8 100644 --- a/net/netfilter/xt_dscp.c +++ b/net/netfilter/xt_dscp.c @@ -1,14 +1,11 @@ -/* x_tables module for setting the IPv4/IPv6 DSCP field, Version 1.8 +/* IP tables module for matching the value of the IPv4/IPv6 DSCP field * * (C) 2002 by Harald Welte - * based on ipt_FTOS.c (C) 2000 by Matthew G. Marsh * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - * - * See RFC2474 for a description of the DSCP field within the IP Header. -*/ + */ #include #include @@ -17,148 +14,102 @@ #include #include -#include +#include MODULE_AUTHOR("Harald Welte "); -MODULE_DESCRIPTION("Xtables: DSCP/TOS field modification"); +MODULE_DESCRIPTION("Xtables: DSCP/TOS field match"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("ipt_DSCP"); -MODULE_ALIAS("ip6t_DSCP"); -MODULE_ALIAS("ipt_TOS"); -MODULE_ALIAS("ip6t_TOS"); +MODULE_ALIAS("ipt_dscp"); +MODULE_ALIAS("ip6t_dscp"); +MODULE_ALIAS("ipt_tos"); +MODULE_ALIAS("ip6t_tos"); -static unsigned int -dscp_tg(struct sk_buff *skb, const struct xt_target_param *par) +static bool +dscp_mt(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct xt_DSCP_info *dinfo = par->targinfo; + const struct xt_dscp_info *info = par->matchinfo; u_int8_t dscp = ipv4_get_dsfield(ip_hdr(skb)) >> XT_DSCP_SHIFT; - if (dscp != dinfo->dscp) { - if (!skb_make_writable(skb, sizeof(struct iphdr))) - return NF_DROP; - - ipv4_change_dsfield(ip_hdr(skb), (__u8)(~XT_DSCP_MASK), - dinfo->dscp << XT_DSCP_SHIFT); - - } - return XT_CONTINUE; + return (dscp == info->dscp) ^ !!info->invert; } -static unsigned int -dscp_tg6(struct sk_buff *skb, const struct xt_target_param *par) +static bool +dscp_mt6(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct xt_DSCP_info *dinfo = par->targinfo; + const struct xt_dscp_info *info = par->matchinfo; u_int8_t dscp = ipv6_get_dsfield(ipv6_hdr(skb)) >> XT_DSCP_SHIFT; - if (dscp != dinfo->dscp) { - if (!skb_make_writable(skb, sizeof(struct ipv6hdr))) - return NF_DROP; - - ipv6_change_dsfield(ipv6_hdr(skb), (__u8)(~XT_DSCP_MASK), - dinfo->dscp << XT_DSCP_SHIFT); - } - return XT_CONTINUE; + return (dscp == info->dscp) ^ !!info->invert; } -static bool dscp_tg_check(const struct xt_tgchk_param *par) +static bool dscp_mt_check(const struct xt_mtchk_param *par) { - const struct xt_DSCP_info *info = par->targinfo; + const struct xt_dscp_info *info = par->matchinfo; if (info->dscp > XT_DSCP_MAX) { - printk(KERN_WARNING "DSCP: dscp %x out of range\n", info->dscp); + printk(KERN_ERR "xt_dscp: dscp %x out of range\n", info->dscp); return false; } + return true; } -static unsigned int -tos_tg(struct sk_buff *skb, const struct xt_target_param *par) +static bool tos_mt(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct xt_tos_target_info *info = par->targinfo; - struct iphdr *iph = ip_hdr(skb); - u_int8_t orig, nv; + const struct xt_tos_match_info *info = par->matchinfo; - orig = ipv4_get_dsfield(iph); - nv = (orig & ~info->tos_mask) ^ info->tos_value; - - if (orig != nv) { - if (!skb_make_writable(skb, sizeof(struct iphdr))) - return NF_DROP; - iph = ip_hdr(skb); - ipv4_change_dsfield(iph, 0, nv); - } - - return XT_CONTINUE; + if (par->match->family == NFPROTO_IPV4) + return ((ip_hdr(skb)->tos & info->tos_mask) == + info->tos_value) ^ !!info->invert; + else + return ((ipv6_get_dsfield(ipv6_hdr(skb)) & info->tos_mask) == + info->tos_value) ^ !!info->invert; } -static unsigned int -tos_tg6(struct sk_buff *skb, const struct xt_target_param *par) -{ - const struct xt_tos_target_info *info = par->targinfo; - struct ipv6hdr *iph = ipv6_hdr(skb); - u_int8_t orig, nv; - - orig = ipv6_get_dsfield(iph); - nv = (orig & info->tos_mask) ^ info->tos_value; - - if (orig != nv) { - if (!skb_make_writable(skb, sizeof(struct iphdr))) - return NF_DROP; - iph = ipv6_hdr(skb); - ipv6_change_dsfield(iph, 0, nv); - } - - return XT_CONTINUE; -} - -static struct xt_target dscp_tg_reg[] __read_mostly = { +static struct xt_match dscp_mt_reg[] __read_mostly = { { - .name = "DSCP", + .name = "dscp", .family = NFPROTO_IPV4, - .checkentry = dscp_tg_check, - .target = dscp_tg, - .targetsize = sizeof(struct xt_DSCP_info), - .table = "mangle", + .checkentry = dscp_mt_check, + .match = dscp_mt, + .matchsize = sizeof(struct xt_dscp_info), .me = THIS_MODULE, }, { - .name = "DSCP", + .name = "dscp", .family = NFPROTO_IPV6, - .checkentry = dscp_tg_check, - .target = dscp_tg6, - .targetsize = sizeof(struct xt_DSCP_info), - .table = "mangle", + .checkentry = dscp_mt_check, + .match = dscp_mt6, + .matchsize = sizeof(struct xt_dscp_info), .me = THIS_MODULE, }, { - .name = "TOS", + .name = "tos", .revision = 1, .family = NFPROTO_IPV4, - .table = "mangle", - .target = tos_tg, - .targetsize = sizeof(struct xt_tos_target_info), + .match = tos_mt, + .matchsize = sizeof(struct xt_tos_match_info), .me = THIS_MODULE, }, { - .name = "TOS", + .name = "tos", .revision = 1, .family = NFPROTO_IPV6, - .table = "mangle", - .target = tos_tg6, - .targetsize = sizeof(struct xt_tos_target_info), + .match = tos_mt, + .matchsize = sizeof(struct xt_tos_match_info), .me = THIS_MODULE, }, }; -static int __init dscp_tg_init(void) +static int __init dscp_mt_init(void) { - return xt_register_targets(dscp_tg_reg, ARRAY_SIZE(dscp_tg_reg)); + return xt_register_matches(dscp_mt_reg, ARRAY_SIZE(dscp_mt_reg)); } -static void __exit dscp_tg_exit(void) +static void __exit dscp_mt_exit(void) { - xt_unregister_targets(dscp_tg_reg, ARRAY_SIZE(dscp_tg_reg)); + xt_unregister_matches(dscp_mt_reg, ARRAY_SIZE(dscp_mt_reg)); } -module_init(dscp_tg_init); -module_exit(dscp_tg_exit); +module_init(dscp_mt_init); +module_exit(dscp_mt_exit); diff --git a/net/netfilter/xt_hl.c b/net/netfilter/xt_hl.c index abdb99b1..7726154c 100644 --- a/net/netfilter/xt_hl.c +++ b/net/netfilter/xt_hl.c @@ -16,8 +16,8 @@ #include #include -#include -#include +#include +#include MODULE_AUTHOR("Maciej Soltysiak "); MODULE_DESCRIPTION("Xtables: Hoplimit/TTL field match"); diff --git a/net/netfilter/xt_mark.c b/net/netfilter/xt_mark.c index 809f9ec3..1db07d81 100644 --- a/net/netfilter/xt_mark.c +++ b/net/netfilter/xt_mark.c @@ -1,9 +1,9 @@ /* - * xt_MARK - Netfilter module to modify the NFMARK field of an skb + * xt_mark - Netfilter module to match NFMARK value * * (C) 1999-2001 Marc Boucher * Copyright © CC Computer Consultants GmbH, 2007 - 2008 - * Jan Engelhardt + * Jan Engelhardt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -12,45 +12,42 @@ #include #include -#include -#include -#include #include +#include MODULE_LICENSE("GPL"); MODULE_AUTHOR("Marc Boucher "); -MODULE_DESCRIPTION("Xtables: packet mark modification"); -MODULE_ALIAS("ipt_MARK"); -MODULE_ALIAS("ip6t_MARK"); +MODULE_DESCRIPTION("Xtables: packet mark match"); +MODULE_ALIAS("ipt_mark"); +MODULE_ALIAS("ip6t_mark"); -static unsigned int -mark_tg(struct sk_buff *skb, const struct xt_target_param *par) +static bool +mark_mt(const struct sk_buff *skb, const struct xt_match_param *par) { - const struct xt_mark_tginfo2 *info = par->targinfo; + const struct xt_mark_mtinfo1 *info = par->matchinfo; - skb->mark = (skb->mark & ~info->mask) ^ info->mark; - return XT_CONTINUE; + return ((skb->mark & info->mask) == info->mark) ^ info->invert; } -static struct xt_target mark_tg_reg __read_mostly = { - .name = "MARK", - .revision = 2, +static struct xt_match mark_mt_reg __read_mostly = { + .name = "mark", + .revision = 1, .family = NFPROTO_UNSPEC, - .target = mark_tg, - .targetsize = sizeof(struct xt_mark_tginfo2), + .match = mark_mt, + .matchsize = sizeof(struct xt_mark_mtinfo1), .me = THIS_MODULE, }; -static int __init mark_tg_init(void) +static int __init mark_mt_init(void) { - return xt_register_target(&mark_tg_reg); + return xt_register_match(&mark_mt_reg); } -static void __exit mark_tg_exit(void) +static void __exit mark_mt_exit(void) { - xt_unregister_target(&mark_tg_reg); + xt_unregister_match(&mark_mt_reg); } -module_init(mark_tg_init); -module_exit(mark_tg_exit); +module_init(mark_mt_init); +module_exit(mark_mt_exit); diff --git a/net/netfilter/xt_rateest.c b/net/netfilter/xt_rateest.c index d80b8192..4fc6a917 100644 --- a/net/netfilter/xt_rateest.c +++ b/net/netfilter/xt_rateest.c @@ -8,176 +8,149 @@ #include #include #include -#include -#include -#include -#include -#include #include -#include +#include #include -static DEFINE_MUTEX(xt_rateest_mutex); -#define RATEEST_HSIZE 16 -static struct hlist_head rateest_hash[RATEEST_HSIZE] __read_mostly; -static unsigned int jhash_rnd __read_mostly; - -static unsigned int xt_rateest_hash(const char *name) +static bool +xt_rateest_mt(const struct sk_buff *skb, const struct xt_match_param *par) { - return jhash(name, FIELD_SIZEOF(struct xt_rateest, name), jhash_rnd) & - (RATEEST_HSIZE - 1); -} + const struct xt_rateest_match_info *info = par->matchinfo; + struct gnet_stats_rate_est *r; + u_int32_t bps1, bps2, pps1, pps2; + bool ret = true; -static void xt_rateest_hash_insert(struct xt_rateest *est) -{ - unsigned int h; + spin_lock_bh(&info->est1->lock); + r = &info->est1->rstats; + if (info->flags & XT_RATEEST_MATCH_DELTA) { + bps1 = info->bps1 >= r->bps ? info->bps1 - r->bps : 0; + pps1 = info->pps1 >= r->pps ? info->pps1 - r->pps : 0; + } else { + bps1 = r->bps; + pps1 = r->pps; + } + spin_unlock_bh(&info->est1->lock); - h = xt_rateest_hash(est->name); - hlist_add_head(&est->list, &rateest_hash[h]); -} - -struct xt_rateest *xt_rateest_lookup(const char *name) -{ - struct xt_rateest *est; - struct hlist_node *n; - unsigned int h; - - h = xt_rateest_hash(name); - mutex_lock(&xt_rateest_mutex); - hlist_for_each_entry(est, n, &rateest_hash[h], list) { - if (strcmp(est->name, name) == 0) { - est->refcnt++; - mutex_unlock(&xt_rateest_mutex); - return est; + if (info->flags & XT_RATEEST_MATCH_ABS) { + bps2 = info->bps2; + pps2 = info->pps2; + } else { + spin_lock_bh(&info->est2->lock); + r = &info->est2->rstats; + if (info->flags & XT_RATEEST_MATCH_DELTA) { + bps2 = info->bps2 >= r->bps ? info->bps2 - r->bps : 0; + pps2 = info->pps2 >= r->pps ? info->pps2 - r->pps : 0; + } else { + bps2 = r->bps; + pps2 = r->pps; } - } - mutex_unlock(&xt_rateest_mutex); - return NULL; -} -EXPORT_SYMBOL_GPL(xt_rateest_lookup); - -void xt_rateest_put(struct xt_rateest *est) -{ - mutex_lock(&xt_rateest_mutex); - if (--est->refcnt == 0) { - hlist_del(&est->list); - gen_kill_estimator(&est->bstats, &est->rstats); - kfree(est); - } - mutex_unlock(&xt_rateest_mutex); -} -EXPORT_SYMBOL_GPL(xt_rateest_put); - -static unsigned int -xt_rateest_tg(struct sk_buff *skb, const struct xt_target_param *par) -{ - const struct xt_rateest_target_info *info = par->targinfo; - struct gnet_stats_basic_packed *stats = &info->est->bstats; - - spin_lock_bh(&info->est->lock); - stats->bytes += skb->len; - stats->packets++; - spin_unlock_bh(&info->est->lock); - - return XT_CONTINUE; -} - -static bool xt_rateest_tg_checkentry(const struct xt_tgchk_param *par) -{ - struct xt_rateest_target_info *info = par->targinfo; - struct xt_rateest *est; - struct { - struct nlattr opt; - struct gnet_estimator est; - } cfg; - - est = xt_rateest_lookup(info->name); - if (est) { - /* - * If estimator parameters are specified, they must match the - * existing estimator. - */ - if ((!info->interval && !info->ewma_log) || - (info->interval != est->params.interval || - info->ewma_log != est->params.ewma_log)) { - xt_rateest_put(est); - return false; - } - info->est = est; - return true; + spin_unlock_bh(&info->est2->lock); } - est = kzalloc(sizeof(*est), GFP_KERNEL); - if (!est) + switch (info->mode) { + case XT_RATEEST_MATCH_LT: + if (info->flags & XT_RATEEST_MATCH_BPS) + ret &= bps1 < bps2; + if (info->flags & XT_RATEEST_MATCH_PPS) + ret &= pps1 < pps2; + break; + case XT_RATEEST_MATCH_GT: + if (info->flags & XT_RATEEST_MATCH_BPS) + ret &= bps1 > bps2; + if (info->flags & XT_RATEEST_MATCH_PPS) + ret &= pps1 > pps2; + break; + case XT_RATEEST_MATCH_EQ: + if (info->flags & XT_RATEEST_MATCH_BPS) + ret &= bps1 == bps2; + if (info->flags & XT_RATEEST_MATCH_PPS) + ret &= pps1 == pps2; + break; + } + + ret ^= info->flags & XT_RATEEST_MATCH_INVERT ? true : false; + return ret; +} + +static bool xt_rateest_mt_checkentry(const struct xt_mtchk_param *par) +{ + struct xt_rateest_match_info *info = par->matchinfo; + struct xt_rateest *est1, *est2; + + if (hweight32(info->flags & (XT_RATEEST_MATCH_ABS | + XT_RATEEST_MATCH_REL)) != 1) goto err1; - strlcpy(est->name, info->name, sizeof(est->name)); - spin_lock_init(&est->lock); - est->refcnt = 1; - est->params.interval = info->interval; - est->params.ewma_log = info->ewma_log; + if (!(info->flags & (XT_RATEEST_MATCH_BPS | XT_RATEEST_MATCH_PPS))) + goto err1; - cfg.opt.nla_len = nla_attr_size(sizeof(cfg.est)); - cfg.opt.nla_type = TCA_STATS_RATE_EST; - cfg.est.interval = info->interval; - cfg.est.ewma_log = info->ewma_log; + switch (info->mode) { + case XT_RATEEST_MATCH_EQ: + case XT_RATEEST_MATCH_LT: + case XT_RATEEST_MATCH_GT: + break; + default: + goto err1; + } - if (gen_new_estimator(&est->bstats, &est->rstats, &est->lock, - &cfg.opt) < 0) - goto err2; + est1 = xt_rateest_lookup(info->name1); + if (!est1) + goto err1; - info->est = est; - xt_rateest_hash_insert(est); + if (info->flags & XT_RATEEST_MATCH_REL) { + est2 = xt_rateest_lookup(info->name2); + if (!est2) + goto err2; + } else + est2 = NULL; + + info->est1 = est1; + info->est2 = est2; return true; err2: - kfree(est); + xt_rateest_put(est1); err1: return false; } -static void xt_rateest_tg_destroy(const struct xt_tgdtor_param *par) +static void xt_rateest_mt_destroy(const struct xt_mtdtor_param *par) { - struct xt_rateest_target_info *info = par->targinfo; + struct xt_rateest_match_info *info = par->matchinfo; - xt_rateest_put(info->est); + xt_rateest_put(info->est1); + if (info->est2) + xt_rateest_put(info->est2); } -static struct xt_target xt_rateest_tg_reg __read_mostly = { - .name = "RATEEST", +static struct xt_match xt_rateest_mt_reg __read_mostly = { + .name = "rateest", .revision = 0, .family = NFPROTO_UNSPEC, - .target = xt_rateest_tg, - .checkentry = xt_rateest_tg_checkentry, - .destroy = xt_rateest_tg_destroy, - .targetsize = sizeof(struct xt_rateest_target_info), + .match = xt_rateest_mt, + .checkentry = xt_rateest_mt_checkentry, + .destroy = xt_rateest_mt_destroy, + .matchsize = sizeof(struct xt_rateest_match_info), .me = THIS_MODULE, }; -static int __init xt_rateest_tg_init(void) +static int __init xt_rateest_mt_init(void) { - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(rateest_hash); i++) - INIT_HLIST_HEAD(&rateest_hash[i]); - - get_random_bytes(&jhash_rnd, sizeof(jhash_rnd)); - return xt_register_target(&xt_rateest_tg_reg); + return xt_register_match(&xt_rateest_mt_reg); } -static void __exit xt_rateest_tg_fini(void) +static void __exit xt_rateest_mt_fini(void) { - xt_unregister_target(&xt_rateest_tg_reg); + xt_unregister_match(&xt_rateest_mt_reg); } - MODULE_AUTHOR("Patrick McHardy "); MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Xtables: packet rate estimator"); -MODULE_ALIAS("ipt_RATEEST"); -MODULE_ALIAS("ip6t_RATEEST"); -module_init(xt_rateest_tg_init); -module_exit(xt_rateest_tg_fini); +MODULE_DESCRIPTION("xtables rate estimator match"); +MODULE_ALIAS("ipt_rateest"); +MODULE_ALIAS("ip6t_rateest"); +module_init(xt_rateest_mt_init); +module_exit(xt_rateest_mt_fini); diff --git a/net/netfilter/xt_tcpmss.c b/net/netfilter/xt_tcpmss.c new file mode 100644 index 00000000..4809b34b --- /dev/null +++ b/net/netfilter/xt_tcpmss.c @@ -0,0 +1,110 @@ +/* Kernel module to match TCP MSS values. */ + +/* Copyright (C) 2000 Marc Boucher + * Portions (C) 2005 by Harald Welte + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include + +#include +#include + +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Marc Boucher "); +MODULE_DESCRIPTION("Xtables: TCP MSS match"); +MODULE_ALIAS("ipt_tcpmss"); +MODULE_ALIAS("ip6t_tcpmss"); + +static bool +tcpmss_mt(const struct sk_buff *skb, const struct xt_match_param *par) +{ + const struct xt_tcpmss_match_info *info = par->matchinfo; + const struct tcphdr *th; + struct tcphdr _tcph; + /* tcp.doff is only 4 bits, ie. max 15 * 4 bytes */ + const u_int8_t *op; + u8 _opt[15 * 4 - sizeof(_tcph)]; + unsigned int i, optlen; + + /* If we don't have the whole header, drop packet. */ + th = skb_header_pointer(skb, par->thoff, sizeof(_tcph), &_tcph); + if (th == NULL) + goto dropit; + + /* Malformed. */ + if (th->doff*4 < sizeof(*th)) + goto dropit; + + optlen = th->doff*4 - sizeof(*th); + if (!optlen) + goto out; + + /* Truncated options. */ + op = skb_header_pointer(skb, par->thoff + sizeof(*th), optlen, _opt); + if (op == NULL) + goto dropit; + + for (i = 0; i < optlen; ) { + if (op[i] == TCPOPT_MSS + && (optlen - i) >= TCPOLEN_MSS + && op[i+1] == TCPOLEN_MSS) { + u_int16_t mssval; + + mssval = (op[i+2] << 8) | op[i+3]; + + return (mssval >= info->mss_min && + mssval <= info->mss_max) ^ info->invert; + } + if (op[i] < 2) + i++; + else + i += op[i+1] ? : 1; + } +out: + return info->invert; + +dropit: + *par->hotdrop = true; + return false; +} + +static struct xt_match tcpmss_mt_reg[] __read_mostly = { + { + .name = "tcpmss", + .family = NFPROTO_IPV4, + .match = tcpmss_mt, + .matchsize = sizeof(struct xt_tcpmss_match_info), + .proto = IPPROTO_TCP, + .me = THIS_MODULE, + }, + { + .name = "tcpmss", + .family = NFPROTO_IPV6, + .match = tcpmss_mt, + .matchsize = sizeof(struct xt_tcpmss_match_info), + .proto = IPPROTO_TCP, + .me = THIS_MODULE, + }, +}; + +static int __init tcpmss_mt_init(void) +{ + return xt_register_matches(tcpmss_mt_reg, ARRAY_SIZE(tcpmss_mt_reg)); +} + +static void __exit tcpmss_mt_exit(void) +{ + xt_unregister_matches(tcpmss_mt_reg, ARRAY_SIZE(tcpmss_mt_reg)); +} + +module_init(tcpmss_mt_init); +module_exit(tcpmss_mt_exit);