# 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
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;
*/
bcmsdh_osinfo->oob_irq_wake_enabled = TRUE;
#endif
+
return 0;
}
free_irq(bcmsdh_osinfo->oob_irq_num, bcmsdh);
bcmsdh_osinfo->oob_irq_registered = FALSE;
}
-#endif
+#endif
/* Module parameters specific to each host-controller driver */
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);
}
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__));
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);
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__);
--- /dev/null
+/** @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.
+ *
+ *
+ * <<Broadcom-WL-IPTag/Open:>>
+ *
+ * $Id: dbus.c 553311 2015-04-29 10:23:08Z $
+ */
+
+
+#include "osl.h"
+#include "dbus.h"
+#include <bcmutils.h>
+
+#if defined(BCM_REQUEST_FW)
+#include <bcmsrom_fmt.h>
+#include <trxhdr.h>
+#include <usbrdl.h>
+#include <bcmendian.h>
+#include <sbpcmcia.h>
+#include <bcmnvram.h>
+#include <bcmdevs.h>
+#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 */
--- /dev/null
+/*
+ * 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.
+ *
+ *
+ * <<Broadcom-WL-IPTag/Open:>>
+ *
+ * $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 <osl.h>
+#include <bcmdefs.h>
+#include <bcmutils.h>
+#include <dbus.h>
+#include <usbrdl.h>
+#include <bcmdevs.h>
+#include <bcmendian.h>
+
+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_<os>.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 */
--- /dev/null
+/*
+ * 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.
+ *
+ *
+ * <<Broadcom-WL-IPTag/Open:>>
+ *
+ * $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 <typedefs.h>
+#include <osl.h>
+
+/**
+ * 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 <usbrdl.h>
+#include <bcmendian.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/random.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <asm/uaccess.h>
+#include <asm/unaligned.h>
+#include <dbus.h>
+#include <bcmutils.h>
+#include <bcmdevs.h>
+#include <linux/usb.h>
+#include <usbrdl.h>
+#include <linux/firmware.h>
+
+#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 <linux/kthread.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <asm/hardirq.h>
+#include <linux/list.h>
+#include <linux_osl.h>
+#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 */
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_HAS_WAKELOCK)
#include <linux/wakelock.h>
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined (CONFIG_HAS_WAKELOCK) */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
+#include <linux/sched/types.h>
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) */
/* The kernel threading is sdio-specific */
struct task_struct;
struct sched_param;
#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
* 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) */
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);
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
/* 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);
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);
#include <dngl_stats.h>
#include <dhd.h>
#include <dhd_proto.h>
+#ifdef BCMDBUS
+#include <dbus.h>
+#else
#include <dhd_bus.h>
+#endif /* BCMDBUS */
#include <dhd_dbg.h>
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);
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;
}
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;
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;
}
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)
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 */
#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
#include <dhd_flowring.h>
#endif
+#ifdef BCMDBUS
+#include <dbus.h>
+#else
#include <dhd_bus.h>
+#endif /* BCMDBUS */
#include <dhd_proto.h>
#include <dhd_config.h>
#include <bcmsdbus.h>
int
dhd_common_socram_dump(dhd_pub_t *dhdp)
{
+#ifdef BCMDBUS
+ return -1;
+#else
return dhd_socram_dump(dhdp->bus);
+#endif /* BCMDBUS */
}
static int
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)
{
return dhd_iovar(dhd, 0, "cons", msg, msglen, NULL, 0, TRUE);
}
-#endif /* DHD_DEBUG && BCMDHDUSB */
+#endif /* DHD_DEBUG && BCMDBUS */
#ifdef DHD_DEBUG
int
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) {
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);
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;
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
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);
dhd_bus_dump_trap_info(dhd_pub->bus, &strbuf);
break;
}
-#ifdef DHD_DEBUG
-#if defined(BCMSDIO) || defined(BCMPCIE)
case IOV_GVAL(IOV_BPADDR):
{
/* 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;
#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 */
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__));
}
break;
#endif
+#endif /* !BCMDBUS */
#ifdef DHD_WMF
case WLC_E_PSTA_PRIMARY_INTF_IND:
dhd_update_psta_interface_for_sta(dhd_pub, event->ifname,
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",
}
}
+ 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));
#define MAXSZ_BUF 1000\r
#define MAXSZ_CONFIG 4096\r
\r
-#define FW_TYPE_STA 0\r
-#define FW_TYPE_APSTA 1\r
-#define FW_TYPE_P2P 2\r
-#define FW_TYPE_ES 3\r
-#define FW_TYPE_MFG 4\r
-#define FW_TYPE_G 0\r
-#define FW_TYPE_AG 1\r
-\r
-#ifdef CONFIG_PATH_AUTO_SELECT\r
-#ifdef BCMSDIO\r
-#define CONFIG_BCM4330B2 "config_40183b2.txt"\r
-#define CONFIG_BCM43362A0 "config_40181a0.txt"\r
-#define CONFIG_BCM43362A2 "config_40181a2.txt"\r
-#define CONFIG_BCM43438A0 "config_43438a0.txt"\r
-#define CONFIG_BCM43438A1 "config_43438a1.txt"\r
-#define CONFIG_BCM43436B0 "config_43436b0.txt"\r
-#define CONFIG_BCM4334B1 "config_4334b1.txt"\r
-#define CONFIG_BCM43341B0 "config_43341b0.txt"\r
-#define CONFIG_BCM43241B4 "config_43241b4.txt"\r
-#define CONFIG_BCM4339A0 "config_4339a0.txt"\r
-#define CONFIG_BCM43454C0 "config_43454c0.txt"\r
-#define CONFIG_BCM43455C0 "config_43455c0.txt"\r
-#define CONFIG_BCM43456C5 "config_43456c5.txt"\r
-#define CONFIG_BCM4354A1 "config_4354a1.txt"\r
-#endif\r
-#define CONFIG_BCM4356A2 "config_4356a2.txt"\r
-#define CONFIG_BCM4358A3 "config_4358a3.txt"\r
-#define CONFIG_BCM4359B1 "config_4359b1.txt"\r
-#define CONFIG_BCM4359C0 "config_4359c0.txt"\r
-#endif\r
-\r
-#ifdef BCMSDIO\r
-#define SBSDIO_CIS_SIZE_LIMIT 0x200\r
-\r
-#define FW_BCM4330B2 "fw_bcm40183b2"\r
-#define FW_BCM4330B2_AG "fw_bcm40183b2_ag"\r
-#define FW_BCM43362A0 "fw_bcm40181a0"\r
-#define FW_BCM43362A2 "fw_bcm40181a2"\r
-#define FW_BCM4334B1 "fw_bcm4334b1_ag"\r
-#define FW_BCM43438A0 "fw_bcm43438a0"\r
-#define FW_BCM43438A1 "fw_bcm43438a1"\r
-#define FW_BCM43436B0 "fw_bcm43436b0"\r
-#define FW_BCM43013B0 "fw_bcm43013b0"\r
-#define FW_BCM43341B1 "fw_bcm43341b0_ag"\r
-#define FW_BCM43241B4 "fw_bcm43241b4_ag"\r
-#define FW_BCM4339A0 "fw_bcm4339a0_ag"\r
-#define FW_BCM43455C0 "fw_bcm43455c0_ag"\r
-#define FW_BCM43456C5 "fw_bcm43456c5_ag"\r
-#define FW_BCM4354A1 "fw_bcm4354a1_ag"\r
-#define FW_BCM4356A2 "fw_bcm4356a2_ag"\r
-#define FW_BCM4358A3 "fw_bcm4358a3_ag"\r
-#define FW_BCM4359B1 "fw_bcm4359b1_ag"\r
-#define FW_BCM4359C0 "fw_bcm4359c0_ag"\r
-#define FW_BCM43751 "fw_bcm43751_ag"\r
-\r
-#define CLM_BCM43013B0 "clm_bcm43013b0"\r
-#endif\r
-#ifdef BCMPCIE\r
-#define FW_BCM4356A2 "fw_bcm4356a2_pcie_ag"\r
-#define FW_BCM4359C0 "fw_bcm4359c0_pcie_ag"\r
-#endif\r
-\r
#define htod32(i) i\r
#define htod16(i) i\r
#define dtoh32(i) i\r
#define htodchanspec(i) i\r
#define dtohchanspec(i) i\r
\r
+typedef struct cihp_name_map_t {\r
+ uint chip;\r
+ uint chiprev;\r
+ uint ag_type;\r
+ bool clm;\r
+ char *chip_name;\r
+ char *module_name;\r
+} cihp_name_map_t;\r
+\r
+/* Map of WLC_E events to connection failure strings */\r
+#define DONT_CARE 9999\r
+const cihp_name_map_t chip_name_map [] = {\r
+ /* ChipID Chiprev AG CLM ChipName ModuleName */\r
+#ifdef BCMSDIO\r
+ {BCM43362_CHIP_ID, 0, DONT_CARE, FALSE, "bcm40181a0", ""},\r
+ {BCM43362_CHIP_ID, 1, DONT_CARE, FALSE, "bcm40181a2", ""},\r
+ {BCM4330_CHIP_ID, 4, FW_TYPE_G, FALSE, "bcm40183b2", ""},\r
+ {BCM4330_CHIP_ID, 4, FW_TYPE_AG, FALSE, "bcm40183b2_ag", ""},\r
+ {BCM43430_CHIP_ID, 0, DONT_CARE, FALSE, "bcm43438a0", ""},\r
+ {BCM43430_CHIP_ID, 1, DONT_CARE, FALSE, "bcm43438a1", ""},\r
+ {BCM43430_CHIP_ID, 2, DONT_CARE, FALSE, "bcm43436b0", ""},\r
+ {BCM43012_CHIP_ID, 1, DONT_CARE, TRUE, "bcm43013b0", ""},\r
+ {BCM4334_CHIP_ID, 3, DONT_CARE, FALSE, "bcm4334b1_ag", ""},\r
+ {BCM43340_CHIP_ID, 2, DONT_CARE, FALSE, "bcm43341b0_ag", ""},\r
+ {BCM43341_CHIP_ID, 2, DONT_CARE, FALSE, "bcm43341b0_ag", ""},\r
+ {BCM4324_CHIP_ID, 5, DONT_CARE, FALSE, "bcm43241b4_ag", ""},\r
+ {BCM4335_CHIP_ID, 2, DONT_CARE, FALSE, "bcm4339a0_ag", ""},\r
+ {BCM4339_CHIP_ID, 1, DONT_CARE, FALSE, "bcm4339a0_ag", ""},\r
+ {BCM4345_CHIP_ID, 6, DONT_CARE, FALSE, "bcm43455c0_ag", ""},\r
+ {BCM43454_CHIP_ID, 6, DONT_CARE, FALSE, "bcm43455c0_ag", ""},\r
+ {BCM4345_CHIP_ID, 9, DONT_CARE, FALSE, "bcm43456c5_ag", ""},\r
+ {BCM43454_CHIP_ID, 9, DONT_CARE, FALSE, "bcm43455c5_ag", ""},\r
+ {BCM4354_CHIP_ID, 1, DONT_CARE, FALSE, "bcm4354a1_ag", ""},\r
+ {BCM4354_CHIP_ID, 2, DONT_CARE, FALSE, "bcm4356a2_ag", ""},\r
+ {BCM4356_CHIP_ID, 2, DONT_CARE, FALSE, "bcm4356a2_ag", ""},\r
+ {BCM4371_CHIP_ID, 2, DONT_CARE, FALSE, "bcm4356a2_ag", ""},\r
+ {BCM43569_CHIP_ID, 3, DONT_CARE, FALSE, "bcm4358a3_ag", ""},\r
+ {BCM4359_CHIP_ID, 5, DONT_CARE, FALSE, "bcm4359b1_ag", ""},\r
+ {BCM4359_CHIP_ID, 9, DONT_CARE, FALSE, "bcm4359c0_ag", ""},\r
+ {BCM4362_CHIP_ID, 0, DONT_CARE, TRUE, "bcm43752a0_ag", ""},\r
+#endif\r
+#ifdef BCMPCIE\r
+ {BCM4354_CHIP_ID, 2, DONT_CARE, FALSE, "bcm4356a2_pcie_ag", ""},\r
+ {BCM4356_CHIP_ID, 2, DONT_CARE, FALSE, "bcm4356a2_pcie_ag", ""},\r
+ {BCM4359_CHIP_ID, 9, DONT_CARE, FALSE, "bcm4359c0_pcie_ag", ""},\r
+ {BCM4362_CHIP_ID, 0, DONT_CARE, TRUE, "bcm43752a0_pcie_ag", ""},\r
+#endif\r
+};\r
+\r
#ifdef BCMSDIO\r
void\r
dhd_conf_free_mac_list(wl_mac_list_ctrl_t *mac_list)\r
}\r
#endif\r
\r
+#define SBSDIO_CIS_SIZE_LIMIT 0x200\r
#define F0_BLOCK_SIZE 32\r
int\r
dhd_conf_set_blksize(bcmsdh_info_t *sdh)\r
uint8 cisd;\r
\r
numfn = bcmsdh_query_iofnum(sdh);\r
- \r
+\r
for (fn = 0; fn <= numfn; fn++) {\r
if (!fn)\r
blksize = F0_BLOCK_SIZE;\r
uint32 oui, nic;\r
wl_mac_list_t *mac_list;\r
wl_mac_range_t *mac_range;\r
- char *pfw_name;\r
int fw_type, fw_type_new;\r
+ char *name_ptr;\r
\r
mac_list = dhd->conf->fw_by_mac.m_mac_list_head;\r
fw_num = dhd->conf->fw_by_mac.count;\r
/* find out the last '/' */\r
i = strlen(fw_path);\r
while (i > 0) {\r
- if (fw_path[i] == '/') break;\r
+ if (fw_path[i] == '/') {\r
+ i++;\r
+ break;\r
+ }\r
i--;\r
}\r
- pfw_name = &fw_path[i+1];\r
- fw_type = (strstr(pfw_name, "_mfg") ?\r
- FW_TYPE_MFG : (strstr(pfw_name, "_apsta") ?\r
- FW_TYPE_APSTA : (strstr(pfw_name, "_p2p") ?\r
- FW_TYPE_P2P : FW_TYPE_STA)));\r
+ name_ptr = &fw_path[i];\r
+\r
+ if (strstr(name_ptr, "_apsta"))\r
+ fw_type = FW_TYPE_APSTA;\r
+ else if (strstr(name_ptr, "_p2p"))\r
+ fw_type = FW_TYPE_P2P;\r
+ else if (strstr(name_ptr, "_mesh"))\r
+ fw_type = FW_TYPE_MESH;\r
+ else if (strstr(name_ptr, "_es"))\r
+ fw_type = FW_TYPE_ES;\r
+ else if (strstr(name_ptr, "_mfg"))\r
+ fw_type = FW_TYPE_MFG;\r
+ else\r
+ fw_type = FW_TYPE_STA;\r
\r
for (i=0; i<fw_num; i++) {\r
mac_num = mac_list[i].count;\r
mac_range = mac_list[i].mac;\r
- fw_type_new = (strstr(mac_list[i].name, "_mfg") ?\r
- FW_TYPE_MFG : (strstr(mac_list[i].name, "_apsta") ?\r
- FW_TYPE_APSTA : (strstr(mac_list[i].name, "_p2p") ?\r
- FW_TYPE_P2P : FW_TYPE_STA)));\r
+ if (strstr(mac_list[i].name, "_apsta"))\r
+ fw_type_new = FW_TYPE_APSTA;\r
+ else if (strstr(mac_list[i].name, "_p2p"))\r
+ fw_type_new = FW_TYPE_P2P;\r
+ else if (strstr(mac_list[i].name, "_mesh"))\r
+ fw_type_new = FW_TYPE_MESH;\r
+ else if (strstr(mac_list[i].name, "_es"))\r
+ fw_type_new = FW_TYPE_ES;\r
+ else if (strstr(mac_list[i].name, "_mfg"))\r
+ fw_type_new = FW_TYPE_MFG;\r
+ else\r
+ fw_type_new = FW_TYPE_STA;\r
if (fw_type != fw_type_new) {\r
printf("%s: fw_typ=%d != fw_type_new=%d\n", __FUNCTION__, fw_type, fw_type_new);\r
continue;\r
for (j=0; j<mac_num; j++) {\r
if (oui == mac_range[j].oui) {\r
if (nic >= mac_range[j].nic_start && nic <= mac_range[j].nic_end) {\r
- strcpy(pfw_name, mac_list[i].name);\r
+ strcpy(name_ptr, mac_list[i].name);\r
printf("%s: matched oui=0x%06X, nic=0x%06X\n",\r
__FUNCTION__, oui, nic);\r
printf("%s: fw_path=%s\n", __FUNCTION__, fw_path);\r
int fw_type, ag_type;\r
uint chip, chiprev;\r
int i;\r
- char fw_tail[20];\r
+ char *name_ptr;\r
\r
chip = dhd->conf->chip;\r
chiprev = dhd->conf->chiprev;\r
}\r
i--;\r
}\r
+ name_ptr = &fw_path[i];\r
#ifdef BAND_AG\r
ag_type = FW_TYPE_AG;\r
#else\r
- ag_type = strstr(&fw_path[i], "_ag") ? FW_TYPE_AG : FW_TYPE_G;\r
+ ag_type = strstr(name_ptr, "_ag") ? FW_TYPE_AG : FW_TYPE_G;\r
#endif\r
- fw_type = (strstr(&fw_path[i], "_mfg") ? FW_TYPE_MFG :\r
- (strstr(&fw_path[i], "_apsta") ? FW_TYPE_APSTA :\r
- (strstr(&fw_path[i], "_p2p") ? FW_TYPE_P2P :\r
- (strstr(&fw_path[i], "_es") ? FW_TYPE_ES :\r
- FW_TYPE_STA))));\r
-\r
- if (fw_type == FW_TYPE_STA)\r
- strcpy(fw_tail, ".bin");\r
- else if (fw_type == FW_TYPE_APSTA)\r
- strcpy(fw_tail, "_apsta.bin");\r
- else if (fw_type == FW_TYPE_P2P)\r
- strcpy(fw_tail, "_p2p.bin");\r
- else if (fw_type == FW_TYPE_ES)\r
- strcpy(fw_tail, "_es.bin");\r
- else if (fw_type == FW_TYPE_MFG)\r
- strcpy(fw_tail, "_mfg.bin");\r
-\r
- switch (chip) {\r
-#ifdef BCMSDIO\r
- case BCM4330_CHIP_ID:\r
- if (ag_type == FW_TYPE_G) {\r
- if (chiprev == BCM4330B2_CHIP_REV)\r
- strcpy(&fw_path[i], FW_BCM4330B2);\r
- } else {\r
- if (chiprev == BCM4330B2_CHIP_REV)\r
- strcpy(&fw_path[i], FW_BCM4330B2_AG);\r
- }\r
- break;\r
- case BCM43362_CHIP_ID:\r
- if (chiprev == BCM43362A0_CHIP_REV)\r
- strcpy(&fw_path[i], FW_BCM43362A0);\r
+ if (strstr(name_ptr, "_apsta"))\r
+ fw_type = FW_TYPE_APSTA;\r
+ else if (strstr(name_ptr, "_p2p"))\r
+ fw_type = FW_TYPE_P2P;\r
+ else if (strstr(name_ptr, "_mesh"))\r
+ fw_type = FW_TYPE_MESH;\r
+ else if (strstr(name_ptr, "_es"))\r
+ fw_type = FW_TYPE_ES;\r
+ else if (strstr(name_ptr, "_mfg"))\r
+ fw_type = FW_TYPE_MFG;\r
+ else\r
+ fw_type = FW_TYPE_STA;\r
+\r
+ for (i = 0; i < sizeof(chip_name_map)/sizeof(chip_name_map[0]); i++) {\r
+ const cihp_name_map_t* row = &chip_name_map[i];\r
+ if (row->chip == chip && row->chiprev == chiprev &&\r
+ (row->ag_type == ag_type || row->ag_type == DONT_CARE)) {\r
+ strcpy(name_ptr, "fw_");\r
+ strcat(fw_path, row->chip_name);\r
+ if (fw_type == FW_TYPE_APSTA)\r
+ strcat(fw_path, "_apsta.bin");\r
+ else if (fw_type == FW_TYPE_P2P)\r
+ strcat(fw_path, "_p2p.bin");\r
+ else if (fw_type == FW_TYPE_MESH)\r
+ strcat(fw_path, "_mesh.bin");\r
+ else if (fw_type == FW_TYPE_ES)\r
+ strcat(fw_path, "_es.bin");\r
+ else if (fw_type == FW_TYPE_MFG)\r
+ strcat(fw_path, "_mfg.bin");\r
else\r
- strcpy(&fw_path[i], FW_BCM43362A2);\r
- break;\r
- case BCM43430_CHIP_ID:\r
- if (chiprev == BCM43430A0_CHIP_REV)\r
- strcpy(&fw_path[i], FW_BCM43438A0);\r
- else if (chiprev == BCM43430A1_CHIP_REV)\r
- strcpy(&fw_path[i], FW_BCM43438A1);\r
- else if (chiprev == BCM43430A2_CHIP_REV)\r
- strcpy(&fw_path[i], FW_BCM43436B0);\r
- break;\r
- case BCM43012_CHIP_ID:\r
- if (chiprev == BCM43013B0_CHIP_REV)\r
- strcpy(&fw_path[i], FW_BCM43013B0);\r
- break;\r
- case BCM4334_CHIP_ID:\r
- if (chiprev == BCM4334B1_CHIP_REV)\r
- strcpy(&fw_path[i], FW_BCM4334B1);\r
- break;\r
- case BCM43340_CHIP_ID:\r
- case BCM43341_CHIP_ID:\r
- if (chiprev == BCM43341B0_CHIP_REV)\r
- strcpy(&fw_path[i], FW_BCM43341B1);\r
- break;\r
- case BCM4324_CHIP_ID:\r
- if (chiprev == BCM43241B4_CHIP_REV)\r
- strcpy(&fw_path[i], FW_BCM43241B4);\r
- break;\r
- case BCM4335_CHIP_ID:\r
- if (chiprev == BCM4335A0_CHIP_REV)\r
- strcpy(&fw_path[i], FW_BCM4339A0);\r
- break;\r
- case BCM4339_CHIP_ID:\r
- if (chiprev == BCM4339A0_CHIP_REV)\r
- strcpy(&fw_path[i], FW_BCM4339A0);\r
- break;\r
- case BCM4345_CHIP_ID:\r
- case BCM43454_CHIP_ID:\r
- if (chiprev == BCM43455C0_CHIP_REV)\r
- strcpy(&fw_path[i], FW_BCM43455C0);\r
- else if (chiprev == BCM43456C5_CHIP_REV)\r
- strcpy(&fw_path[i], FW_BCM43456C5);\r
- break;\r
- case BCM4354_CHIP_ID:\r
- if (chiprev == BCM4354A1_CHIP_REV)\r
- strcpy(&fw_path[i], FW_BCM4354A1);\r
- else if (chiprev == BCM4356A2_CHIP_REV)\r
- strcpy(&fw_path[i], FW_BCM4356A2);\r
- break;\r
- case BCM4356_CHIP_ID:\r
- case BCM4371_CHIP_ID:\r
- if (chiprev == BCM4356A2_CHIP_REV)\r
- strcpy(&fw_path[i], FW_BCM4356A2);\r
- break;\r
- case BCM43569_CHIP_ID:\r
- if (chiprev == BCM4358A3_CHIP_REV)\r
- strcpy(&fw_path[i], FW_BCM4358A3);\r
- break;\r
- case BCM4359_CHIP_ID:\r
- if (chiprev == BCM4359B1_CHIP_REV)\r
- strcpy(&fw_path[i], FW_BCM4359B1);\r
- else if (chiprev == BCM4359C0_CHIP_REV)\r
- strcpy(&fw_path[i], FW_BCM4359C0);\r
- break;\r
- case BCM43751_CHIP_ID:\r
- strcpy(&fw_path[i], FW_BCM43751);\r
- break;\r
-#endif\r
-#ifdef BCMPCIE\r
- case BCM4354_CHIP_ID:\r
- case BCM4356_CHIP_ID:\r
- if (chiprev == BCM4356A2_CHIP_REV)\r
- strcpy(&fw_path[i], FW_BCM4356A2);\r
- break;\r
- case BCM4359_CHIP_ID:\r
- if (chiprev == BCM4359C0_CHIP_REV)\r
- strcpy(&fw_path[i], FW_BCM4359C0);\r
- break;\r
-#endif\r
- default:\r
- strcpy(&fw_path[i], "fw_bcmdhd");\r
+ strcat(fw_path, ".bin");\r
+ }\r
}\r
- strcat(fw_path, fw_tail);\r
+\r
+ dhd->conf->fw_type = fw_type;\r
\r
CONFIG_TRACE(("%s: firmware_path=%s\n", __FUNCTION__, fw_path));\r
}\r
{\r
uint chip, chiprev;\r
int i;\r
- char fw_tail[20];\r
+ char *name_ptr;\r
\r
chip = dhd->conf->chip;\r
chiprev = dhd->conf->chiprev;\r
}\r
i--;\r
}\r
+ name_ptr = &clm_path[i];\r
\r
- strcpy(fw_tail, ".blob");\r
-\r
- switch (chip) {\r
-#ifdef BCMSDIO\r
- case BCM43012_CHIP_ID:\r
- if (chiprev == BCM43013B0_CHIP_REV)\r
- strcpy(&clm_path[i], CLM_BCM43013B0);\r
- break;\r
-#endif\r
- default:\r
- strcpy(&clm_path[i], "clm_bcmdhd");\r
+ for (i = 0; i < sizeof(chip_name_map)/sizeof(chip_name_map[0]); i++) {\r
+ const cihp_name_map_t* row = &chip_name_map[i];\r
+ if (row->chip == chip && row->chiprev == chiprev && row->clm) {\r
+ strcpy(name_ptr, "clm_");\r
+ strcat(clm_path, row->chip_name);\r
+ strcat(clm_path, ".blob");\r
+ }\r
}\r
- strcat(clm_path, fw_tail);\r
\r
CONFIG_TRACE(("%s: clm_path=%s\n", __FUNCTION__, clm_path));\r
}\r
void\r
dhd_conf_set_nv_name_by_chip(dhd_pub_t *dhd, char *nv_path)\r
{\r
- int matched=-1;\r
uint chip, chiprev;\r
int i;\r
+ char *name_ptr;\r
\r
chip = dhd->conf->chip;\r
chiprev = dhd->conf->chiprev;\r
\r
- for (i=0; i<dhd->conf->nv_by_chip.count; i++) {\r
- if (chip==dhd->conf->nv_by_chip.m_chip_nv_path_head[i].chip &&\r
- chiprev==dhd->conf->nv_by_chip.m_chip_nv_path_head[i].chiprev) {\r
- matched = i;\r
- break;\r
- }\r
- }\r
- if (matched < 0)\r
- return;\r
-\r
if (nv_path[0] == '\0') {\r
#ifdef CONFIG_BCMDHD_NVRAM_PATH\r
bcm_strncpy_s(nv_path, MOD_PARAM_PATHLEN-1, CONFIG_BCMDHD_NVRAM_PATH, MOD_PARAM_PATHLEN-1);\r
}\r
i--;\r
}\r
+ name_ptr = &nv_path[i];\r
+\r
+ for (i = 0; i < sizeof(chip_name_map)/sizeof(chip_name_map[0]); i++) {\r
+ const cihp_name_map_t* row = &chip_name_map[i];\r
+ if (row->chip == chip && row->chiprev == chiprev && strlen(row->module_name)) {\r
+ strcpy(name_ptr, row->module_name);\r
+ }\r
+ }\r
\r
- strcpy(&nv_path[i], dhd->conf->nv_by_chip.m_chip_nv_path_head[matched].name);\r
+ for (i=0; i<dhd->conf->nv_by_chip.count; i++) {\r
+ if (chip==dhd->conf->nv_by_chip.m_chip_nv_path_head[i].chip &&\r
+ chiprev==dhd->conf->nv_by_chip.m_chip_nv_path_head[i].chiprev) {\r
+ strcpy(name_ptr, dhd->conf->nv_by_chip.m_chip_nv_path_head[i].name);\r
+ break;\r
+ }\r
+ }\r
\r
CONFIG_TRACE(("%s: nvram_path=%s\n", __FUNCTION__, nv_path));\r
}\r
{\r
uint chip, chiprev;\r
int i;\r
+ char *name_ptr;\r
\r
chip = dhd->conf->chip;\r
chiprev = dhd->conf->chiprev;\r
}\r
i--;\r
}\r
+ name_ptr = conf_path[i];\r
\r
- switch (chip) {\r
-#ifdef BCMSDIO\r
- case BCM4330_CHIP_ID:\r
- if (chiprev == BCM4330B2_CHIP_REV)\r
- strcpy(&conf_path[i], CONFIG_BCM4330B2);\r
- break;\r
- case BCM43362_CHIP_ID:\r
- if (chiprev == BCM43362A0_CHIP_REV)\r
- strcpy(&conf_path[i], CONFIG_BCM43362A0);\r
- else\r
- strcpy(&conf_path[i], CONFIG_BCM43362A2);\r
- break;\r
- case BCM43430_CHIP_ID:\r
- if (chiprev == BCM43430A0_CHIP_REV)\r
- strcpy(&conf_path[i], CONFIG_BCM43438A0);\r
- else if (chiprev == BCM43430A1_CHIP_REV)\r
- strcpy(&conf_path[i], CONFIG_BCM43438A1);\r
- else if (chiprev == BCM43430A2_CHIP_REV)\r
- strcpy(&conf_path[i], CONFIG_BCM43436B0);\r
- break;\r
- case BCM4334_CHIP_ID:\r
- if (chiprev == BCM4334B1_CHIP_REV)\r
- strcpy(&conf_path[i], CONFIG_BCM4334B1);\r
- break;\r
- case BCM43340_CHIP_ID:\r
- case BCM43341_CHIP_ID:\r
- if (chiprev == BCM43341B0_CHIP_REV)\r
- strcpy(&conf_path[i], CONFIG_BCM43341B0);\r
- break;\r
- case BCM4324_CHIP_ID:\r
- if (chiprev == BCM43241B4_CHIP_REV)\r
- strcpy(&conf_path[i], CONFIG_BCM43241B4);\r
- break;\r
- case BCM4335_CHIP_ID:\r
- if (chiprev == BCM4335A0_CHIP_REV)\r
- strcpy(&conf_path[i], CONFIG_BCM4339A0);\r
- break;\r
- case BCM43454_CHIP_ID:\r
- if (chiprev == BCM43455C0_CHIP_REV)\r
- strcpy(&conf_path[i], CONFIG_BCM43454C0);\r
- break;\r
- case BCM4345_CHIP_ID:\r
- if (chiprev == BCM43455C0_CHIP_REV)\r
- strcpy(&conf_path[i], CONFIG_BCM43455C0);\r
- else if (chiprev == BCM43456C5_CHIP_REV)\r
- strcpy(&conf_path[i], CONFIG_BCM43456C5);\r
- break;\r
- case BCM4339_CHIP_ID:\r
- if (chiprev == BCM4339A0_CHIP_REV)\r
- strcpy(&conf_path[i], CONFIG_BCM4339A0);\r
- break;\r
- case BCM4354_CHIP_ID:\r
- if (chiprev == BCM4354A1_CHIP_REV)\r
- strcpy(&conf_path[i], CONFIG_BCM4354A1);\r
- else if (chiprev == BCM4356A2_CHIP_REV)\r
- strcpy(&conf_path[i], CONFIG_BCM4356A2);\r
- break;\r
- case BCM4356_CHIP_ID:\r
- case BCM4371_CHIP_ID:\r
- if (chiprev == BCM4356A2_CHIP_REV)\r
- strcpy(&conf_path[i], CONFIG_BCM4356A2);\r
- break;\r
- case BCM43569_CHIP_ID:\r
- if (chiprev == BCM4358A3_CHIP_REV)\r
- strcpy(&conf_path[i], CONFIG_BCM4358A3);\r
- break;\r
- case BCM4359_CHIP_ID:\r
- if (chiprev == BCM4359B1_CHIP_REV)\r
- strcpy(&conf_path[i], CONFIG_BCM4359B1);\r
- else if (chiprev == BCM4359C0_CHIP_REV)\r
- strcpy(&conf_path[i], CONFIG_BCM4359C0);\r
- break;\r
-#endif\r
-#ifdef BCMPCIE\r
- case BCM4354_CHIP_ID:\r
- case BCM4356_CHIP_ID:\r
- if (chiprev == BCM4356A2_CHIP_REV)\r
- strcpy(&conf_path[i], CONFIG_BCM4356A2);\r
- break;\r
- case BCM4359_CHIP_ID:\r
- if (chiprev == BCM4359C0_CHIP_REV)\r
- strcpy(&conf_path[i], CONFIG_BCM4359C0);\r
- break;\r
-#endif\r
+ for (i = 0; i < sizeof(chip_name_map)/sizeof(chip_name_map[0]); i++) {\r
+ const cihp_name_map_t* row = &chip_name_map[i];\r
+ if (row->chip == chip && row->chiprev == chiprev) {\r
+ strcpy(name_ptr, "config_");\r
+ strcat(conf_path, row->chip_name);\r
+ strcat(conf_path, ".txt");\r
+ }\r
}\r
\r
CONFIG_TRACE(("%s: config_path=%s\n", __FUNCTION__, conf_path));\r
CONFIG_ERROR(("%s: WLC_DOWN setting failed %d\n", __FUNCTION__, ret));\r
}\r
if (cmd == WLC_SET_VAR) {\r
- printf("%s: set %s %d\n", __FUNCTION__, name, val);\r
+ CONFIG_TRACE(("%s: set %s %d\n", __FUNCTION__, name, val));\r
bcm_mkiovar(name, (char *)&val, sizeof(val), iovbuf, sizeof(iovbuf));\r
if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0)\r
CONFIG_ERROR(("%s: %s setting failed %d\n", __FUNCTION__, name, ret));\r
} else {\r
- printf("%s: set %s %d %d\n", __FUNCTION__, name, cmd, val);\r
+ CONFIG_TRACE(("%s: set %s %d %d\n", __FUNCTION__, name, cmd, val));\r
if ((ret = dhd_wl_ioctl_cmd(dhd, cmd, &val, sizeof(val), TRUE, 0)) < 0)\r
CONFIG_ERROR(("%s: %s setting failed %d\n", __FUNCTION__, name, ret));\r
}\r
uint\r
dhd_conf_get_band(dhd_pub_t *dhd)\r
{\r
- uint band = WLC_BAND_AUTO;\r
+ int band = -1;\r
\r
if (dhd && dhd->conf)\r
band = dhd->conf->band;\r
#define MACS "%02x%02x%02x%02x%02x%02x"\r
\r
/*\r
- * 1. Filter out all pkt: actually not to enable this since 4-way handshake will be filter out as well.\r
- * 1) dhd_master_mode=0\r
- * 2) pkt_filter_add=99 0 0 0 0x000000000000 0x000000000000\r
- * 2. Filter in less pkt: ARP(0x0806, ID is 105), BRCM(0x886C), 802.1X(0x888E)\r
+ * Filter in less pkt: ARP(0x0806, ID is 105), BRCM(0x886C), 802.1X(0x888E)\r
* 1) dhd_master_mode=1\r
* 2) pkt_filter_del=100, 102, 103, 104, 105\r
* 3) pkt_filter_add=131 0 0 12 0xFFFF 0x886C, 132 0 0 12 0xFFFF 0x888E\r
- * 3. magic pkt: magic_pkt_filter_add=141 0 1 12\r
- * 4. Filter out netbios pkt:\r
- * Netbios: 121 0 0 12 0xFFFF000000000000000000FF000000000000000000000000FFFF 0x0800000000000000000000110000000000000000000000000089\r
+ * 4) magic_pkt_filter_add=141 0 1 12\r
*/\r
for(i=0; i<dhd->conf->pkt_filter_add.count; i++) {\r
dhd->pktfilter[i+dhd->pktfilter_count] = dhd->conf->pkt_filter_add.filter[i];\r
int\r
dhd_conf_get_pm(dhd_pub_t *dhd)\r
{\r
- if (dhd && dhd->conf)\r
- return dhd->conf->pm;\r
+ if (dhd && dhd->conf) {\r
+ if (dhd->conf->fw_type == FW_TYPE_MESH)\r
+ return PM_OFF;\r
+ else\r
+ return dhd->conf->pm;\r
+ }\r
return -1;\r
}\r
\r
sd_msglevel = (int)simple_strtol(data, NULL, 0);\r
printf("%s: sd_msglevel = 0x%X\n", __FUNCTION__, sd_msglevel);\r
}\r
+#endif\r
+#ifdef BCMDBUS\r
+ else if (!strncmp("dbus_msglevel=", full_param, len_param)) {\r
+ dbus_msglevel = (int)simple_strtol(data, NULL, 0);\r
+ printf("%s: dbus_msglevel = 0x%X\n", __FUNCTION__, dbus_msglevel);\r
+ }\r
#endif\r
else if (!strncmp("android_msg_level=", full_param, len_param)) {\r
android_msg_level = (int)simple_strtol(data, NULL, 0);\r
dhd_conf_read_country_list(dhd_pub_t *dhd, char *full_param, uint len_param)\r
{\r
int i;\r
- char *pch, *pick_tmp;\r
+ char *pch, *pick_tmp, *pick_tmp2;\r
struct dhd_conf *conf = dhd->conf;\r
char *data = full_param+len_param;\r
wl_country_t *cspec;\r
+ conf_country_list_t *country_list = NULL;\r
\r
/* Process country_list:\r
* country_list=[country1]:[ccode1]/[regrev1],\r
* Ex: country_list=US:US/0, TW:TW/1\r
*/\r
if (!strncmp("country_list=", full_param, len_param)) {\r
+ country_list = &dhd->conf->country_list;\r
+ } else if (!strncmp("country_list_nodfs=", full_param, len_param)) {\r
+ country_list = &dhd->conf->country_list_nodfs;\r
+ }\r
+ if (country_list) {\r
pick_tmp = data;\r
for (i=0; i<CONFIG_COUNTRY_LIST_SIZE; i++) {\r
- /* Process country code */\r
- pch = bcmstrtok(&pick_tmp, ":", 0);\r
+ pick_tmp2 = bcmstrtok(&pick_tmp, ", ", 0);\r
+ if (!pick_tmp2)\r
+ break;\r
+ pch = bcmstrtok(&pick_tmp2, ":", 0);\r
if (!pch)\r
break;\r
cspec = NULL;\r
CONFIG_ERROR(("%s: kmalloc failed\n", __FUNCTION__));\r
break;\r
}\r
- conf->country_list.count++;\r
memset(cspec, 0, sizeof(wl_country_t));\r
- conf->country_list.cspec[i] = cspec;\r
\r
strcpy(cspec->country_abbrev, pch);\r
- pch = bcmstrtok(&pick_tmp, "/", 0);\r
- if (!pch)\r
+ pch = bcmstrtok(&pick_tmp2, "/", 0);\r
+ if (!pch) {\r
+ kfree(cspec);\r
break;\r
+ }\r
memcpy(cspec->ccode, pch, 2);\r
- pch = bcmstrtok(&pick_tmp, ", ", 0);\r
- if (!pch)\r
+ pch = bcmstrtok(&pick_tmp2, "/", 0);\r
+ if (!pch) {\r
+ kfree(cspec);\r
break;\r
+ }\r
cspec->rev = (int32)simple_strtol(pch, NULL, 10);\r
+ country_list->count++;\r
+ country_list->cspec[i] = cspec;\r
CONFIG_TRACE(("%s: country_list abbrev=%s, ccode=%s, regrev=%d\n", __FUNCTION__,\r
cspec->country_abbrev, cspec->ccode, cspec->rev));\r
}\r
- printf("%s: %d country in list\n", __FUNCTION__, conf->country_list.count);\r
- }\r
- else if (!strncmp("country_list_nodfs=", full_param, len_param)) {\r
- pick_tmp = data;\r
- for (i=0; i<CONFIG_COUNTRY_LIST_SIZE; i++) {\r
- /* Process country code */\r
- pch = bcmstrtok(&pick_tmp, ":", 0);\r
- if (!pch)\r
- break;\r
- cspec = NULL;\r
- if (!(cspec = kmalloc(sizeof(wl_country_t), GFP_KERNEL))) {\r
- CONFIG_ERROR(("%s: kmalloc failed\n", __FUNCTION__));\r
- break;\r
- }\r
- conf->country_list_nodfs.count++;\r
- memset(cspec, 0, sizeof(wl_country_t));\r
- conf->country_list_nodfs.cspec[i] = cspec;\r
-\r
- strcpy(cspec->country_abbrev, pch);\r
- pch = bcmstrtok(&pick_tmp, "/", 0);\r
- if (!pch)\r
- break;\r
- memcpy(cspec->ccode, pch, 2);\r
- pch = bcmstrtok(&pick_tmp, ", ", 0);\r
- if (!pch)\r
- break;\r
- cspec->rev = (int32)simple_strtol(pch, NULL, 10);\r
- CONFIG_TRACE(("%s: country_list_nodfs abbrev=%s, ccode=%s, regrev=%d\n", __FUNCTION__,\r
- cspec->country_abbrev, cspec->ccode, cspec->rev));\r
+ if (!strncmp("country_list=", full_param, len_param)) {\r
+ printf("%s: %d country in list\n", __FUNCTION__, conf->country_list.count);\r
+ } else if (!strncmp("country_list_nodfs=", full_param, len_param)) {\r
+ printf("%s: %d nodfs country in list\n", __FUNCTION__, conf->country_list.count);\r
}\r
- printf("%s: %d nodfs country in list\n", __FUNCTION__, conf->country_list_nodfs.count);\r
}\r
else\r
return false;\r
struct dhd_conf *conf = dhd->conf;\r
char *data = full_param+len_param;\r
\r
- /* Process mchan_bw and btc_params_mgmt:\r
+ /* Process mchan_bw:\r
* mchan_bw=[val]/[any/go/gc]/[any/source/sink]\r
* Ex: mchan_bw=80/go/source, 30/gc/sink\r
*/\r
dhd_doflow = TRUE;\r
printf("%s: dhd_doflow = %d\n", __FUNCTION__, dhd_doflow);\r
}\r
- else if (!strncmp("dhd_slpauto=", full_param, len_param)) {\r
+ else if (!strncmp("dhd_slpauto=", full_param, len_param) ||\r
+ !strncmp("kso_enable=", full_param, len_param)) {\r
if (!strncmp(data, "0", 1))\r
dhd_slpauto = FALSE;\r
else\r
dhd_slpauto = TRUE;\r
printf("%s: dhd_slpauto = %d\n", __FUNCTION__, dhd_slpauto);\r
}\r
- else if (!strncmp("kso_enable=", full_param, len_param)) {\r
- if (!strncmp(data, "0", 1))\r
- dhd_slpauto = FALSE;\r
- else\r
- dhd_slpauto = TRUE;\r
- printf("%s: dhd_slpauto = %d\n", __FUNCTION__, dhd_slpauto);\r
- }\r
- else if (!strncmp("bus:txglom=", full_param, len_param)) {\r
- conf->bus_txglom = (int)simple_strtol(data, NULL, 10);\r
- printf("%s: bus:txglom = %d\n", __FUNCTION__, conf->bus_txglom);\r
- }\r
else if (!strncmp("use_rxchain=", full_param, len_param)) {\r
conf->use_rxchain = (int)simple_strtol(data, NULL, 10);\r
printf("%s: use_rxchain = %d\n", __FUNCTION__, conf->use_rxchain);\r
conf->rxf_cpucore = (int)simple_strtol(data, NULL, 10);\r
printf("%s: rxf_cpucore = %d\n", __FUNCTION__, conf->rxf_cpucore);\r
}\r
+ else if (!strncmp("orphan_move=", full_param, len_param)) {\r
+ conf->orphan_move = (int)simple_strtol(data, NULL, 10);\r
+ printf("%s: orphan_move = %d\n", __FUNCTION__, conf->orphan_move);\r
+ }\r
#if defined(BCMSDIOH_TXGLOM)\r
else if (!strncmp("txglomsize=", full_param, len_param)) {\r
conf->txglomsize = (uint)simple_strtol(data, NULL, 10);\r
struct dhd_conf *conf = dhd->conf;\r
char *data = full_param+len_param;\r
\r
- if (!strncmp("lpc=", full_param, len_param)) {\r
- conf->lpc = (int)simple_strtol(data, NULL, 10);\r
- printf("%s: lpc = %d\n", __FUNCTION__, conf->lpc);\r
- }\r
- else if (!strncmp("deepsleep=", full_param, len_param)) {\r
+ if (!strncmp("deepsleep=", full_param, len_param)) {\r
if (!strncmp(data, "1", 1))\r
conf->deepsleep = TRUE;\r
else\r
conf->pm_in_suspend = (int)simple_strtol(data, NULL, 10);\r
printf("%s: pm_in_suspend = %d\n", __FUNCTION__, conf->pm_in_suspend);\r
}\r
- else if (!strncmp("pm2_sleep_ret=", full_param, len_param)) {\r
- conf->pm2_sleep_ret = (int)simple_strtol(data, NULL, 10);\r
- printf("%s: pm2_sleep_ret = %d\n", __FUNCTION__, conf->pm2_sleep_ret);\r
+ else if (!strncmp("suspend_bcn_li_dtim=", full_param, len_param)) {\r
+ conf->suspend_bcn_li_dtim = (int)simple_strtol(data, NULL, 10);\r
+ printf("%s: suspend_bcn_li_dtim = %d\n", __FUNCTION__, conf->suspend_bcn_li_dtim);\r
}\r
else if (!strncmp("xmit_in_suspend=", full_param, len_param)) {\r
if (!strncmp(data, "1", 1))\r
conf->band = WLC_BAND_AUTO;\r
printf("%s: band = %d\n", __FUNCTION__, conf->band);\r
}\r
- else if (!strncmp("mimo_bw_cap=", full_param, len_param)) {\r
- conf->mimo_bw_cap = (uint)simple_strtol(data, NULL, 10);\r
- printf("%s: mimo_bw_cap = %d\n", __FUNCTION__, conf->mimo_bw_cap);\r
- }\r
else if (!strncmp("bw_cap_2g=", full_param, len_param)) {\r
conf->bw_cap_2g = (uint)simple_strtol(data, NULL, 0);\r
printf("%s: bw_cap_2g = %d\n", __FUNCTION__, conf->bw_cap_2g);\r
}\r
else if (!strncmp("bw_cap_5g=", full_param, len_param)) {\r
conf->bw_cap_5g = (uint)simple_strtol(data, NULL, 0);\r
- printf("%s: bw_cap_2g = %d\n", __FUNCTION__, conf->bw_cap_5g);\r
+ printf("%s: bw_cap_5g = %d\n", __FUNCTION__, conf->bw_cap_5g);\r
}\r
else if (!strncmp("ccode=", full_param, len_param)) {\r
memset(&conf->cspec, 0, sizeof(wl_country_t));\r
printf("%s: keep_alive_period = %d\n", __FUNCTION__,\r
conf->keep_alive_period);\r
}\r
- else if (!strncmp("stbc=", full_param, len_param)) {\r
- conf->stbc = (int)simple_strtol(data, NULL, 10);\r
- printf("%s: stbc = %d\n", __FUNCTION__, conf->stbc);\r
- }\r
else if (!strncmp("phy_oclscdenable=", full_param, len_param)) {\r
conf->phy_oclscdenable = (int)simple_strtol(data, NULL, 10);\r
printf("%s: phy_oclscdenable = %d\n", __FUNCTION__, conf->phy_oclscdenable);\r
conf->bcn_timeout= (uint)simple_strtol(data, NULL, 10);\r
printf("%s: bcn_timeout = %d\n", __FUNCTION__, conf->bcn_timeout);\r
}\r
- else if (!strncmp("ampdu_ba_wsize=", full_param, len_param)) {\r
- conf->ampdu_ba_wsize = (int)simple_strtol(data, NULL, 10);\r
- printf("%s: ampdu_ba_wsize = %d\n", __FUNCTION__, conf->ampdu_ba_wsize);\r
- }\r
- else if (!strncmp("ampdu_hostreorder=", full_param, len_param)) {\r
- conf->ampdu_hostreorder = (int)simple_strtol(data, NULL, 10);\r
- printf("%s: ampdu_hostreorder = %d\n", __FUNCTION__, conf->ampdu_hostreorder);\r
- }\r
- else if (!strncmp("spect=", full_param, len_param)) {\r
- conf->spect = (int)simple_strtol(data, NULL, 10);\r
- printf("%s: spect = %d\n", __FUNCTION__, conf->spect);\r
- }\r
else if (!strncmp("txbf=", full_param, len_param)) {\r
conf->txbf = (int)simple_strtol(data, NULL, 10);\r
printf("%s: txbf = %d\n", __FUNCTION__, conf->txbf);\r
conf->pktprio8021x = (int)simple_strtol(data, NULL, 10);\r
printf("%s: pktprio8021x = %d\n", __FUNCTION__, conf->pktprio8021x);\r
}\r
+#if defined(BCMSDIO) || defined(BCMPCIE)\r
else if (!strncmp("dhd_txbound=", full_param, len_param)) {\r
dhd_txbound = (uint)simple_strtol(data, NULL, 10);\r
printf("%s: dhd_txbound = %d\n", __FUNCTION__, dhd_txbound);\r
dhd_rxbound = (uint)simple_strtol(data, NULL, 10);\r
printf("%s: dhd_rxbound = %d\n", __FUNCTION__, dhd_rxbound);\r
}\r
- else if (!strncmp("rsdb_mode=", full_param, len_param)) {\r
- conf->rsdb_mode = (int)simple_strtol(data, NULL, 10);\r
- printf("%s: rsdb_mode = %d\n", __FUNCTION__, conf->rsdb_mode);\r
- }\r
- else if (!strncmp("vhtmode=", full_param, len_param)) {\r
- if (!strncmp(data, "0", 1))\r
- conf->vhtmode = 0;\r
- else\r
- conf->vhtmode = 1;\r
- printf("%s: vhtmode = %d\n", __FUNCTION__, conf->vhtmode);\r
- }\r
+#endif\r
else if (!strncmp("num_different_channels=", full_param, len_param)) {\r
conf->num_different_channels = (int)simple_strtol(data, NULL, 10);\r
printf("%s: num_different_channels = %d\n", __FUNCTION__, conf->num_different_channels);\r
}\r
- else if (!strncmp("autocountry=", full_param, len_param)) {\r
- conf->autocountry = (int)simple_strtol(data, NULL, 10);\r
- printf("%s: autocountry = %d\n", __FUNCTION__, conf->autocountry);\r
- }\r
else if (!strncmp("tsq=", full_param, len_param)) {\r
conf->tsq = (int)simple_strtol(data, NULL, 10);\r
printf("%s: tsq = %d\n", __FUNCTION__, conf->tsq);\r
conf->dhd_ioctl_timeout_msec = (int)simple_strtol(data, NULL, 10);\r
printf("%s: dhd_ioctl_timeout_msec = %d\n", __FUNCTION__, conf->dhd_ioctl_timeout_msec);\r
}\r
+ else if (!strncmp("wl_preinit=", full_param, len_param)) {\r
+ if (!(conf->wl_preinit = kmalloc(len_param, GFP_KERNEL))) {\r
+ CONFIG_ERROR(("%s: kmalloc failed\n", __FUNCTION__));\r
+ } else {\r
+ memset(conf->wl_preinit, 0, len_param);\r
+ strcpy(conf->wl_preinit, data);\r
+ printf("%s: wl_preinit = %s\n", __FUNCTION__, conf->wl_preinit);\r
+ }\r
+ }\r
else\r
return false;\r
\r
dhd_conf_read_config(dhd_pub_t *dhd, char *conf_path)\r
{\r
int bcmerror = -1;\r
- uint len, start_pos=0;\r
+ uint len = 0, start_pos=0;\r
void * image = NULL;\r
char * memblock = NULL;\r
char *bufp, *pick = NULL, *pch;\r
#endif\r
// other parameters set in preinit or config.txt\r
} else {\r
- // clear txglom parameters, but don't change swtxglom since it's possible enabled in config.txt\r
+ // clear txglom parameters\r
conf->txglom_ext = FALSE;\r
conf->txglom_bucket_size = 0;\r
conf->txglomsize = 0;\r
if (conf->txglom_ext)\r
printf("%s: txglom_ext=%d, txglom_bucket_size=%d\n", __FUNCTION__,\r
conf->txglom_ext, conf->txglom_bucket_size);\r
- printf("%s: txglomsize=%d, deferred_tx_len=%d, bus_txglom=%d\n", __FUNCTION__,\r
- conf->txglomsize, conf->deferred_tx_len, conf->bus_txglom);\r
+ printf("%s: txglom_mode=%s, use_rxchain=%d\n", __FUNCTION__,\r
+ conf->txglom_mode==SDPCM_TXGLOM_MDESC?"multi-desc":"copy", conf->use_rxchain);\r
+ printf("%s: txglomsize=%d, deferred_tx_len=%d\n", __FUNCTION__,\r
+ conf->txglomsize, conf->deferred_tx_len);\r
printf("%s: tx_in_rx=%d, txinrx_thres=%d, dhd_txminmax=%d\n", __FUNCTION__,\r
conf->tx_in_rx, conf->txinrx_thres, conf->dhd_txminmax);\r
printf("%s: tx_max_offset=%d, txctl_tmo_fix=%d\n", __FUNCTION__,\r
}\r
#endif\r
\r
+bool\r
+dhd_conf_set_wl_preinit(dhd_pub_t *dhd, char *data)\r
+{\r
+ int cmd, val;\r
+ char name[50], *pch, *pick_tmp, *pick_tmp2;\r
+\r
+ /* Process wl_preinit:\r
+ * wl_preinit=[cmd]/[val], [cmd]/[val] \\r
+ * Ex: wl_preinit=85/0, mpc/0\r
+ */\r
+ pick_tmp = data;\r
+ while (pick_tmp && (pick_tmp2 = bcmstrtok(&pick_tmp, ", ", 0)) != NULL) {\r
+ pch = bcmstrtok(&pick_tmp2, "=", 0);\r
+ if (!pch)\r
+ break;\r
+ memset(name, 0 , sizeof (name));\r
+ cmd = (int)simple_strtol(pch, NULL, 0);\r
+ if (cmd == 0) {\r
+ cmd = WLC_SET_VAR;\r
+ strcpy(name, pch);\r
+ }\r
+ pch = bcmstrtok(&pick_tmp2, ", ", 0);\r
+ if (!pch) {\r
+ break;\r
+ }\r
+ val = (int)simple_strtol(pch, NULL, 0);\r
+ dhd_conf_set_intiovar(dhd, cmd, name, val, -1, TRUE);\r
+ }\r
+\r
+ return true;\r
+}\r
+\r
+void\r
+dhd_conf_postinit_ioctls(dhd_pub_t *dhd)\r
+{\r
+ struct dhd_conf *conf = dhd->conf;\r
+\r
+ dhd_conf_set_intiovar(dhd, WLC_UP, "up", 0, 0, FALSE);\r
+ dhd_conf_map_country_list(dhd, &conf->cspec, 0);\r
+ dhd_conf_set_country(dhd, &conf->cspec);\r
+ dhd_conf_fix_country(dhd);\r
+ dhd_conf_get_country(dhd, &dhd->dhd_cspec);\r
+\r
+ dhd_conf_set_intiovar(dhd, WLC_SET_BAND, "WLC_SET_BAND", conf->band, 0, FALSE);\r
+ dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "bcn_timeout", conf->bcn_timeout, 0, FALSE);\r
+ dhd_conf_set_intiovar(dhd, WLC_SET_PM, "PM", conf->pm, 0, FALSE);\r
+ dhd_conf_set_intiovar(dhd, WLC_SET_SRL, "WLC_SET_SRL", conf->srl, 0, TRUE);\r
+ dhd_conf_set_intiovar(dhd, WLC_SET_LRL, "WLC_SET_LRL", conf->lrl, 0, FALSE);\r
+ dhd_conf_set_bw_cap(dhd);\r
+ dhd_conf_set_roam(dhd);\r
+\r
+#if defined(BCMPCIE)\r
+ dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "bus:deepsleep_disable",\r
+ conf->bus_deepsleep_disable, 0, FALSE);\r
+#endif /* defined(BCMPCIE) */\r
+\r
+#ifdef IDHCP\r
+ dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "dhcpc_enable", conf->dhcpc_enable, 0, FALSE);\r
+ if (dhd->conf->dhcpd_enable >= 0) {\r
+ dhd_conf_set_bufiovar(dhd, WLC_SET_VAR, "dhcpd_ip_addr",\r
+ (char *)&conf->dhcpd_ip_addr, sizeof(conf->dhcpd_ip_addr), FALSE);\r
+ dhd_conf_set_bufiovar(dhd, WLC_SET_VAR, "dhcpd_ip_mask",\r
+ (char *)&conf->dhcpd_ip_mask, sizeof(conf->dhcpd_ip_mask), FALSE);\r
+ dhd_conf_set_bufiovar(dhd, WLC_SET_VAR, "dhcpd_ip_start",\r
+ (char *)&conf->dhcpd_ip_start, sizeof(conf->dhcpd_ip_start), FALSE);\r
+ dhd_conf_set_bufiovar(dhd, WLC_SET_VAR, "dhcpd_ip_end",\r
+ (char *)&conf->dhcpd_ip_end, sizeof(conf->dhcpd_ip_end), FALSE);\r
+ dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "dhcpd_enable",\r
+ conf->dhcpd_enable, 0, FALSE);\r
+ }\r
+#endif\r
+ dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "txbf", conf->txbf, 0, FALSE);\r
+ dhd_conf_set_intiovar(dhd, WLC_SET_FAKEFRAG, "WLC_SET_FAKEFRAG", conf->frameburst, 0, FALSE);\r
+\r
+ dhd_conf_set_wl_preinit(dhd, conf->wl_preinit);\r
+\r
+#ifndef WL_CFG80211\r
+ dhd_conf_set_intiovar(dhd, WLC_UP, "up", 0, 0, FALSE);\r
+#endif\r
+\r
+}\r
+\r
int\r
dhd_conf_preinit(dhd_pub_t *dhd)\r
{\r
dhd_conf_free_chip_nv_path_list(&conf->nv_by_chip);\r
#endif\r
dhd_conf_free_country_list(&conf->country_list);\r
- dhd_conf_free_country_list(&dhd->conf->country_list_nodfs);\r
+ dhd_conf_free_country_list(&conf->country_list_nodfs);\r
if (conf->magic_pkt_filter_add)\r
kfree(conf->magic_pkt_filter_add);\r
+ if (conf->wl_preinit)\r
+ kfree(conf->wl_preinit);\r
memset(&conf->country_list, 0, sizeof(conf_country_list_t));\r
conf->band = -1;\r
- conf->mimo_bw_cap = -1;\r
conf->bw_cap_2g = -1;\r
conf->bw_cap_5g = -1;\r
if (conf->chip == BCM43362_CHIP_ID || conf->chip == BCM4330_CHIP_ID) {\r
} else if (conf->chip == BCM4335_CHIP_ID || conf->chip == BCM4339_CHIP_ID ||\r
conf->chip == BCM4354_CHIP_ID || conf->chip == BCM4356_CHIP_ID ||\r
conf->chip == BCM4345_CHIP_ID || conf->chip == BCM4371_CHIP_ID ||\r
- conf->chip == BCM43569_CHIP_ID || conf->chip == BCM4359_CHIP_ID) {\r
+ conf->chip == BCM43569_CHIP_ID || conf->chip == BCM4359_CHIP_ID ||\r
+ conf->chip == BCM4362_CHIP_ID) {\r
strcpy(conf->cspec.country_abbrev, "CN");\r
strcpy(conf->cspec.ccode, "CN");\r
conf->cspec.rev = 38;\r
conf->force_wme_ac = 0;\r
memset(&conf->wme_sta, 0, sizeof(wme_param_t));\r
memset(&conf->wme_ap, 0, sizeof(wme_param_t));\r
- conf->stbc = -1;\r
conf->phy_oclscdenable = -1;\r
#ifdef PKT_FILTER_SUPPORT\r
memset(&conf->pkt_filter_add, 0, sizeof(conf_pkt_filter_add_t));\r
conf->srl = -1;\r
conf->lrl = -1;\r
conf->bcn_timeout = 16;\r
- conf->spect = -1;\r
conf->txbf = -1;\r
- conf->lpc = -1;\r
conf->disable_proptx = -1;\r
conf->dhd_poll = -1;\r
#ifdef BCMSDIO\r
- conf->bus_txglom = -1;\r
conf->use_rxchain = 0;\r
conf->bus_rxglom = TRUE;\r
conf->txglom_ext = FALSE;\r
conf->tx_max_offset = 0;\r
conf->txglomsize = SDPCM_DEFGLOM_SIZE;\r
- conf->txctl_tmo_fix = 5;\r
+ conf->txctl_tmo_fix = 300;\r
conf->tx_in_rx = TRUE;\r
- conf->txglom_mode = SDPCM_TXGLOM_MDESC;\r
+ conf->txglom_mode = SDPCM_TXGLOM_CPY;\r
conf->deferred_tx_len = 0;\r
conf->dhd_txminmax = 1;\r
conf->txinrx_thres = -1;\r
conf->sd_f2_blocksize = 0;\r
conf->oob_enabled_later = FALSE;\r
+ conf->orphan_move = 0;\r
#endif\r
#ifdef BCMPCIE\r
conf->bus_deepsleep_disable = 1;\r
#endif\r
- conf->ampdu_ba_wsize = 0;\r
- conf->ampdu_hostreorder = -1;\r
conf->dpc_cpucore = -1;\r
conf->rxf_cpucore = -1;\r
conf->frameburst = -1;\r
conf->deepsleep = FALSE;\r
conf->pm = -1;\r
conf->pm_in_suspend = -1;\r
- conf->pm2_sleep_ret = -1;\r
+ conf->suspend_bcn_li_dtim = -1;\r
conf->num_different_channels = -1;\r
conf->xmit_in_suspend = TRUE;\r
conf->ap_in_suspend = 0;\r
conf->tsq = 0;\r
#endif\r
#ifdef DHDTCPACK_SUPPRESS\r
+#ifdef BCMSDIO\r
conf->tcpack_sup_mode = TCPACK_SUP_OFF;\r
+#elif defined(BCMPCIE)\r
+ conf->tcpack_sup_mode = TCPACK_SUP_DEFAULT;\r
+#endif\r
#endif\r
conf->pktprio8021x = -1;\r
- conf->rsdb_mode = -2;\r
- conf->vhtmode = -1;\r
- conf->autocountry = -1;\r
conf->ctrl_resched = 2;\r
conf->dhd_ioctl_timeout_msec = 0;\r
#ifdef IAPSTA_PREINIT\r
}\r
#ifdef CUSTOMER_HW_AMLOGIC\r
dhd_slpauto = FALSE;\r
- conf->txglom_mode = SDPCM_TXGLOM_CPY;\r
#endif\r
if (conf->chip == BCM4354_CHIP_ID || conf->chip == BCM4356_CHIP_ID ||\r
conf->chip == BCM4371_CHIP_ID || conf->chip == BCM43569_CHIP_ID ||\r
- conf->chip == BCM4359_CHIP_ID) {\r
+ conf->chip == BCM4359_CHIP_ID || conf->chip == BCM4362_CHIP_ID) {\r
#ifdef DHDTCPACK_SUPPRESS\r
#ifdef BCMSDIO\r
conf->tcpack_sup_mode = TCPACK_SUP_REPLACE;\r
#endif\r
#endif\r
+#if defined(BCMSDIO) || defined(BCMPCIE)\r
dhd_rxbound = 128;\r
dhd_txbound = 64;\r
+#endif\r
conf->txbf = 1;\r
conf->frameburst = 1;\r
#ifdef BCMSDIO\r
conf->txinrx_thres = 128;\r
conf->sd_f2_blocksize = CUSTOM_SDIO_F2_BLKSIZE;\r
conf->oob_enabled_later = TRUE;\r
-#ifdef CUSTOMER_HW_AMLOGIC\r
- conf->rxf_cpucore = 2;\r
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0))\r
+ conf->orphan_move = 1;\r
+#else\r
+ conf->orphan_move = 0;\r
#endif\r
#endif\r
}\r
conf->chip == BCM43340_CHIP_ID || conf->chip == BCM43341_CHIP_ID ||\r
conf->chip == BCM4334_CHIP_ID || conf->chip == BCM4324_CHIP_ID) {\r
conf->txglom_ext = TRUE;\r
- conf->use_rxchain = 0;\r
- conf->tx_in_rx = TRUE;\r
- conf->tx_max_offset = 1;\r
} else {\r
conf->txglom_ext = FALSE;\r
}\r
#endif\r
if (conf->txglomsize > SDPCM_MAXGLOM_SIZE)\r
conf->txglomsize = SDPCM_MAXGLOM_SIZE;\r
- conf->deferred_tx_len = 0;\r
#endif\r
\r
return 0;\r
dhd_conf_free_country_list(&dhd->conf->country_list_nodfs);\r
if (dhd->conf->magic_pkt_filter_add)\r
kfree(dhd->conf->magic_pkt_filter_add);\r
+ if (dhd->conf->wl_preinit)\r
+ kfree(dhd->conf->wl_preinit);\r
memset(dhd->conf, 0, sizeof(dhd_conf_t));\r
return 0;\r
}\r
dhd_conf_free_country_list(&dhd->conf->country_list_nodfs);\r
if (dhd->conf->magic_pkt_filter_add)\r
kfree(dhd->conf->magic_pkt_filter_add);\r
+ if (dhd->conf->wl_preinit)\r
+ kfree(dhd->conf->wl_preinit);\r
MFREE(dhd->osh, dhd->conf, sizeof(dhd_conf_t));\r
}\r
dhd->conf = NULL;\r
#include <wlioctl.h>
#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;
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;
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;
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 */
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;
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;
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);
#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);
#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,
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 */
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;
}
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))
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;
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 <dbus.h>
+#else
#include <dhd_bus.h>
+#endif /* BCMDBUS */
#ifdef DHD_ULP
#include <dhd_ulp.h>
#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;
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;
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 */
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;
+#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[];
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);
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);
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)
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)
{
/* 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;
}
/* 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;
}
#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,
#endif /* BCMPCIE */
#endif /* PROP_TXSTATUS */
+#endif /* BCMDBUS */
return ret;
}
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;
#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;
return &net->stats;
}
+#ifndef BCMDBUS
static int
dhd_watchdog_thread(void *data)
{
tasklet_schedule(&dhd->tasklet);
}
}
+#endif /* BCMDBUS */
static void
dhd_sched_rxf(dhd_pub_t *dhdp, void *skb)
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))) {
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) {
bcmerror = BCME_DONGLE_DOWN;
goto done;
}
+#endif /* !BCMDBUS */
/*
* Flush the TX queue if required for proper message serialization:
#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)
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) {
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
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) {
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);
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) {
}
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 */
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;
}
}
#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 */
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
#endif /* DHD_WET */
/* Initialize thread based operation and lock */
sema_init(&dhd->sdsem, 1);
+#endif /* !BCMDBUS */
/* Link to info module */
dhd->pub.info = dhd;
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.
*/
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
+#ifndef BCMDBUS
/* Set up the watchdog timer */
init_timer(&dhd->timer);
dhd->timer.data = (ulong)dhd;
goto fail;
}
}
+#endif /* !BCMDBUS */
dhd_state |= DHD_ATTACH_STATE_THREADS_CREATED;
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;
}
} EXPORT_SYMBOL(dhd_download_btfw);
#endif /* defined (BT_OVER_SDIO) */
+#ifndef BCMDBUS
int
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)
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;
#endif
shub_control_t shub_ctl;
-#if defined(BCMSDIO)
+#if defined(BCMSDIO) || defined(BCMDBUS)
#ifdef PROP_TXSTATUS
int wlfc_enable = TRUE;
#ifndef DISABLE_11N
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 */
#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;
#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);
#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 */
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,
}
}
#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
#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 */
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);
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);
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) {
}
}
#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) {
#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
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)) {
#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",
DHD_ERROR(("failed to set WNM capabilities\n"));
}
+ dhd_conf_postinit_ioctls(dhd);
done:
if (eventmask_msg)
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
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
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)
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) {
tasklet_kill(&dhd->tasklet);
}
}
+#endif /* BCMDBUS */
#ifdef DHD_LB
if (dhd->dhd_state & DHD_ATTACH_STATE_LB_ATTACH_DONE) {
{
printf("%s: Enter\n", __FUNCTION__);
+#ifdef BCMDBUS
+ dbus_deregister();
+#else
dhd_bus_unregister();
+#endif /* BCMDBUS */
wl_android_exit();
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)
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;
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;
dhd->wd_timer_valid = TRUE;
}
DHD_GENERAL_UNLOCK(pub, flags);
+#endif /* !BCMDBUS */
}
#ifdef DHD_PCIE_RUNTIMEPM
}
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;
}
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;
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
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
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
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
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) {
/*
}
/* 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;
}
/* 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);
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;
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;
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;
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 {
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));
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 */
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 */
#if defined(CONFIG_WIFI_CONTROL_FUNC)
#include <linux/wlan_plat.h>
#endif
+#ifdef BCMDBUS
+#include <dbus.h>
+#endif /* BCMDBUS */
#ifdef CONFIG_DTS
#include<linux/regulator/consumer.h>
#include<linux/of_gpio.h>
#ifdef CONFIG_DTS
struct regulator *wifi_regulator = NULL;
+extern struct wifi_platform_data dhd_wlan_control;
#endif /* CONFIG_DTS */
bool cfg_multichip = FALSE;
#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;
}
{
struct resource *resource;
wifi_adapter_info_t *adapter;
-#ifdef CONFIG_DTS
+#if defined(CONFIG_DTS) && defined(CUSTOMER_OOB)
int irq, gpio;
#endif /* CONFIG_DTS */
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)
return -1;
}
+#if defined(CUSTOMER_OOB)
/* This is to get the irq for the OOB */
gpio = of_get_gpio(pdev->dev.of_node, 0);
/* 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();
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"));
}
#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()
{
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
};
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",
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))
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);
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,
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 */
#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) */
(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,
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) {
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++;
} 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) !=
}
}
#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);
}
- 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)) {
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__));
/* 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);
}
}
+ 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) {
if (chipid == BCM43012_CHIP_ID)
return TRUE;
-
- if (chipid == BCM43751_CHIP_ID)
+ if (chipid == BCM4362_CHIP_ID)
return TRUE;
return FALSE;
#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))) {
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))) {
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;
}
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);
}
}
#include <dngl_stats.h>
#include <dhd.h>
+#ifdef BCMDBUS /* an abstraction layer that hides details of the underlying bus, eg \
+ Linux USB */
+#include <dbus.h>
+#else
#include <dhd_bus.h>
+#endif /* BCMDBUS */
#include <dhd_dbg.h>
#include <dhd_config.h>
#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
#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
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)
PKTFREE(wlfc->osh, pkt, TRUE);
}
} /* _dhd_wlfc_cleanup_txq */
+#endif /* !BCMDBUS */
/** called during eg detach */
void
/*
* 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);
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)
_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)
_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) {
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) {
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));
}
return WLFC_UNSUPPORTED;
}
+#ifndef BCMDBUS
_dhd_wlfc_cleanup_txq(dhd, fn, arg);
+#endif /* !BCMDBUS */
dhd_os_wlfc_unblock(dhd);
#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
#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 */
#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 || \
#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))
#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,
#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_ */
#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
--- /dev/null
+/*
+ * 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.
+ *
+ *
+ * <<Broadcom-WL-IPTag/Open:>>
+ *
+ * $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 */
*/
#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 */
};
/* 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
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;
}
*/
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 */
}
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));
#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"
#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)
{
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;
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;
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) ||
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;
exit:
return err;
}
+
#ifndef WL_SCHED_SCAN
static int wl_android_set_pno_setup(struct net_device *dev, char *command, int total_len)
{
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));
}
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)) {
}
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__));
}
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));
}
}
{
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__));
}
}
#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;
}
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;
}
#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)
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);
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,
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);
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 {
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;
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 */
#include <linux/module.h>
#include <linux/netdevice.h>
#include <net/netlink.h>
+#include <typedefs.h>
+#include <linuxver.h>
+#include <osl.h>
+
+#include <bcmutils.h>
+#include <bcmendian.h>
+#include <ethernet.h>
#include <wl_android.h>
+#include <linux/if_arp.h>
+#include <asm/uaccess.h>
+#include <linux/wireless.h>
+#include <wl_iw.h>
#include <wldev_common.h>
#include <wlioctl.h>
#include <bcmutils.h>
#include <dngl_stats.h>
#include <dhd.h>
#include <dhd_config.h>
+#ifdef WL_CFG80211
+#include <wl_cfg80211.h>
+#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"
#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 <dhd_wlfc.h>
#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;
}
#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)
{
}
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;
ANDROID_ERROR(("%s: Invalid chanspec 0x%x\n", __FUNCTION__, chspec));
err = BCME_ERROR;
}
+ *ret_chspec = fw_chspec;
return err;
}
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));
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);
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));
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) {
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; i<MAX_IF_NUM; i++) {
+ cur_if = &apsta_params->if_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 */
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)) {
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; i<MAX_IF_NUM && pch; i++) {
+ if (!strcmp(pch, "sta"))
+ apsta_params->if_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; i<MAX_IF_NUM && pch; i++) {
+ strcpy(apsta_params->if_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;
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
}
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);
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__));
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));
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__));
}
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; i<MAX_IF_NUM; i++) {
+ if (apsta_params->if_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);
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; i<MAX_IF_NUM; i++) {
+ another_if = &apsta_params->if_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; i<MAX_IF_NUM; i++) {
+ if (apsta_params->if_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);
}
}
- 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);
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; i<MAX_IF_NUM; i++) {
+ if (apsta_params->if_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; i<MAX_IF_NUM; i++) {
+ if (apsta_params->if_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;
#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;
}
#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) {
#include <wl_cfgvendor.h>
#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 <wl_statreport.h>
#endif /* STAT_REPORT */
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);
#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)
/* 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)
#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;
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;
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 &&
}
cfg->wlfc_on = true;
}
-#endif
+#endif /* BCMSDIO || BCMDBUS */
#endif /* PROP_TXSTATUS_VSDB */
/* Dual p2p doesn't support multiple P2PGO interfaces,
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 */
}
}
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;
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;
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;
}
#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 */
}
}
#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,
WL_DBG(("wl interface create success!! bssidx:%d \n", ret));
return ret;
}
+#endif
bool
wl_customer6_legacy_chip_check(struct bcm_cfg80211 *cfg,
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) {
}
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.
}
#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)
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 {
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 */
* 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)
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);
{
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;
}
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 */
}
#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);
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"));
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"));
// 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
}
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);
.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,
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;
}
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)
#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);
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);
#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);
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,
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);
#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;
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 */
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;
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;
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;
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;
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);
cfg->wlfc_on = false;
}
}
-#endif
+#endif /* BCMSDIO || BCMDBUS */
#endif /* PROP_TXSTATUS_VSDB */
}
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"));
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)
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);
#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
enum wl_mode {
WL_MODE_BSS,
WL_MODE_IBSS,
- WL_MODE_AP
+ WL_MODE_AP,
+#ifdef WLMESH
+ WL_MODE_MESH
+#endif
};
/* driver profile list */
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;
#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;
};
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,
#include <dhdioctl.h>
#include <wlioctl.h>
#include <dhd_cfg80211.h>
+#include <dhd_config.h>
#if defined(BCMPCIE) && defined(DHD_FW_COREDUMP)
extern int dhd_bus_mem_dump(dhd_pub_t *dhd);
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 {
}
return p2p_supported;
}
+
/* Cleanup P2P resources */
s32
wl_cfgp2p_down(struct bcm_cfg80211 *cfg)
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) {
}
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));
}
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));
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;