From dfb0f3aea55c1730301f1144468c8008a9e1090e Mon Sep 17 00:00:00 2001 From: Rongjun Chen Date: Sun, 8 Apr 2018 11:52:03 +0800 Subject: [PATCH] wifi: update wifi driver to 1.579.77.41.5 Change-Id: Id27b780e0c87ae14a2b8c7369ef3cd8a4c36e237 Signed-off-by: Rongjun Chen --- bcmdhd.1.579.77.41.1.cn/Makefile | 169 +- bcmdhd.1.579.77.41.1.cn/bcmsdh_linux.c | 11 +- bcmdhd.1.579.77.41.1.cn/bcmsdh_sdmmc.c | 5 +- bcmdhd.1.579.77.41.1.cn/bcmsdh_sdmmc_linux.c | 4 - bcmdhd.1.579.77.41.1.cn/dbus.c | 2131 +++++++++++ bcmdhd.1.579.77.41.1.cn/dbus_usb.c | 1167 ++++++ bcmdhd.1.579.77.41.1.cn/dbus_usb_linux.c | 3385 ++++++++++++++++++ bcmdhd.1.579.77.41.1.cn/dhd.h | 22 +- bcmdhd.1.579.77.41.1.cn/dhd_cdc.c | 112 +- bcmdhd.1.579.77.41.1.cn/dhd_cfg80211.c | 2 + bcmdhd.1.579.77.41.1.cn/dhd_common.c | 48 +- bcmdhd.1.579.77.41.1.cn/dhd_config.c | 763 ++-- bcmdhd.1.579.77.41.1.cn/dhd_config.h | 49 +- bcmdhd.1.579.77.41.1.cn/dhd_gpio.c | 19 +- bcmdhd.1.579.77.41.1.cn/dhd_linux.c | 827 ++++- bcmdhd.1.579.77.41.1.cn/dhd_linux_platdev.c | 48 +- bcmdhd.1.579.77.41.1.cn/dhd_linux_wq.h | 3 + bcmdhd.1.579.77.41.1.cn/dhd_pcie.c | 8 + bcmdhd.1.579.77.41.1.cn/dhd_pcie_linux.c | 6 - bcmdhd.1.579.77.41.1.cn/dhd_pno.c | 1 + bcmdhd.1.579.77.41.1.cn/dhd_sdio.c | 75 +- bcmdhd.1.579.77.41.1.cn/dhd_wlfc.c | 38 +- bcmdhd.1.579.77.41.1.cn/dhd_wlfc.h | 5 + bcmdhd.1.579.77.41.1.cn/include/bcmdevs.h | 6 +- bcmdhd.1.579.77.41.1.cn/include/dbus.h | 14 +- bcmdhd.1.579.77.41.1.cn/include/epivers.h | 2 +- bcmdhd.1.579.77.41.1.cn/include/sbchipc.h | 2 +- bcmdhd.1.579.77.41.1.cn/include/usbrdl.h | 134 + bcmdhd.1.579.77.41.1.cn/include/wlioctl.h | 15 + bcmdhd.1.579.77.41.1.cn/linux_osl.c | 9 + bcmdhd.1.579.77.41.1.cn/siutils.c | 6 +- bcmdhd.1.579.77.41.1.cn/wl_android.c | 90 +- bcmdhd.1.579.77.41.1.cn/wl_android.h | 37 +- bcmdhd.1.579.77.41.1.cn/wl_android_ext.c | 1404 ++++++-- bcmdhd.1.579.77.41.1.cn/wl_cfg80211.c | 448 ++- bcmdhd.1.579.77.41.1.cn/wl_cfg80211.h | 19 +- bcmdhd.1.579.77.41.1.cn/wl_cfgp2p.c | 4 + bcmdhd.1.579.77.41.1.cn/wl_cfgvendor.c | 3 +- bcmdhd.1.579.77.41.1.cn/wl_iw.c | 7 +- 39 files changed, 10062 insertions(+), 1036 deletions(-) create mode 100755 bcmdhd.1.579.77.41.1.cn/dbus.c create mode 100755 bcmdhd.1.579.77.41.1.cn/dbus_usb.c create mode 100755 bcmdhd.1.579.77.41.1.cn/dbus_usb_linux.c create mode 100755 bcmdhd.1.579.77.41.1.cn/include/usbrdl.h diff --git a/bcmdhd.1.579.77.41.1.cn/Makefile b/bcmdhd.1.579.77.41.1.cn/Makefile index 0e7e33d..3cbb808 100755 --- a/bcmdhd.1.579.77.41.1.cn/Makefile +++ b/bcmdhd.1.579.77.41.1.cn/Makefile @@ -5,140 +5,183 @@ # if not confiure pci mode, we use sdio mode as default ifeq ($(CONFIG_BCMDHD_PCIE),) $(info bcm SDIO driver configured) -CONFIG_BCMDHD_SDIO := y CONFIG_DHD_USE_STATIC_BUF :=y endif +#CONFIG_BCMDHD_SDIOO := y +#CONFIG_BCMDHD_PCIE := y +#CONFIG_BCMDHD_USB := y CONFIG_BCMDHD_PROPTXSTATUS := y +CONFIG_MACH_PLATFORM := y +#CONFIG_BCMDHD_DTS := y + export CONFIG_BCMDHD = m export CONFIG_BCMDHD_OOB = y export CONFIG_VTS_SUPPORT = y -DHDCFLAGS = -Wall -Wstrict-prototypes -Dlinux -DBCMDRIVER -DSDTEST \ +DHDCFLAGS = -Wall -Wstrict-prototypes -Dlinux -DBCMDRIVER \ -DBCMDONGLEHOST -DUNRELEASEDCHIP -DBCMDMA32 -DBCMFILEIMAGE \ -DDHDTHREAD -DDHD_DEBUG -DSHOW_EVENTS -DBCMDBG -DGET_OTP_MAC_ENABLE \ -DWIFI_ACT_FRAME -DARP_OFFLOAD_SUPPORT -DSUPPORT_PM2_ONLY \ -DKEEP_ALIVE -DPKT_FILTER_SUPPORT -DPNO_SUPPORT -DDHDTCPACK_SUPPRESS \ -DDHD_DONOT_FORWARD_BCMEVENT_AS_NETWORK_PKT -DRXFRAME_THREAD \ - -DTSQ_MULTIPLIER -DMFP -DWL_EXT_IAPSTA \ - -DENABLE_INSMOD_NO_FW_LOAD \ + -DTSQ_MULTIPLIER -DMFP \ + -DWL_EXT_IAPSTA \ -I$(src) -I$(src)/include -DHDOFILES = aiutils.o siutils.o sbutils.o bcmutils.o bcmwifi_channels.o \ - dhd_linux.o dhd_linux_platdev.o dhd_linux_sched.o dhd_pno.o \ - dhd_common.o dhd_ip.o dhd_linux_wq.o dhd_custom_gpio.o \ - bcmevent.o hndpmu.o linux_osl.o wldev_common.o wl_android.o \ +DHDOFILES = aiutils.o siutils.o sbutils.o bcmutils.o bcmwifi_channels.o \ + dhd_linux.o dhd_linux_platdev.o dhd_linux_sched.o dhd_pno.o \ + dhd_common.o dhd_ip.o dhd_linux_wq.o dhd_custom_gpio.o \ + bcmevent.o hndpmu.o linux_osl.o wldev_common.o wl_android.o \ hnd_pktq.o hnd_pktpool.o dhd_config.o wl_android_ext.o ifneq ($(CONFIG_BCMDHD_SDIO),) DHDCFLAGS += \ -DBCMSDIO -DMMC_SDIO_ABORT -DBCMLXSDMMC -DUSE_SDIOFIFO_IOVAR \ - -DBDC -DDHD_USE_IDLECOUNT -DBCMSDIOH_TXGLOM -DBCMSDIOH_TXGLOM_EXT \ - -DCUSTOM_SDIO_F2_BLKSIZE=256 + -DSDTEST -DBDC -DDHD_USE_IDLECOUNT -DCUSTOM_SDIO_F2_BLKSIZE=256 \ + -DBCMSDIOH_TXGLOM -DBCMSDIOH_TXGLOM_EXT +DHDCFLAGS += -DENABLE_INSMOD_NO_FW_LOAD -DHDOFILES += bcmsdh.o bcmsdh_linux.o bcmsdh_sdmmc.o bcmsdh_sdmmc_linux.o \ +DHDOFILES += bcmsdh.o bcmsdh_linux.o bcmsdh_sdmmc.o bcmsdh_sdmmc_linux.o \ dhd_sdio.o dhd_cdc.o dhd_wlfc.o ifeq ($(CONFIG_BCMDHD_OOB),y) -DHDCFLAGS += -DOOB_INTR_ONLY -DCUSTOMER_OOB -DHW_OOB + DHDCFLAGS += -DOOB_INTR_ONLY -DCUSTOMER_OOB -DHW_OOB ifeq ($(CONFIG_BCMDHD_DISABLE_WOWLAN),y) -DHDCFLAGS += -DDISABLE_WOWLAN + DHDCFLAGS += -DDISABLE_WOWLAN endif else -DHDCFLAGS += -DSDIO_ISR_THREAD -endif -endif - -ifeq ($(CONFIG_BCMDHD_PROPTXSTATUS),y) -ifneq ($(CONFIG_BCMDHD_SDIO),) -DHDCFLAGS += -DPROP_TXSTATUS -endif -ifneq ($(CONFIG_CFG80211),) -DHDCFLAGS += -DPROP_TXSTATUS_VSDB + DHDCFLAGS += -DSDIO_ISR_THREAD endif endif ifneq ($(CONFIG_BCMDHD_PCIE),) DHDCFLAGS += \ - -DPCIE_FULL_DONGLE -DBCMPCIE -DCUSTOM_DPC_PRIO_SETTING=-1 + -DPCIE_FULL_DONGLE -DBCMPCIE -DCUSTOM_DPC_PRIO_SETTING=-1 \ + -DDONGLE_ENABLE_ISOLATION +DHDCFLAGS += -DENABLE_INSMOD_NO_FW_LOAD DHDCFLAGS += -DDHD_PCIE_BAR1_WIN_BASE_FIX=0x200000 + +DHDOFILES += dhd_pcie.o dhd_pcie_linux.o pcie_core.o dhd_flowring.o \ + dhd_msgbuf.o + ifneq ($(CONFIG_PCI_MSI),) -DHDCFLAGS += -DDHD_USE_MSI + DHDCFLAGS += -DDHD_USE_MSI endif ifeq ($(CONFIG_DHD_USE_STATIC_BUF),y) -DHDCFLAGS += -DDHD_USE_STATIC_CTRLBUF + DHDCFLAGS += -DDHD_USE_STATIC_CTRLBUF +endif endif -DHDOFILES += dhd_pcie.o dhd_pcie_linux.o pcie_core.o dhd_flowring.o \ - dhd_msgbuf.o +ifneq ($(CONFIG_BCMDHD_USB),) +DHDCFLAGS += -DUSBOS_TX_THREAD -DBCMDBUS -DBCMTRXV2 -DDBUS_USB_LOOPBACK \ + -DBDC + +DHDOFILES += dbus.o dbus_usb.o dbus_usb_linux.o dhd_cdc.o dhd_wlfc.o +endif + +ifeq ($(CONFIG_BCMDHD_PROPTXSTATUS),y) +ifneq ($(CONFIG_BCMDHD_USB),) + DHDCFLAGS += -DPROP_TXSTATUS +endif +ifneq ($(CONFIG_BCMDHD_SDIO),) + DHDCFLAGS += -DPROP_TXSTATUS +endif +ifneq ($(CONFIG_CFG80211),) + DHDCFLAGS += -DPROP_TXSTATUS_VSDB +endif endif ifeq ($(CONFIG_VTS_SUPPORT),y) DHDCFLAGS += \ - -DGSCAN_SUPPORT -DRTT_SUPPORT -DCUSTOM_FORCE_NODFS_FLAG \ - -DLINKSTAT_SUPPORT -DDEBUGABILITY -DDBG_PKT_MON -DKEEP_ALIVE -DPKT_FILTER_SUPPORT \ - -DAPF -DNDO_CONFIG_SUPPORT -DRSSI_MONITOR_SUPPORT -DDHDTCPACK_SUPPRESS -DDHD_WAKE_STATUS \ + -DGSCAN_SUPPORT -DRTT_SUPPORT -DCUSTOM_FORCE_NODFS_FLAG \ + -DLINKSTAT_SUPPORT -DDEBUGABILITY -DDBG_PKT_MON -DPKT_FILTER_SUPPORT \ + -DAPF -DNDO_CONFIG_SUPPORT -DRSSI_MONITOR_SUPPORT -DDHD_WAKE_STATUS \ -DCUSTOM_COUNTRY_CODE -DDHD_FW_COREDUMP -DEXPLICIT_DISCIF_CLEANUP -DHDOFILES += dhd_debug_linux.o dhd_debug.o bcmxtlv.o \ - dhd_rtt.o bcm_app_utils.o +DHDOFILES += dhd_debug_linux.o dhd_debug.o bcmxtlv.o dhd_rtt.o \ + bcm_app_utils.o endif +# MESH support for kernel 3.10 later +ifeq ($(CONFIG_WL_MESH),y) + DHDCFLAGS += -DWLMESH +ifneq ($(CONFIG_BCMDHD_PCIE),) + DHDCFLAGS += -DBCM_HOST_BUF -DDMA_HOST_BUFFER_LEN=0x80000 +endif + DHDCFLAGS += -DDHD_UPDATE_INTF_MAC + DHDCFLAGS :=$(filter-out -DDHD_FW_COREDUMP,$(DHDCFLAGS)) + DHDCFLAGS :=$(filter-out -DSET_RANDOM_MAC_SOFTAP,$(DHDCFLAGS)) +endif +ifeq ($(CONFIG_BCMDHD_SDIO),y) obj-$(CONFIG_BCMDHD) += dhd.o dhd-objs += $(DHDOFILES) +else +obj-$(CONFIG_BCMDHD) += bcmdhd.o +bcmdhd-objs += $(DHDOFILES) +endif -#ifeq ($(CONFIG_MACH_PLATFORM),y) -DHDOFILES += dhd_gpio.o -DHDCFLAGS += -DCUSTOMER_HW -DDHD_OF_SUPPORT -DHDCFLAGS += -DCUSTOMER_HW_AMLOGIC -#DHDCFLAGS += -DBCMWAPI_WPI -DBCMWAPI_WAI -#endif +ifeq ($(CONFIG_MACH_PLATFORM),y) + DHDOFILES += dhd_gpio.o +ifeq ($(CONFIG_BCMDHD_DTS),y) + DHDCFLAGS += -DCONFIG_DTS +else + DHDCFLAGS += -DCUSTOMER_HW -DDHD_OF_SUPPORT +endif + DHDCFLAGS += -DCUSTOMER_HW_AMLOGIC +# DHDCFLAGS += -DBCMWAPI_WPI -DBCMWAPI_WAI +endif ifeq ($(CONFIG_BCMDHD_AG),y) -DHDCFLAGS += -DBAND_AG + DHDCFLAGS += -DBAND_AG endif ifeq ($(CONFIG_DHD_USE_STATIC_BUF),y) -#obj-m += dhd_static_buf.o -DHDCFLAGS += -DSTATIC_WL_PRIV_STRUCT -DENHANCED_STATIC_BUF -DHDCFLAGS += -DDHD_USE_STATIC_MEMDUMP -DCONFIG_DHD_USE_STATIC_BUF + DHDCFLAGS += -DSTATIC_WL_PRIV_STRUCT -DENHANCED_STATIC_BUF + DHDCFLAGS += -DDHD_USE_STATIC_MEMDUMP -DCONFIG_DHD_USE_STATIC_BUF endif ifneq ($(CONFIG_WIRELESS_EXT),) -DHDOFILES += wl_iw.o wl_escan.o -DHDCFLAGS += -DSOFTAP -DWL_WIRELESS_EXT -DUSE_IW -DWL_ESCAN + DHDOFILES += wl_iw.o wl_escan.o + DHDCFLAGS += -DSOFTAP -DWL_WIRELESS_EXT -DUSE_IW -DWL_ESCAN endif ifneq ($(CONFIG_CFG80211),) -DHDOFILES += wl_cfg80211.o wl_cfgp2p.o wl_linux_mon.o wl_cfg_btcoex.o wl_cfgvendor.o -DHDOFILES += dhd_cfg80211.o -DHDCFLAGS += -DWL_CFG80211 -DWLP2P -DWL_CFG80211_STA_EVENT -DWL_ENABLE_P2P_IF -DHDCFLAGS += -DWL_IFACE_COMB_NUM_CHANNELS -DHDCFLAGS += -DCUSTOM_ROAM_TRIGGER_SETTING=-65 -DHDCFLAGS += -DCUSTOM_ROAM_DELTA_SETTING=15 -DHDCFLAGS += -DCUSTOM_KEEP_ALIVE_SETTING=28000 -DHDCFLAGS += -DCUSTOM_PNO_EVENT_LOCK_xTIME=7 -DHDCFLAGS += -DWL_SUPPORT_AUTO_CHANNEL -DHDCFLAGS += -DWL_SUPPORT_BACKPORTED_KPATCHES -DHDCFLAGS += -DESCAN_RESULT_PATCH -DHDCFLAGS += -DVSDB -DWL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST -DHDCFLAGS += -DWLTDLS -DMIRACAST_AMPDU_SIZE=8 -DHDCFLAGS += -DWL_VIRTUAL_APSTA + DHDOFILES += wl_cfg80211.o wl_cfgp2p.o wl_linux_mon.o wl_cfg_btcoex.o wl_cfgvendor.o + DHDOFILES += dhd_cfg80211.o + DHDCFLAGS += -DWL_CFG80211 -DWLP2P -DWL_CFG80211_STA_EVENT -DWL_ENABLE_P2P_IF + DHDCFLAGS += -DWL_IFACE_COMB_NUM_CHANNELS + DHDCFLAGS += -DCUSTOM_ROAM_TRIGGER_SETTING=-65 + DHDCFLAGS += -DCUSTOM_ROAM_DELTA_SETTING=15 + DHDCFLAGS += -DCUSTOM_KEEP_ALIVE_SETTING=28000 + DHDCFLAGS += -DCUSTOM_PNO_EVENT_LOCK_xTIME=7 + DHDCFLAGS += -DWL_SUPPORT_AUTO_CHANNEL + DHDCFLAGS += -DWL_SUPPORT_BACKPORTED_KPATCHES + DHDCFLAGS += -DESCAN_RESULT_PATCH -DESCAN_BUF_OVERFLOW_MGMT + DHDCFLAGS += -DVSDB -DWL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST + DHDCFLAGS += -DWLTDLS -DMIRACAST_AMPDU_SIZE=8 + DHDCFLAGS += -DWL_VIRTUAL_APSTA endif EXTRA_CFLAGS = $(DHDCFLAGS) ifeq ($(CONFIG_BCMDHD),m) -DHDCFLAGS += -DMULTIPLE_SUPPLICANT + DHDCFLAGS += -DMULTIPLE_SUPPLICANT EXTRA_LDFLAGS += --strip-debug else -DHDCFLAGS += -DBUILD_IN_KERNEL + DHDCFLAGS += -DBUILD_IN_KERNEL endif ARCH ?= arm64 CROSS_COMPILE ?=aarch64-linux-gnu- KDIR ?=../../../../../../common -dhd: - $(MAKE) -C $(KDIR) M=$(PWD) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) modules +all: bcmdhd_sdio bcmdhd_usb + +bcmdhd_sdio: + $(MAKE) -C $(KDIR) M=$(PWD) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) modules CONFIG_BCMDHD_SDIO=y + mv dhd.ko dhd_sdio.ko +bcmdhd_usb: + $(MAKE) -C $(KDIR) M=$(PWD) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) modules CONFIG_BCMDHD_USB=y + mv dhd.ko dhd_usb.ko clean: $(MAKE) -C $(KDIR) M=$(PWD) ARCH=$(ARCH) clean diff --git a/bcmdhd.1.579.77.41.1.cn/bcmsdh_linux.c b/bcmdhd.1.579.77.41.1.cn/bcmsdh_linux.c index 6841c08..772cadd 100644 --- a/bcmdhd.1.579.77.41.1.cn/bcmsdh_linux.c +++ b/bcmdhd.1.579.77.41.1.cn/bcmsdh_linux.c @@ -362,13 +362,13 @@ int bcmsdh_oob_intr_register(bcmsdh_info_t *bcmsdh, bcmsdh_cb_fn_t oob_irq_handl SDLX_MSG(("%s: irq is already registered\n", __FUNCTION__)); return -EBUSY; } - SDLX_MSG(("%s %s irq=%d flags=0x%X\n", __FUNCTION__, #ifdef HW_OOB - "HW_OOB", + printf("%s: HW_OOB irq=%d flags=0x%X\n", __FUNCTION__, + (int)bcmsdh_osinfo->oob_irq_num, (int)bcmsdh_osinfo->oob_irq_flags); #else - "SW_OOB", + printf("%s: SW_OOB irq=%d flags=0x%X\n", __FUNCTION__, + (int)bcmsdh_osinfo->oob_irq_num, (int)bcmsdh_osinfo->oob_irq_flags); #endif - (int)bcmsdh_osinfo->oob_irq_num, (int)bcmsdh_osinfo->oob_irq_flags)); bcmsdh_osinfo->oob_irq_handler = oob_irq_handler; bcmsdh_osinfo->oob_irq_handler_context = oob_irq_handler_context; bcmsdh_osinfo->oob_irq_enabled = TRUE; @@ -399,6 +399,7 @@ int bcmsdh_oob_intr_register(bcmsdh_info_t *bcmsdh, bcmsdh_cb_fn_t oob_irq_handl */ bcmsdh_osinfo->oob_irq_wake_enabled = TRUE; #endif + return 0; } @@ -426,7 +427,7 @@ void bcmsdh_oob_intr_unregister(bcmsdh_info_t *bcmsdh) free_irq(bcmsdh_osinfo->oob_irq_num, bcmsdh); bcmsdh_osinfo->oob_irq_registered = FALSE; } -#endif +#endif /* Module parameters specific to each host-controller driver */ diff --git a/bcmdhd.1.579.77.41.1.cn/bcmsdh_sdmmc.c b/bcmdhd.1.579.77.41.1.cn/bcmsdh_sdmmc.c index 0921a6c..a19a6bf 100644 --- a/bcmdhd.1.579.77.41.1.cn/bcmsdh_sdmmc.c +++ b/bcmdhd.1.579.77.41.1.cn/bcmsdh_sdmmc.c @@ -999,7 +999,6 @@ sdioh_set_mode(sdioh_info_t *sd, uint mode) sd->txglom_mode = mode; else if (mode == SDPCM_TXGLOM_MDESC) sd->txglom_mode = mode; - printf("%s: set txglom_mode to %s\n", __FUNCTION__, mode==SDPCM_TXGLOM_MDESC?"multi-desc":"copy"); return (sd->txglom_mode); } @@ -1292,8 +1291,8 @@ txglomfail: if (sd_msglevel & SDH_COST_VAL) { getnstimeofday(&now); - sd_cost(("%s: rw=%d, cost=%lds %luus\n", __FUNCTION__, - write, now.tv_sec-before.tv_sec, now.tv_nsec/1000-before.tv_nsec/1000)); + sd_cost(("%s: rw=%d, ttl_len=%d, cost=%lds %luus\n", __FUNCTION__, + write, ttl_len, now.tv_sec-before.tv_sec, now.tv_nsec/1000-before.tv_nsec/1000)); } sd_trace(("%s: Exit\n", __FUNCTION__)); diff --git a/bcmdhd.1.579.77.41.1.cn/bcmsdh_sdmmc_linux.c b/bcmdhd.1.579.77.41.1.cn/bcmsdh_sdmmc_linux.c index cdb5af5..dd5d3ba 100644 --- a/bcmdhd.1.579.77.41.1.cn/bcmsdh_sdmmc_linux.c +++ b/bcmdhd.1.579.77.41.1.cn/bcmsdh_sdmmc_linux.c @@ -275,9 +275,7 @@ static int bcmsdh_sdmmc_suspend(struct device *pdev) static int bcmsdh_sdmmc_resume(struct device *pdev) { -#if defined(OOB_INTR_ONLY) sdioh_info_t *sdioh; -#endif struct sdio_func *func = dev_to_sdio_func(pdev); printf("%s Enter func->num=%d\n", __FUNCTION__, func->num); @@ -285,10 +283,8 @@ static int bcmsdh_sdmmc_resume(struct device *pdev) return 0; dhd_mmc_suspend = FALSE; -#if defined(OOB_INTR_ONLY) sdioh = sdio_get_drvdata(func); bcmsdh_resume(sdioh->bcmsdh); -#endif smp_mb(); printf("%s Exit\n", __FUNCTION__); diff --git a/bcmdhd.1.579.77.41.1.cn/dbus.c b/bcmdhd.1.579.77.41.1.cn/dbus.c new file mode 100755 index 0000000..5e3b64c --- /dev/null +++ b/bcmdhd.1.579.77.41.1.cn/dbus.c @@ -0,0 +1,2131 @@ +/** @file dbus.c + * + * Hides details of USB / SDIO / SPI interfaces and OS details. It is intended to shield details and + * provide the caller with one common bus interface for all dongle devices. In practice, it is only + * used for USB interfaces. DBUS is not a protocol, but an abstraction layer. + * + * Copyright (C) 1999-2016, 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: dbus.c 553311 2015-04-29 10:23:08Z $ + */ + + +#include "osl.h" +#include "dbus.h" +#include + +#if defined(BCM_REQUEST_FW) +#include +#include +#include +#include +#include +#include +#include +#endif + + + +#if defined(BCM_REQUEST_FW) +#ifndef VARS_MAX +#define VARS_MAX 8192 +#endif +#endif + +#ifdef DBUS_USB_LOOPBACK +extern bool is_loopback_pkt(void *buf); +extern int matches_loopback_pkt(void *buf); +#endif + +/** General info for all BUS types */ +typedef struct dbus_irbq { + dbus_irb_t *head; + dbus_irb_t *tail; + int cnt; +} dbus_irbq_t; + +/** + * This private structure dbus_info_t is also declared in dbus_usb_linux.c. + * All the fields must be consistent in both declarations. + */ +typedef struct dbus_info { + dbus_pub_t pub; /* MUST BE FIRST */ + + void *cbarg; + dbus_callbacks_t *cbs; /* callbacks to higher level, e.g. dhd_linux.c */ + void *bus_info; + dbus_intf_t *drvintf; /* callbacks to lower level, e.g. dbus_usb.c or dbus_usb_linux.c */ + uint8 *fw; + int fwlen; + uint32 errmask; + int rx_low_watermark; /* avoid rx overflow by filling rx with free IRBs */ + int tx_low_watermark; + bool txoff; + bool txoverride; /* flow control related */ + bool rxoff; + bool tx_timer_ticking; + + + dbus_irbq_t *rx_q; + dbus_irbq_t *tx_q; + + uint8 *nvram; + int nvram_len; + uint8 *image; /* buffer for combine fw and nvram */ + int image_len; + uint8 *orig_fw; + int origfw_len; + int decomp_memsize; + dbus_extdl_t extdl; + int nvram_nontxt; +#if defined(BCM_REQUEST_FW) + void *firmware; + void *nvfile; +#endif +} dbus_info_t; + +struct exec_parms { + union { + /* Can consolidate same params, if need be, but this shows + * group of parameters per function + */ + struct { + dbus_irbq_t *q; + dbus_irb_t *b; + } qenq; + + struct { + dbus_irbq_t *q; + } qdeq; + }; +}; + +#define EXEC_RXLOCK(info, fn, a) \ + info->drvintf->exec_rxlock(dbus_info->bus_info, ((exec_cb_t)fn), ((struct exec_parms *) a)) + +#define EXEC_TXLOCK(info, fn, a) \ + info->drvintf->exec_txlock(dbus_info->bus_info, ((exec_cb_t)fn), ((struct exec_parms *) a)) + +/* + * Callbacks common for all BUS + */ +static void dbus_if_send_irb_timeout(void *handle, dbus_irb_tx_t *txirb); +static void dbus_if_send_irb_complete(void *handle, dbus_irb_tx_t *txirb, int status); +static void dbus_if_recv_irb_complete(void *handle, dbus_irb_rx_t *rxirb, int status); +static void dbus_if_errhandler(void *handle, int err); +static void dbus_if_ctl_complete(void *handle, int type, int status); +static void dbus_if_state_change(void *handle, int state); +static void *dbus_if_pktget(void *handle, uint len, bool send); +static void dbus_if_pktfree(void *handle, void *p, bool send); +static struct dbus_irb *dbus_if_getirb(void *cbarg, bool send); +static void dbus_if_rxerr_indicate(void *handle, bool on); + +/** functions in this file that are called by lower DBUS levels, e.g. dbus_usb.c */ +static dbus_intf_callbacks_t dbus_intf_cbs = { + dbus_if_send_irb_timeout, + dbus_if_send_irb_complete, + dbus_if_recv_irb_complete, + dbus_if_errhandler, + dbus_if_ctl_complete, + dbus_if_state_change, + NULL, /* isr */ + NULL, /* dpc */ + NULL, /* watchdog */ + dbus_if_pktget, + dbus_if_pktfree, + dbus_if_getirb, + dbus_if_rxerr_indicate +}; + +/* + * Need global for probe() and disconnect() since + * attach() is not called at probe and detach() + * can be called inside disconnect() + */ +static dbus_intf_t *g_busintf = NULL; +static probe_cb_t probe_cb = NULL; +static disconnect_cb_t disconnect_cb = NULL; +static void *probe_arg = NULL; +static void *disc_arg = NULL; + +#if defined(BCM_REQUEST_FW) +int8 *nonfwnvram = NULL; /* stand-alone multi-nvram given with driver load */ +int nonfwnvramlen = 0; +#endif /* #if defined(BCM_REQUEST_FW) */ + +static void* q_enq(dbus_irbq_t *q, dbus_irb_t *b); +static void* q_enq_exec(struct exec_parms *args); +static dbus_irb_t*q_deq(dbus_irbq_t *q); +static void* q_deq_exec(struct exec_parms *args); +static int dbus_tx_timer_init(dbus_info_t *dbus_info); +static int dbus_tx_timer_start(dbus_info_t *dbus_info, uint timeout); +static int dbus_tx_timer_stop(dbus_info_t *dbus_info); +static int dbus_irbq_init(dbus_info_t *dbus_info, dbus_irbq_t *q, int nq, int size_irb); +static int dbus_irbq_deinit(dbus_info_t *dbus_info, dbus_irbq_t *q, int size_irb); +static int dbus_rxirbs_fill(dbus_info_t *dbus_info); +static int dbus_send_irb(dbus_pub_t *pub, uint8 *buf, int len, void *pkt, void *info); +static void dbus_disconnect(void *handle); +static void *dbus_probe(void *arg, const char *desc, uint32 bustype, uint32 hdrlen); + +#if defined(BCM_REQUEST_FW) +#if defined(BCM_REQUEST_FW) +extern char * dngl_firmware; +extern unsigned int dngl_fwlen; +#endif /* #if defined(BCM_REQUEST_FW) */ +static int dbus_get_nvram(dbus_info_t *dbus_info); +static int dbus_jumbo_nvram(dbus_info_t *dbus_info); +static int dbus_otp(dbus_info_t *dbus_info, uint16 *boardtype, uint16 *boardrev); +static int dbus_select_nvram(dbus_info_t *dbus_info, int8 *jumbonvram, int jumbolen, +uint16 boardtype, uint16 boardrev, int8 **nvram, int *nvram_len); +#ifndef BCM_REQUEST_FW +static int dbus_zlib_decomp(dbus_info_t *dbus_info); +extern void *dbus_zlib_calloc(int num, int size); +extern void dbus_zlib_free(void *ptr); +#endif +#endif + +/* function */ +void +dbus_flowctrl_tx(void *dbi, bool on) +{ + dbus_info_t *dbus_info = dbi; + + if (dbus_info == NULL) + return; + + DBUSTRACE(("%s on %d\n", __FUNCTION__, on)); + + if (dbus_info->txoff == on) + return; + + dbus_info->txoff = on; + + if (dbus_info->cbs && dbus_info->cbs->txflowcontrol) + dbus_info->cbs->txflowcontrol(dbus_info->cbarg, on); +} + +/** + * if lower level DBUS signaled a rx error, more free rx IRBs should be allocated or flow control + * should kick in to make more free rx IRBs available. + */ +static void +dbus_if_rxerr_indicate(void *handle, bool on) +{ + dbus_info_t *dbus_info = (dbus_info_t *) handle; + + DBUSTRACE(("%s, on %d\n", __FUNCTION__, on)); + + if (dbus_info == NULL) + return; + + if (dbus_info->txoverride == on) + return; + + dbus_info->txoverride = on; /* flow control */ + + if (!on) + dbus_rxirbs_fill(dbus_info); + +} + +/** q_enq()/q_deq() are executed with protection via exec_rxlock()/exec_txlock() */ +static void* +q_enq(dbus_irbq_t *q, dbus_irb_t *b) +{ + ASSERT(q->tail != b); + ASSERT(b->next == NULL); + b->next = NULL; + if (q->tail) { + q->tail->next = b; + q->tail = b; + } else + q->head = q->tail = b; + + q->cnt++; + + return b; +} + +static void* +q_enq_exec(struct exec_parms *args) +{ + return q_enq(args->qenq.q, args->qenq.b); +} + +static dbus_irb_t* +q_deq(dbus_irbq_t *q) +{ + dbus_irb_t *b; + + b = q->head; + if (b) { + q->head = q->head->next; + b->next = NULL; + + if (q->head == NULL) + q->tail = q->head; + + q->cnt--; + } + return b; +} + +static void* +q_deq_exec(struct exec_parms *args) +{ + return q_deq(args->qdeq.q); +} + +/** + * called during attach phase. Status @ Dec 2012: this function does nothing since for all of the + * lower DBUS levels dbus_info->drvintf->tx_timer_init is NULL. + */ +static int +dbus_tx_timer_init(dbus_info_t *dbus_info) +{ + if (dbus_info && dbus_info->drvintf && dbus_info->drvintf->tx_timer_init) + return dbus_info->drvintf->tx_timer_init(dbus_info->bus_info); + else + return DBUS_ERR; +} + +static int +dbus_tx_timer_start(dbus_info_t *dbus_info, uint timeout) +{ + if (dbus_info == NULL) + return DBUS_ERR; + + if (dbus_info->tx_timer_ticking) + return DBUS_OK; + + if (dbus_info->drvintf && dbus_info->drvintf->tx_timer_start) { + if (dbus_info->drvintf->tx_timer_start(dbus_info->bus_info, timeout) == DBUS_OK) { + dbus_info->tx_timer_ticking = TRUE; + return DBUS_OK; + } + } + + return DBUS_ERR; +} + +static int +dbus_tx_timer_stop(dbus_info_t *dbus_info) +{ + if (dbus_info == NULL) + return DBUS_ERR; + + if (!dbus_info->tx_timer_ticking) + return DBUS_OK; + + if (dbus_info->drvintf && dbus_info->drvintf->tx_timer_stop) { + if (dbus_info->drvintf->tx_timer_stop(dbus_info->bus_info) == DBUS_OK) { + dbus_info->tx_timer_ticking = FALSE; + return DBUS_OK; + } + } + + return DBUS_ERR; +} + +/** called during attach phase. */ +static int +dbus_irbq_init(dbus_info_t *dbus_info, dbus_irbq_t *q, int nq, int size_irb) +{ + int i; + dbus_irb_t *irb; + + ASSERT(q); + ASSERT(dbus_info); + + for (i = 0; i < nq; i++) { + /* MALLOC dbus_irb_tx or dbus_irb_rx, but cast to simple dbus_irb_t linkedlist */ + irb = (dbus_irb_t *) MALLOC(dbus_info->pub.osh, size_irb); + if (irb == NULL) { + ASSERT(irb); + return DBUS_ERR; + } + bzero(irb, size_irb); + + /* q_enq() does not need to go through EXEC_xxLOCK() during init() */ + q_enq(q, irb); + } + + return DBUS_OK; +} + +/** called during detach phase or when attach failed */ +static int +dbus_irbq_deinit(dbus_info_t *dbus_info, dbus_irbq_t *q, int size_irb) +{ + dbus_irb_t *irb; + + ASSERT(q); + ASSERT(dbus_info); + + /* q_deq() does not need to go through EXEC_xxLOCK() + * during deinit(); all callbacks are stopped by this time + */ + while ((irb = q_deq(q)) != NULL) { + MFREE(dbus_info->pub.osh, irb, size_irb); + } + + if (q->cnt) + DBUSERR(("deinit: q->cnt=%d > 0\n", q->cnt)); + return DBUS_OK; +} + +/** multiple code paths require the rx queue to be filled with more free IRBs */ +static int +dbus_rxirbs_fill(dbus_info_t *dbus_info) +{ + int err = DBUS_OK; + + + dbus_irb_rx_t *rxirb; + struct exec_parms args; + + ASSERT(dbus_info); + if (dbus_info->pub.busstate != DBUS_STATE_UP) { + DBUSERR(("dbus_rxirbs_fill: DBUS not up \n")); + return DBUS_ERR; + } else if (!dbus_info->drvintf || (dbus_info->drvintf->recv_irb == NULL)) { + /* Lower edge bus interface does not support recv_irb(). + * No need to pre-submit IRBs in this case. + */ + return DBUS_ERR; + } + + /* The dongle recv callback is freerunning without lock. So multiple callbacks(and this + * refill) can run in parallel. While the rxoff condition is triggered outside, + * below while loop has to check and abort posting more to avoid RPC rxq overflow. + */ + args.qdeq.q = dbus_info->rx_q; + while ((!dbus_info->rxoff) && + (rxirb = (EXEC_RXLOCK(dbus_info, q_deq_exec, &args))) != NULL) { + err = dbus_info->drvintf->recv_irb(dbus_info->bus_info, rxirb); + if (err == DBUS_ERR_RXDROP || err == DBUS_ERR_RXFAIL) { + /* Add the the free rxirb back to the queue + * and wait till later + */ + bzero(rxirb, sizeof(dbus_irb_rx_t)); + args.qenq.q = dbus_info->rx_q; + args.qenq.b = (dbus_irb_t *) rxirb; + EXEC_RXLOCK(dbus_info, q_enq_exec, &args); + break; + } else if (err != DBUS_OK) { + int i = 0; + while (i++ < 100) { + DBUSERR(("%s :: memory leak for rxirb note?\n", __FUNCTION__)); + } + } + } + return err; +} /* dbus_rxirbs_fill */ + +/** called when the DBUS interface state changed. */ +void +dbus_flowctrl_rx(dbus_pub_t *pub, bool on) +{ + dbus_info_t *dbus_info = (dbus_info_t *) pub; + + if (dbus_info == NULL) + return; + + DBUSTRACE(("%s\n", __FUNCTION__)); + + if (dbus_info->rxoff == on) + return; + + dbus_info->rxoff = on; + + if (dbus_info->pub.busstate == DBUS_STATE_UP) { + if (!on) { + /* post more irbs, resume rx if necessary */ + dbus_rxirbs_fill(dbus_info); + if (dbus_info && dbus_info->drvintf->recv_resume) { + dbus_info->drvintf->recv_resume(dbus_info->bus_info); + } + } else { + /* ??? cancell posted irbs first */ + + if (dbus_info && dbus_info->drvintf->recv_stop) { + dbus_info->drvintf->recv_stop(dbus_info->bus_info); + } + } + } +} + +/** + * Several code paths in this file want to send a buffer to the dongle. This function handles both + * sending of a buffer or a pkt. + */ +static int +dbus_send_irb(dbus_pub_t *pub, uint8 *buf, int len, void *pkt, void *info) +{ + dbus_info_t *dbus_info = (dbus_info_t *) pub; + int err = DBUS_OK; + dbus_irb_tx_t *txirb = NULL; + int txirb_pending; + struct exec_parms args; + + if (dbus_info == NULL) + return DBUS_ERR; + + DBUSTRACE(("%s\n", __FUNCTION__)); + + if (dbus_info->pub.busstate == DBUS_STATE_UP || + dbus_info->pub.busstate == DBUS_STATE_SLEEP) { + args.qdeq.q = dbus_info->tx_q; + if (dbus_info->drvintf) + txirb = EXEC_TXLOCK(dbus_info, q_deq_exec, &args); + + if (txirb == NULL) { + DBUSERR(("Out of tx dbus_bufs\n")); + return DBUS_ERR; + } + + if (pkt != NULL) { + txirb->pkt = pkt; + txirb->buf = NULL; + txirb->len = 0; + } else if (buf != NULL) { + txirb->pkt = NULL; + txirb->buf = buf; + txirb->len = len; + } else { + ASSERT(0); /* Should not happen */ + } + txirb->info = info; + txirb->arg = NULL; + txirb->retry_count = 0; + + if (dbus_info->drvintf && dbus_info->drvintf->send_irb) { + /* call lower DBUS level send_irb function */ + err = dbus_info->drvintf->send_irb(dbus_info->bus_info, txirb); + if (err == DBUS_ERR_TXDROP) { + /* tx fail and no completion routine to clean up, reclaim irb NOW */ + DBUSERR(("%s: send_irb failed, status = %d\n", __FUNCTION__, err)); + bzero(txirb, sizeof(dbus_irb_tx_t)); + args.qenq.q = dbus_info->tx_q; + args.qenq.b = (dbus_irb_t *) txirb; + EXEC_TXLOCK(dbus_info, q_enq_exec, &args); + } else { + dbus_tx_timer_start(dbus_info, DBUS_TX_TIMEOUT_INTERVAL); + txirb_pending = dbus_info->pub.ntxq - dbus_info->tx_q->cnt; + if (txirb_pending > (dbus_info->tx_low_watermark * 3)) { + dbus_flowctrl_tx(dbus_info, TRUE); + } + } + } + } else { + err = DBUS_ERR_TXFAIL; + DBUSTRACE(("%s: bus down, send_irb failed\n", __FUNCTION__)); + } + + return err; +} /* dbus_send_irb */ + +#if defined(BCM_REQUEST_FW) + +/** + * Before downloading a firmware image into the dongle, the validity of the image must be checked. + */ +static int +check_file(osl_t *osh, unsigned char *headers) +{ + struct trx_header *trx; + int actual_len = -1; + + /* Extract trx header */ + trx = (struct trx_header *)headers; + if (ltoh32(trx->magic) != TRX_MAGIC) { + printf("Error: trx bad hdr %x\n", ltoh32(trx->magic)); + return -1; + } + + headers += SIZEOF_TRX(trx); + + /* TRX V1: get firmware len */ + /* TRX V2: get firmware len and DSG/CFG lengths */ + if (ltoh32(trx->flag_version) & TRX_UNCOMP_IMAGE) { + actual_len = ltoh32(trx->offsets[TRX_OFFSETS_DLFWLEN_IDX]) + + SIZEOF_TRX(trx); +#ifdef BCMTRXV2 + if (ISTRX_V2(trx)) { + actual_len += ltoh32(trx->offsets[TRX_OFFSETS_DSG_LEN_IDX]) + + ltoh32(trx->offsets[TRX_OFFSETS_CFG_LEN_IDX]); + } +#endif + return actual_len; + } else { + printf("compressed image\n"); + } + + return -1; +} + +/** + * It is easy for the user to pass one jumbo nvram file to the driver than a set of smaller files. + * The 'jumbo nvram' file format is essentially a set of nvram files. Before commencing firmware + * download, the dongle needs to be probed so that the correct nvram contents within the jumbo nvram + * file is selected. + */ +static int +dbus_jumbo_nvram(dbus_info_t *dbus_info) +{ + int8 *nvram = NULL; + int nvram_len = 0; + int ret = DBUS_OK; + uint16 boardrev = 0xFFFF; + uint16 boardtype = 0xFFFF; + + /* read the otp for boardrev & boardtype + * if boardtype/rev are present in otp + * select nvram data for that boardtype/rev + */ + dbus_otp(dbus_info, &boardtype, &boardrev); + + ret = dbus_select_nvram(dbus_info, dbus_info->extdl.vars, dbus_info->extdl.varslen, + boardtype, boardrev, &nvram, &nvram_len); + + if (ret == DBUS_JUMBO_BAD_FORMAT) + return DBUS_ERR_NVRAM; + else if (ret == DBUS_JUMBO_NOMATCH && + (boardtype != 0xFFFF || boardrev != 0xFFFF)) { + DBUSERR(("No matching NVRAM for boardtype 0x%02x boardrev 0x%02x\n", + boardtype, boardrev)); + return DBUS_ERR_NVRAM; + } + dbus_info->nvram = nvram; + dbus_info->nvram_len = nvram_len; + + return DBUS_OK; +} + +/** before commencing fw download, the correct NVRAM image to download has to be picked */ +static int +dbus_get_nvram(dbus_info_t *dbus_info) +{ + int len, i; + struct trx_header *hdr; + int actual_fwlen; + uint32 img_offset = 0; + + dbus_info->nvram_len = 0; + if (dbus_info->extdl.varslen) { + if (DBUS_OK != dbus_jumbo_nvram(dbus_info)) + return DBUS_ERR_NVRAM; + DBUSERR(("NVRAM %d bytes downloaded\n", dbus_info->nvram_len)); + } +#if defined(BCM_REQUEST_FW) + else if (nonfwnvram) { + dbus_info->nvram = nonfwnvram; + dbus_info->nvram_len = nonfwnvramlen; + DBUSERR(("NVRAM %d bytes downloaded\n", dbus_info->nvram_len)); + } +#endif + if (dbus_info->nvram) { + uint8 nvram_words_pad = 0; + /* Validate the format/length etc of the file */ + if ((actual_fwlen = check_file(dbus_info->pub.osh, dbus_info->fw)) <= 0) { + DBUSERR(("%s: bad firmware format!\n", __FUNCTION__)); + return DBUS_ERR_NVRAM; + } + + if (!dbus_info->nvram_nontxt) { + /* host supplied nvram could be in .txt format + * with all the comments etc... + */ + dbus_info->nvram_len = process_nvram_vars(dbus_info->nvram, + dbus_info->nvram_len); + } + if (dbus_info->nvram_len % 4) + nvram_words_pad = 4 - dbus_info->nvram_len % 4; + + len = actual_fwlen + dbus_info->nvram_len + nvram_words_pad; + dbus_info->image = MALLOC(dbus_info->pub.osh, len); + dbus_info->image_len = len; + if (dbus_info->image == NULL) { + DBUSERR(("%s: malloc failed!\n", __FUNCTION__)); + return DBUS_ERR_NVRAM; + } + hdr = (struct trx_header *)dbus_info->fw; + /* Step1: Copy trx header + firmwre */ + img_offset = SIZEOF_TRX(hdr) + hdr->offsets[TRX_OFFSETS_DLFWLEN_IDX]; + bcopy(dbus_info->fw, dbus_info->image, img_offset); + /* Step2: Copy NVRAM + pad */ + bcopy(dbus_info->nvram, (uint8 *)(dbus_info->image + img_offset), + dbus_info->nvram_len); + img_offset += dbus_info->nvram_len; + if (nvram_words_pad) { + bzero(&dbus_info->image[img_offset], + nvram_words_pad); + img_offset += nvram_words_pad; + } +#ifdef BCMTRXV2 + /* Step3: Copy DSG/CFG for V2 */ + if (ISTRX_V2(hdr) && + (hdr->offsets[TRX_OFFSETS_DSG_LEN_IDX] || + hdr->offsets[TRX_OFFSETS_CFG_LEN_IDX])) { + + bcopy(dbus_info->fw + SIZEOF_TRX(hdr) + + hdr->offsets[TRX_OFFSETS_DLFWLEN_IDX] + + hdr->offsets[TRX_OFFSETS_NVM_LEN_IDX], + dbus_info->image + img_offset, + hdr->offsets[TRX_OFFSETS_DSG_LEN_IDX] + + hdr->offsets[TRX_OFFSETS_CFG_LEN_IDX]); + + img_offset += hdr->offsets[TRX_OFFSETS_DSG_LEN_IDX] + + hdr->offsets[TRX_OFFSETS_CFG_LEN_IDX]; + } +#endif /* BCMTRXV2 */ + /* Step4: update TRX header for nvram size */ + hdr = (struct trx_header *)dbus_info->image; + hdr->len = htol32(len); + /* Pass the actual fw len */ + hdr->offsets[TRX_OFFSETS_NVM_LEN_IDX] = + htol32(dbus_info->nvram_len + nvram_words_pad); + /* Calculate CRC over header */ + hdr->crc32 = hndcrc32((uint8 *)&hdr->flag_version, + SIZEOF_TRX(hdr) - OFFSETOF(struct trx_header, flag_version), + CRC32_INIT_VALUE); + + /* Calculate CRC over data */ + for (i = SIZEOF_TRX(hdr); i < len; ++i) + hdr->crc32 = hndcrc32((uint8 *)&dbus_info->image[i], 1, hdr->crc32); + hdr->crc32 = htol32(hdr->crc32); + } else { + dbus_info->image = dbus_info->fw; + dbus_info->image_len = (uint32)dbus_info->fwlen; + } + + return DBUS_OK; +} /* dbus_get_nvram */ + +/** + * during driver initialization ('attach') or after PnP 'resume', firmware needs to be loaded into + * the dongle + */ +static int +dbus_do_download(dbus_info_t *dbus_info) +{ + int err = DBUS_OK; +#ifndef BCM_REQUEST_FW + int decomp_override = 0; +#endif +#ifdef BCM_REQUEST_FW + uint16 boardrev = 0xFFFF, boardtype = 0xFFFF; + int8 *temp_nvram; + int temp_len; +#endif + +#if defined(BCM_REQUEST_FW) + dbus_info->firmware = dbus_get_fw_nvfile(dbus_info->pub.attrib.devid, + dbus_info->pub.attrib.chiprev, &dbus_info->fw, &dbus_info->fwlen, + DBUS_FIRMWARE, 0, 0); + if (!dbus_info->firmware) + return DBUS_ERR; +#endif + + dbus_info->image = dbus_info->fw; + dbus_info->image_len = (uint32)dbus_info->fwlen; + +#ifndef BCM_REQUEST_FW + if (UNZIP_ENAB(dbus_info) && !decomp_override) { + err = dbus_zlib_decomp(dbus_info); + if (err) { + DBUSERR(("dbus_attach: fw decompress fail %d\n", err)); + return err; + } + } +#endif + +#if defined(BCM_REQUEST_FW) + /* check if firmware is appended with nvram file */ + err = dbus_otp(dbus_info, &boardtype, &boardrev); + /* check if nvram is provided as separte file */ + nonfwnvram = NULL; + nonfwnvramlen = 0; + dbus_info->nvfile = dbus_get_fw_nvfile(dbus_info->pub.attrib.devid, + dbus_info->pub.attrib.chiprev, (void *)&temp_nvram, &temp_len, + DBUS_NVFILE, boardtype, boardrev); + if (dbus_info->nvfile) { + int8 *tmp = MALLOC(dbus_info->pub.osh, temp_len); + if (tmp) { + bcopy(temp_nvram, tmp, temp_len); + nonfwnvram = tmp; + nonfwnvramlen = temp_len; + } else { + err = DBUS_ERR; + goto fail; + } + } +#endif /* defined(BCM_REQUEST_FW) */ + + err = dbus_get_nvram(dbus_info); + if (err) { + DBUSERR(("dbus_do_download: fail to get nvram %d\n", err)); + return err; + } + + + if (dbus_info->drvintf->dlstart && dbus_info->drvintf->dlrun) { + err = dbus_info->drvintf->dlstart(dbus_info->bus_info, + dbus_info->image, dbus_info->image_len); + + if (err == DBUS_OK) + err = dbus_info->drvintf->dlrun(dbus_info->bus_info); + } else + err = DBUS_ERR; + + if (dbus_info->nvram) { + MFREE(dbus_info->pub.osh, dbus_info->image, dbus_info->image_len); + dbus_info->image = dbus_info->fw; + dbus_info->image_len = (uint32)dbus_info->fwlen; + } + +#ifndef BCM_REQUEST_FW + if (UNZIP_ENAB(dbus_info) && (!decomp_override) && dbus_info->orig_fw) { + MFREE(dbus_info->pub.osh, dbus_info->fw, dbus_info->decomp_memsize); + dbus_info->image = dbus_info->fw = dbus_info->orig_fw; + dbus_info->image_len = dbus_info->fwlen = dbus_info->origfw_len; + } +#endif + +#if defined(BCM_REQUEST_FW) +fail: + if (dbus_info->firmware) { + dbus_release_fw_nvfile(dbus_info->firmware); + dbus_info->firmware = NULL; + } + if (dbus_info->nvfile) { + dbus_release_fw_nvfile(dbus_info->nvfile); + dbus_info->nvfile = NULL; + } + if (nonfwnvram) { + MFREE(dbus_info->pub.osh, nonfwnvram, nonfwnvramlen); + nonfwnvram = NULL; + nonfwnvramlen = 0; + } +#endif + return err; +} /* dbus_do_download */ + +#endif + +/** required for DBUS deregistration */ +static void +dbus_disconnect(void *handle) +{ + DBUSTRACE(("%s\n", __FUNCTION__)); + + if (disconnect_cb) + disconnect_cb(disc_arg); +} + +/** + * This function is called when the sent irb times out without a tx response status. + * DBUS adds reliability by resending timed out IRBs DBUS_TX_RETRY_LIMIT times. + */ +static void +dbus_if_send_irb_timeout(void *handle, dbus_irb_tx_t *txirb) +{ + dbus_info_t *dbus_info = (dbus_info_t *) handle; + + if ((dbus_info == NULL) || (dbus_info->drvintf == NULL) || (txirb == NULL)) { + return; + } + + DBUSTRACE(("%s\n", __FUNCTION__)); + + return; + +} /* dbus_if_send_irb_timeout */ + +/** + * When lower DBUS level signals that a send IRB completed, either successful or not, the higher + * level (e.g. dhd_linux.c) has to be notified, and transmit flow control has to be evaluated. + */ +static void BCMFASTPATH +dbus_if_send_irb_complete(void *handle, dbus_irb_tx_t *txirb, int status) +{ + dbus_info_t *dbus_info = (dbus_info_t *) handle; + int txirb_pending; + struct exec_parms args; + void *pktinfo; + + if ((dbus_info == NULL) || (txirb == NULL)) { + return; + } + + DBUSTRACE(("%s: status = %d\n", __FUNCTION__, status)); + + dbus_tx_timer_stop(dbus_info); + + /* re-queue BEFORE calling send_complete which will assume that this irb + is now available. + */ + pktinfo = txirb->info; + bzero(txirb, sizeof(dbus_irb_tx_t)); + args.qenq.q = dbus_info->tx_q; + args.qenq.b = (dbus_irb_t *) txirb; + EXEC_TXLOCK(dbus_info, q_enq_exec, &args); + + if (dbus_info->pub.busstate != DBUS_STATE_DOWN) { + if ((status == DBUS_OK) || (status == DBUS_ERR_NODEVICE)) { + if (dbus_info->cbs && dbus_info->cbs->send_complete) + dbus_info->cbs->send_complete(dbus_info->cbarg, pktinfo, + status); + + if (status == DBUS_OK) { + txirb_pending = dbus_info->pub.ntxq - dbus_info->tx_q->cnt; + if (txirb_pending) + dbus_tx_timer_start(dbus_info, DBUS_TX_TIMEOUT_INTERVAL); + if ((txirb_pending < dbus_info->tx_low_watermark) && + dbus_info->txoff && !dbus_info->txoverride) { + dbus_flowctrl_tx(dbus_info, OFF); + } + } + } else { + DBUSERR(("%s: %d WARNING freeing orphan pkt %p\n", __FUNCTION__, __LINE__, + pktinfo)); +#if defined(BCM_RPC_NOCOPY) || defined(BCM_RPC_TXNOCOPY) || defined(BCM_RPC_TOC) + if (pktinfo) + if (dbus_info->cbs && dbus_info->cbs->send_complete) + dbus_info->cbs->send_complete(dbus_info->cbarg, pktinfo, + status); +#else + dbus_if_pktfree(dbus_info, (void*)pktinfo, TRUE); +#endif /* defined(BCM_RPC_NOCOPY) || defined(BCM_RPC_TXNOCOPY) || defined(BCM_RPC_TOC) */ + } + } else { + DBUSERR(("%s: %d WARNING freeing orphan pkt %p\n", __FUNCTION__, __LINE__, + pktinfo)); +#if defined(BCM_RPC_NOCOPY) || defined(BCM_RPC_TXNOCOPY) || defined(BCM_RPC_TOC) + if (pktinfo) + if (dbus_info->cbs && dbus_info->cbs->send_complete) + dbus_info->cbs->send_complete(dbus_info->cbarg, pktinfo, + status); +#else + dbus_if_pktfree(dbus_info, (void*)pktinfo, TRUE); +#endif /* defined(BCM_RPC_NOCOPY) || defined(BCM_RPC_TXNOCOPY) defined(BCM_RPC_TOC) */ + } +} /* dbus_if_send_irb_complete */ + +/** + * When lower DBUS level signals that a receive IRB completed, either successful or not, the higher + * level (e.g. dhd_linux.c) has to be notified, and fresh free receive IRBs may have to be given + * to lower levels. + */ +static void BCMFASTPATH +dbus_if_recv_irb_complete(void *handle, dbus_irb_rx_t *rxirb, int status) +{ + dbus_info_t *dbus_info = (dbus_info_t *) handle; + int rxirb_pending; + struct exec_parms args; + + if ((dbus_info == NULL) || (rxirb == NULL)) { + return; + } + DBUSTRACE(("%s\n", __FUNCTION__)); + if (dbus_info->pub.busstate != DBUS_STATE_DOWN && + dbus_info->pub.busstate != DBUS_STATE_SLEEP) { + if (status == DBUS_OK) { + if ((rxirb->buf != NULL) && (rxirb->actual_len > 0)) { +#ifdef DBUS_USB_LOOPBACK + if (is_loopback_pkt(rxirb->buf)) { + matches_loopback_pkt(rxirb->buf); + } else +#endif + if (dbus_info->cbs && dbus_info->cbs->recv_buf) { + dbus_info->cbs->recv_buf(dbus_info->cbarg, rxirb->buf, + rxirb->actual_len); + } + } else if (rxirb->pkt != NULL) { + if (dbus_info->cbs && dbus_info->cbs->recv_pkt) + dbus_info->cbs->recv_pkt(dbus_info->cbarg, rxirb->pkt); + } else { + ASSERT(0); /* Should not happen */ + } + + rxirb_pending = dbus_info->pub.nrxq - dbus_info->rx_q->cnt - 1; + if ((rxirb_pending <= dbus_info->rx_low_watermark) && + !dbus_info->rxoff) { + DBUSTRACE(("Low watermark so submit more %d <= %d \n", + dbus_info->rx_low_watermark, rxirb_pending)); + dbus_rxirbs_fill(dbus_info); + } else if (dbus_info->rxoff) + DBUSTRACE(("rx flow controlled. not filling more. cut_rxq=%d\n", + dbus_info->rx_q->cnt)); + } else if (status == DBUS_ERR_NODEVICE) { + DBUSERR(("%s: %d status = %d, buf %p\n", __FUNCTION__, __LINE__, status, + rxirb->buf)); +#if defined(BCM_RPC_NOCOPY) || defined(BCM_RPC_RXNOCOPY) + if (rxirb->buf) { + PKTFRMNATIVE(dbus_info->pub.osh, rxirb->buf); + PKTFREE(dbus_info->pub.osh, rxirb->buf, FALSE); + } +#endif /* BCM_RPC_NOCOPY || BCM_RPC_TXNOCOPY || BCM_RPC_TOC */ + } else { + if (status != DBUS_ERR_RXZLP) + DBUSERR(("%s: %d status = %d, buf %p\n", __FUNCTION__, __LINE__, + status, rxirb->buf)); +#if defined(BCM_RPC_NOCOPY) || defined(BCM_RPC_RXNOCOPY) + if (rxirb->buf) { + PKTFRMNATIVE(dbus_info->pub.osh, rxirb->buf); + PKTFREE(dbus_info->pub.osh, rxirb->buf, FALSE); + } +#endif /* BCM_RPC_NOCOPY || BCM_RPC_TXNOCOPY || BCM_RPC_TOC */ + } + } else { + DBUSTRACE(("%s: DBUS down, ignoring recv callback. buf %p\n", __FUNCTION__, + rxirb->buf)); +#if defined(BCM_RPC_NOCOPY) || defined(BCM_RPC_RXNOCOPY) + if (rxirb->buf) { + PKTFRMNATIVE(dbus_info->pub.osh, rxirb->buf); + PKTFREE(dbus_info->pub.osh, rxirb->buf, FALSE); + } +#endif /* BCM_RPC_NOCOPY || BCM_RPC_TXNOCOPY || BCM_RPC_TOC */ + } + if (dbus_info->rx_q != NULL) { + bzero(rxirb, sizeof(dbus_irb_rx_t)); + args.qenq.q = dbus_info->rx_q; + args.qenq.b = (dbus_irb_t *) rxirb; + EXEC_RXLOCK(dbus_info, q_enq_exec, &args); + } else + MFREE(dbus_info->pub.osh, rxirb, sizeof(dbus_irb_tx_t)); +} /* dbus_if_recv_irb_complete */ + +/** + * Accumulate errors signaled by lower DBUS levels and signal them to higher (e.g. dhd_linux.c) + * level. + */ +static void +dbus_if_errhandler(void *handle, int err) +{ + dbus_info_t *dbus_info = handle; + uint32 mask = 0; + + if (dbus_info == NULL) + return; + + switch (err) { + case DBUS_ERR_TXFAIL: + dbus_info->pub.stats.tx_errors++; + mask |= ERR_CBMASK_TXFAIL; + break; + case DBUS_ERR_TXDROP: + dbus_info->pub.stats.tx_dropped++; + mask |= ERR_CBMASK_TXFAIL; + break; + case DBUS_ERR_RXFAIL: + dbus_info->pub.stats.rx_errors++; + mask |= ERR_CBMASK_RXFAIL; + break; + case DBUS_ERR_RXDROP: + dbus_info->pub.stats.rx_dropped++; + mask |= ERR_CBMASK_RXFAIL; + break; + default: + break; + } + + if (dbus_info->cbs && dbus_info->cbs->errhandler && (dbus_info->errmask & mask)) + dbus_info->cbs->errhandler(dbus_info->cbarg, err); +} + +/** + * When lower DBUS level signals control IRB completed, higher level (e.g. dhd_linux.c) has to be + * notified. + */ +static void +dbus_if_ctl_complete(void *handle, int type, int status) +{ + dbus_info_t *dbus_info = (dbus_info_t *) handle; + + DBUSTRACE(("%s\n", __FUNCTION__)); + + if (dbus_info == NULL) { + DBUSERR(("%s: dbus_info is NULL\n", __FUNCTION__)); + return; + } + + if (dbus_info->pub.busstate != DBUS_STATE_DOWN) { + if (dbus_info->cbs && dbus_info->cbs->ctl_complete) + dbus_info->cbs->ctl_complete(dbus_info->cbarg, type, status); + } +} + +/** + * Rx related functionality (flow control, posting of free IRBs to rx queue) is dependent upon the + * bus state. When lower DBUS level signals a change in the interface state, take appropriate action + * and forward the signaling to the higher (e.g. dhd_linux.c) level. + */ +static void +dbus_if_state_change(void *handle, int state) +{ + dbus_info_t *dbus_info = (dbus_info_t *) handle; + int old_state; + + if (dbus_info == NULL) + return; + + if (dbus_info->pub.busstate == state) + return; + old_state = dbus_info->pub.busstate; + if (state == DBUS_STATE_DISCONNECT) { + DBUSERR(("DBUS disconnected\n")); + } + + /* Ignore USB SUSPEND while not up yet */ + if (state == DBUS_STATE_SLEEP && old_state != DBUS_STATE_UP) + return; + + DBUSTRACE(("dbus state change from %d to to %d\n", old_state, state)); + + /* Don't update state if it's PnP firmware re-download */ + if (state != DBUS_STATE_PNP_FWDL) + dbus_info->pub.busstate = state; + else + dbus_flowctrl_rx(handle, FALSE); + if (state == DBUS_STATE_SLEEP) + dbus_flowctrl_rx(handle, TRUE); + if (state == DBUS_STATE_UP) { + dbus_rxirbs_fill(dbus_info); + dbus_flowctrl_rx(handle, FALSE); + } + + if (dbus_info->cbs && dbus_info->cbs->state_change) + dbus_info->cbs->state_change(dbus_info->cbarg, state); +} + +/** Forward request for packet from lower DBUS layer to higher layer (e.g. dhd_linux.c) */ +static void * +dbus_if_pktget(void *handle, uint len, bool send) +{ + dbus_info_t *dbus_info = (dbus_info_t *) handle; + void *p = NULL; + + if (dbus_info == NULL) + return NULL; + + if (dbus_info->cbs && dbus_info->cbs->pktget) + p = dbus_info->cbs->pktget(dbus_info->cbarg, len, send); + else + ASSERT(0); + + return p; +} + +/** Forward request to free packet from lower DBUS layer to higher layer (e.g. dhd_linux.c) */ +static void +dbus_if_pktfree(void *handle, void *p, bool send) +{ + dbus_info_t *dbus_info = (dbus_info_t *) handle; + + if (dbus_info == NULL) + return; + + if (dbus_info->cbs && dbus_info->cbs->pktfree) + dbus_info->cbs->pktfree(dbus_info->cbarg, p, send); + else + ASSERT(0); +} + +/** Lower DBUS level requests either a send or receive IRB */ +static struct dbus_irb* +dbus_if_getirb(void *cbarg, bool send) +{ + dbus_info_t *dbus_info = (dbus_info_t *) cbarg; + struct exec_parms args; + struct dbus_irb *irb; + + if ((dbus_info == NULL) || (dbus_info->pub.busstate != DBUS_STATE_UP)) + return NULL; + + if (send == TRUE) { + args.qdeq.q = dbus_info->tx_q; + irb = EXEC_TXLOCK(dbus_info, q_deq_exec, &args); + } else { + args.qdeq.q = dbus_info->rx_q; + irb = EXEC_RXLOCK(dbus_info, q_deq_exec, &args); + } + + return irb; +} + +/** + * Called as part of DBUS bus registration. Calls back into higher level (e.g. dhd_linux.c) probe + * function. + */ +static void * +dbus_probe(void *arg, const char *desc, uint32 bustype, uint32 hdrlen) +{ + DBUSTRACE(("%s\n", __FUNCTION__)); + if (probe_cb) { + disc_arg = probe_cb(probe_arg, desc, bustype, hdrlen); + return disc_arg; + } + + return (void *)DBUS_ERR; +} + +/** + * As part of initialization, higher level (e.g. dhd_linux.c) requests DBUS to prepare for + * action. + */ +int +dbus_register(int vid, int pid, probe_cb_t prcb, + disconnect_cb_t discb, void *prarg, void *param1, void *param2) +{ + int err; + + DBUSTRACE(("%s\n", __FUNCTION__)); + + probe_cb = prcb; + disconnect_cb = discb; + probe_arg = prarg; + + err = dbus_bus_register(vid, pid, dbus_probe, /* call lower DBUS level register function */ + dbus_disconnect, NULL, &g_busintf, param1, param2); + + return err; +} + +int +dbus_deregister() +{ + int ret; + + DBUSTRACE(("%s\n", __FUNCTION__)); + + probe_cb = NULL; + ret = dbus_bus_deregister(); + disconnect_cb = NULL; + probe_arg = NULL; + + return ret; + +} + +/** As part of initialization, data structures have to be allocated and initialized */ +dbus_pub_t * +dbus_attach(osl_t *osh, int rxsize, int nrxq, int ntxq, void *cbarg, + dbus_callbacks_t *cbs, dbus_extdl_t *extdl, struct shared_info *sh) +{ + dbus_info_t *dbus_info; + int err; + + if ((g_busintf == NULL) || (g_busintf->attach == NULL) || (cbs == NULL)) + return NULL; + + DBUSTRACE(("%s\n", __FUNCTION__)); + + if ((nrxq <= 0) || (ntxq <= 0)) + return NULL; + + dbus_info = MALLOC(osh, sizeof(dbus_info_t)); + if (dbus_info == NULL) + return NULL; + + bzero(dbus_info, sizeof(dbus_info_t)); + + /* BUS-specific driver interface (at a lower DBUS level) */ + dbus_info->drvintf = g_busintf; + dbus_info->cbarg = cbarg; + dbus_info->cbs = cbs; + + dbus_info->pub.sh = sh; + dbus_info->pub.osh = osh; + dbus_info->pub.rxsize = rxsize; + + + dbus_info->pub.nrxq = nrxq; + dbus_info->rx_low_watermark = nrxq / 2; /* keep enough posted rx urbs */ + dbus_info->pub.ntxq = ntxq; + dbus_info->tx_low_watermark = ntxq / 4; /* flow control when too many tx urbs posted */ + + dbus_info->tx_q = MALLOC(osh, sizeof(dbus_irbq_t)); + if (dbus_info->tx_q == NULL) + goto error; + else { + bzero(dbus_info->tx_q, sizeof(dbus_irbq_t)); + err = dbus_irbq_init(dbus_info, dbus_info->tx_q, ntxq, sizeof(dbus_irb_tx_t)); + if (err != DBUS_OK) + goto error; + } + + dbus_info->rx_q = MALLOC(osh, sizeof(dbus_irbq_t)); + if (dbus_info->rx_q == NULL) + goto error; + else { + bzero(dbus_info->rx_q, sizeof(dbus_irbq_t)); + err = dbus_irbq_init(dbus_info, dbus_info->rx_q, nrxq, sizeof(dbus_irb_rx_t)); + if (err != DBUS_OK) + goto error; + } + + + dbus_info->bus_info = (void *)g_busintf->attach(&dbus_info->pub, + dbus_info, &dbus_intf_cbs); + if (dbus_info->bus_info == NULL) + goto error; + + dbus_tx_timer_init(dbus_info); + +#if defined(BCM_REQUEST_FW) + /* Need to copy external image for re-download */ + if (extdl && extdl->fw && (extdl->fwlen > 0)) { + dbus_info->extdl.fw = MALLOC(osh, extdl->fwlen); + if (dbus_info->extdl.fw) { + bcopy(extdl->fw, dbus_info->extdl.fw, extdl->fwlen); + dbus_info->extdl.fwlen = extdl->fwlen; + } + } + + if (extdl && extdl->vars && (extdl->varslen > 0)) { + dbus_info->extdl.vars = MALLOC(osh, extdl->varslen); + if (dbus_info->extdl.vars) { + bcopy(extdl->vars, dbus_info->extdl.vars, extdl->varslen); + dbus_info->extdl.varslen = extdl->varslen; + } + } + + if (dbus_download_firmware(&dbus_info->pub) != DBUS_OK) + goto error; +#endif + + return (dbus_pub_t *)dbus_info; + +error: + dbus_detach((dbus_pub_t *)dbus_info); + return NULL; +} /* dbus_attach */ + +void +dbus_detach(dbus_pub_t *pub) +{ + dbus_info_t *dbus_info = (dbus_info_t *) pub; + osl_t *osh; + + DBUSTRACE(("%s\n", __FUNCTION__)); + + if (dbus_info == NULL) + return; + + dbus_tx_timer_stop(dbus_info); + + osh = pub->osh; + + if (dbus_info->drvintf && dbus_info->drvintf->detach) + dbus_info->drvintf->detach((dbus_pub_t *)dbus_info, dbus_info->bus_info); + + if (dbus_info->tx_q) { + dbus_irbq_deinit(dbus_info, dbus_info->tx_q, sizeof(dbus_irb_tx_t)); + MFREE(osh, dbus_info->tx_q, sizeof(dbus_irbq_t)); + dbus_info->tx_q = NULL; + } + + if (dbus_info->rx_q) { + dbus_irbq_deinit(dbus_info, dbus_info->rx_q, sizeof(dbus_irb_rx_t)); + MFREE(osh, dbus_info->rx_q, sizeof(dbus_irbq_t)); + dbus_info->rx_q = NULL; + } + + + if (dbus_info->extdl.fw && (dbus_info->extdl.fwlen > 0)) { + MFREE(osh, dbus_info->extdl.fw, dbus_info->extdl.fwlen); + dbus_info->extdl.fw = NULL; + dbus_info->extdl.fwlen = 0; + } + + if (dbus_info->extdl.vars && (dbus_info->extdl.varslen > 0)) { + MFREE(osh, dbus_info->extdl.vars, dbus_info->extdl.varslen); + dbus_info->extdl.vars = NULL; + dbus_info->extdl.varslen = 0; + } + + MFREE(osh, dbus_info, sizeof(dbus_info_t)); +} /* dbus_detach */ + +#if defined(BCM_REQUEST_FW) + +int dbus_download_firmware(dbus_pub_t *pub) +{ + dbus_info_t *dbus_info = (dbus_info_t *) pub; + int err = DBUS_OK; + + DBUSTRACE(("%s: state %d\n", __FUNCTION__, dbus_info->pub.busstate)); + + if (dbus_info->drvintf->dlneeded) { + if (dbus_info->drvintf->dlneeded(dbus_info->bus_info)) { + dbus_info->pub.busstate = DBUS_STATE_DL_PENDING; + err = dbus_do_download(dbus_info); + if (err == DBUS_OK) { + dbus_info->pub.busstate = DBUS_STATE_DL_DONE; + } else { + DBUSERR(("%s: download failed (%d)\n", __FUNCTION__, err)); + } + } + } + + return err; +} + +#endif + +/** + * higher layer requests us to 'up' the interface to the dongle. Prerequisite is that firmware (not + * bootloader) must be active in the dongle. + */ +int +dbus_up(dbus_pub_t *pub) +{ + dbus_info_t *dbus_info = (dbus_info_t *) pub; + int err = DBUS_OK; + + DBUSTRACE(("%s\n", __FUNCTION__)); + + if (dbus_info == NULL) + return DBUS_ERR; + + if ((dbus_info->pub.busstate == DBUS_STATE_DL_DONE) || + (dbus_info->pub.busstate == DBUS_STATE_DOWN) || + (dbus_info->pub.busstate == DBUS_STATE_SLEEP)) { + if (dbus_info->drvintf && dbus_info->drvintf->up) { + err = dbus_info->drvintf->up(dbus_info->bus_info); + + if (err == DBUS_OK) { + dbus_rxirbs_fill(dbus_info); + } + } + } else + err = DBUS_ERR; + + return err; +} + +/** higher layer requests us to 'down' the interface to the dongle. */ +int +dbus_down(dbus_pub_t *pub) +{ + dbus_info_t *dbus_info = (dbus_info_t *) pub; + + DBUSTRACE(("%s\n", __FUNCTION__)); + + if (dbus_info == NULL) + return DBUS_ERR; + + dbus_tx_timer_stop(dbus_info); + + if (dbus_info->pub.busstate == DBUS_STATE_UP || + dbus_info->pub.busstate == DBUS_STATE_SLEEP) { + if (dbus_info->drvintf && dbus_info->drvintf->down) + return dbus_info->drvintf->down(dbus_info->bus_info); + } + + return DBUS_ERR; +} + +int +dbus_shutdown(dbus_pub_t *pub) +{ + dbus_info_t *dbus_info = (dbus_info_t *) pub; + + DBUSTRACE(("%s\n", __FUNCTION__)); + + if (dbus_info == NULL) + return DBUS_ERR; + + if (dbus_info->drvintf && dbus_info->drvintf->shutdown) + return dbus_info->drvintf->shutdown(dbus_info->bus_info); + + return DBUS_OK; +} + +int +dbus_stop(dbus_pub_t *pub) +{ + dbus_info_t *dbus_info = (dbus_info_t *) pub; + + DBUSTRACE(("%s\n", __FUNCTION__)); + + if (dbus_info == NULL) + return DBUS_ERR; + + if (dbus_info->pub.busstate == DBUS_STATE_UP || + dbus_info->pub.busstate == DBUS_STATE_SLEEP) { + if (dbus_info->drvintf && dbus_info->drvintf->stop) + return dbus_info->drvintf->stop(dbus_info->bus_info); + } + + return DBUS_ERR; +} + +int dbus_send_txdata(dbus_pub_t *dbus, void *pktbuf) +{ + return dbus_send_pkt(dbus, pktbuf, pktbuf /* pktinfo */); +} + +int +dbus_send_buf(dbus_pub_t *pub, uint8 *buf, int len, void *info) +{ + return dbus_send_irb(pub, buf, len, NULL, info); +} + +int +dbus_send_pkt(dbus_pub_t *pub, void *pkt, void *info) +{ + return dbus_send_irb(pub, NULL, 0, pkt, info); +} + +int +dbus_send_ctl(dbus_pub_t *pub, uint8 *buf, int len) +{ + dbus_info_t *dbus_info = (dbus_info_t *) pub; + + if (dbus_info == NULL) + return DBUS_ERR; + + if (dbus_info->pub.busstate == DBUS_STATE_UP || + dbus_info->pub.busstate == DBUS_STATE_SLEEP) { + if (dbus_info->drvintf && dbus_info->drvintf->send_ctl) + return dbus_info->drvintf->send_ctl(dbus_info->bus_info, buf, len); + } + + return DBUS_ERR; +} + +int +dbus_recv_ctl(dbus_pub_t *pub, uint8 *buf, int len) +{ + dbus_info_t *dbus_info = (dbus_info_t *) pub; + + if ((dbus_info == NULL) || (buf == NULL)) + return DBUS_ERR; + + if (dbus_info->pub.busstate == DBUS_STATE_UP || + dbus_info->pub.busstate == DBUS_STATE_SLEEP) { + if (dbus_info->drvintf && dbus_info->drvintf->recv_ctl) + return dbus_info->drvintf->recv_ctl(dbus_info->bus_info, buf, len); + } + + return DBUS_ERR; +} + +/** Only called via RPC (Dec 2012) */ +int +dbus_recv_bulk(dbus_pub_t *pub, uint32 ep_idx) +{ + dbus_info_t *dbus_info = (dbus_info_t *) pub; + + dbus_irb_rx_t *rxirb; + struct exec_parms args; + int status; + + + if (dbus_info == NULL) + return DBUS_ERR; + + args.qdeq.q = dbus_info->rx_q; + if (dbus_info->pub.busstate == DBUS_STATE_UP) { + if (dbus_info->drvintf && dbus_info->drvintf->recv_irb_from_ep) { + if ((rxirb = (EXEC_RXLOCK(dbus_info, q_deq_exec, &args))) != NULL) { + status = dbus_info->drvintf->recv_irb_from_ep(dbus_info->bus_info, + rxirb, ep_idx); + if (status == DBUS_ERR_RXDROP) { + bzero(rxirb, sizeof(dbus_irb_rx_t)); + args.qenq.q = dbus_info->rx_q; + args.qenq.b = (dbus_irb_t *) rxirb; + EXEC_RXLOCK(dbus_info, q_enq_exec, &args); + } + } + } + } + + return DBUS_ERR; +} + +/** only called by dhd_cdc.c (Dec 2012) */ +int +dbus_poll_intr(dbus_pub_t *pub) +{ + dbus_info_t *dbus_info = (dbus_info_t *) pub; + + int status = DBUS_ERR; + + if (dbus_info == NULL) + return DBUS_ERR; + + if (dbus_info->pub.busstate == DBUS_STATE_UP) { + if (dbus_info->drvintf && dbus_info->drvintf->recv_irb_from_ep) { + status = dbus_info->drvintf->recv_irb_from_ep(dbus_info->bus_info, + NULL, 0xff); + } + } + return status; +} + +/** called by nobody (Dec 2012) */ +void * +dbus_pktget(dbus_pub_t *pub, int len) +{ + dbus_info_t *dbus_info = (dbus_info_t *) pub; + + if ((dbus_info == NULL) || (len < 0)) + return NULL; + + return PKTGET(dbus_info->pub.osh, len, TRUE); +} + +/** called by nobody (Dec 2012) */ +void +dbus_pktfree(dbus_pub_t *pub, void* pkt) +{ + dbus_info_t *dbus_info = (dbus_info_t *) pub; + + if ((dbus_info == NULL) || (pkt == NULL)) + return; + + PKTFREE(dbus_info->pub.osh, pkt, TRUE); +} + +/** called by nobody (Dec 2012) */ +int +dbus_get_stats(dbus_pub_t *pub, dbus_stats_t *stats) +{ + dbus_info_t *dbus_info = (dbus_info_t *) pub; + + if ((dbus_info == NULL) || (stats == NULL)) + return DBUS_ERR; + + bcopy(&dbus_info->pub.stats, stats, sizeof(dbus_stats_t)); + + return DBUS_OK; +} + +int +dbus_get_attrib(dbus_pub_t *pub, dbus_attrib_t *attrib) +{ + dbus_info_t *dbus_info = (dbus_info_t *) pub; + int err = DBUS_ERR; + + if ((dbus_info == NULL) || (attrib == NULL)) + return DBUS_ERR; + + if (dbus_info->drvintf && dbus_info->drvintf->get_attrib) { + err = dbus_info->drvintf->get_attrib(dbus_info->bus_info, + &dbus_info->pub.attrib); + } + + bcopy(&dbus_info->pub.attrib, attrib, sizeof(dbus_attrib_t)); + return err; +} + +int +dbus_get_device_speed(dbus_pub_t *pub) +{ + dbus_info_t *dbus_info = (dbus_info_t *) pub; + + if (dbus_info == NULL) + return INVALID_SPEED; + + return (dbus_info->pub.device_speed); +} + +int +dbus_set_config(dbus_pub_t *pub, dbus_config_t *config) +{ + dbus_info_t *dbus_info = (dbus_info_t *) pub; + int err = DBUS_ERR; + + if ((dbus_info == NULL) || (config == NULL)) + return DBUS_ERR; + + if (dbus_info->drvintf && dbus_info->drvintf->set_config) { + err = dbus_info->drvintf->set_config(dbus_info->bus_info, + config); + + if ((config->config_id == DBUS_CONFIG_ID_AGGR_LIMIT) && + (!err) && + (dbus_info->pub.busstate == DBUS_STATE_UP)) { + dbus_rxirbs_fill(dbus_info); + } + } + + return err; +} + +int +dbus_get_config(dbus_pub_t *pub, dbus_config_t *config) +{ + dbus_info_t *dbus_info = (dbus_info_t *) pub; + int err = DBUS_ERR; + + if ((dbus_info == NULL) || (config == NULL)) + return DBUS_ERR; + + if (dbus_info->drvintf && dbus_info->drvintf->get_config) { + err = dbus_info->drvintf->get_config(dbus_info->bus_info, + config); + } + + return err; +} + +int +dbus_set_errmask(dbus_pub_t *pub, uint32 mask) +{ + dbus_info_t *dbus_info = (dbus_info_t *) pub; + int err = DBUS_OK; + + if (dbus_info == NULL) + return DBUS_ERR; + + dbus_info->errmask = mask; + return err; +} + +int +dbus_pnp_resume(dbus_pub_t *pub, int *fw_reload) +{ + dbus_info_t *dbus_info = (dbus_info_t *) pub; + int err = DBUS_ERR; + bool fwdl = FALSE; + + DBUSTRACE(("%s\n", __FUNCTION__)); + + if (dbus_info == NULL) + return DBUS_ERR; + + if (dbus_info->pub.busstate == DBUS_STATE_UP) { + return DBUS_OK; + } + + + + if (dbus_info->drvintf->pnp) { + err = dbus_info->drvintf->pnp(dbus_info->bus_info, + DBUS_PNP_RESUME); + } + + if (dbus_info->drvintf->recv_needed) { + if (dbus_info->drvintf->recv_needed(dbus_info->bus_info)) { + /* Refill after sleep/hibernate */ + dbus_rxirbs_fill(dbus_info); + } + } + + + if (fw_reload) + *fw_reload = fwdl; + + return err; +} /* dbus_pnp_resume */ + +int +dbus_pnp_sleep(dbus_pub_t *pub) +{ + dbus_info_t *dbus_info = (dbus_info_t *) pub; + int err = DBUS_ERR; + + DBUSTRACE(("%s\n", __FUNCTION__)); + + if (dbus_info == NULL) + return DBUS_ERR; + + dbus_tx_timer_stop(dbus_info); + + if (dbus_info->drvintf && dbus_info->drvintf->pnp) { + err = dbus_info->drvintf->pnp(dbus_info->bus_info, + DBUS_PNP_SLEEP); + } + + return err; +} + +int +dbus_pnp_disconnect(dbus_pub_t *pub) +{ + dbus_info_t *dbus_info = (dbus_info_t *) pub; + int err = DBUS_ERR; + + DBUSTRACE(("%s\n", __FUNCTION__)); + + if (dbus_info == NULL) + return DBUS_ERR; + + dbus_tx_timer_stop(dbus_info); + + if (dbus_info->drvintf && dbus_info->drvintf->pnp) { + err = dbus_info->drvintf->pnp(dbus_info->bus_info, + DBUS_PNP_DISCONNECT); + } + + return err; +} + +int +dbus_iovar_op(dbus_pub_t *pub, const char *name, + void *params, int plen, void *arg, int len, bool set) +{ + dbus_info_t *dbus_info = (dbus_info_t *) pub; + int err = DBUS_ERR; + + DBUSTRACE(("%s\n", __FUNCTION__)); + + if (dbus_info == NULL) + return DBUS_ERR; + + if (dbus_info->drvintf && dbus_info->drvintf->iovar_op) { + err = dbus_info->drvintf->iovar_op(dbus_info->bus_info, + name, params, plen, arg, len, set); + } + + return err; +} + + +void * +dhd_dbus_txq(const dbus_pub_t *pub) +{ + return NULL; +} + +uint +dhd_dbus_hdrlen(const dbus_pub_t *pub) +{ + return 0; +} + +void * +dbus_get_devinfo(dbus_pub_t *pub) +{ + return pub->dev_info; +} + +#if defined(BCM_REQUEST_FW) + +static int +dbus_otp(dbus_info_t *dbus_info, uint16 *boardtype, uint16 *boardrev) +{ + uint32 value = 0; + uint8 *cis; + uint16 *otpinfo; + uint32 i; + bool standard_cis = TRUE; + uint8 tup, tlen; + bool btype_present = FALSE; + bool brev_present = FALSE; + int ret; + int devid; + uint16 btype = 0; + uint16 brev = 0; + uint32 otp_size = 0, otp_addr = 0, otp_sw_rgn = 0; + + if (dbus_info == NULL || dbus_info->drvintf == NULL || + dbus_info->drvintf->readreg == NULL) + return DBUS_ERR; + + devid = dbus_info->pub.attrib.devid; + + if ((devid == BCM43234_CHIP_ID) || (devid == BCM43235_CHIP_ID) || + (devid == BCM43236_CHIP_ID)) { + + otp_size = BCM_OTP_SIZE_43236; + otp_sw_rgn = BCM_OTP_SW_RGN_43236; + otp_addr = BCM_OTP_ADDR_43236; + + } else { + return DBUS_ERR_NVRAM; + } + + cis = MALLOC(dbus_info->pub.osh, otp_size * 2); + if (cis == NULL) + return DBUS_ERR; + + otpinfo = (uint16 *) cis; + + for (i = 0; i < otp_size; i++) { + + ret = dbus_info->drvintf->readreg(dbus_info->bus_info, + otp_addr + ((otp_sw_rgn + i) << 1), 2, &value); + + if (ret != DBUS_OK) { + MFREE(dbus_info->pub.osh, cis, otp_size * 2); + return ret; + } + otpinfo[i] = (uint16) value; + } + + for (i = 0; i < (otp_size << 1); ) { + + if (standard_cis) { + tup = cis[i++]; + if (tup == CISTPL_NULL || tup == CISTPL_END) + tlen = 0; + else + tlen = cis[i++]; + } else { + if (cis[i] == CISTPL_NULL || cis[i] == CISTPL_END) { + tlen = 0; + tup = cis[i]; + } else { + tlen = cis[i]; + tup = CISTPL_BRCM_HNBU; + } + ++i; + } + + if (tup == CISTPL_END || (i + tlen) >= (otp_size << 1)) { + break; + } + + switch (tup) { + + case CISTPL_BRCM_HNBU: + + switch (cis[i]) { + + case HNBU_BOARDTYPE: + + btype = (uint16) ((cis[i + 2] << 8) + cis[i + 1]); + btype_present = TRUE; + DBUSTRACE(("%s: HNBU_BOARDTYPE = 0x%2x\n", __FUNCTION__, + (uint32)btype)); + break; + + case HNBU_BOARDREV: + + if (tlen == 2) + brev = (uint16) cis[i + 1]; + else + brev = (uint16) ((cis[i + 2] << 8) + cis[i + 1]); + brev_present = TRUE; + DBUSTRACE(("%s: HNBU_BOARDREV = 0x%2x\n", __FUNCTION__, + (uint32)*boardrev)); + break; + + case HNBU_HNBUCIS: + DBUSTRACE(("%s: HNBU_HNBUCIS\n", __FUNCTION__)); + tlen++; + standard_cis = FALSE; + break; + } + break; + } + + i += tlen; + } + + MFREE(dbus_info->pub.osh, cis, otp_size * 2); + + if (btype_present == TRUE && brev_present == TRUE) { + *boardtype = btype; + *boardrev = brev; + DBUSERR(("otp boardtype = 0x%2x boardrev = 0x%2x\n", + *boardtype, *boardrev)); + + return DBUS_OK; + } + else + return DBUS_ERR; +} /* dbus_otp */ + +static int +dbus_select_nvram(dbus_info_t *dbus_info, int8 *jumbonvram, int jumbolen, +uint16 boardtype, uint16 boardrev, int8 **nvram, int *nvram_len) +{ + /* Multi board nvram file format is contenation of nvram info with \r + * The file format for two contatenated set is + * \nBroadcom Jumbo Nvram file\nfirst_set\nsecond_set\nthird_set\n + */ + uint8 *nvram_start = NULL, *nvram_end = NULL; + uint8 *nvram_start_prev = NULL, *nvram_end_prev = NULL; + uint16 btype = 0, brev = 0; + int len = 0; + char *field; + + *nvram = NULL; + *nvram_len = 0; + + if (strncmp(BCM_JUMBO_START, jumbonvram, strlen(BCM_JUMBO_START))) { + /* single nvram file in the native format */ + DBUSTRACE(("%s: Non-Jumbo NVRAM File \n", __FUNCTION__)); + *nvram = jumbonvram; + *nvram_len = jumbolen; + return DBUS_OK; + } else { + DBUSTRACE(("%s: Jumbo NVRAM File \n", __FUNCTION__)); + } + + /* sanity test the end of the config sets for proper ending */ + if (jumbonvram[jumbolen - 1] != BCM_JUMBO_NVRAM_DELIMIT || + jumbonvram[jumbolen - 2] != '\0') { + DBUSERR(("%s: Bad Jumbo NVRAM file format\n", __FUNCTION__)); + return DBUS_JUMBO_BAD_FORMAT; + } + + dbus_info->nvram_nontxt = DBUS_NVRAM_NONTXT; + + nvram_start = jumbonvram; + + while (*nvram_start != BCM_JUMBO_NVRAM_DELIMIT && len < jumbolen) { + + /* consume the first file info line + * \nBroadcom Jumbo Nvram file\nfile1\n ... + */ + len ++; + nvram_start ++; + } + + nvram_end = nvram_start; + + /* search for "boardrev=0xabcd" and "boardtype=0x1234" information in + * the concatenated nvram config files /sets + */ + + while (len < jumbolen) { + + if (*nvram_end == '\0') { + /* end of a config set is marked by multiple null characters */ + len ++; + nvram_end ++; + DBUSTRACE(("%s: NULL chr len = %d char = 0x%x\n", __FUNCTION__, + len, *nvram_end)); + continue; + + } else if (*nvram_end == BCM_JUMBO_NVRAM_DELIMIT) { + + /* config set delimiter is reached */ + /* check if next config set is present or not + * return if next config is not present + */ + + /* start search the next config set */ + nvram_start_prev = nvram_start; + nvram_end_prev = nvram_end; + + nvram_end ++; + nvram_start = nvram_end; + btype = brev = 0; + DBUSTRACE(("%s: going to next record len = %d " + "char = 0x%x \n", __FUNCTION__, len, *nvram_end)); + len ++; + if (len >= jumbolen) { + + *nvram = nvram_start_prev; + *nvram_len = (int)(nvram_end_prev - nvram_start_prev); + + DBUSTRACE(("%s: no more len = %d nvram_end = 0x%p", + __FUNCTION__, len, nvram_end)); + + return DBUS_JUMBO_NOMATCH; + + } else { + continue; + } + + } else { + + DBUSTRACE(("%s: config str = %s\n", __FUNCTION__, nvram_end)); + + if (bcmp(nvram_end, "boardtype", strlen("boardtype")) == 0) { + + field = strchr(nvram_end, '='); + field++; + btype = (uint16)bcm_strtoul(field, NULL, 0); + + DBUSTRACE(("%s: btype = 0x%x boardtype = 0x%x \n", __FUNCTION__, + btype, boardtype)); + } + + if (bcmp(nvram_end, "boardrev", strlen("boardrev")) == 0) { + + field = strchr(nvram_end, '='); + field++; + brev = (uint16)bcm_strtoul(field, NULL, 0); + + DBUSTRACE(("%s: brev = 0x%x boardrev = 0x%x \n", __FUNCTION__, + brev, boardrev)); + } + if (btype == boardtype && brev == boardrev) { + /* locate nvram config set end - ie.find '\r' char */ + while (*nvram_end != BCM_JUMBO_NVRAM_DELIMIT) + nvram_end ++; + *nvram = nvram_start; + *nvram_len = (int) (nvram_end - nvram_start); + DBUSTRACE(("found len = %d nvram_start = 0x%p " + "nvram_end = 0x%p\n", *nvram_len, nvram_start, nvram_end)); + return DBUS_OK; + } + + len += (strlen(nvram_end) + 1); + nvram_end += (strlen(nvram_end) + 1); + } + } + return DBUS_JUMBO_NOMATCH; +} /* dbus_select_nvram */ + +#endif + + +#ifdef LINUX_EXTERNAL_MODULE_DBUS + +static int __init +bcm_dbus_module_init(void) +{ + printf("Inserting bcm_dbus module \n"); + return 0; +} + +static void __exit +bcm_dbus_module_exit(void) +{ + printf("Removing bcm_dbus module \n"); + return; +} + +EXPORT_SYMBOL(dbus_pnp_sleep); +EXPORT_SYMBOL(dbus_register); +EXPORT_SYMBOL(dbus_get_devinfo); +EXPORT_SYMBOL(dbus_detach); +EXPORT_SYMBOL(dbus_get_attrib); +EXPORT_SYMBOL(dbus_down); +EXPORT_SYMBOL(dbus_pnp_resume); +EXPORT_SYMBOL(dbus_set_config); +EXPORT_SYMBOL(dbus_flowctrl_rx); +EXPORT_SYMBOL(dbus_up); +EXPORT_SYMBOL(dbus_get_device_speed); +EXPORT_SYMBOL(dbus_send_pkt); +EXPORT_SYMBOL(dbus_recv_ctl); +EXPORT_SYMBOL(dbus_attach); +EXPORT_SYMBOL(dbus_deregister); + +MODULE_LICENSE("GPL"); + +module_init(bcm_dbus_module_init); +module_exit(bcm_dbus_module_exit); + +#endif /* #ifdef LINUX_EXTERNAL_MODULE_DBUS */ diff --git a/bcmdhd.1.579.77.41.1.cn/dbus_usb.c b/bcmdhd.1.579.77.41.1.cn/dbus_usb.c new file mode 100755 index 0000000..237e016 --- /dev/null +++ b/bcmdhd.1.579.77.41.1.cn/dbus_usb.c @@ -0,0 +1,1167 @@ +/* + * Dongle BUS interface for USB, OS independent + * + * Copyright (C) 1999-2016, 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: dbus_usb.c 565557 2015-06-22 19:29:44Z $ + */ + +/** + * @file @brief + * This file contains DBUS code that is USB, but not OS specific. DBUS is a Broadcom proprietary + * host specific abstraction layer. + */ + +#include +#include +#include +#include +#include +#include +#include + +uint dbus_msglevel = DBUS_ERROR_VAL; +module_param(dbus_msglevel, int, 0); + + +#define USB_DLIMAGE_RETRY_TIMEOUT 3000 /* retry Timeout */ +#define USB_SFLASH_DLIMAGE_SPINWAIT 150 /* in unit of ms */ +#define USB_SFLASH_DLIMAGE_LIMIT 2000 /* spinwait limit (ms) */ +#define POSTBOOT_ID 0xA123 /* ID to detect if dongle has boot up */ +#define USB_RESETCFG_SPINWAIT 1 /* wait after resetcfg (ms) */ +#define USB_DEV_ISBAD(u) (u->pub->attrib.devid == 0xDEAD) +#define USB_DLGO_SPINWAIT 100 /* wait after DL_GO (ms) */ +#define TEST_CHIP 0x4328 + +typedef struct { + dbus_pub_t *pub; + + void *cbarg; + dbus_intf_callbacks_t *cbs; /** callbacks into higher DBUS level (dbus.c) */ + dbus_intf_t *drvintf; + void *usbosl_info; + uint32 rdlram_base_addr; + uint32 rdlram_size; +} usb_info_t; + +/* + * Callbacks common to all USB + */ +static void dbus_usb_disconnect(void *handle); +static void dbus_usb_send_irb_timeout(void *handle, dbus_irb_tx_t *txirb); +static void dbus_usb_send_irb_complete(void *handle, dbus_irb_tx_t *txirb, int status); +static void dbus_usb_recv_irb_complete(void *handle, dbus_irb_rx_t *rxirb, int status); +static void dbus_usb_errhandler(void *handle, int err); +static void dbus_usb_ctl_complete(void *handle, int type, int status); +static void dbus_usb_state_change(void *handle, int state); +static struct dbus_irb* dbus_usb_getirb(void *handle, bool send); +static void dbus_usb_rxerr_indicate(void *handle, bool on); +static int dbus_usb_resetcfg(usb_info_t *usbinfo); +static int dbus_usb_iovar_op(void *bus, const char *name, + void *params, int plen, void *arg, int len, bool set); +static int dbus_iovar_process(usb_info_t* usbinfo, const char *name, + void *params, int plen, void *arg, int len, bool set); +static int dbus_usb_doiovar(usb_info_t *bus, const bcm_iovar_t *vi, uint32 actionid, + const char *name, void *params, int plen, void *arg, int len, int val_size); +static int dhdusb_downloadvars(usb_info_t *bus, void *arg, int len); + +static int dbus_usb_dl_writeimage(usb_info_t *usbinfo, uint8 *fw, int fwlen); +static int dbus_usb_dlstart(void *bus, uint8 *fw, int len); +static bool dbus_usb_dlneeded(void *bus); +static int dbus_usb_dlrun(void *bus); +static int dbus_usb_rdl_dwnld_state(usb_info_t *usbinfo); + + +/* OS specific */ +extern bool dbus_usbos_dl_cmd(void *info, uint8 cmd, void *buffer, int buflen); +extern int dbus_usbos_wait(void *info, uint16 ms); +extern int dbus_write_membytes(usb_info_t *usbinfo, bool set, uint32 address, + uint8 *data, uint size); +extern bool dbus_usbos_dl_send_bulk(void *info, void *buffer, int len); +extern int dbus_usbos_loopback_tx(void *usbos_info_ptr, int cnt, int size); + +/** + * These functions are called by the lower DBUS level (dbus_usb_os.c) to notify this DBUS level + * (dbus_usb.c) of an event. + */ +static dbus_intf_callbacks_t dbus_usb_intf_cbs = { + dbus_usb_send_irb_timeout, + dbus_usb_send_irb_complete, + dbus_usb_recv_irb_complete, + dbus_usb_errhandler, + dbus_usb_ctl_complete, + dbus_usb_state_change, + NULL, /* isr */ + NULL, /* dpc */ + NULL, /* watchdog */ + NULL, /* dbus_if_pktget */ + NULL, /* dbus_if_pktfree */ + dbus_usb_getirb, + dbus_usb_rxerr_indicate +}; + +/* IOVar table */ +enum { + IOV_SET_DOWNLOAD_STATE = 1, + IOV_DBUS_MSGLEVEL, + IOV_MEMBYTES, + IOV_VARS, + IOV_LOOPBACK_TX +}; + +const bcm_iovar_t dhdusb_iovars[] = { + {"vars", IOV_VARS, 0, IOVT_BUFFER, 0 }, + {"dbus_msglevel", IOV_DBUS_MSGLEVEL, 0, IOVT_UINT32, 0 }, + {"dwnldstate", IOV_SET_DOWNLOAD_STATE, 0, IOVT_BOOL, 0 }, + {"membytes", IOV_MEMBYTES, 0, IOVT_BUFFER, 2 * sizeof(int) }, + {"usb_lb_txfer", IOV_LOOPBACK_TX, 0, IOVT_BUFFER, 2 * sizeof(int) }, + {NULL, 0, 0, 0, 0 } +}; + +/* + * Need global for probe() and disconnect() since + * attach() is not called at probe and detach() + * can be called inside disconnect() + */ +static probe_cb_t probe_cb = NULL; +static disconnect_cb_t disconnect_cb = NULL; +static void *probe_arg = NULL; +static void *disc_arg = NULL; +static dbus_intf_t *g_dbusintf = NULL; +static dbus_intf_t dbus_usb_intf; /** functions called by higher layer DBUS into lower layer */ + +/* + * dbus_intf_t common to all USB + * These functions override dbus_usb_.c. + */ +static void *dbus_usb_attach(dbus_pub_t *pub, void *cbarg, dbus_intf_callbacks_t *cbs); +static void dbus_usb_detach(dbus_pub_t *pub, void *info); +static void * dbus_usb_probe(void *arg, const char *desc, uint32 bustype, uint32 hdrlen); + +/* functions */ + +/** + * As part of DBUS initialization/registration, the higher level DBUS (dbus.c) needs to know what + * lower level DBUS functions to call (in both dbus_usb.c and dbus_usb_os.c). + */ +static void * +dbus_usb_probe(void *arg, const char *desc, uint32 bustype, uint32 hdrlen) +{ + DBUSTRACE(("%s(): \n", __FUNCTION__)); + if (probe_cb) { + + if (g_dbusintf != NULL) { + /* First, initialize all lower-level functions as default + * so that dbus.c simply calls directly to dbus_usb_os.c. + */ + bcopy(g_dbusintf, &dbus_usb_intf, sizeof(dbus_intf_t)); + + /* Second, selectively override functions we need, if any. */ + dbus_usb_intf.attach = dbus_usb_attach; + dbus_usb_intf.detach = dbus_usb_detach; + dbus_usb_intf.iovar_op = dbus_usb_iovar_op; + dbus_usb_intf.dlstart = dbus_usb_dlstart; + dbus_usb_intf.dlneeded = dbus_usb_dlneeded; + dbus_usb_intf.dlrun = dbus_usb_dlrun; + } + + disc_arg = probe_cb(probe_arg, "DBUS USB", USB_BUS, hdrlen); + return disc_arg; + } + + return NULL; +} + +/** + * On return, *intf contains this or lower-level DBUS functions to be called by higher + * level (dbus.c) + */ +int +dbus_bus_register(int vid, int pid, probe_cb_t prcb, + disconnect_cb_t discb, void *prarg, dbus_intf_t **intf, void *param1, void *param2) +{ + int err; + + DBUSTRACE(("%s(): \n", __FUNCTION__)); + probe_cb = prcb; + disconnect_cb = discb; + probe_arg = prarg; + + *intf = &dbus_usb_intf; + + err = dbus_bus_osl_register(vid, pid, dbus_usb_probe, + dbus_usb_disconnect, NULL, &g_dbusintf, param1, param2); + + ASSERT(g_dbusintf); + return err; +} + +int +dbus_bus_deregister() +{ + DBUSTRACE(("%s(): \n", __FUNCTION__)); + return dbus_bus_osl_deregister(); +} + +/** initialization consists of registration followed by 'attach'. */ +void * +dbus_usb_attach(dbus_pub_t *pub, void *cbarg, dbus_intf_callbacks_t *cbs) +{ + usb_info_t *usb_info; + + DBUSTRACE(("%s(): \n", __FUNCTION__)); + + if ((g_dbusintf == NULL) || (g_dbusintf->attach == NULL)) + return NULL; + + /* Sanity check for BUS_INFO() */ + ASSERT(OFFSETOF(usb_info_t, pub) == 0); + + usb_info = MALLOC(pub->osh, sizeof(usb_info_t)); + if (usb_info == NULL) + return NULL; + + bzero(usb_info, sizeof(usb_info_t)); + + usb_info->pub = pub; + usb_info->cbarg = cbarg; + usb_info->cbs = cbs; + + usb_info->usbosl_info = (dbus_pub_t *)g_dbusintf->attach(pub, + usb_info, &dbus_usb_intf_cbs); + if (usb_info->usbosl_info == NULL) { + MFREE(pub->osh, usb_info, sizeof(usb_info_t)); + return NULL; + } + + /* Save USB OS-specific driver entry points */ + usb_info->drvintf = g_dbusintf; + + pub->bus = usb_info; +#if !defined(BCM_REQUEST_FW) + + if (!dbus_usb_resetcfg(usb_info)) { + usb_info->pub->busstate = DBUS_STATE_DL_DONE; + } +#endif + /* Return Lower layer info */ + return (void *) usb_info->usbosl_info; +} + +void +dbus_usb_detach(dbus_pub_t *pub, void *info) +{ + usb_info_t *usb_info = (usb_info_t *) pub->bus; + osl_t *osh = pub->osh; + + if (usb_info == NULL) + return; + + if (usb_info->drvintf && usb_info->drvintf->detach) + usb_info->drvintf->detach(pub, usb_info->usbosl_info); + + MFREE(osh, usb_info, sizeof(usb_info_t)); +} + +void +dbus_usb_disconnect(void *handle) +{ + DBUSTRACE(("%s(): \n", __FUNCTION__)); + if (disconnect_cb) + disconnect_cb(disc_arg); +} + +/** + * When the lower DBUS level (dbus_usb_os.c) signals this event, the higher DBUS level has to be + * notified. + */ +static void +dbus_usb_send_irb_timeout(void *handle, dbus_irb_tx_t *txirb) +{ + usb_info_t *usb_info = (usb_info_t *) handle; + + DBUSTRACE(("%s\n", __FUNCTION__)); + + if (usb_info == NULL) + return; + + if (usb_info->cbs && usb_info->cbs->send_irb_timeout) + usb_info->cbs->send_irb_timeout(usb_info->cbarg, txirb); +} + +/** + * When the lower DBUS level (dbus_usb_os.c) signals this event, the higher DBUS level has to be + * notified. + */ +static void +dbus_usb_send_irb_complete(void *handle, dbus_irb_tx_t *txirb, int status) +{ + usb_info_t *usb_info = (usb_info_t *) handle; + + if (usb_info == NULL) + return; + + if (usb_info->cbs && usb_info->cbs->send_irb_complete) + usb_info->cbs->send_irb_complete(usb_info->cbarg, txirb, status); +} + +/** + * When the lower DBUS level (dbus_usb_os.c) signals this event, the higher DBUS level has to be + * notified. + */ +static void +dbus_usb_recv_irb_complete(void *handle, dbus_irb_rx_t *rxirb, int status) +{ + usb_info_t *usb_info = (usb_info_t *) handle; + + if (usb_info == NULL) + return; + + if (usb_info->cbs && usb_info->cbs->recv_irb_complete) + usb_info->cbs->recv_irb_complete(usb_info->cbarg, rxirb, status); +} + +/** Lower DBUS level (dbus_usb_os.c) requests a free IRB. Pass this on to the higher DBUS level. */ +static struct dbus_irb* +dbus_usb_getirb(void *handle, bool send) +{ + usb_info_t *usb_info = (usb_info_t *) handle; + + if (usb_info == NULL) + return NULL; + + if (usb_info->cbs && usb_info->cbs->getirb) + return usb_info->cbs->getirb(usb_info->cbarg, send); + + return NULL; +} + +/** + * When the lower DBUS level (dbus_usb_os.c) signals this event, the higher DBUS level has to be + * notified. + */ +static void +dbus_usb_rxerr_indicate(void *handle, bool on) +{ + usb_info_t *usb_info = (usb_info_t *) handle; + + if (usb_info == NULL) + return; + + if (usb_info->cbs && usb_info->cbs->rxerr_indicate) + usb_info->cbs->rxerr_indicate(usb_info->cbarg, on); +} + +/** + * When the lower DBUS level (dbus_usb_os.c) signals this event, the higher DBUS level has to be + * notified. + */ +static void +dbus_usb_errhandler(void *handle, int err) +{ + usb_info_t *usb_info = (usb_info_t *) handle; + + if (usb_info == NULL) + return; + + if (usb_info->cbs && usb_info->cbs->errhandler) + usb_info->cbs->errhandler(usb_info->cbarg, err); +} + +/** + * When the lower DBUS level (dbus_usb_os.c) signals this event, the higher DBUS level has to be + * notified. + */ +static void +dbus_usb_ctl_complete(void *handle, int type, int status) +{ + usb_info_t *usb_info = (usb_info_t *) handle; + + DBUSTRACE(("%s\n", __FUNCTION__)); + + if (usb_info == NULL) { + DBUSERR(("%s: usb_info is NULL\n", __FUNCTION__)); + return; + } + + if (usb_info->cbs && usb_info->cbs->ctl_complete) + usb_info->cbs->ctl_complete(usb_info->cbarg, type, status); +} + +/** + * When the lower DBUS level (dbus_usb_os.c) signals this event, the higher DBUS level has to be + * notified. + */ +static void +dbus_usb_state_change(void *handle, int state) +{ + usb_info_t *usb_info = (usb_info_t *) handle; + + if (usb_info == NULL) + return; + + if (usb_info->cbs && usb_info->cbs->state_change) + usb_info->cbs->state_change(usb_info->cbarg, state); +} + +/** called by higher DBUS level (dbus.c) */ +static int +dbus_usb_iovar_op(void *bus, const char *name, + void *params, int plen, void *arg, int len, bool set) +{ + int err = DBUS_OK; + + err = dbus_iovar_process((usb_info_t*)bus, name, params, plen, arg, len, set); + return err; +} + +/** process iovar request from higher DBUS level */ +static int +dbus_iovar_process(usb_info_t* usbinfo, 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; + uint32 actionid; + + DBUSTRACE(("%s: Enter\n", __FUNCTION__)); + + ASSERT(name); + ASSERT(len >= 0); + + /* Get MUST have return space */ + ASSERT(set || (arg && len)); + + /* Set does NOT take qualifiers */ + ASSERT(!set || (!params && !plen)); + + /* Look up var locally; if not found pass to host driver */ + if ((vi = bcm_iovar_lookup(dhdusb_iovars, name)) == NULL) { + /* Not Supported */ + bcmerror = BCME_UNSUPPORTED; + DBUSTRACE(("%s: IOVAR %s is not supported\n", name, __FUNCTION__)); + goto exit; + + } + + DBUSTRACE(("%s: %s %s, len %d plen %d\n", __FUNCTION__, + name, (set ? "set" : "get"), len, plen)); + + /* set up 'params' pointer in case this is a set command so that + * the convenience int and bool code can be common to set and get + */ + 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 + /* all other types are integer sized */ + val_size = sizeof(int); + + actionid = set ? IOV_SVAL(vi->varid) : IOV_GVAL(vi->varid); + bcmerror = dbus_usb_doiovar(usbinfo, vi, actionid, + name, params, plen, arg, len, val_size); + +exit: + return bcmerror; +} /* dbus_iovar_process */ + +static int +dbus_usb_doiovar(usb_info_t *bus, const bcm_iovar_t *vi, uint32 actionid, const char *name, + void *params, int plen, void *arg, int len, int val_size) +{ + int bcmerror = 0; + int32 int_val = 0; + int32 int_val2 = 0; + bool bool_val = 0; + + DBUSTRACE(("%s: Enter, action %d name %s params %p plen %d arg %p len %d val_size %d\n", + __FUNCTION__, actionid, name, params, plen, arg, len, val_size)); + + if ((bcmerror = bcm_iovar_lencheck(vi, arg, len, IOV_ISSET(actionid))) != 0) + goto exit; + + if (plen >= (int)sizeof(int_val)) + bcopy(params, &int_val, sizeof(int_val)); + + if (plen >= (int)sizeof(int_val) * 2) + bcopy((void*)((uintptr)params + sizeof(int_val)), &int_val2, sizeof(int_val2)); + + bool_val = (int_val != 0) ? TRUE : FALSE; + + switch (actionid) { + + case IOV_SVAL(IOV_MEMBYTES): + case IOV_GVAL(IOV_MEMBYTES): + { + uint32 address; + uint size, dsize; + uint8 *data; + + bool set = (actionid == IOV_SVAL(IOV_MEMBYTES)); + + ASSERT(plen >= 2*sizeof(int)); + + address = (uint32)int_val; + BCM_REFERENCE(address); + bcopy((char *)params + sizeof(int_val), &int_val, sizeof(int_val)); + size = (uint)int_val; + + /* Do some validation */ + dsize = set ? plen - (2 * sizeof(int)) : len; + if (dsize < size) { + DBUSTRACE(("%s: error on %s membytes, addr 0x%08x size %d dsize %d\n", + __FUNCTION__, (set ? "set" : "get"), address, size, dsize)); + bcmerror = BCME_BADARG; + break; + } + DBUSTRACE(("%s: Request to %s %d bytes at address 0x%08x\n", __FUNCTION__, + (set ? "write" : "read"), size, address)); + + /* Generate the actual data pointer */ + data = set ? (uint8*)params + 2 * sizeof(int): (uint8*)arg; + + /* Call to do the transfer */ + bcmerror = dbus_usb_dl_writeimage(BUS_INFO(bus, usb_info_t), data, size); + } + break; + + + case IOV_SVAL(IOV_SET_DOWNLOAD_STATE): + + if (bool_val == TRUE) { + bcmerror = dbus_usb_dlneeded(bus); + dbus_usb_rdl_dwnld_state(BUS_INFO(bus, usb_info_t)); + } else { + usb_info_t *usbinfo = BUS_INFO(bus, usb_info_t); + bcmerror = dbus_usb_dlrun(bus); + usbinfo->pub->busstate = DBUS_STATE_DL_DONE; + } + break; + + case IOV_SVAL(IOV_VARS): + bcmerror = dhdusb_downloadvars(BUS_INFO(bus, usb_info_t), arg, len); + break; + + case IOV_GVAL(IOV_DBUS_MSGLEVEL): + int_val = (int32)dbus_msglevel; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_DBUS_MSGLEVEL): + dbus_msglevel = int_val; + break; + +#ifdef DBUS_USB_LOOPBACK + case IOV_SVAL(IOV_LOOPBACK_TX): + bcmerror = dbus_usbos_loopback_tx(BUS_INFO(bus, usb_info_t), int_val, + int_val2); + break; +#endif + default: + bcmerror = BCME_UNSUPPORTED; + break; + } + +exit: + return bcmerror; +} /* dbus_usb_doiovar */ + +/** higher DBUS level (dbus.c) wants to set NVRAM variables in dongle */ +static int +dhdusb_downloadvars(usb_info_t *bus, void *arg, int len) +{ + int bcmerror = 0; + uint32 varsize; + uint32 varaddr; + uint32 varsizew; + + if (!len) { + bcmerror = BCME_BUFTOOSHORT; + goto err; + } + + /* RAM size is not set. Set it at dbus_usb_dlneeded */ + if (!bus->rdlram_size) + bcmerror = BCME_ERROR; + + /* Even if there are no vars are to be written, we still need to set the ramsize. */ + varsize = len ? ROUNDUP(len, 4) : 0; + varaddr = (bus->rdlram_size - 4) - varsize; + + /* Write the vars list */ + DBUSTRACE(("WriteVars: @%x varsize=%d\n", varaddr, varsize)); + bcmerror = dbus_write_membytes(bus->usbosl_info, TRUE, (varaddr + bus->rdlram_base_addr), + arg, varsize); + + /* adjust to the user specified RAM */ + DBUSTRACE(("Usable memory size: %d\n", bus->rdlram_size)); + DBUSTRACE(("Vars are at %d, orig varsize is %d\n", varaddr, varsize)); + + varsize = ((bus->rdlram_size - 4) - varaddr); + + /* + * Determine the length token: + * Varsize, converted to words, in lower 16-bits, checksum in upper 16-bits. + */ + if (bcmerror) { + varsizew = 0; + } else { + varsizew = varsize / 4; + varsizew = (~varsizew << 16) | (varsizew & 0x0000FFFF); + varsizew = htol32(varsizew); + } + + DBUSTRACE(("New varsize is %d, length token=0x%08x\n", varsize, varsizew)); + + /* Write the length token to the last word */ + bcmerror = dbus_write_membytes(bus->usbosl_info, TRUE, ((bus->rdlram_size - 4) + + bus->rdlram_base_addr), (uint8*)&varsizew, 4); +err: + return bcmerror; +} /* dbus_usb_doiovar */ + +/** + * After downloading firmware into dongle and starting it, we need to know if the firmware is + * indeed up and running. + */ +static int +dbus_usb_resetcfg(usb_info_t *usbinfo) +{ + void *osinfo; + bootrom_id_t id; + uint16 waittime = 0; + + uint32 starttime = 0; + uint32 endtime = 0; + + DBUSTRACE(("%s\n", __FUNCTION__)); + + if (usbinfo == NULL) + return DBUS_ERR; + + osinfo = usbinfo->usbosl_info; + ASSERT(osinfo); + + /* Give dongle chance to boot */ + dbus_usbos_wait(osinfo, USB_SFLASH_DLIMAGE_SPINWAIT); + waittime = USB_SFLASH_DLIMAGE_SPINWAIT; + while (waittime < USB_DLIMAGE_RETRY_TIMEOUT) { + + starttime = OSL_SYSUPTIME(); + + id.chip = 0xDEAD; /* Get the ID */ + dbus_usbos_dl_cmd(osinfo, DL_GETVER, &id, sizeof(bootrom_id_t)); + id.chip = ltoh32(id.chip); + + endtime = OSL_SYSUPTIME(); + waittime += (endtime - starttime); + + if (id.chip == POSTBOOT_ID) + break; + } + + if (id.chip == POSTBOOT_ID) { + DBUSERR(("%s: download done. Bootup time = %d ms postboot chip 0x%x/rev 0x%x\n", + __FUNCTION__, waittime, id.chip, id.chiprev)); + + dbus_usbos_dl_cmd(osinfo, DL_RESETCFG, &id, sizeof(bootrom_id_t)); + + dbus_usbos_wait(osinfo, USB_RESETCFG_SPINWAIT); + return DBUS_OK; + } else { + DBUSERR(("%s: Cannot talk to Dongle. Wait time = %d ms. Firmware is not UP \n", + __FUNCTION__, waittime)); + return DBUS_ERR; + } + + return DBUS_OK; +} + +/** before firmware download, the dongle has to be prepared to receive the fw image */ +static int +dbus_usb_rdl_dwnld_state(usb_info_t *usbinfo) +{ + void *osinfo = usbinfo->usbosl_info; + rdl_state_t state; + int err = DBUS_OK; + + /* 1) Prepare USB boot loader for runtime image */ + dbus_usbos_dl_cmd(osinfo, DL_START, &state, sizeof(rdl_state_t)); + + state.state = ltoh32(state.state); + state.bytes = ltoh32(state.bytes); + + /* 2) Check we are in the Waiting state */ + if (state.state != DL_WAITING) { + DBUSERR(("%s: Failed to DL_START\n", __FUNCTION__)); + err = DBUS_ERR; + goto fail; + } + +fail: + return err; +} + +/** + * Dongle contains bootcode in ROM but firmware is (partially) contained in dongle RAM. Therefore, + * firmware has to be downloaded into dongle RAM. + */ +static int +dbus_usb_dl_writeimage(usb_info_t *usbinfo, uint8 *fw, int fwlen) +{ + osl_t *osh = usbinfo->pub->osh; + void *osinfo = usbinfo->usbosl_info; + unsigned int sendlen, sent, dllen; + char *bulkchunk = NULL, *dlpos; + rdl_state_t state; + int err = DBUS_OK; + bootrom_id_t id; + uint16 wait, wait_time; + uint32 dl_trunk_size = RDL_CHUNK; + + if (BCM4350_CHIP(usbinfo->pub->attrib.devid)) + dl_trunk_size = RDL_CHUNK_MAX; + + while (!bulkchunk) { + bulkchunk = MALLOC(osh, dl_trunk_size); + if (dl_trunk_size == RDL_CHUNK) + break; + if (!bulkchunk) { + dl_trunk_size /= 2; + if (dl_trunk_size < RDL_CHUNK) + dl_trunk_size = RDL_CHUNK; + } + } + + if (bulkchunk == NULL) { + err = DBUS_ERR; + goto fail; + } + + sent = 0; + dlpos = fw; + dllen = fwlen; + + /* Get chip id and rev */ + id.chip = usbinfo->pub->attrib.devid; + id.chiprev = usbinfo->pub->attrib.chiprev; + + DBUSTRACE(("enter %s: fwlen=%d\n", __FUNCTION__, fwlen)); + + dbus_usbos_dl_cmd(osinfo, DL_GETSTATE, &state, sizeof(rdl_state_t)); + + /* 3) Load the image */ + while ((sent < dllen)) { + /* Wait until the usb device reports it received all the bytes we sent */ + + if (sent < dllen) { + if ((dllen-sent) < dl_trunk_size) + sendlen = dllen-sent; + else + sendlen = dl_trunk_size; + + /* simply avoid having to send a ZLP by ensuring we never have an even + * multiple of 64 + */ + if (!(sendlen % 64)) + sendlen -= 4; + + /* send data */ + memcpy(bulkchunk, dlpos, sendlen); + if (!dbus_usbos_dl_send_bulk(osinfo, bulkchunk, sendlen)) { + err = DBUS_ERR; + goto fail; + } + + dlpos += sendlen; + sent += sendlen; + DBUSTRACE(("%s: sendlen %d\n", __FUNCTION__, sendlen)); + } + + wait = 0; + wait_time = USB_SFLASH_DLIMAGE_SPINWAIT; + while (!dbus_usbos_dl_cmd(osinfo, DL_GETSTATE, &state, + sizeof(rdl_state_t))) { + if ((id.chip == 43236) && (id.chiprev == 0)) { + DBUSERR(("%s: 43236a0 SFlash delay, waiting for dongle crc check " + "completion!!!\n", __FUNCTION__)); + dbus_usbos_wait(osinfo, wait_time); + wait += wait_time; + if (wait >= USB_SFLASH_DLIMAGE_LIMIT) { + DBUSERR(("%s: DL_GETSTATE Failed xxxx\n", __FUNCTION__)); + err = DBUS_ERR; + goto fail; + break; + } + } else { + DBUSERR(("%s: DL_GETSTATE Failed xxxx\n", __FUNCTION__)); + err = DBUS_ERR; + goto fail; + } + } + + state.state = ltoh32(state.state); + state.bytes = ltoh32(state.bytes); + + /* restart if an error is reported */ + if ((state.state == DL_BAD_HDR) || (state.state == DL_BAD_CRC)) { + DBUSERR(("%s: Bad Hdr or Bad CRC\n", __FUNCTION__)); + err = DBUS_ERR; + goto fail; + } + + } +fail: + if (bulkchunk) + MFREE(osh, bulkchunk, dl_trunk_size); + + return err; +} /* dbus_usb_dl_writeimage */ + +/** Higher level DBUS layer (dbus.c) requests this layer to download image into dongle */ +static int +dbus_usb_dlstart(void *bus, uint8 *fw, int len) +{ + usb_info_t *usbinfo = BUS_INFO(bus, usb_info_t); + int err; + + DBUSTRACE(("%s\n", __FUNCTION__)); + + if (usbinfo == NULL) + return DBUS_ERR; + + if (USB_DEV_ISBAD(usbinfo)) + return DBUS_ERR; + + err = dbus_usb_rdl_dwnld_state(usbinfo); + + if (DBUS_OK == err) { + err = dbus_usb_dl_writeimage(usbinfo, fw, len); + if (err == DBUS_OK) + usbinfo->pub->busstate = DBUS_STATE_DL_DONE; + else + usbinfo->pub->busstate = DBUS_STATE_DL_PENDING; + } else + usbinfo->pub->busstate = DBUS_STATE_DL_PENDING; + + return err; +} + +static bool +dbus_usb_update_chipinfo(usb_info_t *usbinfo, uint32 chip) +{ + bool retval = TRUE; + /* based on the CHIP Id, store the ram size which is needed for NVRAM download. */ + switch (chip) { + + case 0x4319: + usbinfo->rdlram_size = RDL_RAM_SIZE_4319; + usbinfo->rdlram_base_addr = RDL_RAM_BASE_4319; + break; + + case 0x4329: + usbinfo->rdlram_size = RDL_RAM_SIZE_4329; + usbinfo->rdlram_base_addr = RDL_RAM_BASE_4329; + break; + + case 43234: + case 43235: + case 43236: + usbinfo->rdlram_size = RDL_RAM_SIZE_43236; + usbinfo->rdlram_base_addr = RDL_RAM_BASE_43236; + break; + + case 0x4328: + usbinfo->rdlram_size = RDL_RAM_SIZE_4328; + usbinfo->rdlram_base_addr = RDL_RAM_BASE_4328; + break; + + case 0x4322: + usbinfo->rdlram_size = RDL_RAM_SIZE_4322; + usbinfo->rdlram_base_addr = RDL_RAM_BASE_4322; + break; + + case 0x4360: + case 0xAA06: + usbinfo->rdlram_size = RDL_RAM_SIZE_4360; + usbinfo->rdlram_base_addr = RDL_RAM_BASE_4360; + break; + + case 43242: + case 43243: + usbinfo->rdlram_size = RDL_RAM_SIZE_43242; + usbinfo->rdlram_base_addr = RDL_RAM_BASE_43242; + break; + + case 43143: + usbinfo->rdlram_size = RDL_RAM_SIZE_43143; + usbinfo->rdlram_base_addr = RDL_RAM_BASE_43143; + break; + + case 0x4350: + case 43556: + case 43558: + case 43569: + usbinfo->rdlram_size = RDL_RAM_SIZE_4350; + usbinfo->rdlram_base_addr = RDL_RAM_BASE_4350; + break; + + case POSTBOOT_ID: + break; + + default: + DBUSERR(("%s: Chip 0x%x Ram size is not known\n", __FUNCTION__, chip)); + retval = FALSE; + break; + + } + + return retval; +} /* dbus_usb_update_chipinfo */ + +/** higher DBUS level (dbus.c) wants to know if firmware download is required. */ +static bool +dbus_usb_dlneeded(void *bus) +{ + usb_info_t *usbinfo = BUS_INFO(bus, usb_info_t); + void *osinfo; + bootrom_id_t id; + bool dl_needed = TRUE; + + DBUSTRACE(("%s\n", __FUNCTION__)); + + if (usbinfo == NULL) + return FALSE; + + osinfo = usbinfo->usbosl_info; + ASSERT(osinfo); + + /* Check if firmware downloaded already by querying runtime ID */ + id.chip = 0xDEAD; + dbus_usbos_dl_cmd(osinfo, DL_GETVER, &id, sizeof(bootrom_id_t)); + + id.chip = ltoh32(id.chip); + id.chiprev = ltoh32(id.chiprev); + + if (FALSE == dbus_usb_update_chipinfo(usbinfo, id.chip)) { + dl_needed = FALSE; + goto exit; + } + + DBUSERR(("%s: chip 0x%x rev 0x%x\n", __FUNCTION__, id.chip, id.chiprev)); + if (id.chip == POSTBOOT_ID) { + /* This code is needed to support two enumerations on USB1.1 scenario */ + DBUSERR(("%s: Firmware already downloaded\n", __FUNCTION__)); + + dbus_usbos_dl_cmd(osinfo, DL_RESETCFG, &id, sizeof(bootrom_id_t)); + dl_needed = FALSE; + if (usbinfo->pub->busstate == DBUS_STATE_DL_PENDING) + usbinfo->pub->busstate = DBUS_STATE_DL_DONE; + } else { + usbinfo->pub->attrib.devid = id.chip; + usbinfo->pub->attrib.chiprev = id.chiprev; + } + +exit: + return dl_needed; +} + +/** After issuing firmware download, higher DBUS level (dbus.c) wants to start the firmware. */ +static int +dbus_usb_dlrun(void *bus) +{ + usb_info_t *usbinfo = BUS_INFO(bus, usb_info_t); + void *osinfo; + rdl_state_t state; + int err = DBUS_OK; + + DBUSTRACE(("%s\n", __FUNCTION__)); + + if (usbinfo == NULL) + return DBUS_ERR; + + if (USB_DEV_ISBAD(usbinfo)) + return DBUS_ERR; + + osinfo = usbinfo->usbosl_info; + ASSERT(osinfo); + + /* Check we are runnable */ + dbus_usbos_dl_cmd(osinfo, DL_GETSTATE, &state, sizeof(rdl_state_t)); + + state.state = ltoh32(state.state); + state.bytes = ltoh32(state.bytes); + + /* Start the image */ + if (state.state == DL_RUNNABLE) { + DBUSTRACE(("%s: Issue DL_GO\n", __FUNCTION__)); + dbus_usbos_dl_cmd(osinfo, DL_GO, &state, sizeof(rdl_state_t)); + + if (usbinfo->pub->attrib.devid == TEST_CHIP) + dbus_usbos_wait(osinfo, USB_DLGO_SPINWAIT); + + dbus_usb_resetcfg(usbinfo); + /* The Donlge may go for re-enumeration. */ + } else { + DBUSERR(("%s: Dongle not runnable\n", __FUNCTION__)); + err = DBUS_ERR; + } + + return err; +} + +/** + * As preparation for firmware download, higher DBUS level (dbus.c) requests the firmware image + * to be used for the type of dongle detected. Directly called by dbus.c (so not via a callback + * construction) + */ +void +dbus_bus_fw_get(void *bus, uint8 **fw, int *fwlen, int *decomp) +{ + usb_info_t *usbinfo = BUS_INFO(bus, usb_info_t); + unsigned int devid; + unsigned int crev; + + devid = usbinfo->pub->attrib.devid; + crev = usbinfo->pub->attrib.chiprev; + + *fw = NULL; + *fwlen = 0; + + switch (devid) { + case BCM43236_CHIP_ID: + case BCM43235_CHIP_ID: + case BCM43234_CHIP_ID: + case BCM43238_CHIP_ID: { + if (crev == 3 || crev == 2 || crev == 1) { +#ifdef EMBED_IMAGE_43236b + *fw = (uint8 *)dlarray_43236b; + *fwlen = sizeof(dlarray_43236b); + +#endif + } + } break; + case BCM4360_CHIP_ID: + case BCM4352_CHIP_ID: + case BCM43526_CHIP_ID: +#ifdef EMBED_IMAGE_43526a + if (crev <= 2) { + *fw = (uint8 *)dlarray_43526a; + *fwlen = sizeof(dlarray_43526a); + } +#endif +#ifdef EMBED_IMAGE_43526b + if (crev > 2) { + *fw = (uint8 *)dlarray_43526b; + *fwlen = sizeof(dlarray_43526b); + } +#endif + break; + + case BCM43242_CHIP_ID: +#ifdef EMBED_IMAGE_43242a0 + *fw = (uint8 *)dlarray_43242a0; + *fwlen = sizeof(dlarray_43242a0); +#endif + break; + + case BCM43143_CHIP_ID: +#ifdef EMBED_IMAGE_43143a0 + *fw = (uint8 *)dlarray_43143a0; + *fwlen = sizeof(dlarray_43143a0); +#endif +#ifdef EMBED_IMAGE_43143b0 + *fw = (uint8 *)dlarray_43143b0; + *fwlen = sizeof(dlarray_43143b0); +#endif + break; + + case BCM4350_CHIP_ID: + case BCM4354_CHIP_ID: + case BCM43556_CHIP_ID: + case BCM43558_CHIP_ID: + case BCM43566_CHIP_ID: + case BCM43568_CHIP_ID: + case BCM43570_CHIP_ID: + case BCM4358_CHIP_ID: +#ifdef EMBED_IMAGE_4350a0 + if (crev == 0) { + *fw = (uint8 *)dlarray_4350a0; + *fwlen = sizeof(dlarray_4350a0); + } +#endif +#ifdef EMBED_IMAGE_4350b0 + if (crev == 1) { + *fw = (uint8 *)dlarray_4350b0; + *fwlen = sizeof(dlarray_4350b0); + } +#endif +#ifdef EMBED_IMAGE_4350b1 + if (crev == 2) { + *fw = (uint8 *)dlarray_4350b1; + *fwlen = sizeof(dlarray_4350b1); + } +#endif +#ifdef EMBED_IMAGE_43556b1 + if (crev == 2) { + *fw = (uint8 *)dlarray_43556b1; + *fwlen = sizeof(dlarray_43556b1); + } +#endif +#ifdef EMBED_IMAGE_4350c0 + if (crev == 3) { + *fw = (uint8 *)dlarray_4350c0; + *fwlen = sizeof(dlarray_4350c0); + } +#endif /* EMBED_IMAGE_4350c0 */ +#ifdef EMBED_IMAGE_4350c1 + if (crev == 4) { + *fw = (uint8 *)dlarray_4350c1; + *fwlen = sizeof(dlarray_4350c1); + } +#endif /* EMBED_IMAGE_4350c1 */ + break; + case BCM43569_CHIP_ID: +#ifdef EMBED_IMAGE_43569a0 + if (crev == 0) { + *fw = (uint8 *)dlarray_43569a0; + *fwlen = sizeof(dlarray_43569a0); + } +#endif /* EMBED_IMAGE_43569a0 */ + break; + default: +#ifdef EMBED_IMAGE_GENERIC + *fw = (uint8 *)dlarray; + *fwlen = sizeof(dlarray); +#endif + break; + } +} /* dbus_bus_fw_get */ diff --git a/bcmdhd.1.579.77.41.1.cn/dbus_usb_linux.c b/bcmdhd.1.579.77.41.1.cn/dbus_usb_linux.c new file mode 100755 index 0000000..893760b --- /dev/null +++ b/bcmdhd.1.579.77.41.1.cn/dbus_usb_linux.c @@ -0,0 +1,3385 @@ +/* + * Dongle BUS interface + * USB Linux Implementation + * + * Copyright (C) 1999-2016, 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: dbus_usb_linux.c 564663 2015-06-18 02:34:42Z $ + */ + +/** + * @file @brief + * This file contains DBUS code that is USB *and* OS (Linux) specific. DBUS is a Broadcom + * proprietary host specific abstraction layer. + */ + +#include +#include + +/** + * DBUS_LINUX_RXDPC is created for router platform performance tuning. A separate thread is created + * to handle USB RX and avoid the call chain getting too long and enhance cache hit rate. + * + * DBUS_LINUX_RXDPC setting is in wlconfig file. + */ + +/* + * If DBUS_LINUX_RXDPC is off, spin_lock_bh() for CTFPOOL in + * linux_osl.c has to be changed to spin_lock_irqsave() because + * PKTGET/PKTFREE are no longer in bottom half. + * + * Right now we have another queue rpcq in wl_linux.c. Maybe we + * can eliminate that one to reduce the overhead. + * + * Enabling 2nd EP and DBUS_LINUX_RXDPC causing traffic from + * both EP's to be queued in the same rx queue. If we want + * RXDPC to work with 2nd EP. The EP for RPC call return + * should bypass the dpc and go directly up. + */ + +/* #define DBUS_LINUX_RXDPC */ + +/* Dbus histogram for ntxq, nrxq, dpc parameter tuning */ +/* #define DBUS_LINUX_HIST */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(USBOS_THREAD) || defined(USBOS_TX_THREAD) + +/** + * The usb-thread is designed to provide currency on multiprocessors and SMP linux kernels. On the + * dual cores platform, the WLAN driver, without threads, executed only on CPU0. The driver consumed + * almost of 100% on CPU0, while CPU1 remained idle. The behavior was observed on Broadcom's STB. + * + * The WLAN driver consumed most of CPU0 and not CPU1 because tasklets/queues, software irq, and + * hardware irq are executing from CPU0, only. CPU0 became the system's bottle-neck. TPUT is lower + * and system's responsiveness is slower. + * + * To improve system responsiveness and TPUT usb-thread was implemented. The system's threads could + * be scheduled to run on any core. One core could be processing data in the usb-layer and the other + * core could be processing data in the wl-layer. + * + * For further info see [WlThreadAndUsbThread] Twiki. + */ + +#include +#include +#include +#include +#include +#include +#endif /* USBOS_THREAD || USBOS_TX_THREAD */ + + + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) +#define KERNEL26 +#endif + +/** + * Starting with the 3.10 kernel release, dynamic PM support for USB is present whenever + * the kernel was built with CONFIG_PM_RUNTIME enabled. The CONFIG_USB_SUSPEND option has + * been eliminated. + */ +#if ((LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 21)) && defined(CONFIG_USB_SUSPEND)) \ + || ((LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)) && defined(CONFIG_PM_RUNTIME)) +/* For USB power management support, see Linux kernel: Documentation/usb/power-management.txt */ +#define USB_SUSPEND_AVAILABLE +#endif + +/* Define alternate fw/nvram paths used in Android */ +#ifdef OEM_ANDROID +#define CONFIG_ANDROID_BCMDHD_FW_PATH "broadcom/dhd/firmware/fw.bin.trx" +#define CONFIG_ANDROID_BCMDHD_NVRAM_PATH "broadcom/dhd/nvrams/nvm.txt" +#endif /* OEM_ANDROID */ + +static inline int usb_submit_urb_linux(struct urb *urb) +{ + +#ifdef BCM_MAX_URB_LEN + if (urb && (urb->transfer_buffer_length > BCM_MAX_URB_LEN)) { + DBUSERR(("URB transfer length=%d exceeded %d ra=%p\n", urb->transfer_buffer_length, + BCM_MAX_URB_LEN, __builtin_return_address(0))); + return DBUS_ERR; + } +#endif + +#ifdef KERNEL26 + return usb_submit_urb(urb, GFP_ATOMIC); +#else + return usb_submit_urb(urb); +#endif + +} + +#define USB_SUBMIT_URB(urb) usb_submit_urb_linux(urb) + +#ifdef KERNEL26 + +#define USB_ALLOC_URB() usb_alloc_urb(0, GFP_ATOMIC) +#define USB_UNLINK_URB(urb) (usb_kill_urb(urb)) +#define USB_FREE_URB(urb) (usb_free_urb(urb)) +#define USB_REGISTER() usb_register(&dbus_usbdev) +#define USB_DEREGISTER() usb_deregister(&dbus_usbdev) + +#ifdef USB_SUSPEND_AVAILABLE + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33)) +#define USB_AUTOPM_SET_INTERFACE(intf) usb_autopm_set_interface(intf) +#else +#define USB_ENABLE_AUTOSUSPEND(udev) usb_enable_autosuspend(udev) +#define USB_DISABLE_AUTOSUSPEND(udev) usb_disable_autosuspend(udev) +#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33)) */ + +#define USB_AUTOPM_GET_INTERFACE(intf) usb_autopm_get_interface(intf) +#define USB_AUTOPM_PUT_INTERFACE(intf) usb_autopm_put_interface(intf) +#define USB_AUTOPM_GET_INTERFACE_ASYNC(intf) usb_autopm_get_interface_async(intf) +#define USB_AUTOPM_PUT_INTERFACE_ASYNC(intf) usb_autopm_put_interface_async(intf) +#define USB_MARK_LAST_BUSY(dev) usb_mark_last_busy(dev) + +#else /* USB_SUSPEND_AVAILABLE */ + +#define USB_AUTOPM_GET_INTERFACE(intf) do {} while (0) +#define USB_AUTOPM_PUT_INTERFACE(intf) do {} while (0) +#define USB_AUTOPM_GET_INTERFACE_ASYNC(intf) do {} while (0) +#define USB_AUTOPM_PUT_INTERFACE_ASYNC(intf) do {} while (0) +#define USB_MARK_LAST_BUSY(dev) do {} while (0) +#endif /* USB_SUSPEND_AVAILABLE */ + +#define USB_CONTROL_MSG(dev, pipe, request, requesttype, value, index, data, size, timeout) \ + usb_control_msg((dev), (pipe), (request), (requesttype), (value), (index), \ + (data), (size), (timeout)) +#define USB_BULK_MSG(dev, pipe, data, len, actual_length, timeout) \ + usb_bulk_msg((dev), (pipe), (data), (len), (actual_length), (timeout)) +#define USB_BUFFER_ALLOC(dev, size, mem, dma) usb_buffer_alloc(dev, size, mem, dma) +#define USB_BUFFER_FREE(dev, size, data, dma) usb_buffer_free(dev, size, data, dma) + +#ifdef WL_URB_ZPKT +#define URB_QUEUE_BULK URB_ZERO_PACKET +#else +#define URB_QUEUE_BULK 0 +#endif /* WL_URB_ZPKT */ + +#define CALLBACK_ARGS struct urb *urb, struct pt_regs *regs +#define CALLBACK_ARGS_DATA urb, regs +#define CONFIGDESC(usb) (&((usb)->actconfig)->desc) +#define IFPTR(usb, idx) ((usb)->actconfig->interface[idx]) +#define IFALTS(usb, idx) (IFPTR((usb), (idx))->altsetting[0]) +#define IFDESC(usb, idx) IFALTS((usb), (idx)).desc +#define IFEPDESC(usb, idx, ep) (IFALTS((usb), (idx)).endpoint[ep]).desc + +#else /* KERNEL26 */ + +#define USB_ALLOC_URB() usb_alloc_urb(0) +#define USB_UNLINK_URB(urb) usb_unlink_urb(urb) +#define USB_FREE_URB(urb) (usb_free_urb(urb)) +#define USB_REGISTER() usb_register(&dbus_usbdev) +#define USB_DEREGISTER() usb_deregister(&dbus_usbdev) +#define USB_AUTOPM_GET_INTERFACE(intf) do {} while (0) +#define USB_AUTOPM_GET_INTERFACE_ASYNC(intf) do {} while (0) +#define USB_AUTOPM_PUT_INTERFACE_ASYNC(intf) do {} while (0) +#define USB_MARK_LAST_BUSY(dev) do {} while (0) + +#define USB_CONTROL_MSG(dev, pipe, request, requesttype, value, index, data, size, timeout) \ + usb_control_msg((dev), (pipe), (request), (requesttype), (value), (index), \ + (data), (size), (timeout)) +#define USB_BUFFER_ALLOC(dev, size, mem, dma) kmalloc(size, mem) +#define USB_BUFFER_FREE(dev, size, data, dma) kfree(data) + +#ifdef WL_URB_ZPKT +#define URB_QUEUE_BULK USB_QUEUE_BULK|URB_ZERO_PACKET +#else +#define URB_QUEUE_BULK 0 +#endif /* WL_URB_ZPKT */ + +#define CALLBACK_ARGS struct urb *urb +#define CALLBACK_ARGS_DATA urb +#define CONFIGDESC(usb) ((usb)->actconfig) +#define IFPTR(usb, idx) (&(usb)->actconfig->interface[idx]) +#define IFALTS(usb, idx) ((usb)->actconfig->interface[idx].altsetting[0]) +#define IFDESC(usb, idx) IFALTS((usb), (idx)) +#define IFEPDESC(usb, idx, ep) (IFALTS((usb), (idx)).endpoint[ep]) + + +#endif /* KERNEL26 */ + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)) +#define USB_SPEED_SUPER 5 +#endif /* #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)) */ + +#define CONTROL_IF 0 +#define BULK_IF 0 + +#ifdef BCMUSBDEV_COMPOSITE +#define USB_COMPIF_MAX 4 + +#define USB_CLASS_WIRELESS 0xe0 +#define USB_CLASS_MISC 0xef +#define USB_SUBCLASS_COMMON 0x02 +#define USB_PROTO_IAD 0x01 +#define USB_PROTO_VENDOR 0xff + +#define USB_QUIRK_NO_SET_INTF 0x04 /* device does not support set_interface */ +#endif /* BCMUSBDEV_COMPOSITE */ + +#define USB_SYNC_WAIT_TIMEOUT 300 /* ms */ + +/* Private data kept in skb */ +#define SKB_PRIV(skb, idx) (&((void **)skb->cb)[idx]) +#define SKB_PRIV_URB(skb) (*(struct urb **)SKB_PRIV(skb, 0)) + +#ifndef DBUS_USB_RXQUEUE_BATCH_ADD +/* items to add each time within limit */ +#define DBUS_USB_RXQUEUE_BATCH_ADD 8 +#endif + +#ifndef DBUS_USB_RXQUEUE_LOWER_WATERMARK +/* add a new batch req to rx queue when waiting item count reduce to this number */ +#define DBUS_USB_RXQUEUE_LOWER_WATERMARK 4 +#endif + +enum usbos_suspend_state { + USBOS_SUSPEND_STATE_DEVICE_ACTIVE = 0, /* Device is busy, won't allow suspend */ + USBOS_SUSPEND_STATE_SUSPEND_PENDING, /* Device is idle, can be suspended */ + /* Wating PM to suspend */ + USBOS_SUSPEND_STATE_SUSPENDED /* Device suspended */ +}; + +enum usbos_request_state { + USBOS_REQUEST_STATE_UNSCHEDULED = 0, /* USB TX request not scheduled */ + USBOS_REQUEST_STATE_SCHEDULED, /* USB TX request given to TX thread */ + USBOS_REQUEST_STATE_SUBMITTED /* USB TX request submitted */ +}; + +typedef struct { + uint32 notification; + uint32 reserved; +} intr_t; + +typedef struct { + dbus_pub_t *pub; + + void *cbarg; + dbus_intf_callbacks_t *cbs; + + /* Imported */ + struct usb_device *usb; /* USB device pointer from OS */ + struct urb *intr_urb; /* URB for interrupt endpoint */ + struct list_head req_rxfreeq; + struct list_head req_txfreeq; + struct list_head req_rxpostedq; /* Posted down to USB driver for RX */ + struct list_head req_txpostedq; /* Posted down to USB driver for TX */ + spinlock_t rxfree_lock; /* Lock for rx free list */ + spinlock_t txfree_lock; /* Lock for tx free list */ + spinlock_t rxposted_lock; /* Lock for rx posted list */ + spinlock_t txposted_lock; /* Lock for tx posted list */ + uint rx_pipe, tx_pipe, intr_pipe, rx_pipe2; /* Pipe numbers for USB I/O */ + uint rxbuf_len; + + struct list_head req_rxpendingq; /* RXDPC: Pending for dpc to send up */ + spinlock_t rxpending_lock; /* RXDPC: Lock for rx pending list */ + long dpc_pid; + struct semaphore dpc_sem; + struct completion dpc_exited; + int rxpending; + + struct urb *ctl_urb; + int ctl_in_pipe, ctl_out_pipe; + struct usb_ctrlrequest ctl_write; + struct usb_ctrlrequest ctl_read; + struct semaphore ctl_lock; /* Lock for CTRL transfers via tx_thread */ +#ifdef USBOS_TX_THREAD + enum usbos_request_state ctl_state; +#endif /* USBOS_TX_THREAD */ + + spinlock_t rxlock; /* Lock for rxq management */ + spinlock_t txlock; /* Lock for txq management */ + + int intr_size; /* Size of interrupt message */ + int interval; /* Interrupt polling interval */ + intr_t intr; /* Data buffer for interrupt endpoint */ + + int maxps; + atomic_t txposted; + atomic_t rxposted; + atomic_t txallocated; + atomic_t rxallocated; + bool rxctl_deferrespok; /* Get a response for setup from dongle */ + + wait_queue_head_t wait; + bool waitdone; + int sync_urb_status; + + struct urb *blk_urb; /* Used for downloading embedded image */ + +#ifdef USBOS_THREAD + spinlock_t ctrl_lock; + spinlock_t usbos_list_lock; + struct list_head usbos_list; + struct list_head usbos_free_list; + atomic_t usbos_list_cnt; + wait_queue_head_t usbos_queue_head; + struct task_struct *usbos_kt; +#endif /* USBOS_THREAD */ + +#ifdef USBOS_TX_THREAD + spinlock_t usbos_tx_list_lock; + struct list_head usbos_tx_list; + wait_queue_head_t usbos_tx_queue_head; + struct task_struct *usbos_tx_kt; +#endif /* USBOS_TX_THREAD */ + + struct dma_pool *qtd_pool; /* QTD pool for USB optimization only */ + int tx_ep, rx_ep, rx2_ep; /* EPs for USB optimization */ + struct usb_device *usb_device; /* USB device for optimization */ +} usbos_info_t; + +typedef struct urb_req { + void *pkt; + int buf_len; + struct urb *urb; + void *arg; + usbos_info_t *usbinfo; + struct list_head urb_list; +} urb_req_t; + +#ifdef USBOS_THREAD +typedef struct usbos_list_entry { + struct list_head list; /* must be first */ + void *urb_context; + int urb_length; + int urb_status; +} usbos_list_entry_t; + +static void* dbus_usbos_thread_init(usbos_info_t *usbos_info); +static void dbus_usbos_thread_deinit(usbos_info_t *usbos_info); +static void dbus_usbos_dispatch_schedule(CALLBACK_ARGS); +static int dbus_usbos_thread_func(void *data); +#endif /* USBOS_THREAD */ + +#ifdef USBOS_TX_THREAD +void* dbus_usbos_tx_thread_init(usbos_info_t *usbos_info); +void dbus_usbos_tx_thread_deinit(usbos_info_t *usbos_info); +int dbus_usbos_tx_thread_func(void *data); +#endif /* USBOS_TX_THREAD */ + +/* Shared Function prototypes */ +bool dbus_usbos_dl_cmd(usbos_info_t *usbinfo, uint8 cmd, void *buffer, int buflen); +int dbus_usbos_wait(usbos_info_t *usbinfo, uint16 ms); +bool dbus_usbos_dl_send_bulk(usbos_info_t *usbinfo, void *buffer, int len); +int dbus_write_membytes(usbos_info_t *usbinfo, bool set, uint32 address, uint8 *data, uint size); + +/* Local function prototypes */ +static void dbus_usbos_send_complete(CALLBACK_ARGS); +static void dbus_usbos_recv_complete(CALLBACK_ARGS); +static int dbus_usbos_errhandler(void *bus, int err); +static int dbus_usbos_state_change(void *bus, int state); +static void dbusos_stop(usbos_info_t *usbos_info); + +#ifdef KERNEL26 +static int dbus_usbos_probe(struct usb_interface *intf, const struct usb_device_id *id); +static void dbus_usbos_disconnect(struct usb_interface *intf); +#if defined(USB_SUSPEND_AVAILABLE) +static int dbus_usbos_resume(struct usb_interface *intf); +static int dbus_usbos_suspend(struct usb_interface *intf, pm_message_t message); +/* at the moment, used for full dongle host driver only */ +static int dbus_usbos_reset_resume(struct usb_interface *intf); +#endif /* USB_SUSPEND_AVAILABLE */ +#else /* KERNEL26 */ +static void *dbus_usbos_probe(struct usb_device *usb, unsigned int ifnum, + const struct usb_device_id *id); +static void dbus_usbos_disconnect(struct usb_device *usb, void *ptr); +#endif /* KERNEL26 */ + + +/** + * have to disable missing-field-initializers warning as last element {} triggers it + * and different versions of kernel have different number of members so it is impossible + * to specify the initializer. BTW issuing the warning here is bug og GCC as universal + * zero {0} specified in C99 standard as correct way of initialization of struct to all zeros + */ +#if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == \ + 4 && __GNUC_MINOR__ >= 6)) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#endif + +static struct usb_device_id devid_table[] = { + { USB_DEVICE(BCM_DNGL_VID, 0x0000) }, /* Configurable via register() */ +#if defined(BCM_REQUEST_FW) + { USB_DEVICE(BCM_DNGL_VID, BCM_DNGL_BL_PID_4328) }, + { USB_DEVICE(BCM_DNGL_VID, BCM_DNGL_BL_PID_4322) }, + { USB_DEVICE(BCM_DNGL_VID, BCM_DNGL_BL_PID_4319) }, + { USB_DEVICE(BCM_DNGL_VID, BCM_DNGL_BL_PID_43236) }, + { USB_DEVICE(BCM_DNGL_VID, BCM_DNGL_BL_PID_43143) }, + { USB_DEVICE(BCM_DNGL_VID, BCM_DNGL_BL_PID_43242) }, + { USB_DEVICE(BCM_DNGL_VID, BCM_DNGL_BL_PID_4360) }, + { USB_DEVICE(BCM_DNGL_VID, BCM_DNGL_BL_PID_4350) }, + { USB_DEVICE(BCM_DNGL_VID, BCM_DNGL_BL_PID_43569) }, +#endif +#ifdef EXTENDED_VID_PID + EXTENDED_VID_PID, +#endif /* EXTENDED_VID_PID */ + { USB_DEVICE(BCM_DNGL_VID, BCM_DNGL_BDC_PID) }, /* Default BDC */ + { } +}; + +#if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == \ + 4 && __GNUC_MINOR__ >= 6)) +#pragma GCC diagnostic pop +#endif + +MODULE_DEVICE_TABLE(usb, devid_table); + +/** functions called by the Linux kernel USB subsystem */ +static struct usb_driver dbus_usbdev = { + name: "dbus_usbdev", + probe: dbus_usbos_probe, + disconnect: dbus_usbos_disconnect, + id_table: devid_table, +#if defined(USB_SUSPEND_AVAILABLE) + suspend: dbus_usbos_suspend, + resume: dbus_usbos_resume, + reset_resume: dbus_usbos_reset_resume, + /* Linux USB core will allow autosuspend for devices bound to this driver */ + supports_autosuspend: 1 +#endif /* USB_SUSPEND_AVAILABLE */ +}; + +/** + * This stores USB info during Linux probe callback since attach() is not called yet at this point + */ +typedef struct { + void *usbos_info; + struct usb_device *usb; /* USB device pointer from OS */ + uint rx_pipe; /* Pipe numbers for USB I/O */ + uint tx_pipe; /* Pipe numbers for USB I/O */ + uint intr_pipe; /* Pipe numbers for USB I/O */ + uint rx_pipe2; /* Pipe numbers for USB I/O */ + int intr_size; /* Size of interrupt message */ + int interval; /* Interrupt polling interval */ + bool dldone; + int vid; + int pid; + bool dereged; + bool disc_cb_done; + DEVICE_SPEED device_speed; + enum usbos_suspend_state suspend_state; + struct usb_interface *intf; +} probe_info_t; + +/* + * USB Linux dbus_intf_t + */ +static void *dbus_usbos_intf_attach(dbus_pub_t *pub, void *cbarg, dbus_intf_callbacks_t *cbs); +static void dbus_usbos_intf_detach(dbus_pub_t *pub, void *info); +static int dbus_usbos_intf_send_irb(void *bus, dbus_irb_tx_t *txirb); +static int dbus_usbos_intf_recv_irb(void *bus, dbus_irb_rx_t *rxirb); +static int dbus_usbos_intf_recv_irb_from_ep(void *bus, dbus_irb_rx_t *rxirb, uint32 ep_idx); +static int dbus_usbos_intf_cancel_irb(void *bus, dbus_irb_tx_t *txirb); +static int dbus_usbos_intf_send_ctl(void *bus, uint8 *buf, int len); +static int dbus_usbos_intf_recv_ctl(void *bus, uint8 *buf, int len); +static int dbus_usbos_intf_get_attrib(void *bus, dbus_attrib_t *attrib); +static int dbus_usbos_intf_up(void *bus); +static int dbus_usbos_intf_down(void *bus); +static int dbus_usbos_intf_stop(void *bus); +static int dbus_usbos_readreg(void *bus, uint32 regaddr, int datalen, uint32 *value); +extern int dbus_usbos_loopback_tx(void *usbos_info_ptr, int cnt, int size); +int dbus_usbos_writereg(void *bus, uint32 regaddr, int datalen, uint32 data); +static int dbus_usbos_intf_set_config(void *bus, dbus_config_t *config); +static bool dbus_usbos_intf_recv_needed(void *bus); +static void *dbus_usbos_intf_exec_rxlock(void *bus, exec_cb_t cb, struct exec_parms *args); +static void *dbus_usbos_intf_exec_txlock(void *bus, exec_cb_t cb, struct exec_parms *args); +#ifdef BCMUSBDEV_COMPOSITE +static int dbus_usbos_intf_wlan(struct usb_device *usb); +#endif /* BCMUSBDEV_COMPOSITE */ + +/** functions called by dbus_usb.c */ +static dbus_intf_t dbus_usbos_intf = { + .attach = dbus_usbos_intf_attach, + .detach = dbus_usbos_intf_detach, + .up = dbus_usbos_intf_up, + .down = dbus_usbos_intf_down, + .send_irb = dbus_usbos_intf_send_irb, + .recv_irb = dbus_usbos_intf_recv_irb, + .cancel_irb = dbus_usbos_intf_cancel_irb, + .send_ctl = dbus_usbos_intf_send_ctl, + .recv_ctl = dbus_usbos_intf_recv_ctl, + .get_stats = NULL, + .get_attrib = dbus_usbos_intf_get_attrib, + .remove = NULL, + .resume = NULL, + .suspend = NULL, + .stop = dbus_usbos_intf_stop, + .reset = NULL, + .pktget = NULL, + .pktfree = NULL, + .iovar_op = NULL, + .dump = NULL, + .set_config = dbus_usbos_intf_set_config, + .get_config = NULL, + .device_exists = NULL, + .dlneeded = NULL, + .dlstart = NULL, + .dlrun = NULL, + .recv_needed = dbus_usbos_intf_recv_needed, + .exec_rxlock = dbus_usbos_intf_exec_rxlock, + .exec_txlock = dbus_usbos_intf_exec_txlock, + + .tx_timer_init = NULL, + .tx_timer_start = NULL, + .tx_timer_stop = NULL, + + .sched_dpc = NULL, + .lock = NULL, + .unlock = NULL, + .sched_probe_cb = NULL, + + .shutdown = NULL, + + .recv_stop = NULL, + .recv_resume = NULL, + + .recv_irb_from_ep = dbus_usbos_intf_recv_irb_from_ep, + .readreg = dbus_usbos_readreg +}; + +static probe_info_t g_probe_info; +static probe_cb_t probe_cb = NULL; +static disconnect_cb_t disconnect_cb = NULL; +static void *probe_arg = NULL; +static void *disc_arg = NULL; + + + +static volatile int loopback_rx_cnt, loopback_tx_cnt; +int loopback_size; +bool is_loopback_pkt(void *buf); +int matches_loopback_pkt(void *buf); + +/** + * multiple code paths in this file dequeue a URB request, this function makes sure that it happens + * in a concurrency save manner. Don't call this from a sleepable process context. + */ +static urb_req_t * BCMFASTPATH +dbus_usbos_qdeq(struct list_head *urbreq_q, spinlock_t *lock) +{ + unsigned long flags; + urb_req_t *req; + + ASSERT(urbreq_q != NULL); + + spin_lock_irqsave(lock, flags); + + if (list_empty(urbreq_q)) { + req = NULL; + } else { + ASSERT(urbreq_q->next != NULL); + ASSERT(urbreq_q->next != urbreq_q); +#if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif + req = list_entry(urbreq_q->next, urb_req_t, urb_list); +#if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + list_del_init(&req->urb_list); + } + + spin_unlock_irqrestore(lock, flags); + + return req; +} + +static void BCMFASTPATH +dbus_usbos_qenq(struct list_head *urbreq_q, urb_req_t *req, spinlock_t *lock) +{ + unsigned long flags; + + spin_lock_irqsave(lock, flags); + + list_add_tail(&req->urb_list, urbreq_q); + + spin_unlock_irqrestore(lock, flags); +} + +/** + * multiple code paths in this file remove a URB request from a list, this function makes sure that + * it happens in a concurrency save manner. Don't call this from a sleepable process context. + * Is quite similar to dbus_usbos_qdeq(), I wonder why this function is needed. + */ +static void +dbus_usbos_req_del(urb_req_t *req, spinlock_t *lock) +{ + unsigned long flags; + + spin_lock_irqsave(lock, flags); + + list_del_init(&req->urb_list); + + spin_unlock_irqrestore(lock, flags); +} + + +/** + * Driver requires a pool of URBs to operate. This function is called during + * initialization (attach phase), allocates a number of URBs, and puts them + * on the free (req_rxfreeq and req_txfreeq) queue + */ +static int +dbus_usbos_urbreqs_alloc(usbos_info_t *usbos_info, uint32 count, bool is_rx) +{ + int i; + int allocated = 0; + int err = DBUS_OK; + + for (i = 0; i < count; i++) { + urb_req_t *req; + + req = MALLOC(usbos_info->pub->osh, sizeof(urb_req_t)); + if (req == NULL) { + DBUSERR(("%s: MALLOC req failed\n", __FUNCTION__)); + err = DBUS_ERR_NOMEM; + goto fail; + } + bzero(req, sizeof(urb_req_t)); + + req->urb = USB_ALLOC_URB(); + if (req->urb == NULL) { + DBUSERR(("%s: USB_ALLOC_URB req->urb failed\n", __FUNCTION__)); + err = DBUS_ERR_NOMEM; + goto fail; + } + + INIT_LIST_HEAD(&req->urb_list); + + if (is_rx) { +#if defined(BCM_RPC_NOCOPY) || defined(BCM_RPC_RXNOCOPY) + /* don't allocate now. Do it on demand */ + req->pkt = NULL; +#else + /* pre-allocate buffers never to be released */ + req->pkt = MALLOC(usbos_info->pub->osh, usbos_info->rxbuf_len); + if (req->pkt == NULL) { + DBUSERR(("%s: MALLOC req->pkt failed\n", __FUNCTION__)); + err = DBUS_ERR_NOMEM; + goto fail; + } +#endif + req->buf_len = usbos_info->rxbuf_len; + dbus_usbos_qenq(&usbos_info->req_rxfreeq, req, &usbos_info->rxfree_lock); + } else { + req->buf_len = 0; + dbus_usbos_qenq(&usbos_info->req_txfreeq, req, &usbos_info->txfree_lock); + } + allocated++; + continue; + +fail: + if (req) { + if (is_rx && req->pkt) { +#if defined(BCM_RPC_NOCOPY) || defined(BCM_RPC_RXNOCOPY) + /* req->pkt is NULL in "NOCOPY" mode */ +#else + MFREE(usbos_info->pub->osh, req->pkt, req->buf_len); +#endif + } + if (req->urb) { + USB_FREE_URB(req->urb); + } + MFREE(usbos_info->pub->osh, req, sizeof(urb_req_t)); + } + break; + } + + atomic_add(allocated, is_rx ? &usbos_info->rxallocated : &usbos_info->txallocated); + + if (is_rx) { + DBUSTRACE(("%s: add %d (total %d) rx buf, each has %d bytes\n", __FUNCTION__, + allocated, atomic_read(&usbos_info->rxallocated), usbos_info->rxbuf_len)); + } else { + DBUSTRACE(("%s: add %d (total %d) tx req\n", __FUNCTION__, + allocated, atomic_read(&usbos_info->txallocated))); + } + + return err; +} /* dbus_usbos_urbreqs_alloc */ + +/** Typically called during detach or when attach failed. Don't call until all URBs unlinked */ +static int +dbus_usbos_urbreqs_free(usbos_info_t *usbos_info, bool is_rx) +{ + int rtn = 0; + urb_req_t *req; + struct list_head *req_q; + spinlock_t *lock; + + if (is_rx) { + req_q = &usbos_info->req_rxfreeq; + lock = &usbos_info->rxfree_lock; + } else { + req_q = &usbos_info->req_txfreeq; + lock = &usbos_info->txfree_lock; + } + while ((req = dbus_usbos_qdeq(req_q, lock)) != NULL) { + + if (is_rx) { + if (req->pkt) { + /* We do MFREE instead of PKTFREE because the pkt has been + * converted to native already + */ + MFREE(usbos_info->pub->osh, req->pkt, req->buf_len); + req->pkt = NULL; + req->buf_len = 0; + } + } else { + /* sending req should not be assigned pkt buffer */ + ASSERT(req->pkt == NULL); + } + + if (req->urb) { + USB_FREE_URB(req->urb); + req->urb = NULL; + } + MFREE(usbos_info->pub->osh, req, sizeof(urb_req_t)); + + rtn++; + } + return rtn; +} /* dbus_usbos_urbreqs_free */ + +/** + * called by Linux kernel on URB completion. Upper DBUS layer (dbus_usb.c) has to be notified of + * send completion. + */ +void +dbus_usbos_send_complete(CALLBACK_ARGS) +{ + urb_req_t *req = urb->context; + dbus_irb_tx_t *txirb = req->arg; + usbos_info_t *usbos_info = req->usbinfo; + unsigned long flags; + int status = DBUS_OK; + int txposted; + + USB_AUTOPM_PUT_INTERFACE_ASYNC(g_probe_info.intf); + + spin_lock_irqsave(&usbos_info->txlock, flags); + + dbus_usbos_req_del(req, &usbos_info->txposted_lock); + txposted = atomic_dec_return(&usbos_info->txposted); + if (unlikely (txposted < 0)) { + DBUSERR(("%s ERROR: txposted is negative (%d)!!\n", __FUNCTION__, txposted)); + } + spin_unlock_irqrestore(&usbos_info->txlock, flags); + + if (unlikely (urb->status)) { + status = DBUS_ERR_TXFAIL; + DBUSTRACE(("txfail status %d\n", urb->status)); + } + +#if defined(BCM_RPC_NOCOPY) || defined(BCM_RPC_RXNOCOPY) + /* sending req should not be assigned pkt buffer */ + ASSERT(req->pkt == NULL); +#endif + /* txirb should always be set, except for ZLP. ZLP is reusing this callback function. */ + if (txirb != NULL) { + if (txirb->send_buf != NULL) { + MFREE(usbos_info->pub->osh, txirb->send_buf, req->buf_len); + txirb->send_buf = NULL; + req->buf_len = 0; + } + if (likely (usbos_info->cbarg && usbos_info->cbs)) { + if (likely (usbos_info->cbs->send_irb_complete != NULL)) + usbos_info->cbs->send_irb_complete(usbos_info->cbarg, txirb, status); + } + } + + dbus_usbos_qenq(&usbos_info->req_txfreeq, req, &usbos_info->txfree_lock); +} /* dbus_usbos_send_complete */ + +/** + * In order to receive USB traffic from the dongle, we need to supply the Linux kernel with a free + * URB that is going to contain received data. + */ +static int BCMFASTPATH +dbus_usbos_recv_urb_submit(usbos_info_t *usbos_info, dbus_irb_rx_t *rxirb, uint32 ep_idx) +{ + urb_req_t *req; + int ret = DBUS_OK; + unsigned long flags; + void *p; + uint rx_pipe; + int rxposted; + + BCM_REFERENCE(rxposted); + + if (!(req = dbus_usbos_qdeq(&usbos_info->req_rxfreeq, &usbos_info->rxfree_lock))) { + DBUSTRACE(("%s No free URB!\n", __FUNCTION__)); + return DBUS_ERR_RXDROP; + } + + spin_lock_irqsave(&usbos_info->rxlock, flags); + +#if defined(BCM_RPC_NOCOPY) || defined(BCM_RPC_RXNOCOPY) + req->pkt = rxirb->pkt = PKTGET(usbos_info->pub->osh, req->buf_len, FALSE); + if (!rxirb->pkt) { + DBUSERR(("%s: PKTGET failed\n", __FUNCTION__)); + dbus_usbos_qenq(&usbos_info->req_rxfreeq, req, &usbos_info->rxfree_lock); + ret = DBUS_ERR_RXDROP; + goto fail; + } + /* consider the packet "native" so we don't count it as MALLOCED in the osl */ + PKTTONATIVE(usbos_info->pub->osh, req->pkt); + rxirb->buf = NULL; + p = PKTDATA(usbos_info->pub->osh, req->pkt); +#else + if (req->buf_len != usbos_info->rxbuf_len) { + ASSERT(req->pkt); + MFREE(usbos_info->pub->osh, req->pkt, req->buf_len); + DBUSTRACE(("%s: replace rx buff: old len %d, new len %d\n", __FUNCTION__, + req->buf_len, usbos_info->rxbuf_len)); + req->buf_len = 0; + req->pkt = MALLOC(usbos_info->pub->osh, usbos_info->rxbuf_len); + if (req->pkt == NULL) { + DBUSERR(("%s: MALLOC req->pkt failed\n", __FUNCTION__)); + ret = DBUS_ERR_NOMEM; + goto fail; + } + req->buf_len = usbos_info->rxbuf_len; + } + rxirb->buf = req->pkt; + p = rxirb->buf; +#endif /* defined(BCM_RPC_NOCOPY) */ + rxirb->buf_len = req->buf_len; + req->usbinfo = usbos_info; + req->arg = rxirb; + if (ep_idx == 0) { + rx_pipe = usbos_info->rx_pipe; + } else { + rx_pipe = usbos_info->rx_pipe2; + ASSERT(usbos_info->rx_pipe2); + } + /* Prepare the URB */ + usb_fill_bulk_urb(req->urb, usbos_info->usb, rx_pipe, + p, + rxirb->buf_len, + (usb_complete_t)dbus_usbos_recv_complete, req); + req->urb->transfer_flags |= URB_QUEUE_BULK; + + if ((ret = USB_SUBMIT_URB(req->urb))) { + DBUSERR(("%s USB_SUBMIT_URB failed. status %d\n", __FUNCTION__, ret)); + dbus_usbos_qenq(&usbos_info->req_rxfreeq, req, &usbos_info->rxfree_lock); + ret = DBUS_ERR_RXFAIL; + goto fail; + } + rxposted = atomic_inc_return(&usbos_info->rxposted); + + dbus_usbos_qenq(&usbos_info->req_rxpostedq, req, &usbos_info->rxposted_lock); +fail: + spin_unlock_irqrestore(&usbos_info->rxlock, flags); + return ret; +} /* dbus_usbos_recv_urb_submit */ + + +/** + * Called by worked thread when a 'receive URB' completed or Linux kernel when it returns a URB to + * this driver. + */ +static void BCMFASTPATH +dbus_usbos_recv_complete_handle(urb_req_t *req, int len, int status) +{ + dbus_irb_rx_t *rxirb = req->arg; + usbos_info_t *usbos_info = req->usbinfo; + unsigned long flags; + int rxallocated, rxposted; + int dbus_status = DBUS_OK; + bool killed = (g_probe_info.suspend_state == USBOS_SUSPEND_STATE_SUSPEND_PENDING) ? 1 : 0; + + spin_lock_irqsave(&usbos_info->rxlock, flags); + dbus_usbos_req_del(req, &usbos_info->rxposted_lock); + rxposted = atomic_dec_return(&usbos_info->rxposted); + rxallocated = atomic_read(&usbos_info->rxallocated); + spin_unlock_irqrestore(&usbos_info->rxlock, flags); + + if ((rxallocated < usbos_info->pub->nrxq) && (!status) && + (rxposted == DBUS_USB_RXQUEUE_LOWER_WATERMARK)) { + DBUSTRACE(("%s: need more rx buf: rxallocated %d rxposted %d!\n", + __FUNCTION__, rxallocated, rxposted)); + dbus_usbos_urbreqs_alloc(usbos_info, + MIN(DBUS_USB_RXQUEUE_BATCH_ADD, + usbos_info->pub->nrxq - rxallocated), TRUE); + } + + /* Handle errors */ + if (status) { + /* + * Linux 2.4 disconnect: -ENOENT or -EILSEQ for CRC error; rmmod: -ENOENT + * Linux 2.6 disconnect: -EPROTO, rmmod: -ESHUTDOWN + */ + if ((status == -ENOENT && (!killed))|| status == -ESHUTDOWN) { + /* NOTE: unlink() can not be called from URB callback(). + * Do not call dbusos_stop() here. + */ + DBUSTRACE(("%s rx error %d\n", __FUNCTION__, status)); + dbus_usbos_state_change(usbos_info, DBUS_STATE_DOWN); + } else if (status == -EPROTO) { + DBUSTRACE(("%s rx error %d\n", __FUNCTION__, status)); + } else if (killed && (status == -EHOSTUNREACH || status == -ENOENT)) { + /* Device is suspended */ + } else { + DBUSTRACE(("%s rx error %d\n", __FUNCTION__, status)); + dbus_usbos_errhandler(usbos_info, DBUS_ERR_RXFAIL); + } + + /* On error, don't submit more URBs yet */ + rxirb->buf = NULL; + rxirb->actual_len = 0; + dbus_status = DBUS_ERR_RXFAIL; + goto fail; + } + + /* Make the skb represent the received urb */ + rxirb->actual_len = len; + + if (rxirb->actual_len < sizeof(uint32)) { + DBUSTRACE(("small pkt len %d, process as ZLP\n", rxirb->actual_len)); + dbus_status = DBUS_ERR_RXZLP; + } + +fail: +#if defined(BCM_RPC_NOCOPY) || defined(BCM_RPC_RXNOCOPY) + /* detach the packet from the queue */ + req->pkt = NULL; +#endif /* BCM_RPC_NOCOPY || BCM_RPC_RXNOCOPY */ + + if (usbos_info->cbarg && usbos_info->cbs) { + if (usbos_info->cbs->recv_irb_complete) { + usbos_info->cbs->recv_irb_complete(usbos_info->cbarg, rxirb, dbus_status); + } + } + + dbus_usbos_qenq(&usbos_info->req_rxfreeq, req, &usbos_info->rxfree_lock); + + /* Mark the interface as busy to reset USB autosuspend timer */ + USB_MARK_LAST_BUSY(usbos_info->usb); +} /* dbus_usbos_recv_complete_handle */ + +/** called by Linux kernel when it returns a URB to this driver */ +static void +dbus_usbos_recv_complete(CALLBACK_ARGS) +{ +#ifdef USBOS_THREAD + dbus_usbos_dispatch_schedule(CALLBACK_ARGS_DATA); +#else /* !USBOS_THREAD */ + dbus_usbos_recv_complete_handle(urb->context, urb->actual_length, urb->status); +#endif /* USBOS_THREAD */ +} + + +/** + * If Linux notifies our driver that a control read or write URB has completed, we should notify + * the DBUS layer above us (dbus_usb.c in this case). + */ +static void +dbus_usbos_ctl_complete(usbos_info_t *usbos_info, int type, int urbstatus) +{ + int status = DBUS_ERR; + + if (usbos_info == NULL) + return; + + switch (urbstatus) { + case 0: + status = DBUS_OK; + break; + case -EINPROGRESS: + case -ENOENT: + default: +#ifdef INTR_EP_ENABLE + DBUSERR(("%s:%d fail status %d bus:%d susp:%d intr:%d ctli:%d ctlo:%d\n", + __FUNCTION__, type, urbstatus, + usbos_info->pub->busstate, g_probe_info.suspend_state, + usbos_info->intr_urb_submitted, usbos_info->ctlin_urb_submitted, + usbos_info->ctlout_urb_submitted)); +#else + DBUSERR(("%s: failed with status %d\n", __FUNCTION__, urbstatus)); + status = DBUS_ERR; + break; +#endif /* INTR_EP_ENABLE */ + } + + if (usbos_info->cbarg && usbos_info->cbs) { + if (usbos_info->cbs->ctl_complete) + usbos_info->cbs->ctl_complete(usbos_info->cbarg, type, status); + } +} + +/** called by Linux */ +static void +dbus_usbos_ctlread_complete(CALLBACK_ARGS) +{ + usbos_info_t *usbos_info = (usbos_info_t *)urb->context; + + ASSERT(urb); + usbos_info = (usbos_info_t *)urb->context; + + dbus_usbos_ctl_complete(usbos_info, DBUS_CBCTL_READ, urb->status); + +#ifdef USBOS_THREAD + if (usbos_info->rxctl_deferrespok) { + usbos_info->ctl_read.bRequestType = USB_DIR_IN | USB_TYPE_CLASS | + USB_RECIP_INTERFACE; + usbos_info->ctl_read.bRequest = 1; + } +#endif + + up(&usbos_info->ctl_lock); + + USB_AUTOPM_PUT_INTERFACE_ASYNC(g_probe_info.intf); +} + +/** called by Linux */ +static void +dbus_usbos_ctlwrite_complete(CALLBACK_ARGS) +{ + usbos_info_t *usbos_info = (usbos_info_t *)urb->context; + + ASSERT(urb); + usbos_info = (usbos_info_t *)urb->context; + + dbus_usbos_ctl_complete(usbos_info, DBUS_CBCTL_WRITE, urb->status); + +#ifdef USBOS_TX_THREAD + usbos_info->ctl_state = USBOS_REQUEST_STATE_UNSCHEDULED; +#endif /* USBOS_TX_THREAD */ + + up(&usbos_info->ctl_lock); + + USB_AUTOPM_PUT_INTERFACE_ASYNC(g_probe_info.intf); +} + +#ifdef INTR_EP_ENABLE +/** called by Linux */ +static void +dbus_usbos_intr_complete(CALLBACK_ARGS) +{ + usbos_info_t *usbos_info = (usbos_info_t *)urb->context; + bool killed = (g_probe_info.suspend_state == USBOS_SUSPEND_STATE_SUSPEND_PENDING) ? 1 : 0; + + if (usbos_info == NULL || usbos_info->pub == NULL) + return; + if ((urb->status == -ENOENT && (!killed)) || urb->status == -ESHUTDOWN || + urb->status == -ENODEV) { + dbus_usbos_state_change(usbos_info, DBUS_STATE_DOWN); + } + + if (usbos_info->pub->busstate == DBUS_STATE_DOWN) { + DBUSERR(("%s: intr cb when DBUS down, ignoring\n", __FUNCTION__)); + return; + } + dbus_usbos_ctl_complete(usbos_info, DBUS_CBINTR_POLL, urb->status); +} +#endif /* INTR_EP_ENABLE */ + +/** + * when the bus is going to sleep or halt, the Linux kernel requires us to take ownership of our + * URBs again. Multiple code paths in this file require a list of URBs to be cancelled in a + * concurrency save manner. + */ +static void +dbus_usbos_unlink(struct list_head *urbreq_q, spinlock_t *lock) +{ + urb_req_t *req; + + /* dbus_usbos_recv_complete() adds req back to req_freeq */ + while ((req = dbus_usbos_qdeq(urbreq_q, lock)) != NULL) { + ASSERT(req->urb != NULL); + USB_UNLINK_URB(req->urb); + } +} + +/** multiple code paths in this file require the bus to stop */ +static void +dbus_usbos_cancel_all_urbs(usbos_info_t *usbos_info) +{ + int rxposted, txposted; + + DBUSTRACE(("%s: unlink all URBs\n", __FUNCTION__)); + +#ifdef USBOS_TX_THREAD + usbos_info->ctl_state = USBOS_REQUEST_STATE_UNSCHEDULED; + + /* Yield the CPU to TX thread so all pending requests are submitted */ + while (!list_empty(&usbos_info->usbos_tx_list)) { + wake_up_interruptible(&usbos_info->usbos_tx_queue_head); + OSL_SLEEP(10); + } +#endif /* USBOS_TX_THREAD */ + + /* tell Linux kernel to cancel a single intr, ctl and blk URB */ + if (usbos_info->intr_urb) + USB_UNLINK_URB(usbos_info->intr_urb); + if (usbos_info->ctl_urb) + USB_UNLINK_URB(usbos_info->ctl_urb); + if (usbos_info->blk_urb) + USB_UNLINK_URB(usbos_info->blk_urb); + + dbus_usbos_unlink(&usbos_info->req_txpostedq, &usbos_info->txposted_lock); + dbus_usbos_unlink(&usbos_info->req_rxpostedq, &usbos_info->rxposted_lock); + + /* Wait until the callbacks for all submitted URBs have been called, because the + * handler needs to know is an USB suspend is in progress. + */ + SPINWAIT((atomic_read(&usbos_info->txposted) != 0 || + atomic_read(&usbos_info->rxposted) != 0), 10000); + + txposted = atomic_read(&usbos_info->txposted); + rxposted = atomic_read(&usbos_info->rxposted); + if (txposted != 0 || rxposted != 0) { + DBUSERR(("%s ERROR: REQs posted, rx=%d tx=%d!\n", + __FUNCTION__, rxposted, txposted)); + } +} /* dbus_usbos_cancel_all_urbs */ + +/** multiple code paths require the bus to stop */ +static void +dbusos_stop(usbos_info_t *usbos_info) +{ + urb_req_t *req; + int rxposted; + req = NULL; + BCM_REFERENCE(req); + + ASSERT(usbos_info); + + dbus_usbos_state_change(usbos_info, DBUS_STATE_DOWN); + + dbus_usbos_cancel_all_urbs(usbos_info); + +#ifdef USBOS_THREAD + /* yield the CPU to rx packet thread */ + while (1) { + if (atomic_read(&usbos_info->usbos_list_cnt) <= 0) break; + wake_up_interruptible(&usbos_info->usbos_queue_head); + OSL_SLEEP(3); + } +#endif /* USBOS_THREAD */ + + rxposted = atomic_read(&usbos_info->rxposted); + if (rxposted > 0) { + DBUSERR(("%s ERROR: rx REQs posted=%d in stop!\n", __FUNCTION__, + rxposted)); + } + + ASSERT(atomic_read(&usbos_info->txposted) == 0 && rxposted == 0); + +} /* dbusos_stop */ + +#if defined(USB_SUSPEND_AVAILABLE) + +/** + * Linux kernel sports a 'USB auto suspend' feature. See: http://lwn.net/Articles/373550/ + * The suspend method is called by the Linux kernel to warn the driver that the device is going to + * be suspended. If the driver returns a negative error code, the suspend will be aborted. If the + * driver returns 0, it must cancel all outstanding URBs (usb_kill_urb()) and not submit any more. + */ +static int +dbus_usbos_suspend(struct usb_interface *intf, + pm_message_t message) +{ + DBUSERR(("%s suspend state: %d\n", __FUNCTION__, g_probe_info.suspend_state)); + /* DHD for full dongle model */ + g_probe_info.suspend_state = USBOS_SUSPEND_STATE_SUSPEND_PENDING; + dbus_usbos_state_change((usbos_info_t*)g_probe_info.usbos_info, DBUS_STATE_SLEEP); + dbus_usbos_cancel_all_urbs((usbos_info_t*)g_probe_info.usbos_info); + g_probe_info.suspend_state = USBOS_SUSPEND_STATE_SUSPENDED; + + return 0; +} + +/** + * The resume method is called to tell the driver that the device has been resumed and the driver + * can return to normal operation. URBs may once more be submitted. + */ +static int dbus_usbos_resume(struct usb_interface *intf) +{ + DBUSERR(("%s Device resumed\n", __FUNCTION__)); + + dbus_usbos_state_change((usbos_info_t*)g_probe_info.usbos_info, DBUS_STATE_UP); + g_probe_info.suspend_state = USBOS_SUSPEND_STATE_DEVICE_ACTIVE; + return 0; +} + +/** +* This function is directly called by the Linux kernel, when the suspended device has been reset +* instead of being resumed +*/ +static int dbus_usbos_reset_resume(struct usb_interface *intf) +{ + DBUSERR(("%s Device reset resumed\n", __FUNCTION__)); + + /* The device may have lost power, so a firmware download may be required */ + dbus_usbos_state_change((usbos_info_t*)g_probe_info.usbos_info, DBUS_STATE_DL_NEEDED); + g_probe_info.suspend_state = USBOS_SUSPEND_STATE_DEVICE_ACTIVE; + return 0; +} + +#endif /* USB_SUSPEND_AVAILABLE */ + +/** + * Called by Linux kernel at initialization time, kernel wants to know if our driver will accept the + * caller supplied USB interface. Note that USB drivers are bound to interfaces, and not to USB + * devices. + */ +#ifdef KERNEL26 +#define DBUS_USBOS_PROBE() static int dbus_usbos_probe(struct usb_interface *intf, const struct usb_device_id *id) +#define DBUS_USBOS_DISCONNECT() static void dbus_usbos_disconnect(struct usb_interface *intf) +#else +#define DBUS_USBOS_PROBE() static void * dbus_usbos_probe(struct usb_device *usb, unsigned int ifnum, const struct usb_device_id *id) +#define DBUS_USBOS_DISCONNECT() static void dbus_usbos_disconnect(struct usb_device *usb, void *ptr) +#endif /* KERNEL26 */ + +DBUS_USBOS_PROBE() +{ + int ep; + struct usb_endpoint_descriptor *endpoint; + int ret = 0; +#ifdef KERNEL26 + struct usb_device *usb = interface_to_usbdev(intf); +#else + int claimed = 0; +#endif + int num_of_eps; +#ifdef BCMUSBDEV_COMPOSITE + int wlan_if = -1; + bool intr_ep = FALSE; +#endif /* BCMUSBDEV_COMPOSITE */ + + printf("%s: Enter\n", __FUNCTION__); + +#ifdef BCMUSBDEV_COMPOSITE + wlan_if = dbus_usbos_intf_wlan(usb); +#ifdef KERNEL26 + if ((wlan_if >= 0) && (IFPTR(usb, wlan_if) == intf)) +#else + if (wlan_if == ifnum) +#endif /* KERNEL26 */ + { +#endif /* BCMUSBDEV_COMPOSITE */ + g_probe_info.usb = usb; + g_probe_info.dldone = TRUE; +#ifdef BCMUSBDEV_COMPOSITE + } else { + DBUSTRACE(("dbus_usbos_probe: skip probe for non WLAN interface\n")); + ret = BCME_UNSUPPORTED; + goto fail; + } +#endif /* BCMUSBDEV_COMPOSITE */ + +#ifdef KERNEL26 + g_probe_info.intf = intf; +#endif /* KERNEL26 */ + +#ifdef BCMUSBDEV_COMPOSITE + if (IFDESC(usb, wlan_if).bInterfaceNumber > USB_COMPIF_MAX) +#else + if (IFDESC(usb, CONTROL_IF).bInterfaceNumber) +#endif /* BCMUSBDEV_COMPOSITE */ + { + ret = -1; + goto fail; + } + if (id != NULL) { + g_probe_info.vid = id->idVendor; + g_probe_info.pid = id->idProduct; + } + +#ifdef KERNEL26 + usb_set_intfdata(intf, &g_probe_info); +#endif + + /* Check that the device supports only one configuration */ + if (usb->descriptor.bNumConfigurations != 1) { + ret = -1; + goto fail; + } + + if (usb->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC) { +#ifdef BCMUSBDEV_COMPOSITE + if ((usb->descriptor.bDeviceClass != USB_CLASS_MISC) && + (usb->descriptor.bDeviceClass != USB_CLASS_WIRELESS)) { +#endif /* BCMUSBDEV_COMPOSITE */ + ret = -1; + goto fail; +#ifdef BCMUSBDEV_COMPOSITE + } +#endif /* BCMUSBDEV_COMPOSITE */ + } + + /* + * Only the BDC interface configuration is supported: + * Device class: USB_CLASS_VENDOR_SPEC + * if0 class: USB_CLASS_VENDOR_SPEC + * if0/ep0: control + * if0/ep1: bulk in + * if0/ep2: bulk out (ok if swapped with bulk in) + */ + if (CONFIGDESC(usb)->bNumInterfaces != 1) { +#ifdef BCMUSBDEV_COMPOSITE + if (CONFIGDESC(usb)->bNumInterfaces > USB_COMPIF_MAX) { +#endif /* BCMUSBDEV_COMPOSITE */ + ret = -1; + goto fail; +#ifdef BCMUSBDEV_COMPOSITE + } +#endif /* BCMUSBDEV_COMPOSITE */ + } + + /* Check interface */ +#ifndef KERNEL26 +#ifdef BCMUSBDEV_COMPOSITE + if (usb_interface_claimed(IFPTR(usb, wlan_if))) +#else + if (usb_interface_claimed(IFPTR(usb, CONTROL_IF))) +#endif /* BCMUSBDEV_COMPOSITE */ + { + ret = -1; + goto fail; + } +#endif /* !KERNEL26 */ + +#ifdef BCMUSBDEV_COMPOSITE + if ((IFDESC(usb, wlan_if).bInterfaceClass != USB_CLASS_VENDOR_SPEC || + IFDESC(usb, wlan_if).bInterfaceSubClass != 2 || + IFDESC(usb, wlan_if).bInterfaceProtocol != 0xff) && + (IFDESC(usb, wlan_if).bInterfaceClass != USB_CLASS_MISC || + IFDESC(usb, wlan_if).bInterfaceSubClass != USB_SUBCLASS_COMMON || + IFDESC(usb, wlan_if).bInterfaceProtocol != USB_PROTO_IAD)) +#else + if (IFDESC(usb, CONTROL_IF).bInterfaceClass != USB_CLASS_VENDOR_SPEC || + IFDESC(usb, CONTROL_IF).bInterfaceSubClass != 2 || + IFDESC(usb, CONTROL_IF).bInterfaceProtocol != 0xff) +#endif /* BCMUSBDEV_COMPOSITE */ + { +#ifdef BCMUSBDEV_COMPOSITE + DBUSERR(("%s: invalid control interface: class %d, subclass %d, proto %d\n", + __FUNCTION__, + IFDESC(usb, wlan_if).bInterfaceClass, + IFDESC(usb, wlan_if).bInterfaceSubClass, + IFDESC(usb, wlan_if).bInterfaceProtocol)); +#else + DBUSERR(("%s: invalid control interface: class %d, subclass %d, proto %d\n", + __FUNCTION__, + IFDESC(usb, CONTROL_IF).bInterfaceClass, + IFDESC(usb, CONTROL_IF).bInterfaceSubClass, + IFDESC(usb, CONTROL_IF).bInterfaceProtocol)); +#endif /* BCMUSBDEV_COMPOSITE */ + ret = -1; + goto fail; + } + + /* Check control endpoint */ +#ifdef BCMUSBDEV_COMPOSITE + endpoint = &IFEPDESC(usb, wlan_if, 0); +#else + endpoint = &IFEPDESC(usb, CONTROL_IF, 0); +#endif /* BCMUSBDEV_COMPOSITE */ + if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT) { +#ifdef BCMUSBDEV_COMPOSITE + if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != + USB_ENDPOINT_XFER_BULK) { +#endif /* BCMUSBDEV_COMPOSITE */ + DBUSERR(("%s: invalid control endpoint %d\n", + __FUNCTION__, endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)); + ret = -1; + goto fail; +#ifdef BCMUSBDEV_COMPOSITE + } +#endif /* BCMUSBDEV_COMPOSITE */ + } + +#ifdef BCMUSBDEV_COMPOSITE + if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) { +#endif /* BCMUSBDEV_COMPOSITE */ + g_probe_info.intr_pipe = + usb_rcvintpipe(usb, endpoint->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); +#ifdef BCMUSBDEV_COMPOSITE + intr_ep = TRUE; + } +#endif /* BCMUSBDEV_COMPOSITE */ + +#ifndef KERNEL26 + /* Claim interface */ +#ifdef BCMUSBDEV_COMPOSITE + usb_driver_claim_interface(&dbus_usbdev, IFPTR(usb, wlan_if), &g_probe_info); +#else + usb_driver_claim_interface(&dbus_usbdev, IFPTR(usb, CONTROL_IF), &g_probe_info); +#endif /* BCMUSBDEV_COMPOSITE */ + claimed = 1; +#endif /* !KERNEL26 */ + g_probe_info.rx_pipe = 0; + g_probe_info.rx_pipe2 = 0; + g_probe_info.tx_pipe = 0; +#ifdef BCMUSBDEV_COMPOSITE + if (intr_ep) + ep = 1; + else + ep = 0; + num_of_eps = IFDESC(usb, wlan_if).bNumEndpoints - 1; +#else + num_of_eps = IFDESC(usb, BULK_IF).bNumEndpoints - 1; +#endif /* BCMUSBDEV_COMPOSITE */ + + if ((num_of_eps != 2) && (num_of_eps != 3)) { +#ifdef BCMUSBDEV_COMPOSITE + if (num_of_eps > 7) +#endif /* BCMUSBDEV_COMPOSITE */ + ASSERT(0); + } + /* Check data endpoints and get pipes */ +#ifdef BCMUSBDEV_COMPOSITE + for (; ep <= num_of_eps; ep++) +#else + for (ep = 1; ep <= num_of_eps; ep++) +#endif /* BCMUSBDEV_COMPOSITE */ + { +#ifdef BCMUSBDEV_COMPOSITE + endpoint = &IFEPDESC(usb, wlan_if, ep); +#else + endpoint = &IFEPDESC(usb, BULK_IF, ep); +#endif /* BCMUSBDEV_COMPOSITE */ + if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != + USB_ENDPOINT_XFER_BULK) { + DBUSERR(("%s: invalid data endpoint %d\n", + __FUNCTION__, ep)); + ret = -1; + goto fail; + } + + if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) { + /* direction: dongle->host */ + if (!g_probe_info.rx_pipe) { + g_probe_info.rx_pipe = usb_rcvbulkpipe(usb, + (endpoint->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK)); + } else { + g_probe_info.rx_pipe2 = usb_rcvbulkpipe(usb, + (endpoint->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK)); + } + + } else + g_probe_info.tx_pipe = usb_sndbulkpipe(usb, (endpoint->bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK)); + } + + /* Allocate interrupt URB and data buffer */ + /* RNDIS says 8-byte intr, our old drivers used 4-byte */ +#ifdef BCMUSBDEV_COMPOSITE + g_probe_info.intr_size = (IFEPDESC(usb, wlan_if, 0).wMaxPacketSize == 16) ? 8 : 4; + g_probe_info.interval = IFEPDESC(usb, wlan_if, 0).bInterval; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 21)) + usb->quirks |= USB_QUIRK_NO_SET_INTF; +#endif +#else + g_probe_info.intr_size = (IFEPDESC(usb, CONTROL_IF, 0).wMaxPacketSize == 16) ? 8 : 4; + g_probe_info.interval = IFEPDESC(usb, CONTROL_IF, 0).bInterval; +#endif /* BCMUSBDEV_COMPOSITE */ + +#ifndef KERNEL26 + /* usb_fill_int_urb does the interval decoding in 2.6 */ + if (usb->speed == USB_SPEED_HIGH) + g_probe_info.interval = 1 << (g_probe_info.interval - 1); +#endif + if (usb->speed == USB_SPEED_SUPER) { + g_probe_info.device_speed = SUPER_SPEED; + DBUSERR(("super speed device detected\n")); + } else if (usb->speed == USB_SPEED_HIGH) { + g_probe_info.device_speed = HIGH_SPEED; + DBUSERR(("high speed device detected\n")); + } else { + g_probe_info.device_speed = FULL_SPEED; + DBUSERR(("full speed device detected\n")); + } + if (g_probe_info.dereged == FALSE && probe_cb) { + disc_arg = probe_cb(probe_arg, "", USB_BUS, 0); + } + + g_probe_info.disc_cb_done = FALSE; + +#ifdef KERNEL26 + intf->needs_remote_wakeup = 1; +#endif /* KERNEL26 */ + printf("%s: Exit ret=%d\n", __FUNCTION__, ret); + + /* Success */ +#ifdef KERNEL26 + return DBUS_OK; +#else + usb_inc_dev_use(usb); + return &g_probe_info; +#endif + +fail: + printf("%s: Exit ret=%d\n", __FUNCTION__, ret); +#ifdef BCMUSBDEV_COMPOSITE + if (ret != BCME_UNSUPPORTED) +#endif /* BCMUSBDEV_COMPOSITE */ + DBUSERR(("%s: failed with errno %d\n", __FUNCTION__, ret)); +#ifndef KERNEL26 + if (claimed) +#ifdef BCMUSBDEV_COMPOSITE + usb_driver_release_interface(&dbus_usbdev, IFPTR(usb, wlan_if)); +#else + usb_driver_release_interface(&dbus_usbdev, IFPTR(usb, CONTROL_IF)); +#endif /* BCMUSBDEV_COMPOSITE */ +#endif /* !KERNEL26 */ + +#ifdef KERNEL26 + usb_set_intfdata(intf, NULL); + return ret; +#else + return NULL; +#endif +} /* dbus_usbos_probe */ + +/** Called by Linux kernel, is the counter part of dbus_usbos_probe() */ +DBUS_USBOS_DISCONNECT() +{ +#ifdef KERNEL26 + struct usb_device *usb = interface_to_usbdev(intf); + probe_info_t *probe_usb_init_data = usb_get_intfdata(intf); +#else + probe_info_t *probe_usb_init_data = (probe_info_t *) ptr; +#endif + usbos_info_t *usbos_info; + + printf("%s: Enter\n", __FUNCTION__); + + if (probe_usb_init_data) { + usbos_info = (usbos_info_t *) probe_usb_init_data->usbos_info; + if (usbos_info) { + if ((probe_usb_init_data->dereged == FALSE) && disconnect_cb && disc_arg) { + disconnect_cb(disc_arg); + disc_arg = NULL; + probe_usb_init_data->disc_cb_done = TRUE; + } + } + } + + if (usb) { +#ifndef KERNEL26 +#ifdef BCMUSBDEV_COMPOSITE + usb_driver_release_interface(&dbus_usbdev, IFPTR(usb, wlan_if)); +#else + usb_driver_release_interface(&dbus_usbdev, IFPTR(usb, CONTROL_IF)); +#endif /* BCMUSBDEV_COMPOSITE */ + usb_dec_dev_use(usb); +#endif /* !KERNEL26 */ + } + printf("%s: Exit\n", __FUNCTION__); +} /* dbus_usbos_disconnect */ + +#define LOOPBACK_PKT_START 0xBABE1234 + +bool is_loopback_pkt(void *buf) +{ + + uint32 *buf_ptr = (uint32 *) buf; + + if (*buf_ptr == LOOPBACK_PKT_START) + return TRUE; + return FALSE; + +} + +int matches_loopback_pkt(void *buf) +{ + int i, j; + unsigned char *cbuf = (unsigned char *) buf; + + for (i = 4; i < loopback_size; i++) { + if (cbuf[i] != (i % 256)) { + printf("%s: mismatch at i=%d %d : ", __FUNCTION__, i, cbuf[i]); + for (j = i; ((j < i+ 16) && (j < loopback_size)); j++) { + printf("%d ", cbuf[j]); + } + printf("\n"); + return 0; + } + } + loopback_rx_cnt++; + return 1; +} + +int dbus_usbos_loopback_tx(void *usbos_info_ptr, int cnt, int size) +{ + usbos_info_t *usbos_info = (usbos_info_t *) usbos_info_ptr; + unsigned char *buf; + int j; + void* p = NULL; + int rc, last_rx_cnt; + int tx_failed_cnt; + int max_size = 1650; + int usb_packet_size = 512; + int min_packet_size = 10; + + if (size % usb_packet_size == 0) { + size = size - 1; + DBUSERR(("%s: overriding size=%d \n", __FUNCTION__, size)); + } + + if (size < min_packet_size) { + size = min_packet_size; + DBUSERR(("%s: overriding size=%d\n", __FUNCTION__, min_packet_size)); + } + if (size > max_size) { + size = max_size; + DBUSERR(("%s: overriding size=%d\n", __FUNCTION__, max_size)); + } + + loopback_tx_cnt = 0; + loopback_rx_cnt = 0; + tx_failed_cnt = 0; + loopback_size = size; + + while (loopback_tx_cnt < cnt) { + uint32 *x; + int pkt_size = loopback_size; + + p = PKTGET(usbos_info->pub->osh, pkt_size, TRUE); + if (p == NULL) { + DBUSERR(("%s:%d Failed to allocate packet sz=%d\n", + __FUNCTION__, __LINE__, pkt_size)); + return BCME_ERROR; + } + x = (uint32*) PKTDATA(usbos_info->pub->osh, p); + *x = LOOPBACK_PKT_START; + buf = (unsigned char*) x; + for (j = 4; j < pkt_size; j++) { + buf[j] = j % 256; + } + rc = dbus_send_buf(usbos_info->pub, buf, pkt_size, p); + if (rc != BCME_OK) { + DBUSERR(("%s:%d Freeing packet \n", __FUNCTION__, __LINE__)); + PKTFREE(usbos_info->pub->osh, p, TRUE); + dbus_usbos_wait(usbos_info, 1); + tx_failed_cnt++; + } else { + loopback_tx_cnt++; + tx_failed_cnt = 0; + } + if (tx_failed_cnt == 5) { + DBUSERR(("%s : Failed to send loopback packets cnt=%d loopback_tx_cnt=%d\n", + __FUNCTION__, cnt, loopback_tx_cnt)); + break; + } + } + printf("Transmitted %d loopback packets of size %d\n", loopback_tx_cnt, loopback_size); + + last_rx_cnt = loopback_rx_cnt; + while (loopback_rx_cnt < loopback_tx_cnt) { + dbus_usbos_wait(usbos_info, 1); + if (loopback_rx_cnt <= last_rx_cnt) { + DBUSERR(("%s: Matched rx cnt stuck at %d \n", __FUNCTION__, last_rx_cnt)); + return BCME_ERROR; + } + last_rx_cnt = loopback_rx_cnt; + } + printf("Received %d loopback packets of size %d\n", loopback_tx_cnt, loopback_size); + + return BCME_OK; +} /* dbus_usbos_loopback_tx */ + +/** + * Higher layer (dbus_usb.c) wants to transmit an I/O Request Block + * @param[in] txirb txirb->pkt, if non-zero, contains a single or a chain of packets + */ +static int +dbus_usbos_intf_send_irb(void *bus, dbus_irb_tx_t *txirb) +{ + usbos_info_t *usbos_info = (usbos_info_t *) bus; + urb_req_t *req, *req_zlp = NULL; + int ret = DBUS_OK; + unsigned long flags; + void *pkt; + uint32 buffer_length; + uint8 *buf; + + if ((usbos_info == NULL) || !usbos_info->tx_pipe) { + return DBUS_ERR; + } + + if (txirb->pkt != NULL) { + buffer_length = pkttotlen(usbos_info->pub->osh, txirb->pkt); + /* In case of multiple packets the values below may be overwritten */ + txirb->send_buf = NULL; + buf = PKTDATA(usbos_info->pub->osh, txirb->pkt); + } else { /* txirb->buf != NULL */ + ASSERT(txirb->buf != NULL); + ASSERT(txirb->send_buf == NULL); + buffer_length = txirb->len; + buf = txirb->buf; + } + + if (!(req = dbus_usbos_qdeq(&usbos_info->req_txfreeq, &usbos_info->txfree_lock))) { + DBUSERR(("%s No free URB!\n", __FUNCTION__)); + return DBUS_ERR_TXDROP; + } + + /* If not using standard Linux kernel functionality for handling Zero Length Packet(ZLP), + * the dbus needs to generate ZLP when length is multiple of MaxPacketSize. + */ +#ifndef WL_URB_ZPKT + if (!(buffer_length % usbos_info->maxps)) { + if (!(req_zlp = + dbus_usbos_qdeq(&usbos_info->req_txfreeq, &usbos_info->txfree_lock))) { + DBUSERR(("%s No free URB for ZLP!\n", __FUNCTION__)); + dbus_usbos_qenq(&usbos_info->req_txfreeq, req, &usbos_info->txfree_lock); + return DBUS_ERR_TXDROP; + } + + /* No txirb, so that dbus_usbos_send_complete can differentiate between + * DATA and ZLP. + */ + req_zlp->arg = NULL; + req_zlp->usbinfo = usbos_info; + req_zlp->buf_len = 0; + + usb_fill_bulk_urb(req_zlp->urb, usbos_info->usb, usbos_info->tx_pipe, NULL, + 0, (usb_complete_t)dbus_usbos_send_complete, req_zlp); + + req_zlp->urb->transfer_flags |= URB_QUEUE_BULK; + } +#endif /* !WL_URB_ZPKT */ + +#ifndef USBOS_TX_THREAD + /* Disable USB autosuspend until this request completes, request USB resume if needed. + * Because this call runs asynchronously, there is no guarantee the bus is resumed before + * the URB is submitted, and the URB might be dropped. Use USBOS_TX_THREAD to avoid + * this. + */ + USB_AUTOPM_GET_INTERFACE_ASYNC(g_probe_info.intf); +#endif /* !USBOS_TX_THREAD */ + + spin_lock_irqsave(&usbos_info->txlock, flags); + + req->arg = txirb; + req->usbinfo = usbos_info; + req->buf_len = 0; + + /* Prepare the URB */ + if (txirb->pkt != NULL) { + uint32 pktlen; + uint8 *transfer_buf; + + /* For multiple packets, allocate contiguous buffer and copy packet data to it */ + if (PKTNEXT(usbos_info->pub->osh, txirb->pkt)) { + transfer_buf = MALLOC(usbos_info->pub->osh, buffer_length); + if (!transfer_buf) { + ret = DBUS_ERR_TXDROP; + DBUSERR(("fail to alloc to usb buffer\n")); + goto fail; + } + + pkt = txirb->pkt; + txirb->send_buf = transfer_buf; + req->buf_len = buffer_length; + + while (pkt) { + pktlen = PKTLEN(usbos_info->pub->osh, pkt); + bcopy(PKTDATA(usbos_info->pub->osh, pkt), transfer_buf, pktlen); + transfer_buf += pktlen; + pkt = PKTNEXT(usbos_info->pub->osh, pkt); + } + + ASSERT(((uint8 *) txirb->send_buf + buffer_length) == transfer_buf); + + /* Overwrite buf pointer with pointer to allocated contiguous transfer_buf + */ + buf = txirb->send_buf; + } + } + + usb_fill_bulk_urb(req->urb, usbos_info->usb, usbos_info->tx_pipe, buf, + buffer_length, (usb_complete_t)dbus_usbos_send_complete, req); + + req->urb->transfer_flags |= URB_QUEUE_BULK; + +#ifdef USBOS_TX_THREAD + /* Enqueue TX request, the TX thread will resume the bus if needed and submit + * it asynchronously + */ + dbus_usbos_qenq(&usbos_info->usbos_tx_list, req, &usbos_info->usbos_tx_list_lock); + if (req_zlp != NULL) { + dbus_usbos_qenq(&usbos_info->usbos_tx_list, req_zlp, + &usbos_info->usbos_tx_list_lock); + } + spin_unlock_irqrestore(&usbos_info->txlock, flags); + + wake_up_interruptible(&usbos_info->usbos_tx_queue_head); + return DBUS_OK; +#else + if ((ret = USB_SUBMIT_URB(req->urb))) { + ret = DBUS_ERR_TXDROP; + goto fail; + } + + dbus_usbos_qenq(&usbos_info->req_txpostedq, req, &usbos_info->txposted_lock); + atomic_inc(&usbos_info->txposted); + + if (req_zlp != NULL) { + if ((ret = USB_SUBMIT_URB(req_zlp->urb))) { + DBUSERR(("failed to submit ZLP URB!\n")); + ASSERT(0); + ret = DBUS_ERR_TXDROP; + goto fail2; + } + + dbus_usbos_qenq(&usbos_info->req_txpostedq, req_zlp, &usbos_info->txposted_lock); + /* Also increment txposted for zlp packet, as it will be decremented in + * dbus_usbos_send_complete() + */ + atomic_inc(&usbos_info->txposted); + } + + spin_unlock_irqrestore(&usbos_info->txlock, flags); + return DBUS_OK; +#endif /* USBOS_TX_THREAD */ + +fail: + if (txirb->send_buf != NULL) { + MFREE(usbos_info->pub->osh, txirb->send_buf, req->buf_len); + txirb->send_buf = NULL; + req->buf_len = 0; + } + dbus_usbos_qenq(&usbos_info->req_txfreeq, req, &usbos_info->txfree_lock); +#ifndef USBOS_TX_THREAD +fail2: +#endif + if (req_zlp != NULL) { + dbus_usbos_qenq(&usbos_info->req_txfreeq, req_zlp, &usbos_info->txfree_lock); + } + + spin_unlock_irqrestore(&usbos_info->txlock, flags); + +#ifndef USBOS_TX_THREAD + USB_AUTOPM_PUT_INTERFACE_ASYNC(g_probe_info.intf); +#endif /* !USBOS_TX_THREAD */ + + return ret; +} /* dbus_usbos_intf_send_irb */ + +/** Higher layer (dbus_usb.c) recycles a received (and used) packet. */ +static int +dbus_usbos_intf_recv_irb(void *bus, dbus_irb_rx_t *rxirb) +{ + usbos_info_t *usbos_info = (usbos_info_t *) bus; + int ret = DBUS_OK; + + if (usbos_info == NULL) + return DBUS_ERR; + + ret = dbus_usbos_recv_urb_submit(usbos_info, rxirb, 0); + return ret; +} + +static int +dbus_usbos_intf_recv_irb_from_ep(void *bus, dbus_irb_rx_t *rxirb, uint32 ep_idx) +{ + usbos_info_t *usbos_info = (usbos_info_t *) bus; + int ret = DBUS_OK; + + if (usbos_info == NULL) + return DBUS_ERR; + +#ifdef INTR_EP_ENABLE + /* By specifying the ep_idx value of 0xff, the cdc layer is asking to + * submit an interrupt URB + */ + if (rxirb == NULL && ep_idx == 0xff) { + /* submit intr URB */ + if ((ret = USB_SUBMIT_URB(usbos_info->intr_urb)) < 0) { + DBUSERR(("%s intr USB_SUBMIT_URB failed, status %d\n", + __FUNCTION__, ret)); + } + return ret; + } +#else + if (rxirb == NULL) { + return DBUS_ERR; + } +#endif /* INTR_EP_ENABLE */ + + ret = dbus_usbos_recv_urb_submit(usbos_info, rxirb, ep_idx); + return ret; +} + +/** Higher layer (dbus_usb.c) want to cancel an IRB */ +static int +dbus_usbos_intf_cancel_irb(void *bus, dbus_irb_tx_t *txirb) +{ + usbos_info_t *usbos_info = (usbos_info_t *) bus; + + if (usbos_info == NULL) + return DBUS_ERR; + + return DBUS_ERR; +} + +/** Only one CTL transfer can be pending at any time. This function may block. */ +static int +dbus_usbos_intf_send_ctl(void *bus, uint8 *buf, int len) +{ + usbos_info_t *usbos_info = (usbos_info_t *) bus; + uint16 size; +#ifndef USBOS_TX_THREAD + int status; +#endif /* USBOS_TX_THREAD */ + + if ((usbos_info == NULL) || (buf == NULL) || (len == 0)) + return DBUS_ERR; + + if (usbos_info->ctl_urb == NULL) + return DBUS_ERR; + + /* Block until a pending CTL transfer has completed */ + if (down_interruptible(&usbos_info->ctl_lock) != 0) { + return DBUS_ERR_TXCTLFAIL; + } + +#ifdef USBOS_TX_THREAD + ASSERT(usbos_info->ctl_state == USBOS_REQUEST_STATE_UNSCHEDULED); +#else + /* Disable USB autosuspend until this request completes, request USB resume if needed. + * Because this call runs asynchronously, there is no guarantee the bus is resumed before + * the URB is submitted, and the URB might be dropped. Use USBOS_TX_THREAD to avoid + * this. + */ + USB_AUTOPM_GET_INTERFACE_ASYNC(g_probe_info.intf); +#endif /* USBOS_TX_THREAD */ + + size = len; + usbos_info->ctl_write.wLength = cpu_to_le16p(&size); + usbos_info->ctl_urb->transfer_buffer_length = size; + + usb_fill_control_urb(usbos_info->ctl_urb, + usbos_info->usb, + usb_sndctrlpipe(usbos_info->usb, 0), + (unsigned char *) &usbos_info->ctl_write, + buf, size, (usb_complete_t)dbus_usbos_ctlwrite_complete, usbos_info); + +#ifdef USBOS_TX_THREAD + /* Enqueue CTRL request for transmission by the TX thread. The + * USB bus will first be resumed if needed. + */ + usbos_info->ctl_state = USBOS_REQUEST_STATE_SCHEDULED; + wake_up_interruptible(&usbos_info->usbos_tx_queue_head); +#else + status = USB_SUBMIT_URB(usbos_info->ctl_urb); + if (status < 0) { + DBUSERR(("%s: usb_submit_urb failed %d\n", __FUNCTION__, status)); + up(&usbos_info->ctl_lock); + + USB_AUTOPM_PUT_INTERFACE_ASYNC(g_probe_info.intf); + + return DBUS_ERR_TXCTLFAIL; + } +#endif /* USBOS_TX_THREAD */ + + return DBUS_OK; +} /* dbus_usbos_intf_send_ctl */ + +/** This function does not seem to be called by anyone, including dbus_usb.c */ +static int +dbus_usbos_intf_recv_ctl(void *bus, uint8 *buf, int len) +{ + usbos_info_t *usbos_info = (usbos_info_t *) bus; + int status; + uint16 size; + + if ((usbos_info == NULL) || (buf == NULL) || (len == 0)) + return DBUS_ERR; + + if (usbos_info->ctl_urb == NULL) + return DBUS_ERR; + + /* Block until a pending CTRL transfer has completed */ + if (down_interruptible(&usbos_info->ctl_lock) != 0) { + return DBUS_ERR_TXCTLFAIL; + } + + /* Disable USB autosuspend until this request completes, request USB resume if needed. */ + USB_AUTOPM_GET_INTERFACE_ASYNC(g_probe_info.intf); + + size = len; + usbos_info->ctl_read.wLength = cpu_to_le16p(&size); + usbos_info->ctl_urb->transfer_buffer_length = size; + + if (usbos_info->rxctl_deferrespok) { + /* BMAC model */ + usbos_info->ctl_read.bRequestType = USB_DIR_IN | USB_TYPE_VENDOR | + USB_RECIP_INTERFACE; + usbos_info->ctl_read.bRequest = DL_DEFER_RESP_OK; + } else { + /* full dongle model */ + usbos_info->ctl_read.bRequestType = USB_DIR_IN | USB_TYPE_CLASS | + USB_RECIP_INTERFACE; + usbos_info->ctl_read.bRequest = 1; + } + + usb_fill_control_urb(usbos_info->ctl_urb, + usbos_info->usb, + usb_rcvctrlpipe(usbos_info->usb, 0), + (unsigned char *) &usbos_info->ctl_read, + buf, size, (usb_complete_t)dbus_usbos_ctlread_complete, usbos_info); + + status = USB_SUBMIT_URB(usbos_info->ctl_urb); + if (status < 0) { + DBUSERR(("%s: usb_submit_urb failed %d\n", __FUNCTION__, status)); + up(&usbos_info->ctl_lock); + + USB_AUTOPM_PUT_INTERFACE_ASYNC(g_probe_info.intf); + + return DBUS_ERR_RXCTLFAIL; + } + + return DBUS_OK; +} + +static int +dbus_usbos_intf_get_attrib(void *bus, dbus_attrib_t *attrib) +{ + usbos_info_t *usbos_info = (usbos_info_t *) bus; + + if ((usbos_info == NULL) || (attrib == NULL)) + return DBUS_ERR; + + attrib->bustype = DBUS_USB; + attrib->vid = g_probe_info.vid; + attrib->pid = g_probe_info.pid; + attrib->devid = 0x4322; + + attrib->nchan = 1; + + /* MaxPacketSize for USB hi-speed bulk out is 512 bytes + * and 64-bytes for full-speed. + * When sending pkt > MaxPacketSize, Host SW breaks it + * up into multiple packets. + */ + attrib->mtu = usbos_info->maxps; + + return DBUS_OK; +} + +/** Called by higher layer (dbus_usb.c) when it wants to 'up' the USB interface to the dongle */ +static int +dbus_usbos_intf_up(void *bus) +{ + usbos_info_t *usbos_info = (usbos_info_t *) bus; + uint16 ifnum; +#ifdef BCMUSBDEV_COMPOSITE + int wlan_if = 0; +#endif + if (usbos_info == NULL) + return DBUS_ERR; + + if (usbos_info->usb == NULL) + return DBUS_ERR; + +#if defined(INTR_EP_ENABLE) + /* full dongle use intr EP, bmac doesn't use it */ + if (usbos_info->intr_urb) { + int ret; + + usb_fill_int_urb(usbos_info->intr_urb, usbos_info->usb, + usbos_info->intr_pipe, &usbos_info->intr, + usbos_info->intr_size, (usb_complete_t)dbus_usbos_intr_complete, + usbos_info, usbos_info->interval); + + if ((ret = USB_SUBMIT_URB(usbos_info->intr_urb))) { + DBUSERR(("%s USB_SUBMIT_URB failed with status %d\n", __FUNCTION__, ret)); + return DBUS_ERR; + } + } +#endif + + if (usbos_info->ctl_urb) { + usbos_info->ctl_in_pipe = usb_rcvctrlpipe(usbos_info->usb, 0); + usbos_info->ctl_out_pipe = usb_sndctrlpipe(usbos_info->usb, 0); + +#ifdef BCMUSBDEV_COMPOSITE + wlan_if = dbus_usbos_intf_wlan(usbos_info->usb); + ifnum = cpu_to_le16(IFDESC(usbos_info->usb, wlan_if).bInterfaceNumber); +#else + ifnum = cpu_to_le16(IFDESC(usbos_info->usb, CONTROL_IF).bInterfaceNumber); +#endif /* BCMUSBDEV_COMPOSITE */ + /* CTL Write */ + usbos_info->ctl_write.bRequestType = + USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE; + usbos_info->ctl_write.bRequest = 0; + usbos_info->ctl_write.wValue = cpu_to_le16(0); + usbos_info->ctl_write.wIndex = cpu_to_le16p(&ifnum); + + /* CTL Read */ + usbos_info->ctl_read.bRequestType = + USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE; + usbos_info->ctl_read.bRequest = 1; + usbos_info->ctl_read.wValue = cpu_to_le16(0); + usbos_info->ctl_read.wIndex = cpu_to_le16p(&ifnum); + } + + /* Success, indicate usbos_info is fully up */ + dbus_usbos_state_change(usbos_info, DBUS_STATE_UP); + + return DBUS_OK; +} /* dbus_usbos_intf_up */ + +static int +dbus_usbos_intf_down(void *bus) +{ + usbos_info_t *usbos_info = (usbos_info_t *) bus; + + if (usbos_info == NULL) + return DBUS_ERR; + + dbusos_stop(usbos_info); + return DBUS_OK; +} + +static int +dbus_usbos_intf_stop(void *bus) +{ + usbos_info_t *usbos_info = (usbos_info_t *) bus; + + if (usbos_info == NULL) + return DBUS_ERR; + + dbusos_stop(usbos_info); + return DBUS_OK; +} + + +/** Called by higher layer (dbus_usb.c) */ +static int +dbus_usbos_intf_set_config(void *bus, dbus_config_t *config) +{ + int err = DBUS_ERR; + usbos_info_t* usbos_info = bus; + + if (config->config_id == DBUS_CONFIG_ID_RXCTL_DEFERRES) { + usbos_info->rxctl_deferrespok = config->rxctl_deferrespok; + err = DBUS_OK; + } else if (config->config_id == DBUS_CONFIG_ID_AGGR_LIMIT) { + /* DBUS_CONFIG_ID_AGGR_LIMIT shouldn't be called after probe stage */ + ASSERT(disc_arg == NULL); + ASSERT(config->aggr_param.maxrxsf > 0); + ASSERT(config->aggr_param.maxrxsize > 0); + if (config->aggr_param.maxrxsize > usbos_info->rxbuf_len) { + int state = usbos_info->pub->busstate; + dbus_usbos_unlink(&usbos_info->req_rxpostedq, &usbos_info->rxposted_lock); + while (atomic_read(&usbos_info->rxposted)) { + DBUSTRACE(("%s rxposted is %d, delay 1 ms\n", __FUNCTION__, + atomic_read(&usbos_info->rxposted))); + dbus_usbos_wait(usbos_info, 1); + } + usbos_info->rxbuf_len = config->aggr_param.maxrxsize; + dbus_usbos_state_change(usbos_info, state); + } + err = DBUS_OK; + } + + return err; +} + + +/** Called by dbus_usb.c when it wants to download firmware into the dongle */ +bool +dbus_usbos_dl_cmd(usbos_info_t *usbinfo, uint8 cmd, void *buffer, int buflen) +{ + int transferred; + int index = 0; + char *tmpbuf; + + if ((usbinfo == NULL) || (buffer == NULL) || (buflen == 0)) + return FALSE; + + tmpbuf = (char *) MALLOC(usbinfo->pub->osh, buflen); + if (!tmpbuf) { + DBUSERR(("%s: Unable to allocate memory \n", __FUNCTION__)); + return FALSE; + } + +#ifdef BCM_REQUEST_FW + if (cmd == DL_GO) { + index = 1; + } +#endif + + /* Disable USB autosuspend until this request completes, request USB resume if needed. */ + USB_AUTOPM_GET_INTERFACE(g_probe_info.intf); + + transferred = USB_CONTROL_MSG(usbinfo->usb, usb_rcvctrlpipe(usbinfo->usb, 0), + cmd, (USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE), + 0, index, + (void*) tmpbuf, buflen, USB_CTRL_EP_TIMEOUT); + if (transferred == buflen) { + memcpy(buffer, tmpbuf, buflen); + } else { + DBUSERR(("%s: usb_control_msg failed %d\n", __FUNCTION__, transferred)); + } + + USB_AUTOPM_PUT_INTERFACE(g_probe_info.intf); + + MFREE(usbinfo->pub->osh, tmpbuf, buflen); + return (transferred == buflen); +} + +/** + * Called by dbus_usb.c when it wants to download a buffer into the dongle (e.g. as part of the + * download process, when writing nvram variables). + */ +int +dbus_write_membytes(usbos_info_t* usbinfo, bool set, uint32 address, uint8 *data, uint size) +{ + hwacc_t hwacc; + int write_bytes = 4; + int status; + int retval = 0; + + DBUSTRACE(("Enter:%s\n", __FUNCTION__)); + + /* Read is not supported */ + if (set == 0) { + DBUSERR(("Currently read is not supported!!\n")); + return -1; + } + + USB_AUTOPM_GET_INTERFACE(g_probe_info.intf); + + hwacc.cmd = DL_CMD_WRHW; + hwacc.addr = address; + + DBUSTRACE(("Address:%x size:%d", hwacc.addr, size)); + do { + if (size >= 4) { + write_bytes = 4; + } else if (size >= 2) { + write_bytes = 2; + } else { + write_bytes = 1; + } + + hwacc.len = write_bytes; + + while (size >= write_bytes) { + hwacc.data = *((unsigned int*)data); + + status = USB_CONTROL_MSG(usbinfo->usb, usb_sndctrlpipe(usbinfo->usb, 0), + DL_WRHW, (USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE), + 1, 0, (char *)&hwacc, sizeof(hwacc_t), USB_CTRL_EP_TIMEOUT); + + if (status < 0) { + retval = -1; + DBUSERR((" Ctrl write hwacc failed w/status %d @ address:%x \n", + status, hwacc.addr)); + goto err; + } + + hwacc.addr += write_bytes; + data += write_bytes; + size -= write_bytes; + } + } while (size > 0); + +err: + USB_AUTOPM_PUT_INTERFACE(g_probe_info.intf); + + return retval; +} + +int +dbus_usbos_readreg(void *bus, uint32 regaddr, int datalen, uint32 *value) +{ + usbos_info_t *usbinfo = (usbos_info_t *) bus; + int ret = DBUS_OK; + int transferred; + uint32 cmd; + hwacc_t hwacc; + + if (usbinfo == NULL) + return DBUS_ERR; + + if (datalen == 1) + cmd = DL_RDHW8; + else if (datalen == 2) + cmd = DL_RDHW16; + else + cmd = DL_RDHW32; + + USB_AUTOPM_GET_INTERFACE(g_probe_info.intf); + + transferred = USB_CONTROL_MSG(usbinfo->usb, usb_rcvctrlpipe(usbinfo->usb, 0), + cmd, (USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE), + (uint16)(regaddr), (uint16)(regaddr >> 16), + (void *) &hwacc, sizeof(hwacc_t), USB_CTRL_EP_TIMEOUT); + + if (transferred >= sizeof(hwacc_t)) { + *value = hwacc.data; + } else { + DBUSERR(("%s: usb_control_msg failed %d\n", __FUNCTION__, transferred)); + ret = DBUS_ERR; + } + + USB_AUTOPM_PUT_INTERFACE(g_probe_info.intf); + + return ret; +} + +int +dbus_usbos_writereg(void *bus, uint32 regaddr, int datalen, uint32 data) +{ + usbos_info_t *usbinfo = (usbos_info_t *) bus; + int ret = DBUS_OK; + int transferred; + uint32 cmd = DL_WRHW; + hwacc_t hwacc; + + if (usbinfo == NULL) + return DBUS_ERR; + + USB_AUTOPM_GET_INTERFACE(g_probe_info.intf); + + hwacc.cmd = DL_WRHW; + hwacc.addr = regaddr; + hwacc.data = data; + hwacc.len = datalen; + + transferred = USB_CONTROL_MSG(usbinfo->usb, usb_sndctrlpipe(usbinfo->usb, 0), + cmd, (USB_DIR_OUT| USB_TYPE_VENDOR | USB_RECIP_INTERFACE), + 1, 0, + (void *) &hwacc, sizeof(hwacc_t), USB_CTRL_EP_TIMEOUT); + + if (transferred != sizeof(hwacc_t)) { + DBUSERR(("%s: usb_control_msg failed %d\n", __FUNCTION__, transferred)); + ret = DBUS_ERR; + } + + USB_AUTOPM_PUT_INTERFACE(g_probe_info.intf); + + return ret; +} + +int +dbus_usbos_wait(usbos_info_t *usbinfo, uint16 ms) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) + if (in_interrupt()) + mdelay(ms); + else + msleep_interruptible(ms); +#else + wait_ms(ms); +#endif + return DBUS_OK; +} + +/** Called by dbus_usb.c as part of the firmware download process */ +bool +dbus_usbos_dl_send_bulk(usbos_info_t *usbinfo, void *buffer, int len) +{ + bool ret = TRUE; + int status; + int transferred = 0; + + if (usbinfo == NULL) + return DBUS_ERR; + + USB_AUTOPM_GET_INTERFACE(g_probe_info.intf); + + status = USB_BULK_MSG(usbinfo->usb, usbinfo->tx_pipe, + buffer, len, + &transferred, USB_BULK_EP_TIMEOUT); + + if (status < 0) { + DBUSERR(("%s: usb_bulk_msg failed %d\n", __FUNCTION__, status)); + ret = FALSE; + } + + USB_AUTOPM_PUT_INTERFACE(g_probe_info.intf); + + return ret; +} + +static bool +dbus_usbos_intf_recv_needed(void *bus) +{ + return FALSE; +} + +/** + * Higher layer (dbus_usb.c) wants to execute a function on the condition that the rx spin lock has + * been acquired. + */ +static void* +dbus_usbos_intf_exec_rxlock(void *bus, exec_cb_t cb, struct exec_parms *args) +{ + usbos_info_t *usbos_info = (usbos_info_t *) bus; + void *ret; + unsigned long flags; + + if (usbos_info == NULL) + return NULL; + + spin_lock_irqsave(&usbos_info->rxlock, flags); + ret = cb(args); + spin_unlock_irqrestore(&usbos_info->rxlock, flags); + + return ret; +} + +static void* +dbus_usbos_intf_exec_txlock(void *bus, exec_cb_t cb, struct exec_parms *args) +{ + usbos_info_t *usbos_info = (usbos_info_t *) bus; + void *ret; + unsigned long flags; + + if (usbos_info == NULL) + return NULL; + + spin_lock_irqsave(&usbos_info->txlock, flags); + ret = cb(args); + spin_unlock_irqrestore(&usbos_info->txlock, flags); + + return ret; +} + +/** + * if an error condition was detected in this module, the higher DBUS layer (dbus_usb.c) has to + * be notified. + */ +int +dbus_usbos_errhandler(void *bus, int err) +{ + usbos_info_t *usbos_info = (usbos_info_t *) bus; + + if (usbos_info == NULL) + return DBUS_ERR; + + if (usbos_info->cbarg && usbos_info->cbs) { + if (usbos_info->cbs->errhandler) + usbos_info->cbs->errhandler(usbos_info->cbarg, err); + } + + return DBUS_OK; +} + +/** + * if a change in bus state was detected in this module, the higher DBUS layer (dbus_usb.c) has to + * be notified. + */ +int +dbus_usbos_state_change(void *bus, int state) +{ + usbos_info_t *usbos_info = (usbos_info_t *) bus; + + if (usbos_info == NULL) + return DBUS_ERR; + + if (usbos_info->cbarg && usbos_info->cbs) { + if (usbos_info->cbs->state_change) + usbos_info->cbs->state_change(usbos_info->cbarg, state); + } + + usbos_info->pub->busstate = state; + return DBUS_OK; +} + +int +dbus_bus_osl_register(int vid, int pid, probe_cb_t prcb, + disconnect_cb_t discb, void *prarg, dbus_intf_t **intf, void *param1, void *param2) +{ + bzero(&g_probe_info, sizeof(probe_info_t)); + + probe_cb = prcb; + disconnect_cb = discb; + probe_arg = prarg; + + devid_table[0].idVendor = vid; + devid_table[0].idProduct = pid; + + *intf = &dbus_usbos_intf; + + USB_REGISTER(); + + return DBUS_ERR_NODEVICE; +} + +int +dbus_bus_osl_deregister() +{ + g_probe_info.dereged = TRUE; + + if (disconnect_cb && disc_arg && (g_probe_info.disc_cb_done == FALSE)) { + disconnect_cb(disc_arg); + disc_arg = NULL; + } + + USB_DEREGISTER(); + + return DBUS_OK; +} + +void * +dbus_usbos_intf_attach(dbus_pub_t *pub, void *cbarg, dbus_intf_callbacks_t *cbs) +{ + usbos_info_t *usbos_info; + + if (g_probe_info.dldone == FALSE) { + DBUSERR(("%s: err device not downloaded!\n", __FUNCTION__)); + return NULL; + } + + /* Sanity check for BUS_INFO() */ + ASSERT(OFFSETOF(usbos_info_t, pub) == 0); + + usbos_info = MALLOC(pub->osh, sizeof(usbos_info_t)); + if (usbos_info == NULL) + return NULL; + + bzero(usbos_info, sizeof(usbos_info_t)); + + usbos_info->pub = pub; + usbos_info->cbarg = cbarg; + usbos_info->cbs = cbs; + + /* Needed for disconnect() */ + g_probe_info.usbos_info = usbos_info; + + /* Update USB Info */ + usbos_info->usb = g_probe_info.usb; + usbos_info->rx_pipe = g_probe_info.rx_pipe; + usbos_info->rx_pipe2 = g_probe_info.rx_pipe2; + usbos_info->tx_pipe = g_probe_info.tx_pipe; + usbos_info->intr_pipe = g_probe_info.intr_pipe; + usbos_info->intr_size = g_probe_info.intr_size; + usbos_info->interval = g_probe_info.interval; + usbos_info->pub->device_speed = g_probe_info.device_speed; + if (usbos_info->rx_pipe2) { + usbos_info->pub->attrib.has_2nd_bulk_in_ep = 1; + } else { + usbos_info->pub->attrib.has_2nd_bulk_in_ep = 0; + } + + if (usbos_info->tx_pipe) + usbos_info->maxps = usb_maxpacket(usbos_info->usb, + usbos_info->tx_pipe, usb_pipeout(usbos_info->tx_pipe)); + + INIT_LIST_HEAD(&usbos_info->req_rxfreeq); + INIT_LIST_HEAD(&usbos_info->req_txfreeq); + INIT_LIST_HEAD(&usbos_info->req_rxpostedq); + INIT_LIST_HEAD(&usbos_info->req_txpostedq); + spin_lock_init(&usbos_info->rxfree_lock); + spin_lock_init(&usbos_info->txfree_lock); + spin_lock_init(&usbos_info->rxposted_lock); + spin_lock_init(&usbos_info->txposted_lock); + spin_lock_init(&usbos_info->rxlock); + spin_lock_init(&usbos_info->txlock); + + atomic_set(&usbos_info->rxposted, 0); + atomic_set(&usbos_info->txposted, 0); + + +#ifdef USB_DISABLE_INT_EP + usbos_info->intr_urb = NULL; +#else + if (!(usbos_info->intr_urb = USB_ALLOC_URB())) { + DBUSERR(("%s: usb_alloc_urb (tx) failed\n", __FUNCTION__)); + goto fail; + } +#endif + + if (!(usbos_info->ctl_urb = USB_ALLOC_URB())) { + DBUSERR(("%s: usb_alloc_urb (tx) failed\n", __FUNCTION__)); + goto fail; + } + + init_waitqueue_head(&usbos_info->wait); + + if (!(usbos_info->blk_urb = USB_ALLOC_URB())) { /* for embedded image downloading */ + DBUSERR(("%s: usb_alloc_urb (tx) failed\n", __FUNCTION__)); + goto fail; + } + + usbos_info->rxbuf_len = (uint)usbos_info->pub->rxsize; + + + + atomic_set(&usbos_info->txallocated, 0); + if (DBUS_OK != dbus_usbos_urbreqs_alloc(usbos_info, + usbos_info->pub->ntxq, FALSE)) { + goto fail; + } + + atomic_set(&usbos_info->rxallocated, 0); + if (DBUS_OK != dbus_usbos_urbreqs_alloc(usbos_info, + MIN(DBUS_USB_RXQUEUE_BATCH_ADD, usbos_info->pub->nrxq), + TRUE)) { + goto fail; + } + + sema_init(&usbos_info->ctl_lock, 1); + +#ifdef USBOS_THREAD + if (dbus_usbos_thread_init(usbos_info) == NULL) + goto fail; +#endif /* USBOS_THREAD */ + +#ifdef USBOS_TX_THREAD + if (dbus_usbos_tx_thread_init(usbos_info) == NULL) + goto fail; +#endif /* USBOS_TX_THREAD */ + + pub->dev_info = g_probe_info.usb; + + + return (void *) usbos_info; +fail: + if (usbos_info->intr_urb) { + USB_FREE_URB(usbos_info->intr_urb); + usbos_info->intr_urb = NULL; + } + + if (usbos_info->ctl_urb) { + USB_FREE_URB(usbos_info->ctl_urb); + usbos_info->ctl_urb = NULL; + } + +#if defined(BCM_REQUEST_FW) + if (usbos_info->blk_urb) { + USB_FREE_URB(usbos_info->blk_urb); + usbos_info->blk_urb = NULL; + } +#endif + + dbus_usbos_urbreqs_free(usbos_info, TRUE); + atomic_set(&usbos_info->rxallocated, 0); + dbus_usbos_urbreqs_free(usbos_info, FALSE); + atomic_set(&usbos_info->txallocated, 0); + + g_probe_info.usbos_info = NULL; + + MFREE(pub->osh, usbos_info, sizeof(usbos_info_t)); + return NULL; +} /* dbus_usbos_intf_attach */ + +void +dbus_usbos_intf_detach(dbus_pub_t *pub, void *info) +{ + usbos_info_t *usbos_info = (usbos_info_t *) info; + osl_t *osh = pub->osh; + + if (usbos_info == NULL) { + return; + } + +#ifdef USBOS_TX_THREAD + dbus_usbos_tx_thread_deinit(usbos_info); +#endif /* USBOS_TX_THREAD */ + + /* Must unlink all URBs prior to driver unload; + * otherwise an URB callback can occur after driver + * has been de-allocated and rmmod'd + */ + dbusos_stop(usbos_info); + + if (usbos_info->intr_urb) { + USB_FREE_URB(usbos_info->intr_urb); + usbos_info->intr_urb = NULL; + } + + if (usbos_info->ctl_urb) { + USB_FREE_URB(usbos_info->ctl_urb); + usbos_info->ctl_urb = NULL; + } + + if (usbos_info->blk_urb) { + USB_FREE_URB(usbos_info->blk_urb); + usbos_info->blk_urb = NULL; + } + + dbus_usbos_urbreqs_free(usbos_info, TRUE); + atomic_set(&usbos_info->rxallocated, 0); + dbus_usbos_urbreqs_free(usbos_info, FALSE); + atomic_set(&usbos_info->txallocated, 0); + +#ifdef USBOS_THREAD + dbus_usbos_thread_deinit(usbos_info); +#endif /* USBOS_THREAD */ + + g_probe_info.usbos_info = NULL; + MFREE(osh, usbos_info, sizeof(usbos_info_t)); +} /* dbus_usbos_intf_detach */ + + +#ifdef USBOS_TX_THREAD + +void* +dbus_usbos_tx_thread_init(usbos_info_t *usbos_info) +{ + spin_lock_init(&usbos_info->usbos_tx_list_lock); + INIT_LIST_HEAD(&usbos_info->usbos_tx_list); + init_waitqueue_head(&usbos_info->usbos_tx_queue_head); + + usbos_info->usbos_tx_kt = kthread_create(dbus_usbos_tx_thread_func, + usbos_info, "usb-tx-thread"); + + if (IS_ERR(usbos_info->usbos_tx_kt)) { + DBUSERR(("Thread Creation failed\n")); + return (NULL); + } + + usbos_info->ctl_state = USBOS_REQUEST_STATE_UNSCHEDULED; + wake_up_process(usbos_info->usbos_tx_kt); + + return (usbos_info->usbos_tx_kt); +} + +void +dbus_usbos_tx_thread_deinit(usbos_info_t *usbos_info) +{ + urb_req_t *req; + + if (usbos_info->usbos_tx_kt) { + wake_up_interruptible(&usbos_info->usbos_tx_queue_head); + kthread_stop(usbos_info->usbos_tx_kt); + } + + /* Move pending requests to free queue so they can be freed */ + while ((req = dbus_usbos_qdeq( + &usbos_info->usbos_tx_list, &usbos_info->usbos_tx_list_lock)) != NULL) { + dbus_usbos_qenq(&usbos_info->req_txfreeq, req, &usbos_info->txfree_lock); + } +} + +/** + * Allow USB in-band resume to block by submitting CTRL and DATA URBs on a separate thread. + */ +int +dbus_usbos_tx_thread_func(void *data) +{ + usbos_info_t *usbos_info = (usbos_info_t *)data; + urb_req_t *req; + dbus_irb_tx_t *txirb; + int ret; + unsigned long flags; + +#ifdef WL_THREADNICE + set_user_nice(current, WL_THREADNICE); +#endif + + while (1) { + /* Wait until there are URBs to submit */ + wait_event_interruptible_timeout( + usbos_info->usbos_tx_queue_head, + !list_empty(&usbos_info->usbos_tx_list) || + usbos_info->ctl_state == USBOS_REQUEST_STATE_SCHEDULED, + 100); + + if (kthread_should_stop()) + break; + + /* Submit CTRL URB if needed */ + if (usbos_info->ctl_state == USBOS_REQUEST_STATE_SCHEDULED) { + + /* Disable USB autosuspend until this request completes. If the + * interface was suspended, this call blocks until it has been resumed. + */ + USB_AUTOPM_GET_INTERFACE(g_probe_info.intf); + + usbos_info->ctl_state = USBOS_REQUEST_STATE_SUBMITTED; + + ret = USB_SUBMIT_URB(usbos_info->ctl_urb); + if (ret != 0) { + DBUSERR(("%s CTRL USB_SUBMIT_URB failed, status %d\n", + __FUNCTION__, ret)); + + usbos_info->ctl_state = USBOS_REQUEST_STATE_UNSCHEDULED; + up(&usbos_info->ctl_lock); + + USB_AUTOPM_PUT_INTERFACE_ASYNC(g_probe_info.intf); + } + } + + /* Submit all available TX URBs */ + while ((req = dbus_usbos_qdeq(&usbos_info->usbos_tx_list, + &usbos_info->usbos_tx_list_lock)) != NULL) { + + /* Disable USB autosuspend until this request completes. If the + * interface was suspended, this call blocks until it has been resumed. + */ + USB_AUTOPM_GET_INTERFACE(g_probe_info.intf); + + spin_lock_irqsave(&usbos_info->txlock, flags); + + ret = USB_SUBMIT_URB(req->urb); + if (ret == 0) { + /* URB submitted successfully */ + dbus_usbos_qenq(&usbos_info->req_txpostedq, req, + &usbos_info->txposted_lock); + atomic_inc(&usbos_info->txposted); + } else { + /* Submitting the URB failed. */ + DBUSERR(("%s TX USB_SUBMIT_URB failed, status %d\n", + __FUNCTION__, ret)); + + USB_AUTOPM_PUT_INTERFACE_ASYNC(g_probe_info.intf); + } + + spin_unlock_irqrestore(&usbos_info->txlock, flags); + + if (ret != 0) { + /* Cleanup and notify higher layers */ + dbus_usbos_qenq(&usbos_info->req_txfreeq, req, + &usbos_info->txfree_lock); + + txirb = req->arg; + if (txirb->send_buf) { + MFREE(usbos_info->pub->osh, txirb->send_buf, req->buf_len); + txirb->send_buf = NULL; + req->buf_len = 0; + } + + if (likely (usbos_info->cbarg && usbos_info->cbs)) { + if (likely (usbos_info->cbs->send_irb_complete != NULL)) + usbos_info->cbs->send_irb_complete( + usbos_info->cbarg, txirb, DBUS_ERR_TXDROP); + } + } + } + } + + return 0; +} /* dbus_usbos_tx_thread_func */ + +#endif /* USBOS_TX_THREAD */ + +#ifdef USBOS_THREAD + +/** + * Increase system performance by creating a USB thread that runs parallel to other system + * activity. + */ +static void* +dbus_usbos_thread_init(usbos_info_t *usbos_info) +{ + usbos_list_entry_t *entry; + unsigned long flags, ii; + + spin_lock_init(&usbos_info->usbos_list_lock); + spin_lock_init(&usbos_info->ctrl_lock); + INIT_LIST_HEAD(&usbos_info->usbos_list); + INIT_LIST_HEAD(&usbos_info->usbos_free_list); + init_waitqueue_head(&usbos_info->usbos_queue_head); + atomic_set(&usbos_info->usbos_list_cnt, 0); + + + for (ii = 0; ii < (usbos_info->pub->nrxq + usbos_info->pub->ntxq); ii++) { + entry = MALLOC(usbos_info->pub->osh, sizeof(usbos_list_entry_t)); + if (entry) { + spin_lock_irqsave(&usbos_info->usbos_list_lock, flags); + list_add_tail((struct list_head*) entry, &usbos_info->usbos_free_list); + spin_unlock_irqrestore(&usbos_info->usbos_list_lock, flags); + } else { + DBUSERR(("Failed to create list\n")); + } + } + + usbos_info->usbos_kt = kthread_create(dbus_usbos_thread_func, + usbos_info, "usb-thread"); + + if (IS_ERR(usbos_info->usbos_kt)) { + DBUSERR(("Thread Creation failed\n")); + return (NULL); + } + + wake_up_process(usbos_info->usbos_kt); + + return (usbos_info->usbos_kt); +} + +static void +dbus_usbos_thread_deinit(usbos_info_t *usbos_info) +{ + struct list_head *cur, *next; + usbos_list_entry_t *entry; + unsigned long flags; + + if (usbos_info->usbos_kt) { + wake_up_interruptible(&usbos_info->usbos_queue_head); + kthread_stop(usbos_info->usbos_kt); + } +#if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif + list_for_each_safe(cur, next, &usbos_info->usbos_list) + { + entry = list_entry(cur, struct usbos_list_entry, list); + /* detach this entry from the list and then free the entry */ + spin_lock_irqsave(&usbos_info->usbos_list_lock, flags); + list_del(cur); + MFREE(usbos_info->pub->osh, entry, sizeof(usbos_list_entry_t)); + spin_unlock_irqrestore(&usbos_info->usbos_list_lock, flags); + } + + list_for_each_safe(cur, next, &usbos_info->usbos_free_list) + { + entry = list_entry(cur, struct usbos_list_entry, list); + /* detach this entry from the list and then free the entry */ + spin_lock_irqsave(&usbos_info->usbos_list_lock, flags); + list_del(cur); + MFREE(usbos_info->pub->osh, entry, sizeof(usbos_list_entry_t)); + spin_unlock_irqrestore(&usbos_info->usbos_list_lock, flags); + } +#if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__) +#pragma GCC diagnostic pop +#endif +} + +/** Process completed URBs in a worker thread */ +static int +dbus_usbos_thread_func(void *data) +{ + usbos_info_t *usbos_info = (usbos_info_t *)data; + usbos_list_entry_t *entry; + struct list_head *cur, *next; + unsigned long flags; + +#ifdef WL_THREADNICE + set_user_nice(current, WL_THREADNICE); +#endif + + while (1) { + /* If the list is empty, then go to sleep */ + wait_event_interruptible_timeout + (usbos_info->usbos_queue_head, + atomic_read(&usbos_info->usbos_list_cnt) > 0, + 100); + + if (kthread_should_stop()) + break; + + spin_lock_irqsave(&usbos_info->usbos_list_lock, flags); + + /* For each entry on the list, process it. Remove the entry from + * the list when done. + */ +#if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif + list_for_each_safe(cur, next, &usbos_info->usbos_list) + { + urb_req_t *req; + int len; + int stat; + usbos_info_t *usbos_info_local; + + entry = list_entry(cur, struct usbos_list_entry, list); + if (entry == NULL) + break; + + req = entry->urb_context; + len = entry->urb_length; + stat = entry->urb_status; + usbos_info_local = req->usbinfo; + + /* detach this entry from the list and attach it to the free list */ + list_del_init(cur); + spin_unlock_irqrestore(&usbos_info_local->usbos_list_lock, flags); + + dbus_usbos_recv_complete_handle(req, len, stat); + + spin_lock_irqsave(&usbos_info_local->usbos_list_lock, flags); + + list_add_tail(cur, &usbos_info_local->usbos_free_list); + + atomic_dec(&usbos_info_local->usbos_list_cnt); + } + + spin_unlock_irqrestore(&usbos_info->usbos_list_lock, flags); + + } +#if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + + return 0; +} /* dbus_usbos_thread_func */ + +/** Called on Linux calling URB callback, see dbus_usbos_recv_complete() */ +static void +dbus_usbos_dispatch_schedule(CALLBACK_ARGS) +{ + urb_req_t *req = urb->context; + usbos_info_t *usbos_info = req->usbinfo; + usbos_list_entry_t *entry; + unsigned long flags; + struct list_head *cur; + + spin_lock_irqsave(&usbos_info->usbos_list_lock, flags); + + cur = usbos_info->usbos_free_list.next; +#if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif + entry = list_entry(cur, struct usbos_list_entry, list); +#if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + + /* detach this entry from the free list and prepare it insert it to use list */ + list_del_init(cur); + + if (entry) { + entry->urb_context = urb->context; + entry->urb_length = urb->actual_length; + entry->urb_status = urb->status; + + atomic_inc(&usbos_info->usbos_list_cnt); + list_add_tail(cur, &usbos_info->usbos_list); + } else { + DBUSERR(("!!!!!!OUT OF MEMORY!!!!!!!\n")); + } + + spin_unlock_irqrestore(&usbos_info->usbos_list_lock, flags); + + /* thread */ + wake_up_interruptible(&usbos_info->usbos_queue_head); +} /* dbus_usbos_dispatch_schedule */ + +#endif /* USBOS_THREAD */ + + + + +#ifdef BCM_REQUEST_FW + +struct request_fw_context { + const struct firmware *firmware; + struct semaphore lock; +}; + +/* + * Callback for dbus_request_firmware(). + */ +static void +dbus_request_firmware_done(const struct firmware *firmware, void *ctx) +{ + struct request_fw_context *context = (struct request_fw_context*)ctx; + + /* Store the received firmware handle in the context and wake requester */ + context->firmware = firmware; + up(&context->lock); +} + +/* + * Send a firmware request and wait for completion. + * + * The use of the asynchronous version of request_firmware() is needed to avoid + * kernel oopses when we just come out of system hibernate. + */ +static int +dbus_request_firmware(const char *name, const struct firmware **firmware) +{ + struct request_fw_context *context; + int ret; + + context = kzalloc(sizeof(*context), GFP_KERNEL); + if (!context) + return -ENOMEM; + + sema_init(&context->lock, 0); + + ret = request_firmware_nowait(THIS_MODULE, true, name, &g_probe_info.usb->dev, + GFP_KERNEL, context, dbus_request_firmware_done); + if (ret) { + kfree(context); + return ret; + } + + /* Wait for completion */ + if (down_interruptible(&context->lock) != 0) { + kfree(context); + return -ERESTARTSYS; + } + + *firmware = context->firmware; + kfree(context); + + return *firmware != NULL ? 0 : -ENOENT; +} + +static void * +dbus_get_fwfile(int devid, int chiprev, uint8 **fw, int *fwlen, uint16 boardtype, uint16 boardrev) +{ + const struct firmware *firmware = NULL; +#ifndef OEM_ANDROID + s8 *device_id = NULL; + s8 *chip_rev = ""; +#endif /* OEM_ANDROID */ + s8 file_name[64]; + int ret; + +#ifndef OEM_ANDROID + switch (devid) { + case BCM4350_CHIP_ID: + case BCM4354_CHIP_ID: + case BCM43556_CHIP_ID: + case BCM43558_CHIP_ID: + case BCM43566_CHIP_ID: + case BCM43568_CHIP_ID: + case BCM43570_CHIP_ID: + case BCM4358_CHIP_ID: + device_id = "4350"; + break; + case BCM43143_CHIP_ID: + device_id = "43143"; + break; + case BCM43234_CHIP_ID: + case BCM43235_CHIP_ID: + case BCM43236_CHIP_ID: + device_id = "43236"; + break; + case BCM43242_CHIP_ID: + device_id = "43242"; + break; + case BCM43238_CHIP_ID: + device_id = "43238"; + break; + case BCM43526_CHIP_ID: + device_id = "43526"; + break; + case BCM43569_CHIP_ID: + device_id = "43569"; + switch (chiprev) { + case 0: + chip_rev = "a0"; + break; + case 2: + chip_rev = "a2"; + break; + default: + break; + } + break; + default: + DBUSERR(("unsupported device %x\n", devid)); + return NULL; + } + + /* Load firmware */ + snprintf(file_name, sizeof(file_name), "brcm/bcm%s%s-firmware.bin", device_id, chip_rev); +#else + snprintf(file_name, sizeof(file_name), "%s", CONFIG_ANDROID_BCMDHD_FW_PATH); +#endif /* OEM_ANDROID */ + + ret = dbus_request_firmware(file_name, &firmware); + if (ret) { + DBUSERR(("fail to request firmware %s\n", file_name)); + return NULL; + } + + *fwlen = firmware->size; + *fw = (uint8 *)firmware->data; + return (void *)firmware; + +} + +static void * +dbus_get_nvfile(int devid, int chiprev, uint8 **fw, int *fwlen, uint16 boardtype, uint16 boardrev) +{ + const struct firmware *firmware = NULL; +#ifndef OEM_ANDROID + s8 *device_id = NULL; + s8 *chip_rev = ""; +#endif /* OEM_ANDROID */ + s8 file_name[64]; + int ret; + +#ifndef OEM_ANDROID + switch (devid) { + case BCM4350_CHIP_ID: + case BCM4354_CHIP_ID: + case BCM43556_CHIP_ID: + case BCM43558_CHIP_ID: + case BCM43566_CHIP_ID: + case BCM43568_CHIP_ID: + case BCM43570_CHIP_ID: + case BCM4358_CHIP_ID: + device_id = "4350"; + break; + case BCM43143_CHIP_ID: + device_id = "43143"; + break; + case BCM43234_CHIP_ID: + device_id = "43234"; + break; + case BCM43235_CHIP_ID: + device_id = "43235"; + break; + case BCM43236_CHIP_ID: + device_id = "43236"; + break; + case BCM43238_CHIP_ID: + device_id = "43238"; + break; + case BCM43242_CHIP_ID: + device_id = "43242"; + break; + case BCM43526_CHIP_ID: + device_id = "43526"; + break; + case BCM43569_CHIP_ID: + device_id = "43569"; + switch (chiprev) { + case 0: + chip_rev = "a0"; + break; + case 2: + chip_rev = "a2"; + break; + default: + break; + } + break; + default: + DBUSERR(("unsupported device %x\n", devid)); + return NULL; + } + + /* Load board specific nvram file */ + snprintf(file_name, sizeof(file_name), "brcm/bcm%s%s-%2x-%2x.nvm", + device_id, chip_rev, boardtype, boardrev); +#else + snprintf(file_name, sizeof(file_name), "%s", CONFIG_ANDROID_BCMDHD_NVRAM_PATH); +#endif /* OEM_ANDROID */ + + ret = dbus_request_firmware(file_name, &firmware); + if (ret) { + DBUSERR(("fail to request nvram %s\n", file_name)); + +#ifndef OEM_ANDROID + /* Load generic nvram file */ + snprintf(file_name, sizeof(file_name), "brcm/bcm%s%s.nvm", + device_id, chip_rev); + + ret = dbus_request_firmware(file_name, &firmware); +#endif /* OEM_ANDROID */ + + if (ret) { + DBUSERR(("fail to request nvram %s\n", file_name)); + return NULL; + } + } + + *fwlen = firmware->size; + *fw = (uint8 *)firmware->data; + return (void *)firmware; +} + +void * +dbus_get_fw_nvfile(int devid, int chiprev, uint8 **fw, int *fwlen, int type, uint16 boardtype, + uint16 boardrev) +{ + switch (type) { + case DBUS_FIRMWARE: + return dbus_get_fwfile(devid, chiprev, fw, fwlen, boardtype, boardrev); + case DBUS_NVFILE: + return dbus_get_nvfile(devid, chiprev, fw, fwlen, boardtype, boardrev); + default: + return NULL; + } +} + +void +dbus_release_fw_nvfile(void *firmware) +{ + release_firmware((struct firmware *)firmware); +} +#endif /* BCM_REQUEST_FW */ + +#ifdef BCMUSBDEV_COMPOSITE +/** + * For a composite device the interface order is not guaranteed, scan the device struct for the WLAN + * interface. + */ +static int +dbus_usbos_intf_wlan(struct usb_device *usb) +{ + int i, num_of_eps, ep, intf_wlan = -1; + int num_intf = CONFIGDESC(usb)->bNumInterfaces; + struct usb_endpoint_descriptor *endpoint; + + for (i = 0; i < num_intf; i++) { + if (IFDESC(usb, i).bInterfaceClass != USB_CLASS_VENDOR_SPEC) + continue; + num_of_eps = IFDESC(usb, i).bNumEndpoints; + + for (ep = 0; ep < num_of_eps; ep++) { + endpoint = &IFEPDESC(usb, i, ep); + if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == + USB_ENDPOINT_XFER_BULK) { + intf_wlan = i; + break; + } + } + if (ep < num_of_eps) + break; + } + + return intf_wlan; +} +#endif /* BCMUSBDEV_COMPOSITE */ diff --git a/bcmdhd.1.579.77.41.1.cn/dhd.h b/bcmdhd.1.579.77.41.1.cn/dhd.h index 96d774a..8fd55ee 100644 --- a/bcmdhd.1.579.77.41.1.cn/dhd.h +++ b/bcmdhd.1.579.77.41.1.cn/dhd.h @@ -51,6 +51,9 @@ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_HAS_WAKELOCK) #include #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined (CONFIG_HAS_WAKELOCK) */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) +#include +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) */ /* The kernel threading is sdio-specific */ struct task_struct; struct sched_param; @@ -85,10 +88,6 @@ int get_scheduler_policy(struct task_struct *p); #define WL_VENDOR_EXT_SUPPORT #endif /* 3.18 > KERNEL_VER >= 3.14 || defined(CONFIG_BCMDHD_VENDOR_EXT) */ -/*#if !defined(WL_VENDOR_EXT_SUPPORT) -#undef GSCAN_SUPPORT -#endif -*/ #if defined(KEEP_ALIVE) /* Default KEEP_ALIVE Period is 55 sec to prevent AP from sending Keep Alive probe frame */ #define KEEP_ALIVE_PERIOD 55000 @@ -676,6 +675,9 @@ typedef struct dhd_pub { * please do NOT merge it back from other branches !!! */ +#ifdef BCMDBUS + struct dbus_pub *dbus; +#endif /* BCMDBUS */ /* Internal dhd items */ bool up; /* Driver up/down (to OS) */ @@ -1709,6 +1711,9 @@ extern int dhd_event_ifdel(struct dhd_info *dhd, struct wl_event_data_if *ifeven char *name, uint8 *mac); extern int dhd_event_ifchange(struct dhd_info *dhd, struct wl_event_data_if *ifevent, char *name, uint8 *mac); +#ifdef DHD_UPDATE_INTF_MAC +extern int dhd_op_if_update(dhd_pub_t *dhdpub, int ifidx); +#endif /* DHD_UPDATE_INTF_MAC */ extern struct net_device* dhd_allocate_if(dhd_pub_t *dhdpub, int ifidx, const char *name, uint8 *mac, uint8 bssidx, bool need_rtnl_lock, const char *dngl_name); extern int dhd_remove_if(dhd_pub_t *dhdpub, int ifidx, bool need_rtnl_lock); @@ -1830,6 +1835,9 @@ extern uint dhd_console_ms; extern uint android_msg_level; extern uint config_msg_level; extern uint sd_msglevel; +#ifdef BCMDBUS +extern uint dbus_msglevel; +#endif /* BCMDBUS */ #ifdef WL_WIRELESS_EXT extern uint iw_msg_level; #endif @@ -2040,7 +2048,9 @@ extern char fw_path2[MOD_PARAM_PATHLEN]; /* Flag to indicate if we should download firmware on driver load */ extern uint dhd_download_fw_on_driverload; +#ifndef BCMDBUS extern int allow_delay_fwdl; +#endif /* !BCMDBUS */ extern int dhd_process_cid_mac(dhd_pub_t *dhdp, bool prepost); extern int dhd_write_file(const char *filepath, char *buf, int buf_len); @@ -2235,6 +2245,10 @@ extern void dhd_os_general_spin_unlock(dhd_pub_t *pub, unsigned long flags); extern void dhd_dump_to_kernelog(dhd_pub_t *dhdp); +#ifdef BCMDBUS +extern void dhd_bus_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf); +extern void dhd_bus_clearcounts(dhd_pub_t *dhdp); +#endif /* BCMDBUS */ #ifdef DHD_L2_FILTER extern int dhd_get_parp_status(dhd_pub_t *dhdp, uint32 idx); diff --git a/bcmdhd.1.579.77.41.1.cn/dhd_cdc.c b/bcmdhd.1.579.77.41.1.cn/dhd_cdc.c index 11344de..db30d9b 100644 --- a/bcmdhd.1.579.77.41.1.cn/dhd_cdc.c +++ b/bcmdhd.1.579.77.41.1.cn/dhd_cdc.c @@ -41,7 +41,11 @@ #include #include #include +#ifdef BCMDBUS +#include +#else #include +#endif /* BCMDBUS */ #include @@ -68,15 +72,24 @@ typedef struct dhd_prot { uint16 reqid; uint8 pending; uint32 lastcmd; +#ifdef BCMDBUS + uint ctl_completed; +#endif /* BCMDBUS */ uint8 bus_header[BUS_HEADER_LEN]; cdc_ioctl_t msg; unsigned char buf[WLC_IOCTL_MAXLEN + ROUND_UP_MARGIN]; } dhd_prot_t; +#if defined(BCMDBUS) +extern int dhd_dbus_txdata(dhd_pub_t *dhdp, void *pktbuf); +#endif /* BCMDBUS */ static int dhdcdc_msg(dhd_pub_t *dhd) { +#ifdef BCMDBUS + int timeout = 0; +#endif /* BCMDBUS */ int err = 0; dhd_prot_t *prot = dhd->prot; int len = ltoh32(prot->msg.len) + sizeof(cdc_ioctl_t); @@ -93,8 +106,51 @@ dhdcdc_msg(dhd_pub_t *dhd) len = CDC_MAX_MSG_SIZE; /* Send request */ +#ifdef BCMDBUS + DHD_OS_IOCTL_RESP_LOCK(dhd); + prot->ctl_completed = FALSE; + err = dbus_send_ctl(dhd->dbus, (void *)&prot->msg, len); + if (err) { + DHD_ERROR(("dbus_send_ctl error=0x%x\n", err)); + DHD_OS_IOCTL_RESP_UNLOCK(dhd); + DHD_OS_WAKE_UNLOCK(dhd); + return err; + } +#else err = dhd_bus_txctl(dhd->bus, (uchar*)&prot->msg, len); - +#endif /* BCMDBUS */ + +#ifdef BCMDBUS + timeout = dhd_os_ioctl_resp_wait(dhd, &prot->ctl_completed, false); + if ((!timeout) || (!prot->ctl_completed)) { + DHD_ERROR(("Txctl timeout %d ctl_completed %d\n", + timeout, prot->ctl_completed)); + DHD_ERROR(("Txctl wait timed out\n")); + err = -1; + } + DHD_OS_IOCTL_RESP_UNLOCK(dhd); +#endif /* BCMDBUS */ +#if defined(BCMDBUS) && defined(INTR_EP_ENABLE) + /* If the ctl write is successfully completed, wait for an acknowledgement + * that indicates that it is now ok to do ctl read from the dongle + */ + if (err != -1) { + DHD_OS_IOCTL_RESP_LOCK(dhd); + prot->ctl_completed = FALSE; + if (dbus_poll_intr(dhd->dbus)) { + DHD_ERROR(("dbus_poll_intr not submitted\n")); + } else { + /* interrupt polling is sucessfully submitted. Wait for dongle to send + * interrupt + */ + timeout = dhd_os_ioctl_resp_wait(dhd, &prot->ctl_completed, false); + if (!timeout) { + DHD_ERROR(("intr poll wait timed out\n")); + } + } + DHD_OS_IOCTL_RESP_UNLOCK(dhd); + } +#endif /* defined(BCMDBUS) && defined(INTR_EP_ENABLE) */ DHD_OS_WAKE_UNLOCK(dhd); return err; } @@ -102,6 +158,9 @@ dhdcdc_msg(dhd_pub_t *dhd) static int dhdcdc_cmplt(dhd_pub_t *dhd, uint32 id, uint32 len) { +#ifdef BCMDBUS + int timeout = 0; +#endif /* BCMDBUS */ int ret; int cdc_len = len + sizeof(cdc_ioctl_t); dhd_prot_t *prot = dhd->prot; @@ -109,11 +168,37 @@ dhdcdc_cmplt(dhd_pub_t *dhd, uint32 id, uint32 len) DHD_TRACE(("%s: Enter\n", __FUNCTION__)); do { +#ifdef BCMDBUS + DHD_OS_IOCTL_RESP_LOCK(dhd); + prot->ctl_completed = FALSE; + ret = dbus_recv_ctl(dhd->dbus, (uchar*)&prot->msg, cdc_len); + if (ret) { + DHD_ERROR(("dbus_recv_ctl error=0x%x(%d)\n", ret, ret)); + DHD_OS_IOCTL_RESP_UNLOCK(dhd); + goto done; + } + timeout = dhd_os_ioctl_resp_wait(dhd, &prot->ctl_completed, false); + if ((!timeout) || (!prot->ctl_completed)) { + DHD_ERROR(("Rxctl timeout %d ctl_completed %d\n", + timeout, prot->ctl_completed)); + ret = -1; + DHD_OS_IOCTL_RESP_UNLOCK(dhd); + + goto done; + } + DHD_OS_IOCTL_RESP_UNLOCK(dhd); + + ret = cdc_len; +#else ret = dhd_bus_rxctl(dhd->bus, (uchar*)&prot->msg, cdc_len); +#endif /* BCMDBUS */ if (ret < 0) break; } while (CDC_IOC_ID(ltoh32(prot->msg.flags)) != id); +#ifdef BCMDBUS +done: +#endif /* BCMDBUS */ return ret; } @@ -286,6 +371,25 @@ done: return ret; } +#ifdef BCMDBUS +int +dhd_prot_ctl_complete(dhd_pub_t *dhd) +{ + dhd_prot_t *prot; + + if (dhd == NULL) + return BCME_ERROR; + + prot = dhd->prot; + + ASSERT(prot); + DHD_OS_IOCTL_RESP_LOCK(dhd); + prot->ctl_completed = TRUE; + dhd_os_ioctl_resp_wake(dhd); + DHD_OS_IOCTL_RESP_UNLOCK(dhd); + return 0; +} +#endif /* BCMDBUS */ int dhd_prot_ioctl(dhd_pub_t *dhd, int ifidx, wl_ioctl_t * ioc, void * buf, int len) @@ -487,6 +591,12 @@ dhd_prot_hdrpull(dhd_pub_t *dhd, int *ifidx, void *pktbuf, uchar *reorder_buf_in dhd_wlfc_parse_header_info(dhd, pktbuf, (data_offset << 2), reorder_buf_info, reorder_info_len); +#ifdef BCMDBUS +#ifndef DHD_WLFC_THREAD + dhd_wlfc_commit_packets(dhd, + (f_commitpkt_t)dhd_dbus_txdata, (void *)dhd, NULL, FALSE); +#endif /* DHD_WLFC_THREAD */ +#endif /* BCMDBUS */ } #endif /* PROP_TXSTATUS */ diff --git a/bcmdhd.1.579.77.41.1.cn/dhd_cfg80211.c b/bcmdhd.1.579.77.41.1.cn/dhd_cfg80211.c index d01e768..b98fcd3 100644 --- a/bcmdhd.1.579.77.41.1.cn/dhd_cfg80211.c +++ b/bcmdhd.1.579.77.41.1.cn/dhd_cfg80211.c @@ -161,8 +161,10 @@ void dhd_netdev_free(struct net_device *ndev) #ifdef WL_CFG80211 ndev = dhd_cfg80211_netdev_free(ndev); #endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0) if (ndev) free_netdev(ndev); +#endif } static s32 diff --git a/bcmdhd.1.579.77.41.1.cn/dhd_common.c b/bcmdhd.1.579.77.41.1.cn/dhd_common.c index 19a9226..46f270e 100644 --- a/bcmdhd.1.579.77.41.1.cn/dhd_common.c +++ b/bcmdhd.1.579.77.41.1.cn/dhd_common.c @@ -51,7 +51,11 @@ #include #endif +#ifdef BCMDBUS +#include +#else #include +#endif /* BCMDBUS */ #include #include #include @@ -650,7 +654,11 @@ void* dhd_get_fwdump_buf(dhd_pub_t *dhd_pub, uint32 length) int dhd_common_socram_dump(dhd_pub_t *dhdp) { +#ifdef BCMDBUS + return -1; +#else return dhd_socram_dump(dhdp->bus); +#endif /* BCMDBUS */ } static int @@ -1038,7 +1046,7 @@ dhd_iovar_parse_bssidx(dhd_pub_t *dhd_pub, const char *params, uint32 *idx, cons return BCME_OK; } -#if defined(DHD_DEBUG) && defined(BCMDHDUSB) +#if defined(DHD_DEBUG) && defined(BCMDBUS) /* USB Device console input function */ int dhd_bus_console_in(dhd_pub_t *dhd, uchar *msg, uint msglen) { @@ -1047,7 +1055,7 @@ int dhd_bus_console_in(dhd_pub_t *dhd, uchar *msg, uint msglen) return dhd_iovar(dhd, 0, "cons", msg, msglen, NULL, 0, TRUE); } -#endif /* DHD_DEBUG && BCMDHDUSB */ +#endif /* DHD_DEBUG && BCMDBUS */ #ifdef DHD_DEBUG int @@ -1263,10 +1271,12 @@ dhd_doiovar(dhd_pub_t *dhd_pub, const bcm_iovar_t *vi, uint32 actionid, const ch bcopy(&int_val, arg, val_size); break; +#ifndef BCMDBUS case IOV_GVAL(IOV_WDTICK): int_val = (int32)dhd_watchdog_ms; bcopy(&int_val, arg, val_size); break; +#endif /* !BCMDBUS */ case IOV_SVAL(IOV_WDTICK): if (!dhd_pub->up) { @@ -1285,6 +1295,7 @@ dhd_doiovar(dhd_pub_t *dhd_pub, const bcm_iovar_t *vi, uint32 actionid, const ch bcmerror = dhd_dump(dhd_pub, arg, len); break; +#ifndef BCMDBUS case IOV_GVAL(IOV_DCONSOLE_POLL): int_val = (int32)dhd_console_ms; bcopy(&int_val, arg, val_size); @@ -1298,6 +1309,7 @@ dhd_doiovar(dhd_pub_t *dhd_pub, const bcm_iovar_t *vi, uint32 actionid, const ch if (len > 0) bcmerror = dhd_bus_console_in(dhd_pub, arg, len - 1); break; +#endif /* !BCMDBUS */ case IOV_SVAL(IOV_CLEARCOUNTS): dhd_pub->tx_packets = dhd_pub->rx_packets = 0; @@ -1423,9 +1435,9 @@ dhd_doiovar(dhd_pub_t *dhd_pub, const bcm_iovar_t *vi, uint32 actionid, const ch case IOV_GVAL(IOV_BUS_TYPE): /* The dhd application queries the driver to check if its usb or sdio. */ -#ifdef BCMDHDUSB +#ifdef BCMDBUS int_val = BUS_TYPE_USB; -#endif +#endif /* BCMDBUS */ #ifdef BCMSDIO int_val = BUS_TYPE_SDIO; #endif @@ -1952,6 +1964,8 @@ dhd_doiovar(dhd_pub_t *dhd_pub, const bcm_iovar_t *vi, uint32 actionid, const ch break; } #endif /* REPORT_FATAL_TIMEOUTS */ +#ifdef DHD_DEBUG +#if defined(BCMSDIO) || defined(BCMPCIE) case IOV_GVAL(IOV_DONGLE_TRAP_TYPE): if (dhd_pub->dongle_trap_occured) int_val = ltoh32(dhd_pub->last_trap_info.type); @@ -1971,8 +1985,6 @@ dhd_doiovar(dhd_pub_t *dhd_pub, const bcm_iovar_t *vi, uint32 actionid, const ch dhd_bus_dump_trap_info(dhd_pub->bus, &strbuf); break; } -#ifdef DHD_DEBUG -#if defined(BCMSDIO) || defined(BCMPCIE) case IOV_GVAL(IOV_BPADDR): { @@ -2388,11 +2400,21 @@ dhd_ioctl(dhd_pub_t * dhd_pub, dhd_ioctl_t *ioc, void *buf, uint buflen) /* if still not found, try bus module */ if (ioc->cmd == DHD_GET_VAR) { +#ifdef BCMDBUS + bcmerror = dbus_iovar_op(dhd_pub->dbus, buf, + arg, arglen, buf, buflen, IOV_GET); +#else bcmerror = dhd_bus_iovar_op(dhd_pub, buf, arg, arglen, buf, buflen, IOV_GET); +#endif /* BCMDBUS */ } else { +#ifdef BCMDBUS + bcmerror = dbus_iovar_op(dhd_pub->dbus, buf, + NULL, 0, arg, arglen, IOV_SET); +#else bcmerror = dhd_bus_iovar_op(dhd_pub, buf, NULL, 0, arg, arglen, IOV_SET); +#endif /* BCMDBUS */ } if (bcmerror != BCME_UNSUPPORTED) { goto unlock_exit; @@ -2820,12 +2842,14 @@ dngl_host_event_process(dhd_pub_t *dhdp, bcm_dngl_event_t *event, #ifdef DHD_FW_COREDUMP dhdp->memdump_type = DUMP_TYPE_DONGLE_HOST_EVENT; #endif /* DHD_FW_COREDUMP */ +#ifndef BCMDBUS if (dhd_socram_dump(dhdp->bus)) { DHD_ERROR(("%s: socram dump failed\n", __FUNCTION__)); } else { /* Notify framework */ dhd_dbg_send_urgent_evt(dhdp, p, datalen); } +#endif /* !BCMDBUS */ } #endif /* DNGL_EVENT_SUPPORT */ @@ -3113,6 +3137,7 @@ wl_process_host_event(dhd_pub_t *dhd_pub, int *ifidx, void *pktdata, uint pktlen dhd_ifname2idx(dhd_pub->info, event->ifname), &event->addr.octet); break; +#ifndef BCMDBUS #if defined(DHD_FW_COREDUMP) case WLC_E_PSM_WATCHDOG: DHD_ERROR(("%s: WLC_E_PSM_WATCHDOG event received : \n", __FUNCTION__)); @@ -3121,6 +3146,7 @@ wl_process_host_event(dhd_pub_t *dhd_pub, int *ifidx, void *pktdata, uint pktlen } break; #endif +#endif /* !BCMDBUS */ #ifdef DHD_WMF case WLC_E_PSTA_PRIMARY_INTF_IND: dhd_update_psta_interface_for_sta(dhd_pub, event->ifname, @@ -3187,6 +3213,14 @@ wl_process_host_event(dhd_pub_t *dhd_pub, int *ifidx, void *pktdata, uint pktlen default: *ifidx = dhd_ifname2idx(dhd_pub->info, event->ifname); +#ifdef DHD_UPDATE_INTF_MAC + if ((WLC_E_LINK==type)&&(WLC_EVENT_MSG_LINK&flags)) { + dhd_event_ifchange(dhd_pub->info, + (struct wl_event_data_if *)event, + event->ifname, + event->addr.octet); + } +#endif /* DHD_UPDATE_INTF_MAC */ /* push up to external supp/auth */ dhd_event(dhd_pub->info, (char *)pvt_data, evlen, *ifidx); DHD_TRACE(("%s: MAC event %d, flags %x, status %x\n", @@ -4271,6 +4305,8 @@ dhd_get_suspend_bcn_li_dtim(dhd_pub_t *dhd) } } + if (dhd->conf->suspend_bcn_li_dtim >= 0) + bcn_li_dtim = dhd->conf->suspend_bcn_li_dtim; DHD_ERROR(("%s beacon=%d bcn_li_dtim=%d DTIM=%d Listen=%d\n", __FUNCTION__, ap_beacon, bcn_li_dtim, dtim_period, CUSTOM_LISTEN_INTERVAL)); diff --git a/bcmdhd.1.579.77.41.1.cn/dhd_config.c b/bcmdhd.1.579.77.41.1.cn/dhd_config.c index 5f968ba..2de7280 100644 --- a/bcmdhd.1.579.77.41.1.cn/dhd_config.c +++ b/bcmdhd.1.579.77.41.1.cn/dhd_config.c @@ -39,68 +39,6 @@ uint config_msg_level = CONFIG_ERROR_LEVEL; #define MAXSZ_BUF 1000 #define MAXSZ_CONFIG 4096 -#define FW_TYPE_STA 0 -#define FW_TYPE_APSTA 1 -#define FW_TYPE_P2P 2 -#define FW_TYPE_ES 3 -#define FW_TYPE_MFG 4 -#define FW_TYPE_G 0 -#define FW_TYPE_AG 1 - -#ifdef CONFIG_PATH_AUTO_SELECT -#ifdef BCMSDIO -#define CONFIG_BCM4330B2 "config_40183b2.txt" -#define CONFIG_BCM43362A0 "config_40181a0.txt" -#define CONFIG_BCM43362A2 "config_40181a2.txt" -#define CONFIG_BCM43438A0 "config_43438a0.txt" -#define CONFIG_BCM43438A1 "config_43438a1.txt" -#define CONFIG_BCM43436B0 "config_43436b0.txt" -#define CONFIG_BCM4334B1 "config_4334b1.txt" -#define CONFIG_BCM43341B0 "config_43341b0.txt" -#define CONFIG_BCM43241B4 "config_43241b4.txt" -#define CONFIG_BCM4339A0 "config_4339a0.txt" -#define CONFIG_BCM43454C0 "config_43454c0.txt" -#define CONFIG_BCM43455C0 "config_43455c0.txt" -#define CONFIG_BCM43456C5 "config_43456c5.txt" -#define CONFIG_BCM4354A1 "config_4354a1.txt" -#endif -#define CONFIG_BCM4356A2 "config_4356a2.txt" -#define CONFIG_BCM4358A3 "config_4358a3.txt" -#define CONFIG_BCM4359B1 "config_4359b1.txt" -#define CONFIG_BCM4359C0 "config_4359c0.txt" -#endif - -#ifdef BCMSDIO -#define SBSDIO_CIS_SIZE_LIMIT 0x200 - -#define FW_BCM4330B2 "fw_bcm40183b2" -#define FW_BCM4330B2_AG "fw_bcm40183b2_ag" -#define FW_BCM43362A0 "fw_bcm40181a0" -#define FW_BCM43362A2 "fw_bcm40181a2" -#define FW_BCM4334B1 "fw_bcm4334b1_ag" -#define FW_BCM43438A0 "fw_bcm43438a0" -#define FW_BCM43438A1 "fw_bcm43438a1" -#define FW_BCM43436B0 "fw_bcm43436b0" -#define FW_BCM43013B0 "fw_bcm43013b0" -#define FW_BCM43341B1 "fw_bcm43341b0_ag" -#define FW_BCM43241B4 "fw_bcm43241b4_ag" -#define FW_BCM4339A0 "fw_bcm4339a0_ag" -#define FW_BCM43455C0 "fw_bcm43455c0_ag" -#define FW_BCM43456C5 "fw_bcm43456c5_ag" -#define FW_BCM4354A1 "fw_bcm4354a1_ag" -#define FW_BCM4356A2 "fw_bcm4356a2_ag" -#define FW_BCM4358A3 "fw_bcm4358a3_ag" -#define FW_BCM4359B1 "fw_bcm4359b1_ag" -#define FW_BCM4359C0 "fw_bcm4359c0_ag" -#define FW_BCM43751 "fw_bcm43751_ag" - -#define CLM_BCM43013B0 "clm_bcm43013b0" -#endif -#ifdef BCMPCIE -#define FW_BCM4356A2 "fw_bcm4356a2_pcie_ag" -#define FW_BCM4359C0 "fw_bcm4359c0_pcie_ag" -#endif - #define htod32(i) i #define htod16(i) i #define dtoh32(i) i @@ -108,6 +46,55 @@ uint config_msg_level = CONFIG_ERROR_LEVEL; #define htodchanspec(i) i #define dtohchanspec(i) i +typedef struct cihp_name_map_t { + uint chip; + uint chiprev; + uint ag_type; + bool clm; + char *chip_name; + char *module_name; +} cihp_name_map_t; + +/* Map of WLC_E events to connection failure strings */ +#define DONT_CARE 9999 +const cihp_name_map_t chip_name_map [] = { + /* ChipID Chiprev AG CLM ChipName ModuleName */ +#ifdef BCMSDIO + {BCM43362_CHIP_ID, 0, DONT_CARE, FALSE, "bcm40181a0", ""}, + {BCM43362_CHIP_ID, 1, DONT_CARE, FALSE, "bcm40181a2", ""}, + {BCM4330_CHIP_ID, 4, FW_TYPE_G, FALSE, "bcm40183b2", ""}, + {BCM4330_CHIP_ID, 4, FW_TYPE_AG, FALSE, "bcm40183b2_ag", ""}, + {BCM43430_CHIP_ID, 0, DONT_CARE, FALSE, "bcm43438a0", ""}, + {BCM43430_CHIP_ID, 1, DONT_CARE, FALSE, "bcm43438a1", ""}, + {BCM43430_CHIP_ID, 2, DONT_CARE, FALSE, "bcm43436b0", ""}, + {BCM43012_CHIP_ID, 1, DONT_CARE, TRUE, "bcm43013b0", ""}, + {BCM4334_CHIP_ID, 3, DONT_CARE, FALSE, "bcm4334b1_ag", ""}, + {BCM43340_CHIP_ID, 2, DONT_CARE, FALSE, "bcm43341b0_ag", ""}, + {BCM43341_CHIP_ID, 2, DONT_CARE, FALSE, "bcm43341b0_ag", ""}, + {BCM4324_CHIP_ID, 5, DONT_CARE, FALSE, "bcm43241b4_ag", ""}, + {BCM4335_CHIP_ID, 2, DONT_CARE, FALSE, "bcm4339a0_ag", ""}, + {BCM4339_CHIP_ID, 1, DONT_CARE, FALSE, "bcm4339a0_ag", ""}, + {BCM4345_CHIP_ID, 6, DONT_CARE, FALSE, "bcm43455c0_ag", ""}, + {BCM43454_CHIP_ID, 6, DONT_CARE, FALSE, "bcm43455c0_ag", ""}, + {BCM4345_CHIP_ID, 9, DONT_CARE, FALSE, "bcm43456c5_ag", ""}, + {BCM43454_CHIP_ID, 9, DONT_CARE, FALSE, "bcm43455c5_ag", ""}, + {BCM4354_CHIP_ID, 1, DONT_CARE, FALSE, "bcm4354a1_ag", ""}, + {BCM4354_CHIP_ID, 2, DONT_CARE, FALSE, "bcm4356a2_ag", ""}, + {BCM4356_CHIP_ID, 2, DONT_CARE, FALSE, "bcm4356a2_ag", ""}, + {BCM4371_CHIP_ID, 2, DONT_CARE, FALSE, "bcm4356a2_ag", ""}, + {BCM43569_CHIP_ID, 3, DONT_CARE, FALSE, "bcm4358a3_ag", ""}, + {BCM4359_CHIP_ID, 5, DONT_CARE, FALSE, "bcm4359b1_ag", ""}, + {BCM4359_CHIP_ID, 9, DONT_CARE, FALSE, "bcm4359c0_ag", ""}, + {BCM4362_CHIP_ID, 0, DONT_CARE, TRUE, "bcm43752a0_ag", ""}, +#endif +#ifdef BCMPCIE + {BCM4354_CHIP_ID, 2, DONT_CARE, FALSE, "bcm4356a2_pcie_ag", ""}, + {BCM4356_CHIP_ID, 2, DONT_CARE, FALSE, "bcm4356a2_pcie_ag", ""}, + {BCM4359_CHIP_ID, 9, DONT_CARE, FALSE, "bcm4359c0_pcie_ag", ""}, + {BCM4362_CHIP_ID, 0, DONT_CARE, TRUE, "bcm43752a0_pcie_ag", ""}, +#endif +}; + #ifdef BCMSDIO void dhd_conf_free_mac_list(wl_mac_list_ctrl_t *mac_list) @@ -159,6 +146,7 @@ dhd_conf_set_hw_oob_intr(bcmsdh_info_t *sdh, uint chip) } #endif +#define SBSDIO_CIS_SIZE_LIMIT 0x200 #define F0_BLOCK_SIZE 32 int dhd_conf_set_blksize(bcmsdh_info_t *sdh) @@ -169,7 +157,7 @@ dhd_conf_set_blksize(bcmsdh_info_t *sdh) uint8 cisd; numfn = bcmsdh_query_iofnum(sdh); - + for (fn = 0; fn <= numfn; fn++) { if (!fn) blksize = F0_BLOCK_SIZE; @@ -291,8 +279,8 @@ dhd_conf_set_fw_name_by_mac(dhd_pub_t *dhd, bcmsdh_info_t *sdh, char *fw_path) uint32 oui, nic; wl_mac_list_t *mac_list; wl_mac_range_t *mac_range; - char *pfw_name; int fw_type, fw_type_new; + char *name_ptr; mac_list = dhd->conf->fw_by_mac.m_mac_list_head; fw_num = dhd->conf->fw_by_mac.count; @@ -309,22 +297,42 @@ dhd_conf_set_fw_name_by_mac(dhd_pub_t *dhd, bcmsdh_info_t *sdh, char *fw_path) /* find out the last '/' */ i = strlen(fw_path); while (i > 0) { - if (fw_path[i] == '/') break; + if (fw_path[i] == '/') { + i++; + break; + } i--; } - pfw_name = &fw_path[i+1]; - fw_type = (strstr(pfw_name, "_mfg") ? - FW_TYPE_MFG : (strstr(pfw_name, "_apsta") ? - FW_TYPE_APSTA : (strstr(pfw_name, "_p2p") ? - FW_TYPE_P2P : FW_TYPE_STA))); + name_ptr = &fw_path[i]; + + if (strstr(name_ptr, "_apsta")) + fw_type = FW_TYPE_APSTA; + else if (strstr(name_ptr, "_p2p")) + fw_type = FW_TYPE_P2P; + else if (strstr(name_ptr, "_mesh")) + fw_type = FW_TYPE_MESH; + else if (strstr(name_ptr, "_es")) + fw_type = FW_TYPE_ES; + else if (strstr(name_ptr, "_mfg")) + fw_type = FW_TYPE_MFG; + else + fw_type = FW_TYPE_STA; for (i=0; i= mac_range[j].nic_start && nic <= mac_range[j].nic_end) { - strcpy(pfw_name, mac_list[i].name); + strcpy(name_ptr, mac_list[i].name); printf("%s: matched oui=0x%06X, nic=0x%06X\n", __FUNCTION__, oui, nic); printf("%s: fw_path=%s\n", __FUNCTION__, fw_path); @@ -413,7 +421,7 @@ dhd_conf_set_fw_name_by_chip(dhd_pub_t *dhd, char *fw_path) int fw_type, ag_type; uint chip, chiprev; int i; - char fw_tail[20]; + char *name_ptr; chip = dhd->conf->chip; chiprev = dhd->conf->chiprev; @@ -441,125 +449,47 @@ dhd_conf_set_fw_name_by_chip(dhd_pub_t *dhd, char *fw_path) } i--; } + name_ptr = &fw_path[i]; #ifdef BAND_AG ag_type = FW_TYPE_AG; #else - ag_type = strstr(&fw_path[i], "_ag") ? FW_TYPE_AG : FW_TYPE_G; + ag_type = strstr(name_ptr, "_ag") ? FW_TYPE_AG : FW_TYPE_G; #endif - fw_type = (strstr(&fw_path[i], "_mfg") ? FW_TYPE_MFG : - (strstr(&fw_path[i], "_apsta") ? FW_TYPE_APSTA : - (strstr(&fw_path[i], "_p2p") ? FW_TYPE_P2P : - (strstr(&fw_path[i], "_es") ? FW_TYPE_ES : - FW_TYPE_STA)))); - - if (fw_type == FW_TYPE_STA) - strcpy(fw_tail, ".bin"); - else if (fw_type == FW_TYPE_APSTA) - strcpy(fw_tail, "_apsta.bin"); - else if (fw_type == FW_TYPE_P2P) - strcpy(fw_tail, "_p2p.bin"); - else if (fw_type == FW_TYPE_ES) - strcpy(fw_tail, "_es.bin"); - else if (fw_type == FW_TYPE_MFG) - strcpy(fw_tail, "_mfg.bin"); - - switch (chip) { -#ifdef BCMSDIO - case BCM4330_CHIP_ID: - if (ag_type == FW_TYPE_G) { - if (chiprev == BCM4330B2_CHIP_REV) - strcpy(&fw_path[i], FW_BCM4330B2); - } else { - if (chiprev == BCM4330B2_CHIP_REV) - strcpy(&fw_path[i], FW_BCM4330B2_AG); - } - break; - case BCM43362_CHIP_ID: - if (chiprev == BCM43362A0_CHIP_REV) - strcpy(&fw_path[i], FW_BCM43362A0); + if (strstr(name_ptr, "_apsta")) + fw_type = FW_TYPE_APSTA; + else if (strstr(name_ptr, "_p2p")) + fw_type = FW_TYPE_P2P; + else if (strstr(name_ptr, "_mesh")) + fw_type = FW_TYPE_MESH; + else if (strstr(name_ptr, "_es")) + fw_type = FW_TYPE_ES; + else if (strstr(name_ptr, "_mfg")) + fw_type = FW_TYPE_MFG; + else + fw_type = FW_TYPE_STA; + + for (i = 0; i < sizeof(chip_name_map)/sizeof(chip_name_map[0]); i++) { + const cihp_name_map_t* row = &chip_name_map[i]; + if (row->chip == chip && row->chiprev == chiprev && + (row->ag_type == ag_type || row->ag_type == DONT_CARE)) { + strcpy(name_ptr, "fw_"); + strcat(fw_path, row->chip_name); + if (fw_type == FW_TYPE_APSTA) + strcat(fw_path, "_apsta.bin"); + else if (fw_type == FW_TYPE_P2P) + strcat(fw_path, "_p2p.bin"); + else if (fw_type == FW_TYPE_MESH) + strcat(fw_path, "_mesh.bin"); + else if (fw_type == FW_TYPE_ES) + strcat(fw_path, "_es.bin"); + else if (fw_type == FW_TYPE_MFG) + strcat(fw_path, "_mfg.bin"); else - strcpy(&fw_path[i], FW_BCM43362A2); - break; - case BCM43430_CHIP_ID: - if (chiprev == BCM43430A0_CHIP_REV) - strcpy(&fw_path[i], FW_BCM43438A0); - else if (chiprev == BCM43430A1_CHIP_REV) - strcpy(&fw_path[i], FW_BCM43438A1); - else if (chiprev == BCM43430A2_CHIP_REV) - strcpy(&fw_path[i], FW_BCM43436B0); - break; - case BCM43012_CHIP_ID: - if (chiprev == BCM43013B0_CHIP_REV) - strcpy(&fw_path[i], FW_BCM43013B0); - break; - case BCM4334_CHIP_ID: - if (chiprev == BCM4334B1_CHIP_REV) - strcpy(&fw_path[i], FW_BCM4334B1); - break; - case BCM43340_CHIP_ID: - case BCM43341_CHIP_ID: - if (chiprev == BCM43341B0_CHIP_REV) - strcpy(&fw_path[i], FW_BCM43341B1); - break; - case BCM4324_CHIP_ID: - if (chiprev == BCM43241B4_CHIP_REV) - strcpy(&fw_path[i], FW_BCM43241B4); - break; - case BCM4335_CHIP_ID: - if (chiprev == BCM4335A0_CHIP_REV) - strcpy(&fw_path[i], FW_BCM4339A0); - break; - case BCM4339_CHIP_ID: - if (chiprev == BCM4339A0_CHIP_REV) - strcpy(&fw_path[i], FW_BCM4339A0); - break; - case BCM4345_CHIP_ID: - case BCM43454_CHIP_ID: - if (chiprev == BCM43455C0_CHIP_REV) - strcpy(&fw_path[i], FW_BCM43455C0); - else if (chiprev == BCM43456C5_CHIP_REV) - strcpy(&fw_path[i], FW_BCM43456C5); - break; - case BCM4354_CHIP_ID: - if (chiprev == BCM4354A1_CHIP_REV) - strcpy(&fw_path[i], FW_BCM4354A1); - else if (chiprev == BCM4356A2_CHIP_REV) - strcpy(&fw_path[i], FW_BCM4356A2); - break; - case BCM4356_CHIP_ID: - case BCM4371_CHIP_ID: - if (chiprev == BCM4356A2_CHIP_REV) - strcpy(&fw_path[i], FW_BCM4356A2); - break; - case BCM43569_CHIP_ID: - if (chiprev == BCM4358A3_CHIP_REV) - strcpy(&fw_path[i], FW_BCM4358A3); - break; - case BCM4359_CHIP_ID: - if (chiprev == BCM4359B1_CHIP_REV) - strcpy(&fw_path[i], FW_BCM4359B1); - else if (chiprev == BCM4359C0_CHIP_REV) - strcpy(&fw_path[i], FW_BCM4359C0); - break; - case BCM43751_CHIP_ID: - strcpy(&fw_path[i], FW_BCM43751); - break; -#endif -#ifdef BCMPCIE - case BCM4354_CHIP_ID: - case BCM4356_CHIP_ID: - if (chiprev == BCM4356A2_CHIP_REV) - strcpy(&fw_path[i], FW_BCM4356A2); - break; - case BCM4359_CHIP_ID: - if (chiprev == BCM4359C0_CHIP_REV) - strcpy(&fw_path[i], FW_BCM4359C0); - break; -#endif - default: - strcpy(&fw_path[i], "fw_bcmdhd"); + strcat(fw_path, ".bin"); + } } - strcat(fw_path, fw_tail); + + dhd->conf->fw_type = fw_type; CONFIG_TRACE(("%s: firmware_path=%s\n", __FUNCTION__, fw_path)); } @@ -569,7 +499,7 @@ dhd_conf_set_clm_name_by_chip(dhd_pub_t *dhd, char *clm_path) { uint chip, chiprev; int i; - char fw_tail[20]; + char *name_ptr; chip = dhd->conf->chip; chiprev = dhd->conf->chiprev; @@ -588,20 +518,16 @@ dhd_conf_set_clm_name_by_chip(dhd_pub_t *dhd, char *clm_path) } i--; } + name_ptr = &clm_path[i]; - strcpy(fw_tail, ".blob"); - - switch (chip) { -#ifdef BCMSDIO - case BCM43012_CHIP_ID: - if (chiprev == BCM43013B0_CHIP_REV) - strcpy(&clm_path[i], CLM_BCM43013B0); - break; -#endif - default: - strcpy(&clm_path[i], "clm_bcmdhd"); + for (i = 0; i < sizeof(chip_name_map)/sizeof(chip_name_map[0]); i++) { + const cihp_name_map_t* row = &chip_name_map[i]; + if (row->chip == chip && row->chiprev == chiprev && row->clm) { + strcpy(name_ptr, "clm_"); + strcat(clm_path, row->chip_name); + strcat(clm_path, ".blob"); + } } - strcat(clm_path, fw_tail); CONFIG_TRACE(("%s: clm_path=%s\n", __FUNCTION__, clm_path)); } @@ -609,23 +535,13 @@ dhd_conf_set_clm_name_by_chip(dhd_pub_t *dhd, char *clm_path) void dhd_conf_set_nv_name_by_chip(dhd_pub_t *dhd, char *nv_path) { - int matched=-1; uint chip, chiprev; int i; + char *name_ptr; chip = dhd->conf->chip; chiprev = dhd->conf->chiprev; - for (i=0; iconf->nv_by_chip.count; i++) { - if (chip==dhd->conf->nv_by_chip.m_chip_nv_path_head[i].chip && - chiprev==dhd->conf->nv_by_chip.m_chip_nv_path_head[i].chiprev) { - matched = i; - break; - } - } - if (matched < 0) - return; - if (nv_path[0] == '\0') { #ifdef CONFIG_BCMDHD_NVRAM_PATH bcm_strncpy_s(nv_path, MOD_PARAM_PATHLEN-1, CONFIG_BCMDHD_NVRAM_PATH, MOD_PARAM_PATHLEN-1); @@ -646,8 +562,22 @@ dhd_conf_set_nv_name_by_chip(dhd_pub_t *dhd, char *nv_path) } i--; } + name_ptr = &nv_path[i]; + + for (i = 0; i < sizeof(chip_name_map)/sizeof(chip_name_map[0]); i++) { + const cihp_name_map_t* row = &chip_name_map[i]; + if (row->chip == chip && row->chiprev == chiprev && strlen(row->module_name)) { + strcpy(name_ptr, row->module_name); + } + } - strcpy(&nv_path[i], dhd->conf->nv_by_chip.m_chip_nv_path_head[matched].name); + for (i=0; iconf->nv_by_chip.count; i++) { + if (chip==dhd->conf->nv_by_chip.m_chip_nv_path_head[i].chip && + chiprev==dhd->conf->nv_by_chip.m_chip_nv_path_head[i].chiprev) { + strcpy(name_ptr, dhd->conf->nv_by_chip.m_chip_nv_path_head[i].name); + break; + } + } CONFIG_TRACE(("%s: nvram_path=%s\n", __FUNCTION__, nv_path)); } @@ -683,6 +613,7 @@ dhd_conf_set_conf_name_by_chip(dhd_pub_t *dhd, char *conf_path) { uint chip, chiprev; int i; + char *name_ptr; chip = dhd->conf->chip; chiprev = dhd->conf->chiprev; @@ -701,91 +632,15 @@ dhd_conf_set_conf_name_by_chip(dhd_pub_t *dhd, char *conf_path) } i--; } + name_ptr = conf_path[i]; - switch (chip) { -#ifdef BCMSDIO - case BCM4330_CHIP_ID: - if (chiprev == BCM4330B2_CHIP_REV) - strcpy(&conf_path[i], CONFIG_BCM4330B2); - break; - case BCM43362_CHIP_ID: - if (chiprev == BCM43362A0_CHIP_REV) - strcpy(&conf_path[i], CONFIG_BCM43362A0); - else - strcpy(&conf_path[i], CONFIG_BCM43362A2); - break; - case BCM43430_CHIP_ID: - if (chiprev == BCM43430A0_CHIP_REV) - strcpy(&conf_path[i], CONFIG_BCM43438A0); - else if (chiprev == BCM43430A1_CHIP_REV) - strcpy(&conf_path[i], CONFIG_BCM43438A1); - else if (chiprev == BCM43430A2_CHIP_REV) - strcpy(&conf_path[i], CONFIG_BCM43436B0); - break; - case BCM4334_CHIP_ID: - if (chiprev == BCM4334B1_CHIP_REV) - strcpy(&conf_path[i], CONFIG_BCM4334B1); - break; - case BCM43340_CHIP_ID: - case BCM43341_CHIP_ID: - if (chiprev == BCM43341B0_CHIP_REV) - strcpy(&conf_path[i], CONFIG_BCM43341B0); - break; - case BCM4324_CHIP_ID: - if (chiprev == BCM43241B4_CHIP_REV) - strcpy(&conf_path[i], CONFIG_BCM43241B4); - break; - case BCM4335_CHIP_ID: - if (chiprev == BCM4335A0_CHIP_REV) - strcpy(&conf_path[i], CONFIG_BCM4339A0); - break; - case BCM43454_CHIP_ID: - if (chiprev == BCM43455C0_CHIP_REV) - strcpy(&conf_path[i], CONFIG_BCM43454C0); - break; - case BCM4345_CHIP_ID: - if (chiprev == BCM43455C0_CHIP_REV) - strcpy(&conf_path[i], CONFIG_BCM43455C0); - else if (chiprev == BCM43456C5_CHIP_REV) - strcpy(&conf_path[i], CONFIG_BCM43456C5); - break; - case BCM4339_CHIP_ID: - if (chiprev == BCM4339A0_CHIP_REV) - strcpy(&conf_path[i], CONFIG_BCM4339A0); - break; - case BCM4354_CHIP_ID: - if (chiprev == BCM4354A1_CHIP_REV) - strcpy(&conf_path[i], CONFIG_BCM4354A1); - else if (chiprev == BCM4356A2_CHIP_REV) - strcpy(&conf_path[i], CONFIG_BCM4356A2); - break; - case BCM4356_CHIP_ID: - case BCM4371_CHIP_ID: - if (chiprev == BCM4356A2_CHIP_REV) - strcpy(&conf_path[i], CONFIG_BCM4356A2); - break; - case BCM43569_CHIP_ID: - if (chiprev == BCM4358A3_CHIP_REV) - strcpy(&conf_path[i], CONFIG_BCM4358A3); - break; - case BCM4359_CHIP_ID: - if (chiprev == BCM4359B1_CHIP_REV) - strcpy(&conf_path[i], CONFIG_BCM4359B1); - else if (chiprev == BCM4359C0_CHIP_REV) - strcpy(&conf_path[i], CONFIG_BCM4359C0); - break; -#endif -#ifdef BCMPCIE - case BCM4354_CHIP_ID: - case BCM4356_CHIP_ID: - if (chiprev == BCM4356A2_CHIP_REV) - strcpy(&conf_path[i], CONFIG_BCM4356A2); - break; - case BCM4359_CHIP_ID: - if (chiprev == BCM4359C0_CHIP_REV) - strcpy(&conf_path[i], CONFIG_BCM4359C0); - break; -#endif + for (i = 0; i < sizeof(chip_name_map)/sizeof(chip_name_map[0]); i++) { + const cihp_name_map_t* row = &chip_name_map[i]; + if (row->chip == chip && row->chiprev == chiprev) { + strcpy(name_ptr, "config_"); + strcat(conf_path, row->chip_name); + strcat(conf_path, ".txt"); + } } CONFIG_TRACE(("%s: config_path=%s\n", __FUNCTION__, conf_path)); @@ -805,12 +660,12 @@ dhd_conf_set_intiovar(dhd_pub_t *dhd, uint cmd, char *name, int val, CONFIG_ERROR(("%s: WLC_DOWN setting failed %d\n", __FUNCTION__, ret)); } if (cmd == WLC_SET_VAR) { - printf("%s: set %s %d\n", __FUNCTION__, name, val); + CONFIG_TRACE(("%s: set %s %d\n", __FUNCTION__, name, val)); bcm_mkiovar(name, (char *)&val, sizeof(val), iovbuf, sizeof(iovbuf)); if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) CONFIG_ERROR(("%s: %s setting failed %d\n", __FUNCTION__, name, ret)); } else { - printf("%s: set %s %d %d\n", __FUNCTION__, name, cmd, val); + CONFIG_TRACE(("%s: set %s %d %d\n", __FUNCTION__, name, cmd, val)); if ((ret = dhd_wl_ioctl_cmd(dhd, cmd, &val, sizeof(val), TRUE, 0)) < 0) CONFIG_ERROR(("%s: %s setting failed %d\n", __FUNCTION__, name, ret)); } @@ -872,7 +727,7 @@ dhd_conf_get_iovar(dhd_pub_t *dhd, int cmd, char *name, char *buf, int len, int uint dhd_conf_get_band(dhd_pub_t *dhd) { - uint band = WLC_BAND_AUTO; + int band = -1; if (dhd && dhd->conf) band = dhd->conf->band; @@ -1214,16 +1069,11 @@ dhd_conf_add_pkt_filter(dhd_pub_t *dhd) #define MACS "%02x%02x%02x%02x%02x%02x" /* - * 1. Filter out all pkt: actually not to enable this since 4-way handshake will be filter out as well. - * 1) dhd_master_mode=0 - * 2) pkt_filter_add=99 0 0 0 0x000000000000 0x000000000000 - * 2. Filter in less pkt: ARP(0x0806, ID is 105), BRCM(0x886C), 802.1X(0x888E) + * Filter in less pkt: ARP(0x0806, ID is 105), BRCM(0x886C), 802.1X(0x888E) * 1) dhd_master_mode=1 * 2) pkt_filter_del=100, 102, 103, 104, 105 * 3) pkt_filter_add=131 0 0 12 0xFFFF 0x886C, 132 0 0 12 0xFFFF 0x888E - * 3. magic pkt: magic_pkt_filter_add=141 0 1 12 - * 4. Filter out netbios pkt: - * Netbios: 121 0 0 12 0xFFFF000000000000000000FF000000000000000000000000FFFF 0x0800000000000000000000110000000000000000000000000089 + * 4) magic_pkt_filter_add=141 0 1 12 */ for(i=0; iconf->pkt_filter_add.count; i++) { dhd->pktfilter[i+dhd->pktfilter_count] = dhd->conf->pkt_filter_add.filter[i]; @@ -1294,8 +1144,12 @@ dhd_conf_discard_pkt_filter(dhd_pub_t *dhd) int dhd_conf_get_pm(dhd_pub_t *dhd) { - if (dhd && dhd->conf) - return dhd->conf->pm; + if (dhd && dhd->conf) { + if (dhd->conf->fw_type == FW_TYPE_MESH) + return PM_OFF; + else + return dhd->conf->pm; + } return -1; } @@ -1482,6 +1336,12 @@ dhd_conf_read_log_level(dhd_pub_t *dhd, char *full_param, uint len_param) sd_msglevel = (int)simple_strtol(data, NULL, 0); printf("%s: sd_msglevel = 0x%X\n", __FUNCTION__, sd_msglevel); } +#endif +#ifdef BCMDBUS + else if (!strncmp("dbus_msglevel=", full_param, len_param)) { + dbus_msglevel = (int)simple_strtol(data, NULL, 0); + printf("%s: dbus_msglevel = 0x%X\n", __FUNCTION__, dbus_msglevel); + } #endif else if (!strncmp("android_msg_level=", full_param, len_param)) { android_msg_level = (int)simple_strtol(data, NULL, 0); @@ -1801,10 +1661,11 @@ bool dhd_conf_read_country_list(dhd_pub_t *dhd, char *full_param, uint len_param) { int i; - char *pch, *pick_tmp; + char *pch, *pick_tmp, *pick_tmp2; struct dhd_conf *conf = dhd->conf; char *data = full_param+len_param; wl_country_t *cspec; + conf_country_list_t *country_list = NULL; /* Process country_list: * country_list=[country1]:[ccode1]/[regrev1], @@ -1812,10 +1673,17 @@ dhd_conf_read_country_list(dhd_pub_t *dhd, char *full_param, uint len_param) * Ex: country_list=US:US/0, TW:TW/1 */ if (!strncmp("country_list=", full_param, len_param)) { + country_list = &dhd->conf->country_list; + } else if (!strncmp("country_list_nodfs=", full_param, len_param)) { + country_list = &dhd->conf->country_list_nodfs; + } + if (country_list) { pick_tmp = data; for (i=0; icountry_list.count++; memset(cspec, 0, sizeof(wl_country_t)); - conf->country_list.cspec[i] = cspec; strcpy(cspec->country_abbrev, pch); - pch = bcmstrtok(&pick_tmp, "/", 0); - if (!pch) + pch = bcmstrtok(&pick_tmp2, "/", 0); + if (!pch) { + kfree(cspec); break; + } memcpy(cspec->ccode, pch, 2); - pch = bcmstrtok(&pick_tmp, ", ", 0); - if (!pch) + pch = bcmstrtok(&pick_tmp2, "/", 0); + if (!pch) { + kfree(cspec); break; + } cspec->rev = (int32)simple_strtol(pch, NULL, 10); + country_list->count++; + country_list->cspec[i] = cspec; CONFIG_TRACE(("%s: country_list abbrev=%s, ccode=%s, regrev=%d\n", __FUNCTION__, cspec->country_abbrev, cspec->ccode, cspec->rev)); } - printf("%s: %d country in list\n", __FUNCTION__, conf->country_list.count); - } - else if (!strncmp("country_list_nodfs=", full_param, len_param)) { - pick_tmp = data; - for (i=0; icountry_list_nodfs.count++; - memset(cspec, 0, sizeof(wl_country_t)); - conf->country_list_nodfs.cspec[i] = cspec; - - strcpy(cspec->country_abbrev, pch); - pch = bcmstrtok(&pick_tmp, "/", 0); - if (!pch) - break; - memcpy(cspec->ccode, pch, 2); - pch = bcmstrtok(&pick_tmp, ", ", 0); - if (!pch) - break; - cspec->rev = (int32)simple_strtol(pch, NULL, 10); - CONFIG_TRACE(("%s: country_list_nodfs abbrev=%s, ccode=%s, regrev=%d\n", __FUNCTION__, - cspec->country_abbrev, cspec->ccode, cspec->rev)); + if (!strncmp("country_list=", full_param, len_param)) { + printf("%s: %d country in list\n", __FUNCTION__, conf->country_list.count); + } else if (!strncmp("country_list_nodfs=", full_param, len_param)) { + printf("%s: %d nodfs country in list\n", __FUNCTION__, conf->country_list.count); } - printf("%s: %d nodfs country in list\n", __FUNCTION__, conf->country_list_nodfs.count); } else return false; @@ -1885,7 +1731,7 @@ dhd_conf_read_mchan_params(dhd_pub_t *dhd, char *full_param, uint len_param) struct dhd_conf *conf = dhd->conf; char *data = full_param+len_param; - /* Process mchan_bw and btc_params_mgmt: + /* Process mchan_bw: * mchan_bw=[val]/[any/go/gc]/[any/source/sink] * Ex: mchan_bw=80/go/source, 30/gc/sink */ @@ -2103,24 +1949,14 @@ dhd_conf_read_sdio_params(dhd_pub_t *dhd, char *full_param, uint len_param) dhd_doflow = TRUE; printf("%s: dhd_doflow = %d\n", __FUNCTION__, dhd_doflow); } - else if (!strncmp("dhd_slpauto=", full_param, len_param)) { + else if (!strncmp("dhd_slpauto=", full_param, len_param) || + !strncmp("kso_enable=", full_param, len_param)) { if (!strncmp(data, "0", 1)) dhd_slpauto = FALSE; else dhd_slpauto = TRUE; printf("%s: dhd_slpauto = %d\n", __FUNCTION__, dhd_slpauto); } - else if (!strncmp("kso_enable=", full_param, len_param)) { - if (!strncmp(data, "0", 1)) - dhd_slpauto = FALSE; - else - dhd_slpauto = TRUE; - printf("%s: dhd_slpauto = %d\n", __FUNCTION__, dhd_slpauto); - } - else if (!strncmp("bus:txglom=", full_param, len_param)) { - conf->bus_txglom = (int)simple_strtol(data, NULL, 10); - printf("%s: bus:txglom = %d\n", __FUNCTION__, conf->bus_txglom); - } else if (!strncmp("use_rxchain=", full_param, len_param)) { conf->use_rxchain = (int)simple_strtol(data, NULL, 10); printf("%s: use_rxchain = %d\n", __FUNCTION__, conf->use_rxchain); @@ -2152,6 +1988,10 @@ dhd_conf_read_sdio_params(dhd_pub_t *dhd, char *full_param, uint len_param) conf->rxf_cpucore = (int)simple_strtol(data, NULL, 10); printf("%s: rxf_cpucore = %d\n", __FUNCTION__, conf->rxf_cpucore); } + else if (!strncmp("orphan_move=", full_param, len_param)) { + conf->orphan_move = (int)simple_strtol(data, NULL, 10); + printf("%s: orphan_move = %d\n", __FUNCTION__, conf->orphan_move); + } #if defined(BCMSDIOH_TXGLOM) else if (!strncmp("txglomsize=", full_param, len_param)) { conf->txglomsize = (uint)simple_strtol(data, NULL, 10); @@ -2242,11 +2082,7 @@ dhd_conf_read_pm_params(dhd_pub_t *dhd, char *full_param, uint len_param) struct dhd_conf *conf = dhd->conf; char *data = full_param+len_param; - if (!strncmp("lpc=", full_param, len_param)) { - conf->lpc = (int)simple_strtol(data, NULL, 10); - printf("%s: lpc = %d\n", __FUNCTION__, conf->lpc); - } - else if (!strncmp("deepsleep=", full_param, len_param)) { + if (!strncmp("deepsleep=", full_param, len_param)) { if (!strncmp(data, "1", 1)) conf->deepsleep = TRUE; else @@ -2261,9 +2097,9 @@ dhd_conf_read_pm_params(dhd_pub_t *dhd, char *full_param, uint len_param) conf->pm_in_suspend = (int)simple_strtol(data, NULL, 10); printf("%s: pm_in_suspend = %d\n", __FUNCTION__, conf->pm_in_suspend); } - else if (!strncmp("pm2_sleep_ret=", full_param, len_param)) { - conf->pm2_sleep_ret = (int)simple_strtol(data, NULL, 10); - printf("%s: pm2_sleep_ret = %d\n", __FUNCTION__, conf->pm2_sleep_ret); + else if (!strncmp("suspend_bcn_li_dtim=", full_param, len_param)) { + conf->suspend_bcn_li_dtim = (int)simple_strtol(data, NULL, 10); + printf("%s: suspend_bcn_li_dtim = %d\n", __FUNCTION__, conf->suspend_bcn_li_dtim); } else if (!strncmp("xmit_in_suspend=", full_param, len_param)) { if (!strncmp(data, "1", 1)) @@ -2323,17 +2159,13 @@ dhd_conf_read_others(dhd_pub_t *dhd, char *full_param, uint len_param) conf->band = WLC_BAND_AUTO; printf("%s: band = %d\n", __FUNCTION__, conf->band); } - else if (!strncmp("mimo_bw_cap=", full_param, len_param)) { - conf->mimo_bw_cap = (uint)simple_strtol(data, NULL, 10); - printf("%s: mimo_bw_cap = %d\n", __FUNCTION__, conf->mimo_bw_cap); - } else if (!strncmp("bw_cap_2g=", full_param, len_param)) { conf->bw_cap_2g = (uint)simple_strtol(data, NULL, 0); printf("%s: bw_cap_2g = %d\n", __FUNCTION__, conf->bw_cap_2g); } else if (!strncmp("bw_cap_5g=", full_param, len_param)) { conf->bw_cap_5g = (uint)simple_strtol(data, NULL, 0); - printf("%s: bw_cap_2g = %d\n", __FUNCTION__, conf->bw_cap_5g); + printf("%s: bw_cap_5g = %d\n", __FUNCTION__, conf->bw_cap_5g); } else if (!strncmp("ccode=", full_param, len_param)) { memset(&conf->cspec, 0, sizeof(wl_country_t)); @@ -2365,10 +2197,6 @@ dhd_conf_read_others(dhd_pub_t *dhd, char *full_param, uint len_param) printf("%s: keep_alive_period = %d\n", __FUNCTION__, conf->keep_alive_period); } - else if (!strncmp("stbc=", full_param, len_param)) { - conf->stbc = (int)simple_strtol(data, NULL, 10); - printf("%s: stbc = %d\n", __FUNCTION__, conf->stbc); - } else if (!strncmp("phy_oclscdenable=", full_param, len_param)) { conf->phy_oclscdenable = (int)simple_strtol(data, NULL, 10); printf("%s: phy_oclscdenable = %d\n", __FUNCTION__, conf->phy_oclscdenable); @@ -2385,18 +2213,6 @@ dhd_conf_read_others(dhd_pub_t *dhd, char *full_param, uint len_param) conf->bcn_timeout= (uint)simple_strtol(data, NULL, 10); printf("%s: bcn_timeout = %d\n", __FUNCTION__, conf->bcn_timeout); } - else if (!strncmp("ampdu_ba_wsize=", full_param, len_param)) { - conf->ampdu_ba_wsize = (int)simple_strtol(data, NULL, 10); - printf("%s: ampdu_ba_wsize = %d\n", __FUNCTION__, conf->ampdu_ba_wsize); - } - else if (!strncmp("ampdu_hostreorder=", full_param, len_param)) { - conf->ampdu_hostreorder = (int)simple_strtol(data, NULL, 10); - printf("%s: ampdu_hostreorder = %d\n", __FUNCTION__, conf->ampdu_hostreorder); - } - else if (!strncmp("spect=", full_param, len_param)) { - conf->spect = (int)simple_strtol(data, NULL, 10); - printf("%s: spect = %d\n", __FUNCTION__, conf->spect); - } else if (!strncmp("txbf=", full_param, len_param)) { conf->txbf = (int)simple_strtol(data, NULL, 10); printf("%s: txbf = %d\n", __FUNCTION__, conf->txbf); @@ -2419,6 +2235,7 @@ dhd_conf_read_others(dhd_pub_t *dhd, char *full_param, uint len_param) conf->pktprio8021x = (int)simple_strtol(data, NULL, 10); printf("%s: pktprio8021x = %d\n", __FUNCTION__, conf->pktprio8021x); } +#if defined(BCMSDIO) || defined(BCMPCIE) else if (!strncmp("dhd_txbound=", full_param, len_param)) { dhd_txbound = (uint)simple_strtol(data, NULL, 10); printf("%s: dhd_txbound = %d\n", __FUNCTION__, dhd_txbound); @@ -2427,25 +2244,11 @@ dhd_conf_read_others(dhd_pub_t *dhd, char *full_param, uint len_param) dhd_rxbound = (uint)simple_strtol(data, NULL, 10); printf("%s: dhd_rxbound = %d\n", __FUNCTION__, dhd_rxbound); } - else if (!strncmp("rsdb_mode=", full_param, len_param)) { - conf->rsdb_mode = (int)simple_strtol(data, NULL, 10); - printf("%s: rsdb_mode = %d\n", __FUNCTION__, conf->rsdb_mode); - } - else if (!strncmp("vhtmode=", full_param, len_param)) { - if (!strncmp(data, "0", 1)) - conf->vhtmode = 0; - else - conf->vhtmode = 1; - printf("%s: vhtmode = %d\n", __FUNCTION__, conf->vhtmode); - } +#endif else if (!strncmp("num_different_channels=", full_param, len_param)) { conf->num_different_channels = (int)simple_strtol(data, NULL, 10); printf("%s: num_different_channels = %d\n", __FUNCTION__, conf->num_different_channels); } - else if (!strncmp("autocountry=", full_param, len_param)) { - conf->autocountry = (int)simple_strtol(data, NULL, 10); - printf("%s: autocountry = %d\n", __FUNCTION__, conf->autocountry); - } else if (!strncmp("tsq=", full_param, len_param)) { conf->tsq = (int)simple_strtol(data, NULL, 10); printf("%s: tsq = %d\n", __FUNCTION__, conf->tsq); @@ -2458,6 +2261,15 @@ dhd_conf_read_others(dhd_pub_t *dhd, char *full_param, uint len_param) conf->dhd_ioctl_timeout_msec = (int)simple_strtol(data, NULL, 10); printf("%s: dhd_ioctl_timeout_msec = %d\n", __FUNCTION__, conf->dhd_ioctl_timeout_msec); } + else if (!strncmp("wl_preinit=", full_param, len_param)) { + if (!(conf->wl_preinit = kmalloc(len_param, GFP_KERNEL))) { + CONFIG_ERROR(("%s: kmalloc failed\n", __FUNCTION__)); + } else { + memset(conf->wl_preinit, 0, len_param); + strcpy(conf->wl_preinit, data); + printf("%s: wl_preinit = %s\n", __FUNCTION__, conf->wl_preinit); + } + } else return false; @@ -2468,7 +2280,7 @@ int dhd_conf_read_config(dhd_pub_t *dhd, char *conf_path) { int bcmerror = -1; - uint len, start_pos=0; + uint len = 0, start_pos=0; void * image = NULL; char * memblock = NULL; char *bufp, *pick = NULL, *pch; @@ -2634,7 +2446,7 @@ dhd_conf_set_txglom_params(dhd_pub_t *dhd, bool enable) #endif // other parameters set in preinit or config.txt } else { - // clear txglom parameters, but don't change swtxglom since it's possible enabled in config.txt + // clear txglom parameters conf->txglom_ext = FALSE; conf->txglom_bucket_size = 0; conf->txglomsize = 0; @@ -2643,8 +2455,10 @@ dhd_conf_set_txglom_params(dhd_pub_t *dhd, bool enable) if (conf->txglom_ext) printf("%s: txglom_ext=%d, txglom_bucket_size=%d\n", __FUNCTION__, conf->txglom_ext, conf->txglom_bucket_size); - printf("%s: txglomsize=%d, deferred_tx_len=%d, bus_txglom=%d\n", __FUNCTION__, - conf->txglomsize, conf->deferred_tx_len, conf->bus_txglom); + printf("%s: txglom_mode=%s, use_rxchain=%d\n", __FUNCTION__, + conf->txglom_mode==SDPCM_TXGLOM_MDESC?"multi-desc":"copy", conf->use_rxchain); + printf("%s: txglomsize=%d, deferred_tx_len=%d\n", __FUNCTION__, + conf->txglomsize, conf->deferred_tx_len); printf("%s: tx_in_rx=%d, txinrx_thres=%d, dhd_txminmax=%d\n", __FUNCTION__, conf->tx_in_rx, conf->txinrx_thres, conf->dhd_txminmax); printf("%s: tx_max_offset=%d, txctl_tmo_fix=%d\n", __FUNCTION__, @@ -2653,6 +2467,88 @@ dhd_conf_set_txglom_params(dhd_pub_t *dhd, bool enable) } #endif +bool +dhd_conf_set_wl_preinit(dhd_pub_t *dhd, char *data) +{ + int cmd, val; + char name[50], *pch, *pick_tmp, *pick_tmp2; + + /* Process wl_preinit: + * wl_preinit=[cmd]/[val], [cmd]/[val] \ + * Ex: wl_preinit=85/0, mpc/0 + */ + pick_tmp = data; + while (pick_tmp && (pick_tmp2 = bcmstrtok(&pick_tmp, ", ", 0)) != NULL) { + pch = bcmstrtok(&pick_tmp2, "=", 0); + if (!pch) + break; + memset(name, 0 , sizeof (name)); + cmd = (int)simple_strtol(pch, NULL, 0); + if (cmd == 0) { + cmd = WLC_SET_VAR; + strcpy(name, pch); + } + pch = bcmstrtok(&pick_tmp2, ", ", 0); + if (!pch) { + break; + } + val = (int)simple_strtol(pch, NULL, 0); + dhd_conf_set_intiovar(dhd, cmd, name, val, -1, TRUE); + } + + return true; +} + +void +dhd_conf_postinit_ioctls(dhd_pub_t *dhd) +{ + struct dhd_conf *conf = dhd->conf; + + dhd_conf_set_intiovar(dhd, WLC_UP, "up", 0, 0, FALSE); + dhd_conf_map_country_list(dhd, &conf->cspec, 0); + dhd_conf_set_country(dhd, &conf->cspec); + dhd_conf_fix_country(dhd); + dhd_conf_get_country(dhd, &dhd->dhd_cspec); + + dhd_conf_set_intiovar(dhd, WLC_SET_BAND, "WLC_SET_BAND", conf->band, 0, FALSE); + dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "bcn_timeout", conf->bcn_timeout, 0, FALSE); + dhd_conf_set_intiovar(dhd, WLC_SET_PM, "PM", conf->pm, 0, FALSE); + dhd_conf_set_intiovar(dhd, WLC_SET_SRL, "WLC_SET_SRL", conf->srl, 0, TRUE); + dhd_conf_set_intiovar(dhd, WLC_SET_LRL, "WLC_SET_LRL", conf->lrl, 0, FALSE); + dhd_conf_set_bw_cap(dhd); + dhd_conf_set_roam(dhd); + +#if defined(BCMPCIE) + dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "bus:deepsleep_disable", + conf->bus_deepsleep_disable, 0, FALSE); +#endif /* defined(BCMPCIE) */ + +#ifdef IDHCP + dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "dhcpc_enable", conf->dhcpc_enable, 0, FALSE); + if (dhd->conf->dhcpd_enable >= 0) { + dhd_conf_set_bufiovar(dhd, WLC_SET_VAR, "dhcpd_ip_addr", + (char *)&conf->dhcpd_ip_addr, sizeof(conf->dhcpd_ip_addr), FALSE); + dhd_conf_set_bufiovar(dhd, WLC_SET_VAR, "dhcpd_ip_mask", + (char *)&conf->dhcpd_ip_mask, sizeof(conf->dhcpd_ip_mask), FALSE); + dhd_conf_set_bufiovar(dhd, WLC_SET_VAR, "dhcpd_ip_start", + (char *)&conf->dhcpd_ip_start, sizeof(conf->dhcpd_ip_start), FALSE); + dhd_conf_set_bufiovar(dhd, WLC_SET_VAR, "dhcpd_ip_end", + (char *)&conf->dhcpd_ip_end, sizeof(conf->dhcpd_ip_end), FALSE); + dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "dhcpd_enable", + conf->dhcpd_enable, 0, FALSE); + } +#endif + dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "txbf", conf->txbf, 0, FALSE); + dhd_conf_set_intiovar(dhd, WLC_SET_FAKEFRAG, "WLC_SET_FAKEFRAG", conf->frameburst, 0, FALSE); + + dhd_conf_set_wl_preinit(dhd, conf->wl_preinit); + +#ifndef WL_CFG80211 + dhd_conf_set_intiovar(dhd, WLC_UP, "up", 0, 0, FALSE); +#endif + +} + int dhd_conf_preinit(dhd_pub_t *dhd) { @@ -2667,12 +2563,13 @@ dhd_conf_preinit(dhd_pub_t *dhd) dhd_conf_free_chip_nv_path_list(&conf->nv_by_chip); #endif dhd_conf_free_country_list(&conf->country_list); - dhd_conf_free_country_list(&dhd->conf->country_list_nodfs); + dhd_conf_free_country_list(&conf->country_list_nodfs); if (conf->magic_pkt_filter_add) kfree(conf->magic_pkt_filter_add); + if (conf->wl_preinit) + kfree(conf->wl_preinit); memset(&conf->country_list, 0, sizeof(conf_country_list_t)); conf->band = -1; - conf->mimo_bw_cap = -1; conf->bw_cap_2g = -1; conf->bw_cap_5g = -1; if (conf->chip == BCM43362_CHIP_ID || conf->chip == BCM4330_CHIP_ID) { @@ -2682,7 +2579,8 @@ dhd_conf_preinit(dhd_pub_t *dhd) } else if (conf->chip == BCM4335_CHIP_ID || conf->chip == BCM4339_CHIP_ID || conf->chip == BCM4354_CHIP_ID || conf->chip == BCM4356_CHIP_ID || conf->chip == BCM4345_CHIP_ID || conf->chip == BCM4371_CHIP_ID || - conf->chip == BCM43569_CHIP_ID || conf->chip == BCM4359_CHIP_ID) { + conf->chip == BCM43569_CHIP_ID || conf->chip == BCM4359_CHIP_ID || + conf->chip == BCM4362_CHIP_ID) { strcpy(conf->cspec.country_abbrev, "CN"); strcpy(conf->cspec.ccode, "CN"); conf->cspec.rev = 38; @@ -2721,7 +2619,6 @@ dhd_conf_preinit(dhd_pub_t *dhd) conf->force_wme_ac = 0; memset(&conf->wme_sta, 0, sizeof(wme_param_t)); memset(&conf->wme_ap, 0, sizeof(wme_param_t)); - conf->stbc = -1; conf->phy_oclscdenable = -1; #ifdef PKT_FILTER_SUPPORT memset(&conf->pkt_filter_add, 0, sizeof(conf_pkt_filter_add_t)); @@ -2730,39 +2627,35 @@ dhd_conf_preinit(dhd_pub_t *dhd) conf->srl = -1; conf->lrl = -1; conf->bcn_timeout = 16; - conf->spect = -1; conf->txbf = -1; - conf->lpc = -1; conf->disable_proptx = -1; conf->dhd_poll = -1; #ifdef BCMSDIO - conf->bus_txglom = -1; conf->use_rxchain = 0; conf->bus_rxglom = TRUE; conf->txglom_ext = FALSE; conf->tx_max_offset = 0; conf->txglomsize = SDPCM_DEFGLOM_SIZE; - conf->txctl_tmo_fix = 5; + conf->txctl_tmo_fix = 300; conf->tx_in_rx = TRUE; - conf->txglom_mode = SDPCM_TXGLOM_MDESC; + conf->txglom_mode = SDPCM_TXGLOM_CPY; conf->deferred_tx_len = 0; conf->dhd_txminmax = 1; conf->txinrx_thres = -1; conf->sd_f2_blocksize = 0; conf->oob_enabled_later = FALSE; + conf->orphan_move = 0; #endif #ifdef BCMPCIE conf->bus_deepsleep_disable = 1; #endif - conf->ampdu_ba_wsize = 0; - conf->ampdu_hostreorder = -1; conf->dpc_cpucore = -1; conf->rxf_cpucore = -1; conf->frameburst = -1; conf->deepsleep = FALSE; conf->pm = -1; conf->pm_in_suspend = -1; - conf->pm2_sleep_ret = -1; + conf->suspend_bcn_li_dtim = -1; conf->num_different_channels = -1; conf->xmit_in_suspend = TRUE; conf->ap_in_suspend = 0; @@ -2781,12 +2674,13 @@ dhd_conf_preinit(dhd_pub_t *dhd) conf->tsq = 0; #endif #ifdef DHDTCPACK_SUPPRESS +#ifdef BCMSDIO conf->tcpack_sup_mode = TCPACK_SUP_OFF; +#elif defined(BCMPCIE) + conf->tcpack_sup_mode = TCPACK_SUP_DEFAULT; +#endif #endif conf->pktprio8021x = -1; - conf->rsdb_mode = -2; - conf->vhtmode = -1; - conf->autocountry = -1; conf->ctrl_resched = 2; conf->dhd_ioctl_timeout_msec = 0; #ifdef IAPSTA_PREINIT @@ -2799,18 +2693,19 @@ dhd_conf_preinit(dhd_pub_t *dhd) } #ifdef CUSTOMER_HW_AMLOGIC dhd_slpauto = FALSE; - conf->txglom_mode = SDPCM_TXGLOM_CPY; #endif if (conf->chip == BCM4354_CHIP_ID || conf->chip == BCM4356_CHIP_ID || conf->chip == BCM4371_CHIP_ID || conf->chip == BCM43569_CHIP_ID || - conf->chip == BCM4359_CHIP_ID) { + conf->chip == BCM4359_CHIP_ID || conf->chip == BCM4362_CHIP_ID) { #ifdef DHDTCPACK_SUPPRESS #ifdef BCMSDIO conf->tcpack_sup_mode = TCPACK_SUP_REPLACE; #endif #endif +#if defined(BCMSDIO) || defined(BCMPCIE) dhd_rxbound = 128; dhd_txbound = 64; +#endif conf->txbf = 1; conf->frameburst = 1; #ifdef BCMSDIO @@ -2818,8 +2713,10 @@ dhd_conf_preinit(dhd_pub_t *dhd) conf->txinrx_thres = 128; conf->sd_f2_blocksize = CUSTOM_SDIO_F2_BLKSIZE; conf->oob_enabled_later = TRUE; -#ifdef CUSTOMER_HW_AMLOGIC - conf->rxf_cpucore = 2; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)) + conf->orphan_move = 1; +#else + conf->orphan_move = 0; #endif #endif } @@ -2830,9 +2727,6 @@ dhd_conf_preinit(dhd_pub_t *dhd) conf->chip == BCM43340_CHIP_ID || conf->chip == BCM43341_CHIP_ID || conf->chip == BCM4334_CHIP_ID || conf->chip == BCM4324_CHIP_ID) { conf->txglom_ext = TRUE; - conf->use_rxchain = 0; - conf->tx_in_rx = TRUE; - conf->tx_max_offset = 1; } else { conf->txglom_ext = FALSE; } @@ -2848,7 +2742,6 @@ dhd_conf_preinit(dhd_pub_t *dhd) #endif if (conf->txglomsize > SDPCM_MAXGLOM_SIZE) conf->txglomsize = SDPCM_MAXGLOM_SIZE; - conf->deferred_tx_len = 0; #endif return 0; @@ -2866,6 +2759,8 @@ dhd_conf_reset(dhd_pub_t *dhd) dhd_conf_free_country_list(&dhd->conf->country_list_nodfs); if (dhd->conf->magic_pkt_filter_add) kfree(dhd->conf->magic_pkt_filter_add); + if (dhd->conf->wl_preinit) + kfree(dhd->conf->wl_preinit); memset(dhd->conf, 0, sizeof(dhd_conf_t)); return 0; } @@ -2913,6 +2808,8 @@ dhd_conf_detach(dhd_pub_t *dhd) dhd_conf_free_country_list(&dhd->conf->country_list_nodfs); if (dhd->conf->magic_pkt_filter_add) kfree(dhd->conf->magic_pkt_filter_add); + if (dhd->conf->wl_preinit) + kfree(dhd->conf->wl_preinit); MFREE(dhd->osh, dhd->conf, sizeof(dhd_conf_t)); } dhd->conf = NULL; diff --git a/bcmdhd.1.579.77.41.1.cn/dhd_config.h b/bcmdhd.1.579.77.41.1.cn/dhd_config.h index ca295f1..06232d9 100644 --- a/bcmdhd.1.579.77.41.1.cn/dhd_config.h +++ b/bcmdhd.1.579.77.41.1.cn/dhd_config.h @@ -8,36 +8,27 @@ #include #include <802.11.h> +#define FW_TYPE_STA 0 +#define FW_TYPE_APSTA 1 +#define FW_TYPE_P2P 2 +#define FW_TYPE_MESH 3 +#define FW_TYPE_ES 4 +#define FW_TYPE_MFG 5 +#define FW_TYPE_G 0 +#define FW_TYPE_AG 1 + #define FW_PATH_AUTO_SELECT 1 //#define CONFIG_PATH_AUTO_SELECT extern char firmware_path[MOD_PARAM_PATHLEN]; +#if defined(BCMSDIO) || defined(BCMPCIE) extern uint dhd_rxbound; extern uint dhd_txbound; +#endif #ifdef BCMSDIO #define TXGLOM_RECV_OFFSET 8 extern uint dhd_doflow; extern uint dhd_slpauto; - -#define BCM43362A0_CHIP_REV 0 -#define BCM43362A2_CHIP_REV 1 -#define BCM43430A0_CHIP_REV 0 -#define BCM43430A1_CHIP_REV 1 -#define BCM43430A2_CHIP_REV 2 -#define BCM43013B0_CHIP_REV 1 -#define BCM4330B2_CHIP_REV 4 -#define BCM4334B1_CHIP_REV 3 -#define BCM43341B0_CHIP_REV 2 -#define BCM43241B4_CHIP_REV 5 -#define BCM4335A0_CHIP_REV 2 -#define BCM4339A0_CHIP_REV 1 -#define BCM43455C0_CHIP_REV 6 -#define BCM43456C5_CHIP_REV 9 -#define BCM4354A1_CHIP_REV 1 -#define BCM4359B1_CHIP_REV 5 #endif -#define BCM4356A2_CHIP_REV 2 -#define BCM4358A3_CHIP_REV 3 -#define BCM4359C0_CHIP_REV 9 typedef struct wl_mac_range { uint32 oui; @@ -113,13 +104,13 @@ typedef struct mchan_params { typedef struct dhd_conf { uint chip; uint chiprev; + int fw_type; wl_mac_list_ctrl_t fw_by_mac; wl_mac_list_ctrl_t nv_by_mac; wl_chip_nv_path_list_ctrl_t nv_by_chip; conf_country_list_t country_list; conf_country_list_t country_list_nodfs; int band; - int mimo_bw_cap; int bw_cap_2g; int bw_cap_5g; wl_country_t cspec; @@ -134,7 +125,6 @@ typedef struct dhd_conf { int force_wme_ac; wme_param_t wme_sta; wme_param_t wme_ap; - int stbc; int phy_oclscdenable; #ifdef PKT_FILTER_SUPPORT conf_pkt_filter_add_t pkt_filter_add; @@ -144,13 +134,10 @@ typedef struct dhd_conf { int srl; int lrl; uint bcn_timeout; - int spect; int txbf; - int lpc; int disable_proptx; int dhd_poll; #ifdef BCMSDIO - int bus_txglom; int use_rxchain; bool bus_rxglom; bool txglom_ext; /* Only for 43362/4330/43340/43341/43241 */ @@ -174,25 +161,22 @@ typedef struct dhd_conf { int dhd_txminmax; // -1=DATABUFCNT(bus) uint sd_f2_blocksize; bool oob_enabled_later; + int orphan_move; #endif #ifdef BCMPCIE int bus_deepsleep_disable; #endif - int ampdu_ba_wsize; - int ampdu_hostreorder; int dpc_cpucore; int rxf_cpucore; int frameburst; bool deepsleep; int pm; int pm_in_suspend; - int pm2_sleep_ret; + int suspend_bcn_li_dtim; #ifdef DHDTCPACK_SUPPRESS uint8 tcpack_sup_mode; #endif int pktprio8021x; - int rsdb_mode; - int vhtmode; int num_different_channels; int xmit_in_suspend; int ap_in_suspend; @@ -214,10 +198,10 @@ typedef struct dhd_conf { char iapsta_config[300]; char iapsta_enable[50]; #endif - int autocountry; int ctrl_resched; int dhd_ioctl_timeout_msec; struct mchan_params mchan[MCHAN_MAX_NUM]; + char *wl_preinit; int tsq; } dhd_conf_t; @@ -247,8 +231,6 @@ int dhd_conf_get_country(dhd_pub_t *dhd, wl_country_t *cspec); int dhd_conf_map_country_list(dhd_pub_t *dhd, wl_country_t *cspec, int nodfs); int dhd_conf_fix_country(dhd_pub_t *dhd); bool dhd_conf_match_channel(dhd_pub_t *dhd, uint32 channel); -int dhd_conf_set_roam(dhd_pub_t *dhd); -void dhd_conf_set_bw_cap(dhd_pub_t *dhd); void dhd_conf_set_wme(dhd_pub_t *dhd, int mode); void dhd_conf_set_mchan_bw(dhd_pub_t *dhd, int go, int source); void dhd_conf_add_pkt_filter(dhd_pub_t *dhd); @@ -264,6 +246,7 @@ int dhd_conf_get_disable_proptx(dhd_pub_t *dhd); #endif int dhd_conf_get_ap_mode_in_suspend(dhd_pub_t *dhd); int dhd_conf_set_ap_in_suspend(dhd_pub_t *dhd, int suspend); +void dhd_conf_postinit_ioctls(dhd_pub_t *dhd); int dhd_conf_preinit(dhd_pub_t *dhd); int dhd_conf_reset(dhd_pub_t *dhd); int dhd_conf_attach(dhd_pub_t *dhd); diff --git a/bcmdhd.1.579.77.41.1.cn/dhd_gpio.c b/bcmdhd.1.579.77.41.1.cn/dhd_gpio.c index 1d2ce75..abe37a4 100644 --- a/bcmdhd.1.579.77.41.1.cn/dhd_gpio.c +++ b/bcmdhd.1.579.77.41.1.cn/dhd_gpio.c @@ -176,7 +176,6 @@ static int dhd_wlan_get_mac_addr(unsigned char *buf) #ifdef EXAMPLE_GET_MAC_VER2 /* EXAMPLE code */ { - char mac[6] = {0x00,0x11,0x22,0x33,0x44,0xFF}; char macpad[56]= { 0x00,0xaa,0x9c,0x84,0xc7,0xbc,0x9b,0xf6, 0x02,0x33,0xa9,0x4d,0x5c,0xb4,0x0a,0x5d, @@ -185,7 +184,6 @@ static int dhd_wlan_get_mac_addr(unsigned char *buf) 0x4a,0xeb,0xf6,0xe6,0x3c,0xe7,0x5f,0xfc, 0x0e,0xa7,0xb3,0x0f,0x00,0xe4,0x4a,0xaf, 0x87,0x08,0x16,0x6d,0x3a,0xe3,0xc7,0x80}; - bcopy(mac, buf, sizeof(mac)); bcopy(macpad, buf+6, sizeof(macpad)); } #endif /* EXAMPLE_GET_MAC_VER2 */ @@ -278,33 +276,34 @@ int dhd_wlan_init_gpio(void) gpio_wl_host_wake = -1; #endif - printf("%s: GPIO(WL_REG_ON) = %d\n", __FUNCTION__, gpio_wl_reg_on); if (gpio_wl_reg_on >= 0) { err = gpio_request(gpio_wl_reg_on, "WL_REG_ON"); if (err < 0) { - printf("%s: Faiiled to request gpio %d for WL_REG_ON\n", + printf("%s: gpio_request(%d) for WL_REG_ON failed\n", __FUNCTION__, gpio_wl_reg_on); gpio_wl_reg_on = -1; } } #ifdef CUSTOMER_OOB - printf("%s: GPIO(WL_HOST_WAKE) = %d\n", __FUNCTION__, gpio_wl_host_wake); if (gpio_wl_host_wake >= 0) { err = gpio_request(gpio_wl_host_wake, "bcmdhd"); if (err < 0) { - printf("%s: gpio_request failed\n", __FUNCTION__); + printf("%s: gpio_request(%d) for WL_HOST_WAKE failed\n", + __FUNCTION__, gpio_wl_host_wake); return -1; } err = gpio_direction_input(gpio_wl_host_wake); if (err < 0) { - printf("%s: gpio_direction_input failed\n", __FUNCTION__); + printf("%s: gpio_direction_input(%d) for WL_HOST_WAKE failed\n", + __FUNCTION__, gpio_wl_host_wake); gpio_free(gpio_wl_host_wake); return -1; } host_oob_irq = gpio_to_irq(gpio_wl_host_wake); if (host_oob_irq < 0) { - printf("%s: gpio_to_irq failed\n", __FUNCTION__); + printf("%s: gpio_to_irq(%d) for WL_HOST_WAKE failed\n", + __FUNCTION__, gpio_wl_host_wake); gpio_free(gpio_wl_host_wake); return -1; } @@ -316,7 +315,6 @@ int dhd_wlan_init_gpio(void) host_oob_irq = wifi_irq_num(); #endif #endif - printf("%s: host_oob_irq: %d\n", __FUNCTION__, host_oob_irq); #ifdef HW_OOB #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)) @@ -337,7 +335,8 @@ int dhd_wlan_init_gpio(void) dhd_wlan_resources[0].start = dhd_wlan_resources[0].end = host_oob_irq; dhd_wlan_resources[0].flags = host_oob_irq_flags; - printf("%s: host_oob_irq_flags=0x%x\n", __FUNCTION__, host_oob_irq_flags); + printf("%s: WL_REG_ON=%d, WL_HOST_WAKE=%d\n", __FUNCTION__, gpio_wl_reg_on, gpio_wl_host_wake); + printf("%s: oob_irq=%d, oob_irq_flags=0x%x\n", __FUNCTION__, host_oob_irq, host_oob_irq_flags); #endif /* CUSTOMER_OOB */ return 0; diff --git a/bcmdhd.1.579.77.41.1.cn/dhd_linux.c b/bcmdhd.1.579.77.41.1.cn/dhd_linux.c index 66c81e0..1add605 100644 --- a/bcmdhd.1.579.77.41.1.cn/dhd_linux.c +++ b/bcmdhd.1.579.77.41.1.cn/dhd_linux.c @@ -348,11 +348,21 @@ static void dhd_hang_process(void *dhd_info, void *event_data, u8 event); MODULE_LICENSE("GPL and additional rights"); #endif /* LinuxVer */ +#if defined(MULTIPLE_SUPPLICANT) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) +DEFINE_MUTEX(_dhd_sdio_mutex_lock_); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) */ +#endif + #ifdef CONFIG_BCM_DETECT_CONSECUTIVE_HANG #define MAX_CONSECUTIVE_HANG_COUNTS 5 #endif /* CONFIG_BCM_DETECT_CONSECUTIVE_HANG */ +#ifdef BCMDBUS +#include +#else #include +#endif /* BCMDBUS */ #ifdef DHD_ULP #include @@ -619,6 +629,10 @@ typedef struct dhd_info { #ifdef PROP_TXSTATUS spinlock_t wlfc_spinlock; +#ifdef BCMDBUS + ulong wlfc_lock_flags; + ulong wlfc_pub_lock_flags; +#endif /* BCMDBUS */ #endif /* PROP_TXSTATUS */ #ifdef WLMEDIA_HTSF htsf_t htsf; @@ -640,10 +654,14 @@ typedef struct dhd_info { spinlock_t txqlock; spinlock_t rxqlock; spinlock_t dhd_lock; +#ifdef BCMDBUS + ulong txqlock_flags; +#else struct semaphore sdsem; tsk_ctl_t thr_dpc_ctl; tsk_ctl_t thr_wdt_ctl; +#endif /* BCMDBUS */ tsk_ctl_t thr_rxf_ctl; spinlock_t rxf_lock; @@ -953,6 +971,9 @@ static void dhd_ifdel_event_handler(void *handle, void *event_info, u8 event); static void dhd_set_mac_addr_handler(void *handle, void *event_info, u8 event); static void dhd_set_mcast_list_handler(void *handle, void *event_info, u8 event); +#ifdef DHD_UPDATE_INTF_MAC +static void dhd_ifupdate_event_handler(void *handle, void *event_info, u8 event); +#endif /* DHD_UPDATE_INTF_MAC */ #if defined(CONFIG_IPV6) && defined(IPV6_NDO_SUPPORT) static void dhd_inet6_work_handler(void *dhd_info, void *event_data, u8 event); #endif /* CONFIG_IPV6 && IPV6_NDO_SUPPORT */ @@ -1057,10 +1078,10 @@ module_param(dhd_dpc_prio, int, 0); int dhd_rxf_prio = CUSTOM_RXF_PRIO_SETTING; module_param(dhd_rxf_prio, int, 0); -#if !defined(BCMDHDUSB) +#if !defined(BCMDBUS) extern int dhd_dongle_ramsize; module_param(dhd_dongle_ramsize, int, 0); -#endif /* BCMDHDUSB */ +#endif /* !BCMDBUS */ #ifdef WL_CFG80211 int passive_channel_skip = 0; @@ -1906,9 +1927,11 @@ module_param(dhd_pktgen_len, uint, 0); +#ifndef BCMDBUS /* Allow delayed firmware download for debug purpose */ int allow_delay_fwdl = FALSE; module_param(allow_delay_fwdl, int, 0); +#endif /* !BCMDBUS */ extern char dhd_version[]; extern char fw_version[]; @@ -1941,7 +1964,9 @@ int dhd_monitor_uninit(void); struct iw_statistics *dhd_get_wireless_stats(struct net_device *dev); #endif /* defined(WL_WIRELESS_EXT) */ +#ifndef BCMDBUS static void dhd_dpc(ulong data); +#endif /* !BCMDBUS */ /* forward decl */ extern int dhd_wait_pend8021x(struct net_device *dev); void dhd_os_wd_timer_extend(void *bus, bool extend); @@ -1953,6 +1978,9 @@ void dhd_os_wd_timer_extend(void *bus, bool extend); static int dhd_toe_get(dhd_info_t *dhd, int idx, uint32 *toe_ol); static int dhd_toe_set(dhd_info_t *dhd, int idx, uint32 toe_ol); #endif /* TOE */ +#ifdef BCMDBUS +int dhd_dbus_txdata(dhd_pub_t *dhdp, void *pktbuf); +#endif /* BCMDBUS */ static int dhd_wl_host_event(dhd_info_t *dhd, int ifidx, void *pktdata, uint16 pktlen, wl_event_msg_t *event_ptr, void **data_ptr); @@ -3918,6 +3946,255 @@ dhd_bssidx2bssid(dhd_pub_t *dhdp, int idx) return NULL; } +#ifdef BCMDBUS +#define DBUS_NRXQ 50 +#define DBUS_NTXQ 100 + +static void +dhd_dbus_send_complete(void *handle, void *info, int status) +{ + dhd_info_t *dhd = (dhd_info_t *)handle; + void *pkt = info; + + if ((dhd == NULL) || (pkt == NULL)) + return; + + if (status == DBUS_OK) { + dhd->pub.dstats.tx_packets++; + } else { + DHD_ERROR(("TX error=%d\n", status)); + dhd->pub.dstats.tx_errors++; + } +#ifdef PROP_TXSTATUS + if (DHD_PKTTAG_WLFCPKT(PKTTAG(pkt)) && + (dhd_wlfc_txcomplete(&dhd->pub, pkt, status == 0) != WLFC_UNSUPPORTED)) { + return; + } +#endif /* PROP_TXSTATUS */ + PKTFREE(dhd->pub.osh, pkt, TRUE); +} + +static void +dhd_dbus_recv_pkt(void *handle, void *pkt) +{ + uchar reorder_info_buf[WLHOST_REORDERDATA_TOTLEN]; + uint reorder_info_len; + uint pkt_count; + dhd_info_t *dhd = (dhd_info_t *)handle; + int ifidx = 0; + + if (dhd == NULL) + return; + + /* If the protocol uses a data header, check and remove it */ + if (dhd_prot_hdrpull(&dhd->pub, &ifidx, pkt, reorder_info_buf, + &reorder_info_len) != 0) { + DHD_ERROR(("rx protocol error\n")); + PKTFREE(dhd->pub.osh, pkt, FALSE); + dhd->pub.rx_errors++; + return; + } + + if (reorder_info_len) { + /* Reordering info from the firmware */ + dhd_process_pkt_reorder_info(&dhd->pub, reorder_info_buf, reorder_info_len, + &pkt, &pkt_count); + if (pkt_count == 0) + return; + } + else { + pkt_count = 1; + } + dhd_rx_frame(&dhd->pub, ifidx, pkt, pkt_count, 0); +} + +static void +dhd_dbus_recv_buf(void *handle, uint8 *buf, int len) +{ + dhd_info_t *dhd = (dhd_info_t *)handle; + void *pkt; + + if (dhd == NULL) + return; + + if ((pkt = PKTGET(dhd->pub.osh, len, FALSE)) == NULL) { + DHD_ERROR(("PKTGET (rx) failed=%d\n", len)); + return; + } + + bcopy(buf, PKTDATA(dhd->pub.osh, pkt), len); + dhd_dbus_recv_pkt(dhd, pkt); +} + +static void +dhd_dbus_txflowcontrol(void *handle, bool onoff) +{ + dhd_info_t *dhd = (dhd_info_t *)handle; + bool wlfc_enabled = FALSE; + + if (dhd == NULL) + return; + +#ifdef PROP_TXSTATUS + wlfc_enabled = (dhd_wlfc_flowcontrol(&dhd->pub, onoff, !onoff) != WLFC_UNSUPPORTED); +#endif + + if (!wlfc_enabled) { + dhd_txflowcontrol(&dhd->pub, ALL_INTERFACES, onoff); + } +} + +static void +dhd_dbus_errhandler(void *handle, int err) +{ +} + +static void +dhd_dbus_ctl_complete(void *handle, int type, int status) +{ + dhd_info_t *dhd = (dhd_info_t *)handle; + + if (dhd == NULL) + return; + + if (type == DBUS_CBCTL_READ) { + if (status == DBUS_OK) + dhd->pub.rx_ctlpkts++; + else + dhd->pub.rx_ctlerrs++; + } else if (type == DBUS_CBCTL_WRITE) { + if (status == DBUS_OK) + dhd->pub.tx_ctlpkts++; + else + dhd->pub.tx_ctlerrs++; + } + + dhd_prot_ctl_complete(&dhd->pub); +} + +static void +dhd_dbus_state_change(void *handle, int state) +{ + dhd_info_t *dhd = (dhd_info_t *)handle; + + if (dhd == NULL) + return; + + switch (state) { + + case DBUS_STATE_DL_NEEDED: +#if defined(BCM_REQUEST_FW) +#if defined(BCMDBUS) + DHD_TRACE(("%s: firmware request\n", __FUNCTION__)); + up(&dhd->fw_download_lock); +#endif /* BCMDBUS */ +#else + DHD_ERROR(("%s: firmware request cannot be handled\n", __FUNCTION__)); +#endif + break; + case DBUS_STATE_DOWN: + DHD_TRACE(("%s: DBUS is down\n", __FUNCTION__)); + dhd->pub.busstate = DHD_BUS_DOWN; + break; + case DBUS_STATE_UP: + DHD_TRACE(("%s: DBUS is up\n", __FUNCTION__)); + dhd->pub.busstate = DHD_BUS_DATA; + break; + default: + break; + } + + printf("%s: DBUS current state=%d\n", __FUNCTION__, state); +} + +static void * +dhd_dbus_pktget(void *handle, uint len, bool send) +{ + dhd_info_t *dhd = (dhd_info_t *)handle; + void *p = NULL; + + if (dhd == NULL) + return NULL; + + if (send == TRUE) { + dhd_os_sdlock_txq(&dhd->pub); + p = PKTGET(dhd->pub.osh, len, TRUE); + dhd_os_sdunlock_txq(&dhd->pub); + } else { + dhd_os_sdlock_rxq(&dhd->pub); + p = PKTGET(dhd->pub.osh, len, FALSE); + dhd_os_sdunlock_rxq(&dhd->pub); + } + + return p; +} + +static void +dhd_dbus_pktfree(void *handle, void *p, bool send) +{ + dhd_info_t *dhd = (dhd_info_t *)handle; + + if (dhd == NULL) + return; + + if (send == TRUE) { +#ifdef PROP_TXSTATUS + if (DHD_PKTTAG_WLFCPKT(PKTTAG(p)) && + (dhd_wlfc_txcomplete(&dhd->pub, p, FALSE) != WLFC_UNSUPPORTED)) { + return; + } +#endif /* PROP_TXSTATUS */ + + dhd_os_sdlock_txq(&dhd->pub); + PKTFREE(dhd->pub.osh, p, TRUE); + dhd_os_sdunlock_txq(&dhd->pub); + } else { + dhd_os_sdlock_rxq(&dhd->pub); + PKTFREE(dhd->pub.osh, p, FALSE); + dhd_os_sdunlock_rxq(&dhd->pub); + } +} + + +static dbus_callbacks_t dhd_dbus_cbs = { + dhd_dbus_send_complete, + dhd_dbus_recv_buf, + dhd_dbus_recv_pkt, + dhd_dbus_txflowcontrol, + dhd_dbus_errhandler, + dhd_dbus_ctl_complete, + dhd_dbus_state_change, + dhd_dbus_pktget, + dhd_dbus_pktfree +}; + +void +dhd_bus_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf) +{ + bcm_bprintf(strbuf, "Bus USB\n"); +} + +void +dhd_bus_clearcounts(dhd_pub_t *dhdp) +{ +} + +bool +dhd_bus_dpc(struct dhd_bus *bus) +{ + return FALSE; +} + +int +dhd_dbus_txdata(dhd_pub_t *dhdp, void *pktbuf) +{ + + if (dhdp->txoff) + return BCME_EPERM; + return dbus_send_txdata(dhdp->dbus, pktbuf); +} + +#endif /* BCMDBUS */ static void _dhd_set_multicast_list(dhd_info_t *dhd, int ifidx) @@ -4357,6 +4634,87 @@ done: dhd_net_if_unlock_local(dhd); } +#ifdef DHD_UPDATE_INTF_MAC +static void +dhd_ifupdate_event_handler(void *handle, void *event_info, u8 event) +{ + dhd_info_t *dhd = handle; + int ifidx; + dhd_if_event_t *if_event = event_info; + + if (event != DHD_WQ_WORK_IF_UPDATE) { + DHD_ERROR(("%s: unexpected event \n", __FUNCTION__)); + return; + } + + if (!dhd) { + DHD_ERROR(("%s: dhd info not available \n", __FUNCTION__)); + return; + } + + if (!if_event) { + DHD_ERROR(("%s: event data is null \n", __FUNCTION__)); + return; + } + + dhd_net_if_lock_local(dhd); + DHD_OS_WAKE_LOCK(&dhd->pub); + + ifidx = if_event->event.ifidx; + DHD_TRACE(("%s: Update interface with idx %d\n", __FUNCTION__, ifidx)); + + dhd_op_if_update(&dhd->pub, ifidx); + + MFREE(dhd->pub.osh, if_event, sizeof(dhd_if_event_t)); + + DHD_OS_WAKE_UNLOCK(&dhd->pub); + dhd_net_if_unlock_local(dhd); +} + +int dhd_op_if_update(dhd_pub_t *dhdpub, int ifidx) +{ + dhd_info_t * dhdinfo = NULL; + dhd_if_t * ifp = NULL; + int ret = 0; + char buf[128]; + + if ((NULL==dhdpub)||(NULL==dhdpub->info)) { + DHD_ERROR(("%s: *** DHD handler is NULL!\n", __FUNCTION__)); + return -1; + } else { + dhdinfo = (dhd_info_t *)dhdpub->info; + ifp = dhdinfo->iflist[ifidx]; + if (NULL==ifp) { + DHD_ERROR(("%s: *** ifp handler is NULL!\n", __FUNCTION__)); + return -2; + } + } + + DHD_TRACE(("%s: idx %d\n", __FUNCTION__, ifidx)); + // Get MAC address + strcpy(buf, "cur_etheraddr"); + ret = dhd_wl_ioctl_cmd(&dhdinfo->pub, WLC_GET_VAR, buf, sizeof(buf), FALSE, ifp->idx); + if (0>ret) { + DHD_ERROR(("Failed to upudate the MAC address for itf=%s, ret=%d\n", ifp->name, ret)); + // avoid collision + dhdinfo->iflist[ifp->idx]->mac_addr[5] += 1; + // force locally administrate address + ETHER_SET_LOCALADDR(&dhdinfo->iflist[ifp->idx]->mac_addr); + } else { + DHD_EVENT(("Got mac for itf %s, idx %d, MAC=%02X:%02X:%02X:%02X:%02X:%02X\n", + ifp->name, ifp->idx, + (unsigned char)buf[0], (unsigned char)buf[1], (unsigned char)buf[2], + (unsigned char)buf[3], (unsigned char)buf[4], (unsigned char)buf[5])); + memcpy(dhdinfo->iflist[ifp->idx]->mac_addr, buf, ETHER_ADDR_LEN); + if (dhdinfo->iflist[ifp->idx]->net) { + memcpy(dhdinfo->iflist[ifp->idx]->net->dev_addr, buf, ETHER_ADDR_LEN); + } + } + + return ret; +} +#endif /* DHD_UPDATE_INTF_MAC */ + static void dhd_set_mac_addr_handler(void *handle, void *event_info, u8 event) { @@ -4539,7 +4897,11 @@ dhd_os_wlfc_block(dhd_pub_t *pub) /* terence 20161229: don't do spin lock if proptx not enabled */ if (disable_proptx) return 1; +#ifdef BCMDBUS + spin_lock_irqsave(&di->wlfc_spinlock, di->wlfc_lock_flags); +#else spin_lock_bh(&di->wlfc_spinlock); +#endif /* BCMDBUS */ return 1; } @@ -4552,7 +4914,11 @@ dhd_os_wlfc_unblock(dhd_pub_t *pub) /* terence 20161229: don't do spin lock if proptx not enabled */ if (disable_proptx) return 1; +#ifdef BCMDBUS + spin_unlock_irqrestore(&di->wlfc_spinlock, di->wlfc_lock_flags); +#else spin_unlock_bh(&di->wlfc_spinlock); +#endif /* BCMDBUS */ return 1; } @@ -4869,6 +5235,20 @@ __dhd_sendpkt(dhd_pub_t *dhdp, int ifidx, void *pktbuf) #ifdef WLMEDIA_HTSF dhd_htsf_addtxts(dhdp, pktbuf); #endif + +#ifdef BCMDBUS +#ifdef PROP_TXSTATUS + if (dhd_wlfc_commit_packets(dhdp, (f_commitpkt_t)dhd_dbus_txdata, + dhdp, pktbuf, TRUE) == WLFC_UNSUPPORTED) { + /* non-proptxstatus way */ + ret = dhd_dbus_txdata(dhdp, pktbuf); + } +#else + ret = dhd_dbus_txdata(dhdp, pktbuf); +#endif /* PROP_TXSTATUS */ + if (ret) + PKTCFREE(dhdp->osh, pktbuf, TRUE); +#else #ifdef PROP_TXSTATUS { if (dhd_wlfc_commit_packets(dhdp, (f_commitpkt_t)dhd_bus_txdata, @@ -4889,6 +5269,7 @@ __dhd_sendpkt(dhd_pub_t *dhdp, int ifidx, void *pktbuf) #endif /* BCMPCIE */ #endif /* PROP_TXSTATUS */ +#endif /* BCMDBUS */ return ret; } @@ -5653,8 +6034,12 @@ dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt, uint8 chan) continue; } #ifdef DHD_WAKE_STATUS +#ifdef BCMDBUS + wcp = NULL; +#else pkt_wake = dhd_bus_get_bus_wake(dhdp); wcp = dhd_bus_get_wakecount(dhdp); +#endif /* BCMDBUS */ if (wcp == NULL) { /* If wakeinfo count buffer is null do not update wake count values */ pkt_wake = 0; @@ -6096,8 +6481,10 @@ dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt, uint8 chan) #endif /* DHD_WAKE_STATUS */ } +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0) if (ifp->net) ifp->net->last_rx = jiffies; +#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0) */ if (ntoh16(skb->protocol) != ETHER_TYPE_BRCM) { dhdp->dstats.rx_bytes += skb->len; @@ -6244,6 +6631,7 @@ error: return &net->stats; } +#ifndef BCMDBUS static int dhd_watchdog_thread(void *data) { @@ -6735,6 +7123,7 @@ dhd_sched_dpc(dhd_pub_t *dhdp) tasklet_schedule(&dhd->tasklet); } } +#endif /* BCMDBUS */ static void dhd_sched_rxf(dhd_pub_t *dhdp, void *skb) @@ -7005,12 +7394,12 @@ static bool dhd_check_hang(struct net_device *net, dhd_pub_t *dhdp, int error) if (!dhdp->up) return FALSE; -#if !defined(BCMPCIE) +#if !defined(BCMPCIE) && !defined(BCMDBUS) if (dhdp->info->thr_dpc_ctl.thr_pid < 0) { DHD_ERROR(("%s : skipped due to negative pid - unloading?\n", __FUNCTION__)); return FALSE; } -#endif +#endif /* !BCMPCIE && !BCMDBUS */ if ((error == -ETIMEDOUT) || (error == -EREMOTEIO) || ((dhdp->busstate == DHD_BUS_DOWN) && (!dhdp->dongle_reset))) { @@ -7384,6 +7773,7 @@ int dhd_ioctl_process(dhd_pub_t *pub, int ifidx, dhd_ioctl_t *ioc, void *data_bu if (data_buf) buflen = MIN(ioc->len, WLC_IOCTL_MAXLEN); +#ifndef BCMDBUS /* send to dongle (must be up, and wl). */ if (pub->busstate == DHD_BUS_DOWN || pub->busstate == DHD_BUS_LOAD) { if ((!pub->dongle_trap_occured) && allow_delay_fwdl) { @@ -7409,6 +7799,7 @@ int dhd_ioctl_process(dhd_pub_t *pub, int ifidx, dhd_ioctl_t *ioc, void *data_bu bcmerror = BCME_DONGLE_DOWN; goto done; } +#endif /* !BCMDBUS */ /* * Flush the TX queue if required for proper message serialization: @@ -8206,7 +8597,7 @@ exit: #else wl_android_wifi_off(net, TRUE); #ifdef WL_EXT_IAPSTA - wl_android_ext_dettach_netdev(); + wl_ext_iapsta_dettach_netdev(); #endif } else { if (dhd->pub.conf->deepsleep) @@ -8280,6 +8671,10 @@ dhd_open(struct net_device *net) uint32 slot_num = -1; wifi_adapter_info_t *adapter = NULL; #endif +#if defined(WL_EXT_IAPSTA) && defined(IAPSTA_PREINIT) + int bytes_written = 0; + struct dhd_conf *conf; +#endif if (!dhd_download_fw_on_driverload) { if (!dhd_driver_init_done) { @@ -8380,8 +8775,15 @@ dhd_open(struct net_device *net) if (ifidx == 0) { atomic_set(&dhd->pend_8021x_cnt, 0); +#ifdef BCMDBUS + dhd_update_fw_nv_path(dhd); // terence 20140807: fix for op_mode issue + dhd_conf_read_config(&dhd->pub, dhd->pub.conf_path); +#endif /* BCMDBUS */ if (!dhd_download_fw_on_driverload) { DHD_ERROR(("\n%s\n", dhd_version)); +#ifdef WL_EXT_IAPSTA + wl_ext_iapsta_attach_netdev(net, ifidx); +#endif #if defined(USE_INITIAL_SHORT_DWELL_TIME) g_first_broadcast_scan = TRUE; #endif @@ -8397,6 +8799,14 @@ dhd_open(struct net_device *net) ret = -1; goto exit; } +#if defined(WL_EXT_IAPSTA) && defined(IAPSTA_PREINIT) + conf = dhd_get_conf(net); + if (conf) { + wl_android_ext_priv_cmd(net, conf->iapsta_init, 0, &bytes_written); + wl_android_ext_priv_cmd(net, conf->iapsta_config, 0, &bytes_written); + wl_android_ext_priv_cmd(net, conf->iapsta_enable, 0, &bytes_written); + } +#endif } #ifdef FIX_CPU_MIN_CLOCK if (dhd_get_fw_mode(dhd) == DHD_FLAG_HOSTAP_MODE) { @@ -8417,6 +8827,7 @@ dhd_open(struct net_device *net) if (dhd->pub.busstate != DHD_BUS_DATA) { +#ifndef BCMDBUS /* try to bring up bus */ DHD_PERIM_UNLOCK(&dhd->pub); ret = dhd_bus_start(&dhd->pub); @@ -8426,6 +8837,19 @@ dhd_open(struct net_device *net) ret = -1; goto exit; } +#else + if ((ret = dbus_up(dhd->pub.dbus)) != 0) { + DHD_ERROR(("%s: failed to dbus_up with code %d\n", __FUNCTION__, ret)); + goto exit; + } else { + dhd->pub.busstate = DHD_BUS_DATA; + } + + if ((ret = dhd_sync_with_dongle(&dhd->pub)) < 0) { + DHD_ERROR(("%s: failed with code %d\n", __FUNCTION__, ret)); + goto exit; + } +#endif /* !BCMDBUS */ } if (dhd_download_fw_on_driverload) { @@ -8536,9 +8960,6 @@ dhd_open(struct net_device *net) } argos_register_notifier_init(net); -#if defined(DHDTCPACK_SUPPRESS) - dhd_tcpack_suppress_set(&dhd->pub, TCPACK_SUP_DEFAULT); -#endif /* DHDTCPACK_SUPPRESS */ #if defined(NUM_SCB_MAX_PROBE) dhd_set_scb_probe(&dhd->pub); #endif /* NUM_SCB_MAX_PROBE */ @@ -8676,10 +9097,36 @@ dhd_event_ifdel(dhd_info_t *dhdinfo, wl_event_data_if_t *ifevent, char *name, ui int dhd_event_ifchange(dhd_info_t *dhdinfo, wl_event_data_if_t *ifevent, char *name, uint8 *mac) { +#ifdef DHD_UPDATE_INTF_MAC + dhd_if_event_t *if_event; +#endif /* DHD_UPDATE_INTF_MAC */ + #ifdef WL_CFG80211 wl_cfg80211_notify_ifchange(dhd_linux_get_primary_netdev(&dhdinfo->pub), ifevent->ifidx, name, mac, ifevent->bssidx); #endif /* WL_CFG80211 */ + +#ifdef DHD_UPDATE_INTF_MAC + /* handle IF event caused by wl commands, SoftAP, WEXT, MBSS and + * anything else + */ + if_event = MALLOC(dhdinfo->pub.osh, sizeof(dhd_if_event_t)); + if (if_event == NULL) { + DHD_ERROR(("dhd_event_ifdel: malloc failed for if_event, malloced %d bytes", + MALLOCED(dhdinfo->pub.osh))); + return BCME_NOMEM; + } + memcpy(&if_event->event, ifevent, sizeof(if_event->event)); + // construct a change event + if_event->event.ifidx = dhd_ifname2idx(dhdinfo, name); + if_event->event.opcode = WLC_E_IF_CHANGE; + memcpy(if_event->mac, mac, ETHER_ADDR_LEN); + strncpy(if_event->name, name, IFNAMSIZ); + if_event->name[IFNAMSIZ - 1] = '\0'; + dhd_deferred_schedule_work(dhdinfo->dhd_deferred_wq, (void *)if_event, DHD_WQ_WORK_IF_UPDATE, + dhd_ifupdate_event_handler, DHD_WQ_WORK_PRIORITY_LOW); +#endif /* DHD_UPDATE_INTF_MAC */ + return BCME_OK; } @@ -8764,10 +9211,20 @@ dhd_allocate_if(dhd_pub_t *dhdpub, int ifidx, const char *name, } #ifdef WL_CFG80211 - if (ifidx == 0) + if (ifidx == 0) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0) ifp->net->destructor = free_netdev; - else +#else + ifp->net->needs_free_netdev = true; +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) */ + } else { +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0) ifp->net->destructor = dhd_netdev_free; +#else + ifp->net->needs_free_netdev = true; + ifp->net->priv_destructor = dhd_netdev_free; +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) */ + } #else ifp->net->destructor = free_netdev; #endif /* WL_CFG80211 */ @@ -9215,6 +9672,7 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) dhd->pub.dhd_cspec.country_abbrev, &dhd->pub.dhd_cspec, dhd->pub.dhd_cflags); #endif /* CUSTOM_COUNTRY_CODE */ +#ifndef BCMDBUS dhd->thr_dpc_ctl.thr_pid = DHD_PID_KT_TL_INVALID; dhd->thr_wdt_ctl.thr_pid = DHD_PID_KT_INVALID; #ifdef DHD_WET @@ -9222,6 +9680,7 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) #endif /* DHD_WET */ /* Initialize thread based operation and lock */ sema_init(&dhd->sdsem, 1); +#endif /* !BCMDBUS */ /* Link to info module */ dhd->pub.info = dhd; @@ -9231,6 +9690,7 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) dhd->pub.bus = bus; dhd->pub.hdrlen = bus_hdrlen; +#ifndef BCMDBUS /* dhd_conf must be attached after linking dhd to dhd->pub.info, * because dhd_detech will check .info is NULL or not. */ @@ -9241,6 +9701,7 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) dhd_conf_reset(&dhd->pub); dhd_conf_set_chiprev(&dhd->pub, dhd_bus_chip(bus), dhd_bus_chiprev(bus)); dhd_conf_preinit(&dhd->pub); +#endif /* !BCMDBUS */ /* Some DHD modules (e.g. cfg80211) configures operation mode based on firmware name. * This is indeed a hack but we have to make it work properly before we have a better @@ -9434,6 +9895,7 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) +#ifndef BCMDBUS /* Set up the watchdog timer */ init_timer(&dhd->timer); dhd->timer.data = (ulong)dhd; @@ -9493,6 +9955,7 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) goto fail; } } +#endif /* !BCMDBUS */ dhd_state |= DHD_ATTACH_STATE_THREADS_CREATED; @@ -9855,6 +10318,20 @@ bool dhd_update_fw_nv_path(dhd_info_t *dhdinfo) return FALSE; } #endif /* BCMEMBEDIMAGE */ +#ifdef BCMDBUS + if (dhdinfo->conf_path[0] == '\0') { + dhd_conf_set_path(&dhdinfo->pub, "config.txt", dhdinfo->conf_path, dhdinfo->fw_path); + } else { + dhdinfo->pub.conf_path = dhdinfo->conf_path; + printf("%s: conf_path=%s\n", __FUNCTION__, dhdinfo->conf_path); + } + if (dhdinfo->clm_path[0] == '\0') { + dhd_conf_set_path(&dhdinfo->pub, "clm.blob", dhdinfo->clm_path, dhdinfo->fw_path); + } else { + dhdinfo->pub.clm_path= dhdinfo->clm_path; + printf("%s: clm_path=%s\n", __FUNCTION__, dhdinfo->clm_path); + } +#endif /* BCMDBUS */ return TRUE; } @@ -10014,6 +10491,7 @@ int dhd_download_btfw(wlan_bt_handle_t handle, char* btfw_path) } EXPORT_SYMBOL(dhd_download_btfw); #endif /* defined (BT_OVER_SDIO) */ +#ifndef BCMDBUS int dhd_bus_start(dhd_pub_t *dhdp) { @@ -10195,6 +10673,7 @@ dhd_bus_start(dhd_pub_t *dhdp) DHD_PERIM_UNLOCK(dhdp); return 0; } +#endif /* !BCMDBUS */ #ifdef WLTDLS int _dhd_tdls_enable(dhd_pub_t *dhd, bool tdls_on, bool auto_on, struct ether_addr *mac) @@ -10550,9 +11029,6 @@ 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 */ uint32 buf_key_b4_m4 = 1; -#ifndef WL_CFG80211 - u32 up = 0; -#endif uint8 msglen; eventmsgs_ext_t *eventmask_msg = NULL; char* iov_buf = NULL; @@ -10572,7 +11048,7 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) #endif shub_control_t shub_ctl; -#if defined(BCMSDIO) +#if defined(BCMSDIO) || defined(BCMDBUS) #ifdef PROP_TXSTATUS int wlfc_enable = TRUE; #ifndef DISABLE_11N @@ -10580,7 +11056,7 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) uint wl_down = 1; #endif /* DISABLE_11N */ #endif /* PROP_TXSTATUS */ -#endif +#endif /* BCMSDIO || BCMDBUS */ #ifndef PCIE_FULL_DONGLE uint32 wl_ap_isolate; #endif /* PCIE_FULL_DONGLE */ @@ -10600,7 +11076,7 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) #if defined(CUSTOMER_HW2) && defined(USE_WL_CREDALL) uint32 credall = 1; #endif - uint bcn_timeout = dhd->conf->bcn_timeout; + uint bcn_timeout = CUSTOM_BCN_TIMEOUT; uint scancache_enab = TRUE; #ifdef ENABLE_BCN_LI_BCN_WAKEUP uint32 bcn_li_bcn = 1; @@ -10712,7 +11188,6 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) #endif /* CUSTOM_SET_OCLOFF */ DHD_TRACE(("Enter %s\n", __FUNCTION__)); - dhd_conf_set_intiovar(dhd, WLC_SET_BAND, "WLC_SET_BAND", dhd->conf->band, 0, FALSE); #ifdef DHDTCPACK_SUPPRESS printf("%s: Set tcpack_sup_mode %d\n", __FUNCTION__, dhd->conf->tcpack_sup_mode); dhd_tcpack_suppress_set(dhd, dhd->conf->tcpack_sup_mode); @@ -10989,18 +11464,11 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) #endif /* Set Country code */ if (dhd->dhd_cspec.ccode[0] != 0) { - printf("Set country %s, revision %d\n", dhd->dhd_cspec.ccode, dhd->dhd_cspec.rev); ret = dhd_iovar(dhd, 0, "country", (char *)&dhd->dhd_cspec, sizeof(wl_country_t), NULL, 0, TRUE); if (ret < 0) - printf("%s: country code setting failed %d\n", __FUNCTION__, ret); - } else { - dhd_conf_map_country_list(dhd, &dhd->conf->cspec, 0); - dhd_conf_set_country(dhd, &dhd->conf->cspec); - dhd_conf_fix_country(dhd); + DHD_ERROR(("%s: country code setting failed\n", __FUNCTION__)); } - dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "autocountry", dhd->conf->autocountry, 0, FALSE); - dhd_conf_get_country(dhd, &dhd->dhd_cspec); /* Set Listen Interval */ @@ -11037,7 +11505,6 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) if (ret < 0) DHD_ERROR(("%s: roam fullscan period set failed %d\n", __FUNCTION__, ret)); #endif /* ROAM_ENABLE */ - dhd_conf_set_roam(dhd); #ifdef CUSTOM_EVENT_PM_WAKE ret = dhd_iovar(dhd, 0, "const_awake_thresh", (char *)&pm_awake_thresh, @@ -11073,7 +11540,6 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) } } #endif /* DHD_ENABLE_LPC */ - dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "lpc", dhd->conf->lpc, 0, TRUE); #ifdef WLADPS #ifdef WLADPS_SEAK_AP_WAR @@ -11092,10 +11558,7 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) #endif /* WLADPS */ /* Set PowerSave mode */ - if (dhd->conf->pm >= 0) - power_mode = dhd->conf->pm; (void) dhd_wl_ioctl_cmd(dhd, WLC_SET_PM, (char *)&power_mode, sizeof(power_mode), TRUE, 0); - dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "pm2_sleep_ret", dhd->conf->pm2_sleep_ret, 0, FALSE); #if defined(BCMSDIO) /* Match Host and Dongle rx alignment */ @@ -11128,27 +11591,6 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) dhd_iovar(dhd, 0, "apsta", (char *)&apsta, sizeof(apsta), NULL, 0, TRUE); #endif /* defined(AP) && !defined(WLP2P) */ - /* 0:HT20 in ALL, 1:HT40 in ALL, 2: HT20 in 2G HT40 in 5G */ - dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "mimo_bw_cap", dhd->conf->mimo_bw_cap, 0, TRUE); - dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "force_wme_ac", dhd->conf->force_wme_ac, 1, FALSE); - dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "stbc_tx", dhd->conf->stbc, 0, FALSE); - dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "stbc_rx", dhd->conf->stbc, 0, FALSE); - dhd_conf_set_intiovar(dhd, WLC_SET_SRL, "WLC_SET_SRL", dhd->conf->srl, 0, TRUE); - dhd_conf_set_intiovar(dhd, WLC_SET_LRL, "WLC_SET_LRL", dhd->conf->lrl, 0, FALSE); - dhd_conf_set_intiovar(dhd, WLC_SET_SPECT_MANAGMENT, "WLC_SET_SPECT_MANAGMENT", dhd->conf->spect, 0, FALSE); - dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "rsdb_mode", dhd->conf->rsdb_mode, -1, TRUE); - dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "vhtmode", dhd->conf->vhtmode, 0, TRUE); -#ifdef IDHCP - dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "dhcpc_enable", dhd->conf->dhcpc_enable, 0, FALSE); - if(dhd->conf->dhcpd_enable >= 0){ - dhd_conf_set_bufiovar(dhd, WLC_SET_VAR, "dhcpd_ip_addr", (char *)&dhd->conf->dhcpd_ip_addr, sizeof(dhd->conf->dhcpd_ip_addr), FALSE); - dhd_conf_set_bufiovar(dhd, WLC_SET_VAR, "dhcpd_ip_mask", (char *)&dhd->conf->dhcpd_ip_mask, sizeof(dhd->conf->dhcpd_ip_mask), FALSE); - dhd_conf_set_bufiovar(dhd, WLC_SET_VAR, "dhcpd_ip_start", (char *)&dhd->conf->dhcpd_ip_start, sizeof(dhd->conf->dhcpd_ip_start), FALSE); - dhd_conf_set_bufiovar(dhd, WLC_SET_VAR, "dhcpd_ip_end", (char *)&dhd->conf->dhcpd_ip_end, sizeof(dhd->conf->dhcpd_ip_end), FALSE); - dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "dhcpd_enable", dhd->conf->dhcpd_enable, 0, FALSE); - } -#endif - dhd_conf_set_bw_cap(dhd); #ifdef MIMO_ANT_SETTING dhd_sel_ant_from_file(dhd); @@ -11183,7 +11625,6 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) DHD_ERROR(("%s Set txbf failed %d\n", __FUNCTION__, ret)); #endif /* USE_WL_TXBF */ - dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "txbf", dhd->conf->txbf, 0, FALSE); ret = dhd_iovar(dhd, 0, "scancache", (char *)&scancache_enab, sizeof(scancache_enab), NULL, 0, TRUE); @@ -11220,7 +11661,6 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) sizeof(frameburst), TRUE, 0)) < 0) { DHD_INFO(("%s frameburst not supported %d\n", __FUNCTION__, ret)); } - dhd_conf_set_intiovar(dhd, WLC_SET_FAKEFRAG, "WLC_SET_FAKEFRAG", dhd->conf->frameburst, 0, FALSE); iov_buf = (char*)kmalloc(WLC_IOCTL_SMLEN, GFP_KERNEL); if (iov_buf == NULL) { @@ -11244,7 +11684,6 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) } } #endif - dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "ampdu_ba_wsize", dhd->conf->ampdu_ba_wsize, 1, FALSE); #ifdef ENABLE_TEMP_THROTTLING if (dhd->op_mode & DHD_FLAG_STA_MODE) { @@ -11676,14 +12115,9 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) #if defined(BCMSDIO) dhd_txglom_enable(dhd, dhd->conf->bus_rxglom); - // terence 20151210: set bus:txglom after dhd_txglom_enable since it's possible changed in dhd_conf_set_txglom_params - dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "bus:txglom", dhd->conf->bus_txglom, 0, FALSE); #endif /* defined(BCMSDIO) */ -#if defined(BCMPCIE) - dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "bus:deepsleep_disable", dhd->conf->bus_deepsleep_disable, 0, FALSE); -#endif /* defined(BCMPCIE) */ -#if defined(BCMSDIO) +#if defined(BCMSDIO) || defined(BCMDBUS) #ifdef PROP_TXSTATUS if (disable_proptx || #ifdef PROP_TXSTATUS_VSDB @@ -11756,8 +12190,7 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) printf("%s: not define PROP_TXSTATUS\n", __FUNCTION__); dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "ampdu_hostreorder", 0, 0, TRUE); #endif /* PROP_TXSTATUS */ - dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "ampdu_hostreorder", dhd->conf->ampdu_hostreorder, 0, TRUE); -#endif /* BCMSDIO || BCMBUS */ +#endif /* BCMSDIO || BCMDBUS */ #ifndef PCIE_FULL_DONGLE /* For FD we need all the packets at DHD to handle intra-BSS forwarding */ if (FW_SUPPORTED(dhd, ap)) { @@ -11784,9 +12217,6 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) #ifdef WL11U dhd_interworking_enable(dhd); #endif /* WL11U */ -#ifndef WL_CFG80211 - dhd_wl_ioctl_cmd(dhd, WLC_UP, (char *)&up, sizeof(up), TRUE, 0); -#endif #ifdef SUPPORT_SENSORHUB DHD_ERROR(("%s: SensorHub enabled %d\n", @@ -11869,6 +12299,7 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) DHD_ERROR(("failed to set WNM capabilities\n")); } + dhd_conf_postinit_ioctls(dhd); done: if (eventmask_msg) @@ -12420,13 +12851,27 @@ dhd_register_if(dhd_pub_t *dhdp, int ifidx, bool need_rtnl_lock) dhd->pub.rxsz = DBUS_RX_BUFFER_SIZE_DHD(net); +#ifdef WLMESH + if (ifidx >= 2 && dhdp->conf->fw_type == FW_TYPE_MESH) { + temp_addr[4] ^= 0x80; + temp_addr[4] += ifidx; + temp_addr[5] += ifidx; + } +#endif memcpy(net->dev_addr, temp_addr, ETHER_ADDR_LEN); if (ifidx == 0) printf("%s\n", dhd_version); #ifdef WL_EXT_IAPSTA - else if (!strncmp(net->name, "wl0.", strlen("wl0."))) { - wl_android_ext_attach_netdev(net, ifidx); + else + wl_ext_iapsta_attach_netdev(net, ifidx); +#endif +#ifdef WLMESH + if (ifidx != 0 && dhdp->conf->fw_type == FW_TYPE_MESH) { + if (_dhd_set_mac_address(dhd, ifidx, temp_addr) == 0) + DHD_INFO(("%s: MACID is overwritten\n", __FUNCTION__)); + else + DHD_ERROR(("%s: _dhd_set_mac_address() failed\n", __FUNCTION__)); } #endif @@ -12439,6 +12884,11 @@ dhd_register_if(dhd_pub_t *dhdp, int ifidx, bool need_rtnl_lock) DHD_ERROR(("couldn't register the net device [%s], err %d\n", net->name, err)); goto fail; } +#ifdef WL_EXT_IAPSTA + if (ifidx == 0) + wl_ext_iapsta_attach_netdev(net, ifidx); + wl_ext_iapsta_attach_name(net, ifidx); +#endif @@ -12521,7 +12971,16 @@ dhd_bus_detach(dhd_pub_t *dhdp) dhd_prot_stop(&dhd->pub); /* Stop the bus module */ +#ifdef BCMDBUS + /* Force Dongle terminated */ + if (dhd_wl_ioctl_cmd(dhdp, WLC_TERMINATED, NULL, 0, TRUE, 0) < 0) + DHD_ERROR(("%s Setting WLC_TERMINATED failed\n", + __FUNCTION__)); + dbus_stop(dhd->pub.dbus); + dhd->pub.busstate = DHD_BUS_DOWN; +#else dhd_bus_stop(dhd->pub.bus, TRUE); +#endif /* BCMDBUS */ } #if defined(OOB_INTR_ONLY) || defined(BCMPCIE_OOB_HOST_WAKE) @@ -12726,6 +13185,9 @@ void dhd_detach(dhd_pub_t *dhdp) del_timer_sync(&dhd->timer); DHD_DISABLE_RUNTIME_PM(&dhd->pub); +#ifdef BCMDBUS + tasklet_kill(&dhd->tasklet); +#else if (dhd->dhd_state & DHD_ATTACH_STATE_THREADS_CREATED) { #ifdef DHD_PCIE_RUNTIMEPM if (dhd->thr_rpm_ctl.thr_pid >= 0) { @@ -12747,6 +13209,7 @@ void dhd_detach(dhd_pub_t *dhdp) tasklet_kill(&dhd->tasklet); } } +#endif /* BCMDBUS */ #ifdef DHD_LB if (dhd->dhd_state & DHD_ATTACH_STATE_LB_ATTACH_DONE) { @@ -13033,7 +13496,11 @@ dhd_module_cleanup(void) { printf("%s: Enter\n", __FUNCTION__); +#ifdef BCMDBUS + dbus_deregister(); +#else dhd_bus_unregister(); +#endif /* BCMDBUS */ wl_android_exit(); @@ -13130,6 +13597,163 @@ dhd_reboot_callback(struct notifier_block *this, unsigned long code, void *unuse return NOTIFY_DONE; } +#ifdef BCMDBUS +/* + * hdrlen is space to reserve in pkt headroom for DBUS + */ +void * +dhd_dbus_probe_cb(void *arg, const char *desc, uint32 bustype, uint32 hdrlen) +{ + osl_t *osh = NULL; + int ret = 0; + dbus_attrib_t attrib; + dhd_pub_t *pub = NULL; + + printf("%s: Enter\n", __FUNCTION__); + +#if defined(MULTIPLE_SUPPLICANT) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) + if (mutex_is_locked(&_dhd_sdio_mutex_lock_) == 0) { + DHD_ERROR(("%s : no mutex held. set lock\n", __FUNCTION__)); + } + else { + DHD_ERROR(("%s : mutex is locked!. wait for unlocking\n", __FUNCTION__)); + } + mutex_lock(&_dhd_sdio_mutex_lock_); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) */ +#endif + + /* Ask the OS interface part for an OSL handle */ + if (!(osh = osl_attach(NULL, bustype, TRUE))) { + DHD_ERROR(("%s: OSL attach failed\n", __FUNCTION__)); + ret = -ENOMEM; + goto fail; + } + + /* Attach to the dhd/OS interface */ + if (!(pub = dhd_attach(osh, NULL /* bus */, hdrlen))) { + DHD_ERROR(("%s: dhd_attach failed\n", __FUNCTION__)); + ret = -ENXIO; + goto fail; + } + + /* Ok, finish the attach to the OS network interface */ + if (dhd_register_if(pub, 0, TRUE) != 0) { + DHD_ERROR(("%s: dhd_register_if failed\n", __FUNCTION__)); + ret = -ENXIO; + goto fail; + } + + pub->dbus = dbus_attach(pub->osh, pub->rxsz, DBUS_NRXQ, DBUS_NTXQ, + pub->info, &dhd_dbus_cbs, NULL, NULL); + if (pub->dbus) { + dbus_get_attrib(pub->dbus, &attrib); + DHD_ERROR(("DBUS: vid=0x%x pid=0x%x devid=0x%x bustype=0x%x mtu=%d rev=%d\n", + attrib.vid, attrib.pid, attrib.devid, attrib.bustype, attrib.mtu, attrib.chiprev)); + } else { + ret = -ENXIO; + goto fail; + } + + /* dhd_conf must be attached after linking dhd to dhd->dbus, + * because dhd_detech will check .info is NULL or not. + */ + if (dhd_conf_attach(pub) != 0) { + DHD_ERROR(("dhd_conf_attach failed\n")); + goto fail; + } + dhd_conf_reset(pub); + dhd_conf_set_chiprev(pub, attrib.devid, attrib.chiprev); + dhd_conf_preinit(pub); + + /* Some DHD modules (e.g. cfg80211) configures operation mode based on firmware name. + * This is indeed a hack but we have to make it work properly before we have a better + * solution + */ + dhd_update_fw_nv_path(pub->info); + + +#if defined(MULTIPLE_SUPPLICANT) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) + mutex_unlock(&_dhd_sdio_mutex_lock_); + DHD_ERROR(("%s : the lock is released.\n", __FUNCTION__)); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ +#endif + + printf("%s: Exit\n", __FUNCTION__); + /* This is passed to dhd_dbus_disconnect_cb */ + return pub->info; +fail: + /* Release resources in reverse order */ + if (pub) { + if (pub->dbus) { + dbus_detach(pub->dbus); + pub->dbus = NULL; + } + dhd_detach(pub); + dhd_free(pub); + } + if (osh) { + osl_detach(osh); + } +#if defined(MULTIPLE_SUPPLICANT) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) + mutex_unlock(&_dhd_sdio_mutex_lock_); + DHD_ERROR(("%s : the lock is released.\n", __FUNCTION__)); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ +#endif + + BCM_REFERENCE(ret); + printf("%s: Exit\n", __FUNCTION__); + return NULL; +} + +void +dhd_dbus_disconnect_cb(void *arg) +{ + dhd_info_t *dhd = (dhd_info_t *)arg; + dhd_pub_t *pub; + osl_t *osh; + + printf("%s: Enter\n", __FUNCTION__); + if (dhd == NULL) + return; + +#if defined(MULTIPLE_SUPPLICANT) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) + if (mutex_is_locked(&_dhd_sdio_mutex_lock_) == 0) { + DHD_ERROR(("%s : no mutex held. set lock\n", __FUNCTION__)); + } + else { + DHD_ERROR(("%s : mutex is locked!. wait for unlocking\n", __FUNCTION__)); + } + mutex_lock(&_dhd_sdio_mutex_lock_); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) */ +#endif + + pub = &dhd->pub; + osh = pub->osh; + dhd_detach(pub); + if (pub->dbus) { + dbus_detach(pub->dbus); + pub->dbus = NULL; + } + dhd_free(pub); + + if (MALLOCED(osh)) { + DHD_ERROR(("%s: MEMORY LEAK %d bytes\n", __FUNCTION__, MALLOCED(osh))); + } + osl_detach(osh); + +#if defined(MULTIPLE_SUPPLICANT) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) + mutex_unlock(&_dhd_sdio_mutex_lock_); + DHD_ERROR(("%s : the lock is released.\n", __FUNCTION__)); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) */ +#endif /* LINUX */ + printf("%s: Exit\n", __FUNCTION__); +} +#endif /* BCMDBUS */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) #if defined(CONFIG_DEFERRED_INITCALLS) && !defined(EXYNOS_PCIE_MODULE_PATCH) @@ -13386,6 +14010,7 @@ dhd_os_busbusy_wake(dhd_pub_t *pub) void dhd_os_wd_timer_extend(void *bus, bool extend) { +#ifndef BCMDBUS dhd_pub_t *pub = bus; dhd_info_t *dhd = (dhd_info_t *)pub->info; @@ -13393,12 +14018,14 @@ dhd_os_wd_timer_extend(void *bus, bool extend) dhd_os_wd_timer(bus, WATCHDOG_EXTEND_INTERVAL); else dhd_os_wd_timer(bus, dhd->default_wd_interval); +#endif /* !BCMDBUS */ } void dhd_os_wd_timer(void *bus, uint wdtick) { +#ifndef BCMDBUS dhd_pub_t *pub = bus; dhd_info_t *dhd = (dhd_info_t *)pub->info; unsigned long flags; @@ -13433,6 +14060,7 @@ dhd_os_wd_timer(void *bus, uint wdtick) dhd->wd_timer_valid = TRUE; } DHD_GENERAL_UNLOCK(pub, flags); +#endif /* !BCMDBUS */ } #ifdef DHD_PCIE_RUNTIMEPM @@ -13534,15 +14162,21 @@ dhd_os_get_image_block(char *buf, int len, void *image) } size = i_size_read(file_inode(fp)); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + rdlen = kernel_read(fp, buf, MIN(len, size), &fp->f_pos); +#else rdlen = kernel_read(fp, fp->f_pos, buf, MIN(len, size)); +#endif if (len >= size && size != rdlen) { return -EIO; } +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) if (rdlen > 0) { fp->f_pos += rdlen; } +#endif return rdlen; } @@ -13573,7 +14207,11 @@ dhd_os_gets_image(dhd_pub_t *pub, char *str, int len, void *image) if (!image) return 0; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + rd_len = kernel_read(fp, str, len, &fp->f_pos); +#else rd_len = kernel_read(fp, fp->f_pos, str, len); +#endif str_end = strnchr(str, len, '\n'); if (str_end == NULL) { goto err; @@ -13604,10 +14242,14 @@ dhd_os_sdlock(dhd_pub_t *pub) dhd = (dhd_info_t *)(pub->info); +#ifndef BCMDBUS if (dhd_dpc_prio >= 0) down(&dhd->sdsem); else spin_lock_bh(&dhd->sdlock); +#else + spin_lock_bh(&dhd->sdlock); +#endif /* !BCMDBUS */ } void @@ -13617,10 +14259,14 @@ dhd_os_sdunlock(dhd_pub_t *pub) dhd = (dhd_info_t *)(pub->info); +#ifndef BCMDBUS if (dhd_dpc_prio >= 0) up(&dhd->sdsem); else spin_unlock_bh(&dhd->sdlock); +#else + spin_unlock_bh(&dhd->sdlock); +#endif /* !BCMDBUS */ } void @@ -13629,7 +14275,11 @@ dhd_os_sdlock_txq(dhd_pub_t *pub) dhd_info_t *dhd; dhd = (dhd_info_t *)(pub->info); +#ifdef BCMDBUS + spin_lock_irqsave(&dhd->txqlock, dhd->txqlock_flags); +#else spin_lock_bh(&dhd->txqlock); +#endif /* BCMDBUS */ } void @@ -13638,7 +14288,11 @@ dhd_os_sdunlock_txq(dhd_pub_t *pub) dhd_info_t *dhd; dhd = (dhd_info_t *)(pub->info); +#ifdef BCMDBUS + spin_unlock_irqrestore(&dhd->txqlock, dhd->txqlock_flags); +#else spin_unlock_bh(&dhd->txqlock); +#endif /* BCMDBUS */ } void @@ -13780,6 +14434,9 @@ dhd_wl_host_event(dhd_info_t *dhd, int ifidx, void *pktdata, uint16 pktlen, if (bcmerror != BCME_OK) return (bcmerror); +#if defined(WL_EXT_IAPSTA) + wl_ext_iapsta_event(dhd->iflist[ifidx]->net, event, *data); +#endif /* defined(WL_EXT_IAPSTA) */ #if defined(WL_WIRELESS_EXT) if (event->bsscfgidx == 0) { /* @@ -15903,7 +16560,11 @@ int write_file(const char * file_name, uint32 flags, uint8 *buf, int size) } /* Write buf to file */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + ret = kernel_write(fp, buf, size, &pos); +#else ret = vfs_write(fp, buf, size, &pos); +#endif if (ret < 0) { DHD_ERROR(("write file error, err = %d\n", ret)); goto exit; @@ -17551,7 +18212,11 @@ void dhd_get_memdump_info(dhd_pub_t *dhd) } /* Handle success case */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + ret = kernel_read(fp, (char *)&mem_val, 4, NULL); +#else ret = kernel_read(fp, 0, (char *)&mem_val, 4); +#endif if (ret < 0) { DHD_ERROR(("%s: File read error, ret=%d\n", __FUNCTION__, ret)); filp_close(fp, NULL); @@ -17790,7 +18455,11 @@ do_dhd_log_dump(dhd_pub_t *dhdp) goto exit; } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + ret = kernel_write(fp, pre_strs, strlen(pre_strs), &pos); +#else ret = vfs_write(fp, pre_strs, strlen(pre_strs), &pos); +#endif if (ret < 0) { DHD_ERROR(("write file error, err = %d\n", ret)); goto exit; @@ -17808,7 +18477,11 @@ do_dhd_log_dump(dhd_pub_t *dhdp) wr_size = (unsigned int)(dld_buf->present - dld_buf->front); } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + ret = kernel_write(fp, dld_buf->buffer, wr_size, &pos); +#else ret = vfs_write(fp, dld_buf->buffer, wr_size, &pos); +#endif if (ret < 0) { DHD_ERROR(("write file error, err = %d\n", ret)); goto exit; @@ -17829,7 +18502,11 @@ do_dhd_log_dump(dhd_pub_t *dhdp) break; } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + ret = kernel_write(fp, post_strs, strlen(post_strs), &pos); +#else ret = vfs_write(fp, post_strs, strlen(post_strs), &pos); +#endif if (ret < 0) { DHD_ERROR(("write file error, err = %d\n", ret)); goto exit; @@ -17878,7 +18555,11 @@ void dhd_get_assert_info(dhd_pub_t *dhd) if (IS_ERR(fp)) { DHD_ERROR(("%s: File [%s] doesn't exist\n", __FUNCTION__, filepath)); } else { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + ssize_t ret = kernel_read(fp, (char *)&mem_val, 4, NULL); +#else int ret = kernel_read(fp, 0, (char *)&mem_val, 4); +#endif if (ret < 0) { DHD_ERROR(("%s: File read error, ret=%d\n", __FUNCTION__, ret)); } else { @@ -18789,7 +19470,11 @@ dhd_write_file(const char *filepath, char *buf, int buf_len) ret = BCME_ERROR; } else { if (fp->f_mode & FMODE_WRITE) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + ret = kernel_write(fp, buf, buf_len, &fp->f_pos); +#else ret = vfs_write(fp, buf, buf_len, &fp->f_pos); +#endif if (ret < 0) { DHD_ERROR(("%s: Couldn't write file '%s'\n", __FUNCTION__, filepath)); @@ -18825,7 +19510,11 @@ dhd_read_file(const char *filepath, char *buf, int buf_len) return BCME_ERROR; } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + ret = kernel_read(fp, buf, buf_len, NULL); +#else ret = kernel_read(fp, 0, buf, buf_len); +#endif filp_close(fp, NULL); /* restore previous address limit */ @@ -19423,7 +20112,11 @@ dhd_make_hang_with_reason(struct net_device *dev, const char *string_num) wake_counts_t* dhd_get_wakecount(dhd_pub_t *dhdp) { +#ifdef BCMDBUS + return NULL; +#else return dhd_bus_get_wakecount(dhdp); +#endif /* BCMDBUS */ } #endif /* DHD_WAKE_STATUS */ diff --git a/bcmdhd.1.579.77.41.1.cn/dhd_linux_platdev.c b/bcmdhd.1.579.77.41.1.cn/dhd_linux_platdev.c index 6ee1ad4..9577895 100644 --- a/bcmdhd.1.579.77.41.1.cn/dhd_linux_platdev.c +++ b/bcmdhd.1.579.77.41.1.cn/dhd_linux_platdev.c @@ -42,6 +42,9 @@ #if defined(CONFIG_WIFI_CONTROL_FUNC) #include #endif +#ifdef BCMDBUS +#include +#endif /* BCMDBUS */ #ifdef CONFIG_DTS #include #include @@ -58,6 +61,7 @@ extern void dhd_wlan_deinit_plat_data(wifi_adapter_info_t *adapter); #ifdef CONFIG_DTS struct regulator *wifi_regulator = NULL; +extern struct wifi_platform_data dhd_wlan_control; #endif /* CONFIG_DTS */ bool cfg_multichip = FALSE; @@ -167,10 +171,12 @@ int wifi_platform_set_power(wifi_adapter_info_t *adapter, bool on, unsigned long #endif /* BT_OVER_SDIO */ #ifdef CONFIG_DTS if (on) { + printf("======== PULL WL_REG_ON HIGH! ========\n"); err = regulator_enable(wifi_regulator); is_power_on = TRUE; } else { + printf("======== PULL WL_REG_ON LOW! ========\n"); err = regulator_disable(wifi_regulator); is_power_on = FALSE; } @@ -280,7 +286,7 @@ static int wifi_plat_dev_drv_probe(struct platform_device *pdev) { struct resource *resource; wifi_adapter_info_t *adapter; -#ifdef CONFIG_DTS +#if defined(CONFIG_DTS) && defined(CUSTOMER_OOB) int irq, gpio; #endif /* CONFIG_DTS */ @@ -290,7 +296,8 @@ static int wifi_plat_dev_drv_probe(struct platform_device *pdev) ASSERT(dhd_wifi_platdata != NULL); ASSERT(dhd_wifi_platdata->num_adapters == 1); adapter = &dhd_wifi_platdata->adapters[0]; - adapter->wifi_plat_data = (struct wifi_platform_data *)(pdev->dev.platform_data); + adapter->wifi_plat_data = (void *)&dhd_wlan_control; +// adapter->wifi_plat_data = (struct wifi_platform_data *)(pdev->dev.platform_data); resource = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "bcmdhd_wlan_irq"); if (resource == NULL) @@ -310,6 +317,7 @@ static int wifi_plat_dev_drv_probe(struct platform_device *pdev) return -1; } +#if defined(CUSTOMER_OOB) /* This is to get the irq for the OOB */ gpio = of_get_gpio(pdev->dev.of_node, 0); @@ -327,6 +335,7 @@ static int wifi_plat_dev_drv_probe(struct platform_device *pdev) /* need to change the flags according to our requirement */ adapter->intr_flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL | IORESOURCE_IRQ_SHAREABLE; +#endif #endif /* CONFIG_DTS */ wifi_plat_dev_probe_ret = dhd_wifi_platform_load(); @@ -519,7 +528,9 @@ static int wifi_ctrlfunc_register_drv(void) void wifi_ctrlfunc_unregister_drv(void) { +#ifndef CONFIG_DTS wifi_adapter_info_t *adapter; +#endif #if defined(CONFIG_DTS) && !defined(CUSTOMER_HW) DHD_ERROR(("unregister wifi platform drivers\n")); @@ -865,10 +876,43 @@ static int dhd_wifi_platform_load_sdio(void) } #endif /* BCMSDIO */ +#ifdef BCMDBUS +/* User-specified vid/pid */ +int dhd_vid = 0xa5c; +int dhd_pid = 0x48f; +module_param(dhd_vid, int, 0); +module_param(dhd_pid, int, 0); +void *dhd_dbus_probe_cb(void *arg, const char *desc, uint32 bustype, uint32 hdrlen); +void dhd_dbus_disconnect_cb(void *arg); + +static int dhd_wifi_platform_load_usb(void) +{ + int err = 0; + + if (dhd_vid < 0 || dhd_vid > 0xffff) { + DHD_ERROR(("%s: invalid dhd_vid 0x%x\n", __FUNCTION__, dhd_vid)); + return -EINVAL; + } + if (dhd_pid < 0 || dhd_pid > 0xffff) { + DHD_ERROR(("%s: invalid dhd_pid 0x%x\n", __FUNCTION__, dhd_pid)); + return -EINVAL; + } + + err = dbus_register(dhd_vid, dhd_pid, dhd_dbus_probe_cb, dhd_dbus_disconnect_cb, + NULL, NULL, NULL); + + /* Device not detected */ + if (err == DBUS_ERR_NODEVICE) + err = DBUS_OK; + + return err; +} +#else /* BCMDBUS */ static int dhd_wifi_platform_load_usb(void) { return 0; } +#endif /* BCMDBUS */ static int dhd_wifi_platform_load() { diff --git a/bcmdhd.1.579.77.41.1.cn/dhd_linux_wq.h b/bcmdhd.1.579.77.41.1.cn/dhd_linux_wq.h index 6dc41a5..9c51d06 100644 --- a/bcmdhd.1.579.77.41.1.cn/dhd_linux_wq.h +++ b/bcmdhd.1.579.77.41.1.cn/dhd_linux_wq.h @@ -47,6 +47,9 @@ enum _wq_event { DHD_WQ_WORK_DEBUG_UART_DUMP, DHD_WQ_WORK_SSSR_DUMP, DHD_WQ_WORK_PKTLOG_DUMP, +#ifdef DHD_UPDATE_INTF_MAC + DHD_WQ_WORK_IF_UPDATE, +#endif /* DHD_UPDATE_INTF_MAC */ DHD_MAX_WQ_EVENTS }; diff --git a/bcmdhd.1.579.77.41.1.cn/dhd_pcie.c b/bcmdhd.1.579.77.41.1.cn/dhd_pcie.c index 48e3d19..a785fe5 100644 --- a/bcmdhd.1.579.77.41.1.cn/dhd_pcie.c +++ b/bcmdhd.1.579.77.41.1.cn/dhd_pcie.c @@ -994,6 +994,9 @@ dhdpcie_dongle_attach(dhd_bus_t *bus) case BCM4347_CHIP_GRPID: bus->dongle_ram_base = CR4_4347_RAM_BASE; break; + case BCM4362_CHIP_ID: + bus->dongle_ram_base = CR4_4362_RAM_BASE; + break; default: bus->dongle_ram_base = 0; DHD_ERROR(("%s: WARNING: Using default ram base at 0x%x\n", @@ -7088,6 +7091,11 @@ dhdpcie_chipmatch(uint16 vendor, uint16 device) if ((device == BCM4361_D11AC_ID) || (device == BCM4361_D11AC2G_ID) || (device == BCM4361_D11AC5G_ID) || (device == BCM4361_CHIP_ID)) return 0; + + if ((device == BCM4362_D11AX_ID) || (device == BCM4362_D11AX2G_ID) || + (device == BCM4362_D11AX5G_ID) || (device == BCM4362_CHIP_ID)) { + return 0; + } if ((device == BCM4365_D11AC_ID) || (device == BCM4365_D11AC2G_ID) || (device == BCM4365_D11AC5G_ID) || (device == BCM4365_CHIP_ID)) diff --git a/bcmdhd.1.579.77.41.1.cn/dhd_pcie_linux.c b/bcmdhd.1.579.77.41.1.cn/dhd_pcie_linux.c index 5c9561a..577b393 100644 --- a/bcmdhd.1.579.77.41.1.cn/dhd_pcie_linux.c +++ b/bcmdhd.1.579.77.41.1.cn/dhd_pcie_linux.c @@ -178,12 +178,6 @@ static int dhdpcie_init(struct pci_dev *pdev); static irqreturn_t dhdpcie_isr(int irq, void *arg); /* OS Routine functions for PCI suspend/resume */ -#if defined(MULTIPLE_SUPPLICANT) -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) -DEFINE_MUTEX(_dhd_sdio_mutex_lock_); -#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) */ -#endif - static int dhdpcie_set_suspend_resume(dhd_bus_t *bus, bool state); static int dhdpcie_resume_host_dev(dhd_bus_t *bus); static int dhdpcie_suspend_host_dev(dhd_bus_t *bus); diff --git a/bcmdhd.1.579.77.41.1.cn/dhd_pno.c b/bcmdhd.1.579.77.41.1.cn/dhd_pno.c index c20ed7b..570e75e 100644 --- a/bcmdhd.1.579.77.41.1.cn/dhd_pno.c +++ b/bcmdhd.1.579.77.41.1.cn/dhd_pno.c @@ -2766,6 +2766,7 @@ exit: return err; } #endif /* GSCAN_SUPPORT */ + #if defined(GSCAN_SUPPORT) || defined(DHD_GET_VALID_CHANNELS) void * dhd_pno_get_gscan(dhd_pub_t *dhd, dhd_pno_gscan_cmd_cfg_t type, diff --git a/bcmdhd.1.579.77.41.1.cn/dhd_sdio.c b/bcmdhd.1.579.77.41.1.cn/dhd_sdio.c index d69bced..5078ffb 100644 --- a/bcmdhd.1.579.77.41.1.cn/dhd_sdio.c +++ b/bcmdhd.1.579.77.41.1.cn/dhd_sdio.c @@ -182,12 +182,6 @@ DHD_SPINWAIT_SLEEP_INIT(sdioh_spinwait_sleep); pkt_statics_t tx_statics = {0}; #endif -#if defined(MULTIPLE_SUPPLICANT) -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) -DEFINE_MUTEX(_dhd_sdio_mutex_lock_); -#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) */ -#endif - #ifdef SUPPORT_MULTIPLE_BOARD_REV_FROM_HW extern unsigned int system_hw_rev; #endif /* SUPPORT_MULTIPLE_BOARD_REV_FROM_HW */ @@ -341,6 +335,8 @@ typedef struct dhd_bus { #if defined(SUPPORT_P2P_GO_PS) wait_queue_head_t bus_sleep; #endif /* LINUX && SUPPORT_P2P_GO_PS */ + bool ctrl_wait; + wait_queue_head_t ctrl_tx_wait; uint rxflow_mode; /* Rx flow control mode */ bool rxflow; /* Is rx flow control on */ uint prev_rxlim_hit; /* Is prev rx limit exceeded (per dpc schedule) */ @@ -881,8 +877,8 @@ dhdsdio_sr_cap(dhd_bus_t *bus) (bus->sih->chip == BCM4371_CHIP_ID) || (BCM4349_CHIP(bus->sih->chip)) || (bus->sih->chip == BCM4350_CHIP_ID) || - (bus->sih->chip == BCM43751_CHIP_ID) || - (bus->sih->chip == BCM43012_CHIP_ID)) { + (bus->sih->chip == BCM43012_CHIP_ID) || + (bus->sih->chip == BCM4362_CHIP_ID)) { core_capext = TRUE; } else { core_capext = bcmsdh_reg_read(bus->sdh, @@ -978,8 +974,8 @@ dhdsdio_sr_init(dhd_bus_t *bus) if (CHIPID(bus->sih->chip) == BCM43430_CHIP_ID || CHIPID(bus->sih->chip) == BCM43018_CHIP_ID || CHIPID(bus->sih->chip) == BCM4339_CHIP_ID || - CHIPID(bus->sih->chip) == BCM43751_CHIP_ID || - CHIPID(bus->sih->chip) == BCM43012_CHIP_ID) + CHIPID(bus->sih->chip) == BCM43012_CHIP_ID || + CHIPID(bus->sih->chip) == BCM4362_CHIP_ID) dhdsdio_devcap_set(bus, SDIOD_CCCR_BRCM_CARDCAP_CMD_NODEC); if (bus->sih->chip == BCM43012_CHIP_ID) { @@ -1983,12 +1979,16 @@ dhd_bus_txdata(struct dhd_bus *bus, void *pkt) prec = PRIO2PREC((PKTPRIO(pkt) & PRIOMASK)); + /* move from dhdsdio_sendfromq(), try to orphan skb early */ + if (bus->dhd->conf->orphan_move) + PKTORPHAN(pkt, bus->dhd->conf->tsq); + /* Check for existing queue, current flow-control, pending event, or pending clock */ if (dhd_deferred_tx || bus->fcstate || pktq_len(&bus->txq) || bus->dpc_sched || (!DATAOK(bus)) || (bus->flowcontrol & NBITVAL(prec)) || (bus->clkstate != CLK_AVAIL)) { bool deq_ret; - int pkq_len; + int pkq_len = 0; DHD_TRACE(("%s: deferring pktq len %d\n", __FUNCTION__, pktq_len(&bus->txq))); bus->fcqueued++; @@ -2017,10 +2017,12 @@ dhd_bus_txdata(struct dhd_bus *bus, void *pkt) } else ret = BCME_OK; - dhd_os_sdlock_txq(bus->dhd); - pkq_len = pktq_len(&bus->txq); - dhd_os_sdunlock_txq(bus->dhd); - if (pkq_len >= FCHI) { + if (dhd_doflow) { + dhd_os_sdlock_txq(bus->dhd); + pkq_len = pktq_len(&bus->txq); + dhd_os_sdunlock_txq(bus->dhd); + } + if (dhd_doflow && pkq_len >= FCHI) { bool wlfc_enabled = FALSE; #ifdef PROP_TXSTATUS wlfc_enabled = (dhd_wlfc_flowcontrol(bus->dhd, ON, FALSE) != @@ -2624,7 +2626,8 @@ dhdsdio_sendfromq(dhd_bus_t *bus, uint maxframes) } } #endif /* DHD_LOSSLESS_ROAMING */ - PKTORPHAN(pkts[i], bus->dhd->conf->tsq); + if (!bus->dhd->conf->orphan_move) + PKTORPHAN(pkts[i], bus->dhd->conf->tsq); datalen += PKTLEN(osh, pkts[i]); } dhd_os_sdunlock_txq(bus->dhd); @@ -2661,9 +2664,11 @@ dhdsdio_sendfromq(dhd_bus_t *bus, uint maxframes) } - dhd_os_sdlock_txq(bus->dhd); - txpktqlen = pktq_len(&bus->txq); - dhd_os_sdunlock_txq(bus->dhd); + if (dhd_doflow) { + dhd_os_sdlock_txq(bus->dhd); + txpktqlen = pktq_len(&bus->txq); + dhd_os_sdunlock_txq(bus->dhd); + } /* Do flow-control if needed */ if (dhd->up && (dhd->busstate == DHD_BUS_DATA) && (txpktqlen < FCLOW)) { @@ -2716,7 +2721,6 @@ dhd_bus_txctl(struct dhd_bus *bus, uchar *msg, uint msglen) uint8 doff = 0; int ret = -1; uint8 sdpcm_hdrlen = bus->txglom_enable ? SDPCM_HDRLEN_TXGLOM : SDPCM_HDRLEN; - int cnt = 0; DHD_TRACE(("%s: Enter\n", __FUNCTION__)); @@ -2756,17 +2760,13 @@ dhd_bus_txctl(struct dhd_bus *bus, uchar *msg, uint msglen) /* Need to lock here to protect txseq and SDIO tx calls */ -retry: - dhd_os_sdlock(bus->dhd); - if (cnt < bus->dhd->conf->txctl_tmo_fix && !TXCTLOK(bus)) { - cnt++; - dhd_os_sdunlock(bus->dhd); - OSL_SLEEP(1); - if (cnt >= (bus->dhd->conf->txctl_tmo_fix)) - DHD_ERROR(("%s: No bus credit bus->tx_max %d, bus->tx_seq %d, last retry cnt %d\n", - __FUNCTION__, bus->tx_max, bus->tx_seq, cnt)); - goto retry; + if (bus->dhd->conf->txctl_tmo_fix > 0 && !TXCTLOK(bus)) { + bus->ctrl_wait = TRUE; + wait_event_interruptible_timeout(bus->ctrl_tx_wait, TXCTLOK(bus), + msecs_to_jiffies(bus->dhd->conf->txctl_tmo_fix)); + bus->ctrl_wait = FALSE; } + dhd_os_sdlock(bus->dhd); BUS_WAKE(bus); @@ -6844,6 +6844,8 @@ exit: } } + if (bus->ctrl_wait && TXCTLOK(bus)) + wake_up_interruptible(&bus->ctrl_tx_wait); dhd_os_sdunlock(bus->dhd); #ifdef DEBUG_DPC_THREAD_WATCHDOG if (bus->dhd->dhd_bug_on) { @@ -7643,8 +7645,7 @@ dhdsdio_chipmatch(uint16 chipid) if (chipid == BCM43012_CHIP_ID) return TRUE; - - if (chipid == BCM43751_CHIP_ID) + if (chipid == BCM4362_CHIP_ID) return TRUE; return FALSE; @@ -7756,6 +7757,7 @@ dhdsdio_probe(uint16 venid, uint16 devid, uint16 bus_no, uint16 slot, #if defined(SUPPORT_P2P_GO_PS) init_waitqueue_head(&bus->bus_sleep); #endif /* LINUX && SUPPORT_P2P_GO_PS */ + init_waitqueue_head(&bus->ctrl_tx_wait); /* attempt to attach to the dongle */ if (!(dhdsdio_probe_attach(bus, osh, sdh, regsva, devid))) { @@ -7925,7 +7927,7 @@ dhdsdio_probe_attach(struct dhd_bus *bus, osl_t *osh, void *sdh, void *regsva, bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, DHD_INIT_CLKCTL2, &err); OSL_DELAY(200); - + if (DHD_INFO_ON()) { for (fn = 0; fn <= numfn; fn++) { if (!(cis[fn] = MALLOC(osh, SBSDIO_CIS_SIZE_LIMIT))) { @@ -8066,8 +8068,8 @@ dhdsdio_probe_attach(struct dhd_bus *bus, osl_t *osh, void *sdh, void *regsva, case BCM4347_CHIP_GRPID: bus->dongle_ram_base = CR4_4347_RAM_BASE; break; - case BCM43751_CHIP_ID: - bus->dongle_ram_base = CR4_43751_RAM_BASE; + case BCM4362_CHIP_ID: + bus->dongle_ram_base = CR4_4362_RAM_BASE; break; default: bus->dongle_ram_base = 0; @@ -8327,15 +8329,12 @@ dhd_set_bus_params(struct dhd_bus *bus) } if (bus->dhd->conf->use_rxchain >= 0) { bus->use_rxchain = (bool)bus->dhd->conf->use_rxchain; - printf("%s: set use_rxchain %d\n", __FUNCTION__, bus->dhd->conf->use_rxchain); } if (bus->dhd->conf->txinrx_thres >= 0) { bus->txinrx_thres = bus->dhd->conf->txinrx_thres; - printf("%s: set txinrx_thres %d\n", __FUNCTION__, bus->txinrx_thres); } if (bus->dhd->conf->txglomsize >= 0) { bus->txglomsize = bus->dhd->conf->txglomsize; - printf("%s: set txglomsize %d\n", __FUNCTION__, bus->dhd->conf->txglomsize); } } diff --git a/bcmdhd.1.579.77.41.1.cn/dhd_wlfc.c b/bcmdhd.1.579.77.41.1.cn/dhd_wlfc.c index 678dbc3..1a0d1bb 100644 --- a/bcmdhd.1.579.77.41.1.cn/dhd_wlfc.c +++ b/bcmdhd.1.579.77.41.1.cn/dhd_wlfc.c @@ -38,7 +38,12 @@ #include #include +#ifdef BCMDBUS /* an abstraction layer that hides details of the underlying bus, eg \ + Linux USB */ +#include +#else #include +#endif /* BCMDBUS */ #include #include @@ -67,6 +72,9 @@ #define WLFC_THREAD_RETRY_WAIT_MS 10000 /* 10 sec */ #endif /* defined (DHD_WLFC_THREAD) */ +#if defined(BCMDBUS) +extern int dhd_dbus_txdata(dhd_pub_t *dhdp, void *pktbuf); +#endif #ifdef PROP_TXSTATUS @@ -997,6 +1005,8 @@ _dhd_wlfc_send_signalonly_packet(athost_wl_status_info_t* ctx, wlfc_mac_descript #if defined(BCMPCIE) rc = dhd_bus_txdata(dhdp->bus, p, ctx->host_ifidx); +#elif defined(BCMDBUS) + rc = dhd_dbus_txdata(dhdp, p); #else rc = dhd_bus_txdata(dhdp->bus, p); #endif @@ -1617,7 +1627,7 @@ _dhd_wlfc_pktq_flush(athost_wl_status_info_t* ctx, struct pktq *pq, ASSERT(pq->len == 0); } /* _dhd_wlfc_pktq_flush */ - +#ifndef BCMDBUS /** !BCMDBUS specific function. Dequeues a packet from the caller supplied queue. */ static void* _dhd_wlfc_pktq_pdeq_with_fn(struct pktq *pq, int prec, f_processpkt_t fn, void *arg) @@ -1723,6 +1733,7 @@ _dhd_wlfc_cleanup_txq(dhd_pub_t *dhd, f_processpkt_t fn, void *arg) PKTFREE(wlfc->osh, pkt, TRUE); } } /* _dhd_wlfc_cleanup_txq */ +#endif /* !BCMDBUS */ /** called during eg detach */ void @@ -1741,8 +1752,10 @@ _dhd_wlfc_cleanup(dhd_pub_t *dhd, f_processpkt_t fn, void *arg) /* * flush sequence should be txq -> psq -> hanger/afq, hanger has to be last one */ +#ifndef BCMDBUS /* flush bus->txq */ _dhd_wlfc_cleanup_txq(dhd, fn, arg); +#endif /* !BCMDBUS */ /* flush psq, search all entries, include nodes as well as interfaces */ total_entries = sizeof(wlfc->destination_entries)/sizeof(wlfc_mac_descriptor_t); @@ -2465,7 +2478,7 @@ _dhd_wlfc_fifocreditback_indicate(dhd_pub_t *dhd, uint8* credits) return BCME_OK; } /* _dhd_wlfc_fifocreditback_indicate */ - +#ifndef BCMDBUS /** !BCMDBUS specific function */ static void _dhd_wlfc_suppress_txq(dhd_pub_t *dhd, f_processpkt_t fn, void *arg) @@ -2544,6 +2557,7 @@ _dhd_wlfc_suppress_txq(dhd_pub_t *dhd, f_processpkt_t fn, void *arg) _dhd_wlfc_fifocreditback_indicate(dhd, credits); } } /* _dhd_wlfc_suppress_txq */ +#endif /* !BCMDBUS */ static int _dhd_wlfc_dbg_senum_check(dhd_pub_t *dhd, uint8 *value) @@ -3072,10 +3086,12 @@ dhd_wlfc_parse_header_info(dhd_pub_t *dhd, void* pktbuf, int tlv_hdr_len, uchar _dhd_wlfc_interface_update(dhd, value, type); } +#ifndef BCMDBUS if (entry && WLFC_GET_REORDERSUPP(dhd->wlfc_mode)) { /* suppress all packets for this mac entry from bus->txq */ _dhd_wlfc_suppress_txq(dhd, _dhd_wlfc_entrypkt_fn, entry); } +#endif /* !BCMDBUS */ } /* while */ if (remainder != 0 && wlfc) { @@ -3407,6 +3423,15 @@ dhd_wlfc_commit_packets(dhd_pub_t *dhdp, f_commitpkt_t fcommit, void* commit_ctx ctx = (athost_wl_status_info_t*)dhdp->wlfc_state; +#ifdef BCMDBUS + if (!dhdp->up || (dhdp->busstate == DHD_BUS_DOWN)) { + if (pktbuf) { + PKTFREE(ctx->osh, pktbuf, TRUE); + rc = BCME_OK; + } + goto exit; + } +#endif /* BCMDBUS */ if (dhdp->proptxstatus_module_ignore) { if (pktbuf) { @@ -3593,10 +3618,17 @@ dhd_wlfc_init(dhd_pub_t *dhd) DHD_ERROR(("%s: query wlfc_mode succeed, fw_caps=0x%x\n", __FUNCTION__, fw_caps)); if (WLFC_IS_OLD_DEF(fw_caps)) { +#ifdef BCMDBUS + mode = WLFC_MODE_HANGER; +#else /* enable proptxtstatus v2 by default */ mode = WLFC_MODE_AFQ; +#endif /* BCMDBUS */ } else { WLFC_SET_AFQ(mode, WLFC_GET_AFQ(fw_caps)); +#ifdef BCMDBUS + WLFC_SET_AFQ(mode, 0); +#endif /* BCMDBUS */ WLFC_SET_REUSESEQ(mode, WLFC_GET_REUSESEQ(fw_caps)); WLFC_SET_REORDERSUPP(mode, WLFC_GET_REORDERSUPP(fw_caps)); } @@ -3679,7 +3711,9 @@ dhd_wlfc_cleanup_txq(dhd_pub_t *dhd, f_processpkt_t fn, void *arg) return WLFC_UNSUPPORTED; } +#ifndef BCMDBUS _dhd_wlfc_cleanup_txq(dhd, fn, arg); +#endif /* !BCMDBUS */ dhd_os_wlfc_unblock(dhd); diff --git a/bcmdhd.1.579.77.41.1.cn/dhd_wlfc.h b/bcmdhd.1.579.77.41.1.cn/dhd_wlfc.h index 1e8b01f..54c6b3b 100644 --- a/bcmdhd.1.579.77.41.1.cn/dhd_wlfc.h +++ b/bcmdhd.1.579.77.41.1.cn/dhd_wlfc.h @@ -111,8 +111,13 @@ typedef struct wlfc_hanger { #define WLFC_PSQ_LEN (4096 * 8) +#ifdef BCMDBUS +#define WLFC_FLOWCONTROL_HIWATER 512 +#define WLFC_FLOWCONTROL_LOWATER (WLFC_FLOWCONTROL_HIWATER / 4) +#else #define WLFC_FLOWCONTROL_HIWATER ((4096 * 8) - 256) #define WLFC_FLOWCONTROL_LOWATER 256 +#endif #if (WLFC_FLOWCONTROL_HIWATER >= (WLFC_PSQ_LEN - 256)) #undef WLFC_FLOWCONTROL_HIWATER diff --git a/bcmdhd.1.579.77.41.1.cn/include/bcmdevs.h b/bcmdhd.1.579.77.41.1.cn/include/bcmdevs.h index 915f372..5437c8f 100644 --- a/bcmdhd.1.579.77.41.1.cn/include/bcmdevs.h +++ b/bcmdhd.1.579.77.41.1.cn/include/bcmdevs.h @@ -280,6 +280,10 @@ #define BCM4361_D11AC2G_ID 0x4420 /* 4361 802.11ac 2.4G device */ #define BCM4361_D11AC5G_ID 0x4421 /* 4361 802.11ac 5G device */ +#define BCM4362_D11AX_ID 0x4490 /* 4362 802.11ax dualband device */ +#define BCM4362_D11AX2G_ID 0x4491 /* 4362 802.11ax 2.4G device */ +#define BCM4362_D11AX5G_ID 0x4492 /* 4362 802.11ax 5G device */ + #define BCM4364_D11AC_ID 0x4464 /* 4364 802.11ac dualband device */ #define BCM4364_D11AC2G_ID 0x446a /* 4364 802.11ac 2.4G device */ #define BCM4364_D11AC5G_ID 0x446b /* 4364 802.11ac 5G device */ @@ -473,7 +477,6 @@ #define BCM43455_CHIP_ID 43455 /* 43455 chipcommon chipid */ #define BCM43457_CHIP_ID 43457 /* 43457 chipcommon chipid */ #define BCM43458_CHIP_ID 43458 /* 43458 chipcommon chipid */ -#define BCM43751_CHIP_ID 0x4362 /* 43751 chipcommon chipid */ #define BCM4345_CHIP(chipid) (CHIPID(chipid) == BCM4345_CHIP_ID || \ CHIPID(chipid) == BCM43454_CHIP_ID || \ @@ -502,6 +505,7 @@ #define BCM4347_CHIP_ID 0x4347 /* 4347 chipcommon chipid */ #define BCM4357_CHIP_ID 0x4357 /* 4357 chipcommon chipid */ #define BCM4361_CHIP_ID 0x4361 /* 4361 chipcommon chipid */ +#define BCM4362_CHIP_ID 0x4362 /* 4362 chipcommon chipid */ #define BCM4347_CHIP(chipid) ((CHIPID(chipid) == BCM4347_CHIP_ID) || \ (CHIPID(chipid) == BCM4357_CHIP_ID) || \ (CHIPID(chipid) == BCM4361_CHIP_ID)) diff --git a/bcmdhd.1.579.77.41.1.cn/include/dbus.h b/bcmdhd.1.579.77.41.1.cn/include/dbus.h index c926ba7..54357b8 100644 --- a/bcmdhd.1.579.77.41.1.cn/include/dbus.h +++ b/bcmdhd.1.579.77.41.1.cn/include/dbus.h @@ -33,10 +33,20 @@ #include "typedefs.h" -#define DBUSTRACE(args) +extern uint dbus_msglevel; +#define DBUS_ERROR_VAL 0x0001 +#define DBUS_TRACE_VAL 0x0002 +#define DBUS_INFO_VAL 0x0004 + +#if defined(DHD_DEBUG) +#define DBUSERR(args) do {if (dbus_msglevel & DBUS_ERROR_VAL) printf args;} while (0) +#define DBUSTRACE(args) do {if (dbus_msglevel & DBUS_TRACE_VAL) printf args;} while (0) +#define DBUSINFO(args) do {if (dbus_msglevel & DBUS_INFO_VAL) printf args;} while (0) +#else /* defined(DHD_DEBUG) */ #define DBUSERR(args) +#define DBUSTRACE(args) #define DBUSINFO(args) -#define DBUSDBGLOCK(args) +#endif enum { DBUS_OK = 0, diff --git a/bcmdhd.1.579.77.41.1.cn/include/epivers.h b/bcmdhd.1.579.77.41.1.cn/include/epivers.h index a2dc35e..f50d98f 100644 --- a/bcmdhd.1.579.77.41.1.cn/include/epivers.h +++ b/bcmdhd.1.579.77.41.1.cn/include/epivers.h @@ -46,6 +46,6 @@ #define EPI_VERSION_DEV 1.579.77.41 /* Driver Version String, ASCII, 32 chars max */ -#define EPI_VERSION_STR "1.579.77.41.3 (r)" +#define EPI_VERSION_STR "1.579.77.41.5 (r)" #endif /* _epivers_h_ */ diff --git a/bcmdhd.1.579.77.41.1.cn/include/sbchipc.h b/bcmdhd.1.579.77.41.1.cn/include/sbchipc.h index 0082765..cbc75b2 100644 --- a/bcmdhd.1.579.77.41.1.cn/include/sbchipc.h +++ b/bcmdhd.1.579.77.41.1.cn/include/sbchipc.h @@ -3343,7 +3343,7 @@ typedef volatile struct { #define CA7_4365_RAM_BASE (0x200000) #define CR4_4347_RAM_BASE (0x170000) -#define CR4_43751_RAM_BASE (0x170000) +#define CR4_4362_RAM_BASE (0x170000) /* 4335 chip OTP present & OTP select bits. */ #define SPROM4335_OTP_SELECT 0x00000010 diff --git a/bcmdhd.1.579.77.41.1.cn/include/usbrdl.h b/bcmdhd.1.579.77.41.1.cn/include/usbrdl.h new file mode 100755 index 0000000..be5bd69 --- /dev/null +++ b/bcmdhd.1.579.77.41.1.cn/include/usbrdl.h @@ -0,0 +1,134 @@ +/* + * Broadcom USB remote download definitions + * + * Copyright (C) 1999-2016, 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: usbrdl.h 597933 2015-11-06 18:52:06Z $ + */ + +#ifndef _USB_RDL_H +#define _USB_RDL_H + +/* Control messages: bRequest values */ +#define DL_GETSTATE 0 /* returns the rdl_state_t struct */ +#define DL_CHECK_CRC 1 /* currently unused */ +#define DL_GO 2 /* execute downloaded image */ +#define DL_START 3 /* initialize dl state */ +#define DL_REBOOT 4 /* reboot the device in 2 seconds */ +#define DL_GETVER 5 /* returns the bootrom_id_t struct */ +#define DL_GO_PROTECTED 6 /* execute the downloaded code and set reset event + * to occur in 2 seconds. It is the responsibility + * of the downloaded code to clear this event + */ +#define DL_EXEC 7 /* jump to a supplied address */ +#define DL_RESETCFG 8 /* To support single enum on dongle + * - Not used by bootloader + */ +#define DL_DEFER_RESP_OK 9 /* Potentially defer the response to setup + * if resp unavailable + */ +#define DL_CHGSPD 0x0A + +#define DL_HWCMD_MASK 0xfc /* Mask for hardware read commands: */ +#define DL_RDHW 0x10 /* Read a hardware address (Ctl-in) */ +#define DL_RDHW32 0x10 /* Read a 32 bit word */ +#define DL_RDHW16 0x11 /* Read 16 bits */ +#define DL_RDHW8 0x12 /* Read an 8 bit byte */ +#define DL_WRHW 0x14 /* Write a hardware address (Ctl-out) */ +#define DL_WRHW_BLK 0x13 /* Block write to hardware access */ + +#define DL_CMD_WRHW 2 + + +/* states */ +#define DL_WAITING 0 /* waiting to rx first pkt that includes the hdr info */ +#define DL_READY 1 /* hdr was good, waiting for more of the compressed image */ +#define DL_BAD_HDR 2 /* hdr was corrupted */ +#define DL_BAD_CRC 3 /* compressed image was corrupted */ +#define DL_RUNNABLE 4 /* download was successful, waiting for go cmd */ +#define DL_START_FAIL 5 /* failed to initialize correctly */ +#define DL_NVRAM_TOOBIG 6 /* host specified nvram data exceeds DL_NVRAM value */ +#define DL_IMAGE_TOOBIG 7 /* download image too big (exceeds DATA_START for rdl) */ + +#define TIMEOUT 5000 /* Timeout for usb commands */ + +struct bcm_device_id { + char *name; + uint32 vend; + uint32 prod; +}; + +typedef struct { + uint32 state; + uint32 bytes; +} rdl_state_t; + +typedef struct { + uint32 chip; /* Chip id */ + uint32 chiprev; /* Chip rev */ + uint32 ramsize; /* Size of RAM */ + uint32 remapbase; /* Current remap base address */ + uint32 boardtype; /* Type of board */ + uint32 boardrev; /* Board revision */ +} bootrom_id_t; + +/* struct for backplane & jtag accesses */ +typedef struct { + uint32 cmd; /* tag to identify the cmd */ + uint32 addr; /* backplane address for write */ + uint32 len; /* length of data: 1, 2, 4 bytes */ + uint32 data; /* data to write */ +} hwacc_t; + + +/* struct for querying nvram params from bootloader */ +#define QUERY_STRING_MAX 32 +typedef struct { + uint32 cmd; /* tag to identify the cmd */ + char var[QUERY_STRING_MAX]; /* param name */ +} nvparam_t; + +typedef void (*exec_fn_t)(void *sih); + +#define USB_CTRL_IN (USB_TYPE_VENDOR | 0x80 | USB_RECIP_INTERFACE) +#define USB_CTRL_OUT (USB_TYPE_VENDOR | 0 | USB_RECIP_INTERFACE) + +#define USB_CTRL_EP_TIMEOUT 500 /* Timeout used in USB control_msg transactions. */ +#define USB_BULK_EP_TIMEOUT 500 /* Timeout used in USB bulk transactions. */ + +#define RDL_CHUNK_MAX (64 * 1024) /* max size of each dl transfer */ +#define RDL_CHUNK 1500 /* size of each dl transfer */ + +/* bootloader makes special use of trx header "offsets" array */ +#define TRX_OFFSETS_DLFWLEN_IDX 0 /* Size of the fw; used in uncompressed case */ +#define TRX_OFFSETS_JUMPTO_IDX 1 /* RAM address for jumpto after download */ +#define TRX_OFFSETS_NVM_LEN_IDX 2 /* Length of appended NVRAM data */ +#ifdef BCMTRXV2 +#define TRX_OFFSETS_DSG_LEN_IDX 3 /* Length of digital signature for the first image */ +#define TRX_OFFSETS_CFG_LEN_IDX 4 /* Length of config region, which is not digitally signed */ +#endif /* BCMTRXV2 */ + +#define TRX_OFFSETS_DLBASE_IDX 0 /* RAM start address for download */ + +#endif /* _USB_RDL_H */ diff --git a/bcmdhd.1.579.77.41.1.cn/include/wlioctl.h b/bcmdhd.1.579.77.41.1.cn/include/wlioctl.h index 1e6a3a2..812182a 100644 --- a/bcmdhd.1.579.77.41.1.cn/include/wlioctl.h +++ b/bcmdhd.1.579.77.41.1.cn/include/wlioctl.h @@ -11548,6 +11548,15 @@ typedef enum wl_interface_type { */ #define WL_INTERFACE_BSSID_INDEX_USE (1 << 4) +#ifdef WLMESH +typedef struct wl_interface_info { + uint16 ver; /* version of this struct */ + struct ether_addr mac_addr; /* MAC address of the interface */ + char ifname[BCM_MSG_IFNAME_MAX]; /* name of interface */ + uint8 bsscfgidx; /* source bsscfg index */ +} wl_interface_info_t; +#endif + typedef struct wl_interface_create { uint16 ver; /* version of this struct */ uint32 flags; /* flags that defines the operation */ @@ -12462,6 +12471,12 @@ enum wl_mesh_cmd_xtlv_id { }; /* endif WLMESH */ +#ifdef WLMESH +#ifndef SAE_MAX_PASSWD_LEN +#define SAE_MAX_PASSWD_LEN 32 +#endif +#endif + /* Fast BSS Transition parameter configuration */ #define FBT_PARAM_CURRENT_VERSION 0 diff --git a/bcmdhd.1.579.77.41.1.cn/linux_osl.c b/bcmdhd.1.579.77.41.1.cn/linux_osl.c index fe43c3c..b0d8798 100644 --- a/bcmdhd.1.579.77.41.1.cn/linux_osl.c +++ b/bcmdhd.1.579.77.41.1.cn/linux_osl.c @@ -2151,9 +2151,13 @@ osl_os_get_image_block(char *buf, int len, void *image) if (!image) return 0; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + rdlen = kernel_read(fp, buf, len, &fp->f_pos); +#else rdlen = kernel_read(fp, fp->f_pos, buf, len); if (rdlen > 0) fp->f_pos += rdlen; +#endif return rdlen; } @@ -2680,7 +2684,12 @@ osl_pkt_orphan_partial(struct sk_buff *skb, int tsq) */ fraction = skb->truesize * (tsq - 1) / tsq; skb->truesize -= fraction; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0) + atomic_sub(fraction, &skb->sk->sk_wmem_alloc.refs); +#else atomic_sub(fraction, &skb->sk->sk_wmem_alloc); +#endif /* LINUX_VERSION >= 4.13.0 */ + skb_orphan(skb); } #endif /* LINUX_VERSION >= 3.6.0 && TSQ_MULTIPLIER */ diff --git a/bcmdhd.1.579.77.41.1.cn/siutils.c b/bcmdhd.1.579.77.41.1.cn/siutils.c index 11e71bf..71bfe66 100644 --- a/bcmdhd.1.579.77.41.1.cn/siutils.c +++ b/bcmdhd.1.579.77.41.1.cn/siutils.c @@ -604,12 +604,14 @@ si_doattach(si_info_t *sii, uint devid, osl_t *osh, volatile void *regs, } sih->bustype = bustype; -/* if (bustype != BUSTYPE(bustype)) { +#ifdef BCMBUSTYPE + if (bustype != BUSTYPE(bustype)) { SI_ERROR(("si_doattach: bus type %d does not match configured bus type %d\n", bustype, BUSTYPE(bustype))); return NULL; } -*/ +#endif + /* bus/core/clk setup for register access */ if (!si_buscore_prep(sii, bustype, devid, sdh)) { SI_ERROR(("si_doattach: si_core_clk_prep failed %d\n", bustype)); diff --git a/bcmdhd.1.579.77.41.1.cn/wl_android.c b/bcmdhd.1.579.77.41.1.cn/wl_android.c index 42afe36..7154ac5 100644 --- a/bcmdhd.1.579.77.41.1.cn/wl_android.c +++ b/bcmdhd.1.579.77.41.1.cn/wl_android.c @@ -104,6 +104,10 @@ uint android_msg_level = ANDROID_ERROR_LEVEL; #define CMD_SETBAND "SETBAND" #define CMD_GETBAND "GETBAND" #define CMD_COUNTRY "COUNTRY" +#ifdef WLMESH +#define CMD_SAE_SET_PASSWORD "SAE_SET_PASSWORD" +#define CMD_SET_RSDB_MODE "RSDB_MODE" +#endif #define CMD_P2P_SET_NOA "P2P_SET_NOA" #if !defined WL_ENABLE_P2P_IF #define CMD_P2P_GET_NOA "P2P_GET_NOA" @@ -1179,6 +1183,7 @@ static int wl_cfg80211_wbtext_btm_delta(struct net_device *dev, #define PNO_PARAM_SIZE 50 #define VALUE_SIZE 50 #define LIMIT_STR_FMT ("%50s %50s") + static int wls_parse_batching_cmd(struct net_device *dev, char *command, int total_len) { @@ -1187,7 +1192,8 @@ wls_parse_batching_cmd(struct net_device *dev, char *command, int total_len) char *pos, *pos2, *token, *token2, *delim; char param[PNO_PARAM_SIZE+1], value[VALUE_SIZE+1]; struct dhd_pno_batch_params batch_params; - DHD_PNO(("%s: command=%s, len=%d\n", __FUNCTION__, command, total_len)); + + ANDROID_INFO(("%s: command=%s, len=%d\n", __FUNCTION__, command, total_len)); if (total_len < strlen(CMD_WLS_BATCHING)) { ANDROID_ERROR(("%s argument=%d less min size\n", __FUNCTION__, total_len)); err = BCME_ERROR; @@ -1212,13 +1218,13 @@ wls_parse_batching_cmd(struct net_device *dev, char *command, int total_len) tokens = sscanf(token, LIMIT_STR_FMT, param, value); if (!strncmp(param, PNO_PARAM_SCANFREQ, strlen(PNO_PARAM_SCANFREQ))) { batch_params.scan_fr = simple_strtol(value, NULL, 0); - DHD_PNO(("scan_freq : %d\n", batch_params.scan_fr)); + ANDROID_INFO(("scan_freq : %d\n", batch_params.scan_fr)); } else if (!strncmp(param, PNO_PARAM_BESTN, strlen(PNO_PARAM_BESTN))) { batch_params.bestn = simple_strtol(value, NULL, 0); - DHD_PNO(("bestn : %d\n", batch_params.bestn)); + ANDROID_INFO(("bestn : %d\n", batch_params.bestn)); } else if (!strncmp(param, PNO_PARAM_MSCAN, strlen(PNO_PARAM_MSCAN))) { batch_params.mscan = simple_strtol(value, NULL, 0); - DHD_PNO(("mscan : %d\n", batch_params.mscan)); + ANDROID_INFO(("mscan : %d\n", batch_params.mscan)); } else if (!strncmp(param, PNO_PARAM_CHANNEL, strlen(PNO_PARAM_CHANNEL))) { i = 0; pos2 = value; @@ -1238,7 +1244,7 @@ wls_parse_batching_cmd(struct net_device *dev, char *command, int total_len) if (*token2 == 'A' || *token2 == 'B') { batch_params.band = (*token2 == 'A')? WLC_BAND_5G : WLC_BAND_2G; - DHD_PNO(("band : %s\n", + ANDROID_INFO(("band : %s\n", (*token2 == 'A')? "A" : "B")); } else { if ((batch_params.nchan >= WL_NUMCHANNELS) || @@ -1251,13 +1257,13 @@ wls_parse_batching_cmd(struct net_device *dev, char *command, int total_len) batch_params.chan_list[i++] = simple_strtol(token2, NULL, 0); batch_params.nchan++; - DHD_PNO(("channel :%d\n", + ANDROID_INFO(("channel :%d\n", batch_params.chan_list[i-1])); } } } else if (!strncmp(param, PNO_PARAM_RTT, strlen(PNO_PARAM_RTT))) { batch_params.rtt = simple_strtol(value, NULL, 0); - DHD_PNO(("rtt : %d\n", batch_params.rtt)); + ANDROID_INFO(("rtt : %d\n", batch_params.rtt)); } else { ANDROID_ERROR(("%s : unknown param: %s\n", __FUNCTION__, param)); err = BCME_ERROR; @@ -1294,6 +1300,7 @@ wls_parse_batching_cmd(struct net_device *dev, char *command, int total_len) exit: return err; } + #ifndef WL_SCHED_SCAN static int wl_android_set_pno_setup(struct net_device *dev, char *command, int total_len) { @@ -1327,7 +1334,7 @@ static int wl_android_set_pno_setup(struct net_device *dev, char *command, int t 0x00 }; #endif /* PNO_SET_DEBUG */ - DHD_PNO(("%s: command=%s, len=%d\n", __FUNCTION__, command, total_len)); + ANDROID_INFO(("%s: command=%s, len=%d\n", __FUNCTION__, command, total_len)); if (total_len < (strlen(CMD_PNOSETUP_SET) + sizeof(cmd_tlv_t))) { ANDROID_ERROR(("%s argument=%d less min size\n", __FUNCTION__, total_len)); @@ -1362,7 +1369,7 @@ static int wl_android_set_pno_setup(struct net_device *dev, char *command, int t } str_ptr++; pno_time = simple_strtoul(str_ptr, &str_ptr, 16); - DHD_PNO(("%s: pno_time=%d\n", __FUNCTION__, pno_time)); + ANDROID_INFO(("%s: pno_time=%d\n", __FUNCTION__, pno_time)); if (str_ptr[0] != 0) { if ((str_ptr[0] != PNO_TLV_FREQ_REPEAT)) { @@ -1372,7 +1379,7 @@ static int wl_android_set_pno_setup(struct net_device *dev, char *command, int t } str_ptr++; pno_repeat = simple_strtoul(str_ptr, &str_ptr, 16); - DHD_PNO(("%s :got pno_repeat=%d\n", __FUNCTION__, pno_repeat)); + ANDROID_INFO(("%s :got pno_repeat=%d\n", __FUNCTION__, pno_repeat)); if (str_ptr[0] != PNO_TLV_FREQ_EXPO_MAX) { ANDROID_ERROR(("%s FREQ_EXPO_MAX corrupted field size\n", __FUNCTION__)); @@ -1380,7 +1387,7 @@ static int wl_android_set_pno_setup(struct net_device *dev, char *command, int t } str_ptr++; pno_freq_expo_max = simple_strtoul(str_ptr, &str_ptr, 16); - DHD_PNO(("%s: pno_freq_expo_max=%d\n", + ANDROID_INFO(("%s: pno_freq_expo_max=%d\n", __FUNCTION__, pno_freq_expo_max)); } } @@ -1552,10 +1559,6 @@ int wl_android_wifi_on(struct net_device *dev) { int ret = 0; int retry = POWERUP_MAX_RETRY; -#ifdef IAPSTA_PREINIT - int bytes_written = 0; - struct dhd_conf *conf; -#endif if (!dev) { ANDROID_ERROR(("%s: dev is null\n", __FUNCTION__)); @@ -1604,15 +1607,6 @@ int wl_android_wifi_on(struct net_device *dev) } } #endif /* !BCMPCIE */ - -#ifdef IAPSTA_PREINIT - conf = dhd_get_conf(dev); - if (conf) { - wl_android_ext_priv_cmd(dev, conf->iapsta_init, 0, &bytes_written); - wl_android_ext_priv_cmd(dev, conf->iapsta_config, 0, &bytes_written); - wl_android_ext_priv_cmd(dev, conf->iapsta_enable, 0, &bytes_written); - } -#endif g_wifi_on = TRUE; } @@ -1621,11 +1615,13 @@ exit: dhd_net_if_unlock(dev); return ret; -#ifdef BCMSDIO +#ifndef BCMPCIE err: +#ifdef BCMSDIO dhd_net_bus_devreset(dev, TRUE); dhd_net_bus_suspend(dev); dhd_net_wifi_platform_set_power(dev, FALSE, WIFI_TURNOFF_DELAY); +#endif printf("%s: Failed\n", __FUNCTION__); dhd_net_if_unlock(dev); return ret; @@ -4203,6 +4199,38 @@ wl_android_make_hang_with_reason(struct net_device *dev, const char *string_num) } #endif /* DHD_HANG_SEND_UP_TEST */ +#ifdef WLMESH +static int +wl_android_set_rsdb_mode(struct net_device *dev, char *command, int total_len) +{ + int ret; + wl_config_t rsdb_mode_cfg = {-1, 0}; + char smbuf[WLC_IOCTL_SMLEN]; + s32 val = 1; + + if (sscanf(command, "%*s %d", &rsdb_mode_cfg.config) != 1) { + DHD_ERROR(("%s: Failed to get Parameter\n", __FUNCTION__)); + return -1; + } + DHD_INFO(("%s : RSDB_MODE = %d\n", __FUNCTION__, rsdb_mode_cfg.config)); + + ret = wldev_ioctl_set(dev, WLC_DOWN, &val, sizeof(s32)); + if (ret < 0) + DHD_ERROR(("WLC_DOWN error %d\n", ret)); + + ret = wldev_iovar_setbuf(dev, "rsdb_mode", &rsdb_mode_cfg, sizeof(rsdb_mode_cfg), + smbuf, sizeof(smbuf), NULL); + if (ret < 0) + DHD_ERROR(("%s : set rsdb_mode error=%d\n", __FUNCTION__, ret)); + + ret = wldev_ioctl_set(dev, WLC_UP, &val, sizeof(s32)); + if (ret < 0) + DHD_ERROR(("WLC_UP error %d\n", ret)); + + return ret; +} +#endif /* WLMESH */ + #ifdef SUPPORT_LQCM static int wl_android_lqcm_enable(struct net_device *net, int lqcm_enable) @@ -4813,7 +4841,7 @@ wl_handle_private_cmd(struct net_device *net, char *command, u32 cmd_len) bytes_written = BCME_DISABLED; #else /* DISABLE_SETBAND */ uint band = *(command + strlen(CMD_SETBAND) + 1) - '0'; - if (dhd_conf_get_band(dhd_get_pub(net)) != WLC_BAND_AUTO) { + if (dhd_conf_get_band(dhd_get_pub(net)) >= WLC_BAND_AUTO) { printf("%s: Band is fixed in config.txt\n", __FUNCTION__); } else bytes_written = wl_cfg80211_set_if_band(net, band); @@ -4900,6 +4928,16 @@ wl_handle_private_cmd(struct net_device *net, char *command, u32 cmd_len) else if (strnicmp(command, CMD_P2P_DEV_ADDR, strlen(CMD_P2P_DEV_ADDR)) == 0) { bytes_written = wl_android_get_p2p_dev_addr(net, command, priv_cmd.total_len); } +#ifdef WLMESH + else if (strnicmp(command, CMD_SAE_SET_PASSWORD, strlen(CMD_SAE_SET_PASSWORD)) == 0) { + int skip = strlen(CMD_SAE_SET_PASSWORD) + 1; + bytes_written = wl_cfg80211_set_sae_password(net, command + skip, + priv_cmd.total_len - skip); + } + else if (strnicmp(command, CMD_SET_RSDB_MODE, strlen(CMD_SET_RSDB_MODE)) == 0) { + bytes_written = wl_android_set_rsdb_mode(net, command, priv_cmd.total_len); + } +#endif else if (strnicmp(command, CMD_P2P_SET_NOA, strlen(CMD_P2P_SET_NOA)) == 0) { int skip = strlen(CMD_P2P_SET_NOA) + 1; bytes_written = wl_cfg80211_set_p2p_noa(net, command + skip, diff --git a/bcmdhd.1.579.77.41.1.cn/wl_android.h b/bcmdhd.1.579.77.41.1.cn/wl_android.h index fe33304..df25768 100644 --- a/bcmdhd.1.579.77.41.1.cn/wl_android.h +++ b/bcmdhd.1.579.77.41.1.cn/wl_android.h @@ -104,9 +104,14 @@ int wl_handle_private_cmd(struct net_device *net, char *command, u32 cmd_len); s32 wl_netlink_send_msg(int pid, int type, int seq, const void *data, size_t size); #ifdef WL_EXT_IAPSTA -int wl_android_ext_attach_netdev(struct net_device *net, uint8 bssidx); -int wl_android_ext_dettach_netdev(void); -void wl_android_ext_iapsta_disconnect_sta(struct net_device *dev, u32 channel); +int wl_ext_iapsta_attach_netdev(struct net_device *net, uint8 bssidx); +int wl_ext_iapsta_attach_name(struct net_device *net, uint8 bssidx); +int wl_ext_iapsta_dettach_netdev(void); +void wl_ext_iapsta_disconnect_sta(struct net_device *dev, u32 channel); +int wl_ext_iapsta_alive_preinit(struct net_device *dev); +int wl_ext_iapsta_alive_postinit(struct net_device *dev); +int wl_ext_iapsta_event(struct net_device *dev, wl_event_msg_t *e, void* data); +extern int op_mode; #endif int wl_android_ext_priv_cmd(struct net_device *net, char *command, int total_len, int *bytes_written); @@ -125,13 +130,18 @@ typedef enum APSTAMODE { IAPONLY_MODE, IAPSTA_MODE, IDUALAP_MODE, - IGOSTA_MODE, - IGCSTA_MODE + IMESHONLY_MODE, + IMESHSTA_MODE, + IMESHAP_MODE, + IMESHAPSTA_MODE, + IMESHAPAP_MODE, + IGOSTA_MODE } apstamode_t; typedef enum IFMODE { ISTA_MODE = 1, - IAP_MODE + IAP_MODE, + IMESH_MODE } ifmode_t; typedef enum BGNMODE { @@ -158,6 +168,13 @@ typedef enum ENCMODE { ENC_TKIPAES } encmode_t; +enum wl_if_list { + IF_PIF, + IF_VIF, + IF_VIF2, + MAX_IF_NUM +}; + /* i/f query */ typedef struct wl_if_info { struct net_device *dev; @@ -174,14 +191,16 @@ typedef struct wl_if_info { authmode_t amode; encmode_t emode; char key[100]; -} wl_apsta_if_t; +} wl_if_info_t; typedef struct wl_apsta_params { - struct wl_if_info pif; // primary device - struct wl_if_info vif; // virtual device + struct wl_if_info if_info[MAX_IF_NUM]; // primary device int ioctl_ver; bool init; + bool vsdb; apstamode_t apstamode; + bool netif_change; + wait_queue_head_t netif_change_event; } wl_apsta_params_t; /* hostap mac mode */ diff --git a/bcmdhd.1.579.77.41.1.cn/wl_android_ext.c b/bcmdhd.1.579.77.41.1.cn/wl_android_ext.c index 6265338..2d9e199 100644 --- a/bcmdhd.1.579.77.41.1.cn/wl_android_ext.c +++ b/bcmdhd.1.579.77.41.1.cn/wl_android_ext.c @@ -3,8 +3,19 @@ #include #include #include +#include +#include +#include + +#include +#include +#include #include +#include +#include +#include +#include #include #include #include @@ -13,15 +24,29 @@ #include #include #include +#ifdef WL_CFG80211 +#include +#endif +#ifndef WL_CFG80211 #define htod32(i) i #define htod16(i) i #define dtoh32(i) i #define dtoh16(i) i #define htodchanspec(i) i #define dtohchanspec(i) i +#define IEEE80211_BAND_2GHZ 0 +#define IEEE80211_BAND_5GHZ 1 +#define WL_SCAN_JOIN_PROBE_INTERVAL_MS 20 +#define WL_SCAN_JOIN_ACTIVE_DWELL_TIME_MS 320 +#define WL_SCAN_JOIN_PASSIVE_DWELL_TIME_MS 400 +#endif #define strtoul(nptr, endptr, base) bcm_strtoul((nptr), (endptr), (base)) +#ifndef IW_CUSTOM_MAX +#define IW_CUSTOM_MAX 256 /* size of extra buffer used for translation of events */ +#endif /* IW_CUSTOM_MAX */ + #define CMD_CHANNEL "CHANNEL" #define CMD_CHANNELS "CHANNELS" #define CMD_ROAM_TRIGGER "ROAM_TRIGGER" @@ -35,6 +60,10 @@ #define CMD_IAPSTA_CONFIG "IAPSTA_CONFIG" #define CMD_IAPSTA_ENABLE "IAPSTA_ENABLE" #define CMD_IAPSTA_DISABLE "IAPSTA_DISABLE" +#define CMD_ISAM_INIT "ISAM_INIT" +#define CMD_ISAM_CONFIG "ISAM_CONFIG" +#define CMD_ISAM_ENABLE "ISAM_ENABLE" +#define CMD_ISAM_DISABLE "ISAM_DISABLE" #ifdef PROP_TXSTATUS #ifdef PROP_TXSTATUS_VSDB #include @@ -48,9 +77,6 @@ extern int disable_proptx; #endif #define CMD_WL "WL" -#define IEEE80211_BAND_2GHZ 0 -#define IEEE80211_BAND_5GHZ 1 - int wl_ext_ioctl(struct net_device *dev, u32 cmd, void *arg, u32 len, u32 set) { int ret; @@ -108,6 +134,7 @@ int wl_ext_iovar_setbuf(struct net_device *dev, s8 *iovar_name, } #ifdef WL_EXT_IAPSTA +static int wl_ext_iapsta_enable(struct net_device *dev, char *command, int total_len); int wl_ext_iovar_setbuf_bsscfg(struct net_device *dev, s8 *iovar_name, void *param, s32 paramlen, void *buf, s32 buflen, s32 bsscfg_idx, struct mutex* buf_sync) { @@ -225,7 +252,7 @@ wl_ext_get_ioctl_ver(struct net_device *dev, int *ioctl_ver) } static int -wl_ext_set_chanspec(struct net_device *dev, uint16 channel) +wl_ext_set_chanspec(struct net_device *dev, uint16 channel, chanspec_t *ret_chspec) { s32 _chan = channel; chanspec_t chspec = 0; @@ -307,6 +334,7 @@ change_bw: ANDROID_ERROR(("%s: Invalid chanspec 0x%x\n", __FUNCTION__, chspec)); err = BCME_ERROR; } + *ret_chspec = fw_chspec; return err; } @@ -318,13 +346,14 @@ wl_ext_channel(struct net_device *dev, char* command, int total_len) int channel=0; channel_info_t ci; int bytes_written = 0; + chanspec_t fw_chspec; ANDROID_TRACE(("%s: cmd %s\n", __FUNCTION__, command)); sscanf(command, "%*s %d", &channel); if (channel > 0) { - ret = wl_ext_set_chanspec(dev, channel); + ret = wl_ext_set_chanspec(dev, channel, &fw_chspec); } else { if (!(ret = wldev_ioctl(dev, WLC_GET_CHANNEL, &ci, sizeof(channel_info_t), FALSE))) { ANDROID_TRACE(("hw_channel %d\n", ci.hw_channel)); @@ -694,6 +723,13 @@ wl_ext_set_amode(struct wl_if_info *cur_if, struct wl_apsta_params *apsta_params wpa_auth = 132; ANDROID_TRACE(("%s: Authentication: WPA/WPA2-PSK\n", __FUNCTION__)); } + if (cur_if->ifmode == IMESH_MODE) { + s32 val = WL_BSSTYPE_MESH; + wl_ext_ioctl(dev, WLC_SET_INFRA, &val, sizeof(val), 1); + } else if (cur_if->ifmode == ISTA_MODE) { + s32 val = WL_BSSTYPE_INFRA; + wl_ext_ioctl(dev, WLC_SET_INFRA, &val, sizeof(val), 1); + } wl_ext_iovar_setint(dev, "auth", auth); wl_ext_iovar_setint(dev, "wpa_auth", wpa_auth); @@ -708,8 +744,10 @@ wl_ext_set_emode(struct wl_if_info *cur_if, struct wl_apsta_params *apsta_params int wsec=0; struct wl_wsec_key wsec_key; wsec_pmk_t psk; + authmode_t amode = cur_if->amode; encmode_t emode = cur_if->emode; char *key = cur_if->key; + s8 iovar_buf[WLC_IOCTL_SMLEN]; memset(&wsec_key, 0, sizeof(wsec_key)); memset(&psk, 0, sizeof(psk)); @@ -746,7 +784,17 @@ wl_ext_set_emode(struct wl_if_info *cur_if, struct wl_apsta_params *apsta_params wl_ext_iovar_setint(dev, "wsec", wsec); - if (wsec == 1) { + if (cur_if->ifmode == IMESH_MODE) { + if (amode == AUTH_WPA2PSK && emode == ENC_AES) { + wl_ext_iovar_setint(dev, "mesh_auth_proto", 1); + wl_ext_iovar_setint(dev, "mfp", WL_MFP_REQUIRED); + wl_ext_iovar_setbuf(dev, "sae_password", key, strlen(key), + iovar_buf, WLC_IOCTL_SMLEN, NULL); + } else { + wl_ext_iovar_setint(dev, "mesh_auth_proto", 0); + wl_ext_iovar_setint(dev, "mfp", WL_MFP_NONE); + } + } else if (emode == ENC_WEP) { wl_ext_ioctl(dev, WLC_SET_KEY, &wsec_key, sizeof(wsec_key), 1); } else if (emode == ENC_TKIP || emode == ENC_AES || emode == ENC_TKIPAES) { if (dev) { @@ -761,104 +809,258 @@ wl_ext_set_emode(struct wl_if_info *cur_if, struct wl_apsta_params *apsta_params return 0; } -static int -wl_ext_iapsta_init(struct net_device *dev, char *command, int total_len) +static void +wl_ext_ch_to_chanspec(int ch, struct wl_join_params *join_params, + size_t *join_params_size) { - s32 val = 0; - char *pch, *pick_tmp, *param; - wlc_ssid_t ssid = { 0, {0} }; - s8 iovar_buf[WLC_IOCTL_SMLEN]; struct wl_apsta_params *apsta_params = &g_apsta_params; - wl_interface_create_t iface; - struct dhd_pub *dhd; - wl_p2p_if_t ifreq; - wl_country_t cspec = {{0}, 0, {0}}; + chanspec_t chanspec = 0; - if (apsta_params->init) { - ANDROID_ERROR(("%s: don't init twice\n", __FUNCTION__)); - return -1; + if (ch != 0) { + join_params->params.chanspec_num = 1; + join_params->params.chanspec_list[0] = ch; + + if (join_params->params.chanspec_list[0] <= CH_MAX_2G_CHANNEL) + 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] = + wl_ext_chspec_host_to_driver(apsta_params->ioctl_ver, + join_params->params.chanspec_list[0]); + + join_params->params.chanspec_num = + htod32(join_params->params.chanspec_num); + ANDROID_TRACE(("join_params->params.chanspec_list[0]= %X, %d channels\n", + join_params->params.chanspec_list[0], + join_params->params.chanspec_num)); } +} - dhd = dhd_get_pub(dev); - memset(apsta_params, 0, sizeof(struct wl_apsta_params)); +static chanspec_t +wl_ext_chspec_from_legacy(chanspec_t legacy_chspec) +{ + chanspec_t chspec; - ANDROID_TRACE(("%s: command=%s, len=%d\n", __FUNCTION__, command, total_len)); + /* get the channel number */ + chspec = LCHSPEC_CHANNEL(legacy_chspec); - pick_tmp = command; - param = bcmstrtok(&pick_tmp, " ", 0); // skip iapsta_init - param = bcmstrtok(&pick_tmp, " ", 0); - while (param != NULL) { - if (!strcmp(param, "mode")) { - pch = bcmstrtok(&pick_tmp, " ", 0); - if (pch) { - if (!strcmp(pch, "sta")) { - apsta_params->apstamode = ISTAONLY_MODE; - } else if (!strcmp(pch, "ap")) { - apsta_params->apstamode = IAPONLY_MODE; - } else if (!strcmp(pch, "apsta")) { - apsta_params->apstamode = IAPSTA_MODE; - } else if (!strcmp(pch, "dualap")) { - apsta_params->apstamode = IDUALAP_MODE; - } else if (!strcmp(pch, "gosta")) { - if (!FW_SUPPORTED(dhd, p2p)) { - return -1; - } - apsta_params->apstamode = IGOSTA_MODE; - } else if (!strcmp(pch, "gcsta")) { - if (!FW_SUPPORTED(dhd, p2p)) { - return -1; - } - apsta_params->apstamode = IGCSTA_MODE; - } else { - ANDROID_ERROR(("%s: mode [sta|ap|apsta|dualap]\n", __FUNCTION__)); - return -1; - } - } - } else if (!strcmp(param, "vifname")) { - pch = bcmstrtok(&pick_tmp, " ", 0); - if (pch) - strcpy(apsta_params->vif.ifname, pch); - else { - ANDROID_ERROR(("%s: vifname [wlan1]\n", __FUNCTION__)); - return -1; - } + /* convert the band */ + if (LCHSPEC_IS2G(legacy_chspec)) { + chspec |= WL_CHANSPEC_BAND_2G; + } else { + chspec |= WL_CHANSPEC_BAND_5G; + } + + /* convert the bw and sideband */ + if (LCHSPEC_IS20(legacy_chspec)) { + chspec |= WL_CHANSPEC_BW_20; + } else { + chspec |= WL_CHANSPEC_BW_40; + if (LCHSPEC_CTL_SB(legacy_chspec) == WL_LCHANSPEC_CTL_SB_LOWER) { + chspec |= WL_CHANSPEC_CTL_SB_L; + } else { + chspec |= WL_CHANSPEC_CTL_SB_U; } - param = bcmstrtok(&pick_tmp, " ", 0); } - if (apsta_params->apstamode == 0) { - ANDROID_ERROR(("%s: mode [sta|ap|apsta|dualap]\n", __FUNCTION__)); - return -1; + if (wf_chspec_malformed(chspec)) { + ANDROID_ERROR(("wl_ext_chspec_from_legacy: output chanspec (0x%04X) malformed\n", + chspec)); + return INVCHANSPEC; + } + + return chspec; +} + +static chanspec_t +wl_ext_chspec_driver_to_host(int ioctl_ver, chanspec_t chanspec) +{ + chanspec = dtohchanspec(chanspec); + if (ioctl_ver == 1) { + chanspec = wl_ext_chspec_from_legacy(chanspec); + } + + return chanspec; +} + +static s32 +wl_ext_connect(struct wl_if_info *cur_if) +{ + struct wl_apsta_params *apsta_params = &g_apsta_params; + wl_extjoin_params_t *ext_join_params; + struct wl_join_params join_params; + size_t join_params_size; + s32 err = 0; + u32 chan_cnt = 0; + s8 iovar_buf[WLC_IOCTL_SMLEN]; + + if (cur_if->channel) { + chan_cnt = 1; + } + + /* + * Join with specific BSSID and cached SSID + * If SSID is zero join based on BSSID only + */ + join_params_size = WL_EXTJOIN_PARAMS_FIXED_SIZE + + chan_cnt * sizeof(chanspec_t); + ext_join_params = (wl_extjoin_params_t*)kzalloc(join_params_size, GFP_KERNEL); + if (ext_join_params == NULL) { + err = -ENOMEM; + goto exit; + } + ext_join_params->ssid.SSID_len = min(sizeof(ext_join_params->ssid.SSID), strlen(cur_if->ssid)); + memcpy(&ext_join_params->ssid.SSID, cur_if->ssid, ext_join_params->ssid.SSID_len); + ext_join_params->ssid.SSID_len = htod32(ext_join_params->ssid.SSID_len); + /* increate dwell time to receive probe response or detect Beacon + * from target AP at a noisy air only during connect command + */ + ext_join_params->scan.active_time = chan_cnt ? WL_SCAN_JOIN_ACTIVE_DWELL_TIME_MS : -1; + ext_join_params->scan.passive_time = chan_cnt ? WL_SCAN_JOIN_PASSIVE_DWELL_TIME_MS : -1; + /* Set up join scan parameters */ + ext_join_params->scan.scan_type = -1; + ext_join_params->scan.nprobes = chan_cnt ? + (ext_join_params->scan.active_time/WL_SCAN_JOIN_PROBE_INTERVAL_MS) : -1; + ext_join_params->scan.home_time = -1; + + if (memcmp(ðer_null, &cur_if->bssid, ETHER_ADDR_LEN)) + memcpy(&ext_join_params->assoc.bssid, &cur_if->bssid, ETH_ALEN); + else + memcpy(&ext_join_params->assoc.bssid, ðer_bcast, ETH_ALEN); + ext_join_params->assoc.chanspec_num = chan_cnt; + if (chan_cnt) { + u16 channel, band, bw, ctl_sb; + chanspec_t chspec; + channel = cur_if->channel; + band = (channel <= CH_MAX_2G_CHANNEL) ? WL_CHANSPEC_BAND_2G + : WL_CHANSPEC_BAND_5G; + bw = WL_CHANSPEC_BW_20; + ctl_sb = WL_CHANSPEC_CTL_SB_NONE; + chspec = (channel | band | bw | ctl_sb); + ext_join_params->assoc.chanspec_list[0] &= WL_CHANSPEC_CHAN_MASK; + ext_join_params->assoc.chanspec_list[0] |= chspec; + ext_join_params->assoc.chanspec_list[0] = + wl_ext_chspec_host_to_driver(apsta_params->ioctl_ver, + ext_join_params->assoc.chanspec_list[0]); + } + ext_join_params->assoc.chanspec_num = htod32(ext_join_params->assoc.chanspec_num); + if (ext_join_params->ssid.SSID_len < IEEE80211_MAX_SSID_LEN) { + ANDROID_INFO(("ssid \"%s\", len (%d)\n", ext_join_params->ssid.SSID, + ext_join_params->ssid.SSID_len)); + } + + err = wl_ext_iovar_setbuf_bsscfg(cur_if->dev, "join", ext_join_params, join_params_size, + iovar_buf, WLC_IOCTL_SMLEN, cur_if->bssidx, NULL); + + printf("Connecting with " MACDBG " channel (%d) ssid \"%s\", len (%d)\n\n", + MAC2STRDBG((u8*)(&ext_join_params->assoc.bssid)), cur_if->channel, + ext_join_params->ssid.SSID, ext_join_params->ssid.SSID_len); + + kfree(ext_join_params); + if (err) { + if (err == BCME_UNSUPPORTED) { + ANDROID_TRACE(("join iovar is not supported\n")); + goto set_ssid; + } else { + ANDROID_ERROR(("error (%d)\n", err)); + goto exit; + } + } else + goto exit; + +set_ssid: + memset(&join_params, 0, sizeof(join_params)); + join_params_size = sizeof(join_params.ssid); + + join_params.ssid.SSID_len = min(sizeof(join_params.ssid.SSID), strlen(cur_if->ssid)); + memcpy(&join_params.ssid.SSID, cur_if->ssid, join_params.ssid.SSID_len); + join_params.ssid.SSID_len = htod32(join_params.ssid.SSID_len); + if (memcmp(ðer_null, &cur_if->bssid, ETHER_ADDR_LEN)) + memcpy(&join_params.params.bssid, &cur_if->bssid, ETH_ALEN); + else + memcpy(&join_params.params.bssid, ðer_bcast, ETH_ALEN); + + wl_ext_ch_to_chanspec(cur_if->channel, &join_params, &join_params_size); + ANDROID_TRACE(("join_param_size %zu\n", join_params_size)); + + if (join_params.ssid.SSID_len < IEEE80211_MAX_SSID_LEN) { + ANDROID_INFO(("ssid \"%s\", len (%d)\n", join_params.ssid.SSID, + join_params.ssid.SSID_len)); } + err = wl_ext_ioctl(cur_if->dev, WLC_SET_SSID, &join_params,join_params_size, 1); + if (err) { + ANDROID_ERROR(("error (%d)\n", err)); + } + +exit: + return err; + +} + +static void +wl_ext_iapsta_preinit(struct net_device *dev, struct wl_apsta_params *apsta_params) +{ + struct dhd_pub *dhd; + wl_interface_create_t iface; + struct wl_if_info *cur_if; + wlc_ssid_t ssid = { 0, {0} }; + s8 iovar_buf[WLC_IOCTL_SMLEN]; + wl_country_t cspec = {{0}, 0, {0}}; + wl_p2p_if_t ifreq; + s32 val = 0; + int i, dfs = 1; - apsta_params->pif.dev = dev; - apsta_params->pif.bssidx = 0; - strcpy(apsta_params->pif.ifname, dev->name); - strcpy(apsta_params->pif.ssid, "tttp"); - apsta_params->pif.maxassoc = -1; - apsta_params->pif.channel = 1; + dhd = dhd_get_pub(dev); + + if (!strlen(apsta_params->if_info[IF_VIF].ifname)) + strcpy(apsta_params->if_info[IF_VIF].ifname, "wlan1"); + if (!strlen(apsta_params->if_info[IF_VIF2].ifname)) + strcpy(apsta_params->if_info[IF_VIF2].ifname, "wlan2"); - if (!strlen(apsta_params->vif.ifname)) - strcpy(apsta_params->vif.ifname, "wlan1"); - strcpy(apsta_params->vif.ssid, "tttv"); - apsta_params->vif.maxassoc = -1; - apsta_params->vif.channel = 1; + for (i=0; iif_info[i]; + if (cur_if->ifmode == ISTA_MODE) { + cur_if->channel = 0; + cur_if->maxassoc = -1; + cur_if->ifstate = IF_STATE_INIT; + snprintf(cur_if->ssid, DOT11_MAX_SSID_LEN, "ttt%d_sta", i); + } else if (cur_if->ifmode == IAP_MODE) { + cur_if->channel = 1; + cur_if->maxassoc = -1; + cur_if->ifstate = IF_STATE_INIT; + snprintf(cur_if->ssid, DOT11_MAX_SSID_LEN, "ttt%d_ap", i); + dfs = 0; + } else if (cur_if->ifmode == IMESH_MODE) { + cur_if->channel = 1; + cur_if->maxassoc = -1; + cur_if->ifstate = IF_STATE_INIT; + snprintf(cur_if->ssid, DOT11_MAX_SSID_LEN, "ttt%d_mesh", i); + dfs = 0; + } + } + if (dfs == 0) { + dhd_conf_get_country(dhd, &cspec); + if (!dhd_conf_map_country_list(dhd, &cspec, 1)) { + dhd_conf_set_country(dhd, &cspec); + dhd_bus_country_set(dev, &cspec, TRUE); + } + } if (apsta_params->apstamode == ISTAONLY_MODE) { - apsta_params->pif.ifmode = ISTA_MODE; - apsta_params->pif.ifstate = IF_STATE_INIT; wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1); wl_ext_iovar_setint(dev, "apsta", 1); // keep 1 as we set in dhd_preinit_ioctls // don't set WLC_SET_AP to 0, some parameters will be reset, such as bcn_timeout and roam_off wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1); } else if (apsta_params->apstamode == IAPONLY_MODE) { - apsta_params->pif.ifmode = IAP_MODE; - apsta_params->pif.ifstate = IF_STATE_INIT; - dhd_conf_get_country(dhd, &cspec); - if (!dhd_conf_map_country_list(dhd, &cspec, 1)) { - dhd_conf_set_country(dhd, &cspec); - dhd_bus_country_set(dev, &cspec, TRUE); - } wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1); #ifdef ARP_OFFLOAD_SUPPORT /* IF SoftAP is enabled, disable arpoe */ @@ -869,18 +1071,22 @@ wl_ext_iapsta_init(struct net_device *dev, char *command, int total_len) wl_ext_iovar_setint(dev, "apsta", 0); val = 1; wl_ext_ioctl(dev, WLC_SET_AP, &val, sizeof(val), 1); - } else if (apsta_params->apstamode == IAPSTA_MODE) { - apsta_params->pif.ifmode = ISTA_MODE; - apsta_params->pif.ifstate = IF_STATE_INIT; - apsta_params->vif.ifmode = IAP_MODE; - apsta_params->vif.ifstate = IF_STATE_INIT; - dhd_conf_get_country(dhd, &cspec); - if (!dhd_conf_map_country_list(dhd, &cspec, 1)) { - dhd_conf_set_country(dhd, &cspec); - dhd_bus_country_set(dev, &cspec, TRUE); +#ifdef PROP_TXSTATUS_VSDB +#if defined(BCMSDIO) + if (!FW_SUPPORTED(dhd, rsdb) && !disable_proptx) { + bool enabled; + dhd_wlfc_get_enable(dhd, &enabled); + if (!enabled) { + dhd_wlfc_init(dhd); + wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1); + } } - wl_ext_iovar_setint(dev, "mpc", 0); +#endif +#endif /* PROP_TXSTATUS_VSDB */ + } + else if (apsta_params->apstamode == IAPSTA_MODE) { wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1); + wl_ext_iovar_setint(dev, "mpc", 0); wl_ext_iovar_setint(dev, "apsta", 1); wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1); if (FW_SUPPORTED(dhd, rsdb)) { @@ -893,96 +1099,271 @@ wl_ext_iapsta_init(struct net_device *dev, char *command, int total_len) wl_ext_iovar_setbuf_bsscfg(dev, "ssid", &ssid, sizeof(ssid), iovar_buf, WLC_IOCTL_SMLEN, 1, NULL); } + apsta_params->netif_change = FALSE; + wait_event_interruptible_timeout(apsta_params->netif_change_event, + apsta_params->netif_change, msecs_to_jiffies(1500)); } else if (apsta_params->apstamode == IDUALAP_MODE) { - apsta_params->pif.ifmode = IAP_MODE; - apsta_params->pif.ifstate = IF_STATE_INIT; - apsta_params->vif.ifmode = IAP_MODE; - apsta_params->vif.ifstate = IF_STATE_INIT; - dhd_conf_get_country(dhd, &cspec); - if (!dhd_conf_map_country_list(dhd, &cspec, 1)) { - dhd_conf_set_country(dhd, &cspec); - dhd_bus_country_set(dev, &cspec, TRUE); - } wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1); - wl_ext_iovar_setint(dev, "apsta", 0); - wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1); - val = 1; - wl_ext_ioctl(dev, WLC_SET_AP, &val, sizeof(val), 1); /* IF SoftAP is enabled, disable arpoe or wlan1 will ping fail */ #ifdef ARP_OFFLOAD_SUPPORT /* IF SoftAP is enabled, disable arpoe */ dhd_arp_offload_set(dhd, 0); dhd_arp_offload_enable(dhd, FALSE); #endif /* ARP_OFFLOAD_SUPPORT */ + wl_ext_iovar_setint(dev, "mpc", 0); + wl_ext_iovar_setint(dev, "apsta", 0); + wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1); + val = 1; + wl_ext_ioctl(dev, WLC_SET_AP, &val, sizeof(val), 1); bzero(&iface, sizeof(wl_interface_create_t)); iface.ver = WL_INTERFACE_CREATE_VER; iface.flags = WL_INTERFACE_CREATE_AP; wl_ext_iovar_getbuf_bsscfg(dev, "interface_create", &iface, sizeof(iface), iovar_buf, WLC_IOCTL_SMLEN, 1, NULL); + apsta_params->netif_change = FALSE; + wait_event_interruptible_timeout(apsta_params->netif_change_event, + apsta_params->netif_change, msecs_to_jiffies(1500)); } - else if (apsta_params->apstamode == IGOSTA_MODE) { - apsta_params->pif.ifmode = ISTA_MODE; - apsta_params->pif.ifstate = IF_STATE_INIT; - apsta_params->vif.ifmode = IAP_MODE; - apsta_params->vif.ifstate = IF_STATE_INIT; + else if (apsta_params->apstamode == IMESHONLY_MODE) { + wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1); + wl_ext_iovar_setint(dev, "mpc", 0); + wl_ext_iovar_setint(dev, "apsta", 1); // keep 1 as we set in dhd_preinit_ioctls + // don't set WLC_SET_AP to 0, some parameters will be reset, such as bcn_timeout and roam_off + } + else if (apsta_params->apstamode == IMESHSTA_MODE) { wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1); + wl_ext_iovar_setint(dev, "mpc", 0); wl_ext_iovar_setint(dev, "apsta", 1); wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1); - bzero(&ifreq, sizeof(wl_p2p_if_t)); - ifreq.type = htod32(WL_P2P_IF_GO); - wl_ext_iovar_setbuf(dev, "p2p_ifadd", &ifreq, sizeof(ifreq), - iovar_buf, WLC_IOCTL_SMLEN, NULL); + bzero(&iface, sizeof(wl_interface_create_t)); + iface.ver = WL_INTERFACE_CREATE_VER; + iface.flags = WL_INTERFACE_CREATE_STA; + wl_ext_iovar_getbuf_bsscfg(dev, "interface_create", &iface, sizeof(iface), + iovar_buf, WLC_IOCTL_SMLEN, 0, NULL); + apsta_params->netif_change = FALSE; + wait_event_interruptible_timeout(apsta_params->netif_change_event, + apsta_params->netif_change, msecs_to_jiffies(1500)); + } + else if (apsta_params->apstamode == IMESHAP_MODE) { + wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1); + wl_ext_iovar_setint(dev, "mpc", 0); + wl_ext_iovar_setint(dev, "apsta", 1); // keep 1 as we set in dhd_preinit_ioctls + // don't set WLC_SET_AP to 0, some parameters will be reset, such as bcn_timeout and roam_off + bzero(&iface, sizeof(wl_interface_create_t)); + iface.ver = WL_INTERFACE_CREATE_VER; + iface.flags = WL_INTERFACE_CREATE_AP; + wl_ext_iovar_getbuf_bsscfg(dev, "interface_create", &iface, sizeof(iface), + iovar_buf, WLC_IOCTL_SMLEN, 0, NULL); + apsta_params->netif_change = FALSE; + wait_event_interruptible_timeout(apsta_params->netif_change_event, + apsta_params->netif_change, msecs_to_jiffies(1500)); + } + else if (apsta_params->apstamode == IMESHAPSTA_MODE) { + wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1); + wl_ext_iovar_setint(dev, "mpc", 0); + wl_ext_iovar_setint(dev, "apsta", 1); // keep 1 as we set in dhd_preinit_ioctls + // don't set WLC_SET_AP to 0, some parameters will be reset, such as bcn_timeout and roam_off + bzero(&iface, sizeof(wl_interface_create_t)); + iface.ver = WL_INTERFACE_CREATE_VER; + iface.flags = WL_INTERFACE_CREATE_AP; + wl_ext_iovar_getbuf_bsscfg(dev, "interface_create", &iface, sizeof(iface), + iovar_buf, WLC_IOCTL_SMLEN, 0, NULL); + apsta_params->netif_change = FALSE; + wait_event_interruptible_timeout(apsta_params->netif_change_event, + apsta_params->netif_change, msecs_to_jiffies(1500)); + bzero(&iface, sizeof(wl_interface_create_t)); + iface.ver = WL_INTERFACE_CREATE_VER; + iface.flags = WL_INTERFACE_CREATE_STA; + wl_ext_iovar_getbuf_bsscfg(dev, "interface_create", &iface, sizeof(iface), + iovar_buf, WLC_IOCTL_SMLEN, 0, NULL); + apsta_params->netif_change = FALSE; + wait_event_interruptible_timeout(apsta_params->netif_change_event, + apsta_params->netif_change, msecs_to_jiffies(1500)); + } + else if (apsta_params->apstamode == IMESHAPAP_MODE) { + wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1); + wl_ext_iovar_setint(dev, "mpc", 0); + wl_ext_iovar_setint(dev, "apsta", 1); // keep 1 as we set in dhd_preinit_ioctls + // don't set WLC_SET_AP to 0, some parameters will be reset, such as bcn_timeout and roam_off + bzero(&iface, sizeof(wl_interface_create_t)); + iface.ver = WL_INTERFACE_CREATE_VER; + iface.flags = WL_INTERFACE_CREATE_AP; + wl_ext_iovar_getbuf_bsscfg(dev, "interface_create", &iface, sizeof(iface), + iovar_buf, WLC_IOCTL_SMLEN, 0, NULL); + apsta_params->netif_change = FALSE; + wait_event_interruptible_timeout(apsta_params->netif_change_event, + apsta_params->netif_change, msecs_to_jiffies(1500)); + bzero(&iface, sizeof(wl_interface_create_t)); + iface.ver = WL_INTERFACE_CREATE_VER; + iface.flags = WL_INTERFACE_CREATE_AP; + wl_ext_iovar_getbuf_bsscfg(dev, "interface_create", &iface, sizeof(iface), + iovar_buf, WLC_IOCTL_SMLEN, 0, NULL); + apsta_params->netif_change = FALSE; + wait_event_interruptible_timeout(apsta_params->netif_change_event, + apsta_params->netif_change, msecs_to_jiffies(1500)); } - else if (apsta_params->apstamode == IGCSTA_MODE) { - apsta_params->pif.ifmode = ISTA_MODE; - apsta_params->pif.ifstate = IF_STATE_INIT; - apsta_params->vif.ifmode = ISTA_MODE; - apsta_params->vif.ifstate = IF_STATE_INIT; + else if (apsta_params->apstamode == IGOSTA_MODE) { wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1); wl_ext_iovar_setint(dev, "apsta", 1); wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1); bzero(&ifreq, sizeof(wl_p2p_if_t)); - ifreq.type = htod32(WL_P2P_IF_CLIENT); + ifreq.type = htod32(WL_P2P_IF_GO); wl_ext_iovar_setbuf(dev, "p2p_ifadd", &ifreq, sizeof(ifreq), iovar_buf, WLC_IOCTL_SMLEN, NULL); + apsta_params->netif_change = FALSE; + wait_event_interruptible_timeout(apsta_params->netif_change_event, + apsta_params->netif_change, msecs_to_jiffies(1500)); } wl_ext_get_ioctl_ver(dev, &apsta_params->ioctl_ver); - printf("%s: apstamode=%d\n", __FUNCTION__, apsta_params->apstamode); - apsta_params->init = TRUE; - - return 0; + printf("%s: apstamode=%d\n", __FUNCTION__, apsta_params->apstamode); } static int -wl_ext_parse_config(struct wl_if_info *cur_if, char *command, char **pick_next) +wl_ext_isam_init(struct net_device *dev, char *command, int total_len) { - char *pch, *pick_tmp; - char name[20], data[100]; - int i, j; - char *ifname_head = NULL; + char *pch, *pick_tmp, *pick_tmp2, *param; + struct wl_apsta_params *apsta_params = &g_apsta_params; + struct dhd_pub *dhd; + int i; - typedef struct config_map_t { - char name[20]; - char *head; - char *tail; - } config_map_t; - - config_map_t config_map [] = { - {" ifname ", NULL, NULL}, - {" ssid ", NULL, NULL}, - {" bssid ", NULL, NULL}, - {" bgnmode ", NULL, NULL}, - {" hidden ", NULL, NULL}, - {" maxassoc ", NULL, NULL}, - {" chan ", NULL, NULL}, - {" amode ", NULL, NULL}, - {" emode ", NULL, NULL}, - {" key ", NULL, NULL}, - }; - config_map_t *row, *row_prev; + if (apsta_params->init) { + ANDROID_ERROR(("%s: don't init twice\n", __FUNCTION__)); + return -1; + } + + dhd = dhd_get_pub(dev); + + ANDROID_TRACE(("%s: command=%s, len=%d\n", __FUNCTION__, command, total_len)); + + pick_tmp = command; + param = bcmstrtok(&pick_tmp, " ", 0); // skip iapsta_init + param = bcmstrtok(&pick_tmp, " ", 0); + while (param != NULL) { + if (!strcmp(param, "mode")) { + pch = NULL; + pick_tmp2 = bcmstrtok(&pick_tmp, " ", 0); + if (pick_tmp2) { + if (!strcmp(pick_tmp2, "sta")) { + apsta_params->apstamode = ISTAONLY_MODE; + } else if (!strcmp(pick_tmp2, "ap")) { + apsta_params->apstamode = IAPONLY_MODE; + } else if (!strcmp(pick_tmp2, "sta-ap")) { + apsta_params->apstamode = IAPSTA_MODE; + } else if (!strcmp(pick_tmp2, "ap-ap")) { + apsta_params->apstamode = IDUALAP_MODE; + } else if (!strcmp(pick_tmp2, "mesh")) { + apsta_params->apstamode = IMESHONLY_MODE; + } else if (!strcmp(pick_tmp2, "mesh-sta")) { + apsta_params->apstamode = IMESHSTA_MODE; + } else if (!strcmp(pick_tmp2, "mesh-ap")) { + apsta_params->apstamode = IMESHAP_MODE; + } else if (!strcmp(pick_tmp2, "mesh-ap-sta")) { + apsta_params->apstamode = IMESHAPSTA_MODE; + } else if (!strcmp(pick_tmp2, "mesh-ap-ap")) { + apsta_params->apstamode = IMESHAPAP_MODE; + } else if (!strcmp(pick_tmp2, "apsta")) { + apsta_params->apstamode = IAPSTA_MODE; + apsta_params->if_info[IF_PIF].ifmode = ISTA_MODE; + apsta_params->if_info[IF_VIF].ifmode = IAP_MODE; + } else if (!strcmp(pick_tmp2, "dualap")) { + apsta_params->apstamode = IDUALAP_MODE; + apsta_params->if_info[IF_PIF].ifmode = IAP_MODE; + apsta_params->if_info[IF_VIF].ifmode = IAP_MODE; + } else if (!strcmp(pick_tmp2, "gosta")) { + if (!FW_SUPPORTED(dhd, p2p)) { + return -1; + } + apsta_params->apstamode = IGOSTA_MODE; + apsta_params->if_info[IF_PIF].ifmode = ISTA_MODE; + apsta_params->if_info[IF_VIF].ifmode = IAP_MODE; + } else { + ANDROID_ERROR(("%s: mode [sta|ap|sta-ap|ap-ap]\n", __FUNCTION__)); + return -1; + } + pch = bcmstrtok(&pick_tmp2, " -", 0); + for (i=0; iif_info[i].ifmode = ISTA_MODE; + else if (!strcmp(pch, "ap")) + apsta_params->if_info[i].ifmode = IAP_MODE; + else if (!strcmp(pch, "mesh")) + apsta_params->if_info[i].ifmode = IMESH_MODE; + pch = bcmstrtok(&pick_tmp2, " -", 0); + } + } + } else if (!strcmp(param, "vsdb")) { + pch = bcmstrtok(&pick_tmp, " ", 0); + if (pch) { + if (!strcmp(pch, "y")) { + apsta_params->vsdb = TRUE; + } else if (!strcmp(pch, "n")) { + apsta_params->vsdb = FALSE; + } else { + ANDROID_ERROR(("%s: vsdb [y|n]\n", __FUNCTION__)); + return -1; + } + } + } else if (!strcmp(param, "ifname")) { + pch = NULL; + pick_tmp2 = bcmstrtok(&pick_tmp, " ", 0); + if (pick_tmp2) + pch = bcmstrtok(&pick_tmp2, " -", 0); + for (i=0; iif_info[i].ifname, pch); + pch = bcmstrtok(&pick_tmp2, " -", 0); + } + } else if (!strcmp(param, "vifname")) { + pch = bcmstrtok(&pick_tmp, " ", 0); + if (pch) + strcpy(apsta_params->if_info[IF_VIF].ifname, pch); + else { + ANDROID_ERROR(("%s: vifname [wlan1]\n", __FUNCTION__)); + return -1; + } + } + param = bcmstrtok(&pick_tmp, " ", 0); + } + + if (apsta_params->apstamode == 0) { + ANDROID_ERROR(("%s: mode [sta|ap|sta-ap|ap-ap]\n", __FUNCTION__)); + return -1; + } + + wl_ext_iapsta_preinit(dev, apsta_params); + + return 0; +} + +static int +wl_ext_parse_config(struct wl_if_info *cur_if, char *command, char **pick_next) +{ + char *pch, *pick_tmp; + char name[20], data[100]; + int i, j; + char *ifname_head = NULL; + + typedef struct config_map_t { + char name[20]; + char *head; + char *tail; + } config_map_t; + + config_map_t config_map [] = { + {" ifname ", NULL, NULL}, + {" ssid ", NULL, NULL}, + {" bssid ", NULL, NULL}, + {" bgnmode ", NULL, NULL}, + {" hidden ", NULL, NULL}, + {" maxassoc ", NULL, NULL}, + {" chan ", NULL, NULL}, + {" amode ", NULL, NULL}, + {" emode ", NULL, NULL}, + {" key ", NULL, NULL}, + }; + config_map_t *row, *row_prev; pick_tmp = command; @@ -990,7 +1371,7 @@ wl_ext_parse_config(struct wl_if_info *cur_if, char *command, char **pick_next) for (i = 0; i < sizeof(config_map)/sizeof(config_map[0]); i++) { row = &config_map[i]; row->head = NULL; - row->tail = pick_tmp + strlen(pick_tmp);; + row->tail = pick_tmp + strlen(pick_tmp); } // pick head @@ -1050,7 +1431,9 @@ wl_ext_parse_config(struct wl_if_info *cur_if, char *command, char **pick_next) } pick_tmp = data; - if (!strcmp(row->name, " ssid ")) { + if (!strcmp(row->name, " ifname ")) { + break; + } else if (!strcmp(row->name, " ssid ")) { strcpy(cur_if->ssid, pick_tmp); } else if (!strcmp(row->name, " bssid ")) { pch = bcmstrtok(&pick_tmp, ": ", 0); @@ -1135,7 +1518,7 @@ wl_ext_iapsta_config(struct net_device *dev, char *command, int total_len) char *pch, *pch2, *pick_tmp, *pick_next=NULL, *param; struct wl_apsta_params *apsta_params = &g_apsta_params; char ifname[IFNAMSIZ+1]; - struct wl_if_info *cur_if = &apsta_params->pif; + struct wl_if_info *cur_if = &apsta_params->if_info[IF_PIF]; if (!apsta_params->init) { ANDROID_ERROR(("%s: please init first\n", __FUNCTION__)); @@ -1158,10 +1541,12 @@ wl_ext_iapsta_config(struct net_device *dev, char *command, int total_len) ANDROID_ERROR(("%s: ifname [wlanX]\n", __FUNCTION__)); return -1; } - if (!strcmp(apsta_params->pif.dev->name, ifname)) { - cur_if = &apsta_params->pif; - } else if (!strcmp(apsta_params->vif.ifname, ifname)) { - cur_if = &apsta_params->vif; + if (!strcmp(apsta_params->if_info[IF_PIF].dev->name, ifname)) { + cur_if = &apsta_params->if_info[IF_PIF]; + } else if (!strcmp(apsta_params->if_info[IF_VIF].ifname, ifname)) { + cur_if = &apsta_params->if_info[IF_VIF]; + } else if (!strcmp(apsta_params->if_info[IF_VIF2].ifname, ifname)) { + cur_if = &apsta_params->if_info[IF_VIF2]; } else { ANDROID_ERROR(("%s: wrong ifname=%s in apstamode=%d\n", __FUNCTION__, ifname, apsta_params->apstamode)); @@ -1196,8 +1581,9 @@ wl_ext_iapsta_disable(struct net_device *dev, char *command, int total_len) struct wl_apsta_params *apsta_params = &g_apsta_params; apstamode_t apstamode = apsta_params->apstamode; char ifname[IFNAMSIZ+1]; - struct wl_if_info *cur_if; + struct wl_if_info *cur_if = NULL; struct dhd_pub *dhd; + int i; if (!apsta_params->init) { ANDROID_ERROR(("%s: please init first\n", __FUNCTION__)); @@ -1222,34 +1608,34 @@ wl_ext_iapsta_disable(struct net_device *dev, char *command, int total_len) } param = bcmstrtok(&pick_tmp, " ", 0); } - if (!strcmp(apsta_params->pif.dev->name, ifname)) { - cur_if = &apsta_params->pif; - } else if (!strcmp(apsta_params->vif.ifname, ifname)) { - cur_if = &apsta_params->vif; - } else { - ANDROID_ERROR(("%s: wrong ifname=%s\n", __FUNCTION__, ifname)); - return -1; + + for (i=0; iif_info[i].dev && + !strcmp(apsta_params->if_info[i].dev->name, ifname)) { + cur_if = &apsta_params->if_info[i]; + break; + } } - if (!cur_if->dev) { - ANDROID_ERROR(("%s: %s is not ready\n", __FUNCTION__, ifname)); + if (!cur_if) { + ANDROID_ERROR(("%s: wrong ifname=%s or dev not ready\n", __FUNCTION__, ifname)); return -1; } if (cur_if->ifmode == ISTA_MODE) { wl_ext_ioctl(cur_if->dev, WLC_DISASSOC, NULL, 0, 1); - } else if (cur_if->ifmode == IAP_MODE) { + } else if (cur_if->ifmode == IAP_MODE || cur_if->ifmode == IMESH_MODE) { // deauthenticate all STA first memcpy(scbval.ea.octet, ðer_bcast, ETHER_ADDR_LEN); wl_ext_ioctl(cur_if->dev, WLC_SCB_DEAUTHENTICATE, &scbval.ea, ETHER_ADDR_LEN, 1); } - if (apstamode == IAPONLY_MODE) { + if (apstamode == IAPONLY_MODE || apstamode == IMESHONLY_MODE) { wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1); wl_ext_ioctl(dev, WLC_SET_SSID, &ssid, sizeof(ssid), 1); // reset ssid wl_ext_iovar_setint(dev, "mpc", 1); } else if ((apstamode==IAPSTA_MODE || apstamode==IGOSTA_MODE) && cur_if->ifmode == IAP_MODE) { - // vif is AP mode + // if_info[IF_VIF] is AP mode bss_setbuf.tmp = 0xffffffff; bss_setbuf.cfg = 0; // must be 0, or wlan1 can not be down bss_setbuf.val = htod32(0); @@ -1261,102 +1647,245 @@ wl_ext_iapsta_disable(struct net_device *dev, char *command, int total_len) dhd_arp_offload_set(dhd, dhd_arp_mode); dhd_arp_offload_enable(dhd, TRUE); #endif /* ARP_OFFLOAD_SUPPORT */ +#ifdef PROP_TXSTATUS_VSDB +#if defined(BCMSDIO) + if (dhd->conf->disable_proptx!=0) { + bool enabled; + dhd_wlfc_get_enable(dhd, &enabled); + if (enabled) { + dhd_wlfc_deinit(dhd); + } + } +#endif +#endif /* PROP_TXSTATUS_VSDB */ } else if (apstamode == IDUALAP_MODE) { bss_setbuf.tmp = 0xffffffff; bss_setbuf.cfg = 0; // must be 0, or wlan1 can not be down bss_setbuf.val = htod32(0); wl_ext_iovar_setbuf(cur_if->dev, "bss", &bss_setbuf, sizeof(bss_setbuf), iovar_buf, WLC_IOCTL_SMLEN, NULL); + } else if (apstamode == IMESHSTA_MODE || apstamode == IMESHAP_MODE || + apstamode == IMESHAPSTA_MODE || apstamode == IMESHAPAP_MODE) { + bss_setbuf.tmp = 0xffffffff; + bss_setbuf.cfg = 0; // must be 0, or wlan1 can not be down + bss_setbuf.val = htod32(0); + wl_ext_iovar_setbuf(cur_if->dev, "bss", &bss_setbuf, sizeof(bss_setbuf), + iovar_buf, WLC_IOCTL_SMLEN, NULL); } -#ifdef PROP_TXSTATUS_VSDB -#if defined(BCMSDIO) - if (cur_if==&apsta_params->vif && dhd->conf->disable_proptx!=0) { - bool enabled; - dhd_wlfc_get_enable(dhd, &enabled); - if (enabled) { - dhd_wlfc_deinit(dhd); - } + cur_if->ifstate = IF_STATE_DISALBE; + + printf("%s: ifname=%s, mode=%d\n", __FUNCTION__, ifname, cur_if->ifmode); + + return 0; +} + +static bool +wl_ext_iapsta_diff_band(uint16 channel1, uint16 channel2) +{ + ANDROID_TRACE(("%s: cur_chan=%d, channel=%d\n", __FUNCTION__, channel1, channel2)); + if ((channel1 <= CH_MAX_2G_CHANNEL && channel2 > CH_MAX_2G_CHANNEL) || + (channel1 > CH_MAX_2G_CHANNEL && channel2 <= CH_MAX_2G_CHANNEL)) { + return TRUE; + } else { + return FALSE; } -#endif -#endif /* PROP_TXSTATUS_VSDB */ +} - cur_if->ifstate = IF_STATE_DISALBE; - printf("%s: apstamode=%d, ifname=%s\n", __FUNCTION__, apstamode, ifname); +static uint16 +wl_ext_iapsta_is_vsdb(struct net_device *dev, + struct wl_if_info *cur_if, struct wl_if_info *another_if) +{ + struct wl_apsta_params *apsta_params = &g_apsta_params; + int ret = 0, cur_chan = 0; + uint16 another_chan = 0, ctl_chan; + struct dhd_pub *dhd; + struct ether_addr bssid; + u32 chanspec = 0; + + dhd = dhd_get_pub(dev); + + ret = wldev_ioctl(another_if->dev, WLC_GET_BSSID, &bssid, sizeof(bssid), 0); + if (ret != BCME_NOTASSOCIATED && memcmp(ðer_null, &bssid, ETHER_ADDR_LEN)) { + if (wldev_iovar_getint(another_if->dev, "chanspec", (s32 *)&chanspec) == BCME_OK) { + ANDROID_TRACE(("%s: chanspec=0x%x\n", __FUNCTION__, chanspec)); + chanspec = wl_ext_chspec_driver_to_host(apsta_params->ioctl_ver, chanspec); + ctl_chan = wf_chspec_ctlchan(chanspec); + another_chan = (u16)(ctl_chan & 0x00FF); + cur_chan = cur_if->channel; + if (wl_ext_iapsta_diff_band(another_chan, cur_chan)) { + // different band + if (!FW_SUPPORTED(dhd, rsdb)) + return another_chan; + } else { + // same band + if (another_chan != cur_chan) + return another_chan; + } + } + } return 0; } +static void +wl_ext_iapsta_change_channel(struct wl_if_info *cur_if, uint16 chan) +{ + if (chan) { + char cmd[50] = ""; + printf("%s: deauthenticate all STA and move to chan=%d on %s\n", + __FUNCTION__, chan, cur_if->ifname); + snprintf(cmd, 50, "%s %s", "isam_disable ifname", cur_if->ifname); + wl_ext_iapsta_disable(cur_if->dev, cmd, strlen(cmd)); + cur_if->channel = chan; + snprintf(cmd, 50, "%s %s", "isam_enable ifname", cur_if->ifname); + wl_ext_iapsta_enable(cur_if->dev, cmd, strlen(cmd)); + } +} + +static void +wl_ext_iapsta_change_cur_iface_channel(struct net_device *dev, + struct wl_if_info *cur_if) +{ + struct wl_apsta_params *apsta_params = &g_apsta_params; + struct wl_if_info *another_if, *final_if = NULL; + uint16 new_chan = 0; + int i; + + if (cur_if->ifmode == IAP_MODE) { + for (i=MAX_IF_NUM-1; i>=0; i--) { + another_if = &apsta_params->if_info[i]; + if (another_if->ifmode == ISTA_MODE) { + new_chan = wl_ext_iapsta_is_vsdb(dev, cur_if, another_if); + if (new_chan) { + final_if = another_if; + break; + } + } + } + } else if (cur_if->ifmode == IMESH_MODE) { + for (i=MAX_IF_NUM-1; i>=0; i--) { + another_if = &apsta_params->if_info[i]; + if (another_if->ifmode == ISTA_MODE || another_if->ifmode == IAP_MODE) { + new_chan = wl_ext_iapsta_is_vsdb(dev, cur_if, another_if); + if (new_chan) { + final_if = another_if; + break; + } + } + } + } + if (new_chan && !apsta_params->vsdb) { + cur_if->channel = new_chan; + printf("%s: %s ifmode=%d, %s ifmode=%d, channel=%d\n", __FUNCTION__, + cur_if->ifname, cur_if->ifmode, final_if->ifname, final_if->ifmode, + cur_if->channel); + } + +} + +static void +wl_ext_iapsta_change_other_iface_channel(struct net_device *dev, + struct wl_if_info *cur_if) +{ + struct wl_apsta_params *apsta_params = &g_apsta_params; + struct wl_if_info *another_if; + uint16 new_chan = 0; + int i; + + if (cur_if->ifmode == ISTA_MODE) { + for (i=MAX_IF_NUM-1; i>=0; i--) { + another_if = &apsta_params->if_info[i]; + if (another_if->ifmode == IAP_MODE || another_if->ifmode == IMESH_MODE) { + new_chan = wl_ext_iapsta_is_vsdb(dev, cur_if, another_if); + if (new_chan && !apsta_params->vsdb) { + wl_ext_iapsta_change_channel(another_if, cur_if->channel); + } + } + } + } else if (cur_if->ifmode == IAP_MODE) { + for (i=0; iif_info[i]; + if (another_if->ifmode == IMESH_MODE) { + new_chan = wl_ext_iapsta_is_vsdb(dev, cur_if, another_if); + if (new_chan && !apsta_params->vsdb) { + wl_ext_iapsta_change_channel(another_if, cur_if->channel); + } + } + } + } + +} + static int -wl_ext_iapsta_enable(struct net_device *dev, char *command, int total_len) +wl_ext_iapsta_enable_iface(struct net_device *dev, char *ifname) { - int ret = 0; - s32 val = 0; - char *pch, *pick_tmp, *param; + int ret = 0, i; + char *pick_tmp, *param; s8 iovar_buf[WLC_IOCTL_SMLEN]; wlc_ssid_t ssid = { 0, {0} }; + chanspec_t fw_chspec; + struct wl_join_params join_params; + size_t join_params_size; struct { s32 cfg; s32 val; } bss_setbuf; struct wl_apsta_params *apsta_params = &g_apsta_params; apstamode_t apstamode = apsta_params->apstamode; - char ifname[IFNAMSIZ+1]; - struct wl_if_info *cur_if; - char cmd[128] = "iapsta_stop ifname "; + struct wl_if_info *cur_if = NULL; + char cmd[128]; struct dhd_pub *dhd; + struct ether_addr bssid; + uint16 cur_chan; - if (!apsta_params->init) { - ANDROID_ERROR(("%s: please init first\n", __FUNCTION__)); - return -1; - } - - ANDROID_TRACE(("%s: command=%s, len=%d\n", __FUNCTION__, command, total_len)); dhd = dhd_get_pub(dev); - pick_tmp = command; - param = bcmstrtok(&pick_tmp, " ", 0); // skip iapsta_enable - param = bcmstrtok(&pick_tmp, " ", 0); - while (param != NULL) { - if (!strcmp(param, "ifname")) { - pch = bcmstrtok(&pick_tmp, " ", 0); - if (pch) - strcpy(ifname, pch); - else { - ANDROID_ERROR(("%s: ifname [wlanX]\n", __FUNCTION__)); - return -1; - } + for (i=0; iif_info[i].dev && + !strcmp(apsta_params->if_info[i].dev->name, ifname)) { + cur_if = &apsta_params->if_info[i]; + break; } - param = bcmstrtok(&pick_tmp, " ", 0); } - if (!strcmp(apsta_params->pif.dev->name, ifname)) { - cur_if = &apsta_params->pif; - } else if (!strcmp(apsta_params->vif.ifname, ifname)) { - cur_if = &apsta_params->vif; - } else { - ANDROID_ERROR(("%s: wrong ifname=%s\n", __FUNCTION__, ifname)); + if (!cur_if) { + ANDROID_ERROR(("%s: wrong ifname=%s or dev not ready\n", __FUNCTION__, ifname)); return -1; } - if (!cur_if->dev) { - ANDROID_ERROR(("%s: %s is not ready\n", __FUNCTION__, ifname)); - return -1; + + wl_ext_iapsta_change_cur_iface_channel(dev, cur_if); + + if ((apstamode == IMESHSTA_MODE || apstamode == IMESHAP_MODE || + apstamode == IMESHAPSTA_MODE || apstamode == IMESHAPAP_MODE) && + cur_if == &apsta_params->if_info[IF_PIF] && + cur_if->ifstate == IF_STATE_INIT && + FW_SUPPORTED(dhd, rsdb)) { + wl_config_t rsdb_mode_cfg = {1, 0}; + // mesh-ap must set rsdb_mode=1 in same channel or AP mode not easy to be found + printf("%s: set rsdb_mode %d\n", __FUNCTION__, rsdb_mode_cfg.config); + wl_ext_iovar_setbuf(dev, "rsdb_mode", &rsdb_mode_cfg, + sizeof(rsdb_mode_cfg), iovar_buf, sizeof(iovar_buf), NULL); + } + + ret = wldev_ioctl(cur_if->dev, WLC_GET_BSSID, &bssid, sizeof(bssid), 0); + if (ret != BCME_NOTASSOCIATED && memcmp(ðer_null, &bssid, ETHER_ADDR_LEN)) { + ANDROID_INFO(("%s: Associated! ret %d\n", __FUNCTION__, ret)); + return 0; } + ssid.SSID_len = strlen(cur_if->ssid); memcpy(ssid.SSID, cur_if->ssid, ssid.SSID_len); ANDROID_TRACE(("%s: apstamode=%d, bssidx=%d\n", __FUNCTION__, apstamode, cur_if->bssidx)); - snprintf(cmd, 128, "iapsta_stop ifname %s", cur_if->ifname); - ret = wl_ext_iapsta_disable(dev, cmd, strlen(cmd)); - if (ret) - goto exit; + wl_ext_iapsta_change_other_iface_channel(dev, cur_if); - if (cur_if == &apsta_params->vif) { + if (cur_if == &apsta_params->if_info[IF_VIF] || cur_if == &apsta_params->if_info[IF_VIF2]) { wl_ext_iovar_setbuf(cur_if->dev, "cur_etheraddr", (u8 *)cur_if->dev->dev_addr, ETHER_ADDR_LEN, iovar_buf, WLC_IOCTL_SMLEN, NULL); } // set ssid for AP - if (cur_if->ifmode == IAP_MODE) { + if (cur_if->ifmode == IAP_MODE || cur_if->ifmode == IMESH_MODE) { wl_ext_iovar_setint(dev, "mpc", 0); if (apstamode == IAPONLY_MODE) { wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1); @@ -1366,37 +1895,50 @@ wl_ext_iapsta_enable(struct net_device *dev, char *command, int total_len) } } - if (cur_if->ifmode == IAP_MODE) { + if (cur_if->ifmode == IAP_MODE || cur_if->ifmode == IMESH_MODE) { wl_ext_set_bgnmode(cur_if); - wl_ext_set_chanspec(cur_if->dev, cur_if->channel); + cur_chan = cur_if->channel; + if (!cur_chan) { + cur_chan = 1; +#ifdef WL_CFG80211 + snprintf(cmd, 128, "get_best_channels"); + wl_cfg80211_get_best_channels(dev, cmd, strlen(cmd)); + pick_tmp = cmd; + param = bcmstrtok(&pick_tmp, " ", 0); + while (param != NULL) { + if (!strnicmp(param, "2g=", strlen("2g="))) { + cur_chan = (int)simple_strtol(param+strlen("2g="), NULL, 10); + } else if (!strnicmp(param, "5g=", strlen("5g="))) { + cur_chan = (int)simple_strtol(param+strlen("5g="), NULL, 10); + } + param = bcmstrtok(&pick_tmp, " ", 0); + } +#endif + } + wl_ext_set_chanspec(cur_if->dev, cur_chan, &fw_chspec); } + wl_ext_set_amode(cur_if, apsta_params); wl_ext_set_emode(cur_if, apsta_params); - if (apstamode == ISTAONLY_MODE || apstamode == IGCSTA_MODE) { - if (!ETHER_ISBCAST(&cur_if->bssid) && !ETHER_ISNULLADDR(&cur_if->bssid)) { - printf("%s: BSSID: %pM\n", __FUNCTION__, &cur_if->bssid); - wl_ext_ioctl(cur_if->dev, WLC_SET_BSSID, &cur_if->bssid, ETHER_ADDR_LEN, 1); - } - val = 1; - wl_ext_ioctl(dev, WLC_SET_INFRA, &val, sizeof(val), 1); - } if (cur_if->ifmode == IAP_MODE) { if (cur_if->maxassoc >= 0) wl_ext_iovar_setint(dev, "maxassoc", cur_if->maxassoc); - printf("%s: Broadcast SSID: %s\n", __FUNCTION__, cur_if->hidden ? "OFF":"ON"); // terence: fix me, hidden does not work in dualAP mode - wl_ext_ioctl(cur_if->dev, WLC_SET_CLOSED, &cur_if->hidden, sizeof(cur_if->hidden), 1); + if (cur_if->hidden > 0) { + wl_ext_ioctl(cur_if->dev, WLC_SET_CLOSED, &cur_if->hidden, sizeof(cur_if->hidden), 1); + printf("%s: Broadcast SSID: %s\n", __FUNCTION__, cur_if->hidden ? "OFF":"ON"); + } } - if (apstamode == ISTAONLY_MODE || apstamode == IGCSTA_MODE) { - wl_ext_ioctl(cur_if->dev, WLC_SET_SSID, &ssid, sizeof(ssid), 1); + if (apstamode == ISTAONLY_MODE) { + wl_ext_connect(cur_if); } else if (apstamode == IAPONLY_MODE) { wl_ext_ioctl(cur_if->dev, WLC_SET_SSID, &ssid, sizeof(ssid), 1); wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1); } else if (apstamode == IAPSTA_MODE || apstamode == IGOSTA_MODE) { if (cur_if->ifmode == ISTA_MODE) { - wl_ext_ioctl(cur_if->dev, WLC_SET_SSID, &ssid, sizeof(ssid), 1); + wl_ext_connect(cur_if); } else { if (FW_SUPPORTED(dhd, rsdb)) { wl_ext_ioctl(cur_if->dev, WLC_SET_SSID, &ssid, sizeof(ssid), 1); @@ -1411,79 +1953,366 @@ wl_ext_iapsta_enable(struct net_device *dev, char *command, int total_len) dhd_arp_offload_set(dhd, 0); dhd_arp_offload_enable(dhd, FALSE); #endif /* ARP_OFFLOAD_SUPPORT */ +#ifdef PROP_TXSTATUS_VSDB +#if defined(BCMSDIO) + if (!FW_SUPPORTED(dhd, rsdb) && !disable_proptx) { + bool enabled; + dhd_wlfc_get_enable(dhd, &enabled); + if (!enabled) { + dhd_wlfc_init(dhd); + wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1); + } + } +#endif +#endif /* PROP_TXSTATUS_VSDB */ } } else if (apstamode == IDUALAP_MODE) { wl_ext_ioctl(cur_if->dev, WLC_SET_SSID, &ssid, sizeof(ssid), 1); - } - -#ifdef PROP_TXSTATUS_VSDB -#if defined(BCMSDIO) - if (cur_if==&apsta_params->vif && !disable_proptx) { - bool enabled; - dhd_wlfc_get_enable(dhd, &enabled); - if (!enabled) { - dhd_wlfc_init(dhd); + } else if (apstamode == IMESHONLY_MODE || + apstamode == IMESHSTA_MODE || apstamode == IMESHAP_MODE || + apstamode == IMESHAPSTA_MODE || apstamode == IMESHAPAP_MODE) { + if (cur_if->ifmode == ISTA_MODE) { + wl_ext_connect(cur_if); + } else if (cur_if->ifmode == IAP_MODE) { + wl_ext_ioctl(cur_if->dev, WLC_SET_SSID, &ssid, sizeof(ssid), 1); + } else if (cur_if->ifmode == IMESH_MODE) { + // need to up before setting ssid wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1); + memset(&join_params, 0, sizeof(join_params)); + join_params.ssid.SSID_len = strlen(cur_if->ssid); + memcpy((void *)join_params.ssid.SSID, cur_if->ssid, ssid.SSID_len); + join_params.params.chanspec_list[0] = fw_chspec; + join_params.params.chanspec_num = 1; + join_params_size = sizeof(join_params); + wl_ext_ioctl(cur_if->dev, WLC_SET_SSID, &join_params, join_params_size, 1); + } else { + printf("%s: wrong ifmode %d\n", __FUNCTION__, cur_if->ifmode); } } -#endif -#endif /* PROP_TXSTATUS_VSDB */ - printf("%s: ifname=%s, SSID: \"%s\"\n", __FUNCTION__, ifname, cur_if->ssid); + printf("%s: ifname=%s, mode=%d, SSID: \"%s\"\n", __FUNCTION__, + ifname, cur_if->ifmode, cur_if->ssid); cur_if->ifstate = IF_STATE_ENABLE; -exit: + return 0; +} + +static int +wl_ext_iapsta_enable(struct net_device *dev, char *command, int total_len) +{ + int ret = 0; + char *pch, *pick_tmp, *param; + struct wl_apsta_params *apsta_params = &g_apsta_params; + char ifname[IFNAMSIZ+1]; + + if (!apsta_params->init) { + ANDROID_ERROR(("%s: please init first\n", __FUNCTION__)); + return -1; + } + + ANDROID_TRACE(("%s: command=%s, len=%d\n", __FUNCTION__, command, total_len)); + + pick_tmp = command; + param = bcmstrtok(&pick_tmp, " ", 0); // skip iapsta_enable + param = bcmstrtok(&pick_tmp, " ", 0); + while (param != NULL) { + if (!strcmp(param, "ifname")) { + pch = bcmstrtok(&pick_tmp, " ", 0); + if (pch) { + strcpy(ifname, pch); + ret = wl_ext_iapsta_enable_iface(dev, ifname); + if (ret) + return ret; + else + OSL_SLEEP(1000); + } else { + ANDROID_ERROR(("%s: ifname [wlanX]\n", __FUNCTION__)); + return -1; + } + } + param = bcmstrtok(&pick_tmp, " ", 0); + } + return ret; } -void -wl_android_ext_iapsta_disconnect_sta(struct net_device *dev, u32 channel) +int +wl_ext_iapsta_alive_preinit(struct net_device *dev) { struct wl_apsta_params *apsta_params = &g_apsta_params; - struct wl_if_info *cur_if = &apsta_params->vif; - scb_val_t scbval; - int ret; - channel_info_t ci; - struct dhd_pub *dhd; - if (apsta_params->apstamode==IAPSTA_MODE && cur_if->ifstate==IF_STATE_ENABLE) { - dhd = dhd_get_pub(dev); - if (!FW_SUPPORTED(dhd, vsdb)) { - if (!(ret = wldev_ioctl(cur_if->dev, WLC_GET_CHANNEL, &ci, sizeof(channel_info_t), FALSE))) { - if (channel != ci.target_channel) { - printf("%s: deauthenticate all STA on vif\n", __FUNCTION__); - memcpy(scbval.ea.octet, ðer_bcast, ETHER_ADDR_LEN); - wl_ext_ioctl(cur_if->dev, WLC_SCB_DEAUTHENTICATE, &scbval.ea, ETHER_ADDR_LEN, 1); + if (apsta_params->init == TRUE) { + ANDROID_ERROR(("%s: don't init twice\n", __FUNCTION__)); + return -1; + } + + ANDROID_TRACE(("%s: Enter\n", __FUNCTION__)); + + strcpy(apsta_params->if_info[IF_PIF].ssid, "tttp"); + apsta_params->if_info[IF_PIF].maxassoc = -1; + apsta_params->if_info[IF_PIF].channel = 1; + + if (!strlen(apsta_params->if_info[IF_VIF].ifname)) + strcpy(apsta_params->if_info[IF_VIF].ifname, "wlan1"); + strcpy(apsta_params->if_info[IF_VIF].ssid, "tttv"); + apsta_params->if_info[IF_VIF].maxassoc = -1; + apsta_params->if_info[IF_VIF].channel = 1; + + if (!strlen(apsta_params->if_info[IF_VIF2].ifname)) + strcpy(apsta_params->if_info[IF_VIF2].ifname, "wlan2"); + strcpy(apsta_params->if_info[IF_VIF2].ssid, "tttv2"); + apsta_params->if_info[IF_VIF2].maxassoc = -1; + apsta_params->if_info[IF_VIF2].channel = 161; + + apsta_params->init = TRUE; + + return 0; +} + +int +wl_ext_iapsta_alive_postinit(struct net_device *dev) +{ + s32 apsta = 0; + struct wl_apsta_params *apsta_params = &g_apsta_params; + + wl_ext_iovar_getint(dev, "apsta", &apsta); + if (apsta == 1) { + apsta_params->apstamode = ISTAONLY_MODE; + apsta_params->if_info[IF_PIF].ifmode = ISTA_MODE; + op_mode = DHD_FLAG_STA_MODE; + } else { + apsta_params->apstamode = IAPONLY_MODE; + apsta_params->if_info[IF_PIF].ifmode = IAP_MODE; + op_mode = DHD_FLAG_HOSTAP_MODE; + } + // fix me: how to check it's IAPSTA_MODE or IDUALAP_MODE? + + wl_ext_get_ioctl_ver(dev, &apsta_params->ioctl_ver); + printf("%s: apstamode=%d\n", __FUNCTION__, apsta_params->apstamode); + + return op_mode; +} + +static bool +wl_ext_conn_status_str(uint32 event_type, + uint32 status, uint32 reason, char* stringBuf, uint buflen) +{ + int i; + + typedef struct conn_fail_event_map_t { + uint32 inEvent; /* input: event type to match */ + uint32 inStatus; /* input: event status code to match */ + uint32 inReason; /* input: event reason code to match */ + } conn_fail_event_map_t; + + /* Map of WLC_E events to connection failure strings */ +# define WL_IW_DONT_CARE 9999 + const conn_fail_event_map_t event_map [] = { + /* inEvent inStatus inReason */ + {WLC_E_LINK, WL_IW_DONT_CARE, WL_IW_DONT_CARE}, + {WLC_E_DEAUTH, WL_IW_DONT_CARE, WL_IW_DONT_CARE}, + {WLC_E_DEAUTH_IND, WL_IW_DONT_CARE, WL_IW_DONT_CARE}, + {WLC_E_DISASSOC, WL_IW_DONT_CARE, WL_IW_DONT_CARE}, + {WLC_E_DISASSOC_IND, WL_IW_DONT_CARE, WL_IW_DONT_CARE}, + {WLC_E_OVERLAY_REQ, WL_IW_DONT_CARE, WL_IW_DONT_CARE}, + {WLC_E_ASSOC_IND, WL_IW_DONT_CARE, DOT11_SC_SUCCESS}, + {WLC_E_REASSOC_IND, WL_IW_DONT_CARE, DOT11_SC_SUCCESS}, + }; + + /* Search the event map table for a matching event */ + for (i = 0; i < sizeof(event_map)/sizeof(event_map[0]); i++) { + const conn_fail_event_map_t* row = &event_map[i]; + if (row->inEvent == event_type && + (row->inStatus == status || row->inStatus == WL_IW_DONT_CARE) && + (row->inReason == reason || row->inReason == WL_IW_DONT_CARE)) { + memset(stringBuf, 0, buflen); + snprintf(stringBuf, buflen, "isam_event event=%d reason=%d", + event_type, reason); + return TRUE; + } + } + + return FALSE; +} + +int +wl_ext_iapsta_event(struct net_device *dev, wl_event_msg_t *e, void* data) +{ + struct wl_apsta_params *apsta_params = &g_apsta_params; + struct wl_if_info *cur_if = NULL; + char extra[IW_CUSTOM_MAX + 1]; + int i; +#if defined(WL_WIRELESS_EXT) + union iwreq_data wrqu; + int cmd = 0; +#endif + uint32 event_type = ntoh32(e->event_type); + uint32 status = ntoh32(e->status); + uint32 reason = ntoh32(e->reason); + uint16 flags = ntoh16(e->flags); + + if (!apsta_params->init) { + ANDROID_TRACE(("%s: please init first\n", __FUNCTION__)); + return -1; + } + + for (i=0; iif_info[i].bssidx == e->ifidx) { + cur_if = &apsta_params->if_info[i]; + break; + } + } + if (!cur_if || !cur_if->dev) { + ANDROID_ERROR(("%s: %s ifidx %d is not ready\n", __FUNCTION__, + dev->name, e->ifidx)); + return -1; + } + + memset(extra, 0, sizeof(extra)); +#if defined(WL_WIRELESS_EXT) + memset(&wrqu, 0, sizeof(wrqu)); + memcpy(wrqu.addr.sa_data, &e->addr, ETHER_ADDR_LEN); + wrqu.addr.sa_family = ARPHRD_ETHER; +#endif + + if (cur_if->ifmode == ISTA_MODE) { + switch (event_type) { + case WLC_E_LINK: + if (!(flags & WLC_EVENT_MSG_LINK)) { + printf("%s: %s Link Down with BSSID="MACSTR"\n", __FUNCTION__, + cur_if->ifname, MAC2STR((u8 *)&e->addr)); + } else { + printf("%s: %s Link UP with BSSID="MACSTR"\n", __FUNCTION__, + cur_if->ifname, MAC2STR((u8 *)&e->addr)); } - } + break; + default: + /* Cannot translate event */ + break; + } + } else if (cur_if->ifmode == IAP_MODE || cur_if->ifmode == IMESH_MODE) { + if (((event_type == WLC_E_ASSOC_IND) || (event_type == WLC_E_REASSOC_IND)) && + reason == DOT11_SC_SUCCESS) { + printf("%s: %s connected device "MACDBG"\n", __FUNCTION__, + cur_if->ifname, MAC2STRDBG(e->addr.octet)); + } else if (event_type == WLC_E_DISASSOC_IND) { + printf("%s: %s disassociated device "MACDBG"\n", __FUNCTION__, + cur_if->ifname, MAC2STRDBG(e->addr.octet)); + } else if ((event_type == WLC_E_DEAUTH_IND) || + ((event_type == WLC_E_DEAUTH) && (reason != DOT11_RC_RESERVED))) { + printf("%s: %s deauthenticated device "MACDBG"\n", __FUNCTION__, + cur_if->ifname, MAC2STRDBG(e->addr.octet)); + } else { + ANDROID_TRACE(("%s: %s event %d "MACDBG"\n", __FUNCTION__, + cur_if->ifname, event_type, MAC2STRDBG(e->addr.octet))); } } + + if (wl_ext_conn_status_str(event_type, status, reason, extra, sizeof(extra))) { +#if defined(WL_WIRELESS_EXT) + cmd = IWEVCUSTOM; + wrqu.data.length = strlen(extra); + wireless_send_event(cur_if->dev, cmd, &wrqu, extra); +#endif /* WL_WIRELESS_EXT */ + ANDROID_TRACE(("%s: %s event=%d, status=%d, reason=%d sent up\n", __FUNCTION__, + cur_if->ifname, event_type, status, reason)); + } else { + ANDROID_TRACE(("%s: %s event=%d, status=%d, reason=%d\n", __FUNCTION__, + cur_if->ifname, event_type, status, reason)); + } + + return 0; } -int wl_android_ext_attach_netdev(struct net_device *net, uint8 bssidx) +void +wl_ext_iapsta_disconnect_sta(struct net_device *dev, u32 channel) { - g_apsta_params.vif.dev = net; - g_apsta_params.vif.bssidx = bssidx; - if (strlen(g_apsta_params.vif.ifname)) { - memset(net->name, 0, sizeof(IFNAMSIZ)); - strcpy(net->name, g_apsta_params.vif.ifname); - net->name[IFNAMSIZ - 1] = '\0'; + struct wl_apsta_params *apsta_params = &g_apsta_params; + struct wl_if_info *cur_if = NULL; + int i; + + for (i=0; iif_info[i].dev == dev) { + cur_if = &apsta_params->if_info[i]; + cur_if->channel = channel; + wl_ext_iapsta_change_other_iface_channel(apsta_params->if_info[IF_PIF].dev, cur_if); + } + } + +} + +int +wl_ext_iapsta_attach_name(struct net_device *net, uint8 bssidx) +{ + struct wl_apsta_params *apsta_params = &g_apsta_params; + + ANDROID_TRACE(("%s: bssidx=%d, %s\n", __FUNCTION__, bssidx, net->name)); + if (bssidx == 1 && apsta_params->if_info[IF_VIF].ifstate == IF_STATE_INIT) { + strcpy(apsta_params->if_info[IF_VIF].ifname, net->name); + } else if (bssidx == 2 && apsta_params->if_info[IF_VIF2].ifstate == IF_STATE_INIT) { + strcpy(apsta_params->if_info[IF_VIF2].ifname, net->name); } - if (g_apsta_params.pif.dev) { - memcpy(net->dev_addr, g_apsta_params.pif.dev->dev_addr, ETHER_ADDR_LEN); - net->dev_addr[0] |= 0x02; + + return 0; +} + +int +wl_ext_iapsta_attach_netdev(struct net_device *net, uint8 bssidx) +{ + struct wl_apsta_params *apsta_params = &g_apsta_params; + + printf("%s: bssidx=%d\n", __FUNCTION__, bssidx); + if (bssidx == 0) { + memset(apsta_params, 0, sizeof(struct wl_apsta_params)); + apsta_params->vsdb = FALSE; + apsta_params->if_info[IF_PIF].dev = net; + apsta_params->if_info[IF_PIF].bssidx = bssidx; + strcpy(apsta_params->if_info[IF_PIF].ifname, net->name); + init_waitqueue_head(&apsta_params->netif_change_event); + } else if (bssidx == 1 && apsta_params->if_info[IF_VIF].ifstate == IF_STATE_INIT) { + apsta_params->if_info[IF_VIF].dev = net; + apsta_params->if_info[IF_VIF].bssidx = bssidx; + if (strlen(apsta_params->if_info[IF_VIF].ifname)) { + memset(net->name, 0, sizeof(IFNAMSIZ)); + strcpy(net->name, apsta_params->if_info[IF_VIF].ifname); + net->name[IFNAMSIZ-1] = '\0'; + } + if (apsta_params->if_info[IF_PIF].dev) { + memcpy(net->dev_addr, apsta_params->if_info[IF_PIF].dev->dev_addr, ETHER_ADDR_LEN); + net->dev_addr[0] |= 0x02; + } + apsta_params->netif_change = TRUE; + wake_up_interruptible(&apsta_params->netif_change_event); + } else if (bssidx == 2 && apsta_params->if_info[IF_VIF2].ifstate == IF_STATE_INIT) { + apsta_params->if_info[IF_VIF2].dev = net; + apsta_params->if_info[IF_VIF2].bssidx = bssidx; + if (strlen(apsta_params->if_info[IF_VIF2].ifname)) { + memset(net->name, 0, sizeof(IFNAMSIZ)); + strcpy(net->name, apsta_params->if_info[IF_VIF2].ifname); + net->name[IFNAMSIZ-1] = '\0'; + } + if (apsta_params->if_info[IF_PIF].dev) { + memcpy(net->dev_addr, apsta_params->if_info[IF_PIF].dev->dev_addr, ETHER_ADDR_LEN); + net->dev_addr[0] |= 0x02; + net->dev_addr[4] ^= 0x80; + net->dev_addr[4] += bssidx; + net->dev_addr[5] += bssidx; + } + apsta_params->netif_change = TRUE; + wake_up_interruptible(&apsta_params->netif_change_event); } return 0; } -int wl_android_ext_dettach_netdev(void) +int +wl_ext_iapsta_dettach_netdev(void) { struct wl_apsta_params *apsta_params = &g_apsta_params; - ANDROID_TRACE(("%s: Enter\n", __FUNCTION__)); + printf("%s: Enter\n", __FUNCTION__); memset(apsta_params, 0, sizeof(struct wl_apsta_params)); return 0; @@ -1491,7 +2320,8 @@ int wl_android_ext_dettach_netdev(void) #endif #ifdef IDHCP -int wl_ext_ip_dump(int ip, char *buf) +int +wl_ext_ip_dump(int ip, char *buf) { unsigned char bytes[4]; int bytes_written=-1; @@ -1669,17 +2499,29 @@ int wl_android_ext_priv_cmd(struct net_device *net, char *command, int total_len } #ifdef WL_EXT_IAPSTA else if (strnicmp(command, CMD_IAPSTA_INIT, strlen(CMD_IAPSTA_INIT)) == 0) { - *bytes_written = wl_ext_iapsta_init(net, command, total_len); + *bytes_written = wl_ext_isam_init(net, command, total_len); + } + else if (strnicmp(command, CMD_ISAM_INIT, strlen(CMD_ISAM_INIT)) == 0) { + *bytes_written = wl_ext_isam_init(net, command, total_len); } else if (strnicmp(command, CMD_IAPSTA_CONFIG, strlen(CMD_IAPSTA_CONFIG)) == 0) { *bytes_written = wl_ext_iapsta_config(net, command, total_len); } + else if (strnicmp(command, CMD_ISAM_CONFIG, strlen(CMD_ISAM_CONFIG)) == 0) { + *bytes_written = wl_ext_iapsta_config(net, command, total_len); + } else if (strnicmp(command, CMD_IAPSTA_ENABLE, strlen(CMD_IAPSTA_ENABLE)) == 0) { *bytes_written = wl_ext_iapsta_enable(net, command, total_len); } + else if (strnicmp(command, CMD_ISAM_ENABLE, strlen(CMD_ISAM_ENABLE)) == 0) { + *bytes_written = wl_ext_iapsta_enable(net, command, total_len); + } else if (strnicmp(command, CMD_IAPSTA_DISABLE, strlen(CMD_IAPSTA_DISABLE)) == 0) { *bytes_written = wl_ext_iapsta_disable(net, command, total_len); } + else if (strnicmp(command, CMD_ISAM_DISABLE, strlen(CMD_ISAM_DISABLE)) == 0) { + *bytes_written = wl_ext_iapsta_disable(net, command, total_len); + } #endif #ifdef IDHCP else if (strnicmp(command, CMD_DHCPC_ENABLE, strlen(CMD_DHCPC_ENABLE)) == 0) { diff --git a/bcmdhd.1.579.77.41.1.cn/wl_cfg80211.c b/bcmdhd.1.579.77.41.1.cn/wl_cfg80211.c index 5651601..9e5f3fb 100644 --- a/bcmdhd.1.579.77.41.1.cn/wl_cfg80211.c +++ b/bcmdhd.1.579.77.41.1.cn/wl_cfg80211.c @@ -74,6 +74,10 @@ #include #endif /* (LINUX_VERSION_CODE > KERNEL_VERSION(3, 14, 0)) || defined(WL_VENDOR_EXT_SUPPORT) */ +#if !defined(WL_VENDOR_EXT_SUPPORT) +#undef GSCAN_SUPPORT +#endif + #if defined(STAT_REPORT) #include #endif /* STAT_REPORT */ @@ -509,6 +513,14 @@ static s32 wl_cfg80211_del_station(struct wiphy *wiphy, static s32 wl_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev, u8* mac_addr); #endif +#ifdef WLMESH +static s32 wl_cfg80211_join_mesh( + struct wiphy *wiphy, struct net_device *dev, + const struct mesh_config *conf, + const struct mesh_setup *setup); +static s32 wl_cfg80211_leave_mesh(struct wiphy *wiphy, + struct net_device *dev); +#endif /* WLMESH */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)) static s32 wl_cfg80211_change_station(struct wiphy *wiphy, struct net_device *dev, const u8 *mac, struct station_parameters *params); @@ -561,7 +573,11 @@ static s32 wl_cfg80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, #endif #endif #ifdef WL_SCHED_SCAN -static int wl_cfg80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev); +static int wl_cfg80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) + , u64 reqid +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */ +); #endif static s32 wl_cfg80211_set_ap_role(struct bcm_cfg80211 *cfg, struct net_device *dev); #if defined(WL_VIRTUAL_APSTA) || defined(DUAL_STA_STATIC_IF) @@ -1308,6 +1324,13 @@ wl_cfg80211_ether_atoe(const char *a, struct ether_addr *n) /* There isn't a lot of sense in it, but you can transmit anything you like */ static const struct ieee80211_txrx_stypes wl_cfg80211_default_mgmt_stypes[NUM_NL80211_IFTYPES] = { +#ifdef WLMESH + [NL80211_IFTYPE_MESH_POINT] = { + .tx = 0xffff, + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_AUTH >> 4) + }, +#endif /* WLMESH */ [NL80211_IFTYPE_ADHOC] = { .tx = 0xffff, .rx = BIT(IEEE80211_STYPE_ACTION >> 4) @@ -1607,7 +1630,10 @@ wl_cfg80211_add_virtual_iface(struct wiphy *wiphy, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)) unsigned char name_assign_type, #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)) */ - enum nl80211_iftype type, u32 *flags, + enum nl80211_iftype type, +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0)) + u32 *flags, +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0)) */ struct vif_params *params) { s32 err = -ENODEV; @@ -1624,10 +1650,10 @@ wl_cfg80211_add_virtual_iface(struct wiphy *wiphy, struct ether_addr primary_mac; bcm_struct_cfgdev *new_cfgdev; #ifdef PROP_TXSTATUS_VSDB -#if defined(BCMSDIO) +#if defined(BCMSDIO) || defined(BCMDBUS) s32 up = 1; bool enabled; -#endif +#endif /* BCMSDIO || BCMDBUS */ #endif /* PROP_TXSTATUS_VSDB */ dhd_pub_t *dhd; bool hang_required = false; @@ -1767,7 +1793,7 @@ wl_cfg80211_add_virtual_iface(struct wiphy *wiphy, wl_cfg80211_scan_abort(cfg); #ifdef PROP_TXSTATUS_VSDB -#if defined(BCMSDIO) +#if defined(BCMSDIO) || defined(BCMDBUS) if (!cfg->wlfc_on && !disable_proptx) { dhd_wlfc_get_enable(dhd, &enabled); if (!enabled && dhd->op_mode != DHD_FLAG_HOSTAP_MODE && @@ -1779,7 +1805,7 @@ wl_cfg80211_add_virtual_iface(struct wiphy *wiphy, } cfg->wlfc_on = true; } -#endif +#endif /* BCMSDIO || BCMDBUS */ #endif /* PROP_TXSTATUS_VSDB */ /* Dual p2p doesn't support multiple P2PGO interfaces, @@ -1967,14 +1993,14 @@ wl_cfg80211_add_virtual_iface(struct wiphy *wiphy, memset(cfg->p2p->vir_ifname, '\0', IFNAMSIZ); wl_to_p2p_bss_bssidx(cfg, cfg_type) = -1; #ifdef PROP_TXSTATUS_VSDB -#if defined(BCMSDIO) +#if defined(BCMSDIO) || defined(BCMDBUS) dhd_wlfc_get_enable(dhd, &enabled); if (enabled && cfg->wlfc_on && dhd->op_mode != DHD_FLAG_HOSTAP_MODE && dhd->op_mode != DHD_FLAG_IBSS_MODE && dhd->conf->disable_proptx!=0) { dhd_wlfc_deinit(dhd); cfg->wlfc_on = false; } -#endif +#endif /* BCMSDIO || BCMDBUS */ #endif /* PROP_TXSTATUS_VSDB */ } } @@ -2181,7 +2207,10 @@ done: static s32 wl_cfg80211_change_virtual_iface(struct wiphy *wiphy, struct net_device *ndev, - enum nl80211_iftype type, u32 *flags, + enum nl80211_iftype type, +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0)) + u32 *flags, +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0)) */ struct vif_params *params) { s32 ap = 0; @@ -2200,11 +2229,18 @@ wl_cfg80211_change_virtual_iface(struct wiphy *wiphy, struct net_device *ndev, switch (type) { case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_WDS: +#ifndef WLMESH case NL80211_IFTYPE_MESH_POINT: +#endif /* WLMESH */ ap = 1; WL_ERR(("type (%d) : currently we do not support this type\n", type)); break; +#ifdef WLMESH + case NL80211_IFTYPE_MESH_POINT: + infra_ibss = WL_BSSTYPE_MESH; + break; +#endif /* WLMESH */ case NL80211_IFTYPE_ADHOC: mode = WL_MODE_IBSS; infra_ibss = 0; @@ -2429,10 +2465,10 @@ static s32 wl_cfg80211_handle_ifdel(struct bcm_cfg80211 *cfg, wl_if_event_info * s32 type = -1; s32 bssidx = -1; #ifdef PROP_TXSTATUS_VSDB -#if defined(BCMSDIO) +#if defined(BCMSDIO) || defined(BCMDBUS) dhd_pub_t *dhd = (dhd_pub_t *)(cfg->pub); bool enabled; -#endif +#endif /* BCMSDIO || BCMDBUS */ #endif /* PROP_TXSTATUS_VSDB */ bssidx = if_event_info->bssidx; @@ -2462,14 +2498,14 @@ static s32 wl_cfg80211_handle_ifdel(struct bcm_cfg80211 *cfg, wl_if_event_info * } #ifdef PROP_TXSTATUS_VSDB -#if defined(BCMSDIO) +#if defined(BCMSDIO) || defined(BCMDBUS) dhd_wlfc_get_enable(dhd, &enabled); if (enabled && cfg->wlfc_on && dhd->op_mode != DHD_FLAG_HOSTAP_MODE && dhd->op_mode != DHD_FLAG_IBSS_MODE && dhd->conf->disable_proptx!=0) { dhd_wlfc_deinit(dhd); cfg->wlfc_on = false; } -#endif +#endif /* BCMSDIO || BCMDBUS */ #endif /* PROP_TXSTATUS_VSDB */ } @@ -3658,6 +3694,51 @@ fail: } #endif /* WLAIBSS_MCHAN */ +#ifdef WLMESH +s32 +wl_cfg80211_interface_ops(struct bcm_cfg80211 *cfg, + struct net_device *ndev, s32 bsscfg_idx, + enum nl80211_iftype iface_type, s32 del, u8 *addr) +{ + wl_interface_create_t iface; + s32 ret; + wl_interface_info_t *info; + + bzero(&iface, sizeof(wl_interface_create_t)); + + iface.ver = WL_INTERFACE_CREATE_VER; + + if (iface_type == NL80211_IFTYPE_AP) + iface.flags = WL_INTERFACE_CREATE_AP; + else + iface.flags = WL_INTERFACE_CREATE_STA; + + if (del) { + ret = wldev_iovar_setbuf(ndev, "interface_remove", + NULL, 0, cfg->ioctl_buf, WLC_IOCTL_MEDLEN, NULL); + } else { + if (addr) { + memcpy(&iface.mac_addr.octet, addr, ETH_ALEN); + iface.flags |= WL_INTERFACE_MAC_USE; + } + ret = wldev_iovar_getbuf(ndev, "interface_create", + &iface, sizeof(wl_interface_create_t), + cfg->ioctl_buf, WLC_IOCTL_MAXLEN, &cfg->ioctl_buf_sync); + if (ret == 0) { + /* success */ + info = (wl_interface_info_t *)cfg->ioctl_buf; + WL_DBG(("wl interface create success!! bssidx:%d \n", + info->bsscfgidx)); + } + } + + if (ret < 0) + WL_ERR(("Interface %s failed!! ret %d\n", + del ? "remove" : "create", ret)); + + return ret; +} +#else s32 wl_cfg80211_interface_ops(struct bcm_cfg80211 *cfg, struct net_device *ndev, s32 bsscfg_idx, @@ -3752,6 +3833,7 @@ wl_cfg80211_interface_ops(struct bcm_cfg80211 *cfg, WL_DBG(("wl interface create success!! bssidx:%d \n", ret)); return ret; } +#endif bool wl_customer6_legacy_chip_check(struct bcm_cfg80211 *cfg, @@ -4176,6 +4258,9 @@ wl_cfg80211_create_iface(struct wiphy *wiphy, wl_if_event_info *event = NULL; u8 addr[ETH_ALEN]; struct net_info *iter, *next; +#ifdef WLMESH + u16 role = 0, mode = 0; +#endif WL_DBG(("Enter\n")); if (!name) { @@ -4277,6 +4362,11 @@ wl_cfg80211_create_iface(struct wiphy *wiphy, } event = &cfg->if_event_info; +#ifdef WLMESH + cfg80211_to_wl_iftype(iface_type, &role, &mode); + event->role = role; +#endif + /* * Since FW operation is successful,we can go ahead with the * the host interface creation. @@ -4353,6 +4443,157 @@ exit: } #endif /* defined(WL_VIRTUAL_APSTA) || defined(DUAL_STA_STATIC_IF) */ +#ifdef WLMESH +s32 wl_cfg80211_set_sae_password(struct net_device *dev, char* buf, int len) +{ + struct bcm_cfg80211 *cfg = wl_get_cfg(dev); + + sscanf(buf, "%s %d", cfg->sae_password, &cfg->sae_password_len); + return 0; +} + +static s32 wl_cfg80211_join_mesh( + struct wiphy *wiphy, struct net_device *dev, + const struct mesh_config *conf, + const struct mesh_setup *setup) +{ + struct bcm_cfg80211 *cfg = wiphy_priv(wiphy); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)) + struct ieee80211_channel *chan = setup->chandef.chan; +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION (3, 6, 0)) + struct ieee80211_channel *chan = setup->channel; +#endif + u32 param[2] = {0, 0}; + s32 err = 0; + u32 bw_cap = 0; + u32 beacon_interval = setup->beacon_interval; + u32 dtim_period = setup->dtim_period; + size_t join_params_size; + struct wl_join_params join_params; + chanspec_t chanspec = 0; + + cfg->channel = ieee80211_frequency_to_channel(chan->center_freq); + + if (wl_get_drv_status(cfg, CONNECTED, dev)) { + struct wlc_ssid *lssid = (struct wlc_ssid *)wl_read_prof(cfg, dev, WL_PROF_SSID); + u8 *bssid = (u8 *)wl_read_prof(cfg, dev, WL_PROF_BSSID); + u32 *channel = (u32 *)wl_read_prof(cfg, dev, WL_PROF_CHAN); + if ((memcmp(setup->mesh_id, lssid->SSID, lssid->SSID_len) == 0) && + (*channel == cfg->channel)) { + WL_ERR(("MESH connection already existed to " MACDBG "\n", + MAC2STRDBG((u8 *)wl_read_prof(cfg, dev, WL_PROF_BSSID)))); + return -EISCONN; + } + WL_ERR(("Previous connecton existed, please disconnect mesh %s (" MACDBG ") first\n", + lssid->SSID, MAC2STRDBG(bssid))); + return -EISCONN; + } + + if (chan) { + if (chan->band == IEEE80211_BAND_5GHZ) + param[0] = WLC_BAND_5G; + else if (chan->band == IEEE80211_BAND_2GHZ) + param[0] = WLC_BAND_2G; + err = wldev_iovar_getint(dev, "bw_cap", param); + if (unlikely(err)) { + WL_ERR(("Get bw_cap Failed (%d)\n", err)); + return err; + } + bw_cap = param[0]; + chanspec = channel_to_chanspec(wiphy, dev, cfg->channel, bw_cap); + } + + memset(&join_params, 0, sizeof(join_params)); + memcpy((void *)join_params.ssid.SSID, (void *)setup->mesh_id, + setup->mesh_id_len); + + join_params.ssid.SSID_len = htod32(setup->mesh_id_len); + join_params.params.chanspec_list[0] = chanspec; + join_params.params.chanspec_num = 1; + wldev_iovar_setint(dev, "chanspec", chanspec); + join_params_size = sizeof(join_params); + + wldev_iovar_setint(dev, "wpa_auth", WPA_AUTH_DISABLED); + wldev_iovar_setint(dev, "wsec", 0); + + if (cfg->sae_password_len > 0) { + wldev_iovar_setint(dev, "mesh_auth_proto", 1); + wldev_iovar_setint(dev, "wpa_auth", WPA2_AUTH_PSK); + wldev_iovar_setint(dev, "wsec", AES_ENABLED); + wldev_iovar_setint(dev, "mfp", WL_MFP_REQUIRED); + printf("%s: password=%s, len=%d\n", __FUNCTION__, + cfg->sae_password, cfg->sae_password_len); + wldev_iovar_setbuf(dev, "sae_password", cfg->sae_password, cfg->sae_password_len, + cfg->ioctl_buf, WLC_IOCTL_MAXLEN, NULL); + } else { + wldev_iovar_setint(dev, "mesh_auth_proto", 0); + wldev_iovar_setint(dev, "mfp", WL_MFP_NONE); + } + + if (beacon_interval) { + if ((err = wldev_ioctl_set(dev, WLC_SET_BCNPRD, + &beacon_interval, sizeof(s32))) < 0) { + WL_ERR(("Beacon Interval Set Error, %d\n", err)); + return err; + } + } + + if (dtim_period) { + if ((err = wldev_ioctl_set(dev, WLC_SET_DTIMPRD, + &dtim_period, sizeof(s32))) < 0) { + WL_ERR(("DTIM Interval Set Error, %d\n", err)); + return err; + } + } + wldev_iovar_setint(dev, "mpc", 0); + + WL_ERR(("JOIN %s on channel %d with chanspec 0x%4x\n", + join_params.ssid.SSID, cfg->channel, chanspec)); + + err = wldev_ioctl_set(dev, WLC_SET_SSID, &join_params, + join_params_size); + + if (unlikely(err)) { + WL_ERR(("Error (%d)\n", err)); + return err; + } + + wl_update_prof(cfg, dev, NULL, &join_params.ssid, WL_PROF_SSID); + wl_update_prof(cfg, dev, NULL, &cfg->channel, WL_PROF_CHAN); + return err; +} + + +static s32 wl_cfg80211_leave_mesh( + struct wiphy *wiphy, struct net_device *dev) +{ + struct bcm_cfg80211 *cfg = wiphy_priv(wiphy); + s32 err = 0; + scb_val_t scbval; + u8 *curbssid; + + RETURN_EIO_IF_NOT_UP(cfg); + wl_link_down(cfg); + + WL_ERR(("Leave MESH\n")); + curbssid = wl_read_prof(cfg, dev, WL_PROF_BSSID); + wl_set_drv_status(cfg, DISCONNECTING, dev); + scbval.val = 0; + memcpy(&scbval.ea, curbssid, ETHER_ADDR_LEN); + err = wldev_ioctl_set(dev, WLC_DISASSOC, &scbval, + sizeof(scb_val_t)); + if (unlikely(err)) { + wl_clr_drv_status(cfg, DISCONNECTING, dev); + WL_ERR(("error(%d)\n", err)); + return err; + } + memset(cfg->sae_password, 0, SAE_MAX_PASSWD_LEN); + cfg->sae_password_len = 0; + + return err; +} +#endif /* WLMESH */ + static s32 wl_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_ibss_params *params) @@ -4814,11 +5055,11 @@ wl_cfg80211_set_mfp(struct bcm_cfg80211 *cfg, mfp, wpa2_ie, rsn_cap[0], rsn_cap[1], fw_support)); if (fw_support == false) { - if (mfp) { + if (mfp == WL_MFP_REQUIRED) { /* if mfp > 0, mfp capability set in wpa ie, but * FW indicated error for mfp. Propagate the error up. */ - WL_ERR(("mfp capability found in wpaie. But fw doesn't" + WL_ERR(("mfp capability found in wpaie. But fw doesn't " "seem to support MFP\n")); return -EINVAL; } else { @@ -5117,6 +5358,9 @@ wl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, WL_DBG(("In\n")); BCM_REFERENCE(dhdp); +#ifdef WLMESH + wl_config_ifmode(cfg, dev, dev->ieee80211_ptr->iftype); +#endif #if defined(SUPPORT_RANDOM_MAC_SCAN) wl_cfg80211_set_random_mac(dev, FALSE); #endif /* SUPPORT_RANDOM_MAC_SCAN */ @@ -5175,7 +5419,11 @@ wl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, * A start scan occuring during connect is unlikely */ if (cfg->sched_scan_req) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) + wl_cfg80211_sched_scan_stop(wiphy, bcmcfg_to_prmry_ndev(cfg), 0); +#else wl_cfg80211_sched_scan_stop(wiphy, bcmcfg_to_prmry_ndev(cfg)); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */ } #endif #if defined(ESCAN_RESULT_PATCH) @@ -5385,7 +5633,7 @@ wl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, DHD_DISABLE_RUNTIME_PM((dhd_pub_t *)cfg->pub); #endif /* BCMDONGLEHOST && CUSTOMER_HW2 */ #ifdef WL_EXT_IAPSTA - wl_android_ext_iapsta_disconnect_sta(dev, cfg->channel); + wl_ext_iapsta_disconnect_sta(dev, cfg->channel); #endif err = wldev_iovar_setbuf_bsscfg(dev, "join", ext_join_params, join_params_size, cfg->ioctl_buf, WLC_IOCTL_MAXLEN, bssidx, &cfg->ioctl_buf_sync); @@ -5777,9 +6025,24 @@ wl_cfg80211_interface_create(struct net_device *dev, char *name) { struct bcm_cfg80211 *cfg = wl_get_cfg(dev); bcm_struct_cfgdev *new_cfgdev; +#ifdef WLMESH + char ifname[IFNAMSIZ]; + char iftype[IFNAMSIZ]; + enum nl80211_iftype iface_type = NL80211_IFTYPE_STATION; + + sscanf(name, "%s %s", ifname, iftype); + + if (strnicmp(iftype, "AP", strlen("AP")) == 0) { + iface_type = NL80211_IFTYPE_AP; + } +#endif new_cfgdev = wl_cfg80211_create_iface(cfg->wdev->wiphy, +#ifdef WLMESH + iface_type, NULL, ifname); +#else NL80211_IFTYPE_STATION, NULL, name); +#endif if (!new_cfgdev) { return BCME_ERROR; } @@ -8818,6 +9081,9 @@ wl_cfg80211_bcn_bringup_ap( s32 join_params_size = 0; s32 ap = 1; s32 wsec; +#ifdef WLMESH + bool retried = false; +#endif #ifdef SOFTAP_UAPSD_OFF uint32 wme_apsd = 0; #endif /* SOFTAP_UAPSD_OFF */ @@ -8954,6 +9220,9 @@ wl_cfg80211_bcn_bringup_ap( } #endif /* MFP */ +#ifdef WLMESH +ssid_retry: +#endif memset(&join_params, 0, sizeof(join_params)); /* join parameters starts with ssid */ join_params_size = sizeof(join_params.ssid); @@ -8986,6 +9255,13 @@ wl_cfg80211_bcn_bringup_ap( timeout = wait_event_interruptible_timeout(cfg->netif_change_event, wl_get_drv_status(cfg, AP_CREATED, dev), msecs_to_jiffies(MAX_AP_LINK_WAIT_TIME)); if (timeout <= 0 || !wl_get_drv_status(cfg, AP_CREATED, dev)) { +#ifdef WLMESH + if (!retried) { + retried = true; + WL_ERR(("Link up didn't come for AP interface. Try to set ssid again to recover it! \n")); + goto ssid_retry; + } +#endif WL_ERR(("Link up didn't come for AP interface. AP/GO creation failed! \n")); if (timeout == -ERESTARTSYS) { WL_ERR(("waitqueue was interrupted by a signal, returns -ERESTARTSYS\n")); @@ -9397,6 +9673,10 @@ wl_cfg80211_start_ap( s32 bssidx = 0; u32 dev_role = 0; dhd_pub_t *dhd = (dhd_pub_t *)(cfg->pub); +#ifdef WLMESH + struct wl_join_params join_params; + s32 join_params_size = 0; +#endif WL_DBG(("Enter \n")); @@ -9497,6 +9777,35 @@ wl_cfg80211_start_ap( // goto fail; } +#ifdef WLMESH + OSL_SLEEP(1000); + if ((dev_role == NL80211_IFTYPE_P2P_GO) || (dev_role == NL80211_IFTYPE_AP)) { + memset(&join_params, 0, sizeof(join_params)); + /* join parameters starts with ssid */ + join_params_size = sizeof(join_params.ssid); + if (dev_role == NL80211_IFTYPE_P2P_GO) { + join_params.ssid.SSID_len = min(cfg->p2p->ssid.SSID_len, + (uint32)DOT11_MAX_SSID_LEN); + memcpy(join_params.ssid.SSID, cfg->p2p->ssid.SSID, + join_params.ssid.SSID_len); + } else if (dev_role == NL80211_IFTYPE_AP) { + join_params.ssid.SSID_len = min(cfg->hostapd_ssid.SSID_len, + (uint32)DOT11_MAX_SSID_LEN); + memcpy(join_params.ssid.SSID, cfg->hostapd_ssid.SSID, + join_params.ssid.SSID_len); + } + join_params.ssid.SSID_len = htod32(join_params.ssid.SSID_len); + /* create softap */ + if ((err = wldev_ioctl_set(dev, WLC_SET_SSID, &join_params, + join_params_size)) != 0) { + WL_ERR(("SoftAP/GO set ssid failed! \n")); + goto fail; + } else { + WL_DBG((" SoftAP SSID \"%s\" \n", join_params.ssid.SSID)); + } + } +#endif + WL_DBG(("** AP/GO Created **\n")); #ifdef WL_CFG80211_ACL @@ -10120,7 +10429,11 @@ exit: } static int -wl_cfg80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev) +wl_cfg80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) + , u64 reqid +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */ +) { struct bcm_cfg80211 *cfg = wiphy_priv(wiphy); dhd_pub_t *dhdp = (dhd_pub_t *)(cfg->pub); @@ -10406,6 +10719,10 @@ static struct cfg80211_ops wl_cfg80211_ops = { .change_station = wl_cfg80211_change_station, .mgmt_tx_cancel_wait = wl_cfg80211_mgmt_tx_cancel_wait, #endif /* WL_SUPPORT_BACKPORTED_KPATCHES || KERNEL_VERSION >= (3,2,0) */ +#ifdef WLMESH + .join_mesh = wl_cfg80211_join_mesh, + .leave_mesh = wl_cfg80211_leave_mesh, +#endif /* WLMESH */ #if (LINUX_VERSION_CODE > KERNEL_VERSION(3, 2, 0)) .tdls_mgmt = wl_cfg80211_tdls_mgmt, .tdls_oper = wl_cfg80211_tdls_oper, @@ -10434,6 +10751,10 @@ s32 wl_mode_to_nl80211_iftype(s32 mode) return NL80211_IFTYPE_ADHOC; case WL_MODE_AP: return NL80211_IFTYPE_AP; +#ifdef WLMESH + case WL_MODE_MESH: + return NL80211_IFTYPE_MESH_POINT; +#endif default: return NL80211_IFTYPE_UNSPECIFIED; } @@ -10546,8 +10867,15 @@ static s32 wl_setup_wiphy(struct wireless_dev *wdev, struct device *sdiofunc_dev wdev->wiphy->max_sched_scan_ssids = MAX_PFN_LIST_COUNT; wdev->wiphy->max_match_sets = MAX_PFN_LIST_COUNT; wdev->wiphy->max_sched_scan_ie_len = WL_SCAN_IE_LEN_MAX; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) + wdev->wiphy->max_sched_scan_plan_interval = PNO_SCAN_MAX_FW_SEC; +#else wdev->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */ #endif /* WL_SCHED_SCAN */ +#ifdef WLMESH + wdev->wiphy->flags |= WIPHY_FLAG_MESH_AUTH; +#endif wdev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC) @@ -10561,13 +10889,18 @@ static s32 wl_setup_wiphy(struct wireless_dev *wdev, struct device *sdiofunc_dev #if defined(WL_CFG80211_P2P_DEV_IF) | BIT(NL80211_IFTYPE_P2P_DEVICE) #endif /* WL_CFG80211_P2P_DEV_IF */ +#ifdef WLMESH + | BIT(NL80211_IFTYPE_MESH_POINT) +#endif /* WLMESH */ | BIT(NL80211_IFTYPE_AP); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)) && \ (defined(WL_IFACE_COMB_NUM_CHANNELS) || defined(WL_CFG80211_P2P_DEV_IF)) WL_DBG(("Setting interface combinations for common mode\n")); +#ifndef BCMDBUS if (dhd->conf->num_different_channels >= 0) common_iface_combinations[0].num_different_channels = dhd->conf->num_different_channels; +#endif /* !BCMDBUS */ wdev->wiphy->iface_combinations = common_iface_combinations; wdev->wiphy->n_iface_combinations = ARRAY_SIZE(common_iface_combinations); @@ -11907,6 +12240,27 @@ wl_notify_connect_status(struct bcm_cfg80211 *cfg, bcm_struct_cfgdev *cfgdev, wl_get_bss_info(cfg, ndev, (u8*)(&e->addr)); } #endif /* DHD_ENABLE_BIGDATA_LOGGING */ + if (wl_get_drv_status(cfg, CONNECTED, ndev)) { + u8 *curbssid = wl_read_prof(cfg, ndev, WL_PROF_BSSID); + if (memcmp(curbssid, &e->addr, ETHER_ADDR_LEN) != 0) { + bool fw_assoc_state = TRUE; + dhd_pub_t *dhd = (dhd_pub_t *)cfg->pub; + fw_assoc_state = dhd_is_associated(dhd, e->ifidx, &err); + if (!fw_assoc_state) { + WL_ERR(("Event sends up even different BSSID" + " cur: " MACDBG " event: " MACDBG"\n", + MAC2STRDBG(curbssid), + MAC2STRDBG((const u8*)(&e->addr)))); + } else { + WL_ERR(("BSSID of event is not the connected BSSID" + "(ignore it) cur: " MACDBG + " event: " MACDBG"\n", + MAC2STRDBG(curbssid), + MAC2STRDBG((const u8*)(&e->addr)))); + return 0; + } + } + } /* Explicitly calling unlink to remove BSS in CFG */ wiphy = bcmcfg_to_wiphy(cfg); ssid = (struct wlc_ssid *)wl_read_prof(cfg, ndev, WL_PROF_SSID); @@ -12694,6 +13048,9 @@ wl_bss_roaming_done(struct bcm_cfg80211 *cfg, struct net_device *ndev, #if defined(WLADPS_SEAK_AP_WAR) || defined(WBTEXT) dhd_pub_t *dhdp = (dhd_pub_t *)(cfg->pub); #endif /* WLADPS_SEAK_AP_WAR || WBTEXT */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) + struct cfg80211_roam_info roam_info = {}; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */ #ifdef WLADPS_SEAK_AP_WAR BCM_REFERENCE(dhdp); @@ -12758,6 +13115,18 @@ wl_bss_roaming_done(struct bcm_cfg80211 *cfg, struct net_device *ndev, MAC2STRDBG((const u8*)(&e->addr)), *channel); dhd_conf_set_wme(cfg->pub, 0); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) + roam_info.channel = notify_channel; + roam_info.bssid = curbssid; + roam_info.req_ie = conn_info->req_ie; + roam_info.req_ie_len = conn_info->req_ie_len; + roam_info.resp_ie = conn_info->resp_ie; + roam_info.resp_ie_len = conn_info->resp_ie_len; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */ + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) + cfg80211_roamed(ndev, &roam_info, GFP_KERNEL); +#else cfg80211_roamed(ndev, #if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) notify_channel, @@ -12765,6 +13134,7 @@ wl_bss_roaming_done(struct bcm_cfg80211 *cfg, struct net_device *ndev, curbssid, conn_info->req_ie, conn_info->req_ie_len, conn_info->resp_ie, conn_info->resp_ie_len, GFP_KERNEL); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */ WL_DBG(("Report roaming result\n")); memcpy(&cfg->last_roamed_addr, &e->addr, ETHER_ADDR_LEN); @@ -14380,8 +14750,13 @@ static s32 wl_notify_escan_complete(struct bcm_cfg80211 *cfg, #ifdef WL_SCHED_SCAN if (cfg->sched_scan_req && !cfg->scan_request) { WL_PNO((">>> REPORTING SCHED SCAN RESULTS \n")); - if (!aborted) + if (!aborted) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) + cfg80211_sched_scan_results(cfg->sched_scan_req->wiphy, 0); +#else cfg80211_sched_scan_results(cfg->sched_scan_req->wiphy); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */ + } DBG_EVENT_LOG(dhdp, WIFI_EVENT_DRIVER_PNO_SCAN_COMPLETE); cfg->sched_scan_running = FALSE; @@ -15243,9 +15618,9 @@ static s32 wl_init_priv(struct bcm_cfg80211 *cfg) cfg->active_scan = true; cfg->rf_blocked = false; cfg->vsdb_mode = false; -#if defined(BCMSDIO) +#if defined(BCMSDIO) || defined(BCMDBUS) cfg->wlfc_on = false; -#endif +#endif /* BCMSDIO || BCMDBUS */ cfg->roam_flags |= WL_ROAM_OFF_ON_CONCURRENT; cfg->disable_roam_event = false; /* register interested state */ @@ -15439,7 +15814,11 @@ s32 wl_cfg80211_attach(struct net_device *ndev, void *context) kfree(wdev); return -ENOMEM; } +#ifdef WLMESH + wdev->iftype = wl_mode_to_nl80211_iftype(WL_MODE_MESH); +#else wdev->iftype = wl_mode_to_nl80211_iftype(WL_MODE_BSS); +#endif cfg = wiphy_priv(wdev->wiphy); cfg->wdev = wdev; cfg->pub = context; @@ -15453,7 +15832,7 @@ s32 wl_cfg80211_attach(struct net_device *ndev, void *context) SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy)); wdev->netdev = ndev; cfg->state_notifier = wl_notifier_change_state; - err = wl_alloc_netinfo(cfg, ndev, wdev, WL_MODE_BSS, PM_ENABLE, 0); + err = wl_alloc_netinfo(cfg, ndev, wdev, wdev->iftype, PM_ENABLE, 0); if (err) { WL_ERR(("Failed to alloc net_info (%d)\n", err)); goto cfg80211_attach_out; @@ -15769,6 +16148,12 @@ static s32 wl_config_ifmode(struct bcm_cfg80211 *cfg, struct net_device *ndev, s mode = WL_MODE_BSS; infra = 1; break; +#ifdef WLMESH + case NL80211_IFTYPE_MESH_POINT: + mode = WL_MODE_MESH; + infra = WL_BSSTYPE_MESH; + break; +#endif /* WLMESH */ case NL80211_IFTYPE_AP: case NL80211_IFTYPE_P2P_GO: mode = WL_MODE_AP; @@ -16393,9 +16778,9 @@ static s32 __wl_cfg80211_down(struct bcm_cfg80211 *cfg) struct net_device *p2p_net = cfg->p2p_net; #endif #ifdef PROP_TXSTATUS_VSDB -#if defined(BCMSDIO) +#if defined(BCMSDIO) || defined(BCMDBUS) dhd_pub_t *dhd = (dhd_pub_t *)(cfg->pub); -#endif +#endif /* BCMSDIO || BCMDBUS */ #endif /* PROP_TXSTATUS_VSDB */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)) struct cfg80211_scan_info info; @@ -16423,7 +16808,7 @@ static s32 __wl_cfg80211_down(struct bcm_cfg80211 *cfg) if (cfg->p2p_supported) { wl_clr_p2p_status(cfg, GO_NEG_PHASE); #ifdef PROP_TXSTATUS_VSDB -#if defined(BCMSDIO) +#if defined(BCMSDIO) || defined(BCMDBUS) if (wl_cfgp2p_vif_created(cfg)) { bool enabled = false; dhd_wlfc_get_enable(dhd, &enabled); @@ -16433,7 +16818,7 @@ static s32 __wl_cfg80211_down(struct bcm_cfg80211 *cfg) cfg->wlfc_on = false; } } -#endif +#endif /* BCMSDIO || BCMDBUS */ #endif /* PROP_TXSTATUS_VSDB */ } @@ -16605,6 +16990,10 @@ s32 wl_cfg80211_up(struct net_device *net) return err; } } +#ifdef WLMESH + cfg->wdev->wiphy->features |= NL80211_FEATURE_USERSPACE_MPM; +#endif /* WLMESH */ + err = __wl_cfg80211_up(cfg); if (unlikely(err)) WL_ERR(("__wl_cfg80211_up failed\n")); @@ -16694,7 +17083,7 @@ int wl_cfg80211_hang(struct net_device *dev, u16 reason) s32 wl_cfg80211_down(struct net_device *dev) { struct bcm_cfg80211 *cfg = wl_get_cfg(dev); - s32 err = 0; + s32 err; WL_DBG(("In\n")); if (cfg == NULL) @@ -17512,13 +17901,14 @@ wl_cfg80211_get_best_channel(struct net_device *ndev, void *buf, int buflen, ret = wldev_ioctl_get(ndev, WLC_GET_CHANNEL_SEL, &chosen, sizeof(chosen)); if ((ret == 0) && (dtoh32(chosen) != 0)) { chip = dhd_conf_get_chip(dhd_get_pub(ndev)); - if (chip != BCM43362_CHIP_ID && chip != BCM4330_CHIP_ID) { + if (chip != BCM43362_CHIP_ID && chip != BCM4330_CHIP_ID && + chip != BCM43143_CHIP_ID) { u32 chanspec = 0; int ctl_chan; chanspec = wl_chspec_driver_to_host(chosen); - printf("selected chanspec = 0x%x\n", chanspec); + WL_INFORM(("selected chanspec = 0x%x\n", chanspec)); ctl_chan = wf_chspec_ctlchan(chanspec); - printf("selected ctl_chan = %d\n", ctl_chan); + WL_INFORM(("selected ctl_chan = %d\n", ctl_chan)); *channel = (u16)(ctl_chan & 0x00FF); } else *channel = (u16)(chosen & 0x00FF); diff --git a/bcmdhd.1.579.77.41.1.cn/wl_cfg80211.h b/bcmdhd.1.579.77.41.1.cn/wl_cfg80211.h index 9d06534..13c08cd 100644 --- a/bcmdhd.1.579.77.41.1.cn/wl_cfg80211.h +++ b/bcmdhd.1.579.77.41.1.cn/wl_cfg80211.h @@ -205,6 +205,11 @@ do { \ #define IEEE80211_BAND_5GHZ NL80211_BAND_5GHZ #define IEEE80211_NUM_BANDS NUM_NL80211_BANDS #endif +#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0)) +#ifdef WLMESH +#undef WLMESH +#endif +#endif #define WL_SCAN_RETRY_MAX 3 #define WL_NUM_PMKIDS_MAX MAXPMKID @@ -339,7 +344,10 @@ enum wl_status { enum wl_mode { WL_MODE_BSS, WL_MODE_IBSS, - WL_MODE_AP + WL_MODE_AP, +#ifdef WLMESH + WL_MODE_MESH +#endif }; /* driver profile list */ @@ -735,7 +743,7 @@ struct bcm_cfg80211 { bool pwr_save; bool roam_on; /* on/off switch for self-roaming */ bool scan_tried; /* indicates if first scan attempted */ -#if defined(BCMSDIO) || defined(BCMPCIE) +#if defined(BCMSDIO) || defined(BCMDBUS) bool wlfc_on; #endif bool vsdb_mode; @@ -846,6 +854,10 @@ struct bcm_cfg80211 { #ifdef STAT_REPORT void *stat_report_info; #endif +#ifdef WLMESH + char sae_password[SAE_MAX_PASSWD_LEN]; + uint sae_password_len; +#endif /* WLMESH */ int p2p_disconnected; // terence 20130703: Fix for wrong group_capab (timing issue) struct ether_addr disconnected_bssid; }; @@ -1462,6 +1474,9 @@ extern s32 wl_cfg80211_set_wps_p2p_ie(struct net_device *net, char *buf, int len extern s32 wl_cfg80211_set_p2p_ps(struct net_device *net, char* buf, int len); extern s32 wl_cfg80211_set_p2p_ecsa(struct net_device *net, char* buf, int len); extern s32 wl_cfg80211_increase_p2p_bw(struct net_device *net, char* buf, int len); +#ifdef WLMESH +extern s32 wl_cfg80211_set_sae_password(struct net_device *net, char* buf, int len); +#endif #ifdef WL11ULB extern s32 wl_cfg80211_set_ulb_mode(struct net_device *dev, int mode); extern s32 wl_cfg80211_set_ulb_bw(struct net_device *dev, diff --git a/bcmdhd.1.579.77.41.1.cn/wl_cfgp2p.c b/bcmdhd.1.579.77.41.1.cn/wl_cfgp2p.c index f0cf56e..6d775f7 100644 --- a/bcmdhd.1.579.77.41.1.cn/wl_cfgp2p.c +++ b/bcmdhd.1.579.77.41.1.cn/wl_cfgp2p.c @@ -56,6 +56,7 @@ #include #include #include +#include #if defined(BCMPCIE) && defined(DHD_FW_COREDUMP) extern int dhd_bus_mem_dump(dhd_pub_t *dhd); @@ -1742,6 +1743,8 @@ wl_cfgp2p_supported(struct bcm_cfg80211 *cfg, struct net_device *ndev) return ret; } } + if (cfg->pub->conf->fw_type == FW_TYPE_MESH) + p2p_supported = 0; if (p2p_supported == 1) { CFGP2P_INFO(("p2p is supported\n")); } else { @@ -1750,6 +1753,7 @@ wl_cfgp2p_supported(struct bcm_cfg80211 *cfg, struct net_device *ndev) } return p2p_supported; } + /* Cleanup P2P resources */ s32 wl_cfgp2p_down(struct bcm_cfg80211 *cfg) diff --git a/bcmdhd.1.579.77.41.1.cn/wl_cfgvendor.c b/bcmdhd.1.579.77.41.1.cn/wl_cfgvendor.c index 8806a5f..bd918e6 100644 --- a/bcmdhd.1.579.77.41.1.cn/wl_cfgvendor.c +++ b/bcmdhd.1.579.77.41.1.cn/wl_cfgvendor.c @@ -252,7 +252,7 @@ wl_cfgvendor_set_country(struct wiphy *wiphy, int err = BCME_ERROR, rem, type; char country_code[WLC_CNTRY_BUF_SZ] = {0}; const struct nlattr *iter; - WL_ERR(("enter wl_cfgvendor_set_country: \n")); + nla_for_each_attr(iter, data, len, rem) { type = nla_type(iter); switch (type) { @@ -267,7 +267,6 @@ wl_cfgvendor_set_country(struct wiphy *wiphy, } err = wldev_set_country(wdev->netdev, country_code, true, true, -1); - WL_ERR(("Set country code ret:%d\n", err)); if (err < 0) { WL_ERR(("Set country failed ret:%d\n", err)); } diff --git a/bcmdhd.1.579.77.41.1.cn/wl_iw.c b/bcmdhd.1.579.77.41.1.cn/wl_iw.c index a817c01..0c6b395 100644 --- a/bcmdhd.1.579.77.41.1.cn/wl_iw.c +++ b/bcmdhd.1.579.77.41.1.cn/wl_iw.c @@ -3299,6 +3299,7 @@ wl_iw_event(struct net_device *dev, wl_event_msg_t *e, void* data) uint16 flags = ntoh16(e->flags); uint32 datalen = ntoh32(e->datalen); uint32 status = ntoh32(e->status); + uint32 reason = ntoh32(e->reason); memset(&wrqu, 0, sizeof(wrqu)); memset(extra, 0, sizeof(extra)); @@ -3328,12 +3329,12 @@ wl_iw_event(struct net_device *dev, wl_event_msg_t *e, void* data) cmd = SIOCGIWAP; wrqu.data.length = strlen(extra); if (!(flags & WLC_EVENT_MSG_LINK)) { - printf("%s: Link Down with BSSID="MACSTR"\n", __FUNCTION__, - MAC2STR((u8 *)wrqu.addr.sa_data)); + printf("%s: Link Down with "MACSTR", reason=%d\n", __FUNCTION__, + MAC2STR((u8 *)wrqu.addr.sa_data), reason); bzero(wrqu.addr.sa_data, ETHER_ADDR_LEN); bzero(&extra, ETHER_ADDR_LEN); } else { - printf("%s: Link UP with BSSID="MACSTR"\n", __FUNCTION__, + printf("%s: Link UP with "MACSTR"\n", __FUNCTION__, MAC2STR((u8 *)wrqu.addr.sa_data)); } break; -- 2.20.1