diff --git a/projects/Rockchip/README.md b/projects/Rockchip/README.md
index 29ec99515c..135b3e1f17 100644
--- a/projects/Rockchip/README.md
+++ b/projects/Rockchip/README.md
@@ -18,6 +18,7 @@ This project is for Rockchip SoC devices
* [96rocks ROCK960](devices/RK3399)
* [Khadas Edge](devices/RK3399)
* [PINE64 RockPro64](devices/RK3399)
+* [Radxa ROCK Pi 4](devices/RK3399)
* [Rockchip Sapphire Board](devices/RK3399)
**My single-board computer is not listed, will it be added in the future?**
diff --git a/projects/Rockchip/devices/RK3399/README.md b/projects/Rockchip/devices/RK3399/README.md
index 8497aed240..844c2948c9 100644
--- a/projects/Rockchip/devices/RK3399/README.md
+++ b/projects/Rockchip/devices/RK3399/README.md
@@ -6,5 +6,6 @@ This is a SoC device for RK3399
* `PROJECT=Rockchip DEVICE=RK3399 ARCH=arm UBOOT_SYSTEM=khadas-edge make image`
* `PROJECT=Rockchip DEVICE=RK3399 ARCH=arm UBOOT_SYSTEM=rock960 make image`
+* `PROJECT=Rockchip DEVICE=RK3399 ARCH=arm UBOOT_SYSTEM=rock-pi-4 make image`
* `PROJECT=Rockchip DEVICE=RK3399 ARCH=arm UBOOT_SYSTEM=rockpro64 make image`
* `PROJECT=Rockchip DEVICE=RK3399 ARCH=arm UBOOT_SYSTEM=sapphire make image`
diff --git a/projects/Rockchip/devices/RK3399/options b/projects/Rockchip/devices/RK3399/options
index 33840f751b..c432f4571b 100644
--- a/projects/Rockchip/devices/RK3399/options
+++ b/projects/Rockchip/devices/RK3399/options
@@ -28,6 +28,7 @@
KERNEL_MAKE_EXTRACMD=""
KERNEL_MAKE_EXTRACMD+=" rockchip/rk3399-khadas-edge.dtb"
KERNEL_MAKE_EXTRACMD+=" rockchip/rk3399-rock960.dtb"
+ KERNEL_MAKE_EXTRACMD+=" rockchip/rk3399-rock-pi-4.dtb"
KERNEL_MAKE_EXTRACMD+=" rockchip/rk3399-rockpro64.dtb"
KERNEL_MAKE_EXTRACMD+=" rockchip/rk3399-sapphire.dtb"
diff --git a/projects/Rockchip/patches/linux/rockchip-4.4/linux-0001-rockchip.patch b/projects/Rockchip/patches/linux/rockchip-4.4/linux-0001-rockchip.patch
index 91db938aa6..3bd8ea1067 100644
--- a/projects/Rockchip/patches/linux/rockchip-4.4/linux-0001-rockchip.patch
+++ b/projects/Rockchip/patches/linux/rockchip-4.4/linux-0001-rockchip.patch
@@ -1444,3 +1444,22867 @@ index 240cecac65b5..8fe4163214e0 100644
psp = POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX;
if (power_supply_get_property(psy, psp, &val) == 0)
+
+From 21d2c1a5736234ee156e40e7fbaa9f48f9868ea3 Mon Sep 17 00:00:00 2001
+From: Finley Xiao
+Date: Fri, 28 Sep 2018 11:42:38 +0800
+Subject: [PATCH] drm/rockchip: vop: Use Use pm_runtime_put_sync() in
+ vop_crtc_disable()
+
+The procedure of rpm_idle() is as follows.
+rpm_idle
+->rpm_suspend
+ ->pm_genpd_runtime_suspend
+ ->pm_clk_suspend
+ ->clk_disable
+ ->genpd_poweroff
+
+Pm_runtime_put(dev) causes rpm_idle() to be queued up, when
+pm_clk_suspend() is executed, the rockchip dmcfreq lock is
+released, vop clocks may be closed while changing ddr frequency.
+Use pm_runtime_put_sync() instead of pm_runtime_put(), so that
+rpm_idle can be executed before the lock is released.
+
+Change-Id: Ibf4ff70b65782427eaf0fe9f7566ebff602d3757
+Signed-off-by: Finley Xiao
+---
+ drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+index 0916b4284f88..b3f7a8ebcb5d 100644
+--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
++++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+@@ -1429,7 +1429,7 @@ static void vop_crtc_disable(struct drm_crtc *crtc)
+ vop->is_iommu_enabled = false;
+ }
+
+- pm_runtime_put(vop->dev);
++ pm_runtime_put_sync(vop->dev);
+ clk_disable_unprepare(vop->dclk);
+ clk_disable_unprepare(vop->aclk);
+ clk_disable_unprepare(vop->hclk);
+
+From fabe40745dfd1b53b48fcb246300722ff71562c8 Mon Sep 17 00:00:00 2001
+From: William Wu
+Date: Fri, 19 Oct 2018 10:46:27 +0800
+Subject: [PATCH] usb: dwc3: rockchip: fix usb reenumerated upon pm resume
+
+On rk3399 platforms, Type-c1 can be simplified to Type-A
+port and support USB 3.0 Host only mode. It has a problem
+that the dwc3_rockchip_resume() will reset the controller
+upon pm resume, and this may cause usb device(e.g. usb 4G
+modem) to be reenumerated. This patch sets the flag of
+connected to true and avoid to do the reset operation upon
+pm resume.
+
+Change-Id: I57f92d0277a19ce1c7b881fe2da6470fd3a70b73
+Signed-off-by: William Wu
+---
+ drivers/usb/dwc3/dwc3-rockchip.c | 9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+diff --git a/drivers/usb/dwc3/dwc3-rockchip.c b/drivers/usb/dwc3/dwc3-rockchip.c
+index db482d37df56..3d3b5774b9bb 100644
+--- a/drivers/usb/dwc3/dwc3-rockchip.c
++++ b/drivers/usb/dwc3/dwc3-rockchip.c
+@@ -836,6 +836,15 @@ static int dwc3_rockchip_probe(struct platform_device *pdev)
+ (extcon_get_cable_state_(rockchip->edev,
+ EXTCON_USB_HOST) > 0))
+ schedule_work(&rockchip->otg_work);
++ } else {
++ /*
++ * DWC3 work as Host only mode or Peripheral
++ * only mode, set connected flag to true, it
++ * can avoid to reset the DWC3 controller when
++ * resume from PM suspend which may cause the
++ * usb device to be reenumerated.
++ */
++ rockchip->connected = true;
+ }
+
+ dwc3_rockchip_debugfs_init(rockchip);
+
+From 3b7090b60c9690eca1556c1ede27a089ddb674c3 Mon Sep 17 00:00:00 2001
+From: Yao Xiao
+Date: Tue, 4 Sep 2018 09:09:18 +0800
+Subject: [PATCH] net: wireless: update bcmdhd driver to "1.579.77.41.9 (r)"
+
+Change-Id: I5b5a30393157192fd8c1c033169931e7d5b03df0
+Signed-off-by: Yao Xiao
+---
+ .../wireless/rockchip_wlan/rkwifi/bcmdhd/Kconfig | 3 +
+ .../wireless/rockchip_wlan/rkwifi/bcmdhd/Makefile | 171 +-
+ .../rockchip_wlan/rkwifi/bcmdhd/bcmsdh_linux.c | 12 +-
+ .../rockchip_wlan/rkwifi/bcmdhd/bcmsdh_sdmmc.c | 5 +-
+ .../rkwifi/bcmdhd/bcmsdh_sdmmc_linux.c | 8 +-
+ .../wireless/rockchip_wlan/rkwifi/bcmdhd/dbus.c | 2929 ++++++++++
+ .../rockchip_wlan/rkwifi/bcmdhd/dbus_usb.c | 1173 ++++
+ .../rockchip_wlan/rkwifi/bcmdhd/dbus_usb_linux.c | 3404 ++++++++++++
+ .../net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd.h | 71 +-
+ .../wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_bus.h | 4 +
+ .../wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_cdc.c | 117 +-
+ .../rockchip_wlan/rkwifi/bcmdhd/dhd_cfg80211.c | 2 +
+ .../rockchip_wlan/rkwifi/bcmdhd/dhd_common.c | 38 +-
+ .../rockchip_wlan/rkwifi/bcmdhd/dhd_config.c | 1198 ++--
+ .../rockchip_wlan/rkwifi/bcmdhd/dhd_config.h | 91 +-
+ .../rockchip_wlan/rkwifi/bcmdhd/dhd_gpio.c | 36 +-
+ .../wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_ip.c | 16 +-
+ .../rockchip_wlan/rkwifi/bcmdhd/dhd_linux.c | 569 +-
+ .../rockchip_wlan/rkwifi/bcmdhd/dhd_linux.h | 18 +
+ .../rkwifi/bcmdhd/dhd_linux_platdev.c | 118 +-
+ .../rockchip_wlan/rkwifi/bcmdhd/dhd_linux_wq.h | 3 +
+ .../rockchip_wlan/rkwifi/bcmdhd/dhd_msgbuf.c | 23 +-
+ .../rockchip_wlan/rkwifi/bcmdhd/dhd_pcie.c | 73 +-
+ .../rockchip_wlan/rkwifi/bcmdhd/dhd_pcie.h | 1 +
+ .../rockchip_wlan/rkwifi/bcmdhd/dhd_pcie_linux.c | 90 +-
+ .../wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_pno.c | 5 +
+ .../rockchip_wlan/rkwifi/bcmdhd/dhd_sdio.c | 199 +-
+ .../rockchip_wlan/rkwifi/bcmdhd/dhd_static_buf.c | 26 +-
+ .../rockchip_wlan/rkwifi/bcmdhd/dhd_wlfc.c | 28 +-
+ .../rockchip_wlan/rkwifi/bcmdhd/dhd_wlfc.h | 5 +
+ .../rockchip_wlan/rkwifi/bcmdhd/include/bcmdevs.h | 5 +
+ .../rockchip_wlan/rkwifi/bcmdhd/include/dbus.h | 41 +-
+ .../rockchip_wlan/rkwifi/bcmdhd/include/epivers.h | 2 +-
+ .../rkwifi/bcmdhd/include/linux_osl.h | 2 +
+ .../rockchip_wlan/rkwifi/bcmdhd/include/sbchipc.h | 1 +
+ .../rockchip_wlan/rkwifi/bcmdhd/include/usbrdl.h | 135 +
+ .../rockchip_wlan/rkwifi/bcmdhd/include/wlioctl.h | 15 +
+ .../rockchip_wlan/rkwifi/bcmdhd/linux_osl.c | 11 +
+ .../wireless/rockchip_wlan/rkwifi/bcmdhd/siutils.c | 3 +-
+ .../rockchip_wlan/rkwifi/bcmdhd/wl_android.c | 192 +-
+ .../rockchip_wlan/rkwifi/bcmdhd/wl_android.h | 93 +-
+ .../rockchip_wlan/rkwifi/bcmdhd/wl_android_ext.c | 5736 +++++++++++++-------
+ .../rockchip_wlan/rkwifi/bcmdhd/wl_cfg80211.c | 615 ++-
+ .../rockchip_wlan/rkwifi/bcmdhd/wl_cfg80211.h | 31 +-
+ .../rockchip_wlan/rkwifi/bcmdhd/wl_cfg_btcoex.c | 16 +-
+ .../rockchip_wlan/rkwifi/bcmdhd/wl_cfgp2p.c | 20 +-
+ .../rockchip_wlan/rkwifi/bcmdhd/wl_cfgp2p.h | 20 +-
+ .../rockchip_wlan/rkwifi/bcmdhd/wl_cfgvendor.c | 7 +-
+ .../rockchip_wlan/rkwifi/bcmdhd/wl_escan.c | 155 +-
+ .../rockchip_wlan/rkwifi/bcmdhd/wl_escan.h | 16 +-
+ .../wireless/rockchip_wlan/rkwifi/bcmdhd/wl_iw.c | 54 +-
+ .../rockchip_wlan/rkwifi/bcmdhd/wldev_common.c | 12 +-
+ 52 files changed, 14258 insertions(+), 3360 deletions(-)
+ mode change 100755 => 100644 drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/bcmsdh_sdmmc_linux.c
+ create mode 100644 drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dbus.c
+ create mode 100644 drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dbus_usb.c
+ create mode 100644 drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dbus_usb_linux.c
+ mode change 100755 => 100644 drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_cdc.c
+ mode change 100755 => 100644 drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_cfg80211.c
+ mode change 100755 => 100644 drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_ip.c
+ mode change 100755 => 100644 drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_linux_platdev.c
+ mode change 100755 => 100644 drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_linux_wq.h
+ mode change 100755 => 100644 drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_msgbuf.c
+ mode change 100755 => 100644 drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_pcie.h
+ mode change 100755 => 100644 drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_pcie_linux.c
+ mode change 100755 => 100644 drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_pno.c
+ mode change 100755 => 100644 drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_wlfc.h
+ mode change 100755 => 100644 drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/bcmdevs.h
+ mode change 100755 => 100644 drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/linux_osl.h
+ create mode 100644 drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/usbrdl.h
+ mode change 100755 => 100644 drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/linux_osl.c
+ mode change 100755 => 100644 drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_cfg80211.h
+ mode change 100755 => 100644 drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_cfg_btcoex.c
+ mode change 100755 => 100644 drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_cfgp2p.c
+ mode change 100755 => 100644 drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_cfgp2p.h
+
+diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/Kconfig b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/Kconfig
+index 303e009bf4a1..10c06951a65d 100644
+--- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/Kconfig
++++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/Kconfig
+@@ -38,6 +38,9 @@ config BCMDHD_SDIO
+ config BCMDHD_PCIE
+ bool "PCIe bus interface support"
+ depends on BCMDHD && PCI
++config BCMDHD_USB
++ bool "USB bus interface support"
++ depends on BCMDHD && USB
+ endchoice
+
+ choice
+diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/Makefile b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/Makefile
+index a24a0826fd71..c49feed9d913 100644
+--- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/Makefile
++++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/Makefile
+@@ -6,122 +6,165 @@
+ MODULE_NAME = bcmdhd
+ CONFIG_BCMDHD_SDIO := y
+ #CONFIG_BCMDHD_PCIE := y
++#CONFIG_BCMDHD_USB := y
+ CONFIG_BCMDHD_OOB := y
+ CONFIG_BCMDHD_PROPTXSTATUS := y
+ CONFIG_BCMDHD_AG := y
+ #CONFIG_DHD_USE_STATIC_BUF := y
+ CONFIG_VTS_SUPPORT := y
++#CONFIG_LOGTRACE := y
+
+-DHDCFLAGS = -Wall -Wstrict-prototypes -Dlinux -DBCMDRIVER -DSDTEST \
++CONFIG_MACH_PLATFORM := y
++#CONFIG_BCMDHD_DTS := y
++
++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 \
++ -DDHD_DONOT_FORWARD_BCMEVENT_AS_NETWORK_PKT \
++ -DMULTIPLE_SUPPLICANT -DTSQ_MULTIPLIER -DMFP \
++ -DWL_EXT_IAPSTA \
+ -DENABLE_INSMOD_NO_FW_LOAD -DDHD_UNSUPPORT_IF_CNTS \
+ -Idrivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd \
+ -Idrivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/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
+
++#BCMDHD_SDIO
+ 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
+-
+-DHDOFILES += bcmsdh.o bcmsdh_linux.o bcmsdh_sdmmc.o bcmsdh_sdmmc_linux.o \
+- dhd_sdio.o dhd_cdc.o dhd_wlfc.o
+-
++DHDCFLAGS += -DBCMSDIO -DMMC_SDIO_ABORT -DBCMLXSDMMC -DUSE_SDIOFIFO_IOVAR \
++ -DSDTEST -DBDC -DDHD_USE_IDLECOUNT -DCUSTOM_SDIO_F2_BLKSIZE=256 \
++ -DBCMSDIOH_TXGLOM -DBCMSDIOH_TXGLOM_EXT -DRXFRAME_THREAD
+ 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
++ DHDCFLAGS += -DSDIO_ISR_THREAD
+ endif
+
+-ifeq ($(CONFIG_BCMDHD_PROPTXSTATUS),y)
+-ifneq ($(CONFIG_BCMDHD_SDIO),)
+-DHDCFLAGS += -DPROP_TXSTATUS
+-endif
+-ifneq ($(CONFIG_CFG80211),)
+-DHDCFLAGS += -DPROP_TXSTATUS_VSDB
+-endif
++DHDOFILES += bcmsdh.o bcmsdh_linux.o bcmsdh_sdmmc.o bcmsdh_sdmmc_linux.o \
++ dhd_sdio.o dhd_cdc.o dhd_wlfc.o
+ endif
+
++#BCMDHD_PCIE
+ ifneq ($(CONFIG_BCMDHD_PCIE),)
+-DHDCFLAGS += \
+- -DPCIE_FULL_DONGLE -DBCMPCIE -DCUSTOM_DPC_PRIO_SETTING=-1
++DHDCFLAGS += -DPCIE_FULL_DONGLE -DBCMPCIE -DCUSTOM_DPC_PRIO_SETTING=-1 \
++ -DDONGLE_ENABLE_ISOLATION
++ifneq ($(CONFIG_PCI_MSI),)
++ DHDCFLAGS += -DDHD_USE_MSI
++endif
+ ifeq ($(CONFIG_DHD_USE_STATIC_BUF),y)
+-DHDCFLAGS += -DDHD_USE_STATIC_CTRLBUF
++ DHDCFLAGS += -DDHD_USE_STATIC_CTRLBUF
+ endif
+
+-DHDOFILES += dhd_pcie.o dhd_pcie_linux.o pcie_core.o dhd_flowring.o \
++DHDOFILES += dhd_pcie.o dhd_pcie_linux.o pcie_core.o dhd_flowring.o \
+ dhd_msgbuf.o
+ endif
+
++#BCMDHD_USB
++ifneq ($(CONFIG_BCMDHD_USB),)
++DHDCFLAGS += -DUSBOS_TX_THREAD -DBCMDBUS -DBCMTRXV2 -DDBUS_USB_LOOPBACK \
++ -DBDC
++DHDCFLAGS += -DBCM_REQUEST_FW -DEXTERNAL_FW_PATH
++#DHDCFLAGS :=$(filter-out -DENABLE_INSMOD_NO_FW_LOAD,$(DHDCFLAGS))
++
++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
++
++#VTS_SUPPORT
+ 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 \
++ifneq ($(CONFIG_CFG80211),)
++DHDCFLAGS += -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 += bcmxtlv.o dhd_rtt.o bcm_app_utils.o
++CONFIG_LOGTRACE := y
++endif
++endif
++
++#LOGTRACE
++ifeq ($(CONFIG_LOGTRACE),y)
++ DHDCFLAGS += -DSHOW_LOGTRACE
++ DHDOFILES += dhd_debug_linux.o dhd_debug.o dhd_mschdbg.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
++
++#obj-$(CONFIG_RKWIFI) += bcmdhd.o
+ obj-$(CONFIG_AP6XXX) += bcmdhd.o
+ bcmdhd-objs += $(DHDOFILES)
+
+-#ifeq ($(CONFIG_MACH_PLATFORM),y)
+-DHDOFILES += dhd_gpio.o
+-DHDCFLAGS += -DCUSTOMER_HW -DDHD_OF_SUPPORT
+-#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 += -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
++ obj-m += dhd_static_buf.o
++ 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
+ EXTRA_LDFLAGS += --strip-debug
+-else
+-DHDCFLAGS += -DBUILD_IN_KERNEL
+ endif
+diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/bcmsdh_linux.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/bcmsdh_linux.c
+index 9e3f641bf038..41462a37e6e6 100644
+--- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/bcmsdh_linux.c
++++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/bcmsdh_linux.c
+@@ -52,7 +52,6 @@ extern void dhdsdio_isr(void * args);
+ #endif /* defined(CONFIG_ARCH_ODIN) */
+ #include
+
+-
+ /* driver info, initialized when bcmsdh_register is called */
+ static bcmsdh_driver_t drvinfo = {NULL, NULL, NULL, NULL};
+
+@@ -363,13 +362,13 @@ int bcmsdh_oob_intr_register(bcmsdh_info_t *bcmsdh, bcmsdh_cb_fn_t oob_irq_handl
+ SDLX_MSG(("%s: irq is already registered\n", __FUNCTION__));
+ return -EBUSY;
+ }
+- SDLX_MSG(("%s %s irq=%d flags=0x%X\n", __FUNCTION__,
+ #ifdef HW_OOB
+- "HW_OOB",
++ printf("%s: HW_OOB irq=%d flags=0x%X\n", __FUNCTION__,
++ (int)bcmsdh_osinfo->oob_irq_num, (int)bcmsdh_osinfo->oob_irq_flags);
+ #else
+- "SW_OOB",
++ printf("%s: SW_OOB irq=%d flags=0x%X\n", __FUNCTION__,
++ (int)bcmsdh_osinfo->oob_irq_num, (int)bcmsdh_osinfo->oob_irq_flags);
+ #endif
+- (int)bcmsdh_osinfo->oob_irq_num, (int)bcmsdh_osinfo->oob_irq_flags));
+ bcmsdh_osinfo->oob_irq_handler = oob_irq_handler;
+ bcmsdh_osinfo->oob_irq_handler_context = oob_irq_handler_context;
+ bcmsdh_osinfo->oob_irq_enabled = TRUE;
+@@ -398,6 +397,7 @@ int bcmsdh_oob_intr_register(bcmsdh_info_t *bcmsdh, bcmsdh_cb_fn_t oob_irq_handl
+ else
+ bcmsdh_osinfo->oob_irq_wake_enabled = TRUE;
+ #endif
++
+ return 0;
+ }
+
+@@ -423,7 +423,7 @@ void bcmsdh_oob_intr_unregister(bcmsdh_info_t *bcmsdh)
+ free_irq(bcmsdh_osinfo->oob_irq_num, bcmsdh);
+ bcmsdh_osinfo->oob_irq_registered = FALSE;
+ }
+-#endif
++#endif
+
+ /* Module parameters specific to each host-controller driver */
+
+diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/bcmsdh_sdmmc.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/bcmsdh_sdmmc.c
+index ccfcce2c66ca..31b05ce71b72 100644
+--- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/bcmsdh_sdmmc.c
++++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/bcmsdh_sdmmc.c
+@@ -995,7 +995,6 @@ sdioh_set_mode(sdioh_info_t *sd, uint mode)
+ sd->txglom_mode = mode;
+ else if (mode == SDPCM_TXGLOM_MDESC)
+ sd->txglom_mode = mode;
+- printf("%s: set txglom_mode to %s\n", __FUNCTION__, mode==SDPCM_TXGLOM_MDESC?"multi-desc":"copy");
+
+ return (sd->txglom_mode);
+ }
+@@ -1288,8 +1287,8 @@ txglomfail:
+
+ if (sd_msglevel & SDH_COST_VAL) {
+ getnstimeofday(&now);
+- sd_cost(("%s: rw=%d, cost=%lds %luus\n", __FUNCTION__,
+- write, now.tv_sec-before.tv_sec, now.tv_nsec/1000-before.tv_nsec/1000));
++ sd_cost(("%s: rw=%d, ttl_len=%d, cost=%lds %luus\n", __FUNCTION__,
++ write, ttl_len, now.tv_sec-before.tv_sec, now.tv_nsec/1000-before.tv_nsec/1000));
+ }
+
+ sd_trace(("%s: Exit\n", __FUNCTION__));
+diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/bcmsdh_sdmmc_linux.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/bcmsdh_sdmmc_linux.c
+old mode 100755
+new mode 100644
+index b5a388cc3cbe..35b91ff7fc27
+--- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/bcmsdh_sdmmc_linux.c
++++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/bcmsdh_sdmmc_linux.c
+@@ -232,7 +232,7 @@ static const struct sdio_device_id bcmsdh_sdmmc_ids[] = {
+
+ MODULE_DEVICE_TABLE(sdio, bcmsdh_sdmmc_ids);
+
+-#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM_SLEEP)
++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM)
+ static int bcmsdh_sdmmc_suspend(struct device *pdev)
+ {
+ int err;
+@@ -275,9 +275,7 @@ static int bcmsdh_sdmmc_suspend(struct device *pdev)
+
+ static int bcmsdh_sdmmc_resume(struct device *pdev)
+ {
+-#if defined(OOB_INTR_ONLY)
+ sdioh_info_t *sdioh;
+-#endif
+ struct sdio_func *func = dev_to_sdio_func(pdev);
+
+ printf("%s Enter func->num=%d\n", __FUNCTION__, func->num);
+@@ -285,10 +283,8 @@ static int bcmsdh_sdmmc_resume(struct device *pdev)
+ return 0;
+
+ dhd_mmc_suspend = FALSE;
+-#if defined(OOB_INTR_ONLY)
+ sdioh = sdio_get_drvdata(func);
+ bcmsdh_resume(sdioh->bcmsdh);
+-#endif
+
+ smp_mb();
+ printf("%s Exit\n", __FUNCTION__);
+@@ -346,7 +342,7 @@ static struct sdio_driver bcmsdh_sdmmc_driver = {
+ .remove = bcmsdh_sdmmc_remove,
+ .name = "bcmsdh_sdmmc",
+ .id_table = bcmsdh_sdmmc_ids,
+-#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM_SLEEP)
++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM)
+ .drv = {
+ .pm = &bcmsdh_sdmmc_pm_ops,
+ },
+diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dbus.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dbus.c
+new file mode 100644
+index 000000000000..aeec7761fdc3
+--- /dev/null
++++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dbus.c
+@@ -0,0 +1,2929 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/** @file dbus.c
++ *
++ * Hides details of USB / SDIO / SPI interfaces and OS details. It is intended to shield details and
++ * provide the caller with one common bus interface for all dongle devices. In practice, it is only
++ * used for USB interfaces. DBUS is not a protocol, but an abstraction layer.
++ *
++ * Copyright (C) 1999-2016, Broadcom Corporation
++ *
++ * Unless you and Broadcom execute a separate written software license
++ * agreement governing use of this software, this software is licensed to you
++ * under the terms of the GNU General Public License version 2 (the "GPL"),
++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
++ * following added to such license:
++ *
++ * As a special exception, the copyright holders of this software give you
++ * permission to link this software with independent modules, and to copy and
++ * distribute the resulting executable under terms of your choice, provided that
++ * you also meet, for each linked independent module, the terms and conditions of
++ * the license of that module. An independent module is a module which is not
++ * derived from this software. The special exception does not apply to any
++ * modifications of the software.
++ *
++ * Notwithstanding the above, under no circumstances may you combine this
++ * software in any way with any other Broadcom software provided under a license
++ * other than the GPL, without Broadcom's express prior written consent.
++ *
++ *
++ * <>
++ *
++ * $Id: dbus.c 553311 2015-04-29 10:23:08Z $
++ */
++
++
++#include "osl.h"
++#include "dbus.h"
++#include
++#include
++#include
++#include
++#ifdef PROP_TXSTATUS /* a form of flow control between host and dongle */
++#include
++#endif
++#include
++
++#if defined(BCM_REQUEST_FW)
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#endif
++
++
++
++#if defined(BCM_REQUEST_FW)
++#ifndef VARS_MAX
++#define VARS_MAX 8192
++#endif
++#endif
++
++#ifdef DBUS_USB_LOOPBACK
++extern bool is_loopback_pkt(void *buf);
++extern int matches_loopback_pkt(void *buf);
++#endif
++
++/** General info for all BUS types */
++typedef struct dbus_irbq {
++ dbus_irb_t *head;
++ dbus_irb_t *tail;
++ int cnt;
++} dbus_irbq_t;
++
++/**
++ * This private structure dhd_bus_t is also declared in dbus_usb_linux.c.
++ * All the fields must be consistent in both declarations.
++ */
++typedef struct dhd_bus {
++ dbus_pub_t pub; /* MUST BE FIRST */
++ dhd_pub_t *dhd;
++
++ 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
++ char *fw_path; /* module_param: path to firmware image */
++ char *nv_path; /* module_param: path to nvram vars file */
++} dhd_bus_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(dhd_bus->bus_info, ((exec_cb_t)fn), ((struct exec_parms *) a))
++
++#define EXEC_TXLOCK(info, fn, a) \
++ info->drvintf->exec_txlock(dhd_bus->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);
++
++void * dhd_dbus_probe_cb(void *arg, const char *desc, uint32 bustype,
++ uint16 bus_no, uint16 slot, uint32 hdrlen);
++void dhd_dbus_disconnect_cb(void *arg);
++void dbus_detach(dhd_bus_t *pub);
++
++/** 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(dhd_bus_t *dhd_bus);
++static int dbus_tx_timer_start(dhd_bus_t *dhd_bus, uint timeout);
++static int dbus_tx_timer_stop(dhd_bus_t *dhd_bus);
++static int dbus_irbq_init(dhd_bus_t *dhd_bus, dbus_irbq_t *q, int nq, int size_irb);
++static int dbus_irbq_deinit(dhd_bus_t *dhd_bus, dbus_irbq_t *q, int size_irb);
++static int dbus_rxirbs_fill(dhd_bus_t *dhd_bus);
++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,
++ uint16 bus_no, uint16 slot, uint32 hdrlen);
++
++#if defined(BCM_REQUEST_FW)
++extern char * dngl_firmware;
++extern unsigned int dngl_fwlen;
++#ifndef EXTERNAL_FW_PATH
++static int dbus_get_nvram(dhd_bus_t *dhd_bus);
++static int dbus_jumbo_nvram(dhd_bus_t *dhd_bus);
++static int dbus_otp(dhd_bus_t *dhd_bus, uint16 *boardtype, uint16 *boardrev);
++static int dbus_select_nvram(dhd_bus_t *dhd_bus, int8 *jumbonvram, int jumbolen,
++uint16 boardtype, uint16 boardrev, int8 **nvram, int *nvram_len);
++#endif /* !EXTERNAL_FW_PATH */
++extern int dbus_zlib_decomp(dhd_bus_t *dhd_bus);
++extern void *dbus_zlib_calloc(int num, int size);
++extern void dbus_zlib_free(void *ptr);
++#endif
++
++/* function */
++void
++dbus_flowctrl_tx(void *dbi, bool on)
++{
++ dhd_bus_t *dhd_bus = dbi;
++
++ if (dhd_bus == NULL)
++ return;
++
++ DBUSTRACE(("%s on %d\n", __FUNCTION__, on));
++
++ if (dhd_bus->txoff == on)
++ return;
++
++ dhd_bus->txoff = on;
++
++ if (dhd_bus->cbs && dhd_bus->cbs->txflowcontrol)
++ dhd_bus->cbs->txflowcontrol(dhd_bus->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)
++{
++ dhd_bus_t *dhd_bus = (dhd_bus_t *) handle;
++
++ DBUSTRACE(("%s, on %d\n", __FUNCTION__, on));
++
++ if (dhd_bus == NULL)
++ return;
++
++ if (dhd_bus->txoverride == on)
++ return;
++
++ dhd_bus->txoverride = on; /* flow control */
++
++ if (!on)
++ dbus_rxirbs_fill(dhd_bus);
++
++}
++
++/** 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 dhd_bus->drvintf->tx_timer_init is NULL.
++ */
++static int
++dbus_tx_timer_init(dhd_bus_t *dhd_bus)
++{
++ if (dhd_bus && dhd_bus->drvintf && dhd_bus->drvintf->tx_timer_init)
++ return dhd_bus->drvintf->tx_timer_init(dhd_bus->bus_info);
++ else
++ return DBUS_ERR;
++}
++
++static int
++dbus_tx_timer_start(dhd_bus_t *dhd_bus, uint timeout)
++{
++ if (dhd_bus == NULL)
++ return DBUS_ERR;
++
++ if (dhd_bus->tx_timer_ticking)
++ return DBUS_OK;
++
++ if (dhd_bus->drvintf && dhd_bus->drvintf->tx_timer_start) {
++ if (dhd_bus->drvintf->tx_timer_start(dhd_bus->bus_info, timeout) == DBUS_OK) {
++ dhd_bus->tx_timer_ticking = TRUE;
++ return DBUS_OK;
++ }
++ }
++
++ return DBUS_ERR;
++}
++
++static int
++dbus_tx_timer_stop(dhd_bus_t *dhd_bus)
++{
++ if (dhd_bus == NULL)
++ return DBUS_ERR;
++
++ if (!dhd_bus->tx_timer_ticking)
++ return DBUS_OK;
++
++ if (dhd_bus->drvintf && dhd_bus->drvintf->tx_timer_stop) {
++ if (dhd_bus->drvintf->tx_timer_stop(dhd_bus->bus_info) == DBUS_OK) {
++ dhd_bus->tx_timer_ticking = FALSE;
++ return DBUS_OK;
++ }
++ }
++
++ return DBUS_ERR;
++}
++
++/** called during attach phase. */
++static int
++dbus_irbq_init(dhd_bus_t *dhd_bus, dbus_irbq_t *q, int nq, int size_irb)
++{
++ int i;
++ dbus_irb_t *irb;
++
++ ASSERT(q);
++ ASSERT(dhd_bus);
++
++ 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(dhd_bus->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(dhd_bus_t *dhd_bus, dbus_irbq_t *q, int size_irb)
++{
++ dbus_irb_t *irb;
++
++ ASSERT(q);
++ ASSERT(dhd_bus);
++
++ /* 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(dhd_bus->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(dhd_bus_t *dhd_bus)
++{
++ int err = DBUS_OK;
++
++
++ dbus_irb_rx_t *rxirb;
++ struct exec_parms args;
++
++ ASSERT(dhd_bus);
++ if (dhd_bus->pub.busstate != DBUS_STATE_UP) {
++ DBUSERR(("dbus_rxirbs_fill: DBUS not up \n"));
++ return DBUS_ERR;
++ } else if (!dhd_bus->drvintf || (dhd_bus->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 = dhd_bus->rx_q;
++ while ((!dhd_bus->rxoff) &&
++ (rxirb = (EXEC_RXLOCK(dhd_bus, q_deq_exec, &args))) != NULL) {
++ err = dhd_bus->drvintf->recv_irb(dhd_bus->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 = dhd_bus->rx_q;
++ args.qenq.b = (dbus_irb_t *) rxirb;
++ EXEC_RXLOCK(dhd_bus, 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)
++{
++ dhd_bus_t *dhd_bus = (dhd_bus_t *) pub;
++
++ if (dhd_bus == NULL)
++ return;
++
++ DBUSTRACE(("%s\n", __FUNCTION__));
++
++ if (dhd_bus->rxoff == on)
++ return;
++
++ dhd_bus->rxoff = on;
++
++ if (dhd_bus->pub.busstate == DBUS_STATE_UP) {
++ if (!on) {
++ /* post more irbs, resume rx if necessary */
++ dbus_rxirbs_fill(dhd_bus);
++ if (dhd_bus && dhd_bus->drvintf->recv_resume) {
++ dhd_bus->drvintf->recv_resume(dhd_bus->bus_info);
++ }
++ } else {
++ /* ??? cancell posted irbs first */
++
++ if (dhd_bus && dhd_bus->drvintf->recv_stop) {
++ dhd_bus->drvintf->recv_stop(dhd_bus->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)
++{
++ dhd_bus_t *dhd_bus = (dhd_bus_t *) pub;
++ int err = DBUS_OK;
++ dbus_irb_tx_t *txirb = NULL;
++ int txirb_pending;
++ struct exec_parms args;
++
++ if (dhd_bus == NULL)
++ return DBUS_ERR;
++
++ DBUSTRACE(("%s\n", __FUNCTION__));
++
++ if (dhd_bus->pub.busstate == DBUS_STATE_UP ||
++ dhd_bus->pub.busstate == DBUS_STATE_SLEEP) {
++ args.qdeq.q = dhd_bus->tx_q;
++ if (dhd_bus->drvintf)
++ txirb = EXEC_TXLOCK(dhd_bus, 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 (dhd_bus->drvintf && dhd_bus->drvintf->send_irb) {
++ /* call lower DBUS level send_irb function */
++ err = dhd_bus->drvintf->send_irb(dhd_bus->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 = dhd_bus->tx_q;
++ args.qenq.b = (dbus_irb_t *) txirb;
++ EXEC_TXLOCK(dhd_bus, q_enq_exec, &args);
++ } else {
++ dbus_tx_timer_start(dhd_bus, DBUS_TX_TIMEOUT_INTERVAL);
++ txirb_pending = dhd_bus->pub.ntxq - dhd_bus->tx_q->cnt;
++ if (txirb_pending > (dhd_bus->tx_low_watermark * 3)) {
++ dbus_flowctrl_tx(dhd_bus, 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;
++}
++
++#ifdef EXTERNAL_FW_PATH
++static int
++dbus_get_fw_nvram(dhd_bus_t *dhd_bus, char *pfw_path, char *pnv_path)
++{
++ int bcmerror = -1, i;
++ uint len, total_len;
++ void *nv_image = NULL, *fw_image = NULL;
++ char *nv_memblock = NULL, *fw_memblock = NULL;
++ char *bufp;
++ bool file_exists;
++ uint8 nvram_words_pad = 0;
++ uint memblock_size = 2048;
++ uint8 *memptr;
++ int actual_fwlen;
++ struct trx_header *hdr;
++ uint32 img_offset = 0;
++ int offset = 0;
++
++ /* For Get nvram */
++ file_exists = ((pnv_path != NULL) && (pnv_path[0] != '\0'));
++ if (file_exists) {
++ nv_image = dhd_os_open_image(pnv_path);
++ if (nv_image == NULL) {
++ printf("%s: Open nvram file failed %s\n", __FUNCTION__, pnv_path);
++ goto err;
++ }
++ }
++ nv_memblock = MALLOC(dhd_bus->pub.osh, MAX_NVRAMBUF_SIZE);
++ if (nv_memblock == NULL) {
++ DBUSERR(("%s: Failed to allocate memory %d bytes\n",
++ __FUNCTION__, MAX_NVRAMBUF_SIZE));
++ goto err;
++ }
++ len = dhd_os_get_image_block(nv_memblock, MAX_NVRAMBUF_SIZE, nv_image);
++ if (len > 0 && len < MAX_NVRAMBUF_SIZE) {
++ bufp = (char *)nv_memblock;
++ bufp[len] = 0;
++ dhd_bus->nvram_len = process_nvram_vars(bufp, len);
++ if (dhd_bus->nvram_len % 4)
++ nvram_words_pad = 4 - dhd_bus->nvram_len % 4;
++ } else {
++ DBUSERR(("%s: error reading nvram file: %d\n", __FUNCTION__, len));
++ bcmerror = DBUS_ERR_NVRAM;
++ goto err;
++ }
++ if (nv_image)
++ dhd_os_close_image(nv_image);
++
++ /* For Get first block of fw to calculate total_len */
++ file_exists = ((pfw_path != NULL) && (pfw_path[0] != '\0'));
++ if (file_exists) {
++ fw_image = dhd_os_open_image(pfw_path);
++ if (fw_image == NULL) {
++ printf("%s: Open fw file failed %s\n", __FUNCTION__, pfw_path);
++ goto err;
++ }
++ }
++ memptr = fw_memblock = MALLOC(dhd_bus->pub.osh, memblock_size);
++ if (fw_memblock == NULL) {
++ DBUSERR(("%s: Failed to allocate memory %d bytes\n", __FUNCTION__,
++ memblock_size));
++ goto err;
++ }
++ len = dhd_os_get_image_block((char*)memptr, memblock_size, fw_image);
++ if ((actual_fwlen = check_file(dhd_bus->pub.osh, memptr)) <= 0) {
++ DBUSERR(("%s: bad firmware format!\n", __FUNCTION__));
++ goto err;
++ }
++
++ total_len = actual_fwlen + dhd_bus->nvram_len + nvram_words_pad;
++ dhd_bus->image = MALLOC(dhd_bus->pub.osh, total_len);
++ dhd_bus->image_len = total_len;
++ if (dhd_bus->image == NULL) {
++ DBUSERR(("%s: malloc failed!\n", __FUNCTION__));
++ goto err;
++ }
++
++ /* Step1: Copy trx header + firmwre */
++ memptr = fw_memblock;
++ do {
++ if (len < 0) {
++ DBUSERR(("%s: dhd_os_get_image_block failed (%d)\n", __FUNCTION__, len));
++ bcmerror = BCME_ERROR;
++ goto err;
++ }
++ bcopy(memptr, dhd_bus->image+offset, len);
++ offset += len;
++ } while ((len = dhd_os_get_image_block((char*)memptr, memblock_size, fw_image)));
++ /* Step2: Copy NVRAM + pad */
++ hdr = (struct trx_header *)dhd_bus->image;
++ img_offset = SIZEOF_TRX(hdr) + hdr->offsets[TRX_OFFSETS_DLFWLEN_IDX];
++ bcopy(nv_memblock, (uint8 *)(dhd_bus->image + img_offset),
++ dhd_bus->nvram_len);
++ img_offset += dhd_bus->nvram_len;
++ if (nvram_words_pad) {
++ bzero(&dhd_bus->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])) {
++ DBUSERR(("%s: fix me\n", __FUNCTION__));
++ }
++#endif /* BCMTRXV2 */
++ /* Step4: update TRX header for nvram size */
++ hdr = (struct trx_header *)dhd_bus->image;
++ hdr->len = htol32(total_len);
++ /* Pass the actual fw len */
++ hdr->offsets[TRX_OFFSETS_NVM_LEN_IDX] =
++ htol32(dhd_bus->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 < total_len; ++i)
++ hdr->crc32 = hndcrc32((uint8 *)&dhd_bus->image[i], 1, hdr->crc32);
++ hdr->crc32 = htol32(hdr->crc32);
++
++ bcmerror = DBUS_OK;
++
++err:
++ if (fw_memblock)
++ MFREE(dhd_bus->pub.osh, fw_memblock, MAX_NVRAMBUF_SIZE);
++ if (fw_image)
++ dhd_os_close_image(fw_image);
++ if (nv_memblock)
++ MFREE(dhd_bus->pub.osh, nv_memblock, MAX_NVRAMBUF_SIZE);
++ if (nv_image)
++ dhd_os_close_image(nv_image);
++
++ return bcmerror;
++}
++
++/**
++ * during driver initialization ('attach') or after PnP 'resume', firmware needs to be loaded into
++ * the dongle
++ */
++static int
++dbus_do_download(dhd_bus_t *dhd_bus, char *pfw_path, char *pnv_path)
++{
++ int err = DBUS_OK;
++
++ err = dbus_get_fw_nvram(dhd_bus, pfw_path, pnv_path);
++ if (err) {
++ DBUSERR(("dbus_do_download: fail to get nvram %d\n", err));
++ return err;
++ }
++
++ if (dhd_bus->drvintf->dlstart && dhd_bus->drvintf->dlrun) {
++ err = dhd_bus->drvintf->dlstart(dhd_bus->bus_info,
++ dhd_bus->image, dhd_bus->image_len);
++ if (err == DBUS_OK) {
++ err = dhd_bus->drvintf->dlrun(dhd_bus->bus_info);
++ }
++ } else
++ err = DBUS_ERR;
++
++ if (dhd_bus->image) {
++ MFREE(dhd_bus->pub.osh, dhd_bus->image, dhd_bus->image_len);
++ dhd_bus->image = NULL;
++ dhd_bus->image_len = 0;
++ }
++
++ return err;
++} /* dbus_do_download */
++#else
++
++/**
++ * 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(dhd_bus_t *dhd_bus)
++{
++ 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(dhd_bus, &boardtype, &boardrev);
++
++ ret = dbus_select_nvram(dhd_bus, dhd_bus->extdl.vars, dhd_bus->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;
++ }
++ dhd_bus->nvram = nvram;
++ dhd_bus->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(dhd_bus_t *dhd_bus)
++{
++ int len, i;
++ struct trx_header *hdr;
++ int actual_fwlen;
++ uint32 img_offset = 0;
++
++ dhd_bus->nvram_len = 0;
++ if (dhd_bus->extdl.varslen) {
++ if (DBUS_OK != dbus_jumbo_nvram(dhd_bus))
++ return DBUS_ERR_NVRAM;
++ DBUSERR(("NVRAM %d bytes downloaded\n", dhd_bus->nvram_len));
++ }
++#if defined(BCM_REQUEST_FW)
++ else if (nonfwnvram) {
++ dhd_bus->nvram = nonfwnvram;
++ dhd_bus->nvram_len = nonfwnvramlen;
++ DBUSERR(("NVRAM %d bytes downloaded\n", dhd_bus->nvram_len));
++ }
++#endif
++ if (dhd_bus->nvram) {
++ uint8 nvram_words_pad = 0;
++ /* Validate the format/length etc of the file */
++ if ((actual_fwlen = check_file(dhd_bus->pub.osh, dhd_bus->fw)) <= 0) {
++ DBUSERR(("%s: bad firmware format!\n", __FUNCTION__));
++ return DBUS_ERR_NVRAM;
++ }
++
++ if (!dhd_bus->nvram_nontxt) {
++ /* host supplied nvram could be in .txt format
++ * with all the comments etc...
++ */
++ dhd_bus->nvram_len = process_nvram_vars(dhd_bus->nvram,
++ dhd_bus->nvram_len);
++ }
++ if (dhd_bus->nvram_len % 4)
++ nvram_words_pad = 4 - dhd_bus->nvram_len % 4;
++
++ len = actual_fwlen + dhd_bus->nvram_len + nvram_words_pad;
++ dhd_bus->image = MALLOC(dhd_bus->pub.osh, len);
++ dhd_bus->image_len = len;
++ if (dhd_bus->image == NULL) {
++ DBUSERR(("%s: malloc failed!\n", __FUNCTION__));
++ return DBUS_ERR_NVRAM;
++ }
++ hdr = (struct trx_header *)dhd_bus->fw;
++ /* Step1: Copy trx header + firmwre */
++ img_offset = SIZEOF_TRX(hdr) + hdr->offsets[TRX_OFFSETS_DLFWLEN_IDX];
++ bcopy(dhd_bus->fw, dhd_bus->image, img_offset);
++ /* Step2: Copy NVRAM + pad */
++ bcopy(dhd_bus->nvram, (uint8 *)(dhd_bus->image + img_offset),
++ dhd_bus->nvram_len);
++ img_offset += dhd_bus->nvram_len;
++ if (nvram_words_pad) {
++ bzero(&dhd_bus->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(dhd_bus->fw + SIZEOF_TRX(hdr) +
++ hdr->offsets[TRX_OFFSETS_DLFWLEN_IDX] +
++ hdr->offsets[TRX_OFFSETS_NVM_LEN_IDX],
++ dhd_bus->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 *)dhd_bus->image;
++ hdr->len = htol32(len);
++ /* Pass the actual fw len */
++ hdr->offsets[TRX_OFFSETS_NVM_LEN_IDX] =
++ htol32(dhd_bus->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 *)&dhd_bus->image[i], 1, hdr->crc32);
++ hdr->crc32 = htol32(hdr->crc32);
++ } else {
++ dhd_bus->image = dhd_bus->fw;
++ dhd_bus->image_len = (uint32)dhd_bus->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(dhd_bus_t *dhd_bus)
++{
++ 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)
++ dhd_bus->firmware = dbus_get_fw_nvfile(dhd_bus->pub.attrib.devid,
++ dhd_bus->pub.attrib.chiprev, &dhd_bus->fw, &dhd_bus->fwlen,
++ DBUS_FIRMWARE, 0, 0);
++ if (!dhd_bus->firmware)
++ return DBUS_ERR;
++#endif
++
++ dhd_bus->image = dhd_bus->fw;
++ dhd_bus->image_len = (uint32)dhd_bus->fwlen;
++
++#ifndef BCM_REQUEST_FW
++ if (UNZIP_ENAB(dhd_bus) && !decomp_override) {
++ err = dbus_zlib_decomp(dhd_bus);
++ 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(dhd_bus, &boardtype, &boardrev);
++ /* check if nvram is provided as separte file */
++ nonfwnvram = NULL;
++ nonfwnvramlen = 0;
++ dhd_bus->nvfile = dbus_get_fw_nvfile(dhd_bus->pub.attrib.devid,
++ dhd_bus->pub.attrib.chiprev, (void *)&temp_nvram, &temp_len,
++ DBUS_NVFILE, boardtype, boardrev);
++ if (dhd_bus->nvfile) {
++ int8 *tmp = MALLOC(dhd_bus->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(dhd_bus);
++ if (err) {
++ DBUSERR(("dbus_do_download: fail to get nvram %d\n", err));
++ return err;
++ }
++
++
++ if (dhd_bus->drvintf->dlstart && dhd_bus->drvintf->dlrun) {
++ err = dhd_bus->drvintf->dlstart(dhd_bus->bus_info,
++ dhd_bus->image, dhd_bus->image_len);
++
++ if (err == DBUS_OK)
++ err = dhd_bus->drvintf->dlrun(dhd_bus->bus_info);
++ } else
++ err = DBUS_ERR;
++
++ if (dhd_bus->nvram) {
++ MFREE(dhd_bus->pub.osh, dhd_bus->image, dhd_bus->image_len);
++ dhd_bus->image = dhd_bus->fw;
++ dhd_bus->image_len = (uint32)dhd_bus->fwlen;
++ }
++
++#ifndef BCM_REQUEST_FW
++ if (UNZIP_ENAB(dhd_bus) && (!decomp_override) && dhd_bus->orig_fw) {
++ MFREE(dhd_bus->pub.osh, dhd_bus->fw, dhd_bus->decomp_memsize);
++ dhd_bus->image = dhd_bus->fw = dhd_bus->orig_fw;
++ dhd_bus->image_len = dhd_bus->fwlen = dhd_bus->origfw_len;
++ }
++#endif
++
++#if defined(BCM_REQUEST_FW)
++fail:
++ if (dhd_bus->firmware) {
++ dbus_release_fw_nvfile(dhd_bus->firmware);
++ dhd_bus->firmware = NULL;
++ }
++ if (dhd_bus->nvfile) {
++ dbus_release_fw_nvfile(dhd_bus->nvfile);
++ dhd_bus->nvfile = NULL;
++ }
++ if (nonfwnvram) {
++ MFREE(dhd_bus->pub.osh, nonfwnvram, nonfwnvramlen);
++ nonfwnvram = NULL;
++ nonfwnvramlen = 0;
++ }
++#endif
++ return err;
++} /* dbus_do_download */
++#endif /* EXTERNAL_FW_PATH */
++#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)
++{
++ dhd_bus_t *dhd_bus = (dhd_bus_t *) handle;
++
++ if ((dhd_bus == NULL) || (dhd_bus->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)
++{
++ dhd_bus_t *dhd_bus = (dhd_bus_t *) handle;
++ int txirb_pending;
++ struct exec_parms args;
++ void *pktinfo;
++
++ if ((dhd_bus == NULL) || (txirb == NULL)) {
++ return;
++ }
++
++ DBUSTRACE(("%s: status = %d\n", __FUNCTION__, status));
++
++ dbus_tx_timer_stop(dhd_bus);
++
++ /* 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 = dhd_bus->tx_q;
++ args.qenq.b = (dbus_irb_t *) txirb;
++ EXEC_TXLOCK(dhd_bus, q_enq_exec, &args);
++
++ if (dhd_bus->pub.busstate != DBUS_STATE_DOWN) {
++ if ((status == DBUS_OK) || (status == DBUS_ERR_NODEVICE)) {
++ if (dhd_bus->cbs && dhd_bus->cbs->send_complete)
++ dhd_bus->cbs->send_complete(dhd_bus->cbarg, pktinfo,
++ status);
++
++ if (status == DBUS_OK) {
++ txirb_pending = dhd_bus->pub.ntxq - dhd_bus->tx_q->cnt;
++ if (txirb_pending)
++ dbus_tx_timer_start(dhd_bus, DBUS_TX_TIMEOUT_INTERVAL);
++ if ((txirb_pending < dhd_bus->tx_low_watermark) &&
++ dhd_bus->txoff && !dhd_bus->txoverride) {
++ dbus_flowctrl_tx(dhd_bus, 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 (dhd_bus->cbs && dhd_bus->cbs->send_complete)
++ dhd_bus->cbs->send_complete(dhd_bus->cbarg, pktinfo,
++ status);
++#else
++ dbus_if_pktfree(dhd_bus, (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 (dhd_bus->cbs && dhd_bus->cbs->send_complete)
++ dhd_bus->cbs->send_complete(dhd_bus->cbarg, pktinfo,
++ status);
++#else
++ dbus_if_pktfree(dhd_bus, (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)
++{
++ dhd_bus_t *dhd_bus = (dhd_bus_t *) handle;
++ int rxirb_pending;
++ struct exec_parms args;
++
++ if ((dhd_bus == NULL) || (rxirb == NULL)) {
++ return;
++ }
++ DBUSTRACE(("%s\n", __FUNCTION__));
++ if (dhd_bus->pub.busstate != DBUS_STATE_DOWN &&
++ dhd_bus->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 (dhd_bus->cbs && dhd_bus->cbs->recv_buf) {
++ dhd_bus->cbs->recv_buf(dhd_bus->cbarg, rxirb->buf,
++ rxirb->actual_len);
++ }
++ } else if (rxirb->pkt != NULL) {
++ if (dhd_bus->cbs && dhd_bus->cbs->recv_pkt)
++ dhd_bus->cbs->recv_pkt(dhd_bus->cbarg, rxirb->pkt);
++ } else {
++ ASSERT(0); /* Should not happen */
++ }
++
++ rxirb_pending = dhd_bus->pub.nrxq - dhd_bus->rx_q->cnt - 1;
++ if ((rxirb_pending <= dhd_bus->rx_low_watermark) &&
++ !dhd_bus->rxoff) {
++ DBUSTRACE(("Low watermark so submit more %d <= %d \n",
++ dhd_bus->rx_low_watermark, rxirb_pending));
++ dbus_rxirbs_fill(dhd_bus);
++ } else if (dhd_bus->rxoff)
++ DBUSTRACE(("rx flow controlled. not filling more. cut_rxq=%d\n",
++ dhd_bus->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(dhd_bus->pub.osh, rxirb->buf);
++ PKTFREE(dhd_bus->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(dhd_bus->pub.osh, rxirb->buf);
++ PKTFREE(dhd_bus->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(dhd_bus->pub.osh, rxirb->buf);
++ PKTFREE(dhd_bus->pub.osh, rxirb->buf, FALSE);
++ }
++#endif /* BCM_RPC_NOCOPY || BCM_RPC_TXNOCOPY || BCM_RPC_TOC */
++ }
++ if (dhd_bus->rx_q != NULL) {
++ bzero(rxirb, sizeof(dbus_irb_rx_t));
++ args.qenq.q = dhd_bus->rx_q;
++ args.qenq.b = (dbus_irb_t *) rxirb;
++ EXEC_RXLOCK(dhd_bus, q_enq_exec, &args);
++ } else
++ MFREE(dhd_bus->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)
++{
++ dhd_bus_t *dhd_bus = handle;
++ uint32 mask = 0;
++
++ if (dhd_bus == NULL)
++ return;
++
++ switch (err) {
++ case DBUS_ERR_TXFAIL:
++ dhd_bus->pub.stats.tx_errors++;
++ mask |= ERR_CBMASK_TXFAIL;
++ break;
++ case DBUS_ERR_TXDROP:
++ dhd_bus->pub.stats.tx_dropped++;
++ mask |= ERR_CBMASK_TXFAIL;
++ break;
++ case DBUS_ERR_RXFAIL:
++ dhd_bus->pub.stats.rx_errors++;
++ mask |= ERR_CBMASK_RXFAIL;
++ break;
++ case DBUS_ERR_RXDROP:
++ dhd_bus->pub.stats.rx_dropped++;
++ mask |= ERR_CBMASK_RXFAIL;
++ break;
++ default:
++ break;
++ }
++
++ if (dhd_bus->cbs && dhd_bus->cbs->errhandler && (dhd_bus->errmask & mask))
++ dhd_bus->cbs->errhandler(dhd_bus->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)
++{
++ dhd_bus_t *dhd_bus = (dhd_bus_t *) handle;
++
++ DBUSTRACE(("%s\n", __FUNCTION__));
++
++ if (dhd_bus == NULL) {
++ DBUSERR(("%s: dhd_bus is NULL\n", __FUNCTION__));
++ return;
++ }
++
++ if (dhd_bus->pub.busstate != DBUS_STATE_DOWN) {
++ if (dhd_bus->cbs && dhd_bus->cbs->ctl_complete)
++ dhd_bus->cbs->ctl_complete(dhd_bus->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)
++{
++ dhd_bus_t *dhd_bus = (dhd_bus_t *) handle;
++ int old_state;
++
++ if (dhd_bus == NULL)
++ return;
++
++ if (dhd_bus->pub.busstate == state)
++ return;
++ old_state = dhd_bus->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)
++ dhd_bus->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(dhd_bus);
++ dbus_flowctrl_rx(handle, FALSE);
++ }
++
++ if (dhd_bus->cbs && dhd_bus->cbs->state_change)
++ dhd_bus->cbs->state_change(dhd_bus->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)
++{
++ dhd_bus_t *dhd_bus = (dhd_bus_t *) handle;
++ void *p = NULL;
++
++ if (dhd_bus == NULL)
++ return NULL;
++
++ if (dhd_bus->cbs && dhd_bus->cbs->pktget)
++ p = dhd_bus->cbs->pktget(dhd_bus->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)
++{
++ dhd_bus_t *dhd_bus = (dhd_bus_t *) handle;
++
++ if (dhd_bus == NULL)
++ return;
++
++ if (dhd_bus->cbs && dhd_bus->cbs->pktfree)
++ dhd_bus->cbs->pktfree(dhd_bus->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)
++{
++ dhd_bus_t *dhd_bus = (dhd_bus_t *) cbarg;
++ struct exec_parms args;
++ struct dbus_irb *irb;
++
++ if ((dhd_bus == NULL) || (dhd_bus->pub.busstate != DBUS_STATE_UP))
++ return NULL;
++
++ if (send == TRUE) {
++ args.qdeq.q = dhd_bus->tx_q;
++ irb = EXEC_TXLOCK(dhd_bus, q_deq_exec, &args);
++ } else {
++ args.qdeq.q = dhd_bus->rx_q;
++ irb = EXEC_RXLOCK(dhd_bus, 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, uint16 bus_no,
++ uint16 slot, uint32 hdrlen)
++{
++ DBUSTRACE(("%s\n", __FUNCTION__));
++ if (probe_cb) {
++ disc_arg = probe_cb(probe_arg, desc, bustype, bus_no, slot, 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
++dhd_bus_register(void)
++{
++ int err;
++
++ DBUSTRACE(("%s: Enter\n", __FUNCTION__));
++
++ probe_cb = dhd_dbus_probe_cb;
++ disconnect_cb = dhd_dbus_disconnect_cb;
++ probe_arg = NULL;
++
++ err = dbus_bus_register(0xa5c, 0x48f, dbus_probe, /* call lower DBUS level register function */
++ dbus_disconnect, NULL, &g_busintf, NULL, NULL);
++
++ /* Device not detected */
++ if (err == DBUS_ERR_NODEVICE)
++ err = DBUS_OK;
++
++ return err;
++}
++
++dhd_pub_t *g_pub = NULL;
++void
++dhd_bus_unregister(void)
++{
++ int ret;
++
++ DBUSTRACE(("%s\n", __FUNCTION__));
++
++ DHD_MUTEX_LOCK();
++ if (g_pub) {
++ g_pub->dhd_remove = TRUE;
++ if (!g_pub->bus) {
++ dhd_dbus_disconnect_cb(g_pub->bus);
++ }
++ }
++ probe_cb = NULL;
++ DHD_MUTEX_UNLOCK();
++ ret = dbus_bus_deregister();
++ disconnect_cb = NULL;
++ probe_arg = NULL;
++}
++
++/** As part of initialization, data structures have to be allocated and initialized */
++dhd_bus_t *
++dbus_attach(osl_t *osh, int rxsize, int nrxq, int ntxq, dhd_pub_t *pub,
++ dbus_callbacks_t *cbs, dbus_extdl_t *extdl, struct shared_info *sh)
++{
++ dhd_bus_t *dhd_bus;
++ 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;
++
++ dhd_bus = MALLOC(osh, sizeof(dhd_bus_t));
++ if (dhd_bus == NULL) {
++ DBUSERR(("%s: malloc failed %d\n", __FUNCTION__, sizeof(dhd_bus_t)));
++ return NULL;
++ }
++
++ bzero(dhd_bus, sizeof(dhd_bus_t));
++
++ /* BUS-specific driver interface (at a lower DBUS level) */
++ dhd_bus->drvintf = g_busintf;
++ dhd_bus->cbarg = pub;
++ dhd_bus->cbs = cbs;
++
++ dhd_bus->pub.sh = sh;
++ dhd_bus->pub.osh = osh;
++ dhd_bus->pub.rxsize = rxsize;
++
++ dhd_bus->pub.nrxq = nrxq;
++ dhd_bus->rx_low_watermark = nrxq / 2; /* keep enough posted rx urbs */
++ dhd_bus->pub.ntxq = ntxq;
++ dhd_bus->tx_low_watermark = ntxq / 4; /* flow control when too many tx urbs posted */
++
++ dhd_bus->tx_q = MALLOC(osh, sizeof(dbus_irbq_t));
++ if (dhd_bus->tx_q == NULL)
++ goto error;
++ else {
++ bzero(dhd_bus->tx_q, sizeof(dbus_irbq_t));
++ err = dbus_irbq_init(dhd_bus, dhd_bus->tx_q, ntxq, sizeof(dbus_irb_tx_t));
++ if (err != DBUS_OK)
++ goto error;
++ }
++
++ dhd_bus->rx_q = MALLOC(osh, sizeof(dbus_irbq_t));
++ if (dhd_bus->rx_q == NULL)
++ goto error;
++ else {
++ bzero(dhd_bus->rx_q, sizeof(dbus_irbq_t));
++ err = dbus_irbq_init(dhd_bus, dhd_bus->rx_q, nrxq, sizeof(dbus_irb_rx_t));
++ if (err != DBUS_OK)
++ goto error;
++ }
++
++
++ dhd_bus->bus_info = (void *)g_busintf->attach(&dhd_bus->pub,
++ dhd_bus, &dbus_intf_cbs);
++ if (dhd_bus->bus_info == NULL)
++ goto error;
++
++ dbus_tx_timer_init(dhd_bus);
++
++#if defined(BCM_REQUEST_FW)
++ /* Need to copy external image for re-download */
++ if (extdl && extdl->fw && (extdl->fwlen > 0)) {
++ dhd_bus->extdl.fw = MALLOC(osh, extdl->fwlen);
++ if (dhd_bus->extdl.fw) {
++ bcopy(extdl->fw, dhd_bus->extdl.fw, extdl->fwlen);
++ dhd_bus->extdl.fwlen = extdl->fwlen;
++ }
++ }
++
++ if (extdl && extdl->vars && (extdl->varslen > 0)) {
++ dhd_bus->extdl.vars = MALLOC(osh, extdl->varslen);
++ if (dhd_bus->extdl.vars) {
++ bcopy(extdl->vars, dhd_bus->extdl.vars, extdl->varslen);
++ dhd_bus->extdl.varslen = extdl->varslen;
++ }
++ }
++#endif
++
++ return (dhd_bus_t *)dhd_bus;
++
++error:
++ DBUSERR(("%s: Failed\n", __FUNCTION__));
++ dbus_detach(dhd_bus);
++ return NULL;
++} /* dbus_attach */
++
++void
++dbus_detach(dhd_bus_t *pub)
++{
++ dhd_bus_t *dhd_bus = (dhd_bus_t *) pub;
++ osl_t *osh;
++
++ DBUSTRACE(("%s\n", __FUNCTION__));
++
++ if (dhd_bus == NULL)
++ return;
++
++ dbus_tx_timer_stop(dhd_bus);
++
++ osh = pub->pub.osh;
++
++ if (dhd_bus->drvintf && dhd_bus->drvintf->detach)
++ dhd_bus->drvintf->detach((dbus_pub_t *)dhd_bus, dhd_bus->bus_info);
++
++ if (dhd_bus->tx_q) {
++ dbus_irbq_deinit(dhd_bus, dhd_bus->tx_q, sizeof(dbus_irb_tx_t));
++ MFREE(osh, dhd_bus->tx_q, sizeof(dbus_irbq_t));
++ dhd_bus->tx_q = NULL;
++ }
++
++ if (dhd_bus->rx_q) {
++ dbus_irbq_deinit(dhd_bus, dhd_bus->rx_q, sizeof(dbus_irb_rx_t));
++ MFREE(osh, dhd_bus->rx_q, sizeof(dbus_irbq_t));
++ dhd_bus->rx_q = NULL;
++ }
++
++
++ if (dhd_bus->extdl.fw && (dhd_bus->extdl.fwlen > 0)) {
++ MFREE(osh, dhd_bus->extdl.fw, dhd_bus->extdl.fwlen);
++ dhd_bus->extdl.fw = NULL;
++ dhd_bus->extdl.fwlen = 0;
++ }
++
++ if (dhd_bus->extdl.vars && (dhd_bus->extdl.varslen > 0)) {
++ MFREE(osh, dhd_bus->extdl.vars, dhd_bus->extdl.varslen);
++ dhd_bus->extdl.vars = NULL;
++ dhd_bus->extdl.varslen = 0;
++ }
++
++ MFREE(osh, dhd_bus, sizeof(dhd_bus_t));
++} /* dbus_detach */
++
++int dbus_dlneeded(dhd_bus_t *pub)
++{
++ dhd_bus_t *dhd_bus = (dhd_bus_t *) pub;
++ int dlneeded = DBUS_ERR;
++
++ if (!dhd_bus) {
++ DBUSERR(("%s: dhd_bus is NULL\n", __FUNCTION__));
++ return DBUS_ERR;
++ }
++
++ DBUSTRACE(("%s: state %d\n", __FUNCTION__, dhd_bus->pub.busstate));
++
++ if (dhd_bus->drvintf->dlneeded) {
++ dlneeded = dhd_bus->drvintf->dlneeded(dhd_bus->bus_info);
++ }
++ printf("%s: dlneeded=%d\n", __FUNCTION__, dlneeded);
++
++ /* dlneeded > 0: need to download
++ * dlneeded = 0: downloaded
++ * dlneeded < 0: bus error*/
++ return dlneeded;
++}
++
++#if defined(BCM_REQUEST_FW)
++int dbus_download_firmware(dhd_bus_t *pub, char *pfw_path, char *pnv_path)
++{
++ dhd_bus_t *dhd_bus = (dhd_bus_t *) pub;
++ int err = DBUS_OK;
++
++ if (!dhd_bus) {
++ DBUSERR(("%s: dhd_bus is NULL\n", __FUNCTION__));
++ return DBUS_ERR;
++ }
++
++ DBUSTRACE(("%s: state %d\n", __FUNCTION__, dhd_bus->pub.busstate));
++
++ dhd_bus->pub.busstate = DBUS_STATE_DL_PENDING;
++#ifdef EXTERNAL_FW_PATH
++ err = dbus_do_download(dhd_bus, pfw_path, pnv_path);
++#else
++ err = dbus_do_download(dhd_bus);
++#endif /* EXTERNAL_FW_PATH */
++ if (err == DBUS_OK) {
++ dhd_bus->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(struct dhd_bus *pub)
++{
++ dhd_bus_t *dhd_bus = (dhd_bus_t *) pub;
++ int err = DBUS_OK;
++
++ DBUSTRACE(("%s\n", __FUNCTION__));
++
++ if (dhd_bus == NULL) {
++ DBUSERR(("%s: dhd_bus is NULL\n", __FUNCTION__));
++ return DBUS_ERR;
++ }
++
++ if ((dhd_bus->pub.busstate == DBUS_STATE_DL_DONE) ||
++ (dhd_bus->pub.busstate == DBUS_STATE_DOWN) ||
++ (dhd_bus->pub.busstate == DBUS_STATE_SLEEP)) {
++ if (dhd_bus->drvintf && dhd_bus->drvintf->up) {
++ err = dhd_bus->drvintf->up(dhd_bus->bus_info);
++
++ if (err == DBUS_OK) {
++ dbus_rxirbs_fill(dhd_bus);
++ }
++ }
++ } else
++ err = DBUS_ERR;
++
++ return err;
++}
++
++/** higher layer requests us to 'down' the interface to the dongle. */
++int
++dbus_down(dbus_pub_t *pub)
++{
++ dhd_bus_t *dhd_bus = (dhd_bus_t *) pub;
++
++ DBUSTRACE(("%s\n", __FUNCTION__));
++
++ if (dhd_bus == NULL)
++ return DBUS_ERR;
++
++ dbus_tx_timer_stop(dhd_bus);
++
++ if (dhd_bus->pub.busstate == DBUS_STATE_UP ||
++ dhd_bus->pub.busstate == DBUS_STATE_SLEEP) {
++ if (dhd_bus->drvintf && dhd_bus->drvintf->down)
++ return dhd_bus->drvintf->down(dhd_bus->bus_info);
++ }
++
++ return DBUS_ERR;
++}
++
++int
++dbus_shutdown(dbus_pub_t *pub)
++{
++ dhd_bus_t *dhd_bus = (dhd_bus_t *) pub;
++
++ DBUSTRACE(("%s\n", __FUNCTION__));
++
++ if (dhd_bus == NULL)
++ return DBUS_ERR;
++
++ if (dhd_bus->drvintf && dhd_bus->drvintf->shutdown)
++ return dhd_bus->drvintf->shutdown(dhd_bus->bus_info);
++
++ return DBUS_OK;
++}
++
++int
++dbus_stop(struct dhd_bus *pub)
++{
++ dhd_bus_t *dhd_bus = (dhd_bus_t *) pub;
++
++ DBUSTRACE(("%s\n", __FUNCTION__));
++
++ if (dhd_bus == NULL)
++ return DBUS_ERR;
++
++ if (dhd_bus->pub.busstate == DBUS_STATE_UP ||
++ dhd_bus->pub.busstate == DBUS_STATE_SLEEP) {
++ if (dhd_bus->drvintf && dhd_bus->drvintf->stop)
++ return dhd_bus->drvintf->stop(dhd_bus->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(struct dhd_bus *pub, uint8 *buf, int len)
++{
++ dhd_bus_t *dhd_bus = (dhd_bus_t *) pub;
++
++ if (dhd_bus == NULL) {
++ DBUSERR(("%s: dhd_bus is NULL\n", __FUNCTION__));
++ return DBUS_ERR;
++ }
++
++ if (dhd_bus->pub.busstate == DBUS_STATE_UP ||
++ dhd_bus->pub.busstate == DBUS_STATE_SLEEP) {
++ if (dhd_bus->drvintf && dhd_bus->drvintf->send_ctl)
++ return dhd_bus->drvintf->send_ctl(dhd_bus->bus_info, buf, len);
++ } else {
++ DBUSERR(("%s: bustate=%d\n", __FUNCTION__, dhd_bus->pub.busstate));
++ }
++
++ return DBUS_ERR;
++}
++
++int
++dbus_recv_ctl(struct dhd_bus *pub, uint8 *buf, int len)
++{
++ dhd_bus_t *dhd_bus = (dhd_bus_t *) pub;
++
++ if ((dhd_bus == NULL) || (buf == NULL))
++ return DBUS_ERR;
++
++ if (dhd_bus->pub.busstate == DBUS_STATE_UP ||
++ dhd_bus->pub.busstate == DBUS_STATE_SLEEP) {
++ if (dhd_bus->drvintf && dhd_bus->drvintf->recv_ctl)
++ return dhd_bus->drvintf->recv_ctl(dhd_bus->bus_info, buf, len);
++ }
++
++ return DBUS_ERR;
++}
++
++/** Only called via RPC (Dec 2012) */
++int
++dbus_recv_bulk(dbus_pub_t *pub, uint32 ep_idx)
++{
++ dhd_bus_t *dhd_bus = (dhd_bus_t *) pub;
++
++ dbus_irb_rx_t *rxirb;
++ struct exec_parms args;
++ int status;
++
++
++ if (dhd_bus == NULL)
++ return DBUS_ERR;
++
++ args.qdeq.q = dhd_bus->rx_q;
++ if (dhd_bus->pub.busstate == DBUS_STATE_UP) {
++ if (dhd_bus->drvintf && dhd_bus->drvintf->recv_irb_from_ep) {
++ if ((rxirb = (EXEC_RXLOCK(dhd_bus, q_deq_exec, &args))) != NULL) {
++ status = dhd_bus->drvintf->recv_irb_from_ep(dhd_bus->bus_info,
++ rxirb, ep_idx);
++ if (status == DBUS_ERR_RXDROP) {
++ bzero(rxirb, sizeof(dbus_irb_rx_t));
++ args.qenq.q = dhd_bus->rx_q;
++ args.qenq.b = (dbus_irb_t *) rxirb;
++ EXEC_RXLOCK(dhd_bus, q_enq_exec, &args);
++ }
++ }
++ }
++ }
++
++ return DBUS_ERR;
++}
++
++/** only called by dhd_cdc.c (Dec 2012) */
++int
++dbus_poll_intr(dbus_pub_t *pub)
++{
++ dhd_bus_t *dhd_bus = (dhd_bus_t *) pub;
++
++ int status = DBUS_ERR;
++
++ if (dhd_bus == NULL)
++ return DBUS_ERR;
++
++ if (dhd_bus->pub.busstate == DBUS_STATE_UP) {
++ if (dhd_bus->drvintf && dhd_bus->drvintf->recv_irb_from_ep) {
++ status = dhd_bus->drvintf->recv_irb_from_ep(dhd_bus->bus_info,
++ NULL, 0xff);
++ }
++ }
++ return status;
++}
++
++/** called by nobody (Dec 2012) */
++void *
++dbus_pktget(dbus_pub_t *pub, int len)
++{
++ dhd_bus_t *dhd_bus = (dhd_bus_t *) pub;
++
++ if ((dhd_bus == NULL) || (len < 0))
++ return NULL;
++
++ return PKTGET(dhd_bus->pub.osh, len, TRUE);
++}
++
++/** called by nobody (Dec 2012) */
++void
++dbus_pktfree(dbus_pub_t *pub, void* pkt)
++{
++ dhd_bus_t *dhd_bus = (dhd_bus_t *) pub;
++
++ if ((dhd_bus == NULL) || (pkt == NULL))
++ return;
++
++ PKTFREE(dhd_bus->pub.osh, pkt, TRUE);
++}
++
++/** called by nobody (Dec 2012) */
++int
++dbus_get_stats(dbus_pub_t *pub, dbus_stats_t *stats)
++{
++ dhd_bus_t *dhd_bus = (dhd_bus_t *) pub;
++
++ if ((dhd_bus == NULL) || (stats == NULL))
++ return DBUS_ERR;
++
++ bcopy(&dhd_bus->pub.stats, stats, sizeof(dbus_stats_t));
++
++ return DBUS_OK;
++}
++
++int
++dbus_get_attrib(dhd_bus_t *pub, dbus_attrib_t *attrib)
++{
++ dhd_bus_t *dhd_bus = (dhd_bus_t *) pub;
++ int err = DBUS_ERR;
++
++ if ((dhd_bus == NULL) || (attrib == NULL))
++ return DBUS_ERR;
++
++ if (dhd_bus->drvintf && dhd_bus->drvintf->get_attrib) {
++ err = dhd_bus->drvintf->get_attrib(dhd_bus->bus_info,
++ &dhd_bus->pub.attrib);
++ }
++
++ bcopy(&dhd_bus->pub.attrib, attrib, sizeof(dbus_attrib_t));
++ return err;
++}
++
++int
++dbus_get_device_speed(dbus_pub_t *pub)
++{
++ dhd_bus_t *dhd_bus = (dhd_bus_t *) pub;
++
++ if (dhd_bus == NULL)
++ return INVALID_SPEED;
++
++ return (dhd_bus->pub.device_speed);
++}
++
++int
++dbus_set_config(dbus_pub_t *pub, dbus_config_t *config)
++{
++ dhd_bus_t *dhd_bus = (dhd_bus_t *) pub;
++ int err = DBUS_ERR;
++
++ if ((dhd_bus == NULL) || (config == NULL))
++ return DBUS_ERR;
++
++ if (dhd_bus->drvintf && dhd_bus->drvintf->set_config) {
++ err = dhd_bus->drvintf->set_config(dhd_bus->bus_info,
++ config);
++
++ if ((config->config_id == DBUS_CONFIG_ID_AGGR_LIMIT) &&
++ (!err) &&
++ (dhd_bus->pub.busstate == DBUS_STATE_UP)) {
++ dbus_rxirbs_fill(dhd_bus);
++ }
++ }
++
++ return err;
++}
++
++int
++dbus_get_config(dbus_pub_t *pub, dbus_config_t *config)
++{
++ dhd_bus_t *dhd_bus = (dhd_bus_t *) pub;
++ int err = DBUS_ERR;
++
++ if ((dhd_bus == NULL) || (config == NULL))
++ return DBUS_ERR;
++
++ if (dhd_bus->drvintf && dhd_bus->drvintf->get_config) {
++ err = dhd_bus->drvintf->get_config(dhd_bus->bus_info,
++ config);
++ }
++
++ return err;
++}
++
++int
++dbus_set_errmask(dbus_pub_t *pub, uint32 mask)
++{
++ dhd_bus_t *dhd_bus = (dhd_bus_t *) pub;
++ int err = DBUS_OK;
++
++ if (dhd_bus == NULL)
++ return DBUS_ERR;
++
++ dhd_bus->errmask = mask;
++ return err;
++}
++
++int
++dbus_pnp_resume(dbus_pub_t *pub, int *fw_reload)
++{
++ dhd_bus_t *dhd_bus = (dhd_bus_t *) pub;
++ int err = DBUS_ERR;
++ bool fwdl = FALSE;
++
++ DBUSTRACE(("%s\n", __FUNCTION__));
++
++ if (dhd_bus == NULL)
++ return DBUS_ERR;
++
++ if (dhd_bus->pub.busstate == DBUS_STATE_UP) {
++ return DBUS_OK;
++ }
++
++
++
++ if (dhd_bus->drvintf->pnp) {
++ err = dhd_bus->drvintf->pnp(dhd_bus->bus_info,
++ DBUS_PNP_RESUME);
++ }
++
++ if (dhd_bus->drvintf->recv_needed) {
++ if (dhd_bus->drvintf->recv_needed(dhd_bus->bus_info)) {
++ /* Refill after sleep/hibernate */
++ dbus_rxirbs_fill(dhd_bus);
++ }
++ }
++
++
++ if (fw_reload)
++ *fw_reload = fwdl;
++
++ return err;
++} /* dbus_pnp_resume */
++
++int
++dbus_pnp_sleep(dbus_pub_t *pub)
++{
++ dhd_bus_t *dhd_bus = (dhd_bus_t *) pub;
++ int err = DBUS_ERR;
++
++ DBUSTRACE(("%s\n", __FUNCTION__));
++
++ if (dhd_bus == NULL)
++ return DBUS_ERR;
++
++ dbus_tx_timer_stop(dhd_bus);
++
++ if (dhd_bus->drvintf && dhd_bus->drvintf->pnp) {
++ err = dhd_bus->drvintf->pnp(dhd_bus->bus_info,
++ DBUS_PNP_SLEEP);
++ }
++
++ return err;
++}
++
++int
++dbus_pnp_disconnect(dbus_pub_t *pub)
++{
++ dhd_bus_t *dhd_bus = (dhd_bus_t *) pub;
++ int err = DBUS_ERR;
++
++ DBUSTRACE(("%s\n", __FUNCTION__));
++
++ if (dhd_bus == NULL)
++ return DBUS_ERR;
++
++ dbus_tx_timer_stop(dhd_bus);
++
++ if (dhd_bus->drvintf && dhd_bus->drvintf->pnp) {
++ err = dhd_bus->drvintf->pnp(dhd_bus->bus_info,
++ DBUS_PNP_DISCONNECT);
++ }
++
++ return err;
++}
++
++int
++dhd_bus_iovar_op(dhd_pub_t *dhdp, const char *name,
++ void *params, int plen, void *arg, int len, bool set)
++{
++ dhd_bus_t *dhd_bus = (dhd_bus_t *) dhdp->bus;
++ int err = DBUS_ERR;
++
++ DBUSTRACE(("%s\n", __FUNCTION__));
++
++ if (dhd_bus == NULL)
++ return DBUS_ERR;
++
++ if (dhd_bus->drvintf && dhd_bus->drvintf->iovar_op) {
++ err = dhd_bus->drvintf->iovar_op(dhd_bus->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) && !defined(EXTERNAL_FW_PATH)
++static int
++dbus_otp(dhd_bus_t *dhd_bus, 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 (dhd_bus == NULL || dhd_bus->drvintf == NULL ||
++ dhd_bus->drvintf->readreg == NULL)
++ return DBUS_ERR;
++
++ devid = dhd_bus->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(dhd_bus->pub.osh, otp_size * 2);
++ if (cis == NULL)
++ return DBUS_ERR;
++
++ otpinfo = (uint16 *) cis;
++
++ for (i = 0; i < otp_size; i++) {
++
++ ret = dhd_bus->drvintf->readreg(dhd_bus->bus_info,
++ otp_addr + ((otp_sw_rgn + i) << 1), 2, &value);
++
++ if (ret != DBUS_OK) {
++ MFREE(dhd_bus->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(dhd_bus->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(dhd_bus_t *dhd_bus, 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;
++ }
++
++ dhd_bus->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
++
++#define DBUS_NRXQ 50
++#define DBUS_NTXQ 100
++
++static void
++dhd_dbus_send_complete(void *handle, void *info, int status)
++{
++ dhd_pub_t *dhd = (dhd_pub_t *)handle;
++ void *pkt = info;
++
++ if ((dhd == NULL) || (pkt == NULL)) {
++ DBUSERR(("dhd or pkt is NULL\n"));
++ return;
++ }
++
++ if (status == DBUS_OK) {
++ dhd->dstats.tx_packets++;
++ } else {
++ DBUSERR(("TX error=%d\n", status));
++ dhd->dstats.tx_errors++;
++ }
++#ifdef PROP_TXSTATUS
++ if (DHD_PKTTAG_WLFCPKT(PKTTAG(pkt)) &&
++ (dhd_wlfc_txcomplete(dhd, pkt, status == 0) != WLFC_UNSUPPORTED)) {
++ return;
++ }
++#endif /* PROP_TXSTATUS */
++ PKTFREE(dhd->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_pub_t *dhd = (dhd_pub_t *)handle;
++ int ifidx = 0;
++
++ if (dhd == NULL) {
++ DBUSERR(("%s: dhd is NULL\n", __FUNCTION__));
++ return;
++ }
++
++ /* If the protocol uses a data header, check and remove it */
++ if (dhd_prot_hdrpull(dhd, &ifidx, pkt, reorder_info_buf,
++ &reorder_info_len) != 0) {
++ DBUSERR(("rx protocol error\n"));
++ PKTFREE(dhd->osh, pkt, FALSE);
++ dhd->rx_errors++;
++ return;
++ }
++
++ if (reorder_info_len) {
++ /* Reordering info from the firmware */
++ dhd_process_pkt_reorder_info(dhd, reorder_info_buf, reorder_info_len,
++ &pkt, &pkt_count);
++ if (pkt_count == 0)
++ return;
++ }
++ else {
++ pkt_count = 1;
++ }
++ dhd_rx_frame(dhd, ifidx, pkt, pkt_count, 0);
++}
++
++static void
++dhd_dbus_recv_buf(void *handle, uint8 *buf, int len)
++{
++ dhd_pub_t *dhd = (dhd_pub_t *)handle;
++ void *pkt;
++
++ if (dhd == NULL) {
++ DBUSERR(("%s: dhd is NULL\n", __FUNCTION__));
++ return;
++ }
++
++ if ((pkt = PKTGET(dhd->osh, len, FALSE)) == NULL) {
++ DBUSERR(("PKTGET (rx) failed=%d\n", len));
++ return;
++ }
++
++ bcopy(buf, PKTDATA(dhd->osh, pkt), len);
++ dhd_dbus_recv_pkt(dhd, pkt);
++}
++
++static void
++dhd_dbus_txflowcontrol(void *handle, bool onoff)
++{
++ dhd_pub_t *dhd = (dhd_pub_t *)handle;
++ bool wlfc_enabled = FALSE;
++
++ if (dhd == NULL) {
++ DBUSERR(("%s: dhd is NULL\n", __FUNCTION__));
++ return;
++ }
++
++#ifdef PROP_TXSTATUS
++ wlfc_enabled = (dhd_wlfc_flowcontrol(dhd, onoff, !onoff) != WLFC_UNSUPPORTED);
++#endif
++
++ if (!wlfc_enabled) {
++ dhd_txflowcontrol(dhd, 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_pub_t *dhd = (dhd_pub_t *)handle;
++
++ if (dhd == NULL) {
++ DBUSERR(("%s: dhd is NULL\n", __FUNCTION__));
++ return;
++ }
++
++ if (type == DBUS_CBCTL_READ) {
++ if (status == DBUS_OK)
++ dhd->rx_ctlpkts++;
++ else
++ dhd->rx_ctlerrs++;
++ } else if (type == DBUS_CBCTL_WRITE) {
++ if (status == DBUS_OK)
++ dhd->tx_ctlpkts++;
++ else
++ dhd->tx_ctlerrs++;
++ }
++
++ dhd_prot_ctl_complete(dhd);
++}
++
++static void
++dhd_dbus_state_change(void *handle, int state)
++{
++ dhd_pub_t *dhd = (dhd_pub_t *)handle;
++
++ if (dhd == NULL) {
++ DBUSERR(("%s: dhd is NULL\n", __FUNCTION__));
++ return;
++ }
++
++ switch (state) {
++
++ case DBUS_STATE_DL_NEEDED:
++ DBUSERR(("%s: firmware request cannot be handled\n", __FUNCTION__));
++ break;
++ case DBUS_STATE_DOWN:
++ DBUSTRACE(("%s: DBUS is down\n", __FUNCTION__));
++ dhd->busstate = DHD_BUS_DOWN;
++ break;
++ case DBUS_STATE_UP:
++ DBUSTRACE(("%s: DBUS is up\n", __FUNCTION__));
++ dhd->busstate = DHD_BUS_DATA;
++ break;
++ default:
++ break;
++ }
++
++ DBUSERR(("%s: DBUS current state=%d\n", __FUNCTION__, state));
++}
++
++static void *
++dhd_dbus_pktget(void *handle, uint len, bool send)
++{
++ dhd_pub_t *dhd = (dhd_pub_t *)handle;
++ void *p = NULL;
++
++ if (dhd == NULL) {
++ DBUSERR(("%s: dhd is NULL\n", __FUNCTION__));
++ return NULL;
++ }
++
++ if (send == TRUE) {
++ dhd_os_sdlock_txq(dhd);
++ p = PKTGET(dhd->osh, len, TRUE);
++ dhd_os_sdunlock_txq(dhd);
++ } else {
++ dhd_os_sdlock_rxq(dhd);
++ p = PKTGET(dhd->osh, len, FALSE);
++ dhd_os_sdunlock_rxq(dhd);
++ }
++
++ return p;
++}
++
++static void
++dhd_dbus_pktfree(void *handle, void *p, bool send)
++{
++ dhd_pub_t *dhd = (dhd_pub_t *)handle;
++
++ if (dhd == NULL) {
++ DBUSERR(("%s: dhd is NULL\n", __FUNCTION__));
++ return;
++ }
++
++ if (send == TRUE) {
++#ifdef PROP_TXSTATUS
++ if (DHD_PKTTAG_WLFCPKT(PKTTAG(p)) &&
++ (dhd_wlfc_txcomplete(dhd, p, FALSE) != WLFC_UNSUPPORTED)) {
++ return;
++ }
++#endif /* PROP_TXSTATUS */
++
++ dhd_os_sdlock_txq(dhd);
++ PKTFREE(dhd->osh, p, TRUE);
++ dhd_os_sdunlock_txq(dhd);
++ } else {
++ dhd_os_sdlock_rxq(dhd);
++ PKTFREE(dhd->osh, p, FALSE);
++ dhd_os_sdunlock_rxq(dhd);
++ }
++}
++
++
++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
++};
++
++uint
++dhd_bus_chip(struct dhd_bus *bus)
++{
++ ASSERT(bus != NULL);
++ return bus->pub.attrib.devid;
++}
++
++uint
++dhd_bus_chiprev(struct dhd_bus *bus)
++{
++ ASSERT(bus);
++ ASSERT(bus != NULL);
++ return bus->pub.attrib.chiprev;
++}
++
++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)
++{
++}
++
++int
++dhd_bus_txdata(struct dhd_bus *bus, void *pktbuf)
++{
++ DBUSTRACE(("%s\n", __FUNCTION__));
++ if (bus->txoff) {
++ DBUSTRACE(("txoff\n"));
++ return BCME_EPERM;
++ }
++ return dbus_send_txdata(&bus->pub, pktbuf);
++}
++
++static void
++dhd_dbus_advertise_bus_cleanup(dhd_pub_t *dhdp)
++{
++ unsigned long flags;
++ int timeleft;
++
++ DHD_LINUX_GENERAL_LOCK(dhdp, flags);
++ dhdp->busstate = DHD_BUS_DOWN_IN_PROGRESS;
++ DHD_LINUX_GENERAL_UNLOCK(dhdp, flags);
++
++ timeleft = dhd_os_busbusy_wait_negation(dhdp, &dhdp->dhd_bus_busy_state);
++ if ((timeleft == 0) || (timeleft == 1)) {
++ DBUSERR(("%s : Timeout due to dhd_bus_busy_state=0x%x\n",
++ __FUNCTION__, dhdp->dhd_bus_busy_state));
++ ASSERT(0);
++ }
++
++ return;
++}
++
++static void
++dhd_dbus_advertise_bus_remove(dhd_pub_t *dhdp)
++{
++ unsigned long flags;
++ int timeleft;
++
++ DHD_LINUX_GENERAL_LOCK(dhdp, flags);
++ dhdp->busstate = DHD_BUS_REMOVE;
++ DHD_LINUX_GENERAL_UNLOCK(dhdp, flags);
++
++ timeleft = dhd_os_busbusy_wait_negation(dhdp, &dhdp->dhd_bus_busy_state);
++ if ((timeleft == 0) || (timeleft == 1)) {
++ DBUSERR(("%s : Timeout due to dhd_bus_busy_state=0x%x\n",
++ __FUNCTION__, dhdp->dhd_bus_busy_state));
++ ASSERT(0);
++ }
++
++ return;
++}
++
++int
++dhd_bus_devreset(dhd_pub_t *dhdp, uint8 flag)
++{
++ int bcmerror = 0;
++ unsigned long flags;
++ wifi_adapter_info_t *adapter = (wifi_adapter_info_t *)dhdp->adapter;
++
++ if (flag == TRUE) {
++ if (!dhdp->dongle_reset) {
++ DBUSERR(("%s: == Power OFF ==\n", __FUNCTION__));
++ dhd_dbus_advertise_bus_cleanup(dhdp);
++ dhd_os_wd_timer(dhdp, 0);
++#if !defined(IGNORE_ETH0_DOWN)
++ /* Force flow control as protection when stop come before ifconfig_down */
++ dhd_txflowcontrol(dhdp, ALL_INTERFACES, ON);
++#endif /* !defined(IGNORE_ETH0_DOWN) */
++ dbus_stop(dhdp->bus);
++
++ dhdp->dongle_reset = TRUE;
++ dhdp->up = FALSE;
++
++ DHD_LINUX_GENERAL_LOCK(dhdp, flags);
++ dhdp->busstate = DHD_BUS_DOWN;
++ DHD_LINUX_GENERAL_UNLOCK(dhdp, flags);
++ wifi_clr_adapter_status(adapter, WIFI_STATUS_FW_READY);
++
++ printf("%s: WLAN OFF DONE\n", __FUNCTION__);
++ /* App can now remove power from device */
++ } else
++ bcmerror = BCME_ERROR;
++ } else {
++ /* App must have restored power to device before calling */
++ printf("\n\n%s: == WLAN ON ==\n", __FUNCTION__);
++ if (dhdp->dongle_reset) {
++ /* Turn on WLAN */
++ DHD_MUTEX_UNLOCK();
++ wait_event_interruptible_timeout(adapter->status_event,
++ wifi_get_adapter_status(adapter, WIFI_STATUS_FW_READY),
++ msecs_to_jiffies(DHD_FW_READY_TIMEOUT));
++ DHD_MUTEX_LOCK();
++ bcmerror = dbus_up(dhdp->bus);
++ if (bcmerror == BCME_OK) {
++ dhdp->dongle_reset = FALSE;
++ dhdp->up = TRUE;
++#if !defined(IGNORE_ETH0_DOWN)
++ /* Restore flow control */
++ dhd_txflowcontrol(dhdp, ALL_INTERFACES, OFF);
++#endif
++ dhd_os_wd_timer(dhdp, dhd_watchdog_ms);
++
++ DBUSTRACE(("%s: WLAN ON DONE\n", __FUNCTION__));
++ } else {
++ DBUSERR(("%s: failed to dbus_up with code %d\n", __FUNCTION__, bcmerror));
++ }
++ }
++ }
++
++#ifdef PKT_STATICS
++ memset((uint8*) &tx_statics, 0, sizeof(pkt_statics_t));
++#endif
++ return bcmerror;
++}
++
++void
++dhd_set_path_params(struct dhd_bus *bus)
++{
++ /* External conf takes precedence if specified */
++ dhd_conf_preinit(bus->dhd);
++
++ if (bus->dhd->conf_path[0] == '\0') {
++ dhd_conf_set_path(bus->dhd, "config.txt", bus->dhd->conf_path, bus->nv_path);
++ }
++ if (bus->dhd->clm_path[0] == '\0') {
++ dhd_conf_set_path(bus->dhd, "clm.blob", bus->dhd->clm_path, bus->fw_path);
++ }
++#ifdef CONFIG_PATH_AUTO_SELECT
++ dhd_conf_set_conf_name_by_chip(bus->dhd, bus->dhd->conf_path);
++#endif
++
++ dhd_conf_read_config(bus->dhd, bus->dhd->conf_path);
++
++ dhd_conf_set_fw_name_by_chip(bus->dhd, bus->fw_path);
++ dhd_conf_set_nv_name_by_chip(bus->dhd, bus->nv_path);
++ dhd_conf_set_clm_name_by_chip(bus->dhd, bus->dhd->clm_path);
++
++ printf("Final fw_path=%s\n", bus->fw_path);
++ printf("Final nv_path=%s\n", bus->nv_path);
++ printf("Final clm_path=%s\n", bus->dhd->clm_path);
++ printf("Final conf_path=%s\n", bus->dhd->conf_path);
++
++}
++
++void
++dhd_bus_update_fw_nv_path(struct dhd_bus *bus, char *pfw_path,
++ char *pnv_path, char *pclm_path, char *pconf_path)
++{
++ DBUSTRACE(("%s\n", __FUNCTION__));
++
++ if (bus == NULL) {
++ DBUSERR(("%s: bus is NULL\n", __FUNCTION__));
++ return;
++ }
++
++ bus->fw_path = pfw_path;
++ bus->nv_path = pnv_path;
++ bus->dhd->clm_path = pclm_path;
++ bus->dhd->conf_path = pconf_path;
++
++ dhd_set_path_params(bus);
++
++}
++
++/*
++ * hdrlen is space to reserve in pkt headroom for DBUS
++ */
++void *
++dhd_dbus_probe_cb(void *arg, const char *desc, uint32 bustype,
++ uint16 bus_no, uint16 slot, uint32 hdrlen)
++{
++ osl_t *osh = NULL;
++ dhd_bus_t *bus = NULL;
++ dhd_pub_t *pub = NULL;
++ uint rxsz;
++ int dlneeded = 0;
++ wifi_adapter_info_t *adapter = NULL;
++
++ DBUSTRACE(("%s: Enter\n", __FUNCTION__));
++
++ adapter = dhd_wifi_platform_get_adapter(bustype, bus_no, slot);
++
++ if (!g_pub) {
++ /* Ask the OS interface part for an OSL handle */
++ if (!(osh = osl_attach(NULL, bustype, TRUE))) {
++ DBUSERR(("%s: OSL attach failed\n", __FUNCTION__));
++ goto fail;
++ }
++
++ /* Attach to the dhd/OS interface */
++ if (!(pub = dhd_attach(osh, bus, hdrlen, adapter))) {
++ DBUSERR(("%s: dhd_attach failed\n", __FUNCTION__));
++ goto fail;
++ }
++ } else {
++ pub = g_pub;
++ }
++
++ if (pub->bus) {
++ DBUSERR(("%s: wrong probe\n", __FUNCTION__));
++ goto fail;
++ }
++
++ rxsz = dhd_get_rxsz(pub);
++ bus = dbus_attach(osh, rxsz, DBUS_NRXQ, DBUS_NTXQ, pub, &dhd_dbus_cbs, NULL, NULL);
++ if (bus) {
++ pub->bus = bus;
++ bus->dhd = pub;
++
++ dlneeded = dbus_dlneeded(bus);
++ if (dlneeded >= 0) {
++ if (!g_pub) {
++ dhd_conf_reset(pub);
++ dhd_conf_set_chiprev(pub, bus->pub.attrib.devid, bus->pub.attrib.chiprev);
++ dhd_conf_preinit(pub);
++ }
++ }
++
++ if (g_pub || dhd_download_fw_on_driverload) {
++ if (dlneeded == 0) {
++ wifi_set_adapter_status(adapter, WIFI_STATUS_FW_READY);
++#ifdef BCM_REQUEST_FW
++ } else if (dlneeded > 0) {
++ dhd_set_path(bus->dhd);
++ if (dbus_download_firmware(bus, bus->fw_path, bus->nv_path) != DBUS_OK)
++ goto fail;
++#endif
++ }
++ }
++ } else {
++ DBUSERR(("%s: dbus_attach failed\n", __FUNCTION__));
++ }
++
++ if (!g_pub) {
++ /* Ok, finish the attach to the OS network interface */
++ if (dhd_register_if(pub, 0, TRUE) != 0) {
++ DBUSERR(("%s: dhd_register_if failed\n", __FUNCTION__));
++ goto fail;
++ }
++ pub->hang_report = TRUE;
++#if defined(MULTIPLE_SUPPLICANT)
++ wl_android_post_init(); // terence 20120530: fix critical section in dhd_open and dhdsdio_probe
++#endif
++ g_pub = pub;
++ }
++
++ DBUSTRACE(("%s: Exit\n", __FUNCTION__));
++ wifi_clr_adapter_status(adapter, WIFI_STATUS_DETTACH);
++ wifi_set_adapter_status(adapter, WIFI_STATUS_ATTACH);
++ wake_up_interruptible(&adapter->status_event);
++ /* This is passed to dhd_dbus_disconnect_cb */
++ return bus;
++
++fail:
++ if (pub && pub->bus) {
++ dbus_detach(pub->bus);
++ pub->bus = NULL;
++ }
++ /* Release resources in reverse order */
++ if (!g_pub) {
++ if (pub) {
++ dhd_detach(pub);
++ dhd_free(pub);
++ }
++ if (osh) {
++ osl_detach(osh);
++ }
++ }
++
++ printf("%s: Failed\n", __FUNCTION__);
++ return NULL;
++}
++
++void
++dhd_dbus_disconnect_cb(void *arg)
++{
++ dhd_bus_t *bus = (dhd_bus_t *)arg;
++ dhd_pub_t *pub = g_pub;
++ osl_t *osh;
++ wifi_adapter_info_t *adapter = NULL;
++
++ adapter = (wifi_adapter_info_t *)pub->adapter;
++
++ if (pub && !pub->dhd_remove && bus == NULL) {
++ DBUSERR(("%s: bus is NULL\n", __FUNCTION__));
++ return;
++ }
++ if (!adapter) {
++ DBUSERR(("%s: adapter is NULL\n", __FUNCTION__));
++ return;
++ }
++
++ printf("%s: Enter dhd_remove=%d on %s\n", __FUNCTION__,
++ pub->dhd_remove, adapter->name);
++ if (!pub->dhd_remove) {
++ /* Advertise bus remove during rmmod */
++ dhd_dbus_advertise_bus_remove(bus->dhd);
++ dbus_detach(pub->bus);
++ pub->bus = NULL;
++ wifi_clr_adapter_status(adapter, WIFI_STATUS_ATTACH);
++ wifi_set_adapter_status(adapter, WIFI_STATUS_DETTACH);
++ wake_up_interruptible(&adapter->status_event);
++ } else {
++ osh = pub->osh;
++ dhd_detach(pub);
++ if (pub->bus) {
++ dbus_detach(pub->bus);
++ pub->bus = NULL;
++ }
++ dhd_free(pub);
++ g_pub = NULL;
++ if (MALLOCED(osh)) {
++ DBUSERR(("%s: MEMORY LEAK %d bytes\n", __FUNCTION__, MALLOCED(osh)));
++ }
++ osl_detach(osh);
++ }
++
++ DBUSTRACE(("%s: Exit\n", __FUNCTION__));
++}
++
++#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_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);
++
++MODULE_LICENSE("GPL");
++
++module_init(bcm_dbus_module_init);
++module_exit(bcm_dbus_module_exit);
++
++#endif /* #ifdef LINUX_EXTERNAL_MODULE_DBUS */
+diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dbus_usb.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dbus_usb.c
+new file mode 100644
+index 000000000000..3be28b2da9d4
+--- /dev/null
++++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dbus_usb.c
+@@ -0,0 +1,1173 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Dongle BUS interface for USB, OS independent
++ *
++ * Copyright (C) 1999-2016, Broadcom Corporation
++ *
++ * Unless you and Broadcom execute a separate written software license
++ * agreement governing use of this software, this software is licensed to you
++ * under the terms of the GNU General Public License version 2 (the "GPL"),
++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
++ * following added to such license:
++ *
++ * As a special exception, the copyright holders of this software give you
++ * permission to link this software with independent modules, and to copy and
++ * distribute the resulting executable under terms of your choice, provided that
++ * you also meet, for each linked independent module, the terms and conditions of
++ * the license of that module. An independent module is a module which is not
++ * derived from this software. The special exception does not apply to any
++ * modifications of the software.
++ *
++ * Notwithstanding the above, under no circumstances may you combine this
++ * software in any way with any other Broadcom software provided under a license
++ * other than the GPL, without Broadcom's express prior written consent.
++ *
++ *
++ * <>
++ *
++ * $Id: dbus_usb.c 565557 2015-06-22 19:29:44Z $
++ */
++
++/**
++ * @file @brief
++ * This file contains DBUS code that is USB, but not OS specific. DBUS is a Broadcom proprietary
++ * host specific abstraction layer.
++ */
++
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++
++uint dbus_msglevel = DBUS_ERROR_VAL;
++module_param(dbus_msglevel, int, 0);
++
++
++#define USB_DLIMAGE_RETRY_TIMEOUT 3000 /* retry Timeout */
++#define USB_SFLASH_DLIMAGE_SPINWAIT 150 /* in unit of ms */
++#define USB_SFLASH_DLIMAGE_LIMIT 2000 /* spinwait limit (ms) */
++#define POSTBOOT_ID 0xA123 /* ID to detect if dongle has boot up */
++#define USB_RESETCFG_SPINWAIT 1 /* wait after resetcfg (ms) */
++#define USB_DEV_ISBAD(u) (u->pub->attrib.devid == 0xDEAD)
++#define USB_DLGO_SPINWAIT 100 /* wait after DL_GO (ms) */
++#define TEST_CHIP 0x4328
++
++typedef struct {
++ dbus_pub_t *pub;
++
++ void *cbarg;
++ dbus_intf_callbacks_t *cbs; /** callbacks into higher DBUS level (dbus.c) */
++ dbus_intf_t *drvintf;
++ void *usbosl_info;
++ uint32 rdlram_base_addr;
++ uint32 rdlram_size;
++} usb_info_t;
++
++/*
++ * Callbacks common to all USB
++ */
++static void dbus_usb_disconnect(void *handle);
++static void dbus_usb_send_irb_timeout(void *handle, dbus_irb_tx_t *txirb);
++static void dbus_usb_send_irb_complete(void *handle, dbus_irb_tx_t *txirb, int status);
++static void dbus_usb_recv_irb_complete(void *handle, dbus_irb_rx_t *rxirb, int status);
++static void dbus_usb_errhandler(void *handle, int err);
++static void dbus_usb_ctl_complete(void *handle, int type, int status);
++static void dbus_usb_state_change(void *handle, int state);
++static struct dbus_irb* dbus_usb_getirb(void *handle, bool send);
++static void dbus_usb_rxerr_indicate(void *handle, bool on);
++#if !defined(BCM_REQUEST_FW)
++static int dbus_usb_resetcfg(usb_info_t *usbinfo);
++#endif
++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 int dbus_usb_dlneeded(void *bus);
++static int dbus_usb_dlrun(void *bus);
++static int dbus_usb_rdl_dwnld_state(usb_info_t *usbinfo);
++
++
++/* OS specific */
++extern bool dbus_usbos_dl_cmd(void *info, uint8 cmd, void *buffer, int buflen);
++extern int dbus_usbos_wait(void *info, uint16 ms);
++extern int dbus_write_membytes(usb_info_t *usbinfo, bool set, uint32 address,
++ uint8 *data, uint size);
++extern bool dbus_usbos_dl_send_bulk(void *info, void *buffer, int len);
++extern int dbus_usbos_loopback_tx(void *usbos_info_ptr, int cnt, int size);
++
++/**
++ * These functions are called by the lower DBUS level (dbus_usb_os.c) to notify this DBUS level
++ * (dbus_usb.c) of an event.
++ */
++static dbus_intf_callbacks_t dbus_usb_intf_cbs = {
++ dbus_usb_send_irb_timeout,
++ dbus_usb_send_irb_complete,
++ dbus_usb_recv_irb_complete,
++ dbus_usb_errhandler,
++ dbus_usb_ctl_complete,
++ dbus_usb_state_change,
++ NULL, /* isr */
++ NULL, /* dpc */
++ NULL, /* watchdog */
++ NULL, /* dbus_if_pktget */
++ NULL, /* dbus_if_pktfree */
++ dbus_usb_getirb,
++ dbus_usb_rxerr_indicate
++};
++
++/* IOVar table */
++enum {
++ IOV_SET_DOWNLOAD_STATE = 1,
++ IOV_DBUS_MSGLEVEL,
++ IOV_MEMBYTES,
++ IOV_VARS,
++ IOV_LOOPBACK_TX
++};
++
++const bcm_iovar_t dhdusb_iovars[] = {
++ {"vars", IOV_VARS, 0, IOVT_BUFFER, 0 },
++ {"dbus_msglevel", IOV_DBUS_MSGLEVEL, 0, IOVT_UINT32, 0 },
++ {"dwnldstate", IOV_SET_DOWNLOAD_STATE, 0, IOVT_BOOL, 0 },
++ {"membytes", IOV_MEMBYTES, 0, IOVT_BUFFER, 2 * sizeof(int) },
++ {"usb_lb_txfer", IOV_LOOPBACK_TX, 0, IOVT_BUFFER, 2 * sizeof(int) },
++ {NULL, 0, 0, 0, 0 }
++};
++
++/*
++ * Need global for probe() and disconnect() since
++ * attach() is not called at probe and detach()
++ * can be called inside disconnect()
++ */
++static probe_cb_t probe_cb = NULL;
++static disconnect_cb_t disconnect_cb = NULL;
++static void *probe_arg = NULL;
++static void *disc_arg = NULL;
++static dbus_intf_t *g_dbusintf = NULL;
++static dbus_intf_t dbus_usb_intf; /** functions called by higher layer DBUS into lower layer */
++
++/*
++ * dbus_intf_t common to all USB
++ * These functions override dbus_usb_.c.
++ */
++static void *dbus_usb_attach(dbus_pub_t *pub, void *cbarg, dbus_intf_callbacks_t *cbs);
++static void dbus_usb_detach(dbus_pub_t *pub, void *info);
++static void * dbus_usb_probe(void *arg, const char *desc, uint32 bustype,
++ uint16 bus_no, uint16 slot, 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, uint16 bus_no,
++ uint16 slot, 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, bus_no, slot, 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 */
++
++#if !defined(BCM_REQUEST_FW)
++/**
++ * 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;
++}
++#endif
++
++/** 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 int
++dbus_usb_dlneeded(void *bus)
++{
++ usb_info_t *usbinfo = BUS_INFO(bus, usb_info_t);
++ void *osinfo;
++ bootrom_id_t id;
++ int dl_needed = 1;
++
++ DBUSTRACE(("%s\n", __FUNCTION__));
++
++ if (usbinfo == NULL)
++ return DBUS_ERR;
++
++ 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 = DBUS_ERR;
++ 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 = DBUS_OK;
++ if (usbinfo->pub->busstate == DBUS_STATE_DL_PENDING)
++ usbinfo->pub->busstate = DBUS_STATE_DL_DONE;
++ } else {
++ usbinfo->pub->attrib.devid = id.chip;
++ usbinfo->pub->attrib.chiprev = id.chiprev;
++ }
++
++exit:
++ return dl_needed;
++}
++
++/** After issuing firmware download, higher DBUS level (dbus.c) wants to start the firmware. */
++static int
++dbus_usb_dlrun(void *bus)
++{
++ usb_info_t *usbinfo = BUS_INFO(bus, usb_info_t);
++ void *osinfo;
++ rdl_state_t state;
++ int err = DBUS_OK;
++
++ DBUSTRACE(("%s\n", __FUNCTION__));
++
++ if (usbinfo == NULL)
++ return DBUS_ERR;
++
++ if (USB_DEV_ISBAD(usbinfo))
++ return DBUS_ERR;
++
++ osinfo = usbinfo->usbosl_info;
++ ASSERT(osinfo);
++
++ /* Check we are runnable */
++ dbus_usbos_dl_cmd(osinfo, DL_GETSTATE, &state, sizeof(rdl_state_t));
++
++ state.state = ltoh32(state.state);
++ state.bytes = ltoh32(state.bytes);
++
++ /* Start the image */
++ if (state.state == DL_RUNNABLE) {
++ DBUSTRACE(("%s: Issue DL_GO\n", __FUNCTION__));
++ dbus_usbos_dl_cmd(osinfo, DL_GO, &state, sizeof(rdl_state_t));
++
++ if (usbinfo->pub->attrib.devid == TEST_CHIP)
++ dbus_usbos_wait(osinfo, USB_DLGO_SPINWAIT);
++
++// dbus_usb_resetcfg(usbinfo);
++ /* The Donlge may go for re-enumeration. */
++ } else {
++ DBUSERR(("%s: Dongle not runnable\n", __FUNCTION__));
++ err = DBUS_ERR;
++ }
++
++ return err;
++}
++
++/**
++ * As preparation for firmware download, higher DBUS level (dbus.c) requests the firmware image
++ * to be used for the type of dongle detected. Directly called by dbus.c (so not via a callback
++ * construction)
++ */
++void
++dbus_bus_fw_get(void *bus, uint8 **fw, int *fwlen, int *decomp)
++{
++ usb_info_t *usbinfo = BUS_INFO(bus, usb_info_t);
++ unsigned int devid;
++ unsigned int crev;
++
++ devid = usbinfo->pub->attrib.devid;
++ crev = usbinfo->pub->attrib.chiprev;
++
++ *fw = NULL;
++ *fwlen = 0;
++
++ switch (devid) {
++ case BCM43236_CHIP_ID:
++ case BCM43235_CHIP_ID:
++ case BCM43234_CHIP_ID:
++ case BCM43238_CHIP_ID: {
++ if (crev == 3 || crev == 2 || crev == 1) {
++#ifdef EMBED_IMAGE_43236b
++ *fw = (uint8 *)dlarray_43236b;
++ *fwlen = sizeof(dlarray_43236b);
++
++#endif
++ }
++ } break;
++ case BCM4360_CHIP_ID:
++ case BCM4352_CHIP_ID:
++ case BCM43526_CHIP_ID:
++#ifdef EMBED_IMAGE_43526a
++ if (crev <= 2) {
++ *fw = (uint8 *)dlarray_43526a;
++ *fwlen = sizeof(dlarray_43526a);
++ }
++#endif
++#ifdef EMBED_IMAGE_43526b
++ if (crev > 2) {
++ *fw = (uint8 *)dlarray_43526b;
++ *fwlen = sizeof(dlarray_43526b);
++ }
++#endif
++ break;
++
++ case BCM43242_CHIP_ID:
++#ifdef EMBED_IMAGE_43242a0
++ *fw = (uint8 *)dlarray_43242a0;
++ *fwlen = sizeof(dlarray_43242a0);
++#endif
++ break;
++
++ case BCM43143_CHIP_ID:
++#ifdef EMBED_IMAGE_43143a0
++ *fw = (uint8 *)dlarray_43143a0;
++ *fwlen = sizeof(dlarray_43143a0);
++#endif
++#ifdef EMBED_IMAGE_43143b0
++ *fw = (uint8 *)dlarray_43143b0;
++ *fwlen = sizeof(dlarray_43143b0);
++#endif
++ break;
++
++ case BCM4350_CHIP_ID:
++ case BCM4354_CHIP_ID:
++ case BCM43556_CHIP_ID:
++ case BCM43558_CHIP_ID:
++ case BCM43566_CHIP_ID:
++ case BCM43568_CHIP_ID:
++ case BCM43570_CHIP_ID:
++ case BCM4358_CHIP_ID:
++#ifdef EMBED_IMAGE_4350a0
++ if (crev == 0) {
++ *fw = (uint8 *)dlarray_4350a0;
++ *fwlen = sizeof(dlarray_4350a0);
++ }
++#endif
++#ifdef EMBED_IMAGE_4350b0
++ if (crev == 1) {
++ *fw = (uint8 *)dlarray_4350b0;
++ *fwlen = sizeof(dlarray_4350b0);
++ }
++#endif
++#ifdef EMBED_IMAGE_4350b1
++ if (crev == 2) {
++ *fw = (uint8 *)dlarray_4350b1;
++ *fwlen = sizeof(dlarray_4350b1);
++ }
++#endif
++#ifdef EMBED_IMAGE_43556b1
++ if (crev == 2) {
++ *fw = (uint8 *)dlarray_43556b1;
++ *fwlen = sizeof(dlarray_43556b1);
++ }
++#endif
++#ifdef EMBED_IMAGE_4350c0
++ if (crev == 3) {
++ *fw = (uint8 *)dlarray_4350c0;
++ *fwlen = sizeof(dlarray_4350c0);
++ }
++#endif /* EMBED_IMAGE_4350c0 */
++#ifdef EMBED_IMAGE_4350c1
++ if (crev == 4) {
++ *fw = (uint8 *)dlarray_4350c1;
++ *fwlen = sizeof(dlarray_4350c1);
++ }
++#endif /* EMBED_IMAGE_4350c1 */
++ break;
++ case BCM43569_CHIP_ID:
++#ifdef EMBED_IMAGE_43569a0
++ if (crev == 0) {
++ *fw = (uint8 *)dlarray_43569a0;
++ *fwlen = sizeof(dlarray_43569a0);
++ }
++#endif /* EMBED_IMAGE_43569a0 */
++ break;
++ default:
++#ifdef EMBED_IMAGE_GENERIC
++ *fw = (uint8 *)dlarray;
++ *fwlen = sizeof(dlarray);
++#endif
++ break;
++ }
++} /* dbus_bus_fw_get */
+diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dbus_usb_linux.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dbus_usb_linux.c
+new file mode 100644
+index 000000000000..8aa9646c8822
+--- /dev/null
++++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dbus_usb_linux.c
+@@ -0,0 +1,3404 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Dongle BUS interface
++ * USB Linux Implementation
++ *
++ * Copyright (C) 1999-2016, Broadcom Corporation
++ *
++ * Unless you and Broadcom execute a separate written software license
++ * agreement governing use of this software, this software is licensed to you
++ * under the terms of the GNU General Public License version 2 (the "GPL"),
++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
++ * following added to such license:
++ *
++ * As a special exception, the copyright holders of this software give you
++ * permission to link this software with independent modules, and to copy and
++ * distribute the resulting executable under terms of your choice, provided that
++ * you also meet, for each linked independent module, the terms and conditions of
++ * the license of that module. An independent module is a module which is not
++ * derived from this software. The special exception does not apply to any
++ * modifications of the software.
++ *
++ * Notwithstanding the above, under no circumstances may you combine this
++ * software in any way with any other Broadcom software provided under a license
++ * other than the GPL, without Broadcom's express prior written consent.
++ *
++ *
++ * <>
++ *
++ * $Id: dbus_usb_linux.c 564663 2015-06-18 02:34:42Z $
++ */
++
++/**
++ * @file @brief
++ * This file contains DBUS code that is USB *and* OS (Linux) specific. DBUS is a Broadcom
++ * proprietary host specific abstraction layer.
++ */
++
++#include
++#include
++
++/**
++ * DBUS_LINUX_RXDPC is created for router platform performance tuning. A separate thread is created
++ * to handle USB RX and avoid the call chain getting too long and enhance cache hit rate.
++ *
++ * DBUS_LINUX_RXDPC setting is in wlconfig file.
++ */
++
++/*
++ * If DBUS_LINUX_RXDPC is off, spin_lock_bh() for CTFPOOL in
++ * linux_osl.c has to be changed to spin_lock_irqsave() because
++ * PKTGET/PKTFREE are no longer in bottom half.
++ *
++ * Right now we have another queue rpcq in wl_linux.c. Maybe we
++ * can eliminate that one to reduce the overhead.
++ *
++ * Enabling 2nd EP and DBUS_LINUX_RXDPC causing traffic from
++ * both EP's to be queued in the same rx queue. If we want
++ * RXDPC to work with 2nd EP. The EP for RPC call return
++ * should bypass the dpc and go directly up.
++ */
++
++/* #define DBUS_LINUX_RXDPC */
++
++/* Dbus histogram for ntxq, nrxq, dpc parameter tuning */
++/* #define DBUS_LINUX_HIST */
++
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++
++#if defined(USBOS_THREAD) || defined(USBOS_TX_THREAD)
++
++/**
++ * The usb-thread is designed to provide currency on multiprocessors and SMP linux kernels. On the
++ * dual cores platform, the WLAN driver, without threads, executed only on CPU0. The driver consumed
++ * almost of 100% on CPU0, while CPU1 remained idle. The behavior was observed on Broadcom's STB.
++ *
++ * The WLAN driver consumed most of CPU0 and not CPU1 because tasklets/queues, software irq, and
++ * hardware irq are executing from CPU0, only. CPU0 became the system's bottle-neck. TPUT is lower
++ * and system's responsiveness is slower.
++ *
++ * To improve system responsiveness and TPUT usb-thread was implemented. The system's threads could
++ * be scheduled to run on any core. One core could be processing data in the usb-layer and the other
++ * core could be processing data in the wl-layer.
++ *
++ * For further info see [WlThreadAndUsbThread] Twiki.
++ */
++
++#include
++#include
++#include
++#include
++#include
++#include
++#endif /* USBOS_THREAD || USBOS_TX_THREAD */
++
++
++
++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
++#define KERNEL26
++#endif
++
++/**
++ * Starting with the 3.10 kernel release, dynamic PM support for USB is present whenever
++ * the kernel was built with CONFIG_PM_RUNTIME enabled. The CONFIG_USB_SUSPEND option has
++ * been eliminated.
++ */
++#if ((LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 21)) && defined(CONFIG_USB_SUSPEND)) \
++ || ((LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)) && defined(CONFIG_PM_RUNTIME))
++/* For USB power management support, see Linux kernel: Documentation/usb/power-management.txt */
++#define USB_SUSPEND_AVAILABLE
++#endif
++
++/* Define alternate fw/nvram paths used in Android */
++#ifdef OEM_ANDROID
++#define CONFIG_ANDROID_BCMDHD_FW_PATH "broadcom/dhd/firmware/fw.bin.trx"
++#define CONFIG_ANDROID_BCMDHD_NVRAM_PATH "broadcom/dhd/nvrams/nvm.txt"
++#endif /* OEM_ANDROID */
++
++static inline int usb_submit_urb_linux(struct urb *urb)
++{
++
++#ifdef BCM_MAX_URB_LEN
++ if (urb && (urb->transfer_buffer_length > BCM_MAX_URB_LEN)) {
++ DBUSERR(("URB transfer length=%d exceeded %d ra=%p\n", urb->transfer_buffer_length,
++ BCM_MAX_URB_LEN, __builtin_return_address(0)));
++ return DBUS_ERR;
++ }
++#endif
++
++#ifdef KERNEL26
++ return usb_submit_urb(urb, GFP_ATOMIC);
++#else
++ return usb_submit_urb(urb);
++#endif
++
++}
++
++#define USB_SUBMIT_URB(urb) usb_submit_urb_linux(urb)
++
++#ifdef KERNEL26
++
++#define USB_ALLOC_URB() usb_alloc_urb(0, GFP_ATOMIC)
++#define USB_UNLINK_URB(urb) (usb_kill_urb(urb))
++#define USB_FREE_URB(urb) (usb_free_urb(urb))
++#define USB_REGISTER() usb_register(&dbus_usbdev)
++#define USB_DEREGISTER() usb_deregister(&dbus_usbdev)
++
++#ifdef USB_SUSPEND_AVAILABLE
++
++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33))
++#define USB_AUTOPM_SET_INTERFACE(intf) usb_autopm_set_interface(intf)
++#else
++#define USB_ENABLE_AUTOSUSPEND(udev) usb_enable_autosuspend(udev)
++#define USB_DISABLE_AUTOSUSPEND(udev) usb_disable_autosuspend(udev)
++#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33)) */
++
++#define USB_AUTOPM_GET_INTERFACE(intf) usb_autopm_get_interface(intf)
++#define USB_AUTOPM_PUT_INTERFACE(intf) usb_autopm_put_interface(intf)
++#define USB_AUTOPM_GET_INTERFACE_ASYNC(intf) usb_autopm_get_interface_async(intf)
++#define USB_AUTOPM_PUT_INTERFACE_ASYNC(intf) usb_autopm_put_interface_async(intf)
++#define USB_MARK_LAST_BUSY(dev) usb_mark_last_busy(dev)
++
++#else /* USB_SUSPEND_AVAILABLE */
++
++#define USB_AUTOPM_GET_INTERFACE(intf) do {} while (0)
++#define USB_AUTOPM_PUT_INTERFACE(intf) do {} while (0)
++#define USB_AUTOPM_GET_INTERFACE_ASYNC(intf) do {} while (0)
++#define USB_AUTOPM_PUT_INTERFACE_ASYNC(intf) do {} while (0)
++#define USB_MARK_LAST_BUSY(dev) do {} while (0)
++#endif /* USB_SUSPEND_AVAILABLE */
++
++#define USB_CONTROL_MSG(dev, pipe, request, requesttype, value, index, data, size, timeout) \
++ usb_control_msg((dev), (pipe), (request), (requesttype), (value), (index), \
++ (data), (size), (timeout))
++#define USB_BULK_MSG(dev, pipe, data, len, actual_length, timeout) \
++ usb_bulk_msg((dev), (pipe), (data), (len), (actual_length), (timeout))
++#define USB_BUFFER_ALLOC(dev, size, mem, dma) usb_buffer_alloc(dev, size, mem, dma)
++#define USB_BUFFER_FREE(dev, size, data, dma) usb_buffer_free(dev, size, data, dma)
++
++#ifdef WL_URB_ZPKT
++#define URB_QUEUE_BULK URB_ZERO_PACKET
++#else
++#define URB_QUEUE_BULK 0
++#endif /* WL_URB_ZPKT */
++
++#define CALLBACK_ARGS struct urb *urb, struct pt_regs *regs
++#define CALLBACK_ARGS_DATA urb, regs
++#define CONFIGDESC(usb) (&((usb)->actconfig)->desc)
++#define IFPTR(usb, idx) ((usb)->actconfig->interface[idx])
++#define IFALTS(usb, idx) (IFPTR((usb), (idx))->altsetting[0])
++#define IFDESC(usb, idx) IFALTS((usb), (idx)).desc
++#define IFEPDESC(usb, idx, ep) (IFALTS((usb), (idx)).endpoint[ep]).desc
++
++#else /* KERNEL26 */
++
++#define USB_ALLOC_URB() usb_alloc_urb(0)
++#define USB_UNLINK_URB(urb) usb_unlink_urb(urb)
++#define USB_FREE_URB(urb) (usb_free_urb(urb))
++#define USB_REGISTER() usb_register(&dbus_usbdev)
++#define USB_DEREGISTER() usb_deregister(&dbus_usbdev)
++#define USB_AUTOPM_GET_INTERFACE(intf) do {} while (0)
++#define USB_AUTOPM_GET_INTERFACE_ASYNC(intf) do {} while (0)
++#define USB_AUTOPM_PUT_INTERFACE_ASYNC(intf) do {} while (0)
++#define USB_MARK_LAST_BUSY(dev) do {} while (0)
++
++#define USB_CONTROL_MSG(dev, pipe, request, requesttype, value, index, data, size, timeout) \
++ usb_control_msg((dev), (pipe), (request), (requesttype), (value), (index), \
++ (data), (size), (timeout))
++#define USB_BUFFER_ALLOC(dev, size, mem, dma) kmalloc(size, mem)
++#define USB_BUFFER_FREE(dev, size, data, dma) kfree(data)
++
++#ifdef WL_URB_ZPKT
++#define URB_QUEUE_BULK USB_QUEUE_BULK|URB_ZERO_PACKET
++#else
++#define URB_QUEUE_BULK 0
++#endif /* WL_URB_ZPKT */
++
++#define CALLBACK_ARGS struct urb *urb
++#define CALLBACK_ARGS_DATA urb
++#define CONFIGDESC(usb) ((usb)->actconfig)
++#define IFPTR(usb, idx) (&(usb)->actconfig->interface[idx])
++#define IFALTS(usb, idx) ((usb)->actconfig->interface[idx].altsetting[0])
++#define IFDESC(usb, idx) IFALTS((usb), (idx))
++#define IFEPDESC(usb, idx, ep) (IFALTS((usb), (idx)).endpoint[ep])
++
++
++#endif /* KERNEL26 */
++
++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31))
++#define USB_SPEED_SUPER 5
++#endif /* #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)) */
++
++#define CONTROL_IF 0
++#define BULK_IF 0
++
++#ifdef BCMUSBDEV_COMPOSITE
++#define USB_COMPIF_MAX 4
++
++#define USB_CLASS_WIRELESS 0xe0
++#define USB_CLASS_MISC 0xef
++#define USB_SUBCLASS_COMMON 0x02
++#define USB_PROTO_IAD 0x01
++#define USB_PROTO_VENDOR 0xff
++
++#define USB_QUIRK_NO_SET_INTF 0x04 /* device does not support set_interface */
++#endif /* BCMUSBDEV_COMPOSITE */
++
++#define USB_SYNC_WAIT_TIMEOUT 300 /* ms */
++
++/* Private data kept in skb */
++#define SKB_PRIV(skb, idx) (&((void **)skb->cb)[idx])
++#define SKB_PRIV_URB(skb) (*(struct urb **)SKB_PRIV(skb, 0))
++
++#ifndef DBUS_USB_RXQUEUE_BATCH_ADD
++/* items to add each time within limit */
++#define DBUS_USB_RXQUEUE_BATCH_ADD 8
++#endif
++
++#ifndef DBUS_USB_RXQUEUE_LOWER_WATERMARK
++/* add a new batch req to rx queue when waiting item count reduce to this number */
++#define DBUS_USB_RXQUEUE_LOWER_WATERMARK 4
++#endif
++
++enum usbos_suspend_state {
++ USBOS_SUSPEND_STATE_DEVICE_ACTIVE = 0, /* Device is busy, won't allow suspend */
++ USBOS_SUSPEND_STATE_SUSPEND_PENDING, /* Device is idle, can be suspended */
++ /* Wating PM to suspend */
++ USBOS_SUSPEND_STATE_SUSPENDED /* Device suspended */
++};
++
++enum usbos_request_state {
++ USBOS_REQUEST_STATE_UNSCHEDULED = 0, /* USB TX request not scheduled */
++ USBOS_REQUEST_STATE_SCHEDULED, /* USB TX request given to TX thread */
++ USBOS_REQUEST_STATE_SUBMITTED /* USB TX request submitted */
++};
++
++typedef struct {
++ uint32 notification;
++ uint32 reserved;
++} intr_t;
++
++typedef struct {
++ dbus_pub_t *pub;
++
++ void *cbarg;
++ dbus_intf_callbacks_t *cbs;
++
++ /* Imported */
++ struct usb_device *usb; /* USB device pointer from OS */
++ struct urb *intr_urb; /* URB for interrupt endpoint */
++ struct list_head req_rxfreeq;
++ struct list_head req_txfreeq;
++ struct list_head req_rxpostedq; /* Posted down to USB driver for RX */
++ struct list_head req_txpostedq; /* Posted down to USB driver for TX */
++ spinlock_t rxfree_lock; /* Lock for rx free list */
++ spinlock_t txfree_lock; /* Lock for tx free list */
++ spinlock_t rxposted_lock; /* Lock for rx posted list */
++ spinlock_t txposted_lock; /* Lock for tx posted list */
++ uint rx_pipe, tx_pipe, intr_pipe, rx_pipe2; /* Pipe numbers for USB I/O */
++ uint rxbuf_len;
++
++ struct list_head req_rxpendingq; /* RXDPC: Pending for dpc to send up */
++ spinlock_t rxpending_lock; /* RXDPC: Lock for rx pending list */
++ long dpc_pid;
++ struct semaphore dpc_sem;
++ struct completion dpc_exited;
++ int rxpending;
++
++ struct urb *ctl_urb;
++ int ctl_in_pipe, ctl_out_pipe;
++ struct usb_ctrlrequest ctl_write;
++ struct usb_ctrlrequest ctl_read;
++ struct semaphore ctl_lock; /* Lock for CTRL transfers via tx_thread */
++#ifdef USBOS_TX_THREAD
++ enum usbos_request_state ctl_state;
++#endif /* USBOS_TX_THREAD */
++
++ spinlock_t rxlock; /* Lock for rxq management */
++ spinlock_t txlock; /* Lock for txq management */
++
++ int intr_size; /* Size of interrupt message */
++ int interval; /* Interrupt polling interval */
++ intr_t intr; /* Data buffer for interrupt endpoint */
++
++ int maxps;
++ atomic_t txposted;
++ atomic_t rxposted;
++ atomic_t txallocated;
++ atomic_t rxallocated;
++ bool rxctl_deferrespok; /* Get a response for setup from dongle */
++
++ wait_queue_head_t wait;
++ bool waitdone;
++ int sync_urb_status;
++
++ struct urb *blk_urb; /* Used for downloading embedded image */
++
++#ifdef USBOS_THREAD
++ spinlock_t ctrl_lock;
++ spinlock_t usbos_list_lock;
++ struct list_head usbos_list;
++ struct list_head usbos_free_list;
++ atomic_t usbos_list_cnt;
++ wait_queue_head_t usbos_queue_head;
++ struct task_struct *usbos_kt;
++#endif /* USBOS_THREAD */
++
++#ifdef USBOS_TX_THREAD
++ spinlock_t usbos_tx_list_lock;
++ struct list_head usbos_tx_list;
++ wait_queue_head_t usbos_tx_queue_head;
++ struct task_struct *usbos_tx_kt;
++#endif /* USBOS_TX_THREAD */
++
++ struct dma_pool *qtd_pool; /* QTD pool for USB optimization only */
++ int tx_ep, rx_ep, rx2_ep; /* EPs for USB optimization */
++ struct usb_device *usb_device; /* USB device for optimization */
++} usbos_info_t;
++
++typedef struct urb_req {
++ void *pkt;
++ int buf_len;
++ struct urb *urb;
++ void *arg;
++ usbos_info_t *usbinfo;
++ struct list_head urb_list;
++} urb_req_t;
++
++#ifdef USBOS_THREAD
++typedef struct usbos_list_entry {
++ struct list_head list; /* must be first */
++ void *urb_context;
++ int urb_length;
++ int urb_status;
++} usbos_list_entry_t;
++
++static void* dbus_usbos_thread_init(usbos_info_t *usbos_info);
++static void dbus_usbos_thread_deinit(usbos_info_t *usbos_info);
++static void dbus_usbos_dispatch_schedule(CALLBACK_ARGS);
++static int dbus_usbos_thread_func(void *data);
++#endif /* USBOS_THREAD */
++
++#ifdef USBOS_TX_THREAD
++void* dbus_usbos_tx_thread_init(usbos_info_t *usbos_info);
++void dbus_usbos_tx_thread_deinit(usbos_info_t *usbos_info);
++int dbus_usbos_tx_thread_func(void *data);
++#endif /* USBOS_TX_THREAD */
++
++/* Shared Function prototypes */
++bool dbus_usbos_dl_cmd(usbos_info_t *usbinfo, uint8 cmd, void *buffer, int buflen);
++int dbus_usbos_wait(usbos_info_t *usbinfo, uint16 ms);
++bool dbus_usbos_dl_send_bulk(usbos_info_t *usbinfo, void *buffer, int len);
++int dbus_write_membytes(usbos_info_t *usbinfo, bool set, uint32 address, uint8 *data, uint size);
++
++/* Local function prototypes */
++static void dbus_usbos_send_complete(CALLBACK_ARGS);
++static void dbus_usbos_recv_complete(CALLBACK_ARGS);
++static int dbus_usbos_errhandler(void *bus, int err);
++static int dbus_usbos_state_change(void *bus, int state);
++static void dbusos_stop(usbos_info_t *usbos_info);
++
++#ifdef KERNEL26
++static int dbus_usbos_probe(struct usb_interface *intf, const struct usb_device_id *id);
++static void dbus_usbos_disconnect(struct usb_interface *intf);
++#if defined(USB_SUSPEND_AVAILABLE)
++static int dbus_usbos_resume(struct usb_interface *intf);
++static int dbus_usbos_suspend(struct usb_interface *intf, pm_message_t message);
++/* at the moment, used for full dongle host driver only */
++static int dbus_usbos_reset_resume(struct usb_interface *intf);
++#endif /* USB_SUSPEND_AVAILABLE */
++#else /* KERNEL26 */
++static void *dbus_usbos_probe(struct usb_device *usb, unsigned int ifnum,
++ const struct usb_device_id *id);
++static void dbus_usbos_disconnect(struct usb_device *usb, void *ptr);
++#endif /* KERNEL26 */
++
++
++/**
++ * have to disable missing-field-initializers warning as last element {} triggers it
++ * and different versions of kernel have different number of members so it is impossible
++ * to specify the initializer. BTW issuing the warning here is bug og GCC as universal
++ * zero {0} specified in C99 standard as correct way of initialization of struct to all zeros
++ */
++#if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == \
++ 4 && __GNUC_MINOR__ >= 6))
++#pragma GCC diagnostic push
++#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
++#endif
++
++static struct usb_device_id devid_table[] = {
++ { USB_DEVICE(BCM_DNGL_VID, 0x0000) }, /* Configurable via register() */
++#if defined(BCM_REQUEST_FW)
++ { USB_DEVICE(BCM_DNGL_VID, BCM_DNGL_BL_PID_4328) },
++ { USB_DEVICE(BCM_DNGL_VID, BCM_DNGL_BL_PID_4322) },
++ { USB_DEVICE(BCM_DNGL_VID, BCM_DNGL_BL_PID_4319) },
++ { USB_DEVICE(BCM_DNGL_VID, BCM_DNGL_BL_PID_43236) },
++ { USB_DEVICE(BCM_DNGL_VID, BCM_DNGL_BL_PID_43143) },
++ { USB_DEVICE(BCM_DNGL_VID, BCM_DNGL_BL_PID_43242) },
++ { USB_DEVICE(BCM_DNGL_VID, BCM_DNGL_BL_PID_4360) },
++ { USB_DEVICE(BCM_DNGL_VID, BCM_DNGL_BL_PID_4350) },
++ { USB_DEVICE(BCM_DNGL_VID, BCM_DNGL_BL_PID_43569) },
++#endif
++#ifdef EXTENDED_VID_PID
++ EXTENDED_VID_PID,
++#endif /* EXTENDED_VID_PID */
++ { USB_DEVICE(BCM_DNGL_VID, BCM_DNGL_BDC_PID) }, /* Default BDC */
++ { }
++};
++
++#if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == \
++ 4 && __GNUC_MINOR__ >= 6))
++#pragma GCC diagnostic pop
++#endif
++
++MODULE_DEVICE_TABLE(usb, devid_table);
++
++/** functions called by the Linux kernel USB subsystem */
++static struct usb_driver dbus_usbdev = {
++ name: "dbus_usbdev",
++ probe: dbus_usbos_probe,
++ disconnect: dbus_usbos_disconnect,
++ id_table: devid_table,
++#if defined(USB_SUSPEND_AVAILABLE)
++ suspend: dbus_usbos_suspend,
++ resume: dbus_usbos_resume,
++ reset_resume: dbus_usbos_reset_resume,
++ /* Linux USB core will allow autosuspend for devices bound to this driver */
++ supports_autosuspend: 1
++#endif /* USB_SUSPEND_AVAILABLE */
++};
++
++/**
++ * This stores USB info during Linux probe callback since attach() is not called yet at this point
++ */
++typedef struct {
++ void *usbos_info;
++ struct usb_device *usb; /* USB device pointer from OS */
++ uint rx_pipe; /* Pipe numbers for USB I/O */
++ uint tx_pipe; /* Pipe numbers for USB I/O */
++ uint intr_pipe; /* Pipe numbers for USB I/O */
++ uint rx_pipe2; /* Pipe numbers for USB I/O */
++ int intr_size; /* Size of interrupt message */
++ int interval; /* Interrupt polling interval */
++ bool dldone;
++ int vid;
++ int pid;
++ bool dereged;
++ bool disc_cb_done;
++ DEVICE_SPEED device_speed;
++ enum usbos_suspend_state suspend_state;
++ struct usb_interface *intf;
++} probe_info_t;
++
++/*
++ * USB Linux dbus_intf_t
++ */
++static void *dbus_usbos_intf_attach(dbus_pub_t *pub, void *cbarg, dbus_intf_callbacks_t *cbs);
++static void dbus_usbos_intf_detach(dbus_pub_t *pub, void *info);
++static int dbus_usbos_intf_send_irb(void *bus, dbus_irb_tx_t *txirb);
++static int dbus_usbos_intf_recv_irb(void *bus, dbus_irb_rx_t *rxirb);
++static int dbus_usbos_intf_recv_irb_from_ep(void *bus, dbus_irb_rx_t *rxirb, uint32 ep_idx);
++static int dbus_usbos_intf_cancel_irb(void *bus, dbus_irb_tx_t *txirb);
++static int dbus_usbos_intf_send_ctl(void *bus, uint8 *buf, int len);
++static int dbus_usbos_intf_recv_ctl(void *bus, uint8 *buf, int len);
++static int dbus_usbos_intf_get_attrib(void *bus, dbus_attrib_t *attrib);
++static int dbus_usbos_intf_up(void *bus);
++static int dbus_usbos_intf_down(void *bus);
++static int dbus_usbos_intf_stop(void *bus);
++static int dbus_usbos_readreg(void *bus, uint32 regaddr, int datalen, uint32 *value);
++extern int dbus_usbos_loopback_tx(void *usbos_info_ptr, int cnt, int size);
++int dbus_usbos_writereg(void *bus, uint32 regaddr, int datalen, uint32 data);
++static int dbus_usbos_intf_set_config(void *bus, dbus_config_t *config);
++static bool dbus_usbos_intf_recv_needed(void *bus);
++static void *dbus_usbos_intf_exec_rxlock(void *bus, exec_cb_t cb, struct exec_parms *args);
++static void *dbus_usbos_intf_exec_txlock(void *bus, exec_cb_t cb, struct exec_parms *args);
++#ifdef BCMUSBDEV_COMPOSITE
++static int dbus_usbos_intf_wlan(struct usb_device *usb);
++#endif /* BCMUSBDEV_COMPOSITE */
++
++/** functions called by dbus_usb.c */
++static dbus_intf_t dbus_usbos_intf = {
++ .attach = dbus_usbos_intf_attach,
++ .detach = dbus_usbos_intf_detach,
++ .up = dbus_usbos_intf_up,
++ .down = dbus_usbos_intf_down,
++ .send_irb = dbus_usbos_intf_send_irb,
++ .recv_irb = dbus_usbos_intf_recv_irb,
++ .cancel_irb = dbus_usbos_intf_cancel_irb,
++ .send_ctl = dbus_usbos_intf_send_ctl,
++ .recv_ctl = dbus_usbos_intf_recv_ctl,
++ .get_stats = NULL,
++ .get_attrib = dbus_usbos_intf_get_attrib,
++ .remove = NULL,
++ .resume = NULL,
++ .suspend = NULL,
++ .stop = dbus_usbos_intf_stop,
++ .reset = NULL,
++ .pktget = NULL,
++ .pktfree = NULL,
++ .iovar_op = NULL,
++ .dump = NULL,
++ .set_config = dbus_usbos_intf_set_config,
++ .get_config = NULL,
++ .device_exists = NULL,
++ .dlneeded = NULL,
++ .dlstart = NULL,
++ .dlrun = NULL,
++ .recv_needed = dbus_usbos_intf_recv_needed,
++ .exec_rxlock = dbus_usbos_intf_exec_rxlock,
++ .exec_txlock = dbus_usbos_intf_exec_txlock,
++
++ .tx_timer_init = NULL,
++ .tx_timer_start = NULL,
++ .tx_timer_stop = NULL,
++
++ .sched_dpc = NULL,
++ .lock = NULL,
++ .unlock = NULL,
++ .sched_probe_cb = NULL,
++
++ .shutdown = NULL,
++
++ .recv_stop = NULL,
++ .recv_resume = NULL,
++
++ .recv_irb_from_ep = dbus_usbos_intf_recv_irb_from_ep,
++ .readreg = dbus_usbos_readreg
++};
++
++static probe_info_t g_probe_info;
++static probe_cb_t probe_cb = NULL;
++static disconnect_cb_t disconnect_cb = NULL;
++static void *probe_arg = NULL;
++static void *disc_arg = NULL;
++
++
++
++static volatile int loopback_rx_cnt, loopback_tx_cnt;
++int loopback_size;
++bool is_loopback_pkt(void *buf);
++int matches_loopback_pkt(void *buf);
++
++/**
++ * multiple code paths in this file dequeue a URB request, this function makes sure that it happens
++ * in a concurrency save manner. Don't call this from a sleepable process context.
++ */
++static urb_req_t * BCMFASTPATH
++dbus_usbos_qdeq(struct list_head *urbreq_q, spinlock_t *lock)
++{
++ unsigned long flags;
++ urb_req_t *req;
++
++ ASSERT(urbreq_q != NULL);
++
++ spin_lock_irqsave(lock, flags);
++
++ if (list_empty(urbreq_q)) {
++ req = NULL;
++ } else {
++ ASSERT(urbreq_q->next != NULL);
++ ASSERT(urbreq_q->next != urbreq_q);
++#if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
++#pragma GCC diagnostic push
++#pragma GCC diagnostic ignored "-Wcast-qual"
++#endif
++ req = list_entry(urbreq_q->next, urb_req_t, urb_list);
++#if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
++#pragma GCC diagnostic pop
++#endif
++ list_del_init(&req->urb_list);
++ }
++
++ spin_unlock_irqrestore(lock, flags);
++
++ return req;
++}
++
++static void BCMFASTPATH
++dbus_usbos_qenq(struct list_head *urbreq_q, urb_req_t *req, spinlock_t *lock)
++{
++ unsigned long flags;
++
++ spin_lock_irqsave(lock, flags);
++
++ list_add_tail(&req->urb_list, urbreq_q);
++
++ spin_unlock_irqrestore(lock, flags);
++}
++
++/**
++ * multiple code paths in this file remove a URB request from a list, this function makes sure that
++ * it happens in a concurrency save manner. Don't call this from a sleepable process context.
++ * Is quite similar to dbus_usbos_qdeq(), I wonder why this function is needed.
++ */
++static void
++dbus_usbos_req_del(urb_req_t *req, spinlock_t *lock)
++{
++ unsigned long flags;
++
++ spin_lock_irqsave(lock, flags);
++
++ list_del_init(&req->urb_list);
++
++ spin_unlock_irqrestore(lock, flags);
++}
++
++
++/**
++ * Driver requires a pool of URBs to operate. This function is called during
++ * initialization (attach phase), allocates a number of URBs, and puts them
++ * on the free (req_rxfreeq and req_txfreeq) queue
++ */
++static int
++dbus_usbos_urbreqs_alloc(usbos_info_t *usbos_info, uint32 count, bool is_rx)
++{
++ int i;
++ int allocated = 0;
++ int err = DBUS_OK;
++
++ for (i = 0; i < count; i++) {
++ urb_req_t *req;
++
++ req = MALLOC(usbos_info->pub->osh, sizeof(urb_req_t));
++ if (req == NULL) {
++ DBUSERR(("%s: MALLOC req failed\n", __FUNCTION__));
++ err = DBUS_ERR_NOMEM;
++ goto fail;
++ }
++ bzero(req, sizeof(urb_req_t));
++
++ req->urb = USB_ALLOC_URB();
++ if (req->urb == NULL) {
++ DBUSERR(("%s: USB_ALLOC_URB req->urb failed\n", __FUNCTION__));
++ err = DBUS_ERR_NOMEM;
++ goto fail;
++ }
++
++ INIT_LIST_HEAD(&req->urb_list);
++
++ if (is_rx) {
++#if defined(BCM_RPC_NOCOPY) || defined(BCM_RPC_RXNOCOPY)
++ /* don't allocate now. Do it on demand */
++ req->pkt = NULL;
++#else
++ /* pre-allocate buffers never to be released */
++ req->pkt = MALLOC(usbos_info->pub->osh, usbos_info->rxbuf_len);
++ if (req->pkt == NULL) {
++ DBUSERR(("%s: MALLOC req->pkt failed\n", __FUNCTION__));
++ err = DBUS_ERR_NOMEM;
++ goto fail;
++ }
++#endif
++ req->buf_len = usbos_info->rxbuf_len;
++ dbus_usbos_qenq(&usbos_info->req_rxfreeq, req, &usbos_info->rxfree_lock);
++ } else {
++ req->buf_len = 0;
++ dbus_usbos_qenq(&usbos_info->req_txfreeq, req, &usbos_info->txfree_lock);
++ }
++ allocated++;
++ continue;
++
++fail:
++ if (req) {
++ if (is_rx && req->pkt) {
++#if defined(BCM_RPC_NOCOPY) || defined(BCM_RPC_RXNOCOPY)
++ /* req->pkt is NULL in "NOCOPY" mode */
++#else
++ MFREE(usbos_info->pub->osh, req->pkt, req->buf_len);
++#endif
++ }
++ if (req->urb) {
++ USB_FREE_URB(req->urb);
++ }
++ MFREE(usbos_info->pub->osh, req, sizeof(urb_req_t));
++ }
++ break;
++ }
++
++ atomic_add(allocated, is_rx ? &usbos_info->rxallocated : &usbos_info->txallocated);
++
++ if (is_rx) {
++ DBUSTRACE(("%s: add %d (total %d) rx buf, each has %d bytes\n", __FUNCTION__,
++ allocated, atomic_read(&usbos_info->rxallocated), usbos_info->rxbuf_len));
++ } else {
++ DBUSTRACE(("%s: add %d (total %d) tx req\n", __FUNCTION__,
++ allocated, atomic_read(&usbos_info->txallocated)));
++ }
++
++ return err;
++} /* dbus_usbos_urbreqs_alloc */
++
++/** Typically called during detach or when attach failed. Don't call until all URBs unlinked */
++static int
++dbus_usbos_urbreqs_free(usbos_info_t *usbos_info, bool is_rx)
++{
++ int rtn = 0;
++ urb_req_t *req;
++ struct list_head *req_q;
++ spinlock_t *lock;
++
++ if (is_rx) {
++ req_q = &usbos_info->req_rxfreeq;
++ lock = &usbos_info->rxfree_lock;
++ } else {
++ req_q = &usbos_info->req_txfreeq;
++ lock = &usbos_info->txfree_lock;
++ }
++ while ((req = dbus_usbos_qdeq(req_q, lock)) != NULL) {
++
++ if (is_rx) {
++ if (req->pkt) {
++ /* We do MFREE instead of PKTFREE because the pkt has been
++ * converted to native already
++ */
++ MFREE(usbos_info->pub->osh, req->pkt, req->buf_len);
++ req->pkt = NULL;
++ req->buf_len = 0;
++ }
++ } else {
++ /* sending req should not be assigned pkt buffer */
++ ASSERT(req->pkt == NULL);
++ }
++
++ if (req->urb) {
++ USB_FREE_URB(req->urb);
++ req->urb = NULL;
++ }
++ MFREE(usbos_info->pub->osh, req, sizeof(urb_req_t));
++
++ rtn++;
++ }
++ return rtn;
++} /* dbus_usbos_urbreqs_free */
++
++/**
++ * called by Linux kernel on URB completion. Upper DBUS layer (dbus_usb.c) has to be notified of
++ * send completion.
++ */
++void
++dbus_usbos_send_complete(CALLBACK_ARGS)
++{
++ urb_req_t *req = urb->context;
++ dbus_irb_tx_t *txirb = req->arg;
++ usbos_info_t *usbos_info = req->usbinfo;
++ unsigned long flags;
++ int status = DBUS_OK;
++ int txposted;
++
++ USB_AUTOPM_PUT_INTERFACE_ASYNC(g_probe_info.intf);
++
++ spin_lock_irqsave(&usbos_info->txlock, flags);
++
++ dbus_usbos_req_del(req, &usbos_info->txposted_lock);
++ txposted = atomic_dec_return(&usbos_info->txposted);
++ if (unlikely (txposted < 0)) {
++ DBUSERR(("%s ERROR: txposted is negative (%d)!!\n", __FUNCTION__, txposted));
++ }
++ spin_unlock_irqrestore(&usbos_info->txlock, flags);
++
++ if (unlikely (urb->status)) {
++ status = DBUS_ERR_TXFAIL;
++ DBUSTRACE(("txfail status %d\n", urb->status));
++ }
++
++#if defined(BCM_RPC_NOCOPY) || defined(BCM_RPC_RXNOCOPY)
++ /* sending req should not be assigned pkt buffer */
++ ASSERT(req->pkt == NULL);
++#endif
++ /* txirb should always be set, except for ZLP. ZLP is reusing this callback function. */
++ if (txirb != NULL) {
++ if (txirb->send_buf != NULL) {
++ MFREE(usbos_info->pub->osh, txirb->send_buf, req->buf_len);
++ txirb->send_buf = NULL;
++ req->buf_len = 0;
++ }
++ if (likely (usbos_info->cbarg && usbos_info->cbs)) {
++ if (likely (usbos_info->cbs->send_irb_complete != NULL))
++ usbos_info->cbs->send_irb_complete(usbos_info->cbarg, txirb, status);
++ }
++ }
++
++ dbus_usbos_qenq(&usbos_info->req_txfreeq, req, &usbos_info->txfree_lock);
++} /* dbus_usbos_send_complete */
++
++/**
++ * In order to receive USB traffic from the dongle, we need to supply the Linux kernel with a free
++ * URB that is going to contain received data.
++ */
++static int BCMFASTPATH
++dbus_usbos_recv_urb_submit(usbos_info_t *usbos_info, dbus_irb_rx_t *rxirb, uint32 ep_idx)
++{
++ urb_req_t *req;
++ int ret = DBUS_OK;
++ unsigned long flags;
++ void *p;
++ uint rx_pipe;
++ int rxposted;
++
++ BCM_REFERENCE(rxposted);
++
++ if (!(req = dbus_usbos_qdeq(&usbos_info->req_rxfreeq, &usbos_info->rxfree_lock))) {
++ DBUSTRACE(("%s No free URB!\n", __FUNCTION__));
++ return DBUS_ERR_RXDROP;
++ }
++
++ spin_lock_irqsave(&usbos_info->rxlock, flags);
++
++#if defined(BCM_RPC_NOCOPY) || defined(BCM_RPC_RXNOCOPY)
++ req->pkt = rxirb->pkt = PKTGET(usbos_info->pub->osh, req->buf_len, FALSE);
++ if (!rxirb->pkt) {
++ DBUSERR(("%s: PKTGET failed\n", __FUNCTION__));
++ dbus_usbos_qenq(&usbos_info->req_rxfreeq, req, &usbos_info->rxfree_lock);
++ ret = DBUS_ERR_RXDROP;
++ goto fail;
++ }
++ /* consider the packet "native" so we don't count it as MALLOCED in the osl */
++ PKTTONATIVE(usbos_info->pub->osh, req->pkt);
++ rxirb->buf = NULL;
++ p = PKTDATA(usbos_info->pub->osh, req->pkt);
++#else
++ if (req->buf_len != usbos_info->rxbuf_len) {
++ ASSERT(req->pkt);
++ MFREE(usbos_info->pub->osh, req->pkt, req->buf_len);
++ DBUSTRACE(("%s: replace rx buff: old len %d, new len %d\n", __FUNCTION__,
++ req->buf_len, usbos_info->rxbuf_len));
++ req->buf_len = 0;
++ req->pkt = MALLOC(usbos_info->pub->osh, usbos_info->rxbuf_len);
++ if (req->pkt == NULL) {
++ DBUSERR(("%s: MALLOC req->pkt failed\n", __FUNCTION__));
++ ret = DBUS_ERR_NOMEM;
++ goto fail;
++ }
++ req->buf_len = usbos_info->rxbuf_len;
++ }
++ rxirb->buf = req->pkt;
++ p = rxirb->buf;
++#endif /* defined(BCM_RPC_NOCOPY) */
++ rxirb->buf_len = req->buf_len;
++ req->usbinfo = usbos_info;
++ req->arg = rxirb;
++ if (ep_idx == 0) {
++ rx_pipe = usbos_info->rx_pipe;
++ } else {
++ rx_pipe = usbos_info->rx_pipe2;
++ ASSERT(usbos_info->rx_pipe2);
++ }
++ /* Prepare the URB */
++ usb_fill_bulk_urb(req->urb, usbos_info->usb, rx_pipe,
++ p,
++ rxirb->buf_len,
++ (usb_complete_t)dbus_usbos_recv_complete, req);
++ req->urb->transfer_flags |= URB_QUEUE_BULK;
++
++ if ((ret = USB_SUBMIT_URB(req->urb))) {
++ DBUSERR(("%s USB_SUBMIT_URB failed. status %d\n", __FUNCTION__, ret));
++ dbus_usbos_qenq(&usbos_info->req_rxfreeq, req, &usbos_info->rxfree_lock);
++ ret = DBUS_ERR_RXFAIL;
++ goto fail;
++ }
++ rxposted = atomic_inc_return(&usbos_info->rxposted);
++
++ dbus_usbos_qenq(&usbos_info->req_rxpostedq, req, &usbos_info->rxposted_lock);
++fail:
++ spin_unlock_irqrestore(&usbos_info->rxlock, flags);
++ return ret;
++} /* dbus_usbos_recv_urb_submit */
++
++
++/**
++ * Called by worked thread when a 'receive URB' completed or Linux kernel when it returns a URB to
++ * this driver.
++ */
++static void BCMFASTPATH
++dbus_usbos_recv_complete_handle(urb_req_t *req, int len, int status)
++{
++ dbus_irb_rx_t *rxirb = req->arg;
++ usbos_info_t *usbos_info = req->usbinfo;
++ unsigned long flags;
++ int rxallocated, rxposted;
++ int dbus_status = DBUS_OK;
++ bool killed = (g_probe_info.suspend_state == USBOS_SUSPEND_STATE_SUSPEND_PENDING) ? 1 : 0;
++
++ spin_lock_irqsave(&usbos_info->rxlock, flags);
++ dbus_usbos_req_del(req, &usbos_info->rxposted_lock);
++ rxposted = atomic_dec_return(&usbos_info->rxposted);
++ rxallocated = atomic_read(&usbos_info->rxallocated);
++ spin_unlock_irqrestore(&usbos_info->rxlock, flags);
++
++ if ((rxallocated < usbos_info->pub->nrxq) && (!status) &&
++ (rxposted == DBUS_USB_RXQUEUE_LOWER_WATERMARK)) {
++ DBUSTRACE(("%s: need more rx buf: rxallocated %d rxposted %d!\n",
++ __FUNCTION__, rxallocated, rxposted));
++ dbus_usbos_urbreqs_alloc(usbos_info,
++ MIN(DBUS_USB_RXQUEUE_BATCH_ADD,
++ usbos_info->pub->nrxq - rxallocated), TRUE);
++ }
++
++ /* Handle errors */
++ if (status) {
++ /*
++ * Linux 2.4 disconnect: -ENOENT or -EILSEQ for CRC error; rmmod: -ENOENT
++ * Linux 2.6 disconnect: -EPROTO, rmmod: -ESHUTDOWN
++ */
++ if ((status == -ENOENT && (!killed))|| status == -ESHUTDOWN) {
++ /* NOTE: unlink() can not be called from URB callback().
++ * Do not call dbusos_stop() here.
++ */
++ DBUSTRACE(("%s rx error %d\n", __FUNCTION__, status));
++ dbus_usbos_state_change(usbos_info, DBUS_STATE_DOWN);
++ } else if (status == -EPROTO) {
++ DBUSTRACE(("%s rx error %d\n", __FUNCTION__, status));
++ } else if (killed && (status == -EHOSTUNREACH || status == -ENOENT)) {
++ /* Device is suspended */
++ } else {
++ DBUSTRACE(("%s rx error %d\n", __FUNCTION__, status));
++ dbus_usbos_errhandler(usbos_info, DBUS_ERR_RXFAIL);
++ }
++
++ /* On error, don't submit more URBs yet */
++ rxirb->buf = NULL;
++ rxirb->actual_len = 0;
++ dbus_status = DBUS_ERR_RXFAIL;
++ goto fail;
++ }
++
++ /* Make the skb represent the received urb */
++ rxirb->actual_len = len;
++
++ if (rxirb->actual_len < sizeof(uint32)) {
++ DBUSTRACE(("small pkt len %d, process as ZLP\n", rxirb->actual_len));
++ dbus_status = DBUS_ERR_RXZLP;
++ }
++
++fail:
++#if defined(BCM_RPC_NOCOPY) || defined(BCM_RPC_RXNOCOPY)
++ /* detach the packet from the queue */
++ req->pkt = NULL;
++#endif /* BCM_RPC_NOCOPY || BCM_RPC_RXNOCOPY */
++
++ if (usbos_info->cbarg && usbos_info->cbs) {
++ if (usbos_info->cbs->recv_irb_complete) {
++ usbos_info->cbs->recv_irb_complete(usbos_info->cbarg, rxirb, dbus_status);
++ }
++ }
++
++ dbus_usbos_qenq(&usbos_info->req_rxfreeq, req, &usbos_info->rxfree_lock);
++
++ /* Mark the interface as busy to reset USB autosuspend timer */
++ USB_MARK_LAST_BUSY(usbos_info->usb);
++} /* dbus_usbos_recv_complete_handle */
++
++/** called by Linux kernel when it returns a URB to this driver */
++static void
++dbus_usbos_recv_complete(CALLBACK_ARGS)
++{
++#ifdef USBOS_THREAD
++ dbus_usbos_dispatch_schedule(CALLBACK_ARGS_DATA);
++#else /* !USBOS_THREAD */
++ dbus_usbos_recv_complete_handle(urb->context, urb->actual_length, urb->status);
++#endif /* USBOS_THREAD */
++}
++
++
++/**
++ * If Linux notifies our driver that a control read or write URB has completed, we should notify
++ * the DBUS layer above us (dbus_usb.c in this case).
++ */
++static void
++dbus_usbos_ctl_complete(usbos_info_t *usbos_info, int type, int urbstatus)
++{
++ int status = DBUS_ERR;
++
++ if (usbos_info == NULL)
++ return;
++
++ switch (urbstatus) {
++ case 0:
++ status = DBUS_OK;
++ break;
++ case -EINPROGRESS:
++ case -ENOENT:
++ default:
++#ifdef INTR_EP_ENABLE
++ DBUSERR(("%s:%d fail status %d bus:%d susp:%d intr:%d ctli:%d ctlo:%d\n",
++ __FUNCTION__, type, urbstatus,
++ usbos_info->pub->busstate, g_probe_info.suspend_state,
++ usbos_info->intr_urb_submitted, usbos_info->ctlin_urb_submitted,
++ usbos_info->ctlout_urb_submitted));
++#else
++ DBUSERR(("%s: failed with status %d\n", __FUNCTION__, urbstatus));
++ status = DBUS_ERR;
++ break;
++#endif /* INTR_EP_ENABLE */
++ }
++
++ if (usbos_info->cbarg && usbos_info->cbs) {
++ if (usbos_info->cbs->ctl_complete)
++ usbos_info->cbs->ctl_complete(usbos_info->cbarg, type, status);
++ }
++}
++
++/** called by Linux */
++static void
++dbus_usbos_ctlread_complete(CALLBACK_ARGS)
++{
++ usbos_info_t *usbos_info = (usbos_info_t *)urb->context;
++
++ ASSERT(urb);
++ usbos_info = (usbos_info_t *)urb->context;
++
++ dbus_usbos_ctl_complete(usbos_info, DBUS_CBCTL_READ, urb->status);
++
++#ifdef USBOS_THREAD
++ if (usbos_info->rxctl_deferrespok) {
++ usbos_info->ctl_read.bRequestType = USB_DIR_IN | USB_TYPE_CLASS |
++ USB_RECIP_INTERFACE;
++ usbos_info->ctl_read.bRequest = 1;
++ }
++#endif
++
++ up(&usbos_info->ctl_lock);
++
++ USB_AUTOPM_PUT_INTERFACE_ASYNC(g_probe_info.intf);
++}
++
++/** called by Linux */
++static void
++dbus_usbos_ctlwrite_complete(CALLBACK_ARGS)
++{
++ usbos_info_t *usbos_info = (usbos_info_t *)urb->context;
++
++ ASSERT(urb);
++ usbos_info = (usbos_info_t *)urb->context;
++
++ dbus_usbos_ctl_complete(usbos_info, DBUS_CBCTL_WRITE, urb->status);
++
++#ifdef USBOS_TX_THREAD
++ usbos_info->ctl_state = USBOS_REQUEST_STATE_UNSCHEDULED;
++#endif /* USBOS_TX_THREAD */
++
++ up(&usbos_info->ctl_lock);
++
++ USB_AUTOPM_PUT_INTERFACE_ASYNC(g_probe_info.intf);
++}
++
++#ifdef INTR_EP_ENABLE
++/** called by Linux */
++static void
++dbus_usbos_intr_complete(CALLBACK_ARGS)
++{
++ usbos_info_t *usbos_info = (usbos_info_t *)urb->context;
++ bool killed = (g_probe_info.suspend_state == USBOS_SUSPEND_STATE_SUSPEND_PENDING) ? 1 : 0;
++
++ if (usbos_info == NULL || usbos_info->pub == NULL)
++ return;
++ if ((urb->status == -ENOENT && (!killed)) || urb->status == -ESHUTDOWN ||
++ urb->status == -ENODEV) {
++ dbus_usbos_state_change(usbos_info, DBUS_STATE_DOWN);
++ }
++
++ if (usbos_info->pub->busstate == DBUS_STATE_DOWN) {
++ DBUSERR(("%s: intr cb when DBUS down, ignoring\n", __FUNCTION__));
++ return;
++ }
++ dbus_usbos_ctl_complete(usbos_info, DBUS_CBINTR_POLL, urb->status);
++}
++#endif /* INTR_EP_ENABLE */
++
++/**
++ * when the bus is going to sleep or halt, the Linux kernel requires us to take ownership of our
++ * URBs again. Multiple code paths in this file require a list of URBs to be cancelled in a
++ * concurrency save manner.
++ */
++static void
++dbus_usbos_unlink(struct list_head *urbreq_q, spinlock_t *lock)
++{
++ urb_req_t *req;
++
++ /* dbus_usbos_recv_complete() adds req back to req_freeq */
++ while ((req = dbus_usbos_qdeq(urbreq_q, lock)) != NULL) {
++ ASSERT(req->urb != NULL);
++ USB_UNLINK_URB(req->urb);
++ }
++}
++
++/** multiple code paths in this file require the bus to stop */
++static void
++dbus_usbos_cancel_all_urbs(usbos_info_t *usbos_info)
++{
++ int rxposted, txposted;
++
++ DBUSTRACE(("%s: unlink all URBs\n", __FUNCTION__));
++
++#ifdef USBOS_TX_THREAD
++ usbos_info->ctl_state = USBOS_REQUEST_STATE_UNSCHEDULED;
++
++ /* Yield the CPU to TX thread so all pending requests are submitted */
++ while (!list_empty(&usbos_info->usbos_tx_list)) {
++ wake_up_interruptible(&usbos_info->usbos_tx_queue_head);
++ OSL_SLEEP(10);
++ }
++#endif /* USBOS_TX_THREAD */
++
++ /* tell Linux kernel to cancel a single intr, ctl and blk URB */
++ if (usbos_info->intr_urb)
++ USB_UNLINK_URB(usbos_info->intr_urb);
++ if (usbos_info->ctl_urb)
++ USB_UNLINK_URB(usbos_info->ctl_urb);
++ if (usbos_info->blk_urb)
++ USB_UNLINK_URB(usbos_info->blk_urb);
++
++ dbus_usbos_unlink(&usbos_info->req_txpostedq, &usbos_info->txposted_lock);
++ dbus_usbos_unlink(&usbos_info->req_rxpostedq, &usbos_info->rxposted_lock);
++
++ /* Wait until the callbacks for all submitted URBs have been called, because the
++ * handler needs to know is an USB suspend is in progress.
++ */
++ SPINWAIT((atomic_read(&usbos_info->txposted) != 0 ||
++ atomic_read(&usbos_info->rxposted) != 0), 10000);
++
++ txposted = atomic_read(&usbos_info->txposted);
++ rxposted = atomic_read(&usbos_info->rxposted);
++ if (txposted != 0 || rxposted != 0) {
++ DBUSERR(("%s ERROR: REQs posted, rx=%d tx=%d!\n",
++ __FUNCTION__, rxposted, txposted));
++ }
++} /* dbus_usbos_cancel_all_urbs */
++
++/** multiple code paths require the bus to stop */
++static void
++dbusos_stop(usbos_info_t *usbos_info)
++{
++ urb_req_t *req;
++ int rxposted;
++ req = NULL;
++ BCM_REFERENCE(req);
++
++ ASSERT(usbos_info);
++
++ dbus_usbos_state_change(usbos_info, DBUS_STATE_DOWN);
++
++ dbus_usbos_cancel_all_urbs(usbos_info);
++
++#ifdef USBOS_THREAD
++ /* yield the CPU to rx packet thread */
++ while (1) {
++ if (atomic_read(&usbos_info->usbos_list_cnt) <= 0) break;
++ wake_up_interruptible(&usbos_info->usbos_queue_head);
++ OSL_SLEEP(3);
++ }
++#endif /* USBOS_THREAD */
++
++ rxposted = atomic_read(&usbos_info->rxposted);
++ if (rxposted > 0) {
++ DBUSERR(("%s ERROR: rx REQs posted=%d in stop!\n", __FUNCTION__,
++ rxposted));
++ }
++
++ ASSERT(atomic_read(&usbos_info->txposted) == 0 && rxposted == 0);
++
++} /* dbusos_stop */
++
++#if defined(USB_SUSPEND_AVAILABLE)
++
++/**
++ * Linux kernel sports a 'USB auto suspend' feature. See: http://lwn.net/Articles/373550/
++ * The suspend method is called by the Linux kernel to warn the driver that the device is going to
++ * be suspended. If the driver returns a negative error code, the suspend will be aborted. If the
++ * driver returns 0, it must cancel all outstanding URBs (usb_kill_urb()) and not submit any more.
++ */
++static int
++dbus_usbos_suspend(struct usb_interface *intf,
++ pm_message_t message)
++{
++ DBUSERR(("%s suspend state: %d\n", __FUNCTION__, g_probe_info.suspend_state));
++ /* DHD for full dongle model */
++ g_probe_info.suspend_state = USBOS_SUSPEND_STATE_SUSPEND_PENDING;
++ dbus_usbos_state_change((usbos_info_t*)g_probe_info.usbos_info, DBUS_STATE_SLEEP);
++ dbus_usbos_cancel_all_urbs((usbos_info_t*)g_probe_info.usbos_info);
++ g_probe_info.suspend_state = USBOS_SUSPEND_STATE_SUSPENDED;
++
++ return 0;
++}
++
++/**
++ * The resume method is called to tell the driver that the device has been resumed and the driver
++ * can return to normal operation. URBs may once more be submitted.
++ */
++static int dbus_usbos_resume(struct usb_interface *intf)
++{
++ DBUSERR(("%s Device resumed\n", __FUNCTION__));
++
++ dbus_usbos_state_change((usbos_info_t*)g_probe_info.usbos_info, DBUS_STATE_UP);
++ g_probe_info.suspend_state = USBOS_SUSPEND_STATE_DEVICE_ACTIVE;
++ return 0;
++}
++
++/**
++* This function is directly called by the Linux kernel, when the suspended device has been reset
++* instead of being resumed
++*/
++static int dbus_usbos_reset_resume(struct usb_interface *intf)
++{
++ DBUSERR(("%s Device reset resumed\n", __FUNCTION__));
++
++ /* The device may have lost power, so a firmware download may be required */
++ dbus_usbos_state_change((usbos_info_t*)g_probe_info.usbos_info, DBUS_STATE_DL_NEEDED);
++ g_probe_info.suspend_state = USBOS_SUSPEND_STATE_DEVICE_ACTIVE;
++ return 0;
++}
++
++#endif /* USB_SUSPEND_AVAILABLE */
++
++/**
++ * Called by Linux kernel at initialization time, kernel wants to know if our driver will accept the
++ * caller supplied USB interface. Note that USB drivers are bound to interfaces, and not to USB
++ * devices.
++ */
++#ifdef KERNEL26
++#define DBUS_USBOS_PROBE() static int dbus_usbos_probe(struct usb_interface *intf, const struct usb_device_id *id)
++#define DBUS_USBOS_DISCONNECT() static void dbus_usbos_disconnect(struct usb_interface *intf)
++#else
++#define DBUS_USBOS_PROBE() static void * dbus_usbos_probe(struct usb_device *usb, unsigned int ifnum, const struct usb_device_id *id)
++#define DBUS_USBOS_DISCONNECT() static void dbus_usbos_disconnect(struct usb_device *usb, void *ptr)
++#endif /* KERNEL26 */
++
++DBUS_USBOS_PROBE()
++{
++ int ep;
++ struct usb_endpoint_descriptor *endpoint;
++ int ret = 0;
++#ifdef KERNEL26
++ struct usb_device *usb = interface_to_usbdev(intf);
++#else
++ int claimed = 0;
++#endif
++ int num_of_eps;
++#ifdef BCMUSBDEV_COMPOSITE
++ int wlan_if = -1;
++ bool intr_ep = FALSE;
++#endif /* BCMUSBDEV_COMPOSITE */
++ wifi_adapter_info_t *adapter;
++
++ DHD_MUTEX_LOCK();
++
++ DBUSERR(("%s: bus num(busnum)=%d, slot num (portnum)=%d\n", __FUNCTION__,
++ usb->bus->busnum, usb->portnum));
++ adapter = dhd_wifi_platform_attach_adapter(USB_BUS, usb->bus->busnum,
++ usb->portnum, WIFI_STATUS_POWER_ON);
++ if (adapter == NULL) {
++ DBUSERR(("%s: can't find adapter info for this chip\n", __FUNCTION__));
++ goto fail;
++ }
++
++#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, usb->bus->busnum, usb->portnum, 0);
++ }
++
++ g_probe_info.disc_cb_done = FALSE;
++
++#ifdef KERNEL26
++ intf->needs_remote_wakeup = 1;
++#endif /* KERNEL26 */
++ DHD_MUTEX_UNLOCK();
++
++ /* 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 */
++
++ DHD_MUTEX_UNLOCK();
++#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;
++
++ DHD_MUTEX_LOCK();
++
++ DBUSERR(("%s: bus num(busnum)=%d, slot num (portnum)=%d\n", __FUNCTION__,
++ usb->bus->busnum, usb->portnum));
++
++ 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 */
++ }
++ DHD_MUTEX_UNLOCK();
++} /* 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;
++
++ DHD_MUTEX_LOCK();
++ if (disconnect_cb && disc_arg && (g_probe_info.disc_cb_done == FALSE)) {
++ disconnect_cb(disc_arg);
++ disc_arg = NULL;
++ }
++ DHD_MUTEX_UNLOCK();
++
++ USB_DEREGISTER();
++
++ return DBUS_OK;
++}
++
++void *
++dbus_usbos_intf_attach(dbus_pub_t *pub, void *cbarg, dbus_intf_callbacks_t *cbs)
++{
++ usbos_info_t *usbos_info;
++
++ if (g_probe_info.dldone == FALSE) {
++ DBUSERR(("%s: err device not downloaded!\n", __FUNCTION__));
++ return NULL;
++ }
++
++ /* Sanity check for BUS_INFO() */
++ ASSERT(OFFSETOF(usbos_info_t, pub) == 0);
++
++ usbos_info = MALLOC(pub->osh, sizeof(usbos_info_t));
++ if (usbos_info == NULL)
++ return NULL;
++
++ bzero(usbos_info, sizeof(usbos_info_t));
++
++ usbos_info->pub = pub;
++ usbos_info->cbarg = cbarg;
++ usbos_info->cbs = cbs;
++
++ /* Needed for disconnect() */
++ g_probe_info.usbos_info = usbos_info;
++
++ /* Update USB Info */
++ usbos_info->usb = g_probe_info.usb;
++ usbos_info->rx_pipe = g_probe_info.rx_pipe;
++ usbos_info->rx_pipe2 = g_probe_info.rx_pipe2;
++ usbos_info->tx_pipe = g_probe_info.tx_pipe;
++ usbos_info->intr_pipe = g_probe_info.intr_pipe;
++ usbos_info->intr_size = g_probe_info.intr_size;
++ usbos_info->interval = g_probe_info.interval;
++ usbos_info->pub->device_speed = g_probe_info.device_speed;
++ if (usbos_info->rx_pipe2) {
++ usbos_info->pub->attrib.has_2nd_bulk_in_ep = 1;
++ } else {
++ usbos_info->pub->attrib.has_2nd_bulk_in_ep = 0;
++ }
++
++ if (usbos_info->tx_pipe)
++ usbos_info->maxps = usb_maxpacket(usbos_info->usb,
++ usbos_info->tx_pipe, usb_pipeout(usbos_info->tx_pipe));
++
++ INIT_LIST_HEAD(&usbos_info->req_rxfreeq);
++ INIT_LIST_HEAD(&usbos_info->req_txfreeq);
++ INIT_LIST_HEAD(&usbos_info->req_rxpostedq);
++ INIT_LIST_HEAD(&usbos_info->req_txpostedq);
++ spin_lock_init(&usbos_info->rxfree_lock);
++ spin_lock_init(&usbos_info->txfree_lock);
++ spin_lock_init(&usbos_info->rxposted_lock);
++ spin_lock_init(&usbos_info->txposted_lock);
++ spin_lock_init(&usbos_info->rxlock);
++ spin_lock_init(&usbos_info->txlock);
++
++ atomic_set(&usbos_info->rxposted, 0);
++ atomic_set(&usbos_info->txposted, 0);
++
++
++#ifdef USB_DISABLE_INT_EP
++ usbos_info->intr_urb = NULL;
++#else
++ if (!(usbos_info->intr_urb = USB_ALLOC_URB())) {
++ DBUSERR(("%s: usb_alloc_urb (tx) failed\n", __FUNCTION__));
++ goto fail;
++ }
++#endif
++
++ if (!(usbos_info->ctl_urb = USB_ALLOC_URB())) {
++ DBUSERR(("%s: usb_alloc_urb (tx) failed\n", __FUNCTION__));
++ goto fail;
++ }
++
++ init_waitqueue_head(&usbos_info->wait);
++
++ if (!(usbos_info->blk_urb = USB_ALLOC_URB())) { /* for embedded image downloading */
++ DBUSERR(("%s: usb_alloc_urb (tx) failed\n", __FUNCTION__));
++ goto fail;
++ }
++
++ usbos_info->rxbuf_len = (uint)usbos_info->pub->rxsize;
++
++
++
++ atomic_set(&usbos_info->txallocated, 0);
++ if (DBUS_OK != dbus_usbos_urbreqs_alloc(usbos_info,
++ usbos_info->pub->ntxq, FALSE)) {
++ goto fail;
++ }
++
++ atomic_set(&usbos_info->rxallocated, 0);
++ if (DBUS_OK != dbus_usbos_urbreqs_alloc(usbos_info,
++ MIN(DBUS_USB_RXQUEUE_BATCH_ADD, usbos_info->pub->nrxq),
++ TRUE)) {
++ goto fail;
++ }
++
++ sema_init(&usbos_info->ctl_lock, 1);
++
++#ifdef USBOS_THREAD
++ if (dbus_usbos_thread_init(usbos_info) == NULL)
++ goto fail;
++#endif /* USBOS_THREAD */
++
++#ifdef USBOS_TX_THREAD
++ if (dbus_usbos_tx_thread_init(usbos_info) == NULL)
++ goto fail;
++#endif /* USBOS_TX_THREAD */
++
++ pub->dev_info = g_probe_info.usb;
++
++
++ return (void *) usbos_info;
++fail:
++ if (usbos_info->intr_urb) {
++ USB_FREE_URB(usbos_info->intr_urb);
++ usbos_info->intr_urb = NULL;
++ }
++
++ if (usbos_info->ctl_urb) {
++ USB_FREE_URB(usbos_info->ctl_urb);
++ usbos_info->ctl_urb = NULL;
++ }
++
++#if defined(BCM_REQUEST_FW)
++ if (usbos_info->blk_urb) {
++ USB_FREE_URB(usbos_info->blk_urb);
++ usbos_info->blk_urb = NULL;
++ }
++#endif
++
++ dbus_usbos_urbreqs_free(usbos_info, TRUE);
++ atomic_set(&usbos_info->rxallocated, 0);
++ dbus_usbos_urbreqs_free(usbos_info, FALSE);
++ atomic_set(&usbos_info->txallocated, 0);
++
++ g_probe_info.usbos_info = NULL;
++
++ MFREE(pub->osh, usbos_info, sizeof(usbos_info_t));
++ return NULL;
++} /* dbus_usbos_intf_attach */
++
++void
++dbus_usbos_intf_detach(dbus_pub_t *pub, void *info)
++{
++ usbos_info_t *usbos_info = (usbos_info_t *) info;
++ osl_t *osh = pub->osh;
++
++ if (usbos_info == NULL) {
++ return;
++ }
++
++#ifdef USBOS_TX_THREAD
++ dbus_usbos_tx_thread_deinit(usbos_info);
++#endif /* USBOS_TX_THREAD */
++
++ /* Must unlink all URBs prior to driver unload;
++ * otherwise an URB callback can occur after driver
++ * has been de-allocated and rmmod'd
++ */
++ dbusos_stop(usbos_info);
++
++ if (usbos_info->intr_urb) {
++ USB_FREE_URB(usbos_info->intr_urb);
++ usbos_info->intr_urb = NULL;
++ }
++
++ if (usbos_info->ctl_urb) {
++ USB_FREE_URB(usbos_info->ctl_urb);
++ usbos_info->ctl_urb = NULL;
++ }
++
++ if (usbos_info->blk_urb) {
++ USB_FREE_URB(usbos_info->blk_urb);
++ usbos_info->blk_urb = NULL;
++ }
++
++ dbus_usbos_urbreqs_free(usbos_info, TRUE);
++ atomic_set(&usbos_info->rxallocated, 0);
++ dbus_usbos_urbreqs_free(usbos_info, FALSE);
++ atomic_set(&usbos_info->txallocated, 0);
++
++#ifdef USBOS_THREAD
++ dbus_usbos_thread_deinit(usbos_info);
++#endif /* USBOS_THREAD */
++
++ g_probe_info.usbos_info = NULL;
++ MFREE(osh, usbos_info, sizeof(usbos_info_t));
++} /* dbus_usbos_intf_detach */
++
++
++#ifdef USBOS_TX_THREAD
++
++void*
++dbus_usbos_tx_thread_init(usbos_info_t *usbos_info)
++{
++ spin_lock_init(&usbos_info->usbos_tx_list_lock);
++ INIT_LIST_HEAD(&usbos_info->usbos_tx_list);
++ init_waitqueue_head(&usbos_info->usbos_tx_queue_head);
++
++ usbos_info->usbos_tx_kt = kthread_create(dbus_usbos_tx_thread_func,
++ usbos_info, "usb-tx-thread");
++
++ if (IS_ERR(usbos_info->usbos_tx_kt)) {
++ DBUSERR(("Thread Creation failed\n"));
++ return (NULL);
++ }
++
++ usbos_info->ctl_state = USBOS_REQUEST_STATE_UNSCHEDULED;
++ wake_up_process(usbos_info->usbos_tx_kt);
++
++ return (usbos_info->usbos_tx_kt);
++}
++
++void
++dbus_usbos_tx_thread_deinit(usbos_info_t *usbos_info)
++{
++ urb_req_t *req;
++
++ if (usbos_info->usbos_tx_kt) {
++ wake_up_interruptible(&usbos_info->usbos_tx_queue_head);
++ kthread_stop(usbos_info->usbos_tx_kt);
++ }
++
++ /* Move pending requests to free queue so they can be freed */
++ while ((req = dbus_usbos_qdeq(
++ &usbos_info->usbos_tx_list, &usbos_info->usbos_tx_list_lock)) != NULL) {
++ dbus_usbos_qenq(&usbos_info->req_txfreeq, req, &usbos_info->txfree_lock);
++ }
++}
++
++/**
++ * Allow USB in-band resume to block by submitting CTRL and DATA URBs on a separate thread.
++ */
++int
++dbus_usbos_tx_thread_func(void *data)
++{
++ usbos_info_t *usbos_info = (usbos_info_t *)data;
++ urb_req_t *req;
++ dbus_irb_tx_t *txirb;
++ int ret;
++ unsigned long flags;
++
++#ifdef WL_THREADNICE
++ set_user_nice(current, WL_THREADNICE);
++#endif
++
++ while (1) {
++ /* Wait until there are URBs to submit */
++ wait_event_interruptible_timeout(
++ usbos_info->usbos_tx_queue_head,
++ !list_empty(&usbos_info->usbos_tx_list) ||
++ usbos_info->ctl_state == USBOS_REQUEST_STATE_SCHEDULED,
++ 100);
++
++ if (kthread_should_stop())
++ break;
++
++ /* Submit CTRL URB if needed */
++ if (usbos_info->ctl_state == USBOS_REQUEST_STATE_SCHEDULED) {
++
++ /* Disable USB autosuspend until this request completes. If the
++ * interface was suspended, this call blocks until it has been resumed.
++ */
++ USB_AUTOPM_GET_INTERFACE(g_probe_info.intf);
++
++ usbos_info->ctl_state = USBOS_REQUEST_STATE_SUBMITTED;
++
++ ret = USB_SUBMIT_URB(usbos_info->ctl_urb);
++ if (ret != 0) {
++ DBUSERR(("%s CTRL USB_SUBMIT_URB failed, status %d\n",
++ __FUNCTION__, ret));
++
++ usbos_info->ctl_state = USBOS_REQUEST_STATE_UNSCHEDULED;
++ up(&usbos_info->ctl_lock);
++
++ USB_AUTOPM_PUT_INTERFACE_ASYNC(g_probe_info.intf);
++ }
++ }
++
++ /* Submit all available TX URBs */
++ while ((req = dbus_usbos_qdeq(&usbos_info->usbos_tx_list,
++ &usbos_info->usbos_tx_list_lock)) != NULL) {
++
++ /* Disable USB autosuspend until this request completes. If the
++ * interface was suspended, this call blocks until it has been resumed.
++ */
++ USB_AUTOPM_GET_INTERFACE(g_probe_info.intf);
++
++ spin_lock_irqsave(&usbos_info->txlock, flags);
++
++ ret = USB_SUBMIT_URB(req->urb);
++ if (ret == 0) {
++ /* URB submitted successfully */
++ dbus_usbos_qenq(&usbos_info->req_txpostedq, req,
++ &usbos_info->txposted_lock);
++ atomic_inc(&usbos_info->txposted);
++ } else {
++ /* Submitting the URB failed. */
++ DBUSERR(("%s TX USB_SUBMIT_URB failed, status %d\n",
++ __FUNCTION__, ret));
++
++ USB_AUTOPM_PUT_INTERFACE_ASYNC(g_probe_info.intf);
++ }
++
++ spin_unlock_irqrestore(&usbos_info->txlock, flags);
++
++ if (ret != 0) {
++ /* Cleanup and notify higher layers */
++ dbus_usbos_qenq(&usbos_info->req_txfreeq, req,
++ &usbos_info->txfree_lock);
++
++ txirb = req->arg;
++ if (txirb->send_buf) {
++ MFREE(usbos_info->pub->osh, txirb->send_buf, req->buf_len);
++ txirb->send_buf = NULL;
++ req->buf_len = 0;
++ }
++
++ if (likely (usbos_info->cbarg && usbos_info->cbs)) {
++ if (likely (usbos_info->cbs->send_irb_complete != NULL))
++ usbos_info->cbs->send_irb_complete(
++ usbos_info->cbarg, txirb, DBUS_ERR_TXDROP);
++ }
++ }
++ }
++ }
++
++ return 0;
++} /* dbus_usbos_tx_thread_func */
++
++#endif /* USBOS_TX_THREAD */
++
++#ifdef USBOS_THREAD
++
++/**
++ * Increase system performance by creating a USB thread that runs parallel to other system
++ * activity.
++ */
++static void*
++dbus_usbos_thread_init(usbos_info_t *usbos_info)
++{
++ usbos_list_entry_t *entry;
++ unsigned long flags, ii;
++
++ spin_lock_init(&usbos_info->usbos_list_lock);
++ spin_lock_init(&usbos_info->ctrl_lock);
++ INIT_LIST_HEAD(&usbos_info->usbos_list);
++ INIT_LIST_HEAD(&usbos_info->usbos_free_list);
++ init_waitqueue_head(&usbos_info->usbos_queue_head);
++ atomic_set(&usbos_info->usbos_list_cnt, 0);
++
++
++ for (ii = 0; ii < (usbos_info->pub->nrxq + usbos_info->pub->ntxq); ii++) {
++ entry = MALLOC(usbos_info->pub->osh, sizeof(usbos_list_entry_t));
++ if (entry) {
++ spin_lock_irqsave(&usbos_info->usbos_list_lock, flags);
++ list_add_tail((struct list_head*) entry, &usbos_info->usbos_free_list);
++ spin_unlock_irqrestore(&usbos_info->usbos_list_lock, flags);
++ } else {
++ DBUSERR(("Failed to create list\n"));
++ }
++ }
++
++ usbos_info->usbos_kt = kthread_create(dbus_usbos_thread_func,
++ usbos_info, "usb-thread");
++
++ if (IS_ERR(usbos_info->usbos_kt)) {
++ DBUSERR(("Thread Creation failed\n"));
++ return (NULL);
++ }
++
++ wake_up_process(usbos_info->usbos_kt);
++
++ return (usbos_info->usbos_kt);
++}
++
++static void
++dbus_usbos_thread_deinit(usbos_info_t *usbos_info)
++{
++ struct list_head *cur, *next;
++ usbos_list_entry_t *entry;
++ unsigned long flags;
++
++ if (usbos_info->usbos_kt) {
++ wake_up_interruptible(&usbos_info->usbos_queue_head);
++ kthread_stop(usbos_info->usbos_kt);
++ }
++#if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
++#pragma GCC diagnostic push
++#pragma GCC diagnostic ignored "-Wcast-qual"
++#endif
++ list_for_each_safe(cur, next, &usbos_info->usbos_list)
++ {
++ entry = list_entry(cur, struct usbos_list_entry, list);
++ /* detach this entry from the list and then free the entry */
++ spin_lock_irqsave(&usbos_info->usbos_list_lock, flags);
++ list_del(cur);
++ MFREE(usbos_info->pub->osh, entry, sizeof(usbos_list_entry_t));
++ spin_unlock_irqrestore(&usbos_info->usbos_list_lock, flags);
++ }
++
++ list_for_each_safe(cur, next, &usbos_info->usbos_free_list)
++ {
++ entry = list_entry(cur, struct usbos_list_entry, list);
++ /* detach this entry from the list and then free the entry */
++ spin_lock_irqsave(&usbos_info->usbos_list_lock, flags);
++ list_del(cur);
++ MFREE(usbos_info->pub->osh, entry, sizeof(usbos_list_entry_t));
++ spin_unlock_irqrestore(&usbos_info->usbos_list_lock, flags);
++ }
++#if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
++#pragma GCC diagnostic pop
++#endif
++}
++
++/** Process completed URBs in a worker thread */
++static int
++dbus_usbos_thread_func(void *data)
++{
++ usbos_info_t *usbos_info = (usbos_info_t *)data;
++ usbos_list_entry_t *entry;
++ struct list_head *cur, *next;
++ unsigned long flags;
++
++#ifdef WL_THREADNICE
++ set_user_nice(current, WL_THREADNICE);
++#endif
++
++ while (1) {
++ /* If the list is empty, then go to sleep */
++ wait_event_interruptible_timeout
++ (usbos_info->usbos_queue_head,
++ atomic_read(&usbos_info->usbos_list_cnt) > 0,
++ 100);
++
++ if (kthread_should_stop())
++ break;
++
++ spin_lock_irqsave(&usbos_info->usbos_list_lock, flags);
++
++ /* For each entry on the list, process it. Remove the entry from
++ * the list when done.
++ */
++#if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
++#pragma GCC diagnostic push
++#pragma GCC diagnostic ignored "-Wcast-qual"
++#endif
++ list_for_each_safe(cur, next, &usbos_info->usbos_list)
++ {
++ urb_req_t *req;
++ int len;
++ int stat;
++ usbos_info_t *usbos_info_local;
++
++ entry = list_entry(cur, struct usbos_list_entry, list);
++ if (entry == NULL)
++ break;
++
++ req = entry->urb_context;
++ len = entry->urb_length;
++ stat = entry->urb_status;
++ usbos_info_local = req->usbinfo;
++
++ /* detach this entry from the list and attach it to the free list */
++ list_del_init(cur);
++ spin_unlock_irqrestore(&usbos_info_local->usbos_list_lock, flags);
++
++ dbus_usbos_recv_complete_handle(req, len, stat);
++
++ spin_lock_irqsave(&usbos_info_local->usbos_list_lock, flags);
++
++ list_add_tail(cur, &usbos_info_local->usbos_free_list);
++
++ atomic_dec(&usbos_info_local->usbos_list_cnt);
++ }
++
++ spin_unlock_irqrestore(&usbos_info->usbos_list_lock, flags);
++
++ }
++#if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
++#pragma GCC diagnostic pop
++#endif
++
++ return 0;
++} /* dbus_usbos_thread_func */
++
++/** Called on Linux calling URB callback, see dbus_usbos_recv_complete() */
++static void
++dbus_usbos_dispatch_schedule(CALLBACK_ARGS)
++{
++ urb_req_t *req = urb->context;
++ usbos_info_t *usbos_info = req->usbinfo;
++ usbos_list_entry_t *entry;
++ unsigned long flags;
++ struct list_head *cur;
++
++ spin_lock_irqsave(&usbos_info->usbos_list_lock, flags);
++
++ cur = usbos_info->usbos_free_list.next;
++#if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
++#pragma GCC diagnostic push
++#pragma GCC diagnostic ignored "-Wcast-qual"
++#endif
++ entry = list_entry(cur, struct usbos_list_entry, list);
++#if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
++#pragma GCC diagnostic pop
++#endif
++
++ /* detach this entry from the free list and prepare it insert it to use list */
++ list_del_init(cur);
++
++ if (entry) {
++ entry->urb_context = urb->context;
++ entry->urb_length = urb->actual_length;
++ entry->urb_status = urb->status;
++
++ atomic_inc(&usbos_info->usbos_list_cnt);
++ list_add_tail(cur, &usbos_info->usbos_list);
++ } else {
++ DBUSERR(("!!!!!!OUT OF MEMORY!!!!!!!\n"));
++ }
++
++ spin_unlock_irqrestore(&usbos_info->usbos_list_lock, flags);
++
++ /* thread */
++ wake_up_interruptible(&usbos_info->usbos_queue_head);
++} /* dbus_usbos_dispatch_schedule */
++
++#endif /* USBOS_THREAD */
++
++
++
++
++#ifdef BCM_REQUEST_FW
++
++struct request_fw_context {
++ const struct firmware *firmware;
++ struct semaphore lock;
++};
++
++/*
++ * Callback for dbus_request_firmware().
++ */
++static void
++dbus_request_firmware_done(const struct firmware *firmware, void *ctx)
++{
++ struct request_fw_context *context = (struct request_fw_context*)ctx;
++
++ /* Store the received firmware handle in the context and wake requester */
++ context->firmware = firmware;
++ up(&context->lock);
++}
++
++/*
++ * Send a firmware request and wait for completion.
++ *
++ * The use of the asynchronous version of request_firmware() is needed to avoid
++ * kernel oopses when we just come out of system hibernate.
++ */
++static int
++dbus_request_firmware(const char *name, const struct firmware **firmware)
++{
++ struct request_fw_context *context;
++ int ret;
++
++ context = kzalloc(sizeof(*context), GFP_KERNEL);
++ if (!context)
++ return -ENOMEM;
++
++ sema_init(&context->lock, 0);
++
++ ret = request_firmware_nowait(THIS_MODULE, true, name, &g_probe_info.usb->dev,
++ GFP_KERNEL, context, dbus_request_firmware_done);
++ if (ret) {
++ kfree(context);
++ return ret;
++ }
++
++ /* Wait for completion */
++ if (down_interruptible(&context->lock) != 0) {
++ kfree(context);
++ return -ERESTARTSYS;
++ }
++
++ *firmware = context->firmware;
++ kfree(context);
++
++ return *firmware != NULL ? 0 : -ENOENT;
++}
++
++static void *
++dbus_get_fwfile(int devid, int chiprev, uint8 **fw, int *fwlen, uint16 boardtype, uint16 boardrev)
++{
++ const struct firmware *firmware = NULL;
++#ifndef OEM_ANDROID
++ s8 *device_id = NULL;
++ s8 *chip_rev = "";
++#endif /* OEM_ANDROID */
++ s8 file_name[64];
++ int ret;
++
++#ifndef OEM_ANDROID
++ switch (devid) {
++ case BCM4350_CHIP_ID:
++ case BCM4354_CHIP_ID:
++ case BCM43556_CHIP_ID:
++ case BCM43558_CHIP_ID:
++ case BCM43566_CHIP_ID:
++ case BCM43568_CHIP_ID:
++ case BCM43570_CHIP_ID:
++ case BCM4358_CHIP_ID:
++ device_id = "4350";
++ break;
++ case BCM43143_CHIP_ID:
++ device_id = "43143";
++ break;
++ case BCM43234_CHIP_ID:
++ case BCM43235_CHIP_ID:
++ case BCM43236_CHIP_ID:
++ device_id = "43236";
++ break;
++ case BCM43242_CHIP_ID:
++ device_id = "43242";
++ break;
++ case BCM43238_CHIP_ID:
++ device_id = "43238";
++ break;
++ case BCM43526_CHIP_ID:
++ device_id = "43526";
++ break;
++ case BCM43569_CHIP_ID:
++ device_id = "43569";
++ switch (chiprev) {
++ case 0:
++ chip_rev = "a0";
++ break;
++ case 2:
++ chip_rev = "a2";
++ break;
++ default:
++ break;
++ }
++ break;
++ default:
++ DBUSERR(("unsupported device %x\n", devid));
++ return NULL;
++ }
++
++ /* Load firmware */
++ snprintf(file_name, sizeof(file_name), "brcm/bcm%s%s-firmware.bin", device_id, chip_rev);
++#else
++ snprintf(file_name, sizeof(file_name), "%s", CONFIG_ANDROID_BCMDHD_FW_PATH);
++#endif /* OEM_ANDROID */
++
++ ret = dbus_request_firmware(file_name, &firmware);
++ if (ret) {
++ DBUSERR(("fail to request firmware %s\n", file_name));
++ return NULL;
++ }
++
++ *fwlen = firmware->size;
++ *fw = (uint8 *)firmware->data;
++ return (void *)firmware;
++
++}
++
++static void *
++dbus_get_nvfile(int devid, int chiprev, uint8 **fw, int *fwlen, uint16 boardtype, uint16 boardrev)
++{
++ const struct firmware *firmware = NULL;
++#ifndef OEM_ANDROID
++ s8 *device_id = NULL;
++ s8 *chip_rev = "";
++#endif /* OEM_ANDROID */
++ s8 file_name[64];
++ int ret;
++
++#ifndef OEM_ANDROID
++ switch (devid) {
++ case BCM4350_CHIP_ID:
++ case BCM4354_CHIP_ID:
++ case BCM43556_CHIP_ID:
++ case BCM43558_CHIP_ID:
++ case BCM43566_CHIP_ID:
++ case BCM43568_CHIP_ID:
++ case BCM43570_CHIP_ID:
++ case BCM4358_CHIP_ID:
++ device_id = "4350";
++ break;
++ case BCM43143_CHIP_ID:
++ device_id = "43143";
++ break;
++ case BCM43234_CHIP_ID:
++ device_id = "43234";
++ break;
++ case BCM43235_CHIP_ID:
++ device_id = "43235";
++ break;
++ case BCM43236_CHIP_ID:
++ device_id = "43236";
++ break;
++ case BCM43238_CHIP_ID:
++ device_id = "43238";
++ break;
++ case BCM43242_CHIP_ID:
++ device_id = "43242";
++ break;
++ case BCM43526_CHIP_ID:
++ device_id = "43526";
++ break;
++ case BCM43569_CHIP_ID:
++ device_id = "43569";
++ switch (chiprev) {
++ case 0:
++ chip_rev = "a0";
++ break;
++ case 2:
++ chip_rev = "a2";
++ break;
++ default:
++ break;
++ }
++ break;
++ default:
++ DBUSERR(("unsupported device %x\n", devid));
++ return NULL;
++ }
++
++ /* Load board specific nvram file */
++ snprintf(file_name, sizeof(file_name), "brcm/bcm%s%s-%2x-%2x.nvm",
++ device_id, chip_rev, boardtype, boardrev);
++#else
++ snprintf(file_name, sizeof(file_name), "%s", CONFIG_ANDROID_BCMDHD_NVRAM_PATH);
++#endif /* OEM_ANDROID */
++
++ ret = dbus_request_firmware(file_name, &firmware);
++ if (ret) {
++ DBUSERR(("fail to request nvram %s\n", file_name));
++
++#ifndef OEM_ANDROID
++ /* Load generic nvram file */
++ snprintf(file_name, sizeof(file_name), "brcm/bcm%s%s.nvm",
++ device_id, chip_rev);
++
++ ret = dbus_request_firmware(file_name, &firmware);
++#endif /* OEM_ANDROID */
++
++ if (ret) {
++ DBUSERR(("fail to request nvram %s\n", file_name));
++ return NULL;
++ }
++ }
++
++ *fwlen = firmware->size;
++ *fw = (uint8 *)firmware->data;
++ return (void *)firmware;
++}
++
++void *
++dbus_get_fw_nvfile(int devid, int chiprev, uint8 **fw, int *fwlen, int type, uint16 boardtype,
++ uint16 boardrev)
++{
++ switch (type) {
++ case DBUS_FIRMWARE:
++ return dbus_get_fwfile(devid, chiprev, fw, fwlen, boardtype, boardrev);
++ case DBUS_NVFILE:
++ return dbus_get_nvfile(devid, chiprev, fw, fwlen, boardtype, boardrev);
++ default:
++ return NULL;
++ }
++}
++
++void
++dbus_release_fw_nvfile(void *firmware)
++{
++ release_firmware((struct firmware *)firmware);
++}
++#endif /* BCM_REQUEST_FW */
++
++#ifdef BCMUSBDEV_COMPOSITE
++/**
++ * For a composite device the interface order is not guaranteed, scan the device struct for the WLAN
++ * interface.
++ */
++static int
++dbus_usbos_intf_wlan(struct usb_device *usb)
++{
++ int i, num_of_eps, ep, intf_wlan = -1;
++ int num_intf = CONFIGDESC(usb)->bNumInterfaces;
++ struct usb_endpoint_descriptor *endpoint;
++
++ for (i = 0; i < num_intf; i++) {
++ if (IFDESC(usb, i).bInterfaceClass != USB_CLASS_VENDOR_SPEC)
++ continue;
++ num_of_eps = IFDESC(usb, i).bNumEndpoints;
++
++ for (ep = 0; ep < num_of_eps; ep++) {
++ endpoint = &IFEPDESC(usb, i, ep);
++ if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
++ USB_ENDPOINT_XFER_BULK) {
++ intf_wlan = i;
++ break;
++ }
++ }
++ if (ep < num_of_eps)
++ break;
++ }
++
++ return intf_wlan;
++}
++#endif /* BCMUSBDEV_COMPOSITE */
+diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd.h b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd.h
+index 6b413b5727a7..eb6edcb9b3ea 100644
+--- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd.h
++++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd.h
+@@ -51,6 +51,9 @@
+ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_HAS_WAKELOCK)
+ #include
+ #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined (CONFIG_HAS_WAKELOCK) */
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
++#include
++#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) */
+ /* The kernel threading is sdio-specific */
+ struct task_struct;
+ struct sched_param;
+@@ -106,6 +109,7 @@ enum dhd_bus_state {
+ DHD_BUS_DATA, /* Ready for frame transfers */
+ DHD_BUS_SUSPEND, /* Bus has been suspended */
+ DHD_BUS_DOWN_IN_PROGRESS, /* Bus going Down */
++ DHD_BUS_REMOVE, /* Bus has been removed */
+ };
+
+ /*
+@@ -217,7 +221,11 @@ enum dhd_bus_state {
+ DHD_BUS_BUSY_CHECK_RPM_SUSPEND_IN_PROGRESS(dhdp))
+
+ #define DHD_BUS_CHECK_DOWN_OR_DOWN_IN_PROGRESS(dhdp) \
+- ((dhdp)->busstate == DHD_BUS_DOWN || (dhdp)->busstate == DHD_BUS_DOWN_IN_PROGRESS)
++ ((dhdp)->busstate == DHD_BUS_DOWN || (dhdp)->busstate == DHD_BUS_DOWN_IN_PROGRESS || \
++ (dhdp)->busstate == DHD_BUS_REMOVE)
++
++#define DHD_BUS_CHECK_REMOVE(dhdp) \
++ ((dhdp)->busstate == DHD_BUS_REMOVE)
+
+ /* Macro to print Ethernet Address as String
+ * expects both arguements as (char *)
+@@ -667,6 +675,9 @@ typedef struct dhd_pub {
+ * please do NOT merge it back from other branches !!!
+ */
+
++#ifdef BCMDBUS
++ struct dbus_pub *dbus;
++#endif /* BCMDBUS */
+
+ /* Internal dhd items */
+ bool up; /* Driver up/down (to OS) */
+@@ -1028,6 +1039,10 @@ typedef struct dhd_pub {
+ char *clm_path; /* module_param: path to clm vars file */
+ char *conf_path; /* module_param: path to config vars file */
+ struct dhd_conf *conf; /* Bus module handle */
++ void *adapter; /* adapter information, interrupt, fw path etc. */
++#ifdef BCMDBUS
++ bool dhd_remove;
++#endif /* BCMDBUS */
+ } dhd_pub_t;
+
+ typedef struct {
+@@ -1347,12 +1362,36 @@ typedef enum dhd_ioctl_recieved_status
+ */
+ void dhd_net_if_lock(struct net_device *dev);
+ void dhd_net_if_unlock(struct net_device *dev);
+-
+ #if defined(MULTIPLE_SUPPLICANT)
+-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1
+-extern struct mutex _dhd_sdio_mutex_lock_;
++extern void wl_android_post_init(void); // terence 20120530: fix critical section in dhd_open and dhdsdio_probe
++#endif
++
++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && defined(MULTIPLE_SUPPLICANT)
++extern struct mutex _dhd_mutex_lock_;
++#define DHD_MUTEX_IS_LOCK_RETURN() \
++ if (mutex_is_locked(&_dhd_mutex_lock_) != 0) { \
++ printf("%s : probe is already running! return.\n", __FUNCTION__); \
++ return 0; \
++ }
++#define DHD_MUTEX_LOCK() \
++ do { \
++ if (mutex_is_locked(&_dhd_mutex_lock_) == 0) { \
++ printf("%s : no mutex held. set lock\n", __FUNCTION__); \
++ } else { \
++ printf("%s : mutex is locked!. wait for unlocking\n", __FUNCTION__); \
++ } \
++ mutex_lock(&_dhd_mutex_lock_); \
++ } while (0)
++#define DHD_MUTEX_UNLOCK() \
++ do { \
++ mutex_unlock(&_dhd_mutex_lock_); \
++ printf("%s : the lock is released.\n", __FUNCTION__); \
++ } while (0)
++#else
++#define DHD_MUTEX_IS_LOCK_RETURN(a) do {} while (0)
++#define DHD_MUTEX_LOCK(a) do {} while (0)
++#define DHD_MUTEX_UNLOCK(a) do {} while (0)
+ #endif
+-#endif /* MULTIPLE_SUPPLICANT */
+
+ typedef enum dhd_attach_states
+ {
+@@ -1386,7 +1425,11 @@ typedef enum dhd_attach_states
+ * Returned structure should have bus and prot pointers filled in.
+ * bus_hdrlen specifies required headroom for bus module header.
+ */
+-extern dhd_pub_t *dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen);
++extern dhd_pub_t *dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen
++#ifdef BCMDBUS
++ , void *adapter
++#endif
++);
+ #if defined(WLP2P) && defined(WL_CFG80211)
+ /* To allow attach/detach calls corresponding to p2p0 interface */
+ extern int dhd_attach_p2p(dhd_pub_t *);
+@@ -1482,7 +1525,7 @@ extern void dhd_os_dhdiovar_lock(dhd_pub_t *pub);
+ extern void dhd_os_dhdiovar_unlock(dhd_pub_t *pub);
+ extern int dhd_os_proto_block(dhd_pub_t * pub);
+ extern int dhd_os_proto_unblock(dhd_pub_t * pub);
+-extern int dhd_os_ioctl_resp_wait(dhd_pub_t * pub, uint * condition);
++extern int dhd_os_ioctl_resp_wait(dhd_pub_t * pub, uint * condition, bool resched);
+ extern int dhd_os_ioctl_resp_wake(dhd_pub_t * pub);
+ extern unsigned int dhd_os_get_ioctl_resp_timeout(void);
+ extern void dhd_os_set_ioctl_resp_timeout(unsigned int timeout_msec);
+@@ -1700,6 +1743,9 @@ extern int dhd_event_ifdel(struct dhd_info *dhd, struct wl_event_data_if *ifeven
+ char *name, uint8 *mac);
+ extern int dhd_event_ifchange(struct dhd_info *dhd, struct wl_event_data_if *ifevent,
+ char *name, uint8 *mac);
++#ifdef DHD_UPDATE_INTF_MAC
++extern int dhd_op_if_update(dhd_pub_t *dhdpub, int ifidx);
++#endif /* DHD_UPDATE_INTF_MAC */
+ extern struct net_device* dhd_allocate_if(dhd_pub_t *dhdpub, int ifidx, const char *name,
+ uint8 *mac, uint8 bssidx, bool need_rtnl_lock, const char *dngl_name);
+ extern int dhd_remove_if(dhd_pub_t *dhdpub, int ifidx, bool need_rtnl_lock);
+@@ -1821,6 +1867,9 @@ extern uint dhd_console_ms;
+ extern uint android_msg_level;
+ extern uint config_msg_level;
+ extern uint sd_msglevel;
++#ifdef BCMDBUS
++extern uint dbus_msglevel;
++#endif /* BCMDBUS */
+ #ifdef WL_WIRELESS_EXT
+ extern uint iw_msg_level;
+ #endif
+@@ -2031,7 +2080,9 @@ extern char fw_path2[MOD_PARAM_PATHLEN];
+
+ /* Flag to indicate if we should download firmware on driver load */
+ extern uint dhd_download_fw_on_driverload;
++#ifndef BCMDBUS
+ extern int allow_delay_fwdl;
++#endif /* !BCMDBUS */
+
+ extern int dhd_process_cid_mac(dhd_pub_t *dhdp, bool prepost);
+ extern int dhd_write_file(const char *filepath, char *buf, int buf_len);
+@@ -2226,6 +2277,12 @@ extern void dhd_os_general_spin_unlock(dhd_pub_t *pub, unsigned long flags);
+
+ extern void dhd_dump_to_kernelog(dhd_pub_t *dhdp);
+
++#ifdef BCMDBUS
++extern uint dhd_get_rxsz(dhd_pub_t *pub);
++extern void dhd_set_path(dhd_pub_t *pub);
++extern void dhd_bus_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf);
++extern void dhd_bus_clearcounts(dhd_pub_t *dhdp);
++#endif /* BCMDBUS */
+
+ #ifdef DHD_L2_FILTER
+ extern int dhd_get_parp_status(dhd_pub_t *dhdp, uint32 idx);
+diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_bus.h b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_bus.h
+index c785f1210997..e0f048333077 100644
+--- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_bus.h
++++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_bus.h
+@@ -33,6 +33,10 @@
+ #ifndef _dhd_bus_h_
+ #define _dhd_bus_h_
+
++extern int dbus_up(struct dhd_bus *pub);
++extern int dbus_stop(struct dhd_bus *pub);
++extern int dbus_send_ctl(struct dhd_bus *pub, uint8 *buf, int len);
++extern int dbus_recv_ctl(struct dhd_bus *pub, uint8 *buf, int len);
+ /*
+ * Exported from dhd bus module (dhd_usb, dhd_sdio)
+ */
+diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_cdc.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_cdc.c
+old mode 100755
+new mode 100644
+index 11344de2a068..3fb5e457040a
+--- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_cdc.c
++++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_cdc.c
+@@ -49,6 +49,9 @@
+ #include
+ #include
+ #endif
++#ifdef BCMDBUS
++#include
++#endif /* BCMDBUS */
+
+ #ifdef DHD_ULP
+ #include
+@@ -68,15 +71,20 @@ typedef struct dhd_prot {
+ uint16 reqid;
+ uint8 pending;
+ uint32 lastcmd;
++#ifdef BCMDBUS
++ uint ctl_completed;
++#endif /* BCMDBUS */
+ uint8 bus_header[BUS_HEADER_LEN];
+ cdc_ioctl_t msg;
+ unsigned char buf[WLC_IOCTL_MAXLEN + ROUND_UP_MARGIN];
+ } dhd_prot_t;
+
+-
+ static int
+ dhdcdc_msg(dhd_pub_t *dhd)
+ {
++#ifdef BCMDBUS
++ int timeout = 0;
++#endif /* BCMDBUS */
+ int err = 0;
+ dhd_prot_t *prot = dhd->prot;
+ int len = ltoh32(prot->msg.len) + sizeof(cdc_ioctl_t);
+@@ -93,8 +101,51 @@ dhdcdc_msg(dhd_pub_t *dhd)
+ len = CDC_MAX_MSG_SIZE;
+
+ /* Send request */
++#ifdef BCMDBUS
++ DHD_OS_IOCTL_RESP_LOCK(dhd);
++ prot->ctl_completed = FALSE;
++ err = dbus_send_ctl(dhd->bus, (void *)&prot->msg, len);
++ if (err) {
++ DHD_ERROR(("dbus_send_ctl error=%d\n", err));
++ DHD_OS_IOCTL_RESP_UNLOCK(dhd);
++ DHD_OS_WAKE_UNLOCK(dhd);
++ return err;
++ }
++#else
+ err = dhd_bus_txctl(dhd->bus, (uchar*)&prot->msg, len);
+-
++#endif /* BCMDBUS */
++
++#ifdef BCMDBUS
++ timeout = dhd_os_ioctl_resp_wait(dhd, &prot->ctl_completed, false);
++ if ((!timeout) || (!prot->ctl_completed)) {
++ DHD_ERROR(("Txctl timeout %d ctl_completed %d\n",
++ timeout, prot->ctl_completed));
++ DHD_ERROR(("Txctl wait timed out\n"));
++ err = -1;
++ }
++ DHD_OS_IOCTL_RESP_UNLOCK(dhd);
++#endif /* BCMDBUS */
++#if defined(BCMDBUS) && defined(INTR_EP_ENABLE)
++ /* If the ctl write is successfully completed, wait for an acknowledgement
++ * that indicates that it is now ok to do ctl read from the dongle
++ */
++ if (err != -1) {
++ DHD_OS_IOCTL_RESP_LOCK(dhd);
++ prot->ctl_completed = FALSE;
++ if (dbus_poll_intr(dhd->dbus)) {
++ DHD_ERROR(("dbus_poll_intr not submitted\n"));
++ } else {
++ /* interrupt polling is sucessfully submitted. Wait for dongle to send
++ * interrupt
++ */
++ timeout = dhd_os_ioctl_resp_wait(dhd, &prot->ctl_completed, false);
++ if (!timeout) {
++ DHD_ERROR(("intr poll wait timed out\n"));
++ }
++ }
++ DHD_OS_IOCTL_RESP_UNLOCK(dhd);
++ }
++#endif /* defined(BCMDBUS) && defined(INTR_EP_ENABLE) */
+ DHD_OS_WAKE_UNLOCK(dhd);
+ return err;
+ }
+@@ -102,6 +153,9 @@ dhdcdc_msg(dhd_pub_t *dhd)
+ static int
+ dhdcdc_cmplt(dhd_pub_t *dhd, uint32 id, uint32 len)
+ {
++#ifdef BCMDBUS
++ int timeout = 0;
++#endif /* BCMDBUS */
+ int ret;
+ int cdc_len = len + sizeof(cdc_ioctl_t);
+ dhd_prot_t *prot = dhd->prot;
+@@ -109,11 +163,37 @@ dhdcdc_cmplt(dhd_pub_t *dhd, uint32 id, uint32 len)
+ DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+ do {
++#ifdef BCMDBUS
++ DHD_OS_IOCTL_RESP_LOCK(dhd);
++ prot->ctl_completed = FALSE;
++ ret = dbus_recv_ctl(dhd->bus, (uchar*)&prot->msg, cdc_len);
++ if (ret) {
++ DHD_ERROR(("dbus_recv_ctl error=0x%x(%d)\n", ret, ret));
++ DHD_OS_IOCTL_RESP_UNLOCK(dhd);
++ goto done;
++ }
++ timeout = dhd_os_ioctl_resp_wait(dhd, &prot->ctl_completed, false);
++ if ((!timeout) || (!prot->ctl_completed)) {
++ DHD_ERROR(("Rxctl timeout %d ctl_completed %d\n",
++ timeout, prot->ctl_completed));
++ ret = -1;
++ DHD_OS_IOCTL_RESP_UNLOCK(dhd);
++
++ goto done;
++ }
++ DHD_OS_IOCTL_RESP_UNLOCK(dhd);
++
++ ret = cdc_len;
++#else
+ ret = dhd_bus_rxctl(dhd->bus, (uchar*)&prot->msg, cdc_len);
++#endif /* BCMDBUS */
+ if (ret < 0)
+ break;
+ } while (CDC_IOC_ID(ltoh32(prot->msg.flags)) != id);
+
++#ifdef BCMDBUS
++done:
++#endif /* BCMDBUS */
+ return ret;
+ }
+
+@@ -286,6 +366,25 @@ done:
+ return ret;
+ }
+
++#ifdef BCMDBUS
++int
++dhd_prot_ctl_complete(dhd_pub_t *dhd)
++{
++ dhd_prot_t *prot;
++
++ if (dhd == NULL)
++ return BCME_ERROR;
++
++ prot = dhd->prot;
++
++ ASSERT(prot);
++ DHD_OS_IOCTL_RESP_LOCK(dhd);
++ prot->ctl_completed = TRUE;
++ dhd_os_ioctl_resp_wake(dhd);
++ DHD_OS_IOCTL_RESP_UNLOCK(dhd);
++ return 0;
++}
++#endif /* BCMDBUS */
+
+ int
+ dhd_prot_ioctl(dhd_pub_t *dhd, int ifidx, wl_ioctl_t * ioc, void * buf, int len)
+@@ -487,6 +586,12 @@ dhd_prot_hdrpull(dhd_pub_t *dhd, int *ifidx, void *pktbuf, uchar *reorder_buf_in
+ dhd_wlfc_parse_header_info(dhd, pktbuf, (data_offset << 2),
+ reorder_buf_info, reorder_info_len);
+
++#ifdef BCMDBUS
++#ifndef DHD_WLFC_THREAD
++ dhd_wlfc_commit_packets(dhd,
++ (f_commitpkt_t)dhd_bus_txdata, dhd->bus, NULL, FALSE);
++#endif /* DHD_WLFC_THREAD */
++#endif /* BCMDBUS */
+ }
+ #endif /* PROP_TXSTATUS */
+
+@@ -572,6 +677,14 @@ dhd_sync_with_dongle(dhd_pub_t *dhd)
+ ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_REVINFO, &revinfo, sizeof(revinfo), FALSE, 0);
+ if (ret < 0)
+ goto done;
++#if defined(BCMDBUS)
++ if (dhd_download_fw_on_driverload) {
++ dhd_conf_reset(dhd);
++ dhd_conf_set_chiprev(dhd, revinfo.chipnum, revinfo.chiprev);
++ dhd_conf_preinit(dhd);
++ dhd_conf_read_config(dhd, dhd->conf_path);
++ }
++#endif /* BCMDBUS */
+
+
+ DHD_SSSR_DUMP_INIT(dhd);
+diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_cfg80211.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_cfg80211.c
+old mode 100755
+new mode 100644
+index d01e7680142d..b98fcd36f599
+--- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_cfg80211.c
++++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_cfg80211.c
+@@ -161,8 +161,10 @@ void dhd_netdev_free(struct net_device *ndev)
+ #ifdef WL_CFG80211
+ ndev = dhd_cfg80211_netdev_free(ndev);
+ #endif
++#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)
+ if (ndev)
+ free_netdev(ndev);
++#endif
+ }
+
+ static s32
+diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_common.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_common.c
+index 485594e9c308..bbab84aebdc6 100644
+--- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_common.c
++++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_common.c
+@@ -650,7 +650,11 @@ void* dhd_get_fwdump_buf(dhd_pub_t *dhd_pub, uint32 length)
+ int
+ dhd_common_socram_dump(dhd_pub_t *dhdp)
+ {
++#ifdef BCMDBUS
++ return 0;
++#else
+ return dhd_socram_dump(dhdp->bus);
++#endif /* BCMDBUS */
+ }
+
+ static int
+@@ -1038,7 +1042,7 @@ dhd_iovar_parse_bssidx(dhd_pub_t *dhd_pub, const char *params, uint32 *idx, cons
+ return BCME_OK;
+ }
+
+-#if defined(DHD_DEBUG) && defined(BCMDHDUSB)
++#if defined(DHD_DEBUG) && defined(BCMDBUS)
+ /* USB Device console input function */
+ int dhd_bus_console_in(dhd_pub_t *dhd, uchar *msg, uint msglen)
+ {
+@@ -1047,7 +1051,7 @@ int dhd_bus_console_in(dhd_pub_t *dhd, uchar *msg, uint msglen)
+ return dhd_iovar(dhd, 0, "cons", msg, msglen, NULL, 0, TRUE);
+
+ }
+-#endif /* DHD_DEBUG && BCMDHDUSB */
++#endif /* DHD_DEBUG && BCMDBUS */
+
+ #ifdef DHD_DEBUG
+ int
+@@ -1263,10 +1267,12 @@ dhd_doiovar(dhd_pub_t *dhd_pub, const bcm_iovar_t *vi, uint32 actionid, const ch
+ bcopy(&int_val, arg, val_size);
+ break;
+
++#ifndef BCMDBUS
+ case IOV_GVAL(IOV_WDTICK):
+ int_val = (int32)dhd_watchdog_ms;
+ bcopy(&int_val, arg, val_size);
+ break;
++#endif /* !BCMDBUS */
+
+ case IOV_SVAL(IOV_WDTICK):
+ if (!dhd_pub->up) {
+@@ -1285,6 +1291,7 @@ dhd_doiovar(dhd_pub_t *dhd_pub, const bcm_iovar_t *vi, uint32 actionid, const ch
+ bcmerror = dhd_dump(dhd_pub, arg, len);
+ break;
+
++#ifndef BCMDBUS
+ case IOV_GVAL(IOV_DCONSOLE_POLL):
+ int_val = (int32)dhd_console_ms;
+ bcopy(&int_val, arg, val_size);
+@@ -1298,6 +1305,7 @@ dhd_doiovar(dhd_pub_t *dhd_pub, const bcm_iovar_t *vi, uint32 actionid, const ch
+ if (len > 0)
+ bcmerror = dhd_bus_console_in(dhd_pub, arg, len - 1);
+ break;
++#endif /* !BCMDBUS */
+
+ case IOV_SVAL(IOV_CLEARCOUNTS):
+ dhd_pub->tx_packets = dhd_pub->rx_packets = 0;
+@@ -1423,9 +1431,9 @@ dhd_doiovar(dhd_pub_t *dhd_pub, const bcm_iovar_t *vi, uint32 actionid, const ch
+
+ case IOV_GVAL(IOV_BUS_TYPE):
+ /* The dhd application queries the driver to check if its usb or sdio. */
+-#ifdef BCMDHDUSB
++#ifdef BCMDBUS
+ int_val = BUS_TYPE_USB;
+-#endif
++#endif /* BCMDBUS */
+ #ifdef BCMSDIO
+ int_val = BUS_TYPE_SDIO;
+ #endif
+@@ -1952,6 +1960,8 @@ dhd_doiovar(dhd_pub_t *dhd_pub, const bcm_iovar_t *vi, uint32 actionid, const ch
+ break;
+ }
+ #endif /* REPORT_FATAL_TIMEOUTS */
++#ifdef DHD_DEBUG
++#if defined(BCMSDIO) || defined(BCMPCIE)
+ case IOV_GVAL(IOV_DONGLE_TRAP_TYPE):
+ if (dhd_pub->dongle_trap_occured)
+ int_val = ltoh32(dhd_pub->last_trap_info.type);
+@@ -1971,8 +1981,6 @@ dhd_doiovar(dhd_pub_t *dhd_pub, const bcm_iovar_t *vi, uint32 actionid, const ch
+ dhd_bus_dump_trap_info(dhd_pub->bus, &strbuf);
+ break;
+ }
+-#ifdef DHD_DEBUG
+-#if defined(BCMSDIO) || defined(BCMPCIE)
+
+ case IOV_GVAL(IOV_BPADDR):
+ {
+@@ -2820,12 +2828,14 @@ dngl_host_event_process(dhd_pub_t *dhdp, bcm_dngl_event_t *event,
+ #ifdef DHD_FW_COREDUMP
+ dhdp->memdump_type = DUMP_TYPE_DONGLE_HOST_EVENT;
+ #endif /* DHD_FW_COREDUMP */
++#ifndef BCMDBUS
+ if (dhd_socram_dump(dhdp->bus)) {
+ DHD_ERROR(("%s: socram dump failed\n", __FUNCTION__));
+ } else {
+ /* Notify framework */
+ dhd_dbg_send_urgent_evt(dhdp, p, datalen);
+ }
++#endif /* !BCMDBUS */
+ }
+ #endif /* DNGL_EVENT_SUPPORT */
+
+@@ -3113,6 +3123,7 @@ wl_process_host_event(dhd_pub_t *dhd_pub, int *ifidx, void *pktdata, uint pktlen
+ dhd_ifname2idx(dhd_pub->info, event->ifname),
+ &event->addr.octet);
+ break;
++#ifndef BCMDBUS
+ #if defined(DHD_FW_COREDUMP)
+ case WLC_E_PSM_WATCHDOG:
+ DHD_ERROR(("%s: WLC_E_PSM_WATCHDOG event received : \n", __FUNCTION__));
+@@ -3121,6 +3132,7 @@ wl_process_host_event(dhd_pub_t *dhd_pub, int *ifidx, void *pktdata, uint pktlen
+ }
+ break;
+ #endif
++#endif /* !BCMDBUS */
+ #ifdef DHD_WMF
+ case WLC_E_PSTA_PRIMARY_INTF_IND:
+ dhd_update_psta_interface_for_sta(dhd_pub, event->ifname,
+@@ -3187,6 +3199,14 @@ wl_process_host_event(dhd_pub_t *dhd_pub, int *ifidx, void *pktdata, uint pktlen
+
+ default:
+ *ifidx = dhd_ifname2idx(dhd_pub->info, event->ifname);
++#ifdef DHD_UPDATE_INTF_MAC
++ if ((WLC_E_LINK==type)&&(WLC_EVENT_MSG_LINK&flags)) {
++ dhd_event_ifchange(dhd_pub->info,
++ (struct wl_event_data_if *)event,
++ event->ifname,
++ event->addr.octet);
++ }
++#endif /* DHD_UPDATE_INTF_MAC */
+ /* push up to external supp/auth */
+ dhd_event(dhd_pub->info, (char *)pvt_data, evlen, *ifidx);
+ DHD_TRACE(("%s: MAC event %d, flags %x, status %x\n",
+@@ -3580,7 +3600,7 @@ dhd_pktfilter_offload_set(dhd_pub_t * dhd, char *arg)
+ htod16(WL_PKT_FILTER_MFLAG_NEG);
+ (argv[i])++;
+ }
+- if (strlen(argv[i]) == 0) {
++ if (*argv[i] == '\0') {
+ printf("Pattern not provided\n");
+ goto fail;
+ }
+@@ -4271,6 +4291,8 @@ dhd_get_suspend_bcn_li_dtim(dhd_pub_t *dhd)
+ }
+ }
+
++ if (dhd->conf->suspend_bcn_li_dtim >= 0)
++ bcn_li_dtim = dhd->conf->suspend_bcn_li_dtim;
+ DHD_ERROR(("%s beacon=%d bcn_li_dtim=%d DTIM=%d Listen=%d\n",
+ __FUNCTION__, ap_beacon, bcn_li_dtim, dtim_period, CUSTOM_LISTEN_INTERVAL));
+
+@@ -4865,7 +4887,7 @@ dhd_apply_default_clm(dhd_pub_t *dhd, char *clm_path)
+ char iovbuf[WLC_IOCTL_SMLEN] = {0};
+ int status = FALSE;
+
+- if (clm_path[0] != '\0') {
++ if (clm_path && clm_path[0] != '\0') {
+ if (strlen(clm_path) > MOD_PARAM_PATHLEN) {
+ DHD_ERROR(("clm path exceeds max len\n"));
+ return BCME_ERROR;
+diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_config.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_config.c
+index 47480b261dd1..4f333a464fda 100644
+--- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_config.c
++++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_config.c
+@@ -1,5 +1,4 @@
+-/* SPDX-License-Identifier: GPL-2.0 */
+-
++/* SPDX-License-Identifier: GPL-2.0 */
+ #include
+ #include
+
+@@ -40,66 +39,6 @@ uint config_msg_level = CONFIG_ERROR_LEVEL;
+ #define MAXSZ_BUF 1000
+ #define MAXSZ_CONFIG 4096
+
+-#define FW_TYPE_STA 0
+-#define FW_TYPE_APSTA 1
+-#define FW_TYPE_P2P 2
+-#define FW_TYPE_ES 3
+-#define FW_TYPE_MFG 4
+-#define FW_TYPE_G 0
+-#define FW_TYPE_AG 1
+-
+-#ifdef CONFIG_PATH_AUTO_SELECT
+-#ifdef BCMSDIO
+-#define CONFIG_BCM4330B2 "config_40183b2.txt"
+-#define CONFIG_BCM43362A0 "config_40181a0.txt"
+-#define CONFIG_BCM43362A2 "config_40181a2.txt"
+-#define CONFIG_BCM43438A0 "config_43438a0.txt"
+-#define CONFIG_BCM43438A1 "config_43438a1.txt"
+-#define CONFIG_BCM43436B0 "config_43436b0.txt"
+-#define CONFIG_BCM4334B1 "config_4334b1.txt"
+-#define CONFIG_BCM43341B0 "config_43341b0.txt"
+-#define CONFIG_BCM43241B4 "config_43241b4.txt"
+-#define CONFIG_BCM4339A0 "config_4339a0.txt"
+-#define CONFIG_BCM43454C0 "config_43454c0.txt"
+-#define CONFIG_BCM43455C0 "config_43455c0.txt"
+-#define CONFIG_BCM43456C5 "config_43456c5.txt"
+-#define CONFIG_BCM4354A1 "config_4354a1.txt"
+-#endif
+-#define CONFIG_BCM4356A2 "config_4356a2.txt"
+-#define CONFIG_BCM4358A3 "config_4358a3.txt"
+-#define CONFIG_BCM4359B1 "config_4359b1.txt"
+-#define CONFIG_BCM4359C0 "config_4359c0.txt"
+-#endif
+-
+-#ifdef BCMSDIO
+-#define SBSDIO_CIS_SIZE_LIMIT 0x200
+-
+-#define FW_BCM4330B2 "fw_RK903b2"
+-#define FW_BCM4330B2_AG "fw_RK903_ag"
+-#define FW_BCM43362A0 "fw_RK901a0"
+-#define FW_BCM43362A2 "fw_RK901a2"
+-#define FW_BCM4334B1 "fw_bcm4334b1_ag"
+-#define FW_BCM43438A0 "fw_bcm43438a0"
+-#define FW_BCM43438A1 "fw_bcm43438a1"
+-#define FW_BCM43436B0 "fw_bcm43436b0"
+-#define FW_BCM43012B0 "fw_bcm43012b0"
+-#define FW_BCM43341B1 "fw_bcm43341b0_ag"
+-#define FW_BCM43241B4 "fw_bcm43241b4_ag"
+-#define FW_BCM4339A0 "fw_bcm4339a0_ag"
+-#define FW_BCM43455C0 "fw_bcm43455c0_ag"
+-#define FW_BCM43456C5 "fw_bcm43456c5_ag"
+-#define FW_BCM4354A1 "fw_bcm4354a1_ag"
+-#define FW_BCM4356A2 "fw_bcm4356a2_ag"
+-#define FW_BCM4358A3 "fw_bcm4358a3_ag"
+-#define FW_BCM4359B1 "fw_bcm4359b1_ag"
+-#define FW_BCM4359C0 "fw_bcm4359c0_ag"
+-
+-#define CLM_BCM43012B0 "clm_bcm43012b0"
+-#endif
+-#ifdef BCMPCIE
+-#define FW_BCM4356A2 "fw_bcm4356a2_pcie_ag"
+-#endif
+-
+ #define htod32(i) i
+ #define htod16(i) i
+ #define dtoh32(i) i
+@@ -107,6 +46,61 @@ uint config_msg_level = CONFIG_ERROR_LEVEL;
+ #define htodchanspec(i) i
+ #define dtohchanspec(i) i
+
++typedef struct cihp_name_map_t {
++ uint chip;
++ uint chiprev;
++ uint ag_type;
++ bool clm;
++ char *chip_name;
++ char *module_name;
++} cihp_name_map_t;
++
++/* Map of WLC_E events to connection failure strings */
++#define DONT_CARE 9999
++const cihp_name_map_t chip_name_map [] = {
++ /* ChipID Chiprev AG CLM ChipName ModuleName */
++#ifdef BCMSDIO
++ {BCM43362_CHIP_ID, 0, DONT_CARE, FALSE, "RK901a0", ""},
++ //{BCM43362_CHIP_ID, 1, DONT_CARE, FALSE, "RK901a2", "nvram_AP6210.txt"},
++ {BCM43362_CHIP_ID, 1, DONT_CARE, FALSE, "bcm40181a2", "nvram_ap6181.txt"},
++ {BCM4330_CHIP_ID, 4, FW_TYPE_G, FALSE, "RK903b2", ""},
++ {BCM4330_CHIP_ID, 4, FW_TYPE_AG, FALSE, "RK903_ag", "nvram_AP6330.txt"},
++ {BCM43430_CHIP_ID, 0, DONT_CARE, FALSE, "bcm43438a0", "nvram_ap6212.txt"},
++ {BCM43430_CHIP_ID, 1, DONT_CARE, FALSE, "bcm43438a1", "nvram_ap6212a.txt"},
++ {BCM43430_CHIP_ID, 2, DONT_CARE, FALSE, "bcm43436b0", "nvram_ap6236.txt"},
++ {BCM43012_CHIP_ID, 1, DONT_CARE, TRUE, "bcm43013b0", ""},
++ {BCM4334_CHIP_ID, 3, DONT_CARE, FALSE, "bcm4334b1_ag", ""},
++ {BCM43340_CHIP_ID, 2, DONT_CARE, FALSE, "bcm43341b0_ag", ""},
++ {BCM43341_CHIP_ID, 2, DONT_CARE, FALSE, "bcm43341b0_ag", ""},
++ {BCM4324_CHIP_ID, 5, DONT_CARE, FALSE, "bcm43241b4_ag", "nvram_ap62x2.txt"},
++ {BCM4335_CHIP_ID, 2, DONT_CARE, FALSE, "bcm4339a0_ag", "nvram_AP6335.txt"},
++ {BCM4339_CHIP_ID, 1, DONT_CARE, FALSE, "bcm4339a0_ag", "nvram_AP6335.txt"},
++ {BCM4345_CHIP_ID, 6, DONT_CARE, FALSE, "bcm43455c0_ag", "nvram_ap6255.txt"},
++ {BCM43454_CHIP_ID, 6, DONT_CARE, FALSE, "bcm43455c0_ag", ""},
++ {BCM4345_CHIP_ID, 9, DONT_CARE, FALSE, "bcm43456c5_ag", "nvram_ap6256.txt"},
++ {BCM43454_CHIP_ID, 9, DONT_CARE, FALSE, "bcm43456c5_ag", ""},
++ {BCM4354_CHIP_ID, 1, DONT_CARE, FALSE, "bcm4354a1_ag", "nvram_ap6354.txt"},
++ {BCM4354_CHIP_ID, 2, DONT_CARE, FALSE, "bcm4356a2_ag", "nvram_ap6356.txt"},
++ {BCM4356_CHIP_ID, 2, DONT_CARE, FALSE, "bcm4356a2_ag", "nvram_ap6356.txt"},
++ {BCM4371_CHIP_ID, 2, DONT_CARE, FALSE, "bcm4356a2_ag", ""},
++ {BCM43569_CHIP_ID, 3, DONT_CARE, FALSE, "bcm4358a3_ag", ""},
++ {BCM4359_CHIP_ID, 5, DONT_CARE, FALSE, "bcm4359b1_ag", ""},
++ {BCM4359_CHIP_ID, 9, DONT_CARE, FALSE, "bcm4359c0_ag", "nvram_ap6398s.txt"},
++ {BCM4362_CHIP_ID, 0, DONT_CARE, TRUE, "bcm43752a0_ag", ""},
++#endif
++#ifdef BCMPCIE
++ {BCM4354_CHIP_ID, 2, DONT_CARE, FALSE, "bcm4356a2_pcie_ag", ""},
++ {BCM4356_CHIP_ID, 2, DONT_CARE, FALSE, "bcm4356a2_pcie_ag", ""},
++ {BCM4359_CHIP_ID, 9, DONT_CARE, FALSE, "bcm4359c0_pcie_ag", ""},
++ {BCM4362_CHIP_ID, 0, DONT_CARE, TRUE, "bcm43752a0_pcie_ag", ""},
++#endif
++#ifdef BCMDBUS
++ {BCM43143_CHIP_ID, 2, DONT_CARE, FALSE, "bcm43143b0", ""},
++ {BCM43242_CHIP_ID, 1, DONT_CARE, FALSE, "bcm43242a1_ag", ""},
++ {BCM43569_CHIP_ID, 2, DONT_CARE, FALSE, "bcm4358u_ag", ""},
++#endif
++};
++
+ #ifdef BCMSDIO
+ void
+ dhd_conf_free_mac_list(wl_mac_list_ctrl_t *mac_list)
+@@ -158,6 +152,7 @@ dhd_conf_set_hw_oob_intr(bcmsdh_info_t *sdh, uint chip)
+ }
+ #endif
+
++#define SBSDIO_CIS_SIZE_LIMIT 0x200
+ #define F0_BLOCK_SIZE 32
+ int
+ dhd_conf_set_blksize(bcmsdh_info_t *sdh)
+@@ -168,7 +163,7 @@ dhd_conf_set_blksize(bcmsdh_info_t *sdh)
+ uint8 cisd;
+
+ numfn = bcmsdh_query_iofnum(sdh);
+-
++
+ for (fn = 0; fn <= numfn; fn++) {
+ if (!fn)
+ blksize = F0_BLOCK_SIZE;
+@@ -290,8 +285,8 @@ dhd_conf_set_fw_name_by_mac(dhd_pub_t *dhd, bcmsdh_info_t *sdh, char *fw_path)
+ uint32 oui, nic;
+ wl_mac_list_t *mac_list;
+ wl_mac_range_t *mac_range;
+- char *pfw_name;
+ int fw_type, fw_type_new;
++ char *name_ptr;
+
+ mac_list = dhd->conf->fw_by_mac.m_mac_list_head;
+ fw_num = dhd->conf->fw_by_mac.count;
+@@ -308,22 +303,42 @@ dhd_conf_set_fw_name_by_mac(dhd_pub_t *dhd, bcmsdh_info_t *sdh, char *fw_path)
+ /* find out the last '/' */
+ i = strlen(fw_path);
+ while (i > 0) {
+- if (fw_path[i] == '/') break;
++ if (fw_path[i] == '/') {
++ i++;
++ break;
++ }
+ i--;
+ }
+- pfw_name = &fw_path[i+1];
+- fw_type = (strstr(pfw_name, "_mfg") ?
+- FW_TYPE_MFG : (strstr(pfw_name, "_apsta") ?
+- FW_TYPE_APSTA : (strstr(pfw_name, "_p2p") ?
+- FW_TYPE_P2P : FW_TYPE_STA)));
++ name_ptr = &fw_path[i];
++
++ if (strstr(name_ptr, "_apsta"))
++ fw_type = FW_TYPE_APSTA;
++ else if (strstr(name_ptr, "_p2p"))
++ fw_type = FW_TYPE_P2P;
++ else if (strstr(name_ptr, "_mesh"))
++ fw_type = FW_TYPE_MESH;
++ else if (strstr(name_ptr, "_es"))
++ fw_type = FW_TYPE_ES;
++ else if (strstr(name_ptr, "_mfg"))
++ fw_type = FW_TYPE_MFG;
++ else
++ fw_type = FW_TYPE_STA;
+
+ for (i=0; i= mac_range[j].nic_start && nic <= mac_range[j].nic_end) {
+- strcpy(pfw_name, mac_list[i].name);
++ strcpy(name_ptr, mac_list[i].name);
+ printf("%s: matched oui=0x%06X, nic=0x%06X\n",
+ __FUNCTION__, oui, nic);
+ printf("%s: fw_path=%s\n", __FUNCTION__, fw_path);
+@@ -392,12 +407,27 @@ dhd_conf_set_nv_name_by_mac(dhd_pub_t *dhd, bcmsdh_info_t *sdh, char *nv_path)
+ #endif
+
+ void
+-dhd_conf_set_fw_name_by_chip(dhd_pub_t *dhd, char *fw_path, char *nv_path)
++dhd_conf_free_country_list(conf_country_list_t *country_list)
++{
++ int i;
++
++ CONFIG_TRACE(("%s called\n", __FUNCTION__));
++ for (i=0; icount; i++) {
++ if (country_list->cspec[i]) {
++ CONFIG_TRACE(("%s Free cspec %p\n", __FUNCTION__, country_list->cspec[i]));
++ kfree(country_list->cspec[i]);
++ }
++ }
++ country_list->count = 0;
++}
++
++void
++dhd_conf_set_fw_name_by_chip(dhd_pub_t *dhd, char *fw_path)
+ {
+ int fw_type, ag_type;
+ uint chip, chiprev;
+- int i, j;
+- char fw_tail[20];
++ int i;
++ char *name_ptr;
+
+ chip = dhd->conf->chip;
+ chiprev = dhd->conf->chiprev;
+@@ -419,143 +449,53 @@ dhd_conf_set_fw_name_by_chip(dhd_pub_t *dhd, char *fw_path, char *nv_path)
+ /* find out the last '/' */
+ i = strlen(fw_path);
+ while (i > 0) {
+- if (fw_path[i] == '/') break;
+- i--;
+- }
+- j = strlen(nv_path);
+- while (j > 0) {
+- if (nv_path[j] == '/')
++ if (fw_path[i] == '/') {
++ i++;
+ break;
+- j--;
++ }
++ i--;
+ }
++ name_ptr = &fw_path[i];
+ #ifdef BAND_AG
+ ag_type = FW_TYPE_AG;
+ #else
+- ag_type = strstr(&fw_path[i], "_ag") ? FW_TYPE_AG : FW_TYPE_G;
++ ag_type = strstr(name_ptr, "_ag") ? FW_TYPE_AG : FW_TYPE_G;
+ #endif
+- fw_type = (strstr(&fw_path[i], "_mfg") ? FW_TYPE_MFG :
+- (strstr(&fw_path[i], "_apsta") ? FW_TYPE_APSTA :
+- (strstr(&fw_path[i], "_p2p") ? FW_TYPE_P2P :
+- (strstr(&fw_path[i], "_es") ? FW_TYPE_ES :
+- FW_TYPE_STA))));
+-
+- if (fw_type == FW_TYPE_STA)
+- strcpy(fw_tail, ".bin");
+- else if (fw_type == FW_TYPE_APSTA)
+- strcpy(fw_tail, "_apsta.bin");
+- else if (fw_type == FW_TYPE_P2P)
+- strcpy(fw_tail, "_p2p.bin");
+- else if (fw_type == FW_TYPE_ES)
+- strcpy(fw_tail, "_es.bin");
+- else if (fw_type == FW_TYPE_MFG)
+- strcpy(fw_tail, "_mfg.bin");
+-
+- switch (chip) {
+-#ifdef BCMSDIO
+- case BCM4330_CHIP_ID:
+- if (ag_type == FW_TYPE_G) {
+- if (chiprev == BCM4330B2_CHIP_REV)
+- strcpy(&fw_path[i+1], FW_BCM4330B2);
+- } else {
+- if (chiprev == BCM4330B2_CHIP_REV)
+- strcpy(&fw_path[i+1], FW_BCM4330B2_AG);
+- strcpy(&nv_path[j + 1], "nvram_AP6330.txt");
+- }
+- break;
+- case BCM43362_CHIP_ID:
+- if (chiprev == BCM43362A0_CHIP_REV)
+- strcpy(&fw_path[i+1], FW_BCM43362A0);
++ if (strstr(name_ptr, "_apsta"))
++ fw_type = FW_TYPE_APSTA;
++ else if (strstr(name_ptr, "_p2p"))
++ fw_type = FW_TYPE_P2P;
++ else if (strstr(name_ptr, "_mesh"))
++ fw_type = FW_TYPE_MESH;
++ else if (strstr(name_ptr, "_es"))
++ fw_type = FW_TYPE_ES;
++ else if (strstr(name_ptr, "_mfg"))
++ fw_type = FW_TYPE_MFG;
++ else
++ fw_type = FW_TYPE_STA;
++
++ for (i = 0; i < sizeof(chip_name_map)/sizeof(chip_name_map[0]); i++) {
++ const cihp_name_map_t* row = &chip_name_map[i];
++ if (row->chip == chip && row->chiprev == chiprev &&
++ (row->ag_type == ag_type || row->ag_type == DONT_CARE)) {
++ strcpy(name_ptr, "fw_");
++ strcat(fw_path, row->chip_name);
++ if (fw_type == FW_TYPE_APSTA)
++ strcat(fw_path, "_apsta.bin");
++ else if (fw_type == FW_TYPE_P2P)
++ strcat(fw_path, "_p2p.bin");
++ else if (fw_type == FW_TYPE_MESH)
++ strcat(fw_path, "_mesh.bin");
++ else if (fw_type == FW_TYPE_ES)
++ strcat(fw_path, "_es.bin");
++ else if (fw_type == FW_TYPE_MFG)
++ strcat(fw_path, "_mfg.bin");
+ else
+- strcpy(&fw_path[i+1], FW_BCM43362A2);
+- if (!strstr(nv_path, "6476"))
+- strcpy(&nv_path[j + 1], "nvram_AP6210.txt");
+- break;
+- case BCM43430_CHIP_ID:
+- if (chiprev == BCM43430A0_CHIP_REV) {
+- strcpy(&fw_path[i+1], FW_BCM43438A0);
+- strcpy(&nv_path[j + 1], "nvram_ap6212.txt");
+- } else if (chiprev == BCM43430A1_CHIP_REV) {
+- strcpy(&fw_path[i+1], FW_BCM43438A1);
+- strcpy(&nv_path[j + 1], "nvram_ap6212a.txt");
+- } else if (chiprev == BCM43430A2_CHIP_REV) {
+- strcpy(&fw_path[i+1], FW_BCM43436B0);
+- strcpy(&nv_path[j + 1], "nvram_ap6236.txt");
+- }
+- break;
+- case BCM43012_CHIP_ID:
+- if (chiprev == BCM43012B0_CHIP_REV)
+- strcpy(&fw_path[i+1], FW_BCM43012B0);
+- break;
+- case BCM4334_CHIP_ID:
+- if (chiprev == BCM4334B1_CHIP_REV)
+- strcpy(&fw_path[i+1], FW_BCM4334B1);
+- break;
+- case BCM43340_CHIP_ID:
+- case BCM43341_CHIP_ID:
+- if (chiprev == BCM43341B0_CHIP_REV)
+- strcpy(&fw_path[i+1], FW_BCM43341B1);
+- break;
+- case BCM4324_CHIP_ID:
+- if (chiprev == BCM43241B4_CHIP_REV)
+- strcpy(&fw_path[i+1], FW_BCM43241B4);
+- strcpy(&nv_path[j + 1], "nvram_ap62x2.txt");
+- break;
+- case BCM4335_CHIP_ID:
+- if (chiprev == BCM4335A0_CHIP_REV)
+- strcpy(&fw_path[i+1], FW_BCM4339A0);
+- strcpy(&nv_path[j + 1], "nvram_AP6335.txt");
+- break;
+- case BCM4339_CHIP_ID:
+- if (chiprev == BCM4339A0_CHIP_REV)
+- strcpy(&fw_path[i+1], FW_BCM4339A0);
+- strcpy(&nv_path[j + 1], "nvram_AP6335.txt");
+- break;
+- case BCM4345_CHIP_ID:
+- case BCM43454_CHIP_ID:
+- if (chiprev == BCM43455C0_CHIP_REV) {
+- strcpy(&fw_path[i+1], FW_BCM43455C0);
+- strcpy(&nv_path[j + 1], "nvram_ap6255.txt");
+- } else if (chiprev == BCM43456C5_CHIP_REV) {
+- strcpy(&fw_path[i+1], FW_BCM43456C5);
+- }
+- break;
+- case BCM4354_CHIP_ID:
+- if (chiprev == BCM4354A1_CHIP_REV) {
+- strcpy(&fw_path[i+1], FW_BCM4354A1);
+- strcpy(&nv_path[j + 1], "nvram_ap6354.txt");
+- } else if (chiprev == BCM4356A2_CHIP_REV) {
+- strcpy(&fw_path[i+1], FW_BCM4356A2);
+- strcpy(&nv_path[j + 1], "nvram_ap6356.txt");
+- }
+- break;
+- case BCM4356_CHIP_ID:
+- case BCM4371_CHIP_ID:
+- if (chiprev == BCM4356A2_CHIP_REV)
+- strcpy(&fw_path[i+1], FW_BCM4356A2);
+- strcpy(&nv_path[j + 1], "nvram_ap6356.txt");
+- break;
+- case BCM43569_CHIP_ID:
+- if (chiprev == BCM4358A3_CHIP_REV)
+- strcpy(&fw_path[i+1], FW_BCM4358A3);
+- break;
+- case BCM4359_CHIP_ID:
+- if (chiprev == BCM4359B1_CHIP_REV)
+- strcpy(&fw_path[i+1], FW_BCM4359B1);
+- else if (chiprev == BCM4359C0_CHIP_REV)
+- strcpy(&fw_path[i+1], FW_BCM4359C0);
+- break;
+-#endif
+-#ifdef BCMPCIE
+- case BCM4354_CHIP_ID:
+- case BCM4356_CHIP_ID:
+- if (chiprev == BCM4356A2_CHIP_REV)
+- strcpy(&fw_path[i+1], FW_BCM4356A2);
+- break;
+-#endif
+- default:
+- strcpy(&fw_path[i+1], "fw_bcmdhd");
++ strcat(fw_path, ".bin");
++ }
+ }
+- strcat(fw_path, fw_tail);
++
++ dhd->conf->fw_type = fw_type;
+
+ CONFIG_TRACE(("%s: firmware_path=%s\n", __FUNCTION__, fw_path));
+ }
+@@ -565,7 +505,7 @@ dhd_conf_set_clm_name_by_chip(dhd_pub_t *dhd, char *clm_path)
+ {
+ uint chip, chiprev;
+ int i;
+- char fw_tail[20];
++ char *name_ptr;
+
+ chip = dhd->conf->chip;
+ chiprev = dhd->conf->chiprev;
+@@ -578,23 +518,22 @@ dhd_conf_set_clm_name_by_chip(dhd_pub_t *dhd, char *clm_path)
+ /* find out the last '/' */
+ i = strlen(clm_path);
+ while (i > 0) {
+- if (clm_path[i] == '/') break;
++ if (clm_path[i] == '/') {
++ i++;
++ break;
++ }
+ i--;
+ }
++ name_ptr = &clm_path[i];
+
+- strcpy(fw_tail, ".blob");
+-
+- switch (chip) {
+-#ifdef BCMSDIO
+- case BCM43012_CHIP_ID:
+- if (chiprev == BCM43012B0_CHIP_REV)
+- strcpy(&clm_path[i+1], CLM_BCM43012B0);
+- break;
+-#endif
+- default:
+- strcpy(&clm_path[i+1], "clm_bcmdhd");
++ for (i = 0; i < sizeof(chip_name_map)/sizeof(chip_name_map[0]); i++) {
++ const cihp_name_map_t* row = &chip_name_map[i];
++ if (row->chip == chip && row->chiprev == chiprev && row->clm) {
++ strcpy(name_ptr, "clm_");
++ strcat(clm_path, row->chip_name);
++ strcat(clm_path, ".blob");
++ }
+ }
+- strcat(clm_path, fw_tail);
+
+ CONFIG_TRACE(("%s: clm_path=%s\n", __FUNCTION__, clm_path));
+ }
+@@ -602,23 +541,13 @@ dhd_conf_set_clm_name_by_chip(dhd_pub_t *dhd, char *clm_path)
+ void
+ dhd_conf_set_nv_name_by_chip(dhd_pub_t *dhd, char *nv_path)
+ {
+- int matched=-1;
+ uint chip, chiprev;
+ int i;
++ char *name_ptr;
+
+ chip = dhd->conf->chip;
+ chiprev = dhd->conf->chiprev;
+
+- for (i=0; iconf->nv_by_chip.count; i++) {
+- if (chip==dhd->conf->nv_by_chip.m_chip_nv_path_head[i].chip &&
+- chiprev==dhd->conf->nv_by_chip.m_chip_nv_path_head[i].chiprev) {
+- matched = i;
+- break;
+- }
+- }
+- if (matched < 0)
+- return;
+-
+ if (nv_path[0] == '\0') {
+ #ifdef CONFIG_BCMDHD_NVRAM_PATH
+ bcm_strncpy_s(nv_path, MOD_PARAM_PATHLEN-1, CONFIG_BCMDHD_NVRAM_PATH, MOD_PARAM_PATHLEN-1);
+@@ -633,11 +562,28 @@ dhd_conf_set_nv_name_by_chip(dhd_pub_t *dhd, char *nv_path)
+ /* find out the last '/' */
+ i = strlen(nv_path);
+ while (i > 0) {
+- if (nv_path[i] == '/') break;
++ if (nv_path[i] == '/') {
++ i++;
++ break;
++ }
+ i--;
+ }
++ name_ptr = &nv_path[i];
++
++ for (i = 0; i < sizeof(chip_name_map)/sizeof(chip_name_map[0]); i++) {
++ const cihp_name_map_t* row = &chip_name_map[i];
++ if (row->chip == chip && row->chiprev == chiprev && strlen(row->module_name)) {
++ strcpy(name_ptr, row->module_name);
++ }
++ }
+
+- strcpy(&nv_path[i+1], dhd->conf->nv_by_chip.m_chip_nv_path_head[matched].name);
++ for (i=0; iconf->nv_by_chip.count; i++) {
++ if (chip==dhd->conf->nv_by_chip.m_chip_nv_path_head[i].chip &&
++ chiprev==dhd->conf->nv_by_chip.m_chip_nv_path_head[i].chiprev) {
++ strcpy(name_ptr, dhd->conf->nv_by_chip.m_chip_nv_path_head[i].name);
++ break;
++ }
++ }
+
+ CONFIG_TRACE(("%s: nvram_path=%s\n", __FUNCTION__, nv_path));
+ }
+@@ -656,10 +602,13 @@ dhd_conf_set_path(dhd_pub_t *dhd, char *dst_name, char *dst_path, char *src_path
+ /* find out the last '/' */
+ i = strlen(dst_path);
+ while (i > 0) {
+- if (dst_path[i] == '/') break;
++ if (dst_path[i] == '/') {
++ i++;
++ break;
++ }
+ i--;
+ }
+- strcpy(&dst_path[i+1], dst_name);
++ strcpy(&dst_path[i], dst_name);
+
+ CONFIG_TRACE(("%s: dst_path=%s\n", __FUNCTION__, dst_path));
+ }
+@@ -670,6 +619,7 @@ dhd_conf_set_conf_name_by_chip(dhd_pub_t *dhd, char *conf_path)
+ {
+ uint chip, chiprev;
+ int i;
++ char *name_ptr;
+
+ chip = dhd->conf->chip;
+ chiprev = dhd->conf->chiprev;
+@@ -682,90 +632,21 @@ dhd_conf_set_conf_name_by_chip(dhd_pub_t *dhd, char *conf_path)
+ /* find out the last '/' */
+ i = strlen(conf_path);
+ while (i > 0) {
+- if (conf_path[i] == '/') break;
++ if (conf_path[i] == '/') {
++ i++;
++ break;
++ }
+ i--;
+ }
++ name_ptr = conf_path[i];
+
+- switch (chip) {
+-#ifdef BCMSDIO
+- case BCM4330_CHIP_ID:
+- if (chiprev == BCM4330B2_CHIP_REV)
+- strcpy(&conf_path[i+1], CONFIG_BCM4330B2);
+- break;
+- case BCM43362_CHIP_ID:
+- if (chiprev == BCM43362A0_CHIP_REV)
+- strcpy(&conf_path[i+1], CONFIG_BCM43362A0);
+- else
+- strcpy(&conf_path[i+1], CONFIG_BCM43362A2);
+- break;
+- case BCM43430_CHIP_ID:
+- if (chiprev == BCM43430A0_CHIP_REV)
+- strcpy(&conf_path[i+1], CONFIG_BCM43438A0);
+- else if (chiprev == BCM43430A1_CHIP_REV)
+- strcpy(&conf_path[i+1], CONFIG_BCM43438A1);
+- else if (chiprev == BCM43430A2_CHIP_REV)
+- strcpy(&conf_path[i+1], CONFIG_BCM43436B0);
+- break;
+- case BCM4334_CHIP_ID:
+- if (chiprev == BCM4334B1_CHIP_REV)
+- strcpy(&conf_path[i+1], CONFIG_BCM4334B1);
+- break;
+- case BCM43340_CHIP_ID:
+- case BCM43341_CHIP_ID:
+- if (chiprev == BCM43341B0_CHIP_REV)
+- strcpy(&conf_path[i+1], CONFIG_BCM43341B0);
+- break;
+- case BCM4324_CHIP_ID:
+- if (chiprev == BCM43241B4_CHIP_REV)
+- strcpy(&conf_path[i+1], CONFIG_BCM43241B4);
+- break;
+- case BCM4335_CHIP_ID:
+- if (chiprev == BCM4335A0_CHIP_REV)
+- strcpy(&conf_path[i+1], CONFIG_BCM4339A0);
+- break;
+- case BCM43454_CHIP_ID:
+- if (chiprev == BCM43455C0_CHIP_REV)
+- strcpy(&conf_path[i+1], CONFIG_BCM43454C0);
+- break;
+- case BCM4345_CHIP_ID:
+- if (chiprev == BCM43455C0_CHIP_REV)
+- strcpy(&conf_path[i+1], CONFIG_BCM43455C0);
+- else if (chiprev == BCM43456C5_CHIP_REV)
+- strcpy(&conf_path[i+1], CONFIG_BCM43456C5);
+- break;
+- case BCM4339_CHIP_ID:
+- if (chiprev == BCM4339A0_CHIP_REV)
+- strcpy(&conf_path[i+1], CONFIG_BCM4339A0);
+- break;
+- case BCM4354_CHIP_ID:
+- if (chiprev == BCM4354A1_CHIP_REV)
+- strcpy(&conf_path[i+1], CONFIG_BCM4354A1);
+- else if (chiprev == BCM4356A2_CHIP_REV)
+- strcpy(&conf_path[i+1], CONFIG_BCM4356A2);
+- break;
+- case BCM4356_CHIP_ID:
+- case BCM4371_CHIP_ID:
+- if (chiprev == BCM4356A2_CHIP_REV)
+- strcpy(&conf_path[i+1], CONFIG_BCM4356A2);
+- break;
+- case BCM43569_CHIP_ID:
+- if (chiprev == BCM4358A3_CHIP_REV)
+- strcpy(&conf_path[i+1], CONFIG_BCM4358A3);
+- break;
+- case BCM4359_CHIP_ID:
+- if (chiprev == BCM4359B1_CHIP_REV)
+- strcpy(&conf_path[i+1], CONFIG_BCM4359B1);
+- else if (chiprev == BCM4359C0_CHIP_REV)
+- strcpy(&conf_path[i+1], CONFIG_BCM4359C0);
+- break;
+-#endif
+-#ifdef BCMPCIE
+- case BCM4354_CHIP_ID:
+- case BCM4356_CHIP_ID:
+- if (chiprev == BCM4356A2_CHIP_REV)
+- strcpy(&conf_path[i+1], CONFIG_BCM4356A2);
+- break;
+-#endif
++ for (i = 0; i < sizeof(chip_name_map)/sizeof(chip_name_map[0]); i++) {
++ const cihp_name_map_t* row = &chip_name_map[i];
++ if (row->chip == chip && row->chiprev == chiprev) {
++ strcpy(name_ptr, "config_");
++ strcat(conf_path, row->chip_name);
++ strcat(conf_path, ".txt");
++ }
+ }
+
+ CONFIG_TRACE(("%s: config_path=%s\n", __FUNCTION__, conf_path));
+@@ -785,12 +666,12 @@ dhd_conf_set_intiovar(dhd_pub_t *dhd, uint cmd, char *name, int val,
+ CONFIG_ERROR(("%s: WLC_DOWN setting failed %d\n", __FUNCTION__, ret));
+ }
+ if (cmd == WLC_SET_VAR) {
+- printf("%s: set %s %d\n", __FUNCTION__, name, val);
++ CONFIG_TRACE(("%s: set %s %d\n", __FUNCTION__, name, val));
+ bcm_mkiovar(name, (char *)&val, sizeof(val), iovbuf, sizeof(iovbuf));
+ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0)
+ CONFIG_ERROR(("%s: %s setting failed %d\n", __FUNCTION__, name, ret));
+ } else {
+- printf("%s: set %s %d %d\n", __FUNCTION__, name, cmd, val);
++ CONFIG_TRACE(("%s: set %s %d %d\n", __FUNCTION__, name, cmd, val));
+ if ((ret = dhd_wl_ioctl_cmd(dhd, cmd, &val, sizeof(val), TRUE, 0)) < 0)
+ CONFIG_ERROR(("%s: %s setting failed %d\n", __FUNCTION__, name, ret));
+ }
+@@ -852,7 +733,7 @@ dhd_conf_get_iovar(dhd_pub_t *dhd, int cmd, char *name, char *buf, int len, int
+ uint
+ dhd_conf_get_band(dhd_pub_t *dhd)
+ {
+- uint band = WLC_BAND_AUTO;
++ int band = -1;
+
+ if (dhd && dhd->conf)
+ band = dhd->conf->band;
+@@ -862,19 +743,6 @@ dhd_conf_get_band(dhd_pub_t *dhd)
+ return band;
+ }
+
+-int
+-dhd_conf_set_country(dhd_pub_t *dhd)
+-{
+- int bcmerror = -1;
+-
+- memset(&dhd->dhd_cspec, 0, sizeof(wl_country_t));
+- printf("%s: set country %s, revision %d\n", __FUNCTION__,
+- dhd->conf->cspec.ccode, dhd->conf->cspec.rev);
+- dhd_conf_set_bufiovar(dhd, WLC_SET_VAR, "country", (char *)&dhd->conf->cspec, sizeof(wl_country_t), FALSE);
+-
+- return bcmerror;
+-}
+-
+ int
+ dhd_conf_get_country(dhd_pub_t *dhd, wl_country_t *cspec)
+ {
+@@ -884,23 +752,28 @@ dhd_conf_get_country(dhd_pub_t *dhd, wl_country_t *cspec)
+ bcm_mkiovar("country", NULL, 0, (char*)cspec, sizeof(wl_country_t));
+ if ((bcmerror = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, cspec, sizeof(wl_country_t), FALSE, 0)) < 0)
+ CONFIG_ERROR(("%s: country code getting failed %d\n", __FUNCTION__, bcmerror));
+- else
+- printf("Country code: %s (%s/%d)\n", cspec->country_abbrev, cspec->ccode, cspec->rev);
+
+ return bcmerror;
+ }
+
+ int
+-dhd_conf_get_country_from_config(dhd_pub_t *dhd, wl_country_t *cspec)
++dhd_conf_map_country_list(dhd_pub_t *dhd, wl_country_t *cspec, int nodfs)
+ {
+ int bcmerror = -1, i;
+ struct dhd_conf *conf = dhd->conf;
++ conf_country_list_t *country_list;
+
+- for (i = 0; i < conf->country_list.count; i++) {
+- if (!strncmp(cspec->country_abbrev, conf->country_list.cspec[i].country_abbrev, 2)) {
+- memcpy(cspec->ccode,
+- conf->country_list.cspec[i].ccode, WLC_CNTRY_BUF_SZ);
+- cspec->rev = conf->country_list.cspec[i].rev;
++ if ((nodfs > 0 || dhd->op_mode & DHD_FLAG_HOSTAP_MODE) &&
++ conf->country_list_nodfs.count > 0) {
++ country_list = &conf->country_list_nodfs;
++ } else {
++ country_list = &conf->country_list;
++ }
++
++ for (i = 0; i < country_list->count; i++) {
++ if (!strncmp(cspec->country_abbrev, country_list->cspec[i]->country_abbrev, 2)) {
++ memcpy(cspec->ccode, country_list->cspec[i]->ccode, WLC_CNTRY_BUF_SZ);
++ cspec->rev = country_list->cspec[i]->rev;
+ printf("%s: %s/%d\n", __FUNCTION__, cspec->ccode, cspec->rev);
+ return 0;
+ }
+@@ -909,6 +782,21 @@ dhd_conf_get_country_from_config(dhd_pub_t *dhd, wl_country_t *cspec)
+ return bcmerror;
+ }
+
++int
++dhd_conf_set_country(dhd_pub_t *dhd, wl_country_t *cspec)
++{
++ int bcmerror = -1;
++
++ memset(&dhd->dhd_cspec, 0, sizeof(wl_country_t));
++
++ printf("%s: set country %s, revision %d\n", __FUNCTION__, cspec->ccode, cspec->rev);
++ dhd_conf_set_bufiovar(dhd, WLC_SET_VAR, "country", (char *)cspec, sizeof(wl_country_t), FALSE);
++ dhd_conf_get_country(dhd, cspec);
++ printf("Country code: %s (%s/%d)\n", cspec->country_abbrev, cspec->ccode, cspec->rev);
++
++ return bcmerror;
++}
++
+ int
+ dhd_conf_fix_country(dhd_pub_t *dhd)
+ {
+@@ -916,6 +804,7 @@ dhd_conf_fix_country(dhd_pub_t *dhd)
+ uint band;
+ wl_uint32_list_t *list;
+ u8 valid_chan_list[sizeof(u32)*(WL_NUMCHANNELS + 1)];
++ wl_country_t cspec;
+
+ if (!(dhd && dhd->conf)) {
+ return bcmerror;
+@@ -934,11 +823,13 @@ dhd_conf_fix_country(dhd_pub_t *dhd)
+ dtoh32(list->count)<11)) {
+ CONFIG_ERROR(("%s: bcmerror=%d, # of channels %d\n",
+ __FUNCTION__, bcmerror, dtoh32(list->count)));
+- if ((bcmerror = dhd_conf_set_country(dhd)) < 0) {
+- strcpy(dhd->conf->cspec.country_abbrev, "US");
+- dhd->conf->cspec.rev = 0;
+- strcpy(dhd->conf->cspec.ccode, "US");
+- dhd_conf_set_country(dhd);
++ dhd_conf_map_country_list(dhd, &dhd->conf->cspec, 0);
++ if ((bcmerror = dhd_conf_set_country(dhd, &dhd->conf->cspec)) < 0) {
++ strcpy(cspec.country_abbrev, "US");
++ cspec.rev = 0;
++ strcpy(cspec.ccode, "US");
++ dhd_conf_map_country_list(dhd, &cspec, 0);
++ dhd_conf_set_country(dhd, &cspec);
+ }
+ }
+
+@@ -1002,17 +893,19 @@ dhd_conf_set_bw_cap(dhd_pub_t *dhd)
+ u32 bw_cap;
+ } param = {0, 0};
+
+- if (dhd->conf->bw_cap_2g >= 0) {
++ if (dhd->conf->bw_cap[0] >= 0) {
++ memset(¶m, 0, sizeof(param));
+ param.band = WLC_BAND_2G;
+- param.bw_cap = (uint)dhd->conf->bw_cap_2g;
+- printf("%s: set bw_cap 2g %d\n", __FUNCTION__, param.bw_cap);
++ param.bw_cap = (uint)dhd->conf->bw_cap[0];
++ printf("%s: set bw_cap 2g 0x%x\n", __FUNCTION__, param.bw_cap);
+ dhd_conf_set_bufiovar(dhd, WLC_SET_VAR, "bw_cap", (char *)¶m, sizeof(param), TRUE);
+ }
+
+- if (dhd->conf->bw_cap_5g >= 0) {
++ if (dhd->conf->bw_cap[1] >= 0) {
++ memset(¶m, 0, sizeof(param));
+ param.band = WLC_BAND_5G;
+- param.bw_cap = (uint)dhd->conf->bw_cap_5g;
+- printf("%s: set bw_cap 5g %d\n", __FUNCTION__, param.bw_cap);
++ param.bw_cap = (uint)dhd->conf->bw_cap[1];
++ printf("%s: set bw_cap 5g 0x%x\n", __FUNCTION__, param.bw_cap);
+ dhd_conf_set_bufiovar(dhd, WLC_SET_VAR, "bw_cap", (char *)¶m, sizeof(param), TRUE);
+ }
+ }
+@@ -1155,6 +1048,26 @@ dhd_conf_set_wme(dhd_pub_t *dhd, int mode)
+ return;
+ }
+
++void
++dhd_conf_set_mchan_bw(dhd_pub_t *dhd, int p2p_mode, int miracast_mode)
++{
++ int i;
++ struct dhd_conf *conf = dhd->conf;
++ bool set = true;
++
++ for (i=0; imchan[i].bw >= 0);
++ set &= ((conf->mchan[i].p2p_mode == -1) | (conf->mchan[i].p2p_mode == p2p_mode));
++ set &= ((conf->mchan[i].miracast_mode == -1) | (conf->mchan[i].miracast_mode == miracast_mode));
++ if (set) {
++ dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "mchan_bw", conf->mchan[i].bw, 0, FALSE);
++ }
++ }
++
++ return;
++}
++
+ #ifdef PKT_FILTER_SUPPORT
+ void
+ dhd_conf_add_pkt_filter(dhd_pub_t *dhd)
+@@ -1164,16 +1077,11 @@ dhd_conf_add_pkt_filter(dhd_pub_t *dhd)
+ #define MACS "%02x%02x%02x%02x%02x%02x"
+
+ /*
+- * 1. Filter out all pkt: actually not to enable this since 4-way handshake will be filter out as well.
+- * 1) dhd_master_mode=0
+- * 2) pkt_filter_add=99 0 0 0 0x000000000000 0x000000000000
+- * 2. Filter in less pkt: ARP(0x0806, ID is 105), BRCM(0x886C), 802.1X(0x888E)
++ * Filter in less pkt: ARP(0x0806, ID is 105), BRCM(0x886C), 802.1X(0x888E)
+ * 1) dhd_master_mode=1
+ * 2) pkt_filter_del=100, 102, 103, 104, 105
+ * 3) pkt_filter_add=131 0 0 12 0xFFFF 0x886C, 132 0 0 12 0xFFFF 0x888E
+- * 3. magic pkt: magic_pkt_filter_add=141 0 1 12
+- * 4. Filter out netbios pkt:
+- * Netbios: 121 0 0 12 0xFFFF000000000000000000FF000000000000000000000000FFFF 0x0800000000000000000000110000000000000000000000000089
++ * 4) magic_pkt_filter_add=141 0 1 12
+ */
+ for(i=0; iconf->pkt_filter_add.count; i++) {
+ dhd->pktfilter[i+dhd->pktfilter_count] = dhd->conf->pkt_filter_add.filter[i];
+@@ -1244,8 +1152,9 @@ dhd_conf_discard_pkt_filter(dhd_pub_t *dhd)
+ int
+ dhd_conf_get_pm(dhd_pub_t *dhd)
+ {
+- if (dhd && dhd->conf)
++ if (dhd && dhd->conf) {
+ return dhd->conf->pm;
++ }
+ return -1;
+ }
+
+@@ -1408,8 +1317,6 @@ pick_config_vars(char *varbuf, uint len, uint start_pos, char *pickbuf)
+ if (pick) {
+ if (varbuf[n] == 0x9)
+ continue;
+- if (pick_column>0 && pickbuf[pick_column-1]==' ' && varbuf[n]==' ')
+- continue;
+ pickbuf[pick_column] = varbuf[n];
+ pick_column++;
+ }
+@@ -1432,6 +1339,12 @@ dhd_conf_read_log_level(dhd_pub_t *dhd, char *full_param, uint len_param)
+ sd_msglevel = (int)simple_strtol(data, NULL, 0);
+ printf("%s: sd_msglevel = 0x%X\n", __FUNCTION__, sd_msglevel);
+ }
++#endif
++#ifdef BCMDBUS
++ else if (!strncmp("dbus_msglevel=", full_param, len_param)) {
++ dbus_msglevel = (int)simple_strtol(data, NULL, 0);
++ printf("%s: dbus_msglevel = 0x%X\n", __FUNCTION__, dbus_msglevel);
++ }
+ #endif
+ else if (!strncmp("android_msg_level=", full_param, len_param)) {
+ android_msg_level = (int)simple_strtol(data, NULL, 0);
+@@ -1751,9 +1664,11 @@ bool
+ dhd_conf_read_country_list(dhd_pub_t *dhd, char *full_param, uint len_param)
+ {
+ int i;
+- char *pch, *pick_tmp;
++ char *pch, *pick_tmp, *pick_tmp2;
+ struct dhd_conf *conf = dhd->conf;
+ char *data = full_param+len_param;
++ wl_country_t *cspec;
++ conf_country_list_t *country_list = NULL;
+
+ /* Process country_list:
+ * country_list=[country1]:[ccode1]/[regrev1],
+@@ -1761,28 +1676,115 @@ dhd_conf_read_country_list(dhd_pub_t *dhd, char *full_param, uint len_param)
+ * Ex: country_list=US:US/0, TW:TW/1
+ */
+ if (!strncmp("country_list=", full_param, len_param)) {
++ country_list = &dhd->conf->country_list;
++ } else if (!strncmp("country_list_nodfs=", full_param, len_param)) {
++ country_list = &dhd->conf->country_list_nodfs;
++ }
++ if (country_list) {
+ pick_tmp = data;
+ for (i=0; icountry_list.cspec[i].country_abbrev, pch);
+- pch = bcmstrtok(&pick_tmp, "/", 0);
++ pch = bcmstrtok(&pick_tmp2, ":", 0);
+ if (!pch)
+ break;
+- memcpy(conf->country_list.cspec[i].ccode, pch, 2);
+- pch = bcmstrtok(&pick_tmp, ", ", 0);
+- if (!pch)
++ cspec = NULL;
++ if (!(cspec = kmalloc(sizeof(wl_country_t), GFP_KERNEL))) {
++ CONFIG_ERROR(("%s: kmalloc failed\n", __FUNCTION__));
+ break;
+- conf->country_list.cspec[i].rev = (int32)simple_strtol(pch, NULL, 10);
+- conf->country_list.count ++;
++ }
++ memset(cspec, 0, sizeof(wl_country_t));
++
++ strcpy(cspec->country_abbrev, pch);
++ pch = bcmstrtok(&pick_tmp2, "/", 0);
++ if (!pch) {
++ kfree(cspec);
++ break;
++ }
++ memcpy(cspec->ccode, pch, 2);
++ pch = bcmstrtok(&pick_tmp2, "/", 0);
++ if (!pch) {
++ kfree(cspec);
++ break;
++ }
++ cspec->rev = (int32)simple_strtol(pch, NULL, 10);
++ country_list->count++;
++ country_list->cspec[i] = cspec;
+ CONFIG_TRACE(("%s: country_list abbrev=%s, ccode=%s, regrev=%d\n", __FUNCTION__,
+- conf->country_list.cspec[i].country_abbrev,
+- conf->country_list.cspec[i].ccode,
+- conf->country_list.cspec[i].rev));
++ cspec->country_abbrev, cspec->ccode, cspec->rev));
++ }
++ if (!strncmp("country_list=", full_param, len_param)) {
++ printf("%s: %d country in list\n", __FUNCTION__, conf->country_list.count);
++ } else if (!strncmp("country_list_nodfs=", full_param, len_param)) {
++ printf("%s: %d nodfs country in list\n", __FUNCTION__, conf->country_list.count);
++ }
++ }
++ else
++ return false;
++
++ return true;
++}
++
++bool
++dhd_conf_read_mchan_params(dhd_pub_t *dhd, char *full_param, uint len_param)
++{
++ int i;
++ char *pch, *pick_tmp, *pick_tmp2;
++ struct dhd_conf *conf = dhd->conf;
++ char *data = full_param+len_param;
++
++ /* Process mchan_bw:
++ * mchan_bw=[val]/[any/go/gc]/[any/source/sink]
++ * Ex: mchan_bw=80/go/source, 30/gc/sink
++ */
++ if (!strncmp("mchan_bw=", full_param, len_param)) {
++ pick_tmp = data;
++ for (i=0; imchan[i].bw = (int)simple_strtol(pch, NULL, 0);
++ if (conf->mchan[i].bw < 0 || conf->mchan[i].bw > 100) {
++ CONFIG_ERROR(("%s: wrong bw %d\n", __FUNCTION__, conf->mchan[i].bw));
++ conf->mchan[i].bw = 0;
++ break;
++ }
++ }
++ pch = bcmstrtok(&pick_tmp2, "/", 0);
++ if (!pch) {
++ break;
++ } else {
++ if (bcmstrstr(pch, "any")) {
++ conf->mchan[i].p2p_mode = -1;
++ } else if (bcmstrstr(pch, "go")) {
++ conf->mchan[i].p2p_mode = WL_P2P_IF_GO;
++ } else if (bcmstrstr(pch, "gc")) {
++ conf->mchan[i].p2p_mode = WL_P2P_IF_CLIENT;
++ }
++ }
++ pch = bcmstrtok(&pick_tmp2, "/", 0);
++ if (!pch) {
++ break;
++ } else {
++ if (bcmstrstr(pch, "any")) {
++ conf->mchan[i].miracast_mode = -1;
++ } else if (bcmstrstr(pch, "source")) {
++ conf->mchan[i].miracast_mode = MIRACAST_SOURCE;
++ } else if (bcmstrstr(pch, "sink")) {
++ conf->mchan[i].miracast_mode = MIRACAST_SINK;
++ }
++ }
++ }
++ for (i=0; imchan[i].bw >= 0)
++ printf("%s: mchan_bw=%d/%d/%d\n", __FUNCTION__,
++ conf->mchan[i].bw, conf->mchan[i].p2p_mode, conf->mchan[i].miracast_mode);
+ }
+- printf("%s: %d country in list\n", __FUNCTION__, conf->country_list.count);
+ }
+ else
+ return false;
+@@ -1842,6 +1844,7 @@ dhd_conf_read_pkt_filter(dhd_pub_t *dhd, char *full_param, uint len_param)
+ if (!(conf->magic_pkt_filter_add = kmalloc(MAGIC_PKT_FILTER_LEN, GFP_KERNEL))) {
+ CONFIG_ERROR(("%s: kmalloc failed\n", __FUNCTION__));
+ } else {
++ memset(conf->magic_pkt_filter_add, 0, MAGIC_PKT_FILTER_LEN);
+ strcpy(conf->magic_pkt_filter_add, data);
+ printf("%s: magic_pkt_filter_add = %s\n", __FUNCTION__, conf->magic_pkt_filter_add);
+ }
+@@ -1853,33 +1856,33 @@ dhd_conf_read_pkt_filter(dhd_pub_t *dhd, char *full_param, uint len_param)
+ }
+ #endif
+
+-#ifdef IAPSTA_PREINIT
++#ifdef ISAM_PREINIT
+ /*
+- * iapsta_init=mode [sta|ap|apsta|dualap] vifname [wlan1]
+- * iapsta_config=ifname [wlan0|wlan1] ssid [xxx] chan [x]
++ * isam_init=mode [sta|ap|apsta|dualap] vifname [wlan1]
++ * isam_config=ifname [wlan0|wlan1] ssid [xxx] chan [x]
+ hidden [y|n] maxassoc [x]
+ amode [open|shared|wpapsk|wpa2psk|wpawpa2psk]
+ emode [none|wep|tkip|aes|tkipaes]
+ key [xxxxx]
+- * iapsta_enable=ifname [wlan0|wlan1]
++ * isam_enable=ifname [wlan0|wlan1]
+ */
+ bool
+-dhd_conf_read_iapsta(dhd_pub_t *dhd, char *full_param, uint len_param)
++dhd_conf_read_isam(dhd_pub_t *dhd, char *full_param, uint len_param)
+ {
+ struct dhd_conf *conf = dhd->conf;
+ char *data = full_param+len_param;
+
+- if (!strncmp("iapsta_init=", full_param, len_param)) {
+- sprintf(conf->iapsta_init, "iapsta_init %s", data);
+- printf("%s: iapsta_init=%s\n", __FUNCTION__, conf->iapsta_init);
++ if (!strncmp("isam_init=", full_param, len_param)) {
++ sprintf(conf->isam_init, "isam_init %s", data);
++ printf("%s: isam_init=%s\n", __FUNCTION__, conf->isam_init);
+ }
+- else if (!strncmp("iapsta_config=", full_param, len_param)) {
+- sprintf(conf->iapsta_config, "iapsta_config %s", data);
+- printf("%s: iapsta_config=%s\n", __FUNCTION__, conf->iapsta_config);
++ else if (!strncmp("isam_config=", full_param, len_param)) {
++ sprintf(conf->isam_config, "isam_config %s", data);
++ printf("%s: isam_config=%s\n", __FUNCTION__, conf->isam_config);
+ }
+- else if (!strncmp("iapsta_enable=", full_param, len_param)) {
+- sprintf(conf->iapsta_enable, "iapsta_enable %s", data);
+- printf("%s: iapsta_enable=%s\n", __FUNCTION__, conf->iapsta_enable);
++ else if (!strncmp("isam_enable=", full_param, len_param)) {
++ sprintf(conf->isam_enable, "isam_enable %s", data);
++ printf("%s: isam_enable=%s\n", __FUNCTION__, conf->isam_enable);
+ }
+ else
+ return false;
+@@ -1949,24 +1952,14 @@ dhd_conf_read_sdio_params(dhd_pub_t *dhd, char *full_param, uint len_param)
+ dhd_doflow = TRUE;
+ printf("%s: dhd_doflow = %d\n", __FUNCTION__, dhd_doflow);
+ }
+- else if (!strncmp("dhd_slpauto=", full_param, len_param)) {
+- if (!strncmp(data, "0", 1))
+- dhd_slpauto = FALSE;
+- else
+- dhd_slpauto = TRUE;
+- printf("%s: dhd_slpauto = %d\n", __FUNCTION__, dhd_slpauto);
+- }
+- else if (!strncmp("kso_enable=", full_param, len_param)) {
++ else if (!strncmp("dhd_slpauto=", full_param, len_param) ||
++ !strncmp("kso_enable=", full_param, len_param)) {
+ if (!strncmp(data, "0", 1))
+ dhd_slpauto = FALSE;
+ else
+ dhd_slpauto = TRUE;
+ printf("%s: dhd_slpauto = %d\n", __FUNCTION__, dhd_slpauto);
+ }
+- else if (!strncmp("bus:txglom=", full_param, len_param)) {
+- conf->bus_txglom = (int)simple_strtol(data, NULL, 10);
+- printf("%s: bus:txglom = %d\n", __FUNCTION__, conf->bus_txglom);
+- }
+ else if (!strncmp("use_rxchain=", full_param, len_param)) {
+ conf->use_rxchain = (int)simple_strtol(data, NULL, 10);
+ printf("%s: use_rxchain = %d\n", __FUNCTION__, conf->use_rxchain);
+@@ -1998,6 +1991,10 @@ dhd_conf_read_sdio_params(dhd_pub_t *dhd, char *full_param, uint len_param)
+ conf->rxf_cpucore = (int)simple_strtol(data, NULL, 10);
+ printf("%s: rxf_cpucore = %d\n", __FUNCTION__, conf->rxf_cpucore);
+ }
++ else if (!strncmp("orphan_move=", full_param, len_param)) {
++ conf->orphan_move = (int)simple_strtol(data, NULL, 10);
++ printf("%s: orphan_move = %d\n", __FUNCTION__, conf->orphan_move);
++ }
+ #if defined(BCMSDIOH_TXGLOM)
+ else if (!strncmp("txglomsize=", full_param, len_param)) {
+ conf->txglomsize = (uint)simple_strtol(data, NULL, 10);
+@@ -2027,13 +2024,6 @@ dhd_conf_read_sdio_params(dhd_pub_t *dhd, char *full_param, uint len_param)
+ conf->bus_rxglom = TRUE;
+ printf("%s: bus:rxglom = %d\n", __FUNCTION__, conf->bus_rxglom);
+ }
+- else if (!strncmp("dhd_poll=", full_param, len_param)) {
+- if (!strncmp(data, "0", 1))
+- conf->dhd_poll = 0;
+- else
+- conf->dhd_poll = 1;
+- printf("%s: dhd_poll = %d\n", __FUNCTION__, conf->dhd_poll);
+- }
+ else if (!strncmp("deferred_tx_len=", full_param, len_param)) {
+ conf->deferred_tx_len = (int)simple_strtol(data, NULL, 10);
+ printf("%s: deferred_tx_len = %d\n", __FUNCTION__, conf->deferred_tx_len);
+@@ -2068,17 +2058,34 @@ dhd_conf_read_sdio_params(dhd_pub_t *dhd, char *full_param, uint len_param)
+ }
+ #endif
+
++#ifdef BCMPCIE
+ bool
+-dhd_conf_read_pm_params(dhd_pub_t *dhd, char *full_param, uint len_param)
++dhd_conf_read_pcie_params(dhd_pub_t *dhd, char *full_param, uint len_param)
+ {
+ struct dhd_conf *conf = dhd->conf;
+ char *data = full_param+len_param;
+
+- if (!strncmp("lpc=", full_param, len_param)) {
+- conf->lpc = (int)simple_strtol(data, NULL, 10);
+- printf("%s: lpc = %d\n", __FUNCTION__, conf->lpc);
++ if (!strncmp("bus:deepsleep_disable=", full_param, len_param)) {
++ if (!strncmp(data, "0", 1))
++ conf->bus_deepsleep_disable = 0;
++ else
++ conf->bus_deepsleep_disable = 1;
++ printf("%s: bus:deepsleep_disable = %d\n", __FUNCTION__, conf->bus_deepsleep_disable);
+ }
+- else if (!strncmp("deepsleep=", full_param, len_param)) {
++ else
++ return false;
++
++ return true;
++}
++#endif
++
++bool
++dhd_conf_read_pm_params(dhd_pub_t *dhd, char *full_param, uint len_param)
++{
++ struct dhd_conf *conf = dhd->conf;
++ char *data = full_param+len_param;
++
++ if (!strncmp("deepsleep=", full_param, len_param)) {
+ if (!strncmp(data, "1", 1))
+ conf->deepsleep = TRUE;
+ else
+@@ -2093,9 +2100,9 @@ dhd_conf_read_pm_params(dhd_pub_t *dhd, char *full_param, uint len_param)
+ conf->pm_in_suspend = (int)simple_strtol(data, NULL, 10);
+ printf("%s: pm_in_suspend = %d\n", __FUNCTION__, conf->pm_in_suspend);
+ }
+- else if (!strncmp("pm2_sleep_ret=", full_param, len_param)) {
+- conf->pm2_sleep_ret = (int)simple_strtol(data, NULL, 10);
+- printf("%s: pm2_sleep_ret = %d\n", __FUNCTION__, conf->pm2_sleep_ret);
++ else if (!strncmp("suspend_bcn_li_dtim=", full_param, len_param)) {
++ conf->suspend_bcn_li_dtim = (int)simple_strtol(data, NULL, 10);
++ printf("%s: suspend_bcn_li_dtim = %d\n", __FUNCTION__, conf->suspend_bcn_li_dtim);
+ }
+ else if (!strncmp("xmit_in_suspend=", full_param, len_param)) {
+ if (!strncmp(data, "1", 1))
+@@ -2108,6 +2115,15 @@ dhd_conf_read_pm_params(dhd_pub_t *dhd, char *full_param, uint len_param)
+ conf->ap_in_suspend = (int)simple_strtol(data, NULL, 10);
+ printf("%s: ap_in_suspend = %d\n", __FUNCTION__, conf->ap_in_suspend);
+ }
++#ifdef SUSPEND_EVENT
++ else if (!strncmp("suspend_eventmask_enable=", full_param, len_param)) {
++ if (!strncmp(data, "1", 1))
++ conf->suspend_eventmask_enable = TRUE;
++ else
++ conf->suspend_eventmask_enable = FALSE;
++ printf("%s: suspend_eventmask_enable = %d\n", __FUNCTION__, conf->suspend_eventmask_enable);
++ }
++#endif
+ else
+ return false;
+
+@@ -2123,7 +2139,18 @@ dhd_conf_read_others(dhd_pub_t *dhd, char *full_param, uint len_param)
+ char *pch, *pick_tmp;
+ int i;
+
+- if (!strncmp("band=", full_param, len_param)) {
++ if (!strncmp("dhd_poll=", full_param, len_param)) {
++ if (!strncmp(data, "0", 1))
++ conf->dhd_poll = 0;
++ else
++ conf->dhd_poll = 1;
++ printf("%s: dhd_poll = %d\n", __FUNCTION__, conf->dhd_poll);
++ }
++ else if (!strncmp("dhd_watchdog_ms=", full_param, len_param)) {
++ dhd_watchdog_ms = (int)simple_strtol(data, NULL, 10);
++ printf("%s: dhd_watchdog_ms = %d\n", __FUNCTION__, dhd_watchdog_ms);
++ }
++ else if (!strncmp("band=", full_param, len_param)) {
+ /* Process band:
+ * band=a for 5GHz only and band=b for 2.4GHz only
+ */
+@@ -2135,17 +2162,26 @@ dhd_conf_read_others(dhd_pub_t *dhd, char *full_param, uint len_param)
+ conf->band = WLC_BAND_AUTO;
+ printf("%s: band = %d\n", __FUNCTION__, conf->band);
+ }
+- else if (!strncmp("mimo_bw_cap=", full_param, len_param)) {
+- conf->mimo_bw_cap = (uint)simple_strtol(data, NULL, 10);
+- printf("%s: mimo_bw_cap = %d\n", __FUNCTION__, conf->mimo_bw_cap);
+- }
+ else if (!strncmp("bw_cap_2g=", full_param, len_param)) {
+- conf->bw_cap_2g = (uint)simple_strtol(data, NULL, 0);
+- printf("%s: bw_cap_2g = %d\n", __FUNCTION__, conf->bw_cap_2g);
++ conf->bw_cap[0] = (uint)simple_strtol(data, NULL, 0);
++ printf("%s: bw_cap_2g = %d\n", __FUNCTION__, conf->bw_cap[0]);
+ }
+ else if (!strncmp("bw_cap_5g=", full_param, len_param)) {
+- conf->bw_cap_5g = (uint)simple_strtol(data, NULL, 0);
+- printf("%s: bw_cap_2g = %d\n", __FUNCTION__, conf->bw_cap_5g);
++ conf->bw_cap[1] = (uint)simple_strtol(data, NULL, 0);
++ printf("%s: bw_cap_5g = %d\n", __FUNCTION__, conf->bw_cap[1]);
++ }
++ else if (!strncmp("bw_cap=", full_param, len_param)) {
++ pick_tmp = data;
++ pch = bcmstrtok(&pick_tmp, " ,.-", 0);
++ if (pch != NULL) {
++ conf->bw_cap[0] = (uint32)simple_strtol(pch, NULL, 0);
++ printf("%s: bw_cap 2g = %d\n", __FUNCTION__, conf->bw_cap[0]);
++ }
++ pch = bcmstrtok(&pick_tmp, " ,.-", 0);
++ if (pch != NULL) {
++ conf->bw_cap[1] = (uint32)simple_strtol(pch, NULL, 0);
++ printf("%s: bw_cap 5g = %d\n", __FUNCTION__, conf->bw_cap[1]);
++ }
+ }
+ else if (!strncmp("ccode=", full_param, len_param)) {
+ memset(&conf->cspec, 0, sizeof(wl_country_t));
+@@ -2177,10 +2213,6 @@ dhd_conf_read_others(dhd_pub_t *dhd, char *full_param, uint len_param)
+ printf("%s: keep_alive_period = %d\n", __FUNCTION__,
+ conf->keep_alive_period);
+ }
+- else if (!strncmp("stbc=", full_param, len_param)) {
+- conf->stbc = (int)simple_strtol(data, NULL, 10);
+- printf("%s: stbc = %d\n", __FUNCTION__, conf->stbc);
+- }
+ else if (!strncmp("phy_oclscdenable=", full_param, len_param)) {
+ conf->phy_oclscdenable = (int)simple_strtol(data, NULL, 10);
+ printf("%s: phy_oclscdenable = %d\n", __FUNCTION__, conf->phy_oclscdenable);
+@@ -2197,18 +2229,6 @@ dhd_conf_read_others(dhd_pub_t *dhd, char *full_param, uint len_param)
+ conf->bcn_timeout= (uint)simple_strtol(data, NULL, 10);
+ printf("%s: bcn_timeout = %d\n", __FUNCTION__, conf->bcn_timeout);
+ }
+- else if (!strncmp("ampdu_ba_wsize=", full_param, len_param)) {
+- conf->ampdu_ba_wsize = (int)simple_strtol(data, NULL, 10);
+- printf("%s: ampdu_ba_wsize = %d\n", __FUNCTION__, conf->ampdu_ba_wsize);
+- }
+- else if (!strncmp("ampdu_hostreorder=", full_param, len_param)) {
+- conf->ampdu_hostreorder = (int)simple_strtol(data, NULL, 10);
+- printf("%s: ampdu_hostreorder = %d\n", __FUNCTION__, conf->ampdu_hostreorder);
+- }
+- else if (!strncmp("spect=", full_param, len_param)) {
+- conf->spect = (int)simple_strtol(data, NULL, 10);
+- printf("%s: spect = %d\n", __FUNCTION__, conf->spect);
+- }
+ else if (!strncmp("txbf=", full_param, len_param)) {
+ conf->txbf = (int)simple_strtol(data, NULL, 10);
+ printf("%s: txbf = %d\n", __FUNCTION__, conf->txbf);
+@@ -2231,6 +2251,7 @@ dhd_conf_read_others(dhd_pub_t *dhd, char *full_param, uint len_param)
+ conf->pktprio8021x = (int)simple_strtol(data, NULL, 10);
+ printf("%s: pktprio8021x = %d\n", __FUNCTION__, conf->pktprio8021x);
+ }
++#if defined(BCMSDIO) || defined(BCMPCIE)
+ else if (!strncmp("dhd_txbound=", full_param, len_param)) {
+ dhd_txbound = (uint)simple_strtol(data, NULL, 10);
+ printf("%s: dhd_txbound = %d\n", __FUNCTION__, dhd_txbound);
+@@ -2239,29 +2260,32 @@ dhd_conf_read_others(dhd_pub_t *dhd, char *full_param, uint len_param)
+ dhd_rxbound = (uint)simple_strtol(data, NULL, 10);
+ printf("%s: dhd_rxbound = %d\n", __FUNCTION__, dhd_rxbound);
+ }
+- else if (!strncmp("rsdb_mode=", full_param, len_param)) {
+- conf->rsdb_mode = (int)simple_strtol(data, NULL, 10);
+- printf("%s: rsdb_mode = %d\n", __FUNCTION__, conf->rsdb_mode);
+- }
+- else if (!strncmp("vhtmode=", full_param, len_param)) {
+- if (!strncmp(data, "0", 1))
+- conf->vhtmode = 0;
+- else
+- conf->vhtmode = 1;
+- printf("%s: vhtmode = %d\n", __FUNCTION__, conf->vhtmode);
+- }
++#endif
+ else if (!strncmp("num_different_channels=", full_param, len_param)) {
+ conf->num_different_channels = (int)simple_strtol(data, NULL, 10);
+ printf("%s: num_different_channels = %d\n", __FUNCTION__, conf->num_different_channels);
+ }
+- else if (!strncmp("autocountry=", full_param, len_param)) {
+- conf->autocountry = (int)simple_strtol(data, NULL, 10);
+- printf("%s: autocountry = %d\n", __FUNCTION__, conf->autocountry);
+- }
+ else if (!strncmp("tsq=", full_param, len_param)) {
+ conf->tsq = (int)simple_strtol(data, NULL, 10);
+ printf("%s: tsq = %d\n", __FUNCTION__, conf->tsq);
+ }
++ else if (!strncmp("ctrl_resched=", full_param, len_param)) {
++ conf->ctrl_resched = (int)simple_strtol(data, NULL, 10);
++ printf("%s: ctrl_resched = %d\n", __FUNCTION__, conf->ctrl_resched);
++ }
++ else if (!strncmp("dhd_ioctl_timeout_msec=", full_param, len_param)) {
++ conf->dhd_ioctl_timeout_msec = (int)simple_strtol(data, NULL, 10);
++ printf("%s: dhd_ioctl_timeout_msec = %d\n", __FUNCTION__, conf->dhd_ioctl_timeout_msec);
++ }
++ else if (!strncmp("wl_preinit=", full_param, len_param)) {
++ if (!(conf->wl_preinit = kmalloc(len_param+1, GFP_KERNEL))) {
++ CONFIG_ERROR(("%s: kmalloc failed\n", __FUNCTION__));
++ } else {
++ memset(conf->wl_preinit, 0, len_param+1);
++ strcpy(conf->wl_preinit, data);
++ printf("%s: wl_preinit = %s\n", __FUNCTION__, conf->wl_preinit);
++ }
++ }
+ else
+ return false;
+
+@@ -2272,7 +2296,7 @@ int
+ dhd_conf_read_config(dhd_pub_t *dhd, char *conf_path)
+ {
+ int bcmerror = -1;
+- uint len, start_pos=0;
++ uint len = 0, start_pos=0;
+ void * image = NULL;
+ char * memblock = NULL;
+ char *bufp, *pick = NULL, *pch;
+@@ -2344,14 +2368,16 @@ dhd_conf_read_config(dhd_pub_t *dhd, char *conf_path)
+ continue;
+ else if (dhd_conf_read_country_list(dhd, pick, len_param))
+ continue;
++ else if (dhd_conf_read_mchan_params(dhd, pick, len_param))
++ continue;
+ #ifdef PKT_FILTER_SUPPORT
+ else if (dhd_conf_read_pkt_filter(dhd, pick, len_param))
+ continue;
+ #endif /* PKT_FILTER_SUPPORT */
+-#ifdef IAPSTA_PREINIT
+- else if (dhd_conf_read_iapsta(dhd, pick, len_param))
++#ifdef ISAM_PREINIT
++ else if (dhd_conf_read_isam(dhd, pick, len_param))
+ continue;
+-#endif /* IAPSTA_PREINIT */
++#endif /* ISAM_PREINIT */
+ #ifdef IDHCP
+ else if (dhd_conf_read_dhcp_params(dhd, pick, len_param))
+ continue;
+@@ -2360,6 +2386,10 @@ dhd_conf_read_config(dhd_pub_t *dhd, char *conf_path)
+ else if (dhd_conf_read_sdio_params(dhd, pick, len_param))
+ continue;
+ #endif /* BCMSDIO */
++#ifdef BCMPCIE
++ else if (dhd_conf_read_pcie_params(dhd, pick, len_param))
++ continue;
++#endif /* BCMPCIE */
+ else if (dhd_conf_read_pm_params(dhd, pick, len_param))
+ continue;
+ else if (dhd_conf_read_others(dhd, pick, len_param))
+@@ -2432,7 +2462,7 @@ dhd_conf_set_txglom_params(dhd_pub_t *dhd, bool enable)
+ #endif
+ // other parameters set in preinit or config.txt
+ } else {
+- // clear txglom parameters, but don't change swtxglom since it's possible enabled in config.txt
++ // clear txglom parameters
+ conf->txglom_ext = FALSE;
+ conf->txglom_bucket_size = 0;
+ conf->txglomsize = 0;
+@@ -2441,8 +2471,10 @@ dhd_conf_set_txglom_params(dhd_pub_t *dhd, bool enable)
+ if (conf->txglom_ext)
+ printf("%s: txglom_ext=%d, txglom_bucket_size=%d\n", __FUNCTION__,
+ conf->txglom_ext, conf->txglom_bucket_size);
+- printf("%s: txglomsize=%d, deferred_tx_len=%d, bus_txglom=%d\n", __FUNCTION__,
+- conf->txglomsize, conf->deferred_tx_len, conf->bus_txglom);
++ printf("%s: txglom_mode=%s\n", __FUNCTION__,
++ conf->txglom_mode==SDPCM_TXGLOM_MDESC?"multi-desc":"copy");
++ printf("%s: txglomsize=%d, deferred_tx_len=%d\n", __FUNCTION__,
++ conf->txglomsize, conf->deferred_tx_len);
+ printf("%s: tx_in_rx=%d, txinrx_thres=%d, dhd_txminmax=%d\n", __FUNCTION__,
+ conf->tx_in_rx, conf->txinrx_thres, conf->dhd_txminmax);
+ printf("%s: tx_max_offset=%d, txctl_tmo_fix=%d\n", __FUNCTION__,
+@@ -2451,10 +2483,151 @@ dhd_conf_set_txglom_params(dhd_pub_t *dhd, bool enable)
+ }
+ #endif
+
++static int
++dhd_conf_rsdb_mode(dhd_pub_t *dhd, char *buf)
++{
++ char *pch;
++ wl_config_t rsdb_mode_cfg = {1, 0};
++
++ pch = buf;
++ rsdb_mode_cfg.config = (int)simple_strtol(pch, NULL, 0);
++
++ if (pch) {
++ dhd_conf_set_bufiovar(dhd, WLC_SET_VAR, "rsdb_mode", (char *)&rsdb_mode_cfg,
++ sizeof(rsdb_mode_cfg), TRUE);
++ printf("%s: rsdb_mode %d\n", __FUNCTION__, rsdb_mode_cfg.config);
++ }
++
++ return 0;
++}
++
++typedef int (tpl_parse_t)(dhd_pub_t *dhd, char *buf);
++
++typedef struct iovar_tpl_t {
++ int cmd;
++ char *name;
++ tpl_parse_t *parse;
++} iovar_tpl_t;
++
++const iovar_tpl_t iovar_tpl_list[] = {
++ {WLC_SET_VAR, "rsdb_mode", dhd_conf_rsdb_mode},
++};
++
++static int iovar_tpl_parse(const iovar_tpl_t *tpl, int tpl_count,
++ dhd_pub_t *dhd, int cmd, char *name, char *buf)
++{
++ int i, ret = 0;
++
++ /* look for a matching code in the table */
++ for (i = 0; i < tpl_count; i++, tpl++) {
++ if (tpl->cmd == cmd && !strcmp(tpl->name, name))
++ break;
++ }
++ if (i < tpl_count && tpl->parse) {
++ ret = tpl->parse(dhd, buf);
++ } else {
++ ret = -1;
++ }
++
++ return ret;
++}
++
++bool
++dhd_conf_set_wl_preinit(dhd_pub_t *dhd, char *data)
++{
++ int cmd, val, ret = 0;
++ char name[32], *pch, *pick_tmp, *pick_tmp2;
++
++ /* Process wl_preinit:
++ * wl_preinit=[cmd]/[val], [cmd]/[val] \
++ * Ex: wl_preinit=86/0, mpc/0
++ */
++ pick_tmp = data;
++ while (pick_tmp && (pick_tmp2 = bcmstrtok(&pick_tmp, ",", 0)) != NULL) {
++ pch = bcmstrtok(&pick_tmp2, "=", 0);
++ if (!pch)
++ break;
++ if (*pch == ' ') {
++ pch++;
++ }
++ memset(name, 0 , sizeof (name));
++ cmd = (int)simple_strtol(pch, NULL, 0);
++ if (cmd == 0) {
++ cmd = WLC_SET_VAR;
++ strcpy(name, pch);
++ }
++ pch = bcmstrtok(&pick_tmp2, ",", 0);
++ if (!pch) {
++ break;
++ }
++ ret = iovar_tpl_parse(iovar_tpl_list, ARRAY_SIZE(iovar_tpl_list),
++ dhd, cmd, name, pch);
++ if (ret) {
++ val = (int)simple_strtol(pch, NULL, 0);
++ dhd_conf_set_intiovar(dhd, cmd, name, val, -1, TRUE);
++ }
++ }
++
++ return true;
++}
++
++void
++dhd_conf_postinit_ioctls(dhd_pub_t *dhd)
++{
++ struct dhd_conf *conf = dhd->conf;
++
++ dhd_conf_set_intiovar(dhd, WLC_UP, "up", 0, 0, FALSE);
++ dhd_conf_map_country_list(dhd, &conf->cspec, 0);
++ dhd_conf_set_country(dhd, &conf->cspec);
++ dhd_conf_fix_country(dhd);
++ dhd_conf_get_country(dhd, &dhd->dhd_cspec);
++
++ dhd_conf_set_intiovar(dhd, WLC_SET_BAND, "WLC_SET_BAND", conf->band, 0, FALSE);
++ dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "bcn_timeout", conf->bcn_timeout, 0, FALSE);
++ if (conf->fw_type == FW_TYPE_MESH)
++ conf->pm = PM_OFF;
++ dhd_conf_set_intiovar(dhd, WLC_SET_PM, "PM", conf->pm, 0, FALSE);
++ dhd_conf_set_intiovar(dhd, WLC_SET_SRL, "WLC_SET_SRL", conf->srl, 0, TRUE);
++ dhd_conf_set_intiovar(dhd, WLC_SET_LRL, "WLC_SET_LRL", conf->lrl, 0, FALSE);
++ dhd_conf_set_bw_cap(dhd);
++ dhd_conf_set_roam(dhd);
++
++#if defined(BCMPCIE)
++ dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "bus:deepsleep_disable",
++ conf->bus_deepsleep_disable, 0, FALSE);
++#endif /* defined(BCMPCIE) */
++
++#ifdef IDHCP
++ dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "dhcpc_enable", conf->dhcpc_enable, 0, FALSE);
++ if (dhd->conf->dhcpd_enable >= 0) {
++ dhd_conf_set_bufiovar(dhd, WLC_SET_VAR, "dhcpd_ip_addr",
++ (char *)&conf->dhcpd_ip_addr, sizeof(conf->dhcpd_ip_addr), FALSE);
++ dhd_conf_set_bufiovar(dhd, WLC_SET_VAR, "dhcpd_ip_mask",
++ (char *)&conf->dhcpd_ip_mask, sizeof(conf->dhcpd_ip_mask), FALSE);
++ dhd_conf_set_bufiovar(dhd, WLC_SET_VAR, "dhcpd_ip_start",
++ (char *)&conf->dhcpd_ip_start, sizeof(conf->dhcpd_ip_start), FALSE);
++ dhd_conf_set_bufiovar(dhd, WLC_SET_VAR, "dhcpd_ip_end",
++ (char *)&conf->dhcpd_ip_end, sizeof(conf->dhcpd_ip_end), FALSE);
++ dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "dhcpd_enable",
++ conf->dhcpd_enable, 0, FALSE);
++ }
++#endif
++ dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "txbf", conf->txbf, 0, FALSE);
++ dhd_conf_set_intiovar(dhd, WLC_SET_FAKEFRAG, "WLC_SET_FAKEFRAG", conf->frameburst, 0, FALSE);
++
++ dhd_conf_set_wl_preinit(dhd, conf->wl_preinit);
++
++#ifndef WL_CFG80211
++ dhd_conf_set_intiovar(dhd, WLC_UP, "up", 0, 0, FALSE);
++#endif
++
++}
++
+ int
+ dhd_conf_preinit(dhd_pub_t *dhd)
+ {
+ struct dhd_conf *conf = dhd->conf;
++ int i;
+
+ CONFIG_TRACE(("%s: Enter\n", __FUNCTION__));
+
+@@ -2463,13 +2636,19 @@ dhd_conf_preinit(dhd_pub_t *dhd)
+ dhd_conf_free_mac_list(&conf->nv_by_mac);
+ dhd_conf_free_chip_nv_path_list(&conf->nv_by_chip);
+ #endif
+- if (conf->magic_pkt_filter_add)
++ dhd_conf_free_country_list(&conf->country_list);
++ dhd_conf_free_country_list(&conf->country_list_nodfs);
++ if (conf->magic_pkt_filter_add) {
+ kfree(conf->magic_pkt_filter_add);
++ conf->magic_pkt_filter_add = NULL;
++ }
++ if (conf->wl_preinit) {
++ kfree(conf->wl_preinit);
++ conf->wl_preinit = NULL;
++ }
+ memset(&conf->country_list, 0, sizeof(conf_country_list_t));
+ conf->band = -1;
+- conf->mimo_bw_cap = -1;
+- conf->bw_cap_2g = -1;
+- conf->bw_cap_5g = -1;
++ memset(&conf->bw_cap, -1, sizeof(conf->bw_cap));
+ if (conf->chip == BCM43362_CHIP_ID || conf->chip == BCM4330_CHIP_ID) {
+ strcpy(conf->cspec.country_abbrev, "ALL");
+ strcpy(conf->cspec.ccode, "ALL");
+@@ -2477,7 +2656,8 @@ dhd_conf_preinit(dhd_pub_t *dhd)
+ } else if (conf->chip == BCM4335_CHIP_ID || conf->chip == BCM4339_CHIP_ID ||
+ conf->chip == BCM4354_CHIP_ID || conf->chip == BCM4356_CHIP_ID ||
+ conf->chip == BCM4345_CHIP_ID || conf->chip == BCM4371_CHIP_ID ||
+- conf->chip == BCM43569_CHIP_ID || conf->chip == BCM4359_CHIP_ID) {
++ conf->chip == BCM43569_CHIP_ID || conf->chip == BCM4359_CHIP_ID ||
++ conf->chip == BCM4362_CHIP_ID) {
+ strcpy(conf->cspec.country_abbrev, "CN");
+ strcpy(conf->cspec.ccode, "CN");
+ conf->cspec.rev = 38;
+@@ -2516,7 +2696,6 @@ dhd_conf_preinit(dhd_pub_t *dhd)
+ conf->force_wme_ac = 0;
+ memset(&conf->wme_sta, 0, sizeof(wme_param_t));
+ memset(&conf->wme_ap, 0, sizeof(wme_param_t));
+- conf->stbc = -1;
+ conf->phy_oclscdenable = -1;
+ #ifdef PKT_FILTER_SUPPORT
+ memset(&conf->pkt_filter_add, 0, sizeof(conf_pkt_filter_add_t));
+@@ -2525,19 +2704,16 @@ dhd_conf_preinit(dhd_pub_t *dhd)
+ conf->srl = -1;
+ conf->lrl = -1;
+ conf->bcn_timeout = 16;
+- conf->spect = -1;
+ conf->txbf = -1;
+- conf->lpc = -1;
+ conf->disable_proptx = -1;
++ conf->dhd_poll = -1;
+ #ifdef BCMSDIO
+- conf->bus_txglom = -1;
+ conf->use_rxchain = 0;
+ conf->bus_rxglom = TRUE;
+ conf->txglom_ext = FALSE;
+ conf->tx_max_offset = 0;
+ conf->txglomsize = SDPCM_DEFGLOM_SIZE;
+- conf->dhd_poll = -1;
+- conf->txctl_tmo_fix = 5;
++ conf->txctl_tmo_fix = 300;
+ conf->tx_in_rx = TRUE;
+ conf->txglom_mode = SDPCM_TXGLOM_CPY;
+ conf->deferred_tx_len = 0;
+@@ -2545,19 +2721,26 @@ dhd_conf_preinit(dhd_pub_t *dhd)
+ conf->txinrx_thres = -1;
+ conf->sd_f2_blocksize = 0;
+ conf->oob_enabled_later = FALSE;
++ conf->orphan_move = 0;
++#endif
++#ifdef BCMPCIE
++ conf->bus_deepsleep_disable = 1;
+ #endif
+- conf->ampdu_ba_wsize = 0;
+- conf->ampdu_hostreorder = -1;
+ conf->dpc_cpucore = -1;
+ conf->rxf_cpucore = -1;
+ conf->frameburst = -1;
+ conf->deepsleep = FALSE;
+ conf->pm = -1;
+ conf->pm_in_suspend = -1;
+- conf->pm2_sleep_ret = -1;
++ conf->suspend_bcn_li_dtim = -1;
+ conf->num_different_channels = -1;
+ conf->xmit_in_suspend = TRUE;
+ conf->ap_in_suspend = 0;
++#ifdef SUSPEND_EVENT
++ conf->suspend_eventmask_enable = FALSE;
++ memset(&conf->suspend_eventmask, 0, sizeof(conf->suspend_eventmask));
++ memset(&conf->resume_eventmask, 0, sizeof(conf->resume_eventmask));
++#endif
+ #ifdef IDHCP
+ conf->dhcpc_enable = -1;
+ conf->dhcpd_enable = -1;
+@@ -2568,25 +2751,35 @@ dhd_conf_preinit(dhd_pub_t *dhd)
+ conf->tsq = 0;
+ #endif
+ #ifdef DHDTCPACK_SUPPRESS
++#ifdef BCMPCIE
++ conf->tcpack_sup_mode = TCPACK_SUP_DEFAULT;
++#else
+ conf->tcpack_sup_mode = TCPACK_SUP_OFF;
++#endif
+ #endif
+ conf->pktprio8021x = -1;
+- conf->rsdb_mode = -2;
+- conf->vhtmode = -1;
+- conf->autocountry = -1;
+-#ifdef IAPSTA_PREINIT
+- memset(conf->iapsta_init, 0, sizeof(conf->iapsta_init));
+- memset(conf->iapsta_config, 0, sizeof(conf->iapsta_config));
+- memset(conf->iapsta_enable, 0, sizeof(conf->iapsta_enable));
++ conf->ctrl_resched = 2;
++ conf->dhd_ioctl_timeout_msec = 0;
++#ifdef ISAM_PREINIT
++ memset(conf->isam_init, 0, sizeof(conf->isam_init));
++ memset(conf->isam_config, 0, sizeof(conf->isam_config));
++ memset(conf->isam_enable, 0, sizeof(conf->isam_enable));
+ #endif
++ for (i=0; imchan[i], -1, sizeof(mchan_params_t));
++ }
+ if (conf->chip == BCM4354_CHIP_ID || conf->chip == BCM4356_CHIP_ID ||
+ conf->chip == BCM4371_CHIP_ID || conf->chip == BCM43569_CHIP_ID ||
+- conf->chip == BCM4359_CHIP_ID) {
++ conf->chip == BCM4359_CHIP_ID || conf->chip == BCM4362_CHIP_ID) {
+ #ifdef DHDTCPACK_SUPPRESS
++#ifdef BCMSDIO
+ conf->tcpack_sup_mode = TCPACK_SUP_REPLACE;
+ #endif
++#endif
++#if defined(BCMSDIO) || defined(BCMPCIE)
+ dhd_rxbound = 128;
+ dhd_txbound = 64;
++#endif
+ conf->txbf = 1;
+ conf->frameburst = 1;
+ #ifdef BCMSDIO
+@@ -2594,6 +2787,11 @@ dhd_conf_preinit(dhd_pub_t *dhd)
+ conf->txinrx_thres = 128;
+ conf->sd_f2_blocksize = CUSTOM_SDIO_F2_BLKSIZE;
+ conf->oob_enabled_later = TRUE;
++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0))
++ conf->orphan_move = 1;
++#else
++ conf->orphan_move = 0;
++#endif
+ #endif
+ }
+
+@@ -2603,9 +2801,6 @@ dhd_conf_preinit(dhd_pub_t *dhd)
+ conf->chip == BCM43340_CHIP_ID || conf->chip == BCM43341_CHIP_ID ||
+ conf->chip == BCM4334_CHIP_ID || conf->chip == BCM4324_CHIP_ID) {
+ conf->txglom_ext = TRUE;
+- conf->use_rxchain = 0;
+- conf->tx_in_rx = TRUE;
+- conf->tx_max_offset = 1;
+ } else {
+ conf->txglom_ext = FALSE;
+ }
+@@ -2621,10 +2816,7 @@ dhd_conf_preinit(dhd_pub_t *dhd)
+ #endif
+ if (conf->txglomsize > SDPCM_MAXGLOM_SIZE)
+ conf->txglomsize = SDPCM_MAXGLOM_SIZE;
+- conf->deferred_tx_len = conf->txglomsize;
+ #endif
+- if (conf->chip == BCM4354_CHIP_ID && conf->chiprev == 1)
+- dhd_slpauto = 0;
+
+ return 0;
+ }
+@@ -2637,8 +2829,16 @@ dhd_conf_reset(dhd_pub_t *dhd)
+ dhd_conf_free_mac_list(&dhd->conf->nv_by_mac);
+ dhd_conf_free_chip_nv_path_list(&dhd->conf->nv_by_chip);
+ #endif
+- if (dhd->conf->magic_pkt_filter_add)
++ dhd_conf_free_country_list(&dhd->conf->country_list);
++ dhd_conf_free_country_list(&dhd->conf->country_list_nodfs);
++ if (dhd->conf->magic_pkt_filter_add) {
+ kfree(dhd->conf->magic_pkt_filter_add);
++ dhd->conf->magic_pkt_filter_add = NULL;
++ }
++ if (dhd->conf->wl_preinit) {
++ kfree(dhd->conf->wl_preinit);
++ dhd->conf->wl_preinit = NULL;
++ }
+ memset(dhd->conf, 0, sizeof(dhd_conf_t));
+ return 0;
+ }
+@@ -2682,8 +2882,16 @@ dhd_conf_detach(dhd_pub_t *dhd)
+ dhd_conf_free_mac_list(&dhd->conf->nv_by_mac);
+ dhd_conf_free_chip_nv_path_list(&dhd->conf->nv_by_chip);
+ #endif
+- if (dhd->conf->magic_pkt_filter_add)
++ dhd_conf_free_country_list(&dhd->conf->country_list);
++ dhd_conf_free_country_list(&dhd->conf->country_list_nodfs);
++ if (dhd->conf->magic_pkt_filter_add) {
+ kfree(dhd->conf->magic_pkt_filter_add);
++ dhd->conf->magic_pkt_filter_add = NULL;
++ }
++ if (dhd->conf->wl_preinit) {
++ kfree(dhd->conf->wl_preinit);
++ dhd->conf->wl_preinit = NULL;
++ }
+ MFREE(dhd->osh, dhd->conf, sizeof(dhd_conf_t));
+ }
+ dhd->conf = NULL;
+diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_config.h b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_config.h
+index bfa6a3ea31e6..6adf2d070750 100644
+--- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_config.h
++++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_config.h
+@@ -1,5 +1,4 @@
+ /* SPDX-License-Identifier: GPL-2.0 */
+-
+ #ifndef _dhd_config_
+ #define _dhd_config_
+
+@@ -9,36 +8,27 @@
+ #include
+ #include <802.11.h>
+
++#define FW_TYPE_STA 0
++#define FW_TYPE_APSTA 1
++#define FW_TYPE_P2P 2
++#define FW_TYPE_MESH 3
++#define FW_TYPE_ES 4
++#define FW_TYPE_MFG 5
++#define FW_TYPE_G 0
++#define FW_TYPE_AG 1
++
+ #define FW_PATH_AUTO_SELECT 1
+ //#define CONFIG_PATH_AUTO_SELECT
+ extern char firmware_path[MOD_PARAM_PATHLEN];
++#if defined(BCMSDIO) || defined(BCMPCIE)
+ extern uint dhd_rxbound;
+ extern uint dhd_txbound;
++#endif
+ #ifdef BCMSDIO
+ #define TXGLOM_RECV_OFFSET 8
+ extern uint dhd_doflow;
+ extern uint dhd_slpauto;
+-
+-#define BCM43362A0_CHIP_REV 0
+-#define BCM43362A2_CHIP_REV 1
+-#define BCM43430A0_CHIP_REV 0
+-#define BCM43430A1_CHIP_REV 1
+-#define BCM43430A2_CHIP_REV 2
+-#define BCM43012B0_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
+-#define BCM4359C0_CHIP_REV 9
+ #endif
+-#define BCM4356A2_CHIP_REV 2
+-#define BCM4358A3_CHIP_REV 3
+
+ typedef struct wl_mac_range {
+ uint32 oui;
+@@ -98,25 +88,35 @@ typedef struct conf_pkt_filter_del {
+ #define CONFIG_COUNTRY_LIST_SIZE 100
+ typedef struct conf_country_list {
+ uint32 count;
+- wl_country_t cspec[CONFIG_COUNTRY_LIST_SIZE];
++ wl_country_t *cspec[CONFIG_COUNTRY_LIST_SIZE];
+ } conf_country_list_t;
+
++/* mchan_params */
++#define MCHAN_MAX_NUM 4
++#define MIRACAST_SOURCE 1
++#define MIRACAST_SINK 2
++typedef struct mchan_params {
++ int bw;
++ int p2p_mode;
++ int miracast_mode;
++} mchan_params_t;
++
+ 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;
++ int bw_cap[2];
+ wl_country_t cspec;
+ wl_channel_list_t channels;
+ uint roam_off;
+ uint roam_off_suspend;
+- int roam_trigger[2];
++ int roam_trigger[2];
+ int roam_scan_period[2];
+ int roam_delta[2];
+ int fullroamperiod;
+@@ -124,7 +124,6 @@ typedef struct dhd_conf {
+ int force_wme_ac;
+ wme_param_t wme_sta;
+ wme_param_t wme_ap;
+- int stbc;
+ int phy_oclscdenable;
+ #ifdef PKT_FILTER_SUPPORT
+ conf_pkt_filter_add_t pkt_filter_add;
+@@ -134,12 +133,10 @@ typedef struct dhd_conf {
+ int srl;
+ int lrl;
+ uint bcn_timeout;
+- int spect;
+ int txbf;
+- int lpc;
+ int disable_proptx;
++ int dhd_poll;
+ #ifdef BCMSDIO
+- int bus_txglom;
+ int use_rxchain;
+ bool bus_rxglom;
+ bool txglom_ext; /* Only for 43362/4330/43340/43341/43241 */
+@@ -150,7 +147,6 @@ typedef struct dhd_conf {
+ */
+ int tx_max_offset;
+ uint txglomsize;
+- int dhd_poll;
+ int txctl_tmo_fix;
+ bool tx_in_rx;
+ bool txglom_mode;
+@@ -164,22 +160,22 @@ typedef struct dhd_conf {
+ int dhd_txminmax; // -1=DATABUFCNT(bus)
+ uint sd_f2_blocksize;
+ bool oob_enabled_later;
++ int orphan_move;
++#endif
++#ifdef BCMPCIE
++ int bus_deepsleep_disable;
+ #endif
+- int ampdu_ba_wsize;
+- int ampdu_hostreorder;
+ int dpc_cpucore;
+ int rxf_cpucore;
+ int frameburst;
+ bool deepsleep;
+ int pm;
+ int pm_in_suspend;
+- int pm2_sleep_ret;
++ int suspend_bcn_li_dtim;
+ #ifdef DHDTCPACK_SUPPRESS
+ uint8 tcpack_sup_mode;
+ #endif
+ int pktprio8021x;
+- int rsdb_mode;
+- int vhtmode;
+ int num_different_channels;
+ int xmit_in_suspend;
+ int ap_in_suspend;
+@@ -196,12 +192,15 @@ typedef struct dhd_conf {
+ struct ipv4_addr dhcpd_ip_start;
+ struct ipv4_addr dhcpd_ip_end;
+ #endif
+-#ifdef IAPSTA_PREINIT
+- char iapsta_init[50];
+- char iapsta_config[300];
+- char iapsta_enable[50];
++#ifdef ISAM_PREINIT
++ char isam_init[50];
++ char isam_config[300];
++ char isam_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;
+
+@@ -215,7 +214,7 @@ void dhd_conf_set_hw_oob_intr(bcmsdh_info_t *sdh, uint chip);
+ void dhd_conf_set_txglom_params(dhd_pub_t *dhd, bool enable);
+ int dhd_conf_set_blksize(bcmsdh_info_t *sdh);
+ #endif
+-void dhd_conf_set_fw_name_by_chip(dhd_pub_t *dhd, char *fw_path, char *nv_path);
++void dhd_conf_set_fw_name_by_chip(dhd_pub_t *dhd, char *fw_path);
+ void dhd_conf_set_clm_name_by_chip(dhd_pub_t *dhd, char *clm_path);
+ void dhd_conf_set_nv_name_by_chip(dhd_pub_t *dhd, char *nv_path);
+ void dhd_conf_set_path(dhd_pub_t *dhd, char *dst_name, char *dst_path, char *src_path);
+@@ -226,14 +225,13 @@ int dhd_conf_set_intiovar(dhd_pub_t *dhd, uint cmd, char *name, int val, int def
+ int dhd_conf_get_iovar(dhd_pub_t *dhd, int cmd, char *name, char *buf, int len, int ifidx);
+ int dhd_conf_set_bufiovar(dhd_pub_t *dhd, uint cmd, char *name, char *buf, int len, bool down);
+ uint dhd_conf_get_band(dhd_pub_t *dhd);
+-int dhd_conf_set_country(dhd_pub_t *dhd);
++int dhd_conf_set_country(dhd_pub_t *dhd, wl_country_t *cspec);
+ int dhd_conf_get_country(dhd_pub_t *dhd, wl_country_t *cspec);
+-int dhd_conf_get_country_from_config(dhd_pub_t *dhd, wl_country_t *cspec);
++int dhd_conf_map_country_list(dhd_pub_t *dhd, wl_country_t *cspec, int nodfs);
+ int dhd_conf_fix_country(dhd_pub_t *dhd);
+ bool dhd_conf_match_channel(dhd_pub_t *dhd, uint32 channel);
+-int dhd_conf_set_roam(dhd_pub_t *dhd);
+-void dhd_conf_set_bw_cap(dhd_pub_t *dhd);
+ void dhd_conf_set_wme(dhd_pub_t *dhd, int mode);
++void dhd_conf_set_mchan_bw(dhd_pub_t *dhd, int go, int source);
+ void dhd_conf_add_pkt_filter(dhd_pub_t *dhd);
+ bool dhd_conf_del_pkt_filter(dhd_pub_t *dhd, uint32 id);
+ void dhd_conf_discard_pkt_filter(dhd_pub_t *dhd);
+@@ -247,6 +245,7 @@ int dhd_conf_get_disable_proptx(dhd_pub_t *dhd);
+ #endif
+ int dhd_conf_get_ap_mode_in_suspend(dhd_pub_t *dhd);
+ int dhd_conf_set_ap_in_suspend(dhd_pub_t *dhd, int suspend);
++void dhd_conf_postinit_ioctls(dhd_pub_t *dhd);
+ int dhd_conf_preinit(dhd_pub_t *dhd);
+ int dhd_conf_reset(dhd_pub_t *dhd);
+ int dhd_conf_attach(dhd_pub_t *dhd);
+diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_gpio.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_gpio.c
+index a7ef7c84b18b..7291322edc4f 100644
+--- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_gpio.c
++++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_gpio.c
+@@ -1,5 +1,4 @@
+ /* SPDX-License-Identifier: GPL-2.0 */
+-
+ #include
+ #include
+ #include
+@@ -148,10 +147,10 @@ static int dhd_wlan_get_mac_addr(unsigned char *buf)
+ bcopy((char *)&ea_example, buf, sizeof(struct ether_addr));
+ }
+ #endif /* EXAMPLE_GET_MAC */
++ err = rockchip_wifi_mac_addr(buf);
+ #ifdef EXAMPLE_GET_MAC_VER2
+ /* EXAMPLE code */
+ {
+- char mac[6] = {0x00,0x11,0x22,0x33,0x44,0xFF};
+ char macpad[56]= {
+ 0x00,0xaa,0x9c,0x84,0xc7,0xbc,0x9b,0xf6,
+ 0x02,0x33,0xa9,0x4d,0x5c,0xb4,0x0a,0x5d,
+@@ -160,11 +159,9 @@ static int dhd_wlan_get_mac_addr(unsigned char *buf)
+ 0x4a,0xeb,0xf6,0xe6,0x3c,0xe7,0x5f,0xfc,
+ 0x0e,0xa7,0xb3,0x0f,0x00,0xe4,0x4a,0xaf,
+ 0x87,0x08,0x16,0x6d,0x3a,0xe3,0xc7,0x80};
+- bcopy(mac, buf, sizeof(mac));
+ bcopy(macpad, buf+6, sizeof(macpad));
+ }
+ #endif /* EXAMPLE_GET_MAC_VER2 */
+- err = rockchip_wifi_mac_addr(buf);
+
+ return err;
+ }
+@@ -244,7 +241,6 @@ int dhd_wlan_init_gpio(void)
+ #ifdef CUSTOMER_OOB
+ int host_oob_irq = -1;
+ uint host_oob_irq_flags = 0;
+- int irq_flags = -1;
+ #endif
+
+ /* Please check your schematic and fill right GPIO number which connected to
+@@ -255,56 +251,54 @@ int dhd_wlan_init_gpio(void)
+ gpio_wl_host_wake = -1;
+ #endif
+
+- printf("%s: GPIO(WL_REG_ON) = %d\n", __FUNCTION__, gpio_wl_reg_on);
+ if (gpio_wl_reg_on >= 0) {
+ err = gpio_request(gpio_wl_reg_on, "WL_REG_ON");
+ if (err < 0) {
+- printf("%s: Faiiled to request gpio %d for WL_REG_ON\n",
++ printf("%s: gpio_request(%d) for WL_REG_ON failed\n",
+ __FUNCTION__, gpio_wl_reg_on);
+ gpio_wl_reg_on = -1;
+ }
+ }
+
+ #ifdef CUSTOMER_OOB
+- printf("%s: GPIO(WL_HOST_WAKE) = %d\n", __FUNCTION__, gpio_wl_host_wake);
+ if (gpio_wl_host_wake >= 0) {
+ err = gpio_request(gpio_wl_host_wake, "bcmdhd");
+ if (err < 0) {
+- printf("%s: gpio_request failed\n", __FUNCTION__);
++ printf("%s: gpio_request(%d) for WL_HOST_WAKE failed\n",
++ __FUNCTION__, gpio_wl_host_wake);
+ return -1;
+ }
+ err = gpio_direction_input(gpio_wl_host_wake);
+ if (err < 0) {
+- printf("%s: gpio_direction_input failed\n", __FUNCTION__);
++ printf("%s: gpio_direction_input(%d) for WL_HOST_WAKE failed\n",
++ __FUNCTION__, gpio_wl_host_wake);
+ gpio_free(gpio_wl_host_wake);
+ return -1;
+ }
+ host_oob_irq = gpio_to_irq(gpio_wl_host_wake);
+ if (host_oob_irq < 0) {
+- printf("%s: gpio_to_irq failed\n", __FUNCTION__);
++ printf("%s: gpio_to_irq(%d) for WL_HOST_WAKE failed\n",
++ __FUNCTION__, gpio_wl_host_wake);
+ gpio_free(gpio_wl_host_wake);
+ return -1;
+ }
+ }
+ host_oob_irq = rockchip_wifi_get_oob_irq();
+- printf("%s: host_oob_irq: %d\n", __FUNCTION__, host_oob_irq);
+
+ #ifdef HW_OOB
+- host_oob_irq_flags = IORESOURCE_IRQ | IORESOURCE_IRQ_SHAREABLE;
+- irq_flags = rockchip_wifi_get_oob_irq_flag();
+- if (irq_flags == 1)
+- host_oob_irq_flags |= IORESOURCE_IRQ_HIGHLEVEL;
+- else if (irq_flags == 0)
+- host_oob_irq_flags |= IORESOURCE_IRQ_LOWLEVEL;
+- else
+- pr_warn("%s: unknown oob irqflags !\n", __func__);
++#ifdef HW_OOB_LOW_LEVEL
++ host_oob_irq_flags = IORESOURCE_IRQ | IORESOURCE_IRQ_LOWLEVEL | IORESOURCE_IRQ_SHAREABLE;
++#else
++ host_oob_irq_flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL | IORESOURCE_IRQ_SHAREABLE;
++#endif
+ #else
+ host_oob_irq_flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE | IORESOURCE_IRQ_SHAREABLE;
+ #endif
+
+ dhd_wlan_resources[0].start = dhd_wlan_resources[0].end = host_oob_irq;
+ dhd_wlan_resources[0].flags = host_oob_irq_flags;
+- printf("%s: host_oob_irq_flags=0x%x\n", __FUNCTION__, host_oob_irq_flags);
++ printf("%s: WL_REG_ON=%d, WL_HOST_WAKE=%d\n", __FUNCTION__, gpio_wl_reg_on, gpio_wl_host_wake);
++ printf("%s: oob_irq=%d, oob_irq_flags=0x%x\n", __FUNCTION__, host_oob_irq, host_oob_irq_flags);
+ #endif /* CUSTOMER_OOB */
+
+ return 0;
+diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_ip.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_ip.c
+old mode 100755
+new mode 100644
+index ee7d105d2317..d8be26cd8cef
+--- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_ip.c
++++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_ip.c
+@@ -287,10 +287,20 @@ static void _tdata_psh_info_pool_deinit(dhd_pub_t *dhdp,
+ return;
+ }
+
+-static void dhd_tcpack_send(ulong data)
++static void dhd_tcpack_send(
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
++ struct timer_list *t
++#else
++ ulong data
++#endif
++)
+ {
+ tcpack_sup_module_t *tcpack_sup_mod;
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
++ tcpack_info_t *cur_tbl = from_timer(cur_tbl, t, timer);
++#else
+ tcpack_info_t *cur_tbl = (tcpack_info_t *)data;
++#endif
+ dhd_pub_t *dhdp;
+ int ifidx;
+ void* pkt;
+@@ -464,9 +474,13 @@ int dhd_tcpack_suppress_set(dhd_pub_t *dhdp, uint8 mode)
+ tcpack_info_t *tcpack_info_tbl =
+ &tcpack_sup_module->tcpack_info_tbl[i];
+ tcpack_info_tbl->dhdp = dhdp;
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
++ timer_setup(&tcpack_info_tbl->timer, dhd_tcpack_send, 0);
++#else
+ init_timer(&tcpack_info_tbl->timer);
+ tcpack_info_tbl->timer.data = (ulong)tcpack_info_tbl;
+ tcpack_info_tbl->timer.function = dhd_tcpack_send;
++#endif
+ }
+ break;
+ }
+diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_linux.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_linux.c
+index 233c1f77c122..87c5df2a8a0f 100644
+--- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_linux.c
++++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_linux.c
+@@ -345,6 +345,12 @@ static void dhd_hang_process(void *dhd_info, void *event_data, u8 event);
+ MODULE_LICENSE("GPL and additional rights");
+ #endif /* LinuxVer */
+
++#if defined(MULTIPLE_SUPPLICANT)
++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25))
++DEFINE_MUTEX(_dhd_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 */
+@@ -600,7 +606,7 @@ typedef struct dhd_info {
+ dhd_pub_t pub;
+ dhd_if_t *iflist[DHD_MAX_IFS]; /* for supporting multiple interfaces */
+
+- void *adapter; /* adapter information, interrupt, fw path etc. */
++ wifi_adapter_info_t *adapter; /* adapter information, interrupt, fw path etc. */
+ char fw_path[PATH_MAX]; /* path to firmware image */
+ char nv_path[PATH_MAX]; /* path to nvram vars file */
+ char clm_path[PATH_MAX]; /* path to clm vars file */
+@@ -616,6 +622,10 @@ typedef struct dhd_info {
+ #ifdef PROP_TXSTATUS
+ spinlock_t wlfc_spinlock;
+
++#ifdef BCMDBUS
++ ulong wlfc_lock_flags;
++ ulong wlfc_pub_lock_flags;
++#endif /* BCMDBUS */
+ #endif /* PROP_TXSTATUS */
+ #ifdef WLMEDIA_HTSF
+ htsf_t htsf;
+@@ -637,10 +647,14 @@ typedef struct dhd_info {
+ spinlock_t txqlock;
+ spinlock_t rxqlock;
+ spinlock_t dhd_lock;
++#ifdef BCMDBUS
++ ulong txqlock_flags;
++#else
+
+ struct semaphore sdsem;
+ tsk_ctl_t thr_dpc_ctl;
+ tsk_ctl_t thr_wdt_ctl;
++#endif /* BCMDBUS */
+
+ tsk_ctl_t thr_rxf_ctl;
+ spinlock_t rxf_lock;
+@@ -940,7 +954,7 @@ int op_mode = 0;
+ int disable_proptx = 0;
+ module_param(op_mode, int, 0644);
+ extern int wl_control_wl_start(struct net_device *dev);
+-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(BCMLXSDMMC)
++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && (defined(BCMLXSDMMC) || defined(BCMDBUS))
+ struct semaphore dhd_registration_sem;
+ #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */
+
+@@ -950,6 +964,9 @@ static void dhd_ifdel_event_handler(void *handle, void *event_info, u8 event);
+ static void dhd_set_mac_addr_handler(void *handle, void *event_info, u8 event);
+ static void dhd_set_mcast_list_handler(void *handle, void *event_info, u8 event);
+
++#ifdef DHD_UPDATE_INTF_MAC
++static void dhd_ifupdate_event_handler(void *handle, void *event_info, u8 event);
++#endif /* DHD_UPDATE_INTF_MAC */
+ #if defined(CONFIG_IPV6) && defined(IPV6_NDO_SUPPORT)
+ static void dhd_inet6_work_handler(void *dhd_info, void *event_data, u8 event);
+ #endif /* CONFIG_IPV6 && IPV6_NDO_SUPPORT */
+@@ -1054,10 +1071,10 @@ module_param(dhd_dpc_prio, int, 0);
+ int dhd_rxf_prio = CUSTOM_RXF_PRIO_SETTING;
+ module_param(dhd_rxf_prio, int, 0);
+
+-#if !defined(BCMDHDUSB)
++#if !defined(BCMDBUS)
+ extern int dhd_dongle_ramsize;
+ module_param(dhd_dongle_ramsize, int, 0);
+-#endif /* BCMDHDUSB */
++#endif /* !BCMDBUS */
+
+ #ifdef WL_CFG80211
+ int passive_channel_skip = 0;
+@@ -1903,9 +1920,11 @@ module_param(dhd_pktgen_len, uint, 0);
+
+
+
++#ifndef BCMDBUS
+ /* Allow delayed firmware download for debug purpose */
+ int allow_delay_fwdl = FALSE;
+ module_param(allow_delay_fwdl, int, 0);
++#endif /* !BCMDBUS */
+
+ extern char dhd_version[];
+ extern char fw_version[];
+@@ -1938,7 +1957,9 @@ int dhd_monitor_uninit(void);
+ struct iw_statistics *dhd_get_wireless_stats(struct net_device *dev);
+ #endif /* defined(WL_WIRELESS_EXT) */
+
++#ifndef BCMDBUS
+ static void dhd_dpc(ulong data);
++#endif /* !BCMDBUS */
+ /* forward decl */
+ extern int dhd_wait_pend8021x(struct net_device *dev);
+ void dhd_os_wd_timer_extend(void *bus, bool extend);
+@@ -3208,8 +3229,8 @@ int dhd_process_cid_mac(dhd_pub_t *dhdp, bool prepost)
+ }
+
+ // terence 20160615: fix building error if ARP_OFFLOAD_SUPPORT removed
+-#if defined(PKT_FILTER_SUPPORT) && defined(ARP_OFFLOAD_SUPPORT)
+-#ifndef GAN_LITE_NAT_KEEPALIVE_FILTER
++#if defined(PKT_FILTER_SUPPORT)
++#if defined(ARP_OFFLOAD_SUPPORT) && !defined(GAN_LITE_NAT_KEEPALIVE_FILTER)
+ static bool
+ _turn_on_arp_filter(dhd_pub_t *dhd, int op_mode_param)
+ {
+@@ -4354,6 +4375,87 @@ done:
+ dhd_net_if_unlock_local(dhd);
+ }
+
++#ifdef DHD_UPDATE_INTF_MAC
++static void
++dhd_ifupdate_event_handler(void *handle, void *event_info, u8 event)
++{
++ dhd_info_t *dhd = handle;
++ int ifidx;
++ dhd_if_event_t *if_event = event_info;
++
++ if (event != DHD_WQ_WORK_IF_UPDATE) {
++ DHD_ERROR(("%s: unexpected event \n", __FUNCTION__));
++ return;
++ }
++
++ if (!dhd) {
++ DHD_ERROR(("%s: dhd info not available \n", __FUNCTION__));
++ return;
++ }
++
++ if (!if_event) {
++ DHD_ERROR(("%s: event data is null \n", __FUNCTION__));
++ return;
++ }
++
++ dhd_net_if_lock_local(dhd);
++ DHD_OS_WAKE_LOCK(&dhd->pub);
++
++ ifidx = if_event->event.ifidx;
++ DHD_TRACE(("%s: Update interface with idx %d\n", __FUNCTION__, ifidx));
++
++ dhd_op_if_update(&dhd->pub, ifidx);
++
++ MFREE(dhd->pub.osh, if_event, sizeof(dhd_if_event_t));
++
++ DHD_OS_WAKE_UNLOCK(&dhd->pub);
++ dhd_net_if_unlock_local(dhd);
++}
++
++int dhd_op_if_update(dhd_pub_t *dhdpub, int ifidx)
++{
++ dhd_info_t * dhdinfo = NULL;
++ dhd_if_t * ifp = NULL;
++ int ret = 0;
++ char buf[128];
++
++ if ((NULL==dhdpub)||(NULL==dhdpub->info)) {
++ DHD_ERROR(("%s: *** DHD handler is NULL!\n", __FUNCTION__));
++ return -1;
++ } else {
++ dhdinfo = (dhd_info_t *)dhdpub->info;
++ ifp = dhdinfo->iflist[ifidx];
++ if (NULL==ifp) {
++ DHD_ERROR(("%s: *** ifp handler is NULL!\n", __FUNCTION__));
++ return -2;
++ }
++ }
++
++ DHD_TRACE(("%s: idx %d\n", __FUNCTION__, ifidx));
++ // Get MAC address
++ strcpy(buf, "cur_etheraddr");
++ ret = dhd_wl_ioctl_cmd(&dhdinfo->pub, WLC_GET_VAR, buf, sizeof(buf), FALSE, ifp->idx);
++ if (0>ret) {
++ DHD_ERROR(("Failed to upudate the MAC address for itf=%s, ret=%d\n", ifp->name, ret));
++ // avoid collision
++ dhdinfo->iflist[ifp->idx]->mac_addr[5] += 1;
++ // force locally administrate address
++ ETHER_SET_LOCALADDR(&dhdinfo->iflist[ifp->idx]->mac_addr);
++ } else {
++ DHD_EVENT(("Got mac for itf %s, idx %d, MAC=%02X:%02X:%02X:%02X:%02X:%02X\n",
++ ifp->name, ifp->idx,
++ (unsigned char)buf[0], (unsigned char)buf[1], (unsigned char)buf[2],
++ (unsigned char)buf[3], (unsigned char)buf[4], (unsigned char)buf[5]));
++ memcpy(dhdinfo->iflist[ifp->idx]->mac_addr, buf, ETHER_ADDR_LEN);
++ if (dhdinfo->iflist[ifp->idx]->net) {
++ memcpy(dhdinfo->iflist[ifp->idx]->net->dev_addr, buf, ETHER_ADDR_LEN);
++ }
++ }
++
++ return ret;
++}
++#endif /* DHD_UPDATE_INTF_MAC */
++
+ static void
+ dhd_set_mac_addr_handler(void *handle, void *event_info, u8 event)
+ {
+@@ -4536,7 +4638,11 @@ dhd_os_wlfc_block(dhd_pub_t *pub)
+ /* terence 20161229: don't do spin lock if proptx not enabled */
+ if (disable_proptx)
+ return 1;
++#ifdef BCMDBUS
++ spin_lock_irqsave(&di->wlfc_spinlock, di->wlfc_lock_flags);
++#else
+ spin_lock_bh(&di->wlfc_spinlock);
++#endif /* BCMDBUS */
+ return 1;
+ }
+
+@@ -4549,7 +4655,11 @@ dhd_os_wlfc_unblock(dhd_pub_t *pub)
+ /* terence 20161229: don't do spin lock if proptx not enabled */
+ if (disable_proptx)
+ return 1;
++#ifdef BCMDBUS
++ spin_unlock_irqrestore(&di->wlfc_spinlock, di->wlfc_lock_flags);
++#else
+ spin_unlock_bh(&di->wlfc_spinlock);
++#endif /* BCMDBUS */
+ return 1;
+ }
+
+@@ -4885,6 +4995,10 @@ __dhd_sendpkt(dhd_pub_t *dhdp, int ifidx, void *pktbuf)
+ ret = dhd_bus_txdata(dhdp->bus, pktbuf);
+ #endif /* BCMPCIE */
+ #endif /* PROP_TXSTATUS */
++#ifdef BCMDBUS
++ if (ret)
++ PKTCFREE(dhdp->osh, pktbuf, TRUE);
++#endif /* BCMDBUS */
+
+ return ret;
+ }
+@@ -5054,6 +5168,7 @@ dhd_start_xmit(struct sk_buff *skb, struct net_device *net)
+ __FUNCTION__, dhd->pub.busstate, dhd->pub.dhd_bus_busy_state));
+ }
+ #endif
++
+ DHD_OS_WAKE_LOCK(&dhd->pub);
+ DHD_PERIM_LOCK_TRY(DHD_FWDER_UNIT(dhd), lock_taken);
+
+@@ -5070,7 +5185,7 @@ dhd_start_xmit(struct sk_buff *skb, struct net_device *net)
+ __FUNCTION__, dhd->pub.up, dhd->pub.busstate));
+ netif_stop_queue(net);
+ /* Send Event when bus down detected during data session */
+- if (dhd->pub.up && !dhd->pub.hang_was_sent) {
++ if (dhd->pub.up && !dhd->pub.hang_was_sent && !DHD_BUS_CHECK_REMOVE(&dhd->pub)) {
+ DHD_ERROR(("%s: Event HANG sent up\n", __FUNCTION__));
+ dhd->pub.hang_reason = HANG_REASON_BUS_DOWN;
+ net_os_send_hang_message(net);
+@@ -5656,8 +5771,12 @@ dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt, uint8 chan)
+ continue;
+ }
+ #ifdef DHD_WAKE_STATUS
++#ifdef BCMDBUS
++ wcp = NULL;
++#else
+ pkt_wake = dhd_bus_get_bus_wake(dhdp);
+ wcp = dhd_bus_get_wakecount(dhdp);
++#endif /* BCMDBUS */
+ if (wcp == NULL) {
+ /* If wakeinfo count buffer is null do not update wake count values */
+ pkt_wake = 0;
+@@ -6099,8 +6218,10 @@ dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt, uint8 chan)
+ #endif /* DHD_WAKE_STATUS */
+ }
+
++#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)
+ if (ifp->net)
+ ifp->net->last_rx = jiffies;
++#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0) */
+
+ if (ntoh16(skb->protocol) != ETHER_TYPE_BRCM) {
+ dhdp->dstats.rx_bytes += skb->len;
+@@ -6247,6 +6368,7 @@ error:
+ return &net->stats;
+ }
+
++#ifndef BCMDBUS
+ static int
+ dhd_watchdog_thread(void *data)
+ {
+@@ -6309,9 +6431,19 @@ dhd_watchdog_thread(void *data)
+ complete_and_exit(&tsk->completed, 0);
+ }
+
+-static void dhd_watchdog(ulong data)
++static void dhd_watchdog(
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
++ struct timer_list *t
++#else
++ ulong data
++#endif
++)
+ {
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
++ dhd_info_t *dhd = from_timer(dhd, t, timer);
++#else
+ dhd_info_t *dhd = (dhd_info_t *)data;
++#endif
+ unsigned long flags;
+
+ if (dhd->pub.dongle_reset) {
+@@ -6391,9 +6523,19 @@ dhd_rpm_state_thread(void *data)
+ complete_and_exit(&tsk->completed, 0);
+ }
+
+-static void dhd_runtimepm(ulong data)
++static void dhd_runtimepm(
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
++ struct timer_list *t
++#else
++ ulong data
++#endif
++)
+ {
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
++ dhd_info_t *dhd = from_timer(dhd, t, rpm_timer);
++#else
+ dhd_info_t *dhd = (dhd_info_t *)data;
++#endif
+
+ if (dhd->pub.dongle_reset) {
+ return;
+@@ -6457,6 +6599,7 @@ exit:
+ return 0;
+ }
+ #endif /* DEBUG_CPU_FREQ */
++
+ static int
+ dhd_dpc_thread(void *data)
+ {
+@@ -6738,6 +6881,7 @@ dhd_sched_dpc(dhd_pub_t *dhdp)
+ tasklet_schedule(&dhd->tasklet);
+ }
+ }
++#endif /* BCMDBUS */
+
+ static void
+ dhd_sched_rxf(dhd_pub_t *dhdp, void *skb)
+@@ -7008,12 +7152,12 @@ static bool dhd_check_hang(struct net_device *net, dhd_pub_t *dhdp, int error)
+ if (!dhdp->up)
+ return FALSE;
+
+-#if !defined(BCMPCIE)
++#if !defined(BCMPCIE) && !defined(BCMDBUS)
+ if (dhdp->info->thr_dpc_ctl.thr_pid < 0) {
+ DHD_ERROR(("%s : skipped due to negative pid - unloading?\n", __FUNCTION__));
+ return FALSE;
+ }
+-#endif
++#endif /* !BCMPCIE && !BCMDBUS */
+
+ if ((error == -ETIMEDOUT) || (error == -EREMOTEIO) ||
+ ((dhdp->busstate == DHD_BUS_DOWN) && (!dhdp->dongle_reset))) {
+@@ -7387,6 +7531,7 @@ int dhd_ioctl_process(dhd_pub_t *pub, int ifidx, dhd_ioctl_t *ioc, void *data_bu
+ if (data_buf)
+ buflen = MIN(ioc->len, WLC_IOCTL_MAXLEN);
+
++#ifndef BCMDBUS
+ /* send to dongle (must be up, and wl). */
+ if (pub->busstate == DHD_BUS_DOWN || pub->busstate == DHD_BUS_LOAD) {
+ if ((!pub->dongle_trap_occured) && allow_delay_fwdl) {
+@@ -7412,6 +7557,7 @@ int dhd_ioctl_process(dhd_pub_t *pub, int ifidx, dhd_ioctl_t *ioc, void *data_bu
+ bcmerror = BCME_DONGLE_DOWN;
+ goto done;
+ }
++#endif /* !BCMDBUS */
+
+ /*
+ * Flush the TX queue if required for proper message serialization:
+@@ -8230,7 +8376,7 @@ exit:
+ #else
+ wl_android_wifi_off(net, TRUE);
+ #ifdef WL_EXT_IAPSTA
+- wl_android_ext_dettach_netdev();
++ wl_ext_iapsta_dettach_netdev();
+ #endif
+ } else {
+ if (dhd->pub.conf->deepsleep)
+@@ -8304,6 +8450,10 @@ dhd_open(struct net_device *net)
+ uint32 slot_num = -1;
+ wifi_adapter_info_t *adapter = NULL;
+ #endif
++#if defined(WL_EXT_IAPSTA) && defined(ISAM_PREINIT)
++ int bytes_written = 0;
++ struct dhd_conf *conf;
++#endif
+
+ if (!dhd_download_fw_on_driverload) {
+ if (!dhd_driver_init_done) {
+@@ -8313,14 +8463,7 @@ dhd_open(struct net_device *net)
+ }
+
+ printf("%s: Enter %p\n", __FUNCTION__, net);
+-#if defined(MULTIPLE_SUPPLICANT)
+-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1
+- if (mutex_is_locked(&_dhd_sdio_mutex_lock_) != 0) {
+- DHD_ERROR(("%s : dhd_open: call dev open before insmod complete!\n", __FUNCTION__));
+- }
+- mutex_lock(&_dhd_sdio_mutex_lock_);
+-#endif
+-#endif /* MULTIPLE_SUPPLICANT */
++ DHD_MUTEX_LOCK();
+ /* Init wakelock */
+ if (!dhd_download_fw_on_driverload) {
+ if (!(dhd->dhd_state & DHD_ATTACH_STATE_WAKELOCKS_INIT)) {
+@@ -8406,6 +8549,9 @@ dhd_open(struct net_device *net)
+ atomic_set(&dhd->pend_8021x_cnt, 0);
+ 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
+@@ -8421,6 +8567,14 @@ dhd_open(struct net_device *net)
+ ret = -1;
+ goto exit;
+ }
++#if defined(WL_EXT_IAPSTA) && defined(ISAM_PREINIT)
++ conf = dhd_get_conf(net);
++ if (conf) {
++ wl_android_ext_priv_cmd(net, conf->isam_init, 0, &bytes_written);
++ wl_android_ext_priv_cmd(net, conf->isam_config, 0, &bytes_written);
++ wl_android_ext_priv_cmd(net, conf->isam_enable, 0, &bytes_written);
++ }
++#endif
+ }
+ #ifdef FIX_CPU_MIN_CLOCK
+ if (dhd_get_fw_mode(dhd) == DHD_FLAG_HOSTAP_MODE) {
+@@ -8440,7 +8594,24 @@ dhd_open(struct net_device *net)
+ #endif
+
+ if (dhd->pub.busstate != DHD_BUS_DATA) {
+-
++#ifdef BCMDBUS
++ dhd_set_path(&dhd->pub);
++ DHD_MUTEX_UNLOCK();
++ wait_event_interruptible_timeout(dhd->adapter->status_event,
++ wifi_get_adapter_status(dhd->adapter, WIFI_STATUS_FW_READY),
++ msecs_to_jiffies(DHD_FW_READY_TIMEOUT));
++ DHD_MUTEX_LOCK();
++ if ((ret = dbus_up(dhd->pub.bus)) != 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;
++ }
++#else
+ /* try to bring up bus */
+ DHD_PERIM_UNLOCK(&dhd->pub);
+ ret = dhd_bus_start(&dhd->pub);
+@@ -8450,8 +8621,12 @@ dhd_open(struct net_device *net)
+ ret = -1;
+ goto exit;
+ }
++#endif /* !BCMDBUS */
+
+ }
++#ifdef WL_EXT_IAPSTA
++ wl_ext_iapsta_attach_name(net, ifidx);
++#endif
+ if (dhd_download_fw_on_driverload) {
+ if (dhd->pub.conf->deepsleep)
+ dhd_deepsleep(dhd, 0);
+@@ -8560,9 +8735,6 @@ dhd_open(struct net_device *net)
+ }
+
+ argos_register_notifier_init(net);
+-#if defined(DHDTCPACK_SUPPRESS)
+- dhd_tcpack_suppress_set(&dhd->pub, TCPACK_SUP_DEFAULT);
+-#endif /* DHDTCPACK_SUPPRESS */
+ #if defined(NUM_SCB_MAX_PROBE)
+ dhd_set_scb_probe(&dhd->pub);
+ #endif /* NUM_SCB_MAX_PROBE */
+@@ -8586,12 +8758,7 @@ exit:
+
+ DHD_PERIM_UNLOCK(&dhd->pub);
+ DHD_OS_WAKE_UNLOCK(&dhd->pub);
+-
+-#if defined(MULTIPLE_SUPPLICANT)
+-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1
+- mutex_unlock(&_dhd_sdio_mutex_lock_);
+-#endif
+-#endif /* MULTIPLE_SUPPLICANT */
++ DHD_MUTEX_UNLOCK();
+
+ printf("%s: Exit ret=%d\n", __FUNCTION__, ret);
+ return ret;
+@@ -8606,14 +8773,7 @@ int dhd_do_driver_init(struct net_device *net)
+ return -EINVAL;
+ }
+
+-#ifdef MULTIPLE_SUPPLICANT
+-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1 && defined(BCMSDIO)
+- if (mutex_is_locked(&_dhd_sdio_mutex_lock_) != 0) {
+- DHD_ERROR(("%s : dhdsdio_probe is already running!\n", __FUNCTION__));
+- return 0;
+- }
+-#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) */
+-#endif /* MULTIPLE_SUPPLICANT */
++ DHD_MUTEX_IS_LOCK_RETURN();
+
+ /* && defined(OEM_ANDROID) && defined(BCMSDIO) */
+ dhd = DHD_DEV_INFO(net);
+@@ -8700,10 +8860,36 @@ dhd_event_ifdel(dhd_info_t *dhdinfo, wl_event_data_if_t *ifevent, char *name, ui
+ int
+ dhd_event_ifchange(dhd_info_t *dhdinfo, wl_event_data_if_t *ifevent, char *name, uint8 *mac)
+ {
++#ifdef DHD_UPDATE_INTF_MAC
++ dhd_if_event_t *if_event;
++#endif /* DHD_UPDATE_INTF_MAC */
++
+ #ifdef WL_CFG80211
+ wl_cfg80211_notify_ifchange(dhd_linux_get_primary_netdev(&dhdinfo->pub),
+ ifevent->ifidx, name, mac, ifevent->bssidx);
+ #endif /* WL_CFG80211 */
++
++#ifdef DHD_UPDATE_INTF_MAC
++ /* handle IF event caused by wl commands, SoftAP, WEXT, MBSS and
++ * anything else
++ */
++ if_event = MALLOC(dhdinfo->pub.osh, sizeof(dhd_if_event_t));
++ if (if_event == NULL) {
++ DHD_ERROR(("dhd_event_ifdel: malloc failed for if_event, malloced %d bytes",
++ MALLOCED(dhdinfo->pub.osh)));
++ return BCME_NOMEM;
++ }
++ memcpy(&if_event->event, ifevent, sizeof(if_event->event));
++ // construct a change event
++ if_event->event.ifidx = dhd_ifname2idx(dhdinfo, name);
++ if_event->event.opcode = WLC_E_IF_CHANGE;
++ memcpy(if_event->mac, mac, ETHER_ADDR_LEN);
++ strncpy(if_event->name, name, IFNAMSIZ);
++ if_event->name[IFNAMSIZ - 1] = '\0';
++ dhd_deferred_schedule_work(dhdinfo->dhd_deferred_wq, (void *)if_event, DHD_WQ_WORK_IF_UPDATE,
++ dhd_ifupdate_event_handler, DHD_WQ_WORK_PRIORITY_LOW);
++#endif /* DHD_UPDATE_INTF_MAC */
++
+ return BCME_OK;
+ }
+
+@@ -8788,12 +8974,26 @@ dhd_allocate_if(dhd_pub_t *dhdpub, int ifidx, const char *name,
+ }
+
+ #ifdef WL_CFG80211
+- if (ifidx == 0)
++ if (ifidx == 0) {
++#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)
+ ifp->net->destructor = free_netdev;
+- else
++#else
++ ifp->net->needs_free_netdev = true;
++#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) */
++ } else {
++#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)
+ ifp->net->destructor = dhd_netdev_free;
+ #else
++ ifp->net->needs_free_netdev = true;
++ ifp->net->priv_destructor = dhd_netdev_free;
++#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) */
++ }
++#else
++#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)
+ ifp->net->destructor = free_netdev;
++#else
++ ifp->net->needs_free_netdev = true;
++#endif
+ #endif /* WL_CFG80211 */
+ strncpy(ifp->name, ifp->net->name, IFNAMSIZ);
+ ifp->name[IFNAMSIZ - 1] = '\0';
+@@ -9173,20 +9373,61 @@ fail1:
+
+ #endif /* SHOW_LOGTRACE */
+
++#ifdef BCMDBUS
++uint
++dhd_get_rxsz(dhd_pub_t *pub)
++{
++ struct net_device *net = NULL;
++ dhd_info_t *dhd = NULL;
++ uint rxsz;
++
++ /* Assign rxsz for dbus_attach */
++ dhd = pub->info;
++ net = dhd->iflist[0]->net;
++ net->hard_header_len = ETH_HLEN + pub->hdrlen;
++ rxsz = DBUS_RX_BUFFER_SIZE_DHD(net);
++
++ return rxsz;
++}
++
++void
++dhd_set_path(dhd_pub_t *pub)
++{
++ dhd_info_t *dhd = NULL;
++
++ dhd = pub->info;
++
++ /* try to download image and nvram to the dongle */
++ if (dhd_update_fw_nv_path(dhd) && dhd->pub.bus) {
++ DHD_INFO(("%s: fw %s, nv %s, conf %s\n",
++ __FUNCTION__, dhd->fw_path, dhd->nv_path, dhd->conf_path));
++ dhd_bus_update_fw_nv_path(dhd->pub.bus,
++ dhd->fw_path, dhd->nv_path, dhd->clm_path, dhd->conf_path);
++ }
++}
++#endif
+
+ dhd_pub_t *
+-dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen)
++dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen
++#ifdef BCMDBUS
++ , void *data
++#endif
++)
+ {
+ dhd_info_t *dhd = NULL;
+ struct net_device *net = NULL;
+ char if_name[IFNAMSIZ] = {'\0'};
+- uint32 bus_type = -1;
+- uint32 bus_num = -1;
+- uint32 slot_num = -1;
+ #ifdef SHOW_LOGTRACE
+ int ret;
+ #endif /* SHOW_LOGTRACE */
++#if defined(BCMSDIO) || defined(BCMPCIE)
++ uint32 bus_type = -1;
++ uint32 bus_num = -1;
++ uint32 slot_num = -1;
+ wifi_adapter_info_t *adapter = NULL;
++#elif defined(BCMDBUS)
++ wifi_adapter_info_t *adapter = data;
++#endif
+
+ dhd_attach_states_t dhd_state = DHD_ATTACH_STATE_INIT;
+ DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+@@ -9198,7 +9439,9 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen)
+ #if defined(BCMSDIO)
+ dhd_bus_get_ids(bus, &bus_type, &bus_num, &slot_num);
+ #endif
++#if defined(BCMSDIO) || defined(BCMPCIE)
+ adapter = dhd_wifi_platform_get_adapter(bus_type, bus_num, slot_num);
++#endif
+
+ /* Allocate primary dhd_info */
+ dhd = wifi_platform_prealloc(adapter, DHD_PREALLOC_DHD_INFO, sizeof(dhd_info_t));
+@@ -9219,6 +9462,7 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen)
+ dll_init(&(dhd->pub.dump_iovlist_head));
+ #endif /* DUMP_IOCTL_IOV_LIST */
+ dhd->adapter = adapter;
++ dhd->pub.adapter = (void *)adapter;
+ #ifdef DHD_DEBUG
+ dll_init(&(dhd->pub.mw_list_head));
+ #endif /* DHD_DEBUG */
+@@ -9239,6 +9483,7 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen)
+ dhd->pub.dhd_cspec.country_abbrev, &dhd->pub.dhd_cspec,
+ dhd->pub.dhd_cflags);
+ #endif /* CUSTOM_COUNTRY_CODE */
++#ifndef BCMDBUS
+ dhd->thr_dpc_ctl.thr_pid = DHD_PID_KT_TL_INVALID;
+ dhd->thr_wdt_ctl.thr_pid = DHD_PID_KT_INVALID;
+ #ifdef DHD_WET
+@@ -9246,6 +9491,7 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen)
+ #endif /* DHD_WET */
+ /* Initialize thread based operation and lock */
+ sema_init(&dhd->sdsem, 1);
++#endif /* !BCMDBUS */
+
+ /* Link to info module */
+ dhd->pub.info = dhd;
+@@ -9262,9 +9508,11 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen)
+ DHD_ERROR(("dhd_conf_attach failed\n"));
+ goto fail;
+ }
++#ifndef BCMDBUS
+ 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
+@@ -9458,10 +9706,15 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen)
+
+
+
++#ifndef BCMDBUS
+ /* Set up the watchdog timer */
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
++ timer_setup(&dhd->timer, dhd_watchdog, 0);
++#else
+ init_timer(&dhd->timer);
+ dhd->timer.data = (ulong)dhd;
+ dhd->timer.function = dhd_watchdog;
++#endif
+ dhd->default_wd_interval = dhd_watchdog_ms;
+
+ if (dhd_watchdog_prio >= 0) {
+@@ -9477,9 +9730,13 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen)
+
+ #ifdef DHD_PCIE_RUNTIMEPM
+ /* Setup up the runtime PM Idlecount timer */
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
++ timer_setup(&dhd->rpm_timer, dhd_runtimepm, 0);
++#else
+ init_timer(&dhd->rpm_timer);
+ dhd->rpm_timer.data = (ulong)dhd;
+ dhd->rpm_timer.function = dhd_runtimepm;
++#endif
+ dhd->rpm_timer_valid = FALSE;
+
+ dhd->thr_rpm_ctl.thr_pid = DHD_PID_KT_INVALID;
+@@ -9492,9 +9749,6 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen)
+ #ifdef DEBUGGER
+ debugger_init((void *) bus);
+ #endif
+-#ifdef SHOW_LOGTRACE
+- skb_queue_head_init(&dhd->evt_trace_queue);
+-#endif /* SHOW_LOGTRACE */
+
+ /* Set up the bottom half handler */
+ if (dhd_dpc_prio >= 0) {
+@@ -9517,6 +9771,10 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen)
+ goto fail;
+ }
+ }
++#endif /* !BCMDBUS */
++#ifdef SHOW_LOGTRACE
++ skb_queue_head_init(&dhd->evt_trace_queue);
++#endif /* SHOW_LOGTRACE */
+
+ dhd_state |= DHD_ATTACH_STATE_THREADS_CREATED;
+
+@@ -10046,6 +10304,7 @@ int dhd_download_btfw(wlan_bt_handle_t handle, char* btfw_path)
+ } EXPORT_SYMBOL(dhd_download_btfw);
+ #endif /* defined (BT_OVER_SDIO) */
+
++#ifndef BCMDBUS
+ int
+ dhd_bus_start(dhd_pub_t *dhdp)
+ {
+@@ -10227,6 +10486,7 @@ dhd_bus_start(dhd_pub_t *dhdp)
+ DHD_PERIM_UNLOCK(dhdp);
+ return 0;
+ }
++#endif /* !BCMDBUS */
+
+ #ifdef WLTDLS
+ int _dhd_tdls_enable(dhd_pub_t *dhd, bool tdls_on, bool auto_on, struct ether_addr *mac)
+@@ -10582,9 +10842,6 @@ dhd_preinit_ioctls(dhd_pub_t *dhd)
+ char eventmask[WL_EVENTING_MASK_LEN];
+ char iovbuf[WL_EVENTING_MASK_LEN + 12]; /* Room for "event_msgs" + '\0' + bitvec */
+ uint32 buf_key_b4_m4 = 1;
+-#ifndef WL_CFG80211
+- u32 up = 0;
+-#endif
+ uint8 msglen;
+ eventmsgs_ext_t *eventmask_msg = NULL;
+ char* iov_buf = NULL;
+@@ -10604,7 +10861,7 @@ dhd_preinit_ioctls(dhd_pub_t *dhd)
+ #endif
+ shub_control_t shub_ctl;
+
+-#if defined(BCMSDIO)
++#if defined(BCMSDIO) || defined(BCMDBUS)
+ #ifdef PROP_TXSTATUS
+ int wlfc_enable = TRUE;
+ #ifndef DISABLE_11N
+@@ -10612,7 +10869,7 @@ dhd_preinit_ioctls(dhd_pub_t *dhd)
+ uint wl_down = 1;
+ #endif /* DISABLE_11N */
+ #endif /* PROP_TXSTATUS */
+-#endif
++#endif /* BCMSDIO || BCMDBUS */
+ #ifndef PCIE_FULL_DONGLE
+ uint32 wl_ap_isolate;
+ #endif /* PCIE_FULL_DONGLE */
+@@ -10632,7 +10889,7 @@ dhd_preinit_ioctls(dhd_pub_t *dhd)
+ #if defined(CUSTOMER_HW2) && defined(USE_WL_CREDALL)
+ uint32 credall = 1;
+ #endif
+- uint bcn_timeout = dhd->conf->bcn_timeout;
++ uint bcn_timeout = CUSTOM_BCN_TIMEOUT;
+ uint scancache_enab = TRUE;
+ #ifdef ENABLE_BCN_LI_BCN_WAKEUP
+ uint32 bcn_li_bcn = 1;
+@@ -10744,7 +11001,6 @@ dhd_preinit_ioctls(dhd_pub_t *dhd)
+ #endif /* CUSTOM_SET_OCLOFF */
+ DHD_TRACE(("Enter %s\n", __FUNCTION__));
+
+- dhd_conf_set_intiovar(dhd, WLC_SET_BAND, "WLC_SET_BAND", dhd->conf->band, 0, FALSE);
+ #ifdef DHDTCPACK_SUPPRESS
+ printf("%s: Set tcpack_sup_mode %d\n", __FUNCTION__, dhd->conf->tcpack_sup_mode);
+ dhd_tcpack_suppress_set(dhd, dhd->conf->tcpack_sup_mode);
+@@ -11021,17 +11277,11 @@ dhd_preinit_ioctls(dhd_pub_t *dhd)
+ #endif
+ /* Set Country code */
+ if (dhd->dhd_cspec.ccode[0] != 0) {
+- printf("Set country %s, revision %d\n", dhd->dhd_cspec.ccode, dhd->dhd_cspec.rev);
+ ret = dhd_iovar(dhd, 0, "country", (char *)&dhd->dhd_cspec, sizeof(wl_country_t),
+ NULL, 0, TRUE);
+ if (ret < 0)
+- printf("%s: country code setting failed %d\n", __FUNCTION__, ret);
+- } else {
+- dhd_conf_set_country(dhd);
+- 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 */
+@@ -11068,7 +11318,6 @@ dhd_preinit_ioctls(dhd_pub_t *dhd)
+ if (ret < 0)
+ DHD_ERROR(("%s: roam fullscan period set failed %d\n", __FUNCTION__, ret));
+ #endif /* ROAM_ENABLE */
+- dhd_conf_set_roam(dhd);
+
+ #ifdef CUSTOM_EVENT_PM_WAKE
+ ret = dhd_iovar(dhd, 0, "const_awake_thresh", (char *)&pm_awake_thresh,
+@@ -11104,7 +11353,6 @@ dhd_preinit_ioctls(dhd_pub_t *dhd)
+ }
+ }
+ #endif /* DHD_ENABLE_LPC */
+- dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "lpc", dhd->conf->lpc, 0, TRUE);
+
+ #ifdef WLADPS
+ #ifdef WLADPS_SEAK_AP_WAR
+@@ -11123,10 +11371,7 @@ dhd_preinit_ioctls(dhd_pub_t *dhd)
+ #endif /* WLADPS */
+
+ /* Set PowerSave mode */
+- if (dhd->conf->pm >= 0)
+- power_mode = dhd->conf->pm;
+ (void) dhd_wl_ioctl_cmd(dhd, WLC_SET_PM, (char *)&power_mode, sizeof(power_mode), TRUE, 0);
+- dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "pm2_sleep_ret", dhd->conf->pm2_sleep_ret, 0, FALSE);
+
+ #if defined(BCMSDIO)
+ /* Match Host and Dongle rx alignment */
+@@ -11159,27 +11404,6 @@ dhd_preinit_ioctls(dhd_pub_t *dhd)
+ dhd_iovar(dhd, 0, "apsta", (char *)&apsta, sizeof(apsta), NULL, 0, TRUE);
+
+ #endif /* defined(AP) && !defined(WLP2P) */
+- /* 0:HT20 in ALL, 1:HT40 in ALL, 2: HT20 in 2G HT40 in 5G */
+- dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "mimo_bw_cap", dhd->conf->mimo_bw_cap, 0, TRUE);
+- dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "force_wme_ac", dhd->conf->force_wme_ac, 1, FALSE);
+- dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "stbc_tx", dhd->conf->stbc, 0, FALSE);
+- dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "stbc_rx", dhd->conf->stbc, 0, FALSE);
+- dhd_conf_set_intiovar(dhd, WLC_SET_SRL, "WLC_SET_SRL", dhd->conf->srl, 0, TRUE);
+- dhd_conf_set_intiovar(dhd, WLC_SET_LRL, "WLC_SET_LRL", dhd->conf->lrl, 0, FALSE);
+- dhd_conf_set_intiovar(dhd, WLC_SET_SPECT_MANAGMENT, "WLC_SET_SPECT_MANAGMENT", dhd->conf->spect, 0, FALSE);
+- dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "rsdb_mode", dhd->conf->rsdb_mode, -1, TRUE);
+- dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "vhtmode", dhd->conf->vhtmode, 0, TRUE);
+-#ifdef IDHCP
+- dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "dhcpc_enable", dhd->conf->dhcpc_enable, 0, FALSE);
+- if(dhd->conf->dhcpd_enable >= 0){
+- dhd_conf_set_bufiovar(dhd, WLC_SET_VAR, "dhcpd_ip_addr", (char *)&dhd->conf->dhcpd_ip_addr, sizeof(dhd->conf->dhcpd_ip_addr), FALSE);
+- dhd_conf_set_bufiovar(dhd, WLC_SET_VAR, "dhcpd_ip_mask", (char *)&dhd->conf->dhcpd_ip_mask, sizeof(dhd->conf->dhcpd_ip_mask), FALSE);
+- dhd_conf_set_bufiovar(dhd, WLC_SET_VAR, "dhcpd_ip_start", (char *)&dhd->conf->dhcpd_ip_start, sizeof(dhd->conf->dhcpd_ip_start), FALSE);
+- dhd_conf_set_bufiovar(dhd, WLC_SET_VAR, "dhcpd_ip_end", (char *)&dhd->conf->dhcpd_ip_end, sizeof(dhd->conf->dhcpd_ip_end), FALSE);
+- dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "dhcpd_enable", dhd->conf->dhcpd_enable, 0, FALSE);
+- }
+-#endif
+- dhd_conf_set_bw_cap(dhd);
+
+ #ifdef MIMO_ANT_SETTING
+ dhd_sel_ant_from_file(dhd);
+@@ -11214,7 +11438,6 @@ dhd_preinit_ioctls(dhd_pub_t *dhd)
+ DHD_ERROR(("%s Set txbf failed %d\n", __FUNCTION__, ret));
+
+ #endif /* USE_WL_TXBF */
+- dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "txbf", dhd->conf->txbf, 0, FALSE);
+
+ ret = dhd_iovar(dhd, 0, "scancache", (char *)&scancache_enab, sizeof(scancache_enab), NULL,
+ 0, TRUE);
+@@ -11251,7 +11474,6 @@ dhd_preinit_ioctls(dhd_pub_t *dhd)
+ sizeof(frameburst), TRUE, 0)) < 0) {
+ DHD_INFO(("%s frameburst not supported %d\n", __FUNCTION__, ret));
+ }
+- dhd_conf_set_intiovar(dhd, WLC_SET_FAKEFRAG, "WLC_SET_FAKEFRAG", dhd->conf->frameburst, 0, FALSE);
+
+ iov_buf = (char*)kmalloc(WLC_IOCTL_SMLEN, GFP_KERNEL);
+ if (iov_buf == NULL) {
+@@ -11275,7 +11497,6 @@ dhd_preinit_ioctls(dhd_pub_t *dhd)
+ }
+ }
+ #endif
+- dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "ampdu_ba_wsize", dhd->conf->ampdu_ba_wsize, 1, FALSE);
+
+ #ifdef ENABLE_TEMP_THROTTLING
+ if (dhd->op_mode & DHD_FLAG_STA_MODE) {
+@@ -11694,9 +11915,7 @@ dhd_preinit_ioctls(dhd_pub_t *dhd)
+ bcmstrtok(&ptr, "\n", 0);
+ strncpy(fw_version, buf, FW_VER_STR_LEN);
+ fw_version[FW_VER_STR_LEN-1] = '\0';
+-#if defined(BCMSDIO) || defined(BCMPCIE)
+ dhd_set_version_info(dhd, buf);
+-#endif /* BCMSDIO || BCMPCIE */
+ #ifdef WRITE_WLANINFO
+ sec_save_wlinfo(buf, EPI_VERSION_STR, dhd->info->nv_path, clm_version);
+ #endif /* WRITE_WLANINFO */
+@@ -11707,11 +11926,9 @@ dhd_preinit_ioctls(dhd_pub_t *dhd)
+
+ #if defined(BCMSDIO)
+ dhd_txglom_enable(dhd, dhd->conf->bus_rxglom);
+- // terence 20151210: set bus:txglom after dhd_txglom_enable since it's possible changed in dhd_conf_set_txglom_params
+- dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "bus:txglom", dhd->conf->bus_txglom, 0, FALSE);
+ #endif /* defined(BCMSDIO) */
+
+-#if defined(BCMSDIO)
++#if defined(BCMSDIO) || defined(BCMDBUS)
+ #ifdef PROP_TXSTATUS
+ if (disable_proptx ||
+ #ifdef PROP_TXSTATUS_VSDB
+@@ -11784,8 +12001,7 @@ dhd_preinit_ioctls(dhd_pub_t *dhd)
+ printf("%s: not define PROP_TXSTATUS\n", __FUNCTION__);
+ dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "ampdu_hostreorder", 0, 0, TRUE);
+ #endif /* PROP_TXSTATUS */
+- dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "ampdu_hostreorder", dhd->conf->ampdu_hostreorder, 0, TRUE);
+-#endif /* BCMSDIO || BCMBUS */
++#endif /* BCMSDIO || BCMDBUS */
+ #ifndef PCIE_FULL_DONGLE
+ /* For FD we need all the packets at DHD to handle intra-BSS forwarding */
+ if (FW_SUPPORTED(dhd, ap)) {
+@@ -11812,9 +12028,6 @@ dhd_preinit_ioctls(dhd_pub_t *dhd)
+ #ifdef WL11U
+ dhd_interworking_enable(dhd);
+ #endif /* WL11U */
+-#ifndef WL_CFG80211
+- dhd_wl_ioctl_cmd(dhd, WLC_UP, (char *)&up, sizeof(up), TRUE, 0);
+-#endif
+
+ #ifdef SUPPORT_SENSORHUB
+ DHD_ERROR(("%s: SensorHub enabled %d\n",
+@@ -11897,6 +12110,7 @@ dhd_preinit_ioctls(dhd_pub_t *dhd)
+ DHD_ERROR(("failed to set WNM capabilities\n"));
+ }
+
++ dhd_conf_postinit_ioctls(dhd);
+ done:
+
+ if (eventmask_msg)
+@@ -12448,13 +12662,27 @@ dhd_register_if(dhd_pub_t *dhdp, int ifidx, bool need_rtnl_lock)
+
+ dhd->pub.rxsz = DBUS_RX_BUFFER_SIZE_DHD(net);
+
++#ifdef WLMESH
++ if (ifidx >= 2 && dhdp->conf->fw_type == FW_TYPE_MESH) {
++ temp_addr[4] ^= 0x80;
++ temp_addr[4] += ifidx;
++ temp_addr[5] += ifidx;
++ }
++#endif
+ memcpy(net->dev_addr, temp_addr, ETHER_ADDR_LEN);
+
+ if (ifidx == 0)
+ printf("%s\n", dhd_version);
+ #ifdef WL_EXT_IAPSTA
+- else if (!strncmp(net->name, "wl0.", strlen("wl0."))) {
+- wl_android_ext_attach_netdev(net, ifidx);
++ else
++ wl_ext_iapsta_attach_netdev(net, ifidx);
++#endif
++#ifdef WLMESH
++ if (ifidx != 0 && dhdp->conf->fw_type == FW_TYPE_MESH) {
++ if (_dhd_set_mac_address(dhd, ifidx, temp_addr) == 0)
++ DHD_INFO(("%s: MACID is overwritten\n", __FUNCTION__));
++ else
++ DHD_ERROR(("%s: _dhd_set_mac_address() failed\n", __FUNCTION__));
+ }
+ #endif
+
+@@ -12467,6 +12695,11 @@ dhd_register_if(dhd_pub_t *dhdp, int ifidx, bool need_rtnl_lock)
+ DHD_ERROR(("couldn't register the net device [%s], err %d\n", net->name, err));
+ goto fail;
+ }
++#ifdef WL_EXT_IAPSTA
++ if (ifidx == 0)
++ wl_ext_iapsta_attach_netdev(net, ifidx);
++ wl_ext_iapsta_attach_name(net, ifidx);
++#endif
+
+
+
+@@ -12482,7 +12715,7 @@ dhd_register_if(dhd_pub_t *dhdp, int ifidx, bool need_rtnl_lock)
+ #endif
+
+ #if (defined(BCMPCIE) || (defined(BCMLXSDMMC) && (LINUX_VERSION_CODE >= \
+- KERNEL_VERSION(2, 6, 27))))
++ KERNEL_VERSION(2, 6, 27))) || defined(BCMDBUS))
+ if (ifidx == 0) {
+ #if defined(BCMLXSDMMC) && !defined(DHD_PRELOAD)
+ up(&dhd_registration_sem);
+@@ -12549,7 +12782,16 @@ dhd_bus_detach(dhd_pub_t *dhdp)
+ dhd_prot_stop(&dhd->pub);
+
+ /* Stop the bus module */
++#ifdef BCMDBUS
++ /* Force Dongle terminated */
++ if (dhd_wl_ioctl_cmd(dhdp, WLC_TERMINATED, NULL, 0, TRUE, 0) < 0)
++ DHD_ERROR(("%s Setting WLC_TERMINATED failed\n",
++ __FUNCTION__));
++ dbus_stop(dhd->pub.bus);
++ 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)
+@@ -12702,9 +12944,6 @@ void dhd_detach(dhd_pub_t *dhdp)
+ ASSERT(ifp);
+ ASSERT(ifp->net);
+ if (ifp && ifp->net) {
+-
+-
+-
+ #ifdef WL_CFG80211
+ cfg = wl_get_cfg(ifp->net);
+ #endif
+@@ -12754,6 +12993,9 @@ void dhd_detach(dhd_pub_t *dhdp)
+ del_timer_sync(&dhd->timer);
+ DHD_DISABLE_RUNTIME_PM(&dhd->pub);
+
++#ifdef BCMDBUS
++ tasklet_kill(&dhd->tasklet);
++#else
+ if (dhd->dhd_state & DHD_ATTACH_STATE_THREADS_CREATED) {
+ #ifdef DHD_PCIE_RUNTIMEPM
+ if (dhd->thr_rpm_ctl.thr_pid >= 0) {
+@@ -12775,6 +13017,7 @@ void dhd_detach(dhd_pub_t *dhdp)
+ tasklet_kill(&dhd->tasklet);
+ }
+ }
++#endif /* BCMDBUS */
+
+ #ifdef DHD_LB
+ if (dhd->dhd_state & DHD_ATTACH_STATE_LB_ATTACH_DONE) {
+@@ -12893,14 +13136,14 @@ void dhd_detach(dhd_pub_t *dhdp)
+ dhd->new_freq = NULL;
+ cpufreq_unregister_notifier(&dhd->freq_trans, CPUFREQ_TRANSITION_NOTIFIER);
+ #endif
++ DHD_TRACE(("wd wakelock count:%d\n", dhd->wakelock_wd_counter));
+ #ifdef CONFIG_HAS_WAKELOCK
+ dhd->wakelock_wd_counter = 0;
+ wake_lock_destroy(&dhd->wl_wdwake);
+- // terence 20161023: can not destroy wl_wifi when wlan down, it will happen null pointer in dhd_ioctl_entry
+- wake_lock_destroy(&dhd->wl_wifi);
++ // terence 20161023: can not destroy wl_wifi when wlan down, it will happen null pointer in dhd_ioctl_entry
++ wake_lock_destroy(&dhd->wl_wifi);
+ #endif /* CONFIG_HAS_WAKELOCK */
+ if (dhd->dhd_state & DHD_ATTACH_STATE_WAKELOCKS_INIT) {
+- DHD_TRACE(("wd wakelock count:%d\n", dhd->wakelock_wd_counter));
+ DHD_OS_WAKE_LOCK_DESTROY(dhd);
+ }
+
+@@ -13264,10 +13507,15 @@ dhd_os_set_ioctl_resp_timeout(unsigned int timeout_msec)
+ }
+
+ int
+-dhd_os_ioctl_resp_wait(dhd_pub_t *pub, uint *condition)
++dhd_os_ioctl_resp_wait(dhd_pub_t *pub, uint *condition, bool resched)
+ {
+ dhd_info_t * dhd = (dhd_info_t *)(pub->info);
+- int timeout;
++ int timeout, timeout_tmp = dhd_ioctl_timeout_msec;
++
++ if (!resched && pub->conf->ctrl_resched>0 && pub->conf->dhd_ioctl_timeout_msec>0) {
++ timeout_tmp = dhd_ioctl_timeout_msec;
++ dhd_ioctl_timeout_msec = pub->conf->dhd_ioctl_timeout_msec;
++ }
+
+ /* Convert timeout in millsecond to jiffies */
+ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
+@@ -13280,6 +13528,10 @@ dhd_os_ioctl_resp_wait(dhd_pub_t *pub, uint *condition)
+
+ timeout = wait_event_timeout(dhd->ioctl_resp_wait, (*condition), timeout);
+
++ if (!resched && pub->conf->ctrl_resched>0 && pub->conf->dhd_ioctl_timeout_msec>0) {
++ dhd_ioctl_timeout_msec = timeout_tmp;
++ }
++
+ DHD_PERIM_LOCK(pub);
+
+ return timeout;
+@@ -13422,6 +13674,7 @@ dhd_os_busbusy_wake(dhd_pub_t *pub)
+ void
+ dhd_os_wd_timer_extend(void *bus, bool extend)
+ {
++#ifndef BCMDBUS
+ dhd_pub_t *pub = bus;
+ dhd_info_t *dhd = (dhd_info_t *)pub->info;
+
+@@ -13429,12 +13682,14 @@ dhd_os_wd_timer_extend(void *bus, bool extend)
+ dhd_os_wd_timer(bus, WATCHDOG_EXTEND_INTERVAL);
+ else
+ dhd_os_wd_timer(bus, dhd->default_wd_interval);
++#endif /* !BCMDBUS */
+ }
+
+
+ void
+ dhd_os_wd_timer(void *bus, uint wdtick)
+ {
++#ifndef BCMDBUS
+ dhd_pub_t *pub = bus;
+ dhd_info_t *dhd = (dhd_info_t *)pub->info;
+ unsigned long flags;
+@@ -13469,6 +13724,7 @@ dhd_os_wd_timer(void *bus, uint wdtick)
+ dhd->wd_timer_valid = TRUE;
+ }
+ DHD_GENERAL_UNLOCK(pub, flags);
++#endif /* !BCMDBUS */
+ }
+
+ #ifdef DHD_PCIE_RUNTIMEPM
+@@ -13570,15 +13826,21 @@ dhd_os_get_image_block(char *buf, int len, void *image)
+ }
+
+ size = i_size_read(file_inode(fp));
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
++ rdlen = kernel_read(fp, buf, MIN(len, size), &fp->f_pos);
++#else
+ rdlen = kernel_read(fp, fp->f_pos, buf, MIN(len, size));
++#endif
+
+ if (len >= size && size != rdlen) {
+ return -EIO;
+ }
+
++#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0)
+ if (rdlen > 0) {
+ fp->f_pos += rdlen;
+ }
++#endif
+
+ return rdlen;
+ }
+@@ -13609,7 +13871,11 @@ dhd_os_gets_image(dhd_pub_t *pub, char *str, int len, void *image)
+ if (!image)
+ return 0;
+
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
++ rd_len = kernel_read(fp, str, len, &fp->f_pos);
++#else
+ rd_len = kernel_read(fp, fp->f_pos, str, len);
++#endif
+ str_end = strnchr(str, len, '\n');
+ if (str_end == NULL) {
+ goto err;
+@@ -13640,10 +13906,14 @@ dhd_os_sdlock(dhd_pub_t *pub)
+
+ dhd = (dhd_info_t *)(pub->info);
+
++#ifdef BCMDBUS
++ spin_lock_bh(&dhd->sdlock);
++#else
+ if (dhd_dpc_prio >= 0)
+ down(&dhd->sdsem);
+ else
+ spin_lock_bh(&dhd->sdlock);
++#endif /* !BCMDBUS */
+ }
+
+ void
+@@ -13653,10 +13923,14 @@ dhd_os_sdunlock(dhd_pub_t *pub)
+
+ dhd = (dhd_info_t *)(pub->info);
+
++#ifdef BCMDBUS
++ spin_unlock_bh(&dhd->sdlock);
++#else
+ if (dhd_dpc_prio >= 0)
+ up(&dhd->sdsem);
+ else
+ spin_unlock_bh(&dhd->sdlock);
++#endif /* !BCMDBUS */
+ }
+
+ void
+@@ -13665,7 +13939,11 @@ dhd_os_sdlock_txq(dhd_pub_t *pub)
+ dhd_info_t *dhd;
+
+ dhd = (dhd_info_t *)(pub->info);
++#ifdef BCMDBUS
++ spin_lock_irqsave(&dhd->txqlock, dhd->txqlock_flags);
++#else
+ spin_lock_bh(&dhd->txqlock);
++#endif /* BCMDBUS */
+ }
+
+ void
+@@ -13674,17 +13952,33 @@ dhd_os_sdunlock_txq(dhd_pub_t *pub)
+ dhd_info_t *dhd;
+
+ dhd = (dhd_info_t *)(pub->info);
++#ifdef BCMDBUS
++ spin_unlock_irqrestore(&dhd->txqlock, dhd->txqlock_flags);
++#else
+ spin_unlock_bh(&dhd->txqlock);
++#endif /* BCMDBUS */
+ }
+
+ void
+ dhd_os_sdlock_rxq(dhd_pub_t *pub)
+ {
++#if 0
++ dhd_info_t *dhd;
++
++ dhd = (dhd_info_t *)(pub->info);
++ spin_lock_bh(&dhd->rxqlock);
++#endif
+ }
+
+ void
+ dhd_os_sdunlock_rxq(dhd_pub_t *pub)
+ {
++#if 0
++ dhd_info_t *dhd;
++
++ dhd = (dhd_info_t *)(pub->info);
++ spin_unlock_bh(&dhd->rxqlock);
++#endif
+ }
+
+ static void
+@@ -13804,6 +14098,9 @@ dhd_wl_host_event(dhd_info_t *dhd, int ifidx, void *pktdata, uint16 pktlen,
+ if (bcmerror != BCME_OK)
+ return (bcmerror);
+
++#if defined(WL_EXT_IAPSTA)
++ wl_ext_iapsta_event(dhd->iflist[ifidx]->net, event, *data);
++#endif /* defined(WL_EXT_IAPSTA) */
+ #if defined(WL_WIRELESS_EXT)
+ if (event->bsscfgidx == 0) {
+ /*
+@@ -13929,7 +14226,7 @@ void dhd_wait_event_wakeup(dhd_pub_t *dhd)
+ return;
+ }
+
+-#if defined(BCMSDIO) || defined(BCMPCIE)
++#if defined(BCMSDIO) || defined(BCMPCIE) || defined(BCMDBUS)
+ int
+ dhd_net_bus_devreset(struct net_device *dev, uint8 flag)
+ {
+@@ -13997,7 +14294,7 @@ dhd_net_bus_resume(struct net_device *dev, uint8 stage)
+ }
+
+ #endif /* BCMSDIO */
+-#endif /* BCMSDIO || BCMPCIE */
++#endif /* BCMSDIO || BCMPCIE || BCMDBUS */
+
+ int net_os_set_suspend_disable(struct net_device *dev, int val)
+ {
+@@ -14167,6 +14464,9 @@ dhd_dev_get_feature_set(struct net_device *dev)
+ if (dhd_is_pno_supported(dhd)) {
+ feature_set |= WIFI_FEATURE_PNO;
+ #ifdef GSCAN_SUPPORT
++ /* terence 20171115: remove to get GTS PASS
++ * com.google.android.gts.wifi.WifiHostTest#testWifiScannerBatchTimestamp
++ */
+ // feature_set |= WIFI_FEATURE_GSCAN;
+ // feature_set |= WIFI_FEATURE_HAL_EPNO;
+ #endif /* GSCAN_SUPPORT */
+@@ -15718,6 +16018,7 @@ void dhd_get_customized_country_code(struct net_device *dev, char *country_iso_c
+
+ BCM_REFERENCE(dhd);
+ }
++
+ void dhd_bus_country_set(struct net_device *dev, wl_country_t *cspec, bool notify)
+ {
+ dhd_info_t *dhd = DHD_DEV_INFO(dev);
+@@ -15923,7 +16224,11 @@ int write_file(const char * file_name, uint32 flags, uint8 *buf, int size)
+ }
+
+ /* Write buf to file */
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
++ ret = kernel_write(fp, buf, size, &pos);
++#else
+ ret = vfs_write(fp, buf, size, &pos);
++#endif
+ if (ret < 0) {
+ DHD_ERROR(("write file error, err = %d\n", ret));
+ goto exit;
+@@ -16848,7 +17153,6 @@ bool dhd_os_check_if_up(dhd_pub_t *pub)
+ return pub->up;
+ }
+
+-#if defined(BCMSDIO) || defined(BCMPCIE)
+ /* function to collect firmware, chip id and chip version info */
+ void dhd_set_version_info(dhd_pub_t *dhdp, char *fw)
+ {
+@@ -16862,10 +17166,9 @@ void dhd_set_version_info(dhd_pub_t *dhdp, char *fw)
+ return;
+
+ i = snprintf(&info_string[i], sizeof(info_string) - i,
+- "\n Chip: %x Rev %x Pkg %x", dhd_bus_chip_id(dhdp),
+- dhd_bus_chiprev_id(dhdp), dhd_bus_chippkg_id(dhdp));
++ "\n Chip: %x Rev %x", dhd_conf_get_chip(dhdp),
++ dhd_conf_get_chiprev(dhdp));
+ }
+-#endif /* BCMSDIO || BCMPCIE */
+
+ int dhd_ioctl_entry_local(struct net_device *net, wl_ioctl_t *ioc, int cmd)
+ {
+@@ -17571,7 +17874,11 @@ void dhd_get_memdump_info(dhd_pub_t *dhd)
+ }
+
+ /* Handle success case */
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
++ ret = kernel_read(fp, (char *)&mem_val, 4, NULL);
++#else
+ ret = kernel_read(fp, 0, (char *)&mem_val, 4);
++#endif
+ if (ret < 0) {
+ DHD_ERROR(("%s: File read error, ret=%d\n", __FUNCTION__, ret));
+ filp_close(fp, NULL);
+@@ -17810,7 +18117,11 @@ do_dhd_log_dump(dhd_pub_t *dhdp)
+ goto exit;
+ }
+
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
++ ret = kernel_write(fp, pre_strs, strlen(pre_strs), &pos);
++#else
+ ret = vfs_write(fp, pre_strs, strlen(pre_strs), &pos);
++#endif
+ if (ret < 0) {
+ DHD_ERROR(("write file error, err = %d\n", ret));
+ goto exit;
+@@ -17828,7 +18139,11 @@ do_dhd_log_dump(dhd_pub_t *dhdp)
+ wr_size = (unsigned int)(dld_buf->present - dld_buf->front);
+ }
+
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
++ ret = kernel_write(fp, dld_buf->buffer, wr_size, &pos);
++#else
+ ret = vfs_write(fp, dld_buf->buffer, wr_size, &pos);
++#endif
+ if (ret < 0) {
+ DHD_ERROR(("write file error, err = %d\n", ret));
+ goto exit;
+@@ -17849,7 +18164,11 @@ do_dhd_log_dump(dhd_pub_t *dhdp)
+ break;
+ }
+
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
++ ret = kernel_write(fp, post_strs, strlen(post_strs), &pos);
++#else
+ ret = vfs_write(fp, post_strs, strlen(post_strs), &pos);
++#endif
+ if (ret < 0) {
+ DHD_ERROR(("write file error, err = %d\n", ret));
+ goto exit;
+@@ -17898,7 +18217,11 @@ void dhd_get_assert_info(dhd_pub_t *dhd)
+ if (IS_ERR(fp)) {
+ DHD_ERROR(("%s: File [%s] doesn't exist\n", __FUNCTION__, filepath));
+ } else {
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
++ ssize_t ret = kernel_read(fp, (char *)&mem_val, 4, NULL);
++#else
+ int ret = kernel_read(fp, 0, (char *)&mem_val, 4);
++#endif
+ if (ret < 0) {
+ DHD_ERROR(("%s: File read error, ret=%d\n", __FUNCTION__, ret));
+ } else {
+@@ -18809,7 +19132,11 @@ dhd_write_file(const char *filepath, char *buf, int buf_len)
+ ret = BCME_ERROR;
+ } else {
+ if (fp->f_mode & FMODE_WRITE) {
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
++ ret = kernel_write(fp, buf, buf_len, &fp->f_pos);
++#else
+ ret = vfs_write(fp, buf, buf_len, &fp->f_pos);
++#endif
+ if (ret < 0) {
+ DHD_ERROR(("%s: Couldn't write file '%s'\n",
+ __FUNCTION__, filepath));
+@@ -18845,7 +19172,11 @@ dhd_read_file(const char *filepath, char *buf, int buf_len)
+ return BCME_ERROR;
+ }
+
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
++ ret = kernel_read(fp, buf, buf_len, NULL);
++#else
+ ret = kernel_read(fp, 0, buf, buf_len);
++#endif
+ filp_close(fp, NULL);
+
+ /* restore previous address limit */
+@@ -19444,7 +19775,11 @@ dhd_make_hang_with_reason(struct net_device *dev, const char *string_num)
+ wake_counts_t*
+ dhd_get_wakecount(dhd_pub_t *dhdp)
+ {
++#ifdef BCMDBUS
++ return NULL;
++#else
+ return dhd_bus_get_wakecount(dhdp);
++#endif /* BCMDBUS */
+ }
+ #endif /* DHD_WAKE_STATUS */
+
+diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_linux.h b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_linux.h
+index 4651dc51f69a..248f5271da0b 100644
+--- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_linux.h
++++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_linux.h
+@@ -51,7 +51,21 @@
+ #include
+ #endif /* defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND) */
+
++/* dongle status */
++enum wifi_adapter_status {
++ WIFI_STATUS_POWER_ON = 0,
++ WIFI_STATUS_ATTACH,
++ WIFI_STATUS_FW_READY,
++ WIFI_STATUS_DETTACH
++};
++#define wifi_chk_adapter_status(adapter, stat) (test_bit(stat, &(adapter)->status))
++#define wifi_get_adapter_status(adapter, stat) (test_bit(stat, &(adapter)->status))
++#define wifi_set_adapter_status(adapter, stat) (set_bit(stat, &(adapter)->status))
++#define wifi_clr_adapter_status(adapter, stat) (clear_bit(stat, &(adapter)->status))
++#define wifi_chg_adapter_status(adapter, stat) (change_bit(stat, &(adapter)->status))
++
+ #define DHD_REGISTRATION_TIMEOUT 12000 /* msec : allowed time to finished dhd registration */
++#define DHD_FW_READY_TIMEOUT 5000 /* msec : allowed time to finished fw download */
+
+ typedef struct wifi_adapter_info {
+ const char *name;
+@@ -65,6 +79,8 @@ typedef struct wifi_adapter_info {
+ uint bus_type;
+ uint bus_num;
+ uint slot_num;
++ wait_queue_head_t status_event;
++ unsigned long status;
+ #if defined(BT_OVER_SDIO)
+ const char *btfw_path;
+ #endif /* defined (BT_OVER_SDIO) */
+@@ -120,6 +136,8 @@ typedef dhd_sta_t dhd_sta_pool_t;
+
+ int dhd_wifi_platform_register_drv(void);
+ void dhd_wifi_platform_unregister_drv(void);
++wifi_adapter_info_t* dhd_wifi_platform_attach_adapter(uint32 bus_type,
++ uint32 bus_num, uint32 slot_num, unsigned long status);
+ wifi_adapter_info_t* dhd_wifi_platform_get_adapter(uint32 bus_type, uint32 bus_num,
+ uint32 slot_num);
+ int wifi_platform_set_power(wifi_adapter_info_t *adapter, bool on, unsigned long msec);
+diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_linux_platdev.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_linux_platdev.c
+old mode 100755
+new mode 100644
+index 7be2fa30d1eb..98592283e728
+--- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_linux_platdev.c
++++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_linux_platdev.c
+@@ -58,6 +58,7 @@ extern void dhd_wlan_deinit_plat_data(wifi_adapter_info_t *adapter);
+
+ #ifdef CONFIG_DTS
+ struct regulator *wifi_regulator = NULL;
++extern struct wifi_platform_data dhd_wlan_control;
+ #endif /* CONFIG_DTS */
+
+ bool cfg_multichip = FALSE;
+@@ -93,6 +94,30 @@ extern void bcm_bt_unlock(int cookie);
+ static int lock_cookie_wifi = 'W' | 'i'<<8 | 'F'<<16 | 'i'<<24; /* cookie is "WiFi" */
+ #endif /* ENABLE_4335BT_WAR */
+
++wifi_adapter_info_t* dhd_wifi_platform_attach_adapter(uint32 bus_type,
++ uint32 bus_num, uint32 slot_num, unsigned long status)
++{
++ int i;
++
++ if (dhd_wifi_platdata == NULL)
++ return NULL;
++
++ for (i = 0; i < dhd_wifi_platdata->num_adapters; i++) {
++ wifi_adapter_info_t *adapter = &dhd_wifi_platdata->adapters[i];
++ if ((adapter->bus_type == -1 || adapter->bus_type == bus_type) &&
++ (adapter->bus_num == -1 || adapter->bus_num == bus_num) &&
++ (adapter->slot_num == -1 || adapter->slot_num == slot_num)
++#if defined(ENABLE_INSMOD_NO_FW_LOAD)
++ && (wifi_chk_adapter_status(adapter, status))
++#endif
++ ) {
++ DHD_ERROR(("attach adapter info '%s'\n", adapter->name));
++ return adapter;
++ }
++ }
++ return NULL;
++}
++
+ wifi_adapter_info_t* dhd_wifi_platform_get_adapter(uint32 bus_type, uint32 bus_num, uint32 slot_num)
+ {
+ int i;
+@@ -165,20 +190,31 @@ int wifi_platform_set_power(wifi_adapter_info_t *adapter, bool on, unsigned long
+ return -EINVAL;
+ }
+ #endif /* BT_OVER_SDIO */
++ if (on) {
++ wifi_set_adapter_status(adapter, WIFI_STATUS_POWER_ON);
++ } else {
++ wifi_clr_adapter_status(adapter, WIFI_STATUS_POWER_ON);
++ }
+ #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;
+ }
+- if (err < 0)
++ if (err < 0) {
+ DHD_ERROR(("%s: regulator enable/disable failed", __FUNCTION__));
++ goto fail;
++ }
+ #else
+- if (!adapter || !adapter->wifi_plat_data)
+- return -EINVAL;
++ if (!adapter || !adapter->wifi_plat_data) {
++ err = -EINVAL;
++ goto fail;
++ }
+ plat_data = adapter->wifi_plat_data;
+
+ DHD_ERROR(("%s = %d\n", __FUNCTION__, on));
+@@ -213,6 +249,13 @@ int wifi_platform_set_power(wifi_adapter_info_t *adapter, bool on, unsigned long
+
+ #endif /* CONFIG_DTS */
+
++ return err;
++fail:
++ if (on) {
++ wifi_clr_adapter_status(adapter, WIFI_STATUS_POWER_ON);
++ } else {
++ wifi_set_adapter_status(adapter, WIFI_STATUS_POWER_ON);
++ }
+ return err;
+ }
+
+@@ -280,7 +323,7 @@ static int wifi_plat_dev_drv_probe(struct platform_device *pdev)
+ {
+ struct resource *resource;
+ wifi_adapter_info_t *adapter;
+-#ifdef CONFIG_DTS
++#if defined(CONFIG_DTS) && defined(CUSTOMER_OOB)
+ int irq, gpio;
+ #endif /* CONFIG_DTS */
+
+@@ -290,7 +333,8 @@ static int wifi_plat_dev_drv_probe(struct platform_device *pdev)
+ ASSERT(dhd_wifi_platdata != NULL);
+ ASSERT(dhd_wifi_platdata->num_adapters == 1);
+ adapter = &dhd_wifi_platdata->adapters[0];
+- adapter->wifi_plat_data = (struct wifi_platform_data *)(pdev->dev.platform_data);
++ adapter->wifi_plat_data = (void *)&dhd_wlan_control;
++// adapter->wifi_plat_data = (struct wifi_platform_data *)(pdev->dev.platform_data);
+
+ resource = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "bcmdhd_wlan_irq");
+ if (resource == NULL)
+@@ -310,6 +354,7 @@ static int wifi_plat_dev_drv_probe(struct platform_device *pdev)
+ return -1;
+ }
+
++#if defined(CUSTOMER_OOB)
+ /* This is to get the irq for the OOB */
+ gpio = of_get_gpio(pdev->dev.of_node, 0);
+
+@@ -327,6 +372,7 @@ static int wifi_plat_dev_drv_probe(struct platform_device *pdev)
+ /* need to change the flags according to our requirement */
+ adapter->intr_flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL |
+ IORESOURCE_IRQ_SHAREABLE;
++#endif
+ #endif /* CONFIG_DTS */
+
+ wifi_plat_dev_probe_ret = dhd_wifi_platform_load();
+@@ -469,6 +515,7 @@ static int wifi_ctrlfunc_register_drv(void)
+ dhd_wifi_platdata = kzalloc(sizeof(bcmdhd_wifi_platdata_t), GFP_KERNEL);
+ dhd_wifi_platdata->num_adapters = 1;
+ dhd_wifi_platdata->adapters = adapter;
++ init_waitqueue_head(&adapter->status_event);
+
+ #ifndef CUSTOMER_HW
+ if (dev1) {
+@@ -519,7 +566,9 @@ static int wifi_ctrlfunc_register_drv(void)
+
+ void wifi_ctrlfunc_unregister_drv(void)
+ {
++#ifndef CONFIG_DTS
+ wifi_adapter_info_t *adapter;
++#endif
+
+ #if defined(CONFIG_DTS) && !defined(CUSTOMER_HW)
+ DHD_ERROR(("unregister wifi platform drivers\n"));
+@@ -730,7 +779,7 @@ void dhd_wifi_platform_unregister_drv(void)
+ extern int dhd_watchdog_prio;
+ extern int dhd_dpc_prio;
+ extern uint dhd_deferred_tx;
+-#if defined(BCMLXSDMMC)
++#if defined(BCMLXSDMMC) || defined(BCMDBUS)
+ extern struct semaphore dhd_registration_sem;
+ #endif
+
+@@ -854,10 +903,67 @@ static int dhd_wifi_platform_load_sdio(void)
+ }
+ #endif /* BCMSDIO */
+
++#ifdef BCMDBUS
++static int dhd_wifi_platform_load_usb(void)
++{
++ wifi_adapter_info_t *adapter;
++ s32 timeout = -1;
++ int i;
++ int err = 0;
++ enum wifi_adapter_status wait_status;
++
++ err = dhd_bus_register();
++ if (err) {
++ DHD_ERROR(("%s: usb_register failed\n", __FUNCTION__));
++ goto exit;
++ }
++
++ /* power up all adapters */
++ for (i = 0; i < dhd_wifi_platdata->num_adapters; i++) {
++ adapter = &dhd_wifi_platdata->adapters[i];
++ DHD_ERROR(("Power-up adapter '%s'\n", adapter->name));
++ DHD_INFO((" - irq %d [flags %d], firmware: %s, nvram: %s\n",
++ adapter->irq_num, adapter->intr_flags, adapter->fw_path, adapter->nv_path));
++ DHD_INFO((" - bus type %d, bus num %d, slot num %d\n\n",
++ adapter->bus_type, adapter->bus_num, adapter->slot_num));
++ err = wifi_platform_set_power(adapter, TRUE, WIFI_TURNON_DELAY);
++ if (err) {
++ DHD_ERROR(("failed to wifi_platform_set_power on %s\n", adapter->name));
++ goto fail;
++ }
++ if (dhd_download_fw_on_driverload)
++ wait_status = WIFI_STATUS_ATTACH;
++ else
++ wait_status = WIFI_STATUS_DETTACH;
++ timeout = wait_event_interruptible_timeout(adapter->status_event,
++ wifi_get_adapter_status(adapter, wait_status),
++ msecs_to_jiffies(DHD_REGISTRATION_TIMEOUT));
++ if (timeout <= 0) {
++ err = -1;
++ DHD_ERROR(("%s: usb_register_driver timeout\n", __FUNCTION__));
++ goto fail;
++ }
++ }
++
++exit:
++ return err;
++
++fail:
++ dhd_bus_unregister();
++ /* power down all adapters */
++ for (i = 0; i < dhd_wifi_platdata->num_adapters; i++) {
++ adapter = &dhd_wifi_platdata->adapters[i];
++ wifi_platform_set_power(adapter, FALSE, WIFI_TURNOFF_DELAY);
++ }
++
++ return err;
++}
++#else /* BCMDBUS */
+ static int dhd_wifi_platform_load_usb(void)
+ {
+ return 0;
+ }
++#endif /* BCMDBUS */
+
+ static int dhd_wifi_platform_load()
+ {
+diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_linux_wq.h b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_linux_wq.h
+old mode 100755
+new mode 100644
+index 6dc41a5dc3a3..9c51d0665b27
+--- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_linux_wq.h
++++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_linux_wq.h
+@@ -47,6 +47,9 @@ enum _wq_event {
+ DHD_WQ_WORK_DEBUG_UART_DUMP,
+ DHD_WQ_WORK_SSSR_DUMP,
+ DHD_WQ_WORK_PKTLOG_DUMP,
++#ifdef DHD_UPDATE_INTF_MAC
++ DHD_WQ_WORK_IF_UPDATE,
++#endif /* DHD_UPDATE_INTF_MAC */
+ DHD_MAX_WQ_EVENTS
+ };
+
+diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_msgbuf.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_msgbuf.c
+old mode 100755
+new mode 100644
+index e602d24f2e6a..455f125a9687
+--- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_msgbuf.c
++++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_msgbuf.c
+@@ -52,6 +52,7 @@
+ #include
+ #include
+ #include
++#include
+ #ifdef DHD_TIMESYNC
+ #include
+ #endif /* DHD_TIMESYNC */
+@@ -6013,6 +6014,7 @@ dhd_msgbuf_wait_ioctl_cmplt(dhd_pub_t *dhd, uint32 len, void *buf)
+ int timeleft;
+ unsigned long flags;
+ int ret = 0;
++ static uint cnt = 0;
+
+ DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+@@ -6021,7 +6023,7 @@ dhd_msgbuf_wait_ioctl_cmplt(dhd_pub_t *dhd, uint32 len, void *buf)
+ goto out;
+ }
+
+- timeleft = dhd_os_ioctl_resp_wait(dhd, (uint *)&prot->ioctl_received);
++ timeleft = dhd_os_ioctl_resp_wait(dhd, (uint *)&prot->ioctl_received, false);
+
+ #ifdef DHD_RECOVER_TIMEOUT
+ if (prot->ioctl_received == 0) {
+@@ -6053,6 +6055,25 @@ dhd_msgbuf_wait_ioctl_cmplt(dhd_pub_t *dhd, uint32 len, void *buf)
+ }
+ #endif /* DHD_RECOVER_TIMEOUT */
+
++ if (dhd->conf->ctrl_resched > 0 && timeleft == 0 && (!dhd_query_bus_erros(dhd))) {
++ cnt++;
++ if (cnt <= dhd->conf->ctrl_resched) {
++ uint32 intstatus = 0, intmask = 0;
++ intstatus = si_corereg(dhd->bus->sih, dhd->bus->sih->buscoreidx, PCIMailBoxInt, 0, 0);
++ intmask = si_corereg(dhd->bus->sih, dhd->bus->sih->buscoreidx, PCIMailBoxMask, 0, 0);
++ if (intstatus) {
++ DHD_ERROR(("%s: reschedule dhd_dpc, cnt=%d, intstatus=0x%x, intmask=0x%x\n",
++ __FUNCTION__, cnt, intstatus, intmask));
++ dhd->bus->ipend = TRUE;
++ dhd->bus->dpc_sched = TRUE;
++ dhd_sched_dpc(dhd);
++ timeleft = dhd_os_ioctl_resp_wait(dhd, &prot->ioctl_received, true);
++ }
++ }
++ } else {
++ cnt = 0;
++ }
++
+ if (timeleft == 0 && (!dhd_query_bus_erros(dhd))) {
+ uint32 intstatus;
+
+diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_pcie.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_pcie.c
+index 91a1203ebc65..a785fe52be96 100644
+--- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_pcie.c
++++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_pcie.c
+@@ -497,7 +497,9 @@ uint32
+ dhdpcie_bus_intstatus(dhd_bus_t *bus)
+ {
+ uint32 intstatus = 0;
++#ifndef DHD_READ_INTSTATUS_IN_DPC
+ uint32 intmask = 0;
++#endif /* DHD_READ_INTSTATUS_IN_DPC */
+
+ if ((bus->dhd->busstate == DHD_BUS_SUSPEND || bus->d3_suspend_pending) &&
+ bus->wait_for_d3_ack) {
+@@ -521,10 +523,12 @@ dhdpcie_bus_intstatus(dhd_bus_t *bus)
+ /* this is a PCIE core register..not a config register... */
+ intstatus = si_corereg(bus->sih, bus->sih->buscoreidx, PCIMailBoxInt, 0, 0);
+
++#ifndef DHD_READ_INTSTATUS_IN_DPC
+ /* this is a PCIE core register..not a config register... */
+ intmask = si_corereg(bus->sih, bus->sih->buscoreidx, PCIMailBoxMask, 0, 0);
+
+ intstatus &= intmask;
++#endif /* DHD_READ_INTSTATUS_IN_DPC */
+ /* Is device removed. intstatus & intmask read 0xffffffff */
+ if (intstatus == (uint32)-1) {
+ DHD_ERROR(("%s: Device is removed or Link is down.\n", __FUNCTION__));
+@@ -600,6 +604,7 @@ dhdpcie_bus_isr(dhd_bus_t *bus)
+ }
+ }
+
++#ifndef DHD_READ_INTSTATUS_IN_DPC
+ intstatus = dhdpcie_bus_intstatus(bus);
+
+ /* Check if the interrupt is ours or not */
+@@ -627,6 +632,7 @@ dhdpcie_bus_isr(dhd_bus_t *bus)
+
+ /* Count the interrupt call */
+ bus->intrcount++;
++#endif /* DHD_READ_INTSTATUS_IN_DPC */
+
+ bus->ipend = TRUE;
+
+@@ -988,6 +994,9 @@ dhdpcie_dongle_attach(dhd_bus_t *bus)
+ case BCM4347_CHIP_GRPID:
+ bus->dongle_ram_base = CR4_4347_RAM_BASE;
+ break;
++ case BCM4362_CHIP_ID:
++ bus->dongle_ram_base = CR4_4362_RAM_BASE;
++ break;
+ default:
+ bus->dongle_ram_base = 0;
+ DHD_ERROR(("%s: WARNING: Using default ram base at 0x%x\n",
+@@ -1008,6 +1017,8 @@ dhdpcie_dongle_attach(dhd_bus_t *bus)
+
+ /* Set the poll and/or interrupt flags */
+ bus->intr = (bool)dhd_intr;
++ if ((bus->poll = (bool)dhd_poll))
++ bus->pollrate = 1;
+
+ bus->wait_for_d3_ack = 1;
+ #ifdef PCIE_OOB
+@@ -1116,6 +1127,27 @@ dhdpcie_advertise_bus_cleanup(dhd_pub_t *dhdp)
+ return;
+ }
+
++static void
++dhdpcie_advertise_bus_remove(dhd_pub_t *dhdp)
++{
++ unsigned long flags;
++ int timeleft;
++
++ DHD_GENERAL_LOCK(dhdp, flags);
++ dhdp->busstate = DHD_BUS_REMOVE;
++ DHD_GENERAL_UNLOCK(dhdp, flags);
++
++ timeleft = dhd_os_busbusy_wait_negation(dhdp, &dhdp->dhd_bus_busy_state);
++ if ((timeleft == 0) || (timeleft == 1)) {
++ DHD_ERROR(("%s : Timeout due to dhd_bus_busy_state=0x%x\n",
++ __FUNCTION__, dhdp->dhd_bus_busy_state));
++ ASSERT(0);
++ }
++
++ return;
++}
++
++
+ static void
+ dhdpcie_bus_remove_prep(dhd_bus_t *bus)
+ {
+@@ -1169,7 +1201,7 @@ dhdpcie_bus_release(dhd_bus_t *bus)
+ ASSERT(osh);
+
+ if (bus->dhd) {
+- dhdpcie_advertise_bus_cleanup(bus->dhd);
++ dhdpcie_advertise_bus_remove(bus->dhd);
+ dongle_isolation = bus->dhd->dongle_isolation;
+ bus->dhd->is_pcie_watchdog_reset = FALSE;
+ dhdpcie_bus_remove_prep(bus);
+@@ -1509,6 +1541,14 @@ bool dhd_bus_watchdog(dhd_pub_t *dhd)
+ }
+ }
+
++#ifdef DHD_READ_INTSTATUS_IN_DPC
++ if (bus->poll) {
++ bus->ipend = TRUE;
++ bus->dpc_sched = TRUE;
++ dhd_sched_dpc(bus->dhd); /* queue DPC now!! */
++ }
++#endif /* DHD_READ_INTSTATUS_IN_DPC */
++
+ #if defined(PCIE_OOB) || defined(PCIE_INB_DW)
+ /* If haven't communicated with device for a while, deassert the Device_Wake GPIO */
+ if (dhd_doorbell_timeout != 0 && dhd->busstate == DHD_BUS_DATA &&
+@@ -1618,6 +1658,17 @@ dhd_set_path_params(struct dhd_bus *bus)
+
+ }
+
++void
++dhd_set_bus_params(struct dhd_bus *bus)
++{
++ if (bus->dhd->conf->dhd_poll >= 0) {
++ bus->poll = bus->dhd->conf->dhd_poll;
++ if (!bus->pollrate)
++ bus->pollrate = 1;
++ printf("%s: set polling mode %d\n", __FUNCTION__, bus->dhd->conf->dhd_poll);
++ }
++}
++
+ static int
+ dhdpcie_download_firmware(struct dhd_bus *bus, osl_t *osh)
+ {
+@@ -1659,6 +1710,7 @@ dhdpcie_download_firmware(struct dhd_bus *bus, osl_t *osh)
+ DHD_OS_WAKE_LOCK(bus->dhd);
+
+ dhd_set_path_params(bus);
++ dhd_set_bus_params(bus);
+
+ ret = _dhdpcie_download_firmware(bus);
+
+@@ -6045,10 +6097,24 @@ dhd_bus_dpc(struct dhd_bus *bus)
+ DHD_BUS_BUSY_SET_IN_DPC(bus->dhd);
+ DHD_GENERAL_UNLOCK(bus->dhd, flags);
+
++#ifdef DHD_READ_INTSTATUS_IN_DPC
++ if (bus->ipend) {
++ bus->ipend = FALSE;
++ bus->intstatus = dhdpcie_bus_intstatus(bus);
++ /* Check if the interrupt is ours or not */
++ if (bus->intstatus == 0) {
++ goto INTR_ON;
++ }
++ bus->intrcount++;
++ }
++#endif /* DHD_READ_INTSTATUS_IN_DPC */
+
+ resched = dhdpcie_bus_process_mailbox_intr(bus, bus->intstatus);
+ if (!resched) {
+ bus->intstatus = 0;
++#ifdef DHD_READ_INTSTATUS_IN_DPC
++INTR_ON:
++#endif /* DHD_READ_INTSTATUS_IN_DPC */
+ bus->dpc_intr_enable_count++;
+ dhdpcie_bus_intr_enable(bus); /* Enable back interrupt using Intmask!! */
+ }
+@@ -7025,6 +7091,11 @@ dhdpcie_chipmatch(uint16 vendor, uint16 device)
+ if ((device == BCM4361_D11AC_ID) || (device == BCM4361_D11AC2G_ID) ||
+ (device == BCM4361_D11AC5G_ID) || (device == BCM4361_CHIP_ID))
+ return 0;
++
++ if ((device == BCM4362_D11AX_ID) || (device == BCM4362_D11AX2G_ID) ||
++ (device == BCM4362_D11AX5G_ID) || (device == BCM4362_CHIP_ID)) {
++ return 0;
++ }
+
+ if ((device == BCM4365_D11AC_ID) || (device == BCM4365_D11AC2G_ID) ||
+ (device == BCM4365_D11AC5G_ID) || (device == BCM4365_CHIP_ID))
+diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_pcie.h b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_pcie.h
+old mode 100755
+new mode 100644
+index eb8de62956bf..92b07c6e4bc7
+--- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_pcie.h
++++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_pcie.h
+@@ -259,6 +259,7 @@ typedef struct dhd_bus {
+ struct pktq txq; /* Queue length used for flow-control */
+
+ bool intr; /* Use interrupts */
++ bool poll; /* Use polling */
+ bool ipend; /* Device interrupt is pending */
+ bool intdis; /* Interrupts disabled by isr */
+ uint intrcount; /* Count of device interrupt callbacks */
+diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_pcie_linux.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_pcie_linux.c
+old mode 100755
+new mode 100644
+index 51664a7e217b..ecaed3e93558
+--- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_pcie_linux.c
++++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_pcie_linux.c
+@@ -178,12 +178,6 @@ static int dhdpcie_init(struct pci_dev *pdev);
+ static irqreturn_t dhdpcie_isr(int irq, void *arg);
+ /* OS Routine functions for PCI suspend/resume */
+
+-#if defined(MULTIPLE_SUPPLICANT)
+-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25))
+-DEFINE_MUTEX(_dhd_sdio_mutex_lock_);
+-#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) */
+-#endif
+-
+ static int dhdpcie_set_suspend_resume(dhd_bus_t *bus, bool state);
+ static int dhdpcie_resume_host_dev(dhd_bus_t *bus);
+ static int dhdpcie_suspend_host_dev(dhd_bus_t *bus);
+@@ -890,9 +884,7 @@ dhdpcie_bus_unregister(void)
+ int __devinit
+ dhdpcie_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+ {
+-#ifdef BUS_POWER_RESTORE
+- wifi_adapter_info_t *adapter = NULL;
+-#endif
++ DHD_MUTEX_LOCK();
+
+ if (dhdpcie_chipmatch (pdev->vendor, pdev->device)) {
+ DHD_ERROR(("%s: chipmatch failed!!\n", __FUNCTION__));
+@@ -912,15 +904,8 @@ dhdpcie_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+ device_disable_async_suspend(&pdev->dev);
+ #endif /* BCMPCIE_DISABLE_ASYNC_SUSPEND */
+
+-#ifdef BUS_POWER_RESTORE
+- adapter = dhd_wifi_platform_get_adapter(PCI_BUS, pdev->bus->number,
+- PCI_SLOT(pdev->devfn));
+-
+- if (adapter != NULL)
+- adapter->pci_dev = pdev;
+-#endif
+-
+ DHD_TRACE(("%s: PCIe Enumeration done!!\n", __FUNCTION__));
++ DHD_MUTEX_UNLOCK();
+ return 0;
+ }
+
+@@ -948,17 +933,7 @@ dhdpcie_pci_remove(struct pci_dev *pdev)
+
+ DHD_TRACE(("%s Enter\n", __FUNCTION__));
+
+-#if defined(MULTIPLE_SUPPLICANT)
+-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25))
+- if (mutex_is_locked(&_dhd_sdio_mutex_lock_) == 0) {
+- DHD_ERROR(("%s : no mutex held. set lock\n", __FUNCTION__));
+- }
+- 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
++ DHD_MUTEX_LOCK();
+
+ pch = pci_get_drvdata(pdev);
+ bus = pch->bus;
+@@ -1006,12 +981,7 @@ dhdpcie_pci_remove(struct pci_dev *pdev)
+
+ dhdpcie_init_succeeded = FALSE;
+
+-#if defined(MULTIPLE_SUPPLICANT)
+-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25))
+- mutex_unlock(&_dhd_sdio_mutex_lock_);
+- DHD_ERROR(("%s : the lock is released.\n", __FUNCTION__));
+-#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) */
+-#endif /* LINUX */
++ DHD_MUTEX_UNLOCK();
+
+ DHD_TRACE(("%s Exit\n", __FUNCTION__));
+
+@@ -1029,10 +999,22 @@ dhdpcie_request_irq(dhdpcie_info_t *dhdpcie_info)
+ if (!bus->irq_registered) {
+ snprintf(dhdpcie_info->pciname, sizeof(dhdpcie_info->pciname),
+ "dhdpcie:%s", pci_name(pdev));
++#ifdef DHD_USE_MSI
++ printf("%s: MSI enabled\n", __FUNCTION__);
++ err = pci_enable_msi(pdev);
++ if (err < 0) {
++ DHD_ERROR(("%s: pci_enable_msi() failed, %d, fall back to INTx\n", __FUNCTION__, err));
++ }
++#else
++ printf("%s: MSI not enabled\n", __FUNCTION__);
++#endif /* DHD_USE_MSI */
+ err = request_irq(pdev->irq, dhdpcie_isr, IRQF_SHARED,
+ dhdpcie_info->pciname, bus);
+ if (err) {
+ DHD_ERROR(("%s: request_irq() failed\n", __FUNCTION__));
++#ifdef DHD_USE_MSI
++ pci_disable_msi(pdev);
++#endif /* DHD_USE_MSI */
+ return -1;
+ } else {
+ bus->irq_registered = TRUE;
+@@ -1226,10 +1208,6 @@ void dhdpcie_linkdown_cb(struct_pcie_notify *noti)
+ */
+ #endif /* SUPPORT_LINKDOWN_RECOVERY */
+
+-#if defined(MULTIPLE_SUPPLICANT)
+-extern void wl_android_post_init(void); // terence 20120530: fix critical section in dhd_open and dhdsdio_probe
+-#endif
+-
+ int dhdpcie_init(struct pci_dev *pdev)
+ {
+
+@@ -1244,18 +1222,6 @@ int dhdpcie_init(struct pci_dev *pdev)
+ dhdpcie_smmu_info_t *dhdpcie_smmu_info = NULL;
+ #endif /* USE_SMMU_ARCH_MSM */
+
+-#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
+-
+ do {
+ /* osl attach */
+ if (!(osh = osl_attach(pdev, PCI_BUS, FALSE))) {
+@@ -1266,9 +1232,12 @@ int dhdpcie_init(struct pci_dev *pdev)
+ /* initialize static buffer */
+ adapter = dhd_wifi_platform_get_adapter(PCI_BUS, pdev->bus->number,
+ PCI_SLOT(pdev->devfn));
+- if (adapter != NULL)
++ if (adapter != NULL) {
+ DHD_ERROR(("%s: found adapter info '%s'\n", __FUNCTION__, adapter->name));
+- else
++#ifdef BUS_POWER_RESTORE
++ adapter->pci_dev = pdev;
++#endif
++ } else
+ DHD_ERROR(("%s: can't find adapter info for this chip\n", __FUNCTION__));
+ osl_static_mem_init(osh, adapter);
+
+@@ -1438,11 +1407,7 @@ int dhdpcie_init(struct pci_dev *pdev)
+
+ #if defined(MULTIPLE_SUPPLICANT)
+ wl_android_post_init(); // terence 20120530: fix critical section in dhd_open and dhdsdio_probe
+-#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
++#endif /* MULTIPLE_SUPPLICANT */
+
+ DHD_TRACE(("%s:Exit - SUCCESS \n", __FUNCTION__));
+ return 0; /* return SUCCESS */
+@@ -1473,12 +1438,6 @@ int dhdpcie_init(struct pci_dev *pdev)
+ osl_detach(osh);
+
+ dhdpcie_init_succeeded = FALSE;
+-#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
+
+ DHD_TRACE(("%s:Exit - FAILURE \n", __FUNCTION__));
+
+@@ -1497,6 +1456,9 @@ dhdpcie_free_irq(dhd_bus_t *bus)
+ if (bus->irq_registered) {
+ free_irq(pdev->irq, bus);
+ bus->irq_registered = FALSE;
++#ifdef DHD_USE_MSI
++ pci_disable_msi(pdev);
++#endif /* DHD_USE_MSI */
+ } else {
+ DHD_ERROR(("%s: PCIe IRQ is not registered\n", __FUNCTION__));
+ }
+@@ -2296,6 +2258,7 @@ bool dhdpcie_is_resume_done(dhd_pub_t *dhdp)
+ return bus->runtime_resume_done;
+ }
+ #endif /* DHD_PCIE_RUNTIMEPM */
++
+ struct device * dhd_bus_to_dev(dhd_bus_t *bus)
+ {
+ struct pci_dev *pdev;
+@@ -2306,6 +2269,7 @@ struct device * dhd_bus_to_dev(dhd_bus_t *bus)
+ else
+ return NULL;
+ }
++
+ #ifdef HOFFLOAD_MODULES
+ void
+ dhd_free_module_memory(struct dhd_bus *bus, struct module_metadata *hmem)
+diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_pno.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_pno.c
+old mode 100755
+new mode 100644
+index c553733f682e..570e75ec8167
+--- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_pno.c
++++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_pno.c
+@@ -910,6 +910,7 @@ exit:
+ bytes_written = (int32)(bp - buf);
+ return bytes_written;
+ }
++
+ static int
+ _dhd_pno_clear_all_batch_results(dhd_pub_t *dhd, struct list_head *head, bool only_last)
+ {
+@@ -992,6 +993,7 @@ _dhd_pno_cfg(dhd_pub_t *dhd, uint16 *channel_list, int nchan)
+ exit:
+ return err;
+ }
++
+ static int
+ _dhd_pno_reinitialize_prof(dhd_pub_t *dhd, dhd_pno_params_t *params, dhd_pno_mode_t mode)
+ {
+@@ -1084,6 +1086,7 @@ _dhd_pno_reinitialize_prof(dhd_pub_t *dhd, dhd_pno_params_t *params, dhd_pno_mod
+ mutex_unlock(&_pno_state->pno_mutex);
+ return err;
+ }
++
+ static int
+ _dhd_pno_add_bssid(dhd_pub_t *dhd, wl_pfn_bssid_t *p_pfn_bssid, int nbssid)
+ {
+@@ -2763,6 +2766,7 @@ exit:
+ return err;
+ }
+ #endif /* GSCAN_SUPPORT */
++
+ #if defined(GSCAN_SUPPORT) || defined(DHD_GET_VALID_CHANNELS)
+ void *
+ dhd_pno_get_gscan(dhd_pub_t *dhd, dhd_pno_gscan_cmd_cfg_t type,
+@@ -4007,6 +4011,7 @@ exit:
+ kfree(buf);
+ return err;
+ }
++
+ int dhd_pno_deinit(dhd_pub_t *dhd)
+ {
+ int err = BCME_OK;
+diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_sdio.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_sdio.c
+index a4b20b94ee9d..3e035df90f9b 100644
+--- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_sdio.c
++++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_sdio.c
+@@ -68,10 +68,6 @@
+ #include
+ #include
+
+-#include
+-#include
+-#include "bcmsdh_sdmmc.h"
+-
+ #ifdef PROP_TXSTATUS
+ #include
+ #endif
+@@ -186,12 +182,6 @@ DHD_SPINWAIT_SLEEP_INIT(sdioh_spinwait_sleep);
+ pkt_statics_t tx_statics = {0};
+ #endif
+
+-#if defined(MULTIPLE_SUPPLICANT)
+-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25))
+-DEFINE_MUTEX(_dhd_sdio_mutex_lock_);
+-#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) */
+-#endif
+-
+ #ifdef SUPPORT_MULTIPLE_BOARD_REV_FROM_HW
+ extern unsigned int system_hw_rev;
+ #endif /* SUPPORT_MULTIPLE_BOARD_REV_FROM_HW */
+@@ -345,6 +335,8 @@ typedef struct dhd_bus {
+ #if defined(SUPPORT_P2P_GO_PS)
+ wait_queue_head_t bus_sleep;
+ #endif /* LINUX && SUPPORT_P2P_GO_PS */
++ bool ctrl_wait;
++ wait_queue_head_t ctrl_tx_wait;
+ uint rxflow_mode; /* Rx flow control mode */
+ bool rxflow; /* Is rx flow control on */
+ uint prev_rxlim_hit; /* Is prev rx limit exceeded (per dpc schedule) */
+@@ -700,7 +692,7 @@ static int dhdsdio_txpkt_preprocess(dhd_bus_t *bus, void *pkt, int chan, int txs
+ int prev_chain_total_len, bool last_chained_pkt,
+ int *pad_pkt_len, void **new_pkt
+ #if defined(BCMSDIOH_TXGLOM_EXT)
+- , int frist_frame
++ , int first_frame
+ #endif
+ );
+ static int dhdsdio_txpkt_postprocess(dhd_bus_t *bus, void *pkt);
+@@ -724,6 +716,7 @@ static int dhd_bcmsdh_send_buffer(void *bus, uint8 *frame, uint16 len);
+ static int dhdsdio_set_sdmode(dhd_bus_t *bus, int32 sd_mode);
+ static int dhdsdio_sdclk(dhd_bus_t *bus, bool on);
+ static void dhdsdio_advertise_bus_cleanup(dhd_pub_t *dhdp);
++static void dhdsdio_advertise_bus_remove(dhd_pub_t *dhdp);
+ #ifdef SUPPORT_MULTIPLE_BOARD_REV_FROM_DT
+ int dhd_get_system_rev(void);
+ #endif /* SUPPORT_MULTIPLE_BOARD_REV_FROM_DT */
+@@ -884,7 +877,8 @@ dhdsdio_sr_cap(dhd_bus_t *bus)
+ (bus->sih->chip == BCM4371_CHIP_ID) ||
+ (BCM4349_CHIP(bus->sih->chip)) ||
+ (bus->sih->chip == BCM4350_CHIP_ID) ||
+- (bus->sih->chip == 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,
+@@ -980,7 +974,8 @@ dhdsdio_sr_init(dhd_bus_t *bus)
+ if (CHIPID(bus->sih->chip) == BCM43430_CHIP_ID ||
+ CHIPID(bus->sih->chip) == BCM43018_CHIP_ID ||
+ CHIPID(bus->sih->chip) == BCM4339_CHIP_ID ||
+- CHIPID(bus->sih->chip) == 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) {
+@@ -1048,18 +1043,8 @@ dhdsdio_clk_kso_enab(dhd_bus_t *bus, bool on)
+
+ wr_val |= (on << SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT);
+
+- {
+- struct mmc_host *host;
+- struct sdioh_info *sd = (struct sdioh_info *)(bus->sdh->sdioh);
+- struct sdio_func *func = sd->func[SDIO_FUNC_0];
+-
+- host = func->card->host;
++ bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SLEEPCSR, wr_val, &err);
+
+- mmc_retune_disable(host);
+- bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SLEEPCSR,
+- wr_val, &err);
+- mmc_retune_enable(host);
+- }
+
+ /* In case of 43012 chip, the chip could go down immediately after KSO bit is cleared.
+ * So the further reads of KSO register could fail. Thereby just bailing out immediately
+@@ -1994,12 +1979,16 @@ dhd_bus_txdata(struct dhd_bus *bus, void *pkt)
+
+ prec = PRIO2PREC((PKTPRIO(pkt) & PRIOMASK));
+
++ /* move from dhdsdio_sendfromq(), try to orphan skb early */
++ if (bus->dhd->conf->orphan_move)
++ PKTORPHAN(pkt, bus->dhd->conf->tsq);
++
+ /* Check for existing queue, current flow-control, pending event, or pending clock */
+ if (dhd_deferred_tx || bus->fcstate || pktq_len(&bus->txq) || bus->dpc_sched ||
+ (!DATAOK(bus)) || (bus->flowcontrol & NBITVAL(prec)) ||
+ (bus->clkstate != CLK_AVAIL)) {
+ bool deq_ret;
+- int pkq_len;
++ int pkq_len = 0;
+
+ DHD_TRACE(("%s: deferring pktq len %d\n", __FUNCTION__, pktq_len(&bus->txq)));
+ bus->fcqueued++;
+@@ -2028,10 +2017,12 @@ dhd_bus_txdata(struct dhd_bus *bus, void *pkt)
+ } else
+ ret = BCME_OK;
+
+- dhd_os_sdlock_txq(bus->dhd);
+- pkq_len = pktq_len(&bus->txq);
+- dhd_os_sdunlock_txq(bus->dhd);
+- if (pkq_len >= FCHI) {
++ if (dhd_doflow) {
++ dhd_os_sdlock_txq(bus->dhd);
++ pkq_len = pktq_len(&bus->txq);
++ dhd_os_sdunlock_txq(bus->dhd);
++ }
++ if (dhd_doflow && pkq_len >= FCHI) {
+ bool wlfc_enabled = FALSE;
+ #ifdef PROP_TXSTATUS
+ wlfc_enabled = (dhd_wlfc_flowcontrol(bus->dhd, ON, FALSE) !=
+@@ -2635,7 +2626,8 @@ dhdsdio_sendfromq(dhd_bus_t *bus, uint maxframes)
+ }
+ }
+ #endif /* DHD_LOSSLESS_ROAMING */
+- PKTORPHAN(pkts[i], bus->dhd->conf->tsq);
++ if (!bus->dhd->conf->orphan_move)
++ PKTORPHAN(pkts[i], bus->dhd->conf->tsq);
+ datalen += PKTLEN(osh, pkts[i]);
+ }
+ dhd_os_sdunlock_txq(bus->dhd);
+@@ -2672,9 +2664,11 @@ dhdsdio_sendfromq(dhd_bus_t *bus, uint maxframes)
+
+ }
+
+- dhd_os_sdlock_txq(bus->dhd);
+- txpktqlen = pktq_len(&bus->txq);
+- dhd_os_sdunlock_txq(bus->dhd);
++ if (dhd_doflow) {
++ dhd_os_sdlock_txq(bus->dhd);
++ txpktqlen = pktq_len(&bus->txq);
++ dhd_os_sdunlock_txq(bus->dhd);
++ }
+
+ /* Do flow-control if needed */
+ if (dhd->up && (dhd->busstate == DHD_BUS_DATA) && (txpktqlen < FCLOW)) {
+@@ -2727,7 +2721,6 @@ dhd_bus_txctl(struct dhd_bus *bus, uchar *msg, uint msglen)
+ uint8 doff = 0;
+ int ret = -1;
+ uint8 sdpcm_hdrlen = bus->txglom_enable ? SDPCM_HDRLEN_TXGLOM : SDPCM_HDRLEN;
+- int cnt = 0;
+
+ DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+@@ -2767,17 +2760,13 @@ dhd_bus_txctl(struct dhd_bus *bus, uchar *msg, uint msglen)
+
+
+ /* Need to lock here to protect txseq and SDIO tx calls */
+-retry:
+- dhd_os_sdlock(bus->dhd);
+- if (cnt < bus->dhd->conf->txctl_tmo_fix && !TXCTLOK(bus)) {
+- cnt++;
+- dhd_os_sdunlock(bus->dhd);
+- OSL_SLEEP(1);
+- if (cnt >= (bus->dhd->conf->txctl_tmo_fix))
+- DHD_ERROR(("%s: No bus credit bus->tx_max %d, bus->tx_seq %d, last retry cnt %d\n",
+- __FUNCTION__, bus->tx_max, bus->tx_seq, cnt));
+- goto retry;
++ if (bus->dhd->conf->txctl_tmo_fix > 0 && !TXCTLOK(bus)) {
++ bus->ctrl_wait = TRUE;
++ wait_event_interruptible_timeout(bus->ctrl_tx_wait, TXCTLOK(bus),
++ msecs_to_jiffies(bus->dhd->conf->txctl_tmo_fix));
++ bus->ctrl_wait = FALSE;
+ }
++ dhd_os_sdlock(bus->dhd);
+
+ BUS_WAKE(bus);
+
+@@ -2923,6 +2912,7 @@ dhd_bus_rxctl(struct dhd_bus *bus, uchar *msg, uint msglen)
+ {
+ int timeleft;
+ uint rxlen = 0;
++ static uint cnt = 0;
+
+ DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+@@ -2930,7 +2920,7 @@ dhd_bus_rxctl(struct dhd_bus *bus, uchar *msg, uint msglen)
+ return -EIO;
+
+ /* Wait until control frame is available */
+- timeleft = dhd_os_ioctl_resp_wait(bus->dhd, &bus->rxlen);
++ timeleft = dhd_os_ioctl_resp_wait(bus->dhd, &bus->rxlen, false);
+
+ dhd_os_sdlock(bus->dhd);
+ rxlen = bus->rxlen;
+@@ -2938,6 +2928,32 @@ dhd_bus_rxctl(struct dhd_bus *bus, uchar *msg, uint msglen)
+ bus->rxlen = 0;
+ dhd_os_sdunlock(bus->dhd);
+
++ if (bus->dhd->conf->ctrl_resched > 0 && !rxlen && timeleft == 0) {
++ cnt++;
++ if (cnt <= bus->dhd->conf->ctrl_resched) {
++ uint32 status, retry = 0;
++ R_SDREG(status, &bus->regs->intstatus, retry);
++ if ((status & I_HMB_HOST_INT) || PKT_AVAILABLE(bus, status)) {
++ DHD_ERROR(("%s: reschedule dhd_dpc, cnt=%d, status=0x%x\n",
++ __FUNCTION__, cnt, status));
++ bus->ipend = TRUE;
++ bus->dpc_sched = TRUE;
++ dhd_sched_dpc(bus->dhd);
++
++ /* Wait until control frame is available */
++ timeleft = dhd_os_ioctl_resp_wait(bus->dhd, &bus->rxlen, true);
++
++ dhd_os_sdlock(bus->dhd);
++ rxlen = bus->rxlen;
++ bcopy(bus->rxctl, msg, MIN(msglen, rxlen));
++ bus->rxlen = 0;
++ dhd_os_sdunlock(bus->dhd);
++ }
++ }
++ } else {
++ cnt = 0;
++ }
++
+ if (rxlen) {
+ DHD_CTL(("%s: resumed on rxctl frame, got %d expected %d\n",
+ __FUNCTION__, rxlen, msglen));
+@@ -6832,6 +6848,8 @@ exit:
+ }
+ }
+
++ if (bus->ctrl_wait && TXCTLOK(bus))
++ wake_up_interruptible(&bus->ctrl_tx_wait);
+ dhd_os_sdunlock(bus->dhd);
+ #ifdef DEBUG_DPC_THREAD_WATCHDOG
+ if (bus->dhd->dhd_bug_on) {
+@@ -7631,14 +7649,12 @@ dhdsdio_chipmatch(uint16 chipid)
+
+ if (chipid == BCM43012_CHIP_ID)
+ return TRUE;
++ if (chipid == BCM4362_CHIP_ID)
++ return TRUE;
+
+ return FALSE;
+ }
+
+-#if defined(MULTIPLE_SUPPLICANT)
+-extern void wl_android_post_init(void); // terence 20120530: fix critical section in dhd_open and dhdsdio_probe
+-#endif
+-
+ static void *
+ dhdsdio_probe(uint16 venid, uint16 devid, uint16 bus_no, uint16 slot,
+ uint16 func, uint bustype, void *regsva, osl_t * osh, void *sdh)
+@@ -7649,17 +7665,7 @@ dhdsdio_probe(uint16 venid, uint16 devid, uint16 bus_no, uint16 slot,
+ struct ether_addr ea_addr;
+ #endif
+
+-#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
++ DHD_MUTEX_LOCK();
+
+ /* Init global variables at run-time, not as part of the declaration.
+ * This is required to support init/de-init of the driver. Initialization
+@@ -7741,6 +7747,7 @@ dhdsdio_probe(uint16 venid, uint16 devid, uint16 bus_no, uint16 slot,
+ #if defined(SUPPORT_P2P_GO_PS)
+ init_waitqueue_head(&bus->bus_sleep);
+ #endif /* LINUX && SUPPORT_P2P_GO_PS */
++ init_waitqueue_head(&bus->ctrl_tx_wait);
+
+ /* attempt to attach to the dongle */
+ if (!(dhdsdio_probe_attach(bus, osh, sdh, regsva, devid))) {
+@@ -7839,11 +7846,8 @@ dhdsdio_probe(uint16 venid, uint16 devid, uint16 bus_no, uint16 slot,
+
+ #if defined(MULTIPLE_SUPPLICANT)
+ wl_android_post_init(); // terence 20120530: fix critical section in dhd_open and dhdsdio_probe
+-#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
++#endif /* MULTIPLE_SUPPLICANT */
++ DHD_MUTEX_UNLOCK();
+
+ return bus;
+
+@@ -7851,12 +7855,7 @@ fail:
+ dhdsdio_release(bus, osh);
+
+ forcereturn:
+-#if defined(MULTIPLE_SUPPLICANT)
+-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25))
+- mutex_unlock(&_dhd_sdio_mutex_lock_);
+- DHD_ERROR(("%s : the lock is released.\n", __FUNCTION__));
+-#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */
+-#endif
++ DHD_MUTEX_UNLOCK();
+
+ return NULL;
+ }
+@@ -7910,7 +7909,7 @@ dhdsdio_probe_attach(struct dhd_bus *bus, osl_t *osh, void *sdh, void *regsva,
+ bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
+ DHD_INIT_CLKCTL2, &err);
+ OSL_DELAY(200);
+-
++
+ if (DHD_INFO_ON()) {
+ for (fn = 0; fn <= numfn; fn++) {
+ if (!(cis[fn] = MALLOC(osh, SBSDIO_CIS_SIZE_LIMIT))) {
+@@ -8051,6 +8050,9 @@ dhdsdio_probe_attach(struct dhd_bus *bus, osl_t *osh, void *sdh, void *regsva,
+ case BCM4347_CHIP_GRPID:
+ bus->dongle_ram_base = CR4_4347_RAM_BASE;
+ break;
++ case 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",
+@@ -8284,7 +8286,7 @@ dhd_set_path_params(struct dhd_bus *bus)
+
+ dhd_conf_read_config(bus->dhd, bus->dhd->conf_path);
+
+- dhd_conf_set_fw_name_by_chip(bus->dhd, bus->fw_path, bus->nv_path);
++ dhd_conf_set_fw_name_by_chip(bus->dhd, bus->fw_path);
+ dhd_conf_set_nv_name_by_chip(bus->dhd, bus->nv_path);
+ dhd_conf_set_clm_name_by_chip(bus->dhd, bus->dhd->clm_path);
+
+@@ -8309,15 +8311,12 @@ dhd_set_bus_params(struct dhd_bus *bus)
+ }
+ if (bus->dhd->conf->use_rxchain >= 0) {
+ bus->use_rxchain = (bool)bus->dhd->conf->use_rxchain;
+- printf("%s: set use_rxchain %d\n", __FUNCTION__, bus->dhd->conf->use_rxchain);
+ }
+ if (bus->dhd->conf->txinrx_thres >= 0) {
+ bus->txinrx_thres = bus->dhd->conf->txinrx_thres;
+- printf("%s: set txinrx_thres %d\n", __FUNCTION__, bus->txinrx_thres);
+ }
+ if (bus->dhd->conf->txglomsize >= 0) {
+ bus->txglomsize = bus->dhd->conf->txglomsize;
+- printf("%s: set txglomsize %d\n", __FUNCTION__, bus->dhd->conf->txglomsize);
+ }
+ }
+
+@@ -8462,33 +8461,14 @@ dhdsdio_disconnect(void *ptr)
+
+ DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+-#if defined(MULTIPLE_SUPPLICANT)
+-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25))
+- if (mutex_is_locked(&_dhd_sdio_mutex_lock_) == 0) {
+- DHD_ERROR(("%s : no mutex held. set lock\n", __FUNCTION__));
+- }
+- 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
+-
+-
++ DHD_MUTEX_LOCK();
+ if (bus) {
+ ASSERT(bus->dhd);
+- /* Advertise bus cleanup during rmmod */
+- dhdsdio_advertise_bus_cleanup(bus->dhd);
++ /* Advertise bus remove during rmmod */
++ dhdsdio_advertise_bus_remove(bus->dhd);
+ dhdsdio_release(bus, bus->dhd->osh);
+ }
+-
+-#if defined(MULTIPLE_SUPPLICANT)
+-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25))
+- mutex_unlock(&_dhd_sdio_mutex_lock_);
+- DHD_ERROR(("%s : the lock is released.\n", __FUNCTION__));
+-#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) */
+-#endif /* LINUX */
+-
++ DHD_MUTEX_UNLOCK();
+
+ DHD_TRACE(("%s: Disconnected\n", __FUNCTION__));
+ }
+@@ -9262,6 +9242,27 @@ dhdsdio_advertise_bus_cleanup(dhd_pub_t *dhdp)
+ return;
+ }
+
++static void
++dhdsdio_advertise_bus_remove(dhd_pub_t *dhdp)
++{
++ unsigned long flags;
++ int timeleft;
++
++ DHD_LINUX_GENERAL_LOCK(dhdp, flags);
++ dhdp->busstate = DHD_BUS_REMOVE;
++ DHD_LINUX_GENERAL_UNLOCK(dhdp, flags);
++
++ timeleft = dhd_os_busbusy_wait_negation(dhdp, &dhdp->dhd_bus_busy_state);
++ if ((timeleft == 0) || (timeleft == 1)) {
++ DHD_ERROR(("%s : Timeout due to dhd_bus_busy_state=0x%x\n",
++ __FUNCTION__, dhdp->dhd_bus_busy_state));
++ ASSERT(0);
++ }
++
++ return;
++}
++
++
+ int
+ dhd_bus_devreset(dhd_pub_t *dhdp, uint8 flag)
+ {
+diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_static_buf.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_static_buf.c
+index c615ba041829..188a0ac00dc8 100644
+--- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_static_buf.c
++++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_static_buf.c
+@@ -1,4 +1,4 @@
+-/* SPDX-License-Identifier: GPL-2.0 */
++/* SPDX-License-Identifier: GPL-2.0 */
+ #include
+ #include
+ #include
+@@ -7,7 +7,7 @@
+ #include
+ #include
+
+-#define DHD_STATIC_VERSION_STR "1.579.77.41.1"
++#define DHD_STATIC_VERSION_STR "1.579.77.41.9"
+
+ #define BCMDHD_SDIO
+ #define BCMDHD_PCIE
+@@ -53,7 +53,7 @@ enum dhd_prealloc_index {
+ #define DHD_PREALLOC_OSL_BUF_SIZE (STATIC_BUF_MAX_NUM * STATIC_BUF_SIZE)
+ #define DHD_PREALLOC_WIPHY_ESCAN0_SIZE (64 * 1024)
+ #define DHD_PREALLOC_DHD_INFO_SIZE (30 * 1024)
+-#define DHD_PREALLOC_MEMDUMP_RAM_SIZE (770 * 1024)
++#define DHD_PREALLOC_MEMDUMP_RAM_SIZE (810 * 1024)
+ #define DHD_PREALLOC_DHD_WLFC_HANGER_SIZE (73 * 1024)
+ #define DHD_PREALLOC_WL_ESCAN_INFO_SIZE (66 * 1024)
+ #ifdef CONFIG_64BIT
+@@ -327,6 +327,8 @@ static int dhd_init_wlan_mem(void)
+ wlan_static_if_flow_lkup = kmalloc(DHD_PREALLOC_IF_FLOW_LKUP_SIZE, GFP_KERNEL);
+ if (!wlan_static_if_flow_lkup)
+ goto err_mem_alloc;
++ pr_err("%s: sectoin %d, size=%d\n", __func__,
++ DHD_PREALLOC_IF_FLOW_LKUP, DHD_PREALLOC_IF_FLOW_LKUP_SIZE);
+ #endif /* BCMDHD_PCIE */
+
+ wlan_static_dhd_memdump_ram_buf = kmalloc(DHD_PREALLOC_MEMDUMP_RAM_SIZE, GFP_KERNEL);
+@@ -347,31 +349,29 @@ static int dhd_init_wlan_mem(void)
+ pr_err("%s: sectoin %d, size=%d\n", __func__,
+ DHD_PREALLOC_WL_ESCAN_INFO, DHD_PREALLOC_WL_ESCAN_INFO_SIZE);
+
+- wlan_static_fw_verbose_ring_buf = kmalloc(
+- DHD_PREALLOC_WIPHY_ESCAN0_SIZE,
+- GFP_KERNEL);
++ wlan_static_fw_verbose_ring_buf = kmalloc(FW_VERBOSE_RING_SIZE, GFP_KERNEL);
+ if (!wlan_static_fw_verbose_ring_buf)
+ goto err_mem_alloc;
+ pr_err("%s: sectoin %d, size=%d\n", __func__,
+- DHD_PREALLOC_FW_VERBOSE_RING, DHD_PREALLOC_WL_ESCAN_INFO_SIZE);
++ DHD_PREALLOC_FW_VERBOSE_RING, FW_VERBOSE_RING_SIZE);
+
+- wlan_static_fw_event_ring_buf = kmalloc(DHD_PREALLOC_WIPHY_ESCAN0_SIZE, GFP_KERNEL);
++ wlan_static_fw_event_ring_buf = kmalloc(FW_EVENT_RING_SIZE, GFP_KERNEL);
+ if (!wlan_static_fw_event_ring_buf)
+ goto err_mem_alloc;
+ pr_err("%s: sectoin %d, size=%d\n", __func__,
+- DHD_PREALLOC_FW_EVENT_RING, DHD_PREALLOC_WL_ESCAN_INFO_SIZE);
++ DHD_PREALLOC_FW_EVENT_RING, FW_EVENT_RING_SIZE);
+
+- wlan_static_dhd_event_ring_buf = kmalloc(DHD_PREALLOC_WIPHY_ESCAN0_SIZE, GFP_KERNEL);
++ wlan_static_dhd_event_ring_buf = kmalloc(DHD_EVENT_RING_SIZE, GFP_KERNEL);
+ if (!wlan_static_dhd_event_ring_buf)
+ goto err_mem_alloc;
+ pr_err("%s: sectoin %d, size=%d\n", __func__,
+- DHD_PREALLOC_DHD_EVENT_RING, DHD_PREALLOC_WL_ESCAN_INFO_SIZE);
++ DHD_PREALLOC_DHD_EVENT_RING, DHD_EVENT_RING_SIZE);
+
+- wlan_static_nan_event_ring_buf = kmalloc(DHD_PREALLOC_WIPHY_ESCAN0_SIZE, GFP_KERNEL);
++ wlan_static_nan_event_ring_buf = kmalloc(NAN_EVENT_RING_SIZE, GFP_KERNEL);
+ if (!wlan_static_nan_event_ring_buf)
+ goto err_mem_alloc;
+ pr_err("%s: sectoin %d, size=%d\n", __func__,
+- DHD_PREALLOC_NAN_EVENT_RING, DHD_PREALLOC_WL_ESCAN_INFO_SIZE);
++ DHD_PREALLOC_NAN_EVENT_RING, NAN_EVENT_RING_SIZE);
+
+ return 0;
+
+diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_wlfc.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_wlfc.c
+index 678dbc387f6d..442af325a677 100644
+--- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_wlfc.c
++++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_wlfc.c
+@@ -1617,7 +1617,7 @@ _dhd_wlfc_pktq_flush(athost_wl_status_info_t* ctx, struct pktq *pq,
+ ASSERT(pq->len == 0);
+ } /* _dhd_wlfc_pktq_flush */
+
+-
++#ifndef BCMDBUS
+ /** !BCMDBUS specific function. Dequeues a packet from the caller supplied queue. */
+ static void*
+ _dhd_wlfc_pktq_pdeq_with_fn(struct pktq *pq, int prec, f_processpkt_t fn, void *arg)
+@@ -1723,6 +1723,7 @@ _dhd_wlfc_cleanup_txq(dhd_pub_t *dhd, f_processpkt_t fn, void *arg)
+ PKTFREE(wlfc->osh, pkt, TRUE);
+ }
+ } /* _dhd_wlfc_cleanup_txq */
++#endif /* !BCMDBUS */
+
+ /** called during eg detach */
+ void
+@@ -1741,8 +1742,10 @@ _dhd_wlfc_cleanup(dhd_pub_t *dhd, f_processpkt_t fn, void *arg)
+ /*
+ * flush sequence should be txq -> psq -> hanger/afq, hanger has to be last one
+ */
++#ifndef BCMDBUS
+ /* flush bus->txq */
+ _dhd_wlfc_cleanup_txq(dhd, fn, arg);
++#endif /* !BCMDBUS */
+
+ /* flush psq, search all entries, include nodes as well as interfaces */
+ total_entries = sizeof(wlfc->destination_entries)/sizeof(wlfc_mac_descriptor_t);
+@@ -2465,7 +2468,7 @@ _dhd_wlfc_fifocreditback_indicate(dhd_pub_t *dhd, uint8* credits)
+ return BCME_OK;
+ } /* _dhd_wlfc_fifocreditback_indicate */
+
+-
++#ifndef BCMDBUS
+ /** !BCMDBUS specific function */
+ static void
+ _dhd_wlfc_suppress_txq(dhd_pub_t *dhd, f_processpkt_t fn, void *arg)
+@@ -2544,6 +2547,7 @@ _dhd_wlfc_suppress_txq(dhd_pub_t *dhd, f_processpkt_t fn, void *arg)
+ _dhd_wlfc_fifocreditback_indicate(dhd, credits);
+ }
+ } /* _dhd_wlfc_suppress_txq */
++#endif /* !BCMDBUS */
+
+ static int
+ _dhd_wlfc_dbg_senum_check(dhd_pub_t *dhd, uint8 *value)
+@@ -3072,10 +3076,12 @@ dhd_wlfc_parse_header_info(dhd_pub_t *dhd, void* pktbuf, int tlv_hdr_len, uchar
+ _dhd_wlfc_interface_update(dhd, value, type);
+ }
+
++#ifndef BCMDBUS
+ if (entry && WLFC_GET_REORDERSUPP(dhd->wlfc_mode)) {
+ /* suppress all packets for this mac entry from bus->txq */
+ _dhd_wlfc_suppress_txq(dhd, _dhd_wlfc_entrypkt_fn, entry);
+ }
++#endif /* !BCMDBUS */
+ } /* while */
+
+ if (remainder != 0 && wlfc) {
+@@ -3407,6 +3413,15 @@ dhd_wlfc_commit_packets(dhd_pub_t *dhdp, f_commitpkt_t fcommit, void* commit_ctx
+
+ ctx = (athost_wl_status_info_t*)dhdp->wlfc_state;
+
++#ifdef BCMDBUS
++ if (!dhdp->up || (dhdp->busstate == DHD_BUS_DOWN)) {
++ if (pktbuf) {
++ PKTFREE(ctx->osh, pktbuf, TRUE);
++ rc = BCME_OK;
++ }
++ goto exit;
++ }
++#endif /* BCMDBUS */
+
+ if (dhdp->proptxstatus_module_ignore) {
+ if (pktbuf) {
+@@ -3593,10 +3608,17 @@ dhd_wlfc_init(dhd_pub_t *dhd)
+ DHD_ERROR(("%s: query wlfc_mode succeed, fw_caps=0x%x\n", __FUNCTION__, fw_caps));
+
+ if (WLFC_IS_OLD_DEF(fw_caps)) {
++#ifdef BCMDBUS
++ mode = WLFC_MODE_HANGER;
++#else
+ /* enable proptxtstatus v2 by default */
+ mode = WLFC_MODE_AFQ;
++#endif /* BCMDBUS */
+ } else {
+ WLFC_SET_AFQ(mode, WLFC_GET_AFQ(fw_caps));
++#ifdef BCMDBUS
++ WLFC_SET_AFQ(mode, 0);
++#endif /* BCMDBUS */
+ WLFC_SET_REUSESEQ(mode, WLFC_GET_REUSESEQ(fw_caps));
+ WLFC_SET_REORDERSUPP(mode, WLFC_GET_REORDERSUPP(fw_caps));
+ }
+@@ -3679,7 +3701,9 @@ dhd_wlfc_cleanup_txq(dhd_pub_t *dhd, f_processpkt_t fn, void *arg)
+ return WLFC_UNSUPPORTED;
+ }
+
++#ifndef BCMDBUS
+ _dhd_wlfc_cleanup_txq(dhd, fn, arg);
++#endif /* !BCMDBUS */
+
+ dhd_os_wlfc_unblock(dhd);
+
+diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_wlfc.h b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_wlfc.h
+old mode 100755
+new mode 100644
+index 1e8b01f97a44..54c6b3b4bceb
+--- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_wlfc.h
++++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_wlfc.h
+@@ -111,8 +111,13 @@ typedef struct wlfc_hanger {
+
+ #define WLFC_PSQ_LEN (4096 * 8)
+
++#ifdef BCMDBUS
++#define WLFC_FLOWCONTROL_HIWATER 512
++#define WLFC_FLOWCONTROL_LOWATER (WLFC_FLOWCONTROL_HIWATER / 4)
++#else
+ #define WLFC_FLOWCONTROL_HIWATER ((4096 * 8) - 256)
+ #define WLFC_FLOWCONTROL_LOWATER 256
++#endif
+
+ #if (WLFC_FLOWCONTROL_HIWATER >= (WLFC_PSQ_LEN - 256))
+ #undef WLFC_FLOWCONTROL_HIWATER
+diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/bcmdevs.h b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/bcmdevs.h
+old mode 100755
+new mode 100644
+index 70ef46788483..5437c8f2a1db
+--- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/bcmdevs.h
++++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/bcmdevs.h
+@@ -280,6 +280,10 @@
+ #define BCM4361_D11AC2G_ID 0x4420 /* 4361 802.11ac 2.4G device */
+ #define BCM4361_D11AC5G_ID 0x4421 /* 4361 802.11ac 5G device */
+
++#define BCM4362_D11AX_ID 0x4490 /* 4362 802.11ax dualband device */
++#define BCM4362_D11AX2G_ID 0x4491 /* 4362 802.11ax 2.4G device */
++#define BCM4362_D11AX5G_ID 0x4492 /* 4362 802.11ax 5G device */
++
+ #define BCM4364_D11AC_ID 0x4464 /* 4364 802.11ac dualband device */
+ #define BCM4364_D11AC2G_ID 0x446a /* 4364 802.11ac 2.4G device */
+ #define BCM4364_D11AC5G_ID 0x446b /* 4364 802.11ac 5G device */
+@@ -501,6 +505,7 @@
+ #define BCM4347_CHIP_ID 0x4347 /* 4347 chipcommon chipid */
+ #define BCM4357_CHIP_ID 0x4357 /* 4357 chipcommon chipid */
+ #define BCM4361_CHIP_ID 0x4361 /* 4361 chipcommon chipid */
++#define BCM4362_CHIP_ID 0x4362 /* 4362 chipcommon chipid */
+ #define BCM4347_CHIP(chipid) ((CHIPID(chipid) == BCM4347_CHIP_ID) || \
+ (CHIPID(chipid) == BCM4357_CHIP_ID) || \
+ (CHIPID(chipid) == BCM4361_CHIP_ID))
+diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/dbus.h b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/dbus.h
+index c926ba77e673..f4dec0d9ef36 100644
+--- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/dbus.h
++++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/dbus.h
+@@ -32,11 +32,22 @@
+ #define __DBUS_H__
+
+ #include "typedefs.h"
+-
+-#define DBUSTRACE(args)
++#include
++
++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,
+@@ -181,7 +192,8 @@ typedef struct dbus_extdl {
+ struct dbus_callbacks;
+ struct exec_parms;
+
+-typedef void *(*probe_cb_t)(void *arg, const char *desc, uint32 bustype, uint32 hdrlen);
++typedef void *(*probe_cb_t)(void *arg, const char *desc, uint32 bustype,
++ uint16 bus_no, uint16 slot, uint32 hdrlen);
+ typedef void (*disconnect_cb_t)(void *arg);
+ typedef void *(*exec_cb_t)(struct exec_parms *args);
+
+@@ -237,7 +249,7 @@ typedef struct {
+ int (*get_config)(void *bus, dbus_config_t *config);
+
+ bool (*device_exists)(void *bus);
+- bool (*dlneeded)(void *bus);
++ int (*dlneeded)(void *bus);
+ int (*dlstart)(void *bus, uint8 *fw, int len);
+ int (*dlrun)(void *bus);
+ bool (*recv_needed)(void *bus);
+@@ -299,26 +311,21 @@ extern int dbus_register(int vid, int pid, probe_cb_t prcb, disconnect_cb_t disc
+ void *param1, void *param2);
+ extern int dbus_deregister(void);
+
+-extern dbus_pub_t *dbus_attach(struct osl_info *osh, int rxsize, int nrxq, int ntxq,
+- void *cbarg, dbus_callbacks_t *cbs, dbus_extdl_t *extdl, struct shared_info *sh);
+-extern void dbus_detach(dbus_pub_t *pub);
+-
+-extern int dbus_download_firmware(dbus_pub_t *pub);
+-extern int dbus_up(dbus_pub_t *pub);
++//extern int dbus_download_firmware(dbus_pub_t *pub);
++//extern int dbus_up(struct dhd_bus *pub);
+ extern int dbus_down(dbus_pub_t *pub);
+-extern int dbus_stop(dbus_pub_t *pub);
++//extern int dbus_stop(struct dhd_bus *pub);
+ extern int dbus_shutdown(dbus_pub_t *pub);
+ extern void dbus_flowctrl_rx(dbus_pub_t *pub, bool on);
+
+ extern int dbus_send_txdata(dbus_pub_t *dbus, void *pktbuf);
+ extern int dbus_send_buf(dbus_pub_t *pub, uint8 *buf, int len, void *info);
+ extern int dbus_send_pkt(dbus_pub_t *pub, void *pkt, void *info);
+-extern int dbus_send_ctl(dbus_pub_t *pub, uint8 *buf, int len);
+-extern int dbus_recv_ctl(dbus_pub_t *pub, uint8 *buf, int len);
++//extern int dbus_send_ctl(struct dhd_bus *pub, uint8 *buf, int len);
++//extern int dbus_recv_ctl(struct dhd_bus *pub, uint8 *buf, int len);
+ extern int dbus_recv_bulk(dbus_pub_t *pub, uint32 ep_idx);
+ extern int dbus_poll_intr(dbus_pub_t *pub);
+ extern int dbus_get_stats(dbus_pub_t *pub, dbus_stats_t *stats);
+-extern int dbus_get_attrib(dbus_pub_t *pub, dbus_attrib_t *attrib);
+ extern int dbus_get_device_speed(dbus_pub_t *pub);
+ extern int dbus_set_config(dbus_pub_t *pub, dbus_config_t *config);
+ extern int dbus_get_config(dbus_pub_t *pub, dbus_config_t *config);
+@@ -332,8 +339,8 @@ extern int dbus_pnp_sleep(dbus_pub_t *pub);
+ extern int dbus_pnp_resume(dbus_pub_t *pub, int *fw_reload);
+ extern int dbus_pnp_disconnect(dbus_pub_t *pub);
+
+-extern int dbus_iovar_op(dbus_pub_t *pub, const char *name,
+- void *params, int plen, void *arg, int len, bool set);
++//extern int dhd_bus_iovar_op(dhd_pub_t *dhdp, const char *name,
++// void *params, int plen, void *arg, int len, bool set);
+
+ extern void *dhd_dbus_txq(const dbus_pub_t *pub);
+ extern uint dhd_dbus_hdrlen(const dbus_pub_t *pub);
+diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/epivers.h b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/epivers.h
+index 4cf4c70348c8..c014bb62f540 100644
+--- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/epivers.h
++++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/epivers.h
+@@ -46,6 +46,6 @@
+ #define EPI_VERSION_DEV 1.579.77.41
+
+ /* Driver Version String, ASCII, 32 chars max */
+-#define EPI_VERSION_STR "1.579.77.41.2 (r)"
++#define EPI_VERSION_STR "1.579.77.41.9 (r)"
+
+ #endif /* _epivers_h_ */
+diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/linux_osl.h b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/linux_osl.h
+old mode 100755
+new mode 100644
+index 3dd51bc372e5..b40ec111c08b
+--- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/linux_osl.h
++++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/linux_osl.h
+@@ -1145,6 +1145,7 @@ typedef struct sk_buff_head PKT_LIST;
+ #define PKTLIST_UNLINK(x, y) skb_unlink((struct sk_buff *)(y), (struct sk_buff_head *)(x))
+ #define PKTLIST_FINI(x) skb_queue_purge((struct sk_buff_head *)(x))
+
++#ifdef REPORT_FATAL_TIMEOUTS
+ typedef struct osl_timer {
+ struct timer_list *timer;
+ bool set;
+@@ -1156,5 +1157,6 @@ extern osl_timer_t * osl_timer_init(osl_t *osh, const char *name, void (*fn)(voi
+ extern void osl_timer_add(osl_t *osh, osl_timer_t *t, uint32 ms, bool periodic);
+ extern void osl_timer_update(osl_t *osh, osl_timer_t *t, uint32 ms, bool periodic);
+ extern bool osl_timer_del(osl_t *osh, osl_timer_t *t);
++#endif
+
+ #endif /* _linux_osl_h_ */
+diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/sbchipc.h b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/sbchipc.h
+index ffec624c53dc..cbc75b2f58e0 100644
+--- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/sbchipc.h
++++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/sbchipc.h
+@@ -3343,6 +3343,7 @@ typedef volatile struct {
+ #define CA7_4365_RAM_BASE (0x200000)
+
+ #define CR4_4347_RAM_BASE (0x170000)
++#define CR4_4362_RAM_BASE (0x170000)
+
+ /* 4335 chip OTP present & OTP select bits. */
+ #define SPROM4335_OTP_SELECT 0x00000010
+diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/usbrdl.h b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/usbrdl.h
+new file mode 100644
+index 000000000000..f15fbd697cea
+--- /dev/null
++++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/usbrdl.h
+@@ -0,0 +1,135 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Broadcom USB remote download definitions
++ *
++ * Copyright (C) 1999-2016, Broadcom Corporation
++ *
++ * Unless you and Broadcom execute a separate written software license
++ * agreement governing use of this software, this software is licensed to you
++ * under the terms of the GNU General Public License version 2 (the "GPL"),
++ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
++ * following added to such license:
++ *
++ * As a special exception, the copyright holders of this software give you
++ * permission to link this software with independent modules, and to copy and
++ * distribute the resulting executable under terms of your choice, provided that
++ * you also meet, for each linked independent module, the terms and conditions of
++ * the license of that module. An independent module is a module which is not
++ * derived from this software. The special exception does not apply to any
++ * modifications of the software.
++ *
++ * Notwithstanding the above, under no circumstances may you combine this
++ * software in any way with any other Broadcom software provided under a license
++ * other than the GPL, without Broadcom's express prior written consent.
++ *
++ *
++ * <>
++ *
++ * $Id: usbrdl.h 597933 2015-11-06 18:52:06Z $
++ */
++
++#ifndef _USB_RDL_H
++#define _USB_RDL_H
++
++/* Control messages: bRequest values */
++#define DL_GETSTATE 0 /* returns the rdl_state_t struct */
++#define DL_CHECK_CRC 1 /* currently unused */
++#define DL_GO 2 /* execute downloaded image */
++#define DL_START 3 /* initialize dl state */
++#define DL_REBOOT 4 /* reboot the device in 2 seconds */
++#define DL_GETVER 5 /* returns the bootrom_id_t struct */
++#define DL_GO_PROTECTED 6 /* execute the downloaded code and set reset event
++ * to occur in 2 seconds. It is the responsibility
++ * of the downloaded code to clear this event
++ */
++#define DL_EXEC 7 /* jump to a supplied address */
++#define DL_RESETCFG 8 /* To support single enum on dongle
++ * - Not used by bootloader
++ */
++#define DL_DEFER_RESP_OK 9 /* Potentially defer the response to setup
++ * if resp unavailable
++ */
++#define DL_CHGSPD 0x0A
++
++#define DL_HWCMD_MASK 0xfc /* Mask for hardware read commands: */
++#define DL_RDHW 0x10 /* Read a hardware address (Ctl-in) */
++#define DL_RDHW32 0x10 /* Read a 32 bit word */
++#define DL_RDHW16 0x11 /* Read 16 bits */
++#define DL_RDHW8 0x12 /* Read an 8 bit byte */
++#define DL_WRHW 0x14 /* Write a hardware address (Ctl-out) */
++#define DL_WRHW_BLK 0x13 /* Block write to hardware access */
++
++#define DL_CMD_WRHW 2
++
++
++/* states */
++#define DL_WAITING 0 /* waiting to rx first pkt that includes the hdr info */
++#define DL_READY 1 /* hdr was good, waiting for more of the compressed image */
++#define DL_BAD_HDR 2 /* hdr was corrupted */
++#define DL_BAD_CRC 3 /* compressed image was corrupted */
++#define DL_RUNNABLE 4 /* download was successful, waiting for go cmd */
++#define DL_START_FAIL 5 /* failed to initialize correctly */
++#define DL_NVRAM_TOOBIG 6 /* host specified nvram data exceeds DL_NVRAM value */
++#define DL_IMAGE_TOOBIG 7 /* download image too big (exceeds DATA_START for rdl) */
++
++#define TIMEOUT 5000 /* Timeout for usb commands */
++
++struct bcm_device_id {
++ char *name;
++ uint32 vend;
++ uint32 prod;
++};
++
++typedef struct {
++ uint32 state;
++ uint32 bytes;
++} rdl_state_t;
++
++typedef struct {
++ uint32 chip; /* Chip id */
++ uint32 chiprev; /* Chip rev */
++ uint32 ramsize; /* Size of RAM */
++ uint32 remapbase; /* Current remap base address */
++ uint32 boardtype; /* Type of board */
++ uint32 boardrev; /* Board revision */
++} bootrom_id_t;
++
++/* struct for backplane & jtag accesses */
++typedef struct {
++ uint32 cmd; /* tag to identify the cmd */
++ uint32 addr; /* backplane address for write */
++ uint32 len; /* length of data: 1, 2, 4 bytes */
++ uint32 data; /* data to write */
++} hwacc_t;
++
++
++/* struct for querying nvram params from bootloader */
++#define QUERY_STRING_MAX 32
++typedef struct {
++ uint32 cmd; /* tag to identify the cmd */
++ char var[QUERY_STRING_MAX]; /* param name */
++} nvparam_t;
++
++typedef void (*exec_fn_t)(void *sih);
++
++#define USB_CTRL_IN (USB_TYPE_VENDOR | 0x80 | USB_RECIP_INTERFACE)
++#define USB_CTRL_OUT (USB_TYPE_VENDOR | 0 | USB_RECIP_INTERFACE)
++
++#define USB_CTRL_EP_TIMEOUT 500 /* Timeout used in USB control_msg transactions. */
++#define USB_BULK_EP_TIMEOUT 500 /* Timeout used in USB bulk transactions. */
++
++#define RDL_CHUNK_MAX (64 * 1024) /* max size of each dl transfer */
++#define RDL_CHUNK 1500 /* size of each dl transfer */
++
++/* bootloader makes special use of trx header "offsets" array */
++#define TRX_OFFSETS_DLFWLEN_IDX 0 /* Size of the fw; used in uncompressed case */
++#define TRX_OFFSETS_JUMPTO_IDX 1 /* RAM address for jumpto after download */
++#define TRX_OFFSETS_NVM_LEN_IDX 2 /* Length of appended NVRAM data */
++#ifdef BCMTRXV2
++#define TRX_OFFSETS_DSG_LEN_IDX 3 /* Length of digital signature for the first image */
++#define TRX_OFFSETS_CFG_LEN_IDX 4 /* Length of config region, which is not digitally signed */
++#endif /* BCMTRXV2 */
++
++#define TRX_OFFSETS_DLBASE_IDX 0 /* RAM start address for download */
++
++#endif /* _USB_RDL_H */
+diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/wlioctl.h b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/wlioctl.h
+index 1e6a3a280e08..812182af77b0 100644
+--- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/wlioctl.h
++++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/wlioctl.h
+@@ -11548,6 +11548,15 @@ typedef enum wl_interface_type {
+ */
+ #define WL_INTERFACE_BSSID_INDEX_USE (1 << 4)
+
++#ifdef WLMESH
++typedef struct wl_interface_info {
++ uint16 ver; /* version of this struct */
++ struct ether_addr mac_addr; /* MAC address of the interface */
++ char ifname[BCM_MSG_IFNAME_MAX]; /* name of interface */
++ uint8 bsscfgidx; /* source bsscfg index */
++} wl_interface_info_t;
++#endif
++
+ typedef struct wl_interface_create {
+ uint16 ver; /* version of this struct */
+ uint32 flags; /* flags that defines the operation */
+@@ -12462,6 +12471,12 @@ enum wl_mesh_cmd_xtlv_id {
+ };
+ /* endif WLMESH */
+
++#ifdef WLMESH
++#ifndef SAE_MAX_PASSWD_LEN
++#define SAE_MAX_PASSWD_LEN 32
++#endif
++#endif
++
+ /* Fast BSS Transition parameter configuration */
+ #define FBT_PARAM_CURRENT_VERSION 0
+
+diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/linux_osl.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/linux_osl.c
+old mode 100755
+new mode 100644
+index efbcf36ecc10..ee07bd325532
+--- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/linux_osl.c
++++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/linux_osl.c
+@@ -2148,9 +2148,13 @@ osl_os_get_image_block(char *buf, int len, void *image)
+ if (!image)
+ return 0;
+
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
++ rdlen = kernel_read(fp, buf, len, &fp->f_pos);
++#else
+ rdlen = kernel_read(fp, fp->f_pos, buf, len);
+ if (rdlen > 0)
+ fp->f_pos += rdlen;
++#endif
+
+ return rdlen;
+ }
+@@ -2677,13 +2681,19 @@ osl_pkt_orphan_partial(struct sk_buff *skb, int tsq)
+ */
+ fraction = skb->truesize * (tsq - 1) / tsq;
+ skb->truesize -= fraction;
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
++ atomic_sub(fraction, &skb->sk->sk_wmem_alloc.refs);
++#else
+ atomic_sub(fraction, &skb->sk->sk_wmem_alloc);
++#endif /* LINUX_VERSION >= 4.13.0 */
++ skb_orphan(skb);
+ }
+ #endif /* LINUX_VERSION >= 3.6.0 && TSQ_MULTIPLIER */
+
+ /* timer apis */
+ /* Note: All timer api's are thread unsafe and should be protected with locks by caller */
+
++#ifdef REPORT_FATAL_TIMEOUTS
+ osl_timer_t *
+ osl_timer_init(osl_t *osh, const char *name, void (*fn)(void *arg), void *arg)
+ {
+@@ -2768,3 +2778,4 @@ osl_timer_del(osl_t *osh, osl_timer_t *t)
+ }
+ return (TRUE);
+ }
++#endif
+diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/siutils.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/siutils.c
+index 00ae5869a316..74cdcfafb32c 100644
+--- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/siutils.c
++++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/siutils.c
+@@ -604,7 +604,6 @@ si_doattach(si_info_t *sii, uint devid, osl_t *osh, volatile void *regs,
+ }
+
+ sih->bustype = bustype;
+-
+ #ifdef BCMBUSTYPE
+ if (bustype != BUSTYPE(bustype)) {
+ SI_ERROR(("si_doattach: bus type %d does not match configured bus type %d\n",
+@@ -636,7 +635,7 @@ si_doattach(si_info_t *sii, uint devid, osl_t *osh, volatile void *regs,
+ sih->chiprev = (w & CID_REV_MASK) >> CID_REV_SHIFT;
+ sih->chippkg = (w & CID_PKG_MASK) >> CID_PKG_SHIFT;
+
+-#if defined(HW_OOB) || defined(FORCE_WOWLAN)
++#if defined(BCMSDIO) && (defined(HW_OOB) || defined(FORCE_WOWLAN))
+ dhd_conf_set_hw_oob_intr(sdh, sih->chip);
+ #endif
+
+diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_android.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_android.c
+index 42afe36f8834..a73c28df22b9 100644
+--- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_android.c
++++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_android.c
+@@ -104,6 +104,10 @@ uint android_msg_level = ANDROID_ERROR_LEVEL;
+ #define CMD_SETBAND "SETBAND"
+ #define CMD_GETBAND "GETBAND"
+ #define CMD_COUNTRY "COUNTRY"
++#ifdef WLMESH
++#define CMD_SAE_SET_PASSWORD "SAE_SET_PASSWORD"
++#define CMD_SET_RSDB_MODE "RSDB_MODE"
++#endif
+ #define CMD_P2P_SET_NOA "P2P_SET_NOA"
+ #if !defined WL_ENABLE_P2P_IF
+ #define CMD_P2P_GET_NOA "P2P_GET_NOA"
+@@ -1068,7 +1072,7 @@ wl_cfg80211_get_sta_info(struct net_device *dev, char* command, int total_len)
+ error:
+ return bytes_written;
+ }
+-#endif /* CUSTOMER_HW4_PRIVATE_CMD */
++#endif
+
+ #ifdef WBTEXT
+ static int wl_android_wbtext(struct net_device *dev, char *command, int total_len)
+@@ -1179,6 +1183,7 @@ static int wl_cfg80211_wbtext_btm_delta(struct net_device *dev,
+ #define PNO_PARAM_SIZE 50
+ #define VALUE_SIZE 50
+ #define LIMIT_STR_FMT ("%50s %50s")
++
+ static int
+ wls_parse_batching_cmd(struct net_device *dev, char *command, int total_len)
+ {
+@@ -1187,7 +1192,8 @@ wls_parse_batching_cmd(struct net_device *dev, char *command, int total_len)
+ char *pos, *pos2, *token, *token2, *delim;
+ char param[PNO_PARAM_SIZE+1], value[VALUE_SIZE+1];
+ struct dhd_pno_batch_params batch_params;
+- DHD_PNO(("%s: command=%s, len=%d\n", __FUNCTION__, command, total_len));
++
++ ANDROID_INFO(("%s: command=%s, len=%d\n", __FUNCTION__, command, total_len));
+ if (total_len < strlen(CMD_WLS_BATCHING)) {
+ ANDROID_ERROR(("%s argument=%d less min size\n", __FUNCTION__, total_len));
+ err = BCME_ERROR;
+@@ -1212,13 +1218,13 @@ wls_parse_batching_cmd(struct net_device *dev, char *command, int total_len)
+ tokens = sscanf(token, LIMIT_STR_FMT, param, value);
+ if (!strncmp(param, PNO_PARAM_SCANFREQ, strlen(PNO_PARAM_SCANFREQ))) {
+ batch_params.scan_fr = simple_strtol(value, NULL, 0);
+- DHD_PNO(("scan_freq : %d\n", batch_params.scan_fr));
++ ANDROID_INFO(("scan_freq : %d\n", batch_params.scan_fr));
+ } else if (!strncmp(param, PNO_PARAM_BESTN, strlen(PNO_PARAM_BESTN))) {
+ batch_params.bestn = simple_strtol(value, NULL, 0);
+- DHD_PNO(("bestn : %d\n", batch_params.bestn));
++ ANDROID_INFO(("bestn : %d\n", batch_params.bestn));
+ } else if (!strncmp(param, PNO_PARAM_MSCAN, strlen(PNO_PARAM_MSCAN))) {
+ batch_params.mscan = simple_strtol(value, NULL, 0);
+- DHD_PNO(("mscan : %d\n", batch_params.mscan));
++ ANDROID_INFO(("mscan : %d\n", batch_params.mscan));
+ } else if (!strncmp(param, PNO_PARAM_CHANNEL, strlen(PNO_PARAM_CHANNEL))) {
+ i = 0;
+ pos2 = value;
+@@ -1238,7 +1244,7 @@ wls_parse_batching_cmd(struct net_device *dev, char *command, int total_len)
+ if (*token2 == 'A' || *token2 == 'B') {
+ batch_params.band = (*token2 == 'A')?
+ WLC_BAND_5G : WLC_BAND_2G;
+- DHD_PNO(("band : %s\n",
++ ANDROID_INFO(("band : %s\n",
+ (*token2 == 'A')? "A" : "B"));
+ } else {
+ if ((batch_params.nchan >= WL_NUMCHANNELS) ||
+@@ -1251,13 +1257,13 @@ wls_parse_batching_cmd(struct net_device *dev, char *command, int total_len)
+ batch_params.chan_list[i++] =
+ simple_strtol(token2, NULL, 0);
+ batch_params.nchan++;
+- DHD_PNO(("channel :%d\n",
++ ANDROID_INFO(("channel :%d\n",
+ batch_params.chan_list[i-1]));
+ }
+ }
+ } else if (!strncmp(param, PNO_PARAM_RTT, strlen(PNO_PARAM_RTT))) {
+ batch_params.rtt = simple_strtol(value, NULL, 0);
+- DHD_PNO(("rtt : %d\n", batch_params.rtt));
++ ANDROID_INFO(("rtt : %d\n", batch_params.rtt));
+ } else {
+ ANDROID_ERROR(("%s : unknown param: %s\n", __FUNCTION__, param));
+ err = BCME_ERROR;
+@@ -1294,6 +1300,7 @@ wls_parse_batching_cmd(struct net_device *dev, char *command, int total_len)
+ exit:
+ return err;
+ }
++
+ #ifndef WL_SCHED_SCAN
+ static int wl_android_set_pno_setup(struct net_device *dev, char *command, int total_len)
+ {
+@@ -1327,7 +1334,7 @@ static int wl_android_set_pno_setup(struct net_device *dev, char *command, int t
+ 0x00
+ };
+ #endif /* PNO_SET_DEBUG */
+- DHD_PNO(("%s: command=%s, len=%d\n", __FUNCTION__, command, total_len));
++ ANDROID_INFO(("%s: command=%s, len=%d\n", __FUNCTION__, command, total_len));
+
+ if (total_len < (strlen(CMD_PNOSETUP_SET) + sizeof(cmd_tlv_t))) {
+ ANDROID_ERROR(("%s argument=%d less min size\n", __FUNCTION__, total_len));
+@@ -1362,7 +1369,7 @@ static int wl_android_set_pno_setup(struct net_device *dev, char *command, int t
+ }
+ str_ptr++;
+ pno_time = simple_strtoul(str_ptr, &str_ptr, 16);
+- DHD_PNO(("%s: pno_time=%d\n", __FUNCTION__, pno_time));
++ ANDROID_INFO(("%s: pno_time=%d\n", __FUNCTION__, pno_time));
+
+ if (str_ptr[0] != 0) {
+ if ((str_ptr[0] != PNO_TLV_FREQ_REPEAT)) {
+@@ -1372,7 +1379,7 @@ static int wl_android_set_pno_setup(struct net_device *dev, char *command, int t
+ }
+ str_ptr++;
+ pno_repeat = simple_strtoul(str_ptr, &str_ptr, 16);
+- DHD_PNO(("%s :got pno_repeat=%d\n", __FUNCTION__, pno_repeat));
++ ANDROID_INFO(("%s :got pno_repeat=%d\n", __FUNCTION__, pno_repeat));
+ if (str_ptr[0] != PNO_TLV_FREQ_EXPO_MAX) {
+ ANDROID_ERROR(("%s FREQ_EXPO_MAX corrupted field size\n",
+ __FUNCTION__));
+@@ -1380,7 +1387,7 @@ static int wl_android_set_pno_setup(struct net_device *dev, char *command, int t
+ }
+ str_ptr++;
+ pno_freq_expo_max = simple_strtoul(str_ptr, &str_ptr, 16);
+- DHD_PNO(("%s: pno_freq_expo_max=%d\n",
++ ANDROID_INFO(("%s: pno_freq_expo_max=%d\n",
+ __FUNCTION__, pno_freq_expo_max));
+ }
+ }
+@@ -1552,10 +1559,6 @@ int wl_android_wifi_on(struct net_device *dev)
+ {
+ int ret = 0;
+ int retry = POWERUP_MAX_RETRY;
+-#ifdef IAPSTA_PREINIT
+- int bytes_written = 0;
+- struct dhd_conf *conf;
+-#endif
+
+ if (!dev) {
+ ANDROID_ERROR(("%s: dev is null\n", __FUNCTION__));
+@@ -1589,30 +1592,22 @@ int wl_android_wifi_on(struct net_device *dev)
+ ANDROID_ERROR(("\nfailed to power up wifi chip, max retry reached **\n\n"));
+ goto exit;
+ }
+-#ifdef BCMSDIO
++#if defined(BCMSDIO) || defined(BCMDBUS)
+ ret = dhd_net_bus_devreset(dev, FALSE);
+ if (ret)
+ goto err;
++#ifdef BCMSDIO
+ dhd_net_bus_resume(dev, 1);
+ #endif /* BCMSDIO */
+-
+-#ifndef BCMPCIE
++#endif /* BCMSDIO || BCMDBUS */
++#if defined(BCMSDIO) || defined(BCMDBUS)
+ if (!ret) {
+ if (dhd_dev_init_ioctl(dev) < 0) {
+ ret = -EFAULT;
+ goto err;
+ }
+ }
+-#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
++#endif /* BCMSDIO || BCMDBUS */
+ g_wifi_on = TRUE;
+ }
+
+@@ -1621,15 +1616,17 @@ exit:
+ dhd_net_if_unlock(dev);
+ return ret;
+
+-#ifdef BCMSDIO
++#if defined(BCMSDIO) || defined(BCMDBUS)
+ err:
+ dhd_net_bus_devreset(dev, TRUE);
++#ifdef BCMSDIO
+ dhd_net_bus_suspend(dev);
++#endif /* BCMSDIO */
+ dhd_net_wifi_platform_set_power(dev, FALSE, WIFI_TURNOFF_DELAY);
+ printf("%s: Failed\n", __FUNCTION__);
+ dhd_net_if_unlock(dev);
+ return ret;
+-#endif
++#endif /* BCMSDIO || BCMDBUS */
+ }
+
+ int wl_android_wifi_off(struct net_device *dev, bool on_failure)
+@@ -1652,12 +1649,12 @@ int wl_android_wifi_off(struct net_device *dev, bool on_failure)
+ dhd_net_if_lock(dev);
+ printf("%s in 2: g_wifi_on=%d, on_failure=%d\n", __FUNCTION__, g_wifi_on, on_failure);
+ if (g_wifi_on || on_failure) {
+-#if defined(BCMSDIO) || defined(BCMPCIE)
++#if defined(BCMSDIO) || defined(BCMPCIE) || defined(BCMDBUS)
+ ret = dhd_net_bus_devreset(dev, TRUE);
+ #if defined(BCMSDIO)
+ dhd_net_bus_suspend(dev);
+ #endif /* BCMSDIO */
+-#endif /* BCMSDIO || BCMPCIE */
++#endif /* BCMSDIO || BCMPCIE || BCMDBUS */
+ dhd_net_wifi_platform_set_power(dev, FALSE, WIFI_TURNOFF_DELAY);
+ g_wifi_on = FALSE;
+ }
+@@ -2497,6 +2494,7 @@ wl_android_set_auto_channel(struct net_device *dev, const char* cmd_str,
+ uint32 band = WLC_BAND_2G;
+ uint32 buf_size;
+ char *pos = command;
++ int band_new, band_cur;
+
+ if (cmd_str) {
+ ANDROID_INFO(("Command: %s len:%d \n", cmd_str, (int)strlen(cmd_str)));
+@@ -2516,20 +2514,22 @@ wl_android_set_auto_channel(struct net_device *dev, const char* cmd_str,
+ (channel == APCS_BAND_2G_LEGACY2)) {
+ band = WLC_BAND_2G;
+ } else {
+- ANDROID_ERROR(("Invalid argument\n"));
++ ANDROID_ERROR(("%s: Invalid argument\n", __FUNCTION__));
+ return -EINVAL;
+ }
+ }
+ } else {
+ /* If no argument is provided, default to 2G */
+- ANDROID_ERROR(("No argument given default to 2.4G scan\n"));
++ ANDROID_ERROR(("%s: No argument given default to 2.4G scan\n", __FUNCTION__));
+ band = WLC_BAND_2G;
+ }
+- ANDROID_INFO(("HAPD_AUTO_CHANNEL = %d, band=%d \n", channel, band));
++ ANDROID_INFO(("%s : HAPD_AUTO_CHANNEL = %d, band=%d \n", __FUNCTION__, channel, band));
++
++ ret = wldev_ioctl_set(dev, WLC_GET_BAND, &band_cur, sizeof(band_cur));
+
+ if ((ret =
+ wldev_ioctl_get(dev, WLC_GET_SPECT_MANAGMENT, &spect, sizeof(spect))) < 0) {
+- ANDROID_ERROR(("ACS: error getting the spect\n"));
++ ANDROID_ERROR(("%s: ACS: error getting the spect\n", __FUNCTION__));
+ goto done;
+ }
+
+@@ -2551,15 +2551,19 @@ wl_android_set_auto_channel(struct net_device *dev, const char* cmd_str,
+
+ reqbuf = kzalloc(CHANSPEC_BUF_SIZE, GFP_KERNEL);
+ if (reqbuf == NULL) {
+- ANDROID_ERROR(("failed to allocate chanspec buffer\n"));
++ ANDROID_ERROR(("%s: failed to allocate chanspec buffer\n", __FUNCTION__));
+ return -ENOMEM;
+ }
+
+ if (band == WLC_BAND_AUTO) {
+- ANDROID_INFO(("ACS full channel scan \n"));
++ ANDROID_INFO(("%s: ACS full channel scan \n", __func__));
+ reqbuf[0] = htod32(0);
+ } else if (band == WLC_BAND_5G) {
+- ANDROID_INFO(("ACS 5G band scan \n"));
++ band_new = band_cur==WLC_BAND_2G ? band_cur : WLC_BAND_5G;
++ ret = wldev_ioctl_set(dev, WLC_SET_BAND, &band_new, sizeof(band_new));
++ if (ret < 0)
++ WL_ERR(("WLC_SET_BAND error %d\n", ret));
++ ANDROID_INFO(("%s: ACS 5G band scan \n", __func__));
+ if ((ret = wl_cfg80211_get_chanspecs_5g(dev, reqbuf, CHANSPEC_BUF_SIZE)) < 0) {
+ ANDROID_ERROR(("ACS 5g chanspec retreival failed! \n"));
+ goto done;
+@@ -2569,7 +2573,7 @@ wl_android_set_auto_channel(struct net_device *dev, const char* cmd_str,
+ * If channel argument is not provided/ argument 20 is provided,
+ * Restrict channel to 2GHz, 20MHz BW, No SB
+ */
+- ANDROID_INFO(("ACS 2G band scan \n"));
++ ANDROID_INFO(("%s: ACS 2G band scan \n", __func__));
+ if ((ret = wl_cfg80211_get_chanspecs_2g(dev, reqbuf, CHANSPEC_BUF_SIZE)) < 0) {
+ ANDROID_ERROR(("ACS 2g chanspec retreival failed! \n"));
+ goto done;
+@@ -2579,11 +2583,12 @@ wl_android_set_auto_channel(struct net_device *dev, const char* cmd_str,
+ goto done2;
+ }
+
+- buf_size = (band == WLC_BAND_AUTO) ? sizeof(int) : CHANSPEC_BUF_SIZE;
++ buf_size = CHANSPEC_BUF_SIZE;
+ ret = wldev_ioctl_set(dev, WLC_START_CHANNEL_SEL, (void *)reqbuf,
+ buf_size);
+ if (ret < 0) {
+- ANDROID_ERROR(("can't start auto channel scan, err = %d\n", ret));
++ ANDROID_ERROR(("%s: can't start auto channel scan, err = %d\n",
++ __FUNCTION__, ret));
+ channel = 0;
+ goto done;
+ }
+@@ -2609,6 +2614,18 @@ wl_android_set_auto_channel(struct net_device *dev, const char* cmd_str,
+ chosen = dtoh32(chosen);
+ }
+
++ if ((ret == 0) && (dtoh32(chosen) != 0)) {
++ uint chip;
++ chip = dhd_conf_get_chip(dhd_get_pub(dev));
++ if (chip != BCM43143_CHIP_ID) {
++ u32 chanspec = 0;
++ chanspec = wl_chspec_driver_to_host(chosen);
++ ANDROID_INFO(("%s: selected chanspec = 0x%x\n", __FUNCTION__, chanspec));
++ chosen = wf_chspec_ctlchan(chanspec);
++ ANDROID_INFO(("%s: selected chosen = 0x%x\n", __FUNCTION__, chosen));
++ }
++ }
++
+ if (chosen) {
+ int chosen_band;
+ int apcs_band;
+@@ -2623,12 +2640,15 @@ wl_android_set_auto_channel(struct net_device *dev, const char* cmd_str,
+ #endif /* D11AC_IOTYPES */
+ apcs_band = (band == WLC_BAND_AUTO) ? WLC_BAND_2G : band;
+ chosen_band = (channel <= CH_MAX_2G_CHANNEL) ? WLC_BAND_2G : WLC_BAND_5G;
+- if (apcs_band == chosen_band) {
++ if (band == WLC_BAND_AUTO) {
++ printf("%s: selected channel = %d\n", __FUNCTION__, channel);
++ break;
++ } else if (apcs_band == chosen_band) {
+ printf("%s: selected channel = %d\n", __FUNCTION__, channel);
+ break;
+ }
+ }
+- ANDROID_INFO(("%d tried, ret = %d, chosen = 0x%x\n",
++ ANDROID_INFO(("%s: %d tried, ret = %d, chosen = 0x%x\n", __FUNCTION__,
+ (APCS_MAX_RETRY - retry), ret, chosen));
+ OSL_SLEEP(250);
+ }
+@@ -2641,12 +2661,16 @@ done:
+ } else {
+ channel = APCS_DEFAULT_2G_CH;
+ }
+- ANDROID_ERROR(("ACS failed. Fall back to default channel (%d) \n", channel));
++ ANDROID_ERROR(("%s: ACS failed."
++ " Fall back to default channel (%d) \n", __FUNCTION__, channel));
+ }
+ done2:
++ ret = wldev_ioctl_set(dev, WLC_SET_BAND, &band_cur, sizeof(band_cur));
++ if (ret < 0)
++ WL_ERR(("WLC_SET_BAND error %d\n", ret));
+ if (spect > 0) {
+ if ((ret = wl_cfg80211_set_spect(dev, spect) < 0)) {
+- ANDROID_ERROR(("ACS: error while setting spect\n"));
++ ANDROID_ERROR(("%s: ACS: error while setting spect\n", __FUNCTION__));
+ }
+ }
+
+@@ -2660,7 +2684,7 @@ done2:
+ else
+ pos += snprintf(pos, total_len, "5g=");
+ pos += snprintf(pos, total_len, "%d", channel);
+- ANDROID_INFO(("command result is %s \n", command));
++ ANDROID_INFO(("%s: command result is %s \n", __FUNCTION__, command));
+ return strlen(command);
+ } else {
+ return ret;
+@@ -3754,8 +3778,7 @@ wl_cfg80211_p2plo_offload(struct net_device *dev, char *cmd, char* buf, int len)
+ }
+ #endif /* P2P_LISTEN_OFFLOADING */
+
+-#ifdef WL_CFG80211
+-#ifdef BCM4359_CHIP
++#if defined(BCM4359_CHIP) && defined(WL_CFG80211)
+ int
+ wl_android_murx_bfe_cap(struct net_device *dev, int val)
+ {
+@@ -3797,7 +3820,6 @@ wl_android_murx_bfe_cap(struct net_device *dev, int val)
+ return err;
+ }
+ #endif /* BCM4359_CHIP */
+-#endif
+
+ #ifdef SUPPORT_AP_HIGHER_BEACONRATE
+ int
+@@ -4203,6 +4225,40 @@ wl_android_make_hang_with_reason(struct net_device *dev, const char *string_num)
+ }
+ #endif /* DHD_HANG_SEND_UP_TEST */
+
++#ifdef WL_CFG80211
++#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 */
++#endif /* WL_CFG80211 */
++
+ #ifdef SUPPORT_LQCM
+ static int
+ wl_android_lqcm_enable(struct net_device *net, int lqcm_enable)
+@@ -4813,7 +4869,7 @@ wl_handle_private_cmd(struct net_device *net, char *command, u32 cmd_len)
+ bytes_written = BCME_DISABLED;
+ #else /* DISABLE_SETBAND */
+ uint band = *(command + strlen(CMD_SETBAND) + 1) - '0';
+- if (dhd_conf_get_band(dhd_get_pub(net)) != WLC_BAND_AUTO) {
++ if (dhd_conf_get_band(dhd_get_pub(net)) >= WLC_BAND_AUTO) {
+ printf("%s: Band is fixed in config.txt\n", __FUNCTION__);
+ } else
+ bytes_written = wl_cfg80211_set_if_band(net, band);
+@@ -4824,6 +4880,14 @@ wl_handle_private_cmd(struct net_device *net, char *command, u32 cmd_len)
+ bytes_written = wl_android_get_band(net, command, priv_cmd.total_len);
+ }
+ #ifdef WL_CFG80211
++ else if (strnicmp(command, CMD_SET_CSA, strlen(CMD_SET_CSA)) == 0) {
++ bytes_written = wl_android_set_csa(net, command, priv_cmd.total_len);
++ } else if (strnicmp(command, CMD_80211_MODE, strlen(CMD_80211_MODE)) == 0) {
++ bytes_written = wl_android_get_80211_mode(net, command, priv_cmd.total_len);
++ } else if (strnicmp(command, CMD_CHANSPEC, strlen(CMD_CHANSPEC)) == 0) {
++ bytes_written = wl_android_get_chanspec(net, command, priv_cmd.total_len);
++ }
++#endif /* WL_CFG80211 */
+ /* CUSTOMER_SET_COUNTRY feature is define for only GGSM model */
+ else if (strnicmp(command, CMD_COUNTRY, strlen(CMD_COUNTRY)) == 0) {
+ /*
+@@ -4858,14 +4922,6 @@ wl_handle_private_cmd(struct net_device *net, char *command, u32 cmd_len)
+ #endif /* FCC_PWR_LIMIT_2G */
+ #endif /* CUSTOMER_HW4_PRIVATE_CMD */
+ }
+- else if (strnicmp(command, CMD_SET_CSA, strlen(CMD_SET_CSA)) == 0) {
+- bytes_written = wl_android_set_csa(net, command, priv_cmd.total_len);
+- } else if (strnicmp(command, CMD_80211_MODE, strlen(CMD_80211_MODE)) == 0) {
+- bytes_written = wl_android_get_80211_mode(net, command, priv_cmd.total_len);
+- } else if (strnicmp(command, CMD_CHANSPEC, strlen(CMD_CHANSPEC)) == 0) {
+- bytes_written = wl_android_get_chanspec(net, command, priv_cmd.total_len);
+- }
+-#endif /* WL_CFG80211 */
+ else if (strnicmp(command, CMD_DATARATE, strlen(CMD_DATARATE)) == 0) {
+ bytes_written = wl_android_get_datarate(net, command, priv_cmd.total_len);
+ } else if (strnicmp(command, CMD_ASSOC_CLIENTS, strlen(CMD_ASSOC_CLIENTS)) == 0) {
+@@ -4900,6 +4956,18 @@ wl_handle_private_cmd(struct net_device *net, char *command, u32 cmd_len)
+ else if (strnicmp(command, CMD_P2P_DEV_ADDR, strlen(CMD_P2P_DEV_ADDR)) == 0) {
+ bytes_written = wl_android_get_p2p_dev_addr(net, command, priv_cmd.total_len);
+ }
++#ifdef WL_CFG80211
++#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
++#endif /* WL_CFG80211 */
+ 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,
+@@ -5165,17 +5233,15 @@ wl_handle_private_cmd(struct net_device *net, char *command, u32 cmd_len)
+ bytes_written = wl_cfg80211_get_sta_info(net, command, priv_cmd.total_len);
+ }
+ #endif /* CUSTOMER_HW4_PRIVATE_CMD */
+-#ifdef WL_CFG80211
+ else if (strnicmp(command, CMD_MURX_BFE_CAP,
+ strlen(CMD_MURX_BFE_CAP)) == 0) {
+-#ifdef BCM4359_CHIP
++#if defined(BCM4359_CHIP) && defined(WL_CFG80211)
+ uint val = *(command + strlen(CMD_MURX_BFE_CAP) + 1) - '0';
+ bytes_written = wl_android_murx_bfe_cap(net, val);
+ #else
+ return BCME_UNSUPPORTED;
+ #endif /* BCM4359_CHIP */
+ }
+-#endif
+ #ifdef SUPPORT_AP_HIGHER_BEACONRATE
+ else if (strnicmp(command, CMD_GET_AP_BASICRATE, strlen(CMD_GET_AP_BASICRATE)) == 0) {
+ bytes_written = wl_android_get_ap_basicrate(net, command, priv_cmd.total_len);
+diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_android.h b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_android.h
+index fe3330406134..39fd6ff86efb 100644
+--- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_android.h
++++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_android.h
+@@ -104,9 +104,14 @@ int wl_handle_private_cmd(struct net_device *net, char *command, u32 cmd_len);
+
+ s32 wl_netlink_send_msg(int pid, int type, int seq, const void *data, size_t size);
+ #ifdef WL_EXT_IAPSTA
+-int wl_android_ext_attach_netdev(struct net_device *net, uint8 bssidx);
+-int wl_android_ext_dettach_netdev(void);
+-void wl_android_ext_iapsta_disconnect_sta(struct net_device *dev, u32 channel);
++int wl_ext_iapsta_attach_netdev(struct net_device *net, uint8 bssidx);
++int wl_ext_iapsta_attach_name(struct net_device *net, uint8 bssidx);
++int wl_ext_iapsta_dettach_netdev(void);
++u32 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);
+@@ -114,76 +119,6 @@ int wl_android_ext_priv_cmd(struct net_device *net, char *command, int total_len
+ #define strnicmp(str1, str2, len) strncasecmp((str1), (str2), (len))
+ #endif
+
+-typedef enum IF_STATE {
+- IF_STATE_INIT = 1,
+- IF_STATE_DISALBE,
+- IF_STATE_ENABLE
+-} if_state_t;
+-
+-typedef enum APSTAMODE {
+- ISTAONLY_MODE = 1,
+- IAPONLY_MODE,
+- IAPSTA_MODE,
+- IDUALAP_MODE,
+- IGOSTA_MODE,
+- IGCSTA_MODE
+-} apstamode_t;
+-
+-typedef enum IFMODE {
+- ISTA_MODE = 1,
+- IAP_MODE
+-} ifmode_t;
+-
+-typedef enum BGNMODE {
+- IEEE80211B = 1,
+- IEEE80211G,
+- IEEE80211BG,
+- IEEE80211BGN,
+- IEEE80211BGNAC
+-} bgnmode_t;
+-
+-typedef enum AUTHMODE {
+- AUTH_OPEN,
+- AUTH_SHARED,
+- AUTH_WPAPSK,
+- AUTH_WPA2PSK,
+- AUTH_WPAWPA2PSK
+-} authmode_t;
+-
+-typedef enum ENCMODE {
+- ENC_NONE,
+- ENC_WEP,
+- ENC_TKIP,
+- ENC_AES,
+- ENC_TKIPAES
+-} encmode_t;
+-
+-/* i/f query */
+-typedef struct wl_if_info {
+- struct net_device *dev;
+- if_state_t ifstate;
+- ifmode_t ifmode;
+- uint bssidx;
+- char ifname[IFNAMSIZ+1];
+- char ssid[DOT11_MAX_SSID_LEN];
+- struct ether_addr bssid;
+- bgnmode_t bgnmode;
+- int hidden;
+- int maxassoc;
+- uint16 channel;
+- authmode_t amode;
+- encmode_t emode;
+- char key[100];
+-} wl_apsta_if_t;
+-
+-typedef struct wl_apsta_params {
+- struct wl_if_info pif; // primary device
+- struct wl_if_info vif; // virtual device
+- int ioctl_ver;
+- bool init;
+- apstamode_t apstamode;
+-} wl_apsta_params_t;
+-
+ /* hostap mac mode */
+ #define MACLIST_MODE_DISABLED 0
+ #define MACLIST_MODE_DENY 1
+@@ -224,6 +159,10 @@ int wl_android_set_ap_mac_list(struct net_device *dev, int macmode, struct macli
+ #define REPEATED_SCAN_RESULT_CNT 1
+ #endif
+
++#if defined(RSSIAVG) || defined(RSSIOFFSET)
++extern int g_wifi_on;
++#endif
++
+ #if defined(RSSIAVG)
+ #define RSSIAVG_LEN (4*REPEATED_SCAN_RESULT_CNT)
+ #define RSSICACHE_TIMEOUT 15
+@@ -286,4 +225,12 @@ void wl_update_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl,
+ wl_scan_results_t *ss_list);
+ void wl_release_bss_cache_ctrl(wl_bss_cache_ctrl_t *bss_cache_ctrl);
+ #endif
++int wl_ext_get_best_channel(struct net_device *net,
++#if defined(BSSCACHE)
++ wl_bss_cache_ctrl_t *bss_cache_ctrl,
++#else
++ struct wl_scan_results *bss_list,
++#endif
++ int *best_2g_ch, int *best_5g_ch
++);
+ #endif /* _wl_android_ */
+diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_android_ext.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_android_ext.c
+index e510c241f536..6e7ad34812dc 100644
+--- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_android_ext.c
++++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_android_ext.c
+@@ -1,151 +1,279 @@
+ /* SPDX-License-Identifier: GPL-2.0 */
+-
+-
+-#include
+-#include
+-#include
+-
+-#include
+-#include
+-#include
+-#include
+-#include
+-#include
+-#include
+-#include
+-#include
+-
+-#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 strtoul(nptr, endptr, base) bcm_strtoul((nptr), (endptr), (base))
+-
+-#define CMD_CHANNEL "CHANNEL"
+-#define CMD_CHANNELS "CHANNELS"
+-#define CMD_ROAM_TRIGGER "ROAM_TRIGGER"
+-#define CMD_KEEP_ALIVE "KEEP_ALIVE"
+-#define CMD_PM "PM"
+-#define CMD_MONITOR "MONITOR"
+-#define CMD_SET_SUSPEND_BCN_LI_DTIM "SET_SUSPEND_BCN_LI_DTIM"
+-
+-#ifdef WL_EXT_IAPSTA
+-#define CMD_IAPSTA_INIT "IAPSTA_INIT"
+-#define CMD_IAPSTA_CONFIG "IAPSTA_CONFIG"
+-#define CMD_IAPSTA_ENABLE "IAPSTA_ENABLE"
+-#define CMD_IAPSTA_DISABLE "IAPSTA_DISABLE"
+-#ifdef PROP_TXSTATUS
+-#ifdef PROP_TXSTATUS_VSDB
+-#include
++#include
++#include
++#include
++#include
++#include
++#include
++
++#include
++#include
++#include
++
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#ifdef WL_CFG80211
++#include
++#endif
++#ifdef WL_ESCAN
++#include
++#endif
++
++#ifndef WL_CFG80211
++#define htod32(i) i
++#define htod16(i) i
++#define dtoh32(i) i
++#define dtoh16(i) i
++#define htodchanspec(i) i
++#define dtohchanspec(i) i
++#define IEEE80211_BAND_2GHZ 0
++#define IEEE80211_BAND_5GHZ 1
++#define WL_SCAN_JOIN_PROBE_INTERVAL_MS 20
++#define WL_SCAN_JOIN_ACTIVE_DWELL_TIME_MS 320
++#define WL_SCAN_JOIN_PASSIVE_DWELL_TIME_MS 400
++#endif
++#define strtoul(nptr, endptr, base) bcm_strtoul((nptr), (endptr), (base))
++
++#ifndef IW_CUSTOM_MAX
++#define IW_CUSTOM_MAX 256 /* size of extra buffer used for translation of events */
++#endif /* IW_CUSTOM_MAX */
++
++#define CMD_CHANNEL "CHANNEL"
++#define CMD_CHANNELS "CHANNELS"
++#define CMD_ROAM_TRIGGER "ROAM_TRIGGER"
++#define CMD_KEEP_ALIVE "KEEP_ALIVE"
++#define CMD_PM "PM"
++#define CMD_MONITOR "MONITOR"
++#define CMD_SET_SUSPEND_BCN_LI_DTIM "SET_SUSPEND_BCN_LI_DTIM"
++
++#ifdef WL_EXT_IAPSTA
++#include
++#define CMD_IAPSTA_INIT "IAPSTA_INIT"
++#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"
++#define CMD_ISAM_DUMP "ISAM_DUMP"
++#ifdef PROP_TXSTATUS
++#ifdef PROP_TXSTATUS_VSDB
++#include
+ extern int disable_proptx;
+-#endif /* PROP_TXSTATUS_VSDB */
+-#endif
+-#endif
+-#ifdef IDHCP
+-#define CMD_DHCPC_ENABLE "DHCPC_ENABLE"
+-#define CMD_DHCPC_DUMP "DHCPC_DUMP"
+-#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;
+-
+- ret = wldev_ioctl(dev, cmd, arg, len, set);
+- if (ret)
+- ANDROID_ERROR(("%s: cmd=%d ret=%d\n", __FUNCTION__, cmd, ret));
+- return ret;
+-}
+-
+-int wl_ext_iovar_getint(struct net_device *dev, s8 *iovar, s32 *val)
+-{
+- int ret;
+-
+- ret = wldev_iovar_getint(dev, iovar, val);
+- if (ret)
+- ANDROID_ERROR(("%s: iovar=%s, ret=%d\n", __FUNCTION__, iovar, ret));
+-
+- return ret;
+-}
+-
+-int wl_ext_iovar_setint(struct net_device *dev, s8 *iovar, s32 val)
+-{
+- int ret;
+-
+- ret = wldev_iovar_setint(dev, iovar, val);
+- if (ret)
+- ANDROID_ERROR(("%s: iovar=%s, ret=%d\n", __FUNCTION__, iovar, ret));
+-
+- return ret;
+-}
+-
+-int wl_ext_iovar_getbuf(struct net_device *dev, s8 *iovar_name,
+- void *param, s32 paramlen, void *buf, s32 buflen, struct mutex* buf_sync)
+-{
+- int ret;
+-
+- ret = wldev_iovar_getbuf(dev, iovar_name, param, paramlen, buf, buflen, buf_sync);
+- if (ret != 0)
+- ANDROID_ERROR(("%s: iovar=%s, ret=%d\n", __FUNCTION__, iovar_name, ret));
+-
+- return ret;
+-}
+-
+-int wl_ext_iovar_setbuf(struct net_device *dev, s8 *iovar_name,
+- void *param, s32 paramlen, void *buf, s32 buflen, struct mutex* buf_sync)
+-{
+- int ret;
+-
+- ret = wldev_iovar_setbuf(dev, iovar_name, param, paramlen, buf, buflen, buf_sync);
+- if (ret != 0)
+- ANDROID_ERROR(("%s: iovar=%s, ret=%d\n", __FUNCTION__, iovar_name, ret));
+-
+- return ret;
+-}
+-
+-#ifdef WL_EXT_IAPSTA
+-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)
+-{
+- int ret;
+-
+- ret = wldev_iovar_setbuf_bsscfg(dev, iovar_name, param, paramlen,
+- buf, buflen, bsscfg_idx, buf_sync);
+- if (ret < 0)
+- ANDROID_ERROR(("%s: iovar_name=%s ret=%d\n", __FUNCTION__, iovar_name, ret));
+-
+- return ret;
+-}
+-
+-int wl_ext_iovar_getbuf_bsscfg(struct net_device *dev, s8 *iovar_name,
+- void *param, s32 paramlen, void *buf, s32 buflen, s32 bsscfg_idx, struct mutex* buf_sync)
+-{
+- int ret;
+-
+- ret = wldev_iovar_getbuf_bsscfg(dev, iovar_name, param, paramlen,
+- buf, buflen, bsscfg_idx, buf_sync);
+- if (ret < 0)
+- ANDROID_ERROR(("%s: iovar_name=%s ret=%d\n", __FUNCTION__, iovar_name, ret));
+-
+- return ret;
+-}
+-#endif
++#endif /* PROP_TXSTATUS_VSDB */
++#endif
++#endif
++#ifdef IDHCP
++#define CMD_DHCPC_ENABLE "DHCPC_ENABLE"
++#define CMD_DHCPC_DUMP "DHCPC_DUMP"
++#endif
++#define CMD_AUTOCHANNEL "AUTOCHANNEL"
++#define CMD_WL "WL"
++
++int wl_ext_ioctl(struct net_device *dev, u32 cmd, void *arg, u32 len, u32 set)
++{
++ int ret;
++
++ ret = wldev_ioctl(dev, cmd, arg, len, set);
++ if (ret)
++ ANDROID_ERROR(("%s: cmd=%d ret=%d\n", __FUNCTION__, cmd, ret));
++ return ret;
++}
++
++int wl_ext_iovar_getint(struct net_device *dev, s8 *iovar, s32 *val)
++{
++ int ret;
++
++ ret = wldev_iovar_getint(dev, iovar, val);
++ if (ret)
++ ANDROID_ERROR(("%s: iovar=%s, ret=%d\n", __FUNCTION__, iovar, ret));
++
++ return ret;
++}
++
++int wl_ext_iovar_setint(struct net_device *dev, s8 *iovar, s32 val)
++{
++ int ret;
++
++ ret = wldev_iovar_setint(dev, iovar, val);
++ if (ret)
++ ANDROID_ERROR(("%s: iovar=%s, ret=%d\n", __FUNCTION__, iovar, ret));
++
++ return ret;
++}
++
++int wl_ext_iovar_getbuf(struct net_device *dev, s8 *iovar_name,
++ void *param, s32 paramlen, void *buf, s32 buflen, struct mutex* buf_sync)
++{
++ int ret;
++
++ ret = wldev_iovar_getbuf(dev, iovar_name, param, paramlen, buf, buflen, buf_sync);
++ if (ret != 0)
++ ANDROID_ERROR(("%s: iovar=%s, ret=%d\n", __FUNCTION__, iovar_name, ret));
++
++ return ret;
++}
++
++int wl_ext_iovar_setbuf(struct net_device *dev, s8 *iovar_name,
++ void *param, s32 paramlen, void *buf, s32 buflen, struct mutex* buf_sync)
++{
++ int ret;
++
++ ret = wldev_iovar_setbuf(dev, iovar_name, param, paramlen, buf, buflen, buf_sync);
++ if (ret != 0)
++ ANDROID_ERROR(("%s: iovar=%s, ret=%d\n", __FUNCTION__, iovar_name, ret));
++
++ return ret;
++}
++
++#ifdef WL_EXT_IAPSTA
++typedef enum IF_STATE {
++ IF_STATE_INIT = 1,
++ IF_STATE_DISALBE,
++ IF_STATE_ENABLE
++} if_state_t;
++
++typedef enum APSTAMODE {
++ ISTAONLY_MODE = 1,
++ IAPONLY_MODE,
++ IAPSTA_MODE,
++ IDUALAP_MODE,
++ IMESHONLY_MODE,
++ IMESHSTA_MODE,
++ IMESHAP_MODE,
++ IMESHAPSTA_MODE,
++ IMESHAPAP_MODE,
++ IGOSTA_MODE
++} apstamode_t;
++
++typedef enum IFMODE {
++ ISTA_MODE = 1,
++ IAP_MODE,
++ IMESH_MODE
++} ifmode_t;
++
++typedef enum BGNMODE {
++ IEEE80211B = 1,
++ IEEE80211G,
++ IEEE80211BG,
++ IEEE80211BGN,
++ IEEE80211BGNAC
++} bgnmode_t;
++
++typedef enum AUTHMODE {
++ AUTH_OPEN,
++ AUTH_SHARED,
++ AUTH_WPAPSK,
++ AUTH_WPA2PSK,
++ AUTH_WPAWPA2PSK,
++ AUTH_SAE
++} authmode_t;
++
++typedef enum ENCMODE {
++ ENC_NONE,
++ ENC_WEP,
++ ENC_TKIP,
++ ENC_AES,
++ ENC_TKIPAES
++} encmode_t;
++
++enum wl_if_list {
++ IF_PIF,
++ IF_VIF,
++ IF_VIF2,
++ MAX_IF_NUM
++};
++
++typedef enum WL_PRIO {
++ PRIO_AP,
++ PRIO_MESH,
++ PRIO_STA
++}wl_prio_t;
++
++typedef struct wl_if_info {
++ struct net_device *dev;
++ if_state_t ifstate;
++ ifmode_t ifmode;
++ char prefix;
++ wl_prio_t prio;
++ uint bssidx;
++ char ifname[IFNAMSIZ+1];
++ char ssid[DOT11_MAX_SSID_LEN];
++ struct ether_addr bssid;
++ bgnmode_t bgnmode;
++ int hidden;
++ int maxassoc;
++ uint16 channel;
++ authmode_t amode;
++ encmode_t emode;
++ char key[100];
++} wl_if_info_t;
++
++#define CSA_FW_BIT (1<<0)
++#define CSA_DRV_BIT (1<<1)
++
++typedef struct wl_apsta_params {
++ struct wl_if_info if_info[MAX_IF_NUM];
++ int ioctl_ver;
++ bool init;
++ bool rsdb;
++ bool vsdb;
++ uint csa;
++ apstamode_t apstamode;
++ bool netif_change;
++ wait_queue_head_t netif_change_event;
++} wl_apsta_params_t;
++
++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)
++{
++ int ret;
++
++ ret = wldev_iovar_setbuf_bsscfg(dev, iovar_name, param, paramlen,
++ buf, buflen, bsscfg_idx, buf_sync);
++ if (ret < 0)
++ ANDROID_ERROR(("%s: iovar_name=%s ret=%d\n", __FUNCTION__, iovar_name, ret));
++
++ return ret;
++}
++
++int wl_ext_iovar_getbuf_bsscfg(struct net_device *dev, s8 *iovar_name,
++ void *param, s32 paramlen, void *buf, s32 buflen, s32 bsscfg_idx, struct mutex* buf_sync)
++{
++ int ret;
++
++ ret = wldev_iovar_getbuf_bsscfg(dev, iovar_name, param, paramlen,
++ buf, buflen, bsscfg_idx, buf_sync);
++ if (ret < 0)
++ ANDROID_ERROR(("%s: iovar_name=%s ret=%d\n", __FUNCTION__, iovar_name, ret));
++
++ return ret;
++}
++#endif
+
+ /* Return a legacy chanspec given a new chanspec
+ * Returns INVCHANSPEC on error
+ */
+ static chanspec_t
+-wl_ext_chspec_to_legacy(chanspec_t chspec)
++wl_ext_chspec_to_legacy(chanspec_t chspec)
+ {
+ chanspec_t lchspec;
+
+ if (wf_chspec_malformed(chspec)) {
+- ANDROID_ERROR(("wl_ext_chspec_to_legacy: input chanspec (0x%04X) malformed\n",
++ ANDROID_ERROR(("wl_ext_chspec_to_legacy: input chanspec (0x%04X) malformed\n",
+ chspec));
+ return INVCHANSPEC;
+ }
+@@ -174,25 +302,25 @@ wl_ext_chspec_to_legacy(chanspec_t chspec)
+ } else {
+ /* cannot express the bandwidth */
+ char chanbuf[CHANSPEC_STR_LEN];
+- ANDROID_ERROR((
+- "wl_ext_chspec_to_legacy: unable to convert chanspec %s (0x%04X) "
++ ANDROID_ERROR((
++ "wl_ext_chspec_to_legacy: unable to convert chanspec %s (0x%04X) "
+ "to pre-11ac format\n",
+ wf_chspec_ntoa(chspec, chanbuf), chspec));
+ return INVCHANSPEC;
+ }
+
+ return lchspec;
+-}
++}
+
+ /* given a chanspec value, do the endian and chanspec version conversion to
+ * a chanspec_t value
+ * Returns INVCHANSPEC on error
+ */
+ static chanspec_t
+-wl_ext_chspec_host_to_driver(int ioctl_ver, chanspec_t chanspec)
++wl_ext_chspec_host_to_driver(int ioctl_ver, chanspec_t chanspec)
+ {
+- if (ioctl_ver == 1) {
+- chanspec = wl_ext_chspec_to_legacy(chanspec);
++ if (ioctl_ver == 1) {
++ chanspec = wl_ext_chspec_to_legacy(chanspec);
+ if (chanspec == INVCHANSPEC) {
+ return chanspec;
+ }
+@@ -200,99 +328,148 @@ wl_ext_chspec_host_to_driver(int ioctl_ver, chanspec_t chanspec)
+ chanspec = htodchanspec(chanspec);
+
+ return chanspec;
+-}
+-
+-static int
+-wl_ext_get_ioctl_ver(struct net_device *dev, int *ioctl_ver)
+-{
+- int ret = 0;
+- s32 val = 0;
+-
+- val = 1;
+- ret = wl_ext_ioctl(dev, WLC_GET_VERSION, &val, sizeof(val), 0);
+- if (ret) {
+- ANDROID_ERROR(("WLC_GET_VERSION failed, err=%d\n", ret));
+- return ret;
+- }
+- val = dtoh32(val);
+- if (val != WLC_IOCTL_VERSION && val != 1) {
+- ANDROID_ERROR(("Version mismatch, please upgrade. Got %d, expected %d or 1\n",
+- val, WLC_IOCTL_VERSION));
+- return BCME_VERSION;
+- }
+- *ioctl_ver = val;
+-
+- return ret;
+-}
+-
+-static int
+-wl_ext_set_chanspec(struct net_device *dev, uint16 channel)
+-{
+- s32 _chan = channel;
++}
++
++#if defined(WL_EXT_IAPSTA) || defined(WL_CFG80211) || defined(WL_ESCAN)
++static chanspec_t
++wl_ext_chspec_from_legacy(chanspec_t legacy_chspec)
++{
++ chanspec_t chspec;
++
++ /* get the channel number */
++ chspec = LCHSPEC_CHANNEL(legacy_chspec);
++
++ /* 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;
++ }
++ }
++
++ 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;
++}
++#endif
++
++static int
++wl_ext_get_ioctl_ver(struct net_device *dev, int *ioctl_ver)
++{
++ int ret = 0;
++ s32 val = 0;
++
++ val = 1;
++ ret = wl_ext_ioctl(dev, WLC_GET_VERSION, &val, sizeof(val), 0);
++ if (ret) {
++ ANDROID_ERROR(("WLC_GET_VERSION failed, err=%d\n", ret));
++ return ret;
++ }
++ val = dtoh32(val);
++ if (val != WLC_IOCTL_VERSION && val != 1) {
++ ANDROID_ERROR(("Version mismatch, please upgrade. Got %d, expected %d or 1\n",
++ val, WLC_IOCTL_VERSION));
++ return BCME_VERSION;
++ }
++ *ioctl_ver = val;
++
++ return ret;
++}
++
++static int
++wl_ext_set_chanspec(struct net_device *dev, uint16 channel, chanspec_t *ret_chspec)
++{
++ s32 _chan = channel;
+ chanspec_t chspec = 0;
+- chanspec_t fw_chspec = 0;
+- u32 bw = WL_CHANSPEC_BW_20;
++ chanspec_t fw_chspec = 0;
++ u32 bw = WL_CHANSPEC_BW_20;
+ s32 err = BCME_OK;
+- s32 bw_cap = 0;
+- s8 iovar_buf[WLC_IOCTL_SMLEN];
++ s32 bw_cap = 0;
++ s8 iovar_buf[WLC_IOCTL_SMLEN];
+ struct {
+ u32 band;
+ u32 bw_cap;
+- } param = {0, 0};
+- uint band;
+- int ioctl_ver = 0;
+-
+- if (_chan <= CH_MAX_2G_CHANNEL)
+- band = IEEE80211_BAND_2GHZ;
+- else
+- band = IEEE80211_BAND_5GHZ;
+- wl_ext_get_ioctl_ver(dev, &ioctl_ver);
+-
+- if (band == IEEE80211_BAND_5GHZ) {
++ } param = {0, 0};
++ uint band;
++ int ioctl_ver = 0;
++
++ if (_chan <= CH_MAX_2G_CHANNEL)
++ band = IEEE80211_BAND_2GHZ;
++ else
++ band = IEEE80211_BAND_5GHZ;
++ wl_ext_get_ioctl_ver(dev, &ioctl_ver);
++
++ if (band == IEEE80211_BAND_5GHZ) {
+ param.band = WLC_BAND_5G;
+ err = wldev_iovar_getbuf(dev, "bw_cap", ¶m, sizeof(param),
+- iovar_buf, WLC_IOCTL_SMLEN, NULL);
++ iovar_buf, WLC_IOCTL_SMLEN, NULL);
+ if (err) {
+ if (err != BCME_UNSUPPORTED) {
+- ANDROID_ERROR(("bw_cap failed, %d\n", err));
++ ANDROID_ERROR(("bw_cap failed, %d\n", err));
+ return err;
+ } else {
+ err = wldev_iovar_getint(dev, "mimo_bw_cap", &bw_cap);
+ if (err) {
+- ANDROID_ERROR(("error get mimo_bw_cap (%d)\n", err));
++ ANDROID_ERROR(("error get mimo_bw_cap (%d)\n", err));
+ }
+ if (bw_cap != WLC_N_BW_20ALL)
+ bw = WL_CHANSPEC_BW_40;
+ }
+ } else {
+- if (WL_BW_CAP_80MHZ(iovar_buf[0]))
++ if (WL_BW_CAP_80MHZ(iovar_buf[0]))
+ bw = WL_CHANSPEC_BW_80;
+- else if (WL_BW_CAP_40MHZ(iovar_buf[0]))
++ else if (WL_BW_CAP_40MHZ(iovar_buf[0]))
+ bw = WL_CHANSPEC_BW_40;
+ else
+ bw = WL_CHANSPEC_BW_20;
+
+- }
+- }
+- else if (band == IEEE80211_BAND_2GHZ)
+- bw = WL_CHANSPEC_BW_20;
+-
++ }
++ }
++ else if (band == IEEE80211_BAND_2GHZ)
++ bw = WL_CHANSPEC_BW_20;
++
+ set_channel:
+ chspec = wf_channel2chspec(_chan, bw);
+ if (wf_chspec_valid(chspec)) {
+- fw_chspec = wl_ext_chspec_host_to_driver(ioctl_ver, chspec);
++ fw_chspec = wl_ext_chspec_host_to_driver(ioctl_ver, chspec);
+ if (fw_chspec != INVCHANSPEC) {
+- if ((err = wldev_iovar_setint(dev, "chanspec", fw_chspec)) == BCME_BADCHAN) {
++ if ((err = wldev_iovar_setint(dev, "chanspec", fw_chspec)) == BCME_BADCHAN) {
+ if (bw == WL_CHANSPEC_BW_80)
+- goto change_bw;
+- wl_ext_ioctl(dev, WLC_SET_CHANNEL, &_chan, sizeof(_chan), 1);
+- printf("%s: channel %d\n", __FUNCTION__, _chan);
++ goto change_bw;
++ wl_ext_ioctl(dev, WLC_SET_CHANNEL, &_chan, sizeof(_chan), 1);
++ printf("%s: channel %d\n", __FUNCTION__, _chan);
+ } else if (err) {
+- ANDROID_ERROR(("%s: failed to set chanspec error %d\n", __FUNCTION__, err));
+- } else
+- printf("%s: channel %d, 0x%x\n", __FUNCTION__, channel, chspec);
++ ANDROID_ERROR(("%s: failed to set chanspec error %d\n", __FUNCTION__, err));
++ } else
++ printf("%s: channel %d, 0x%x\n", __FUNCTION__, channel, chspec);
+ } else {
+- ANDROID_ERROR(("%s: failed to convert host chanspec to fw chanspec\n", __FUNCTION__));
++ ANDROID_ERROR(("%s: failed to convert host chanspec to fw chanspec\n", __FUNCTION__));
+ err = BCME_ERROR;
+ }
+ } else {
+@@ -305,1875 +482,3280 @@ change_bw:
+ bw = 0;
+ if (bw)
+ goto set_channel;
+- ANDROID_ERROR(("%s: Invalid chanspec 0x%x\n", __FUNCTION__, chspec));
++ ANDROID_ERROR(("%s: Invalid chanspec 0x%x\n", __FUNCTION__, chspec));
+ err = BCME_ERROR;
+- }
+-
+- return err;
+-}
+-
+-int
+-wl_ext_channel(struct net_device *dev, char* command, int total_len)
+-{
+- int ret;
+- int channel=0;
+- channel_info_t ci;
+- int bytes_written = 0;
+-
+- ANDROID_TRACE(("%s: cmd %s\n", __FUNCTION__, command));
+-
+- sscanf(command, "%*s %d", &channel);
+-
+- if (channel > 0) {
+- ret = wl_ext_set_chanspec(dev, channel);
+- } else {
+- if (!(ret = wldev_ioctl(dev, WLC_GET_CHANNEL, &ci, sizeof(channel_info_t), FALSE))) {
+- ANDROID_TRACE(("hw_channel %d\n", ci.hw_channel));
+- ANDROID_TRACE(("target_channel %d\n", ci.target_channel));
+- ANDROID_TRACE(("scan_channel %d\n", ci.scan_channel));
+- bytes_written = snprintf(command, sizeof(channel_info_t)+2, "channel %d", ci.hw_channel);
+- ANDROID_TRACE(("%s: command result is %s\n", __FUNCTION__, command));
+- ret = bytes_written;
+- }
+- }
+-
+- return ret;
+-}
+-
+-int
+-wl_ext_channels(struct net_device *dev, char* command, int total_len)
+-{
+- int ret, i;
+- int bytes_written = -1;
+- u8 valid_chan_list[sizeof(u32)*(WL_NUMCHANNELS + 1)];
+- wl_uint32_list_t *list;
+-
+- ANDROID_TRACE(("%s: cmd %s\n", __FUNCTION__, command));
+-
+- memset(valid_chan_list, 0, sizeof(valid_chan_list));
+- list = (wl_uint32_list_t *)(void *) valid_chan_list;
+- list->count = htod32(WL_NUMCHANNELS);
+- ret = wldev_ioctl(dev, WLC_GET_VALID_CHANNELS, valid_chan_list, sizeof(valid_chan_list), 0);
+- if (ret<0) {
+- ANDROID_ERROR(("%s: get channels failed with %d\n", __FUNCTION__, ret));
+- } else {
+- bytes_written = snprintf(command, total_len, "channels");
+- for (i = 0; i < dtoh32(list->count); i++) {
+- bytes_written += snprintf(command+bytes_written, total_len, " %d", dtoh32(list->element[i]));
+- printf("%d ", dtoh32(list->element[i]));
+- }
+- printf("\n");
+- ret = bytes_written;
+- }
+-
+- return ret;
+-}
+-
+-int
+-wl_ext_roam_trigger(struct net_device *dev, char* command, int total_len)
+-{
+- int ret = 0;
+- int roam_trigger[2] = {0, 0};
+- int trigger[2]= {0, 0};
+- int bytes_written=-1;
+-
+- sscanf(command, "%*s %10d", &roam_trigger[0]);
+-
+- if (roam_trigger[0]) {
+- roam_trigger[1] = WLC_BAND_ALL;
+- ret = wldev_ioctl(dev, WLC_SET_ROAM_TRIGGER, roam_trigger, sizeof(roam_trigger), 1);
+- if (ret)
+- ANDROID_ERROR(("WLC_SET_ROAM_TRIGGER ERROR %d ret=%d\n", roam_trigger[0], ret));
+- } else {
+- roam_trigger[1] = WLC_BAND_2G;
+- ret = wldev_ioctl(dev, WLC_GET_ROAM_TRIGGER, roam_trigger, sizeof(roam_trigger), 0);
+- if (!ret)
+- trigger[0] = roam_trigger[0];
+- else
+- ANDROID_ERROR(("2G WLC_GET_ROAM_TRIGGER ERROR %d ret=%d\n", roam_trigger[0], ret));
+-
+- roam_trigger[1] = WLC_BAND_5G;
+- ret = wldev_ioctl(dev, WLC_GET_ROAM_TRIGGER, roam_trigger, sizeof(roam_trigger), 0);
+- if (!ret)
+- trigger[1] = roam_trigger[0];
+- else
+- ANDROID_ERROR(("5G WLC_GET_ROAM_TRIGGER ERROR %d ret=%d\n", roam_trigger[0], ret));
+-
+- ANDROID_TRACE(("roam_trigger %d %d\n", trigger[0], trigger[1]));
+- bytes_written = snprintf(command, total_len, "%d %d", trigger[0], trigger[1]);
+- ret = bytes_written;
+- }
+-
+- return ret;
+-}
+-
+-static int
+-wl_ext_pattern_atoh(char *src, char *dst)
+-{
+- int i;
+- if (strncmp(src, "0x", 2) != 0 &&
+- strncmp(src, "0X", 2) != 0) {
+- ANDROID_ERROR(("Mask invalid format. Needs to start with 0x\n"));
+- return -1;
+- }
+- src = src + 2; /* Skip past 0x */
+- if (strlen(src) % 2 != 0) {
+- DHD_ERROR(("Mask invalid format. Needs to be of even length\n"));
+- return -1;
+- }
+- for (i = 0; *src != '\0'; i++) {
+- char num[3];
+- bcm_strncpy_s(num, sizeof(num), src, 2);
+- num[2] = '\0';
+- dst[i] = (uint8)strtoul(num, NULL, 16);
+- src += 2;
+- }
+- return i;
+-}
+-
+-int
+-wl_ext_keep_alive(struct net_device *dev, char *command, int total_len)
+-{
+- wl_mkeep_alive_pkt_t *mkeep_alive_pktp;
+- int ret = -1, i;
+- int id, period=-1, len_bytes=0, buf_len=0;
+- char data[200]="\0";
+- char buf[WLC_IOCTL_SMLEN]="\0", iovar_buf[WLC_IOCTL_SMLEN]="\0";
+- int bytes_written = -1;
+-
+- ANDROID_TRACE(("%s: command = %s\n", __FUNCTION__, command));
+- sscanf(command, "%*s %d %d %s", &id, &period, data);
+- ANDROID_TRACE(("%s: id=%d, period=%d, data=%s\n", __FUNCTION__, id, period, data));
+-
+- if (period >= 0) {
+- mkeep_alive_pktp = (wl_mkeep_alive_pkt_t *)buf;
+- mkeep_alive_pktp->version = htod16(WL_MKEEP_ALIVE_VERSION);
+- mkeep_alive_pktp->length = htod16(WL_MKEEP_ALIVE_FIXED_LEN);
+- mkeep_alive_pktp->keep_alive_id = id;
+- buf_len += WL_MKEEP_ALIVE_FIXED_LEN;
+- mkeep_alive_pktp->period_msec = period;
+- if (strlen(data)) {
+- len_bytes = wl_ext_pattern_atoh(data, (char *) mkeep_alive_pktp->data);
+- buf_len += len_bytes;
+- }
+- mkeep_alive_pktp->len_bytes = htod16(len_bytes);
+-
+- ret = wl_ext_iovar_setbuf(dev, "mkeep_alive", buf, buf_len,
+- iovar_buf, sizeof(iovar_buf), NULL);
+- } else {
+- if (id < 0)
+- id = 0;
+- ret = wl_ext_iovar_getbuf(dev, "mkeep_alive", &id, sizeof(id), buf, sizeof(buf), NULL);
+- if (ret) {
+- goto exit;
+- } else {
+- mkeep_alive_pktp = (wl_mkeep_alive_pkt_t *) buf;
+- printf("Id :%d\n"
+- "Period (msec) :%d\n"
+- "Length :%d\n"
+- "Packet :0x",
+- mkeep_alive_pktp->keep_alive_id,
+- dtoh32(mkeep_alive_pktp->period_msec),
+- dtoh16(mkeep_alive_pktp->len_bytes));
+- for (i=0; ilen_bytes; i++) {
+- printf("%02x", mkeep_alive_pktp->data[i]);
+- }
+- printf("\n");
+- }
+- bytes_written = snprintf(command, total_len, "mkeep_alive_period_msec %d ", dtoh32(mkeep_alive_pktp->period_msec));
+- bytes_written += snprintf(command+bytes_written, total_len, "0x");
+- for (i=0; ilen_bytes; i++) {
+- bytes_written += snprintf(command+bytes_written, total_len, "%x", mkeep_alive_pktp->data[i]);
+- }
+- ANDROID_TRACE(("%s: command result is %s\n", __FUNCTION__, command));
+- ret = bytes_written;
+- }
+-
+-exit:
+- return ret;
+-}
+-
+-int
+-wl_ext_pm(struct net_device *dev, char *command, int total_len)
+-{
+- int pm=-1, ret = -1;
+- char *pm_local;
+- int bytes_written=-1;
+-
+- ANDROID_TRACE(("%s: cmd %s\n", __FUNCTION__, command));
+-
+- sscanf(command, "%*s %d", &pm);
+-
+- if (pm >= 0) {
+- ret = wldev_ioctl(dev, WLC_SET_PM, &pm, sizeof(pm), FALSE);
+- if (ret)
+- ANDROID_ERROR(("WLC_SET_PM ERROR %d ret=%d\n", pm, ret));
+- } else {
+- ret = wldev_ioctl(dev, WLC_GET_PM, &pm, sizeof(pm), FALSE);
+- if (!ret) {
+- ANDROID_TRACE(("%s: PM = %d\n", __func__, pm));
+- if (pm == PM_OFF)
+- pm_local = "PM_OFF";
+- else if(pm == PM_MAX)
+- pm_local = "PM_MAX";
+- else if(pm == PM_FAST)
+- pm_local = "PM_FAST";
+- else {
+- pm = 0;
+- pm_local = "Invalid";
+- }
+- bytes_written = snprintf(command, total_len, "PM %s", pm_local);
+- ANDROID_TRACE(("%s: command result is %s\n", __FUNCTION__, command));
+- ret = bytes_written;
+- }
+- }
+-
+- return ret;
+-}
+-
+-static int
+-wl_ext_monitor(struct net_device *dev, char *command, int total_len)
+-{
+- int val, ret = -1;
+- int bytes_written=-1;
+-
+- sscanf(command, "%*s %d", &val);
+-
+- if (val >=0) {
+- ret = wldev_ioctl(dev, WLC_SET_MONITOR, &val, sizeof(int), 1);
+- if (ret)
+- ANDROID_ERROR(("WLC_SET_MONITOR ERROR %d ret=%d\n", val, ret));
+- } else {
+- ret = wldev_ioctl(dev, WLC_GET_MONITOR, &val, sizeof(val), FALSE);
+- if (!ret) {
+- ANDROID_TRACE(("%s: monitor = %d\n", __FUNCTION__, val));
+- bytes_written = snprintf(command, total_len, "monitor %d", val);
+- ANDROID_TRACE(("%s: command result is %s\n", __FUNCTION__, command));
+- ret = bytes_written;
+- }
+- }
+-
+- return ret;
+-}
+-
+-#ifdef WL_EXT_IAPSTA
+-struct wl_apsta_params g_apsta_params;
+-static int
+-wl_ext_parse_wep(char *key, struct wl_wsec_key *wsec_key)
+-{
+- char hex[] = "XX";
+- unsigned char *data = wsec_key->data;
+- char *keystr = key;
+-
+- switch (strlen(keystr)) {
+- case 5:
+- case 13:
+- case 16:
+- wsec_key->len = strlen(keystr);
+- memcpy(data, keystr, wsec_key->len + 1);
+- break;
+- case 12:
+- case 28:
+- case 34:
+- case 66:
+- /* strip leading 0x */
+- if (!strnicmp(keystr, "0x", 2))
+- keystr += 2;
+- else
+- return -1;
+- /* fall through */
+- case 10:
+- case 26:
+- case 32:
+- case 64:
+- wsec_key->len = strlen(keystr) / 2;
+- while (*keystr) {
+- strncpy(hex, keystr, 2);
+- *data++ = (char) strtoul(hex, NULL, 16);
+- keystr += 2;
+- }
+- break;
+- default:
+- return -1;
+- }
+-
+- switch (wsec_key->len) {
+- case 5:
+- wsec_key->algo = CRYPTO_ALGO_WEP1;
+- break;
+- case 13:
+- wsec_key->algo = CRYPTO_ALGO_WEP128;
+- break;
+- case 16:
+- /* default to AES-CCM */
+- wsec_key->algo = CRYPTO_ALGO_AES_CCM;
+- break;
+- case 32:
+- wsec_key->algo = CRYPTO_ALGO_TKIP;
+- break;
+- default:
+- return -1;
+- }
+-
+- /* Set as primary wsec_key by default */
+- wsec_key->flags |= WL_PRIMARY_KEY;
+-
+- return 0;
+-}
+-
+-static int
+-wl_ext_set_bgnmode(struct wl_if_info *cur_if)
+-{
+- struct net_device *dev = cur_if->dev;
+- bgnmode_t bgnmode = cur_if->bgnmode;
+- int val;
+-
+- if (bgnmode == 0)
+- return 0;
+-
+- wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1);
+- if (bgnmode == IEEE80211B) {
+- wl_ext_iovar_setint(dev, "nmode", 0);
+- val = 0;
+- wl_ext_ioctl(dev, WLC_SET_GMODE, &val, sizeof(val), 1);
+- ANDROID_TRACE(("%s: Network mode: B only\n", __FUNCTION__));
+- } else if (bgnmode == IEEE80211G) {
+- wl_ext_iovar_setint(dev, "nmode", 0);
+- val = 2;
+- wl_ext_ioctl(dev, WLC_SET_GMODE, &val, sizeof(val), 1);
+- ANDROID_TRACE(("%s: Network mode: G only\n", __FUNCTION__));
+- } else if (bgnmode == IEEE80211BG) {
+- wl_ext_iovar_setint(dev, "nmode", 0);
+- val = 1;
+- wl_ext_ioctl(dev, WLC_SET_GMODE, &val, sizeof(val), 1);
+- ANDROID_TRACE(("%s: Network mode: : B/G mixed\n", __FUNCTION__));
+- } else if (bgnmode == IEEE80211BGN) {
+- wl_ext_iovar_setint(dev, "nmode", 0);
+- wl_ext_iovar_setint(dev, "nmode", 1);
+- wl_ext_iovar_setint(dev, "vhtmode", 0);
+- val = 1;
+- wl_ext_ioctl(dev, WLC_SET_GMODE, &val, sizeof(val), 1);
+- ANDROID_TRACE(("%s: Network mode: : B/G/N mixed\n", __FUNCTION__));
+- } else if (bgnmode == IEEE80211BGNAC) {
+- wl_ext_iovar_setint(dev, "nmode", 0);
+- wl_ext_iovar_setint(dev, "nmode", 1);
+- wl_ext_iovar_setint(dev, "vhtmode", 1);
+- val = 1;
+- wl_ext_ioctl(dev, WLC_SET_GMODE, &val, sizeof(val), 1);
+- ANDROID_TRACE(("%s: Network mode: : B/G/N/AC mixed\n", __FUNCTION__));
+- }
+- wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1);
+-
+- return 0;
+-}
+-
+-static int
+-wl_ext_set_amode(struct wl_if_info *cur_if, struct wl_apsta_params *apsta_params)
+-{
+- struct net_device *dev = cur_if->dev;
+- authmode_t amode = cur_if->amode;
+- int auth=0, wpa_auth=0;
+-
+- if (amode == AUTH_OPEN) {
+- auth = 0;
+- wpa_auth = 0;
+- ANDROID_TRACE(("%s: Authentication: Open System\n", __FUNCTION__));
+- } else if (amode == AUTH_SHARED) {
+- auth = 1;
+- wpa_auth = 0;
+- ANDROID_TRACE(("%s: Authentication: Shared Key\n", __FUNCTION__));
+- } else if (amode == AUTH_WPAPSK) {
+- auth = 0;
+- wpa_auth = 4;
+- ANDROID_TRACE(("%s: Authentication: WPA-PSK\n", __FUNCTION__));
+- } else if (amode == AUTH_WPA2PSK) {
+- auth = 0;
+- wpa_auth = 128;
+- ANDROID_TRACE(("%s: Authentication: WPA2-PSK\n", __FUNCTION__));
+- } else if (amode == AUTH_WPAWPA2PSK) {
+- auth = 0;
+- wpa_auth = 132;
+- ANDROID_TRACE(("%s: Authentication: WPA/WPA2-PSK\n", __FUNCTION__));
+- }
+- wl_ext_iovar_setint(dev, "auth", auth);
+-
+- wl_ext_iovar_setint(dev, "wpa_auth", wpa_auth);
+-
+- return 0;
+-}
+-
+-static int
+-wl_ext_set_emode(struct wl_if_info *cur_if, struct wl_apsta_params *apsta_params)
+-{
+- struct net_device *dev = cur_if->dev;
+- int wsec=0;
+- struct wl_wsec_key wsec_key;
+- wsec_pmk_t psk;
+- encmode_t emode = cur_if->emode;
+- char *key = cur_if->key;
+-
+- memset(&wsec_key, 0, sizeof(wsec_key));
+- memset(&psk, 0, sizeof(psk));
+- if (emode == ENC_NONE) {
+- wsec = 0;
+- ANDROID_TRACE(("%s: Encryption: No securiy\n", __FUNCTION__));
+- } else if (emode == ENC_WEP) {
+- wsec = 1;
+- wl_ext_parse_wep(key, &wsec_key);
+- ANDROID_TRACE(("%s: Encryption: WEP\n", __FUNCTION__));
+- ANDROID_TRACE(("%s: Key: %s\n", __FUNCTION__, wsec_key.data));
+- } else if (emode == ENC_TKIP) {
+- wsec = 2;
+- psk.key_len = strlen(key);
+- psk.flags = WSEC_PASSPHRASE;
+- memcpy(psk.key, key, strlen(key));
+- ANDROID_TRACE(("%s: Encryption: TKIP\n", __FUNCTION__));
+- ANDROID_TRACE(("%s: Key: %s\n", __FUNCTION__, psk.key));
+- } else if (emode == ENC_AES) {
+- wsec = 4;
+- psk.key_len = strlen(key);
+- psk.flags = WSEC_PASSPHRASE;
+- memcpy(psk.key, key, strlen(key));
+- ANDROID_TRACE(("%s: Encryption: AES\n", __FUNCTION__));
+- ANDROID_TRACE(("%s: Key: %s\n", __FUNCTION__, psk.key));
+- } else if (emode == ENC_TKIPAES) {
+- wsec = 6;
+- psk.key_len = strlen(key);
+- psk.flags = WSEC_PASSPHRASE;
+- memcpy(psk.key, key, strlen(key));
+- ANDROID_TRACE(("%s: Encryption: TKIP/AES\n", __FUNCTION__));
+- ANDROID_TRACE(("%s: Key: %s\n", __FUNCTION__, psk.key));
+- }
+-
+- wl_ext_iovar_setint(dev, "wsec", wsec);
+-
+- if (wsec == 1) {
+- 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) {
+- if (cur_if->ifmode == ISTA_MODE)
+- wl_ext_iovar_setint(dev, "sup_wpa", 1);
+- wl_ext_ioctl(dev, WLC_SET_WSEC_PMK, &psk, sizeof(psk), 1);
+- } else {
+- ANDROID_ERROR(("%s: apdev is null\n", __FUNCTION__));
+- }
+- }
+-
+- return 0;
+-}
+-
+-static int
+-wl_ext_iapsta_init(struct net_device *dev, char *command, int total_len)
+-{
+- 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;
+-
+- if (apsta_params->init) {
+- ANDROID_ERROR(("%s: don't init twice\n", __FUNCTION__));
+- return -1;
+- }
+-
+- dhd = dhd_get_pub(dev);
+- memset(apsta_params, 0, sizeof(struct wl_apsta_params));
+-
+- 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 = 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;
+- }
+- }
+- param = bcmstrtok(&pick_tmp, " ", 0);
+- }
+-
+- if (apsta_params->apstamode == 0) {
+- ANDROID_ERROR(("%s: mode [sta|ap|apsta|dualap]\n", __FUNCTION__));
+- return -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;
+-
+- 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;
+-
+- 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;
+- wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1);
+-#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);
+- 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;
+- wl_ext_iovar_setint(dev, "mpc", 0);
+- 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);
+- if (FW_SUPPORTED(dhd, rsdb)) {
+- 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);
+- } else {
+- wl_ext_iovar_setbuf_bsscfg(dev, "ssid", &ssid, sizeof(ssid), iovar_buf,
+- WLC_IOCTL_SMLEN, 1, NULL);
+- }
+- }
+- 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;
+- 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 */
+- 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);
+- }
+- 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;
+- 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_GO);
+- wl_ext_iovar_setbuf(dev, "p2p_ifadd", &ifreq, sizeof(ifreq),
+- iovar_buf, WLC_IOCTL_SMLEN, NULL);
+- }
+- 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;
+- 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);
+- wl_ext_iovar_setbuf(dev, "p2p_ifadd", &ifreq, sizeof(ifreq),
+- iovar_buf, WLC_IOCTL_SMLEN, NULL);
+- }
+-
+- 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;
+-}
+-
+-static int
+-wl_ext_iapsta_config(struct net_device *dev, char *command, int total_len)
+-{
+- int i;
+- char *pch, *pick_tmp, *param;
+- struct wl_apsta_params *apsta_params = &g_apsta_params;
+- char ifname[IFNAMSIZ+1];
+- struct wl_if_info *cur_if = &apsta_params->pif;
+-
+- 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_config
+- param = bcmstrtok(&pick_tmp, " ", 0);
+-
+- if (param != NULL) {
+- if (strcmp(param, "ifname")) {
+- ANDROID_ERROR(("%s: first arg must be ifname\n", __FUNCTION__));
+- return -1;
+- }
+- }
+-
+- 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;
+- }
+- 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 in apstamode=%d\n", __FUNCTION__,
+- ifname, apsta_params->apstamode));
+- return -1;
+- }
+- } else if (!strcmp(param, "ssid")) {
+- pch = bcmstrtok(&pick_tmp, " ", 0);
+- if (pch)
+- strcpy(cur_if->ssid, pch);
+- } else if (!strcmp(param, "bssid")) {
+- pch = bcmstrtok(&pick_tmp, ": ", 0);
+- for (i=0; i<6 && pch; i++) {
+- ((u8 *)&cur_if->bssid)[i] = (int)simple_strtol(pch, NULL, 16);
+- pch = bcmstrtok(&pick_tmp, ": ", 0);
+- }
+- } else if (!strcmp(param, "bgnmode")) {
+- pch = bcmstrtok(&pick_tmp, " ", 0);
+- if (pch) {
+- if (!strcmp(pch, "b"))
+- cur_if->bgnmode = IEEE80211B;
+- else if (!strcmp(pch, "g"))
+- cur_if->bgnmode = IEEE80211G;
+- else if (!strcmp(pch, "bg"))
+- cur_if->bgnmode = IEEE80211BG;
+- else if (!strcmp(pch, "bgn"))
+- cur_if->bgnmode = IEEE80211BGN;
+- else if (!strcmp(pch, "bgnac"))
+- cur_if->bgnmode = IEEE80211BGNAC;
+- else {
+- ANDROID_ERROR(("%s: bgnmode [b|g|bg|bgn|bgnac]\n", __FUNCTION__));
+- return -1;
+- }
+- }
+- } else if (!strcmp(param, "hidden")) {
+- pch = bcmstrtok(&pick_tmp, " ", 0);
+- if (pch) {
+- if (!strcmp(pch, "n"))
+- cur_if->hidden = 0;
+- else if (!strcmp(pch, "y"))
+- cur_if->hidden = 1;
+- else {
+- ANDROID_ERROR(("%s: hidden [y|n]\n", __FUNCTION__));
+- return -1;
+- }
+- }
+- } else if (!strcmp(param, "maxassoc")) {
+- pch = bcmstrtok(&pick_tmp, " ", 0);
+- if (pch)
+- cur_if->maxassoc = (int)simple_strtol(pch, NULL, 10);
+- } else if (!strcmp(param, "chan")) {
+- pch = bcmstrtok(&pick_tmp, " ", 0);
+- if (pch)
+- cur_if->channel = (int)simple_strtol(pch, NULL, 10);
+- } else if (!strcmp(param, "amode")) {
+- pch = bcmstrtok(&pick_tmp, " ", 0);
+- if (pch) {
+- if (!strcmp(pch, "open"))
+- cur_if->amode = AUTH_OPEN;
+- else if (!strcmp(pch, "shared"))
+- cur_if->amode = AUTH_SHARED;
+- else if (!strcmp(pch, "wpapsk"))
+- cur_if->amode = AUTH_WPAPSK;
+- else if (!strcmp(pch, "wpa2psk"))
+- cur_if->amode = AUTH_WPA2PSK;
+- else if (!strcmp(pch, "wpawpa2psk"))
+- cur_if->amode = AUTH_WPAWPA2PSK;
+- else {
+- ANDROID_ERROR(("%s: amode [open|shared|wpapsk|wpa2psk|wpawpa2psk]\n",
+- __FUNCTION__));
+- return -1;
+- }
+- }
+- } else if (!strcmp(param, "emode")) {
+- pch = bcmstrtok(&pick_tmp, " ", 0);
+- if (pch) {
+- if (!strcmp(pch, "none"))
+- cur_if->emode = ENC_NONE;
+- else if (!strcmp(pch, "wep"))
+- cur_if->emode = ENC_WEP;
+- else if (!strcmp(pch, "tkip"))
+- cur_if->emode = ENC_TKIP;
+- else if (!strcmp(pch, "aes"))
+- cur_if->emode = ENC_AES;
+- else if (!strcmp(pch, "tkipaes"))
+- cur_if->emode = ENC_TKIPAES;
+- else {
+- ANDROID_ERROR(("%s: emode [none|wep|tkip|aes|tkipaes]\n",
+- __FUNCTION__));
+- return -1;
+- }
+- }
+- } else if (!strcmp(param, "key")) {
+- pch = bcmstrtok(&pick_tmp, " ", 0);
+- if (pch) {
+- strcpy(cur_if->key, pch);
+- }
+- }
+- param = bcmstrtok(&pick_tmp, " ", 0);
+- }
+-
+- return 0;
+-}
+-
+-static int
+-wl_ext_iapsta_disable(struct net_device *dev, char *command, int total_len)
+-{
+- char *pch, *pick_tmp, *param;
+- s8 iovar_buf[WLC_IOCTL_SMLEN];
+- wlc_ssid_t ssid = { 0, {0} };
+- scb_val_t scbval;
+- struct {
+- s32 tmp;
+- 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;
+- struct dhd_pub *dhd;
+-
+- 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_disable
+- 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;
+- }
+- }
+- 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;
+- }
+- if (!cur_if->dev) {
+- ANDROID_ERROR(("%s: %s is 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) {
+- // 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) {
+- 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
+- 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);
+- wl_ext_iovar_setint(dev, "mpc", 1);
+-#ifdef ARP_OFFLOAD_SUPPORT
+- /* IF SoftAP is disabled, enable arpoe back for STA mode. */
+- dhd_arp_offload_set(dhd, dhd_arp_mode);
+- dhd_arp_offload_enable(dhd, TRUE);
+-#endif /* ARP_OFFLOAD_SUPPORT */
+- } 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);
+- }
+-
+-#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);
+- }
+- }
+-#endif
+-#endif /* PROP_TXSTATUS_VSDB */
+-
+- cur_if->ifstate = IF_STATE_DISALBE;
+- printf("%s: apstamode=%d, ifname=%s\n", __FUNCTION__, apstamode, ifname);
+-
+- return 0;
+-}
+-
+-static int
+-wl_ext_iapsta_enable(struct net_device *dev, char *command, int total_len)
+-{
+- int ret = 0;
+- s32 val = 0;
+- char *pch, *pick_tmp, *param;
+- s8 iovar_buf[WLC_IOCTL_SMLEN];
+- wlc_ssid_t ssid = { 0, {0} };
+- 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 dhd_pub *dhd;
+-
+- 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;
+- }
+- }
+- 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;
+- }
+- if (!cur_if->dev) {
+- ANDROID_ERROR(("%s: %s is not ready\n", __FUNCTION__, ifname));
+- return -1;
+- }
+- 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;
+-
+- if (cur_if == &apsta_params->vif) {
+- 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) {
+- wl_ext_iovar_setint(dev, "mpc", 0);
+- if (apstamode == IAPONLY_MODE) {
+- wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1);
+- } else if (apstamode==IAPSTA_MODE || apstamode==IGOSTA_MODE) {
+- wl_ext_iovar_setbuf_bsscfg(cur_if->dev, "ssid", &ssid, sizeof(ssid),
+- iovar_buf, WLC_IOCTL_SMLEN, cur_if->bssidx, NULL);
+- }
+- }
+-
+- if (cur_if->ifmode == IAP_MODE) {
+- wl_ext_set_bgnmode(cur_if);
+- wl_ext_set_chanspec(cur_if->dev, cur_if->channel);
+- }
+- 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 (apstamode == ISTAONLY_MODE || apstamode == IGCSTA_MODE) {
+- wl_ext_ioctl(cur_if->dev, WLC_SET_SSID, &ssid, sizeof(ssid), 1);
+- } 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);
+- } else {
+- if (FW_SUPPORTED(dhd, rsdb)) {
+- wl_ext_ioctl(cur_if->dev, WLC_SET_SSID, &ssid, sizeof(ssid), 1);
+- } else {
+- bss_setbuf.cfg = htod32(cur_if->bssidx);
+- bss_setbuf.val = htod32(1);
+- wl_ext_iovar_setbuf(cur_if->dev, "bss", &bss_setbuf, sizeof(bss_setbuf),
+- iovar_buf, WLC_IOCTL_SMLEN, NULL);
+- }
+-#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 */
+- }
+- }
+- 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);
+- wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1);
+- }
+- }
+-#endif
+-#endif /* PROP_TXSTATUS_VSDB */
+-
+- printf("%s: ifname=%s, SSID: %s\n", __FUNCTION__, ifname, cur_if->ssid);
+-
+- cur_if->ifstate = IF_STATE_ENABLE;
+-
+-exit:
+- return ret;
+-}
+-
+-void
+-wl_android_ext_iapsta_disconnect_sta(struct net_device *dev, u32 channel)
+-{
+- 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);
+- }
+- }
+- }
+- }
+-}
+-
+-int wl_android_ext_attach_netdev(struct net_device *net, uint8 bssidx)
+-{
+- 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';
+- }
+- 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_android_ext_dettach_netdev(void)
+-{
+- struct wl_apsta_params *apsta_params = &g_apsta_params;
+-
+- ANDROID_TRACE(("%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)
+-{
+- unsigned char bytes[4];
+- int bytes_written=-1;
+-
+- bytes[0] = ip & 0xFF;
+- bytes[1] = (ip >> 8) & 0xFF;
+- bytes[2] = (ip >> 16) & 0xFF;
+- bytes[3] = (ip >> 24) & 0xFF;
+- bytes_written = sprintf(buf, "%d.%d.%d.%d", bytes[0], bytes[1], bytes[2], bytes[3]);
+-
+- return bytes_written;
+-}
+-
+-/*
+-terence 20170215:
+-dhd_priv dhcpc_dump ifname [wlan0|wlan1]
+-dhd_priv dhcpc_enable [0|1]
+-*/
+-int
+-wl_ext_dhcpc_enable(struct net_device *dev, char *command, int total_len)
+-{
+- int enable = -1, ret = -1;
+- int bytes_written = -1;
+-
+- ANDROID_TRACE(("%s: cmd %s\n", __FUNCTION__, command));
+-
+- sscanf(command, "%*s %d", &enable);
+-
+- if (enable >= 0)
+- ret = wl_ext_iovar_setint(dev, "dhcpc_enable", enable);
+- else {
+- ret = wl_ext_iovar_getint(dev, "dhcpc_enable", &enable);
+- if (!ret) {
+- bytes_written = snprintf(command, total_len, "%d", enable);
+- ANDROID_TRACE(("%s: command result is %s\n", __FUNCTION__, command));
+- ret = bytes_written;
+- }
+- }
+-
+- return ret;
+-}
+-
+-int
+-wl_ext_dhcpc_dump(struct net_device *dev, char *command, int total_len)
+-{
+-
+- int ret = 0;
+- int bytes_written = 0;
+- uint32 ip_addr;
+- char buf[20]="";
+-
+- ret = wl_ext_iovar_getint(dev, "dhcpc_ip_addr", &ip_addr);
+- if (!ret) {
+- wl_ext_ip_dump(ip_addr, buf);
+- bytes_written += snprintf(command+bytes_written, total_len, "ipaddr %s ", buf);
+- }
+-
+- ret = wl_ext_iovar_getint(dev, "dhcpc_ip_mask", &ip_addr);
+- if (!ret) {
+- wl_ext_ip_dump(ip_addr, buf);
+- bytes_written += snprintf(command+bytes_written, total_len, "mask %s ", buf);
+- }
+-
+- ret = wl_ext_iovar_getint(dev, "dhcpc_ip_gateway", &ip_addr);
+- if (!ret) {
+- wl_ext_ip_dump(ip_addr, buf);
+- bytes_written += snprintf(command+bytes_written, total_len, "gw %s ", buf);
+- }
+-
+- ret = wl_ext_iovar_getint(dev, "dhcpc_ip_dnsserv", &ip_addr);
+- if (!ret) {
+- wl_ext_ip_dump(ip_addr, buf);
+- bytes_written += snprintf(command+bytes_written, total_len, "dnsserv %s ", buf);
+- }
+-
+- if (!bytes_written)
+- bytes_written = -1;
+-
+- ANDROID_TRACE(("%s: command result is %s\n", __FUNCTION__, command));
+-
+- return bytes_written;
+-}
+-#endif
+-
+-/*
+-dhd_priv dhd [string] ==> Not ready
+-1. Get dhd val:
+- Ex: dhd_priv dhd bussleep
+-2. Set dhd val:
+- Ex: dhd_priv dhd bussleep 1
+-
+-dhd_priv wl [WLC_GET_PM] ==> Ready to get int val
+-dhd_priv wl [WLC_SET_PM] [int] ==> Ready to set int val
+-dhd_priv wl [string] ==> Ready to get int val
+-dhd_priv wl [string] [int] ==> Ready to set int val
+-Ex: get/set WLC_PM
+- dhd_priv wl 85
+- dhd_priv wl 86 1
+-Ex: get/set mpc
+- dhd_priv wl mpc
+- dhd_priv wl mpc 1
+-*/
+-int
+-wl_ext_iovar(struct net_device *dev, char *command, int total_len)
+-{
+- int ret = 0;
+- char wl[3]="\0", arg[20]="\0", cmd_str[20]="\0", val_str[20]="\0";
+- int cmd=-1, val=0;
+- int bytes_written=-1;
+-
+- ANDROID_TRACE(("%s: cmd %s\n", __FUNCTION__, command));
+-
+- sscanf(command, "%s %d %s", wl, &cmd, arg);
+- if (cmd < 0)
+- sscanf(command, "%s %s %s", wl, cmd_str, val_str);
+-
+- if (!strcmp(wl, "wl")) {
+- if (cmd>=0 && cmd!=WLC_GET_VAR && cmd!=WLC_SET_VAR) {
+- ret = sscanf(arg, "%d", &val);
+- if (ret > 0) { // set
+- ret = wl_ext_ioctl(dev, cmd, &val, sizeof(val), TRUE);
+- } else { // get
+- ret = wl_ext_ioctl(dev, cmd, &val, sizeof(val), FALSE);
+- if (!ret) {
+- bytes_written = snprintf(command, total_len, "%d", val);
+- ANDROID_TRACE(("%s: command result is %s\n", __FUNCTION__, command));
+- ret = bytes_written;
+- }
+- }
+- } else if (strlen(cmd_str)) {
+- ret = sscanf(val_str, "%d", &val);
+- if (ret > 0) { // set
+- ret = wl_ext_iovar_setint(dev, cmd_str, val);
+- } else { // get
+- ret = wl_ext_iovar_getint(dev, cmd_str, &val);
+- if (!ret) {
+- bytes_written = snprintf(command, total_len, "%d", val);
+- ANDROID_TRACE(("%s: command result is %s\n", __FUNCTION__, command));
+- ret = bytes_written;
+- }
+- }
+- }
+- }
+-
+- return ret;
+-}
+-
+-int wl_android_ext_priv_cmd(struct net_device *net, char *command, int total_len,
+- int *bytes_written)
+-{
+- int ret = 0;
+-
+- if (strnicmp(command, CMD_CHANNELS, strlen(CMD_CHANNELS)) == 0) {
+- *bytes_written = wl_ext_channels(net, command, total_len);
+- }
+- else if (strnicmp(command, CMD_CHANNEL, strlen(CMD_CHANNEL)) == 0) {
+- *bytes_written = wl_ext_channel(net, command, total_len);
+- }
+- else if (strnicmp(command, CMD_ROAM_TRIGGER, strlen(CMD_ROAM_TRIGGER)) == 0) {
+- *bytes_written = wl_ext_roam_trigger(net, command, total_len);
+- }
+- else if (strnicmp(command, CMD_KEEP_ALIVE, strlen(CMD_KEEP_ALIVE)) == 0) {
+- *bytes_written = wl_ext_keep_alive(net, command, total_len);
+- }
+- else if (strnicmp(command, CMD_PM, strlen(CMD_PM)) == 0) {
+- *bytes_written = wl_ext_pm(net, command, total_len);
+- }
+- else if (strnicmp(command, CMD_MONITOR, strlen(CMD_MONITOR)) == 0) {
+- *bytes_written = wl_ext_monitor(net, command, total_len);
+- }
+- else if (strnicmp(command, CMD_SET_SUSPEND_BCN_LI_DTIM, strlen(CMD_SET_SUSPEND_BCN_LI_DTIM)) == 0) {
+- int bcn_li_dtim;
+- bcn_li_dtim = (int)simple_strtol((command + strlen(CMD_SET_SUSPEND_BCN_LI_DTIM) + 1), NULL, 10);
+- *bytes_written = net_os_set_suspend_bcn_li_dtim(net, bcn_li_dtim);
+- }
+-#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);
+- }
+- 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_IAPSTA_ENABLE, strlen(CMD_IAPSTA_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);
+- }
+-#endif
+-#ifdef IDHCP
+- else if (strnicmp(command, CMD_DHCPC_ENABLE, strlen(CMD_DHCPC_ENABLE)) == 0) {
+- *bytes_written = wl_ext_dhcpc_enable(net, command, total_len);
+- }
+- else if (strnicmp(command, CMD_DHCPC_DUMP, strlen(CMD_DHCPC_DUMP)) == 0) {
+- *bytes_written = wl_ext_dhcpc_dump(net, command, total_len);
+- }
+-#endif
+- else if (strnicmp(command, CMD_WL, strlen(CMD_WL)) == 0) {
+- *bytes_written = wl_ext_iovar(net, command, total_len);
+- }
+- else
+- ret = -1;
+-
+- return ret;
+-}
+-
+-#if defined(RSSIAVG)
+-void
+-wl_free_rssi_cache(wl_rssi_cache_ctrl_t *rssi_cache_ctrl)
+-{
+- wl_rssi_cache_t *node, *cur, **rssi_head;
+- int i=0;
+-
+- rssi_head = &rssi_cache_ctrl->m_cache_head;
+- node = *rssi_head;
+-
+- for (;node;) {
+- ANDROID_INFO(("%s: Free %d with BSSID %pM\n",
+- __FUNCTION__, i, &node->BSSID));
+- cur = node;
+- node = cur->next;
+- kfree(cur);
+- i++;
+- }
+- *rssi_head = NULL;
+-}
+-
+-void
+-wl_delete_dirty_rssi_cache(wl_rssi_cache_ctrl_t *rssi_cache_ctrl)
+-{
+- wl_rssi_cache_t *node, *prev, **rssi_head;
+- int i = -1, tmp = 0;
+- struct timeval now;
+-
+- do_gettimeofday(&now);
+-
+- rssi_head = &rssi_cache_ctrl->m_cache_head;
+- node = *rssi_head;
+- prev = node;
+- for (;node;) {
+- i++;
+- if (now.tv_sec > node->tv.tv_sec) {
+- if (node == *rssi_head) {
+- tmp = 1;
+- *rssi_head = node->next;
+- } else {
+- tmp = 0;
+- prev->next = node->next;
+- }
+- ANDROID_INFO(("%s: Del %d with BSSID %pM\n",
+- __FUNCTION__, i, &node->BSSID));
+- kfree(node);
+- if (tmp == 1) {
+- node = *rssi_head;
+- prev = node;
+- } else {
+- node = prev->next;
+- }
+- continue;
+- }
+- prev = node;
+- node = node->next;
+- }
+-}
+-
+-void
+-wl_delete_disconnected_rssi_cache(wl_rssi_cache_ctrl_t *rssi_cache_ctrl, u8 *bssid)
+-{
+- wl_rssi_cache_t *node, *prev, **rssi_head;
+- int i = -1, tmp = 0;
+-
+- rssi_head = &rssi_cache_ctrl->m_cache_head;
+- node = *rssi_head;
+- prev = node;
+- for (;node;) {
+- i++;
+- if (!memcmp(&node->BSSID, bssid, ETHER_ADDR_LEN)) {
+- if (node == *rssi_head) {
+- tmp = 1;
+- *rssi_head = node->next;
+- } else {
+- tmp = 0;
+- prev->next = node->next;
+- }
+- ANDROID_INFO(("%s: Del %d with BSSID %pM\n",
+- __FUNCTION__, i, &node->BSSID));
+- kfree(node);
+- if (tmp == 1) {
+- node = *rssi_head;
+- prev = node;
+- } else {
+- node = prev->next;
+- }
+- continue;
+- }
+- prev = node;
+- node = node->next;
+- }
+-}
+-
+-void
+-wl_reset_rssi_cache(wl_rssi_cache_ctrl_t *rssi_cache_ctrl)
+-{
+- wl_rssi_cache_t *node, **rssi_head;
+-
+- rssi_head = &rssi_cache_ctrl->m_cache_head;
+-
+- /* reset dirty */
+- node = *rssi_head;
+- for (;node;) {
+- node->dirty += 1;
+- node = node->next;
+- }
+-}
+-
+-int
+-wl_update_connected_rssi_cache(struct net_device *net, wl_rssi_cache_ctrl_t *rssi_cache_ctrl, int *rssi_avg)
+-{
+- wl_rssi_cache_t *node, *prev, *leaf, **rssi_head;
+- int j, k=0;
+- int rssi, error=0;
+- struct ether_addr bssid;
+- struct timeval now, timeout;
+-
+- if (!g_wifi_on)
+- return 0;
+-
+- error = wldev_ioctl(net, WLC_GET_BSSID, &bssid, sizeof(bssid), false);
+- if (error == BCME_NOTASSOCIATED) {
+- ANDROID_INFO(("%s: Not Associated! res:%d\n", __FUNCTION__, error));
+- return 0;
+- }
+- if (error) {
+- ANDROID_ERROR(("Could not get bssid (%d)\n", error));
+- }
+- error = wldev_get_rssi(net, &rssi);
+- if (error) {
+- ANDROID_ERROR(("Could not get rssi (%d)\n", error));
+- return error;
+- }
+-
+- do_gettimeofday(&now);
+- timeout.tv_sec = now.tv_sec + RSSICACHE_TIMEOUT;
+- if (timeout.tv_sec < now.tv_sec) {
+- /*
+- * Integer overflow - assume long enough timeout to be assumed
+- * to be infinite, i.e., the timeout would never happen.
+- */
+- ANDROID_TRACE(("%s: Too long timeout (secs=%d) to ever happen - now=%lu, timeout=%lu",
+- __FUNCTION__, RSSICACHE_TIMEOUT, now.tv_sec, timeout.tv_sec));
+- }
+-
+- /* update RSSI */
+- rssi_head = &rssi_cache_ctrl->m_cache_head;
+- node = *rssi_head;
+- prev = NULL;
+- for (;node;) {
+- if (!memcmp(&node->BSSID, &bssid, ETHER_ADDR_LEN)) {
+- ANDROID_INFO(("%s: Update %d with BSSID %pM, RSSI=%d\n",
+- __FUNCTION__, k, &bssid, rssi));
+- for (j=0; jRSSI[j] = node->RSSI[j+1];
+- node->RSSI[j] = rssi;
+- node->dirty = 0;
+- node->tv = timeout;
+- goto exit;
+- }
+- prev = node;
+- node = node->next;
+- k++;
+- }
+-
+- leaf = kmalloc(sizeof(wl_rssi_cache_t), GFP_KERNEL);
+- if (!leaf) {
+- ANDROID_ERROR(("%s: Memory alloc failure %d\n",
+- __FUNCTION__, (int)sizeof(wl_rssi_cache_t)));
+- return 0;
+- }
+- ANDROID_INFO(("%s: Add %d with cached BSSID %pM, RSSI=%3d in the leaf\n",
+- __FUNCTION__, k, &bssid, rssi));
+-
+- leaf->next = NULL;
+- leaf->dirty = 0;
+- leaf->tv = timeout;
+- memcpy(&leaf->BSSID, &bssid, ETHER_ADDR_LEN);
+- for (j=0; jRSSI[j] = rssi;
+-
+- if (!prev)
+- *rssi_head = leaf;
+- else
+- prev->next = leaf;
+-
+-exit:
+- *rssi_avg = (int)wl_get_avg_rssi(rssi_cache_ctrl, &bssid);
+-
+- return error;
+-}
+-
+-void
+-wl_update_rssi_cache(wl_rssi_cache_ctrl_t *rssi_cache_ctrl, wl_scan_results_t *ss_list)
+-{
+- wl_rssi_cache_t *node, *prev, *leaf, **rssi_head;
+- wl_bss_info_t *bi = NULL;
+- int i, j, k;
+- struct timeval now, timeout;
+-
+- if (!ss_list->count)
+- return;
+-
+- do_gettimeofday(&now);
+- timeout.tv_sec = now.tv_sec + RSSICACHE_TIMEOUT;
+- if (timeout.tv_sec < now.tv_sec) {
+- /*
+- * Integer overflow - assume long enough timeout to be assumed
+- * to be infinite, i.e., the timeout would never happen.
+- */
+- ANDROID_TRACE(("%s: Too long timeout (secs=%d) to ever happen - now=%lu, timeout=%lu",
+- __FUNCTION__, RSSICACHE_TIMEOUT, now.tv_sec, timeout.tv_sec));
+- }
+-
+- rssi_head = &rssi_cache_ctrl->m_cache_head;
+-
+- /* update RSSI */
+- for (i = 0; i < ss_list->count; i++) {
+- node = *rssi_head;
+- prev = NULL;
+- k = 0;
+- bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : ss_list->bss_info;
+- for (;node;) {
+- if (!memcmp(&node->BSSID, &bi->BSSID, ETHER_ADDR_LEN)) {
+- ANDROID_INFO(("%s: Update %d with BSSID %pM, RSSI=%3d, SSID \"%s\"\n",
+- __FUNCTION__, k, &bi->BSSID, dtoh16(bi->RSSI), bi->SSID));
+- for (j=0; jRSSI[j] = node->RSSI[j+1];
+- node->RSSI[j] = dtoh16(bi->RSSI);
+- node->dirty = 0;
+- node->tv = timeout;
+- break;
+- }
+- prev = node;
+- node = node->next;
+- k++;
+- }
+-
+- if (node)
+- continue;
+-
+- leaf = kmalloc(sizeof(wl_rssi_cache_t), GFP_KERNEL);
+- if (!leaf) {
+- ANDROID_ERROR(("%s: Memory alloc failure %d\n",
+- __FUNCTION__, (int)sizeof(wl_rssi_cache_t)));
+- return;
+- }
+- ANDROID_INFO(("%s: Add %d with cached BSSID %pM, RSSI=%3d, SSID \"%s\" in the leaf\n",
+- __FUNCTION__, k, &bi->BSSID, dtoh16(bi->RSSI), bi->SSID));
+-
+- leaf->next = NULL;
+- leaf->dirty = 0;
+- leaf->tv = timeout;
+- memcpy(&leaf->BSSID, &bi->BSSID, ETHER_ADDR_LEN);
+- for (j=0; jRSSI[j] = dtoh16(bi->RSSI);
+-
+- if (!prev)
+- *rssi_head = leaf;
+- else
+- prev->next = leaf;
+- }
+-}
+-
+-int16
+-wl_get_avg_rssi(wl_rssi_cache_ctrl_t *rssi_cache_ctrl, void *addr)
+-{
+- wl_rssi_cache_t *node, **rssi_head;
+- int j, rssi_sum, rssi=RSSI_MINVAL;
+-
+- rssi_head = &rssi_cache_ctrl->m_cache_head;
+-
+- node = *rssi_head;
+- for (;node;) {
+- if (!memcmp(&node->BSSID, addr, ETHER_ADDR_LEN)) {
+- rssi_sum = 0;
+- rssi = 0;
+- for (j=0; jRSSI[RSSIAVG_LEN-j-1];
+- rssi = rssi_sum / j;
+- break;
+- }
+- node = node->next;
+- }
+- rssi = MIN(rssi, RSSI_MAXVAL);
+- if (rssi == RSSI_MINVAL) {
+- ANDROID_ERROR(("%s: BSSID %pM does not in RSSI cache\n",
+- __FUNCTION__, addr));
+- }
+- return (int16)rssi;
+-}
+-#endif
+-
+-#if defined(RSSIOFFSET)
+-int
+-wl_update_rssi_offset(struct net_device *net, int rssi)
+-{
+-#if defined(RSSIOFFSET_NEW)
+- int j;
+-#endif
+-
+- if (!g_wifi_on)
+- return rssi;
+-
+-#if defined(RSSIOFFSET_NEW)
+- for (j=0; jm_cache_head;
+- node = *bss_head;
+-
+- for (;node;) {
+- ANDROID_TRACE(("%s: Free %d with BSSID %pM\n",
+- __FUNCTION__, i, &node->results.bss_info->BSSID));
+- cur = node;
+- node = cur->next;
+- kfree(cur);
+- i++;
+- }
+- *bss_head = NULL;
+-}
+-
+-void
+-wl_delete_dirty_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl)
+-{
+- wl_bss_cache_t *node, *prev, **bss_head;
+- int i = -1, tmp = 0;
+- struct timeval now;
+-
+- do_gettimeofday(&now);
+-
+- bss_head = &bss_cache_ctrl->m_cache_head;
+- node = *bss_head;
+- prev = node;
+- for (;node;) {
+- i++;
+- if (now.tv_sec > node->tv.tv_sec) {
+- if (node == *bss_head) {
+- tmp = 1;
+- *bss_head = node->next;
+- } else {
+- tmp = 0;
+- prev->next = node->next;
+- }
+- ANDROID_TRACE(("%s: Del %d with BSSID %pM, RSSI=%3d, SSID \"%s\"\n",
+- __FUNCTION__, i, &node->results.bss_info->BSSID,
+- dtoh16(node->results.bss_info->RSSI), node->results.bss_info->SSID));
+- kfree(node);
+- if (tmp == 1) {
+- node = *bss_head;
+- prev = node;
+- } else {
+- node = prev->next;
+- }
+- continue;
+- }
+- prev = node;
+- node = node->next;
+- }
+-}
+-
+-void
+-wl_delete_disconnected_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl, u8 *bssid)
+-{
+- wl_bss_cache_t *node, *prev, **bss_head;
+- int i = -1, tmp = 0;
+-
+- bss_head = &bss_cache_ctrl->m_cache_head;
+- node = *bss_head;
+- prev = node;
+- for (;node;) {
+- i++;
+- if (!memcmp(&node->results.bss_info->BSSID, bssid, ETHER_ADDR_LEN)) {
+- if (node == *bss_head) {
+- tmp = 1;
+- *bss_head = node->next;
+- } else {
+- tmp = 0;
+- prev->next = node->next;
+- }
+- ANDROID_TRACE(("%s: Del %d with BSSID %pM, RSSI=%3d, SSID \"%s\"\n",
+- __FUNCTION__, i, &node->results.bss_info->BSSID,
+- dtoh16(node->results.bss_info->RSSI), node->results.bss_info->SSID));
+- kfree(node);
+- if (tmp == 1) {
+- node = *bss_head;
+- prev = node;
+- } else {
+- node = prev->next;
+- }
+- continue;
+- }
+- prev = node;
+- node = node->next;
+- }
+-}
+-
+-void
+-wl_reset_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl)
+-{
+- wl_bss_cache_t *node, **bss_head;
+-
+- bss_head = &bss_cache_ctrl->m_cache_head;
+-
+- /* reset dirty */
+- node = *bss_head;
+- for (;node;) {
+- node->dirty += 1;
+- node = node->next;
+- }
+-}
+-
+-void dump_bss_cache(
+-#if defined(RSSIAVG)
+- wl_rssi_cache_ctrl_t *rssi_cache_ctrl,
+-#endif
+- wl_bss_cache_t *node)
+-{
+- int k = 0;
+- int16 rssi;
+-
+- for (;node;) {
+-#if defined(RSSIAVG)
+- rssi = wl_get_avg_rssi(rssi_cache_ctrl, &node->results.bss_info->BSSID);
+-#else
+- rssi = dtoh16(node->results.bss_info->RSSI);
+-#endif
+- ANDROID_TRACE(("%s: dump %d with cached BSSID %pM, RSSI=%3d, SSID \"%s\"\n",
+- __FUNCTION__, k, &node->results.bss_info->BSSID, rssi, node->results.bss_info->SSID));
+- k++;
+- node = node->next;
+- }
+-}
+-
+-void
+-wl_update_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl,
+-#if defined(RSSIAVG)
+- wl_rssi_cache_ctrl_t *rssi_cache_ctrl,
+-#endif
+- wl_scan_results_t *ss_list)
+-{
+- wl_bss_cache_t *node, *prev, *leaf, **bss_head;
+- wl_bss_info_t *bi = NULL;
+- int i, k=0;
+-#if defined(SORT_BSS_BY_RSSI)
+- int16 rssi, rssi_node;
+-#endif
+- struct timeval now, timeout;
+-
+- if (!ss_list->count)
+- return;
+-
+- do_gettimeofday(&now);
+- timeout.tv_sec = now.tv_sec + BSSCACHE_TIMEOUT;
+- if (timeout.tv_sec < now.tv_sec) {
+- /*
+- * Integer overflow - assume long enough timeout to be assumed
+- * to be infinite, i.e., the timeout would never happen.
+- */
+- ANDROID_TRACE(("%s: Too long timeout (secs=%d) to ever happen - now=%lu, timeout=%lu",
+- __FUNCTION__, BSSCACHE_TIMEOUT, now.tv_sec, timeout.tv_sec));
+- }
+-
+- bss_head = &bss_cache_ctrl->m_cache_head;
+-
+- for (i=0; i < ss_list->count; i++) {
+- node = *bss_head;
+- prev = NULL;
+- bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : ss_list->bss_info;
+-
+- for (;node;) {
+- if (!memcmp(&node->results.bss_info->BSSID, &bi->BSSID, ETHER_ADDR_LEN)) {
+- if (node == *bss_head)
+- *bss_head = node->next;
+- else {
+- prev->next = node->next;
+- }
+- break;
+- }
+- prev = node;
+- node = node->next;
+- }
+-
+- leaf = kmalloc(dtoh32(bi->length) + sizeof(wl_bss_cache_t), GFP_KERNEL);
+- if (!leaf) {
+- ANDROID_ERROR(("%s: Memory alloc failure %d\n", __FUNCTION__,
+- dtoh32(bi->length) + (int)sizeof(wl_bss_cache_t)));
+- return;
+- }
+- if (node) {
+- kfree(node);
+- node = NULL;
+- ANDROID_TRACE(("%s: Update %d with cached BSSID %pM, RSSI=%3d, SSID \"%s\"\n",
+- __FUNCTION__, k, &bi->BSSID, dtoh16(bi->RSSI), bi->SSID));
+- } else
+- ANDROID_TRACE(("%s: Add %d with cached BSSID %pM, RSSI=%3d, SSID \"%s\"\n",
+- __FUNCTION__, k, &bi->BSSID, dtoh16(bi->RSSI), bi->SSID));
+-
+- memcpy(leaf->results.bss_info, bi, dtoh32(bi->length));
+- leaf->next = NULL;
+- leaf->dirty = 0;
+- leaf->tv = timeout;
+- leaf->results.count = 1;
+- leaf->results.version = ss_list->version;
+- k++;
+-
+- if (*bss_head == NULL)
+- *bss_head = leaf;
+- else {
+-#if defined(SORT_BSS_BY_RSSI)
+- node = *bss_head;
+-#if defined(RSSIAVG)
+- rssi = wl_get_avg_rssi(rssi_cache_ctrl, &leaf->results.bss_info->BSSID);
+-#else
+- rssi = dtoh16(leaf->results.bss_info->RSSI);
+-#endif
+- for (;node;) {
+-#if defined(RSSIAVG)
+- rssi_node = wl_get_avg_rssi(rssi_cache_ctrl, &node->results.bss_info->BSSID);
+-#else
+- rssi_node = dtoh16(node->results.bss_info->RSSI);
+-#endif
+- if (rssi > rssi_node) {
+- leaf->next = node;
+- if (node == *bss_head)
+- *bss_head = leaf;
+- else
+- prev->next = leaf;
+- break;
+- }
+- prev = node;
+- node = node->next;
+- }
+- if (node == NULL)
+- prev->next = leaf;
+-#else
+- leaf->next = *bss_head;
+- *bss_head = leaf;
+-#endif
+- }
+- }
+- dump_bss_cache(
+-#if defined(RSSIAVG)
+- rssi_cache_ctrl,
+-#endif
+- *bss_head);
+-}
+-
+-void
+-wl_release_bss_cache_ctrl(wl_bss_cache_ctrl_t *bss_cache_ctrl)
+-{
+- ANDROID_TRACE(("%s:\n", __FUNCTION__));
+- wl_free_bss_cache(bss_cache_ctrl);
+-}
+-#endif
+-
+-
++ }
++ *ret_chspec = fw_chspec;
++
++ return err;
++}
++
++int
++wl_ext_channel(struct net_device *dev, char* command, int total_len)
++{
++ int ret;
++ 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, &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));
++ ANDROID_TRACE(("target_channel %d\n", ci.target_channel));
++ ANDROID_TRACE(("scan_channel %d\n", ci.scan_channel));
++ bytes_written = snprintf(command, sizeof(channel_info_t)+2, "channel %d", ci.hw_channel);
++ ANDROID_TRACE(("%s: command result is %s\n", __FUNCTION__, command));
++ ret = bytes_written;
++ }
++ }
++
++ return ret;
++}
++
++int
++wl_ext_channels(struct net_device *dev, char* command, int total_len)
++{
++ int ret, i;
++ int bytes_written = -1;
++ u8 valid_chan_list[sizeof(u32)*(WL_NUMCHANNELS + 1)];
++ wl_uint32_list_t *list;
++
++ ANDROID_TRACE(("%s: cmd %s\n", __FUNCTION__, command));
++
++ memset(valid_chan_list, 0, sizeof(valid_chan_list));
++ list = (wl_uint32_list_t *)(void *) valid_chan_list;
++ list->count = htod32(WL_NUMCHANNELS);
++ ret = wldev_ioctl(dev, WLC_GET_VALID_CHANNELS, valid_chan_list, sizeof(valid_chan_list), 0);
++ if (ret<0) {
++ ANDROID_ERROR(("%s: get channels failed with %d\n", __FUNCTION__, ret));
++ } else {
++ bytes_written = snprintf(command, total_len, "channels");
++ for (i = 0; i < dtoh32(list->count); i++) {
++ bytes_written += snprintf(command+bytes_written, total_len, " %d", dtoh32(list->element[i]));
++ printf("%d ", dtoh32(list->element[i]));
++ }
++ printf("\n");
++ ret = bytes_written;
++ }
++
++ return ret;
++}
++
++int
++wl_ext_roam_trigger(struct net_device *dev, char* command, int total_len)
++{
++ int ret = 0;
++ int roam_trigger[2] = {0, 0};
++ int trigger[2]= {0, 0};
++ int bytes_written=-1;
++
++ sscanf(command, "%*s %10d", &roam_trigger[0]);
++
++ if (roam_trigger[0]) {
++ roam_trigger[1] = WLC_BAND_ALL;
++ ret = wldev_ioctl(dev, WLC_SET_ROAM_TRIGGER, roam_trigger, sizeof(roam_trigger), 1);
++ if (ret)
++ ANDROID_ERROR(("WLC_SET_ROAM_TRIGGER ERROR %d ret=%d\n", roam_trigger[0], ret));
++ } else {
++ roam_trigger[1] = WLC_BAND_2G;
++ ret = wldev_ioctl(dev, WLC_GET_ROAM_TRIGGER, roam_trigger, sizeof(roam_trigger), 0);
++ if (!ret)
++ trigger[0] = roam_trigger[0];
++ else
++ ANDROID_ERROR(("2G WLC_GET_ROAM_TRIGGER ERROR %d ret=%d\n", roam_trigger[0], ret));
++
++ roam_trigger[1] = WLC_BAND_5G;
++ ret = wldev_ioctl(dev, WLC_GET_ROAM_TRIGGER, roam_trigger, sizeof(roam_trigger), 0);
++ if (!ret)
++ trigger[1] = roam_trigger[0];
++ else
++ ANDROID_ERROR(("5G WLC_GET_ROAM_TRIGGER ERROR %d ret=%d\n", roam_trigger[0], ret));
++
++ ANDROID_TRACE(("roam_trigger %d %d\n", trigger[0], trigger[1]));
++ bytes_written = snprintf(command, total_len, "%d %d", trigger[0], trigger[1]);
++ ret = bytes_written;
++ }
++
++ return ret;
++}
++
++static int
++wl_ext_pattern_atoh(char *src, char *dst)
++{
++ int i;
++ if (strncmp(src, "0x", 2) != 0 &&
++ strncmp(src, "0X", 2) != 0) {
++ ANDROID_ERROR(("Mask invalid format. Needs to start with 0x\n"));
++ return -1;
++ }
++ src = src + 2; /* Skip past 0x */
++ if (strlen(src) % 2 != 0) {
++ DHD_ERROR(("Mask invalid format. Needs to be of even length\n"));
++ return -1;
++ }
++ for (i = 0; *src != '\0'; i++) {
++ char num[3];
++ bcm_strncpy_s(num, sizeof(num), src, 2);
++ num[2] = '\0';
++ dst[i] = (uint8)strtoul(num, NULL, 16);
++ src += 2;
++ }
++ return i;
++}
++
++int
++wl_ext_keep_alive(struct net_device *dev, char *command, int total_len)
++{
++ wl_mkeep_alive_pkt_t *mkeep_alive_pktp;
++ int ret = -1, i;
++ int id, period=-1, len_bytes=0, buf_len=0;
++ char data[200]="\0";
++ char buf[WLC_IOCTL_SMLEN]="\0", iovar_buf[WLC_IOCTL_SMLEN]="\0";
++ int bytes_written = -1;
++
++ ANDROID_TRACE(("%s: command = %s\n", __FUNCTION__, command));
++ sscanf(command, "%*s %d %d %s", &id, &period, data);
++ ANDROID_TRACE(("%s: id=%d, period=%d, data=%s\n", __FUNCTION__, id, period, data));
++
++ if (period >= 0) {
++ mkeep_alive_pktp = (wl_mkeep_alive_pkt_t *)buf;
++ mkeep_alive_pktp->version = htod16(WL_MKEEP_ALIVE_VERSION);
++ mkeep_alive_pktp->length = htod16(WL_MKEEP_ALIVE_FIXED_LEN);
++ mkeep_alive_pktp->keep_alive_id = id;
++ buf_len += WL_MKEEP_ALIVE_FIXED_LEN;
++ mkeep_alive_pktp->period_msec = period;
++ if (strlen(data)) {
++ len_bytes = wl_ext_pattern_atoh(data, (char *) mkeep_alive_pktp->data);
++ buf_len += len_bytes;
++ }
++ mkeep_alive_pktp->len_bytes = htod16(len_bytes);
++
++ ret = wl_ext_iovar_setbuf(dev, "mkeep_alive", buf, buf_len,
++ iovar_buf, sizeof(iovar_buf), NULL);
++ } else {
++ if (id < 0)
++ id = 0;
++ ret = wl_ext_iovar_getbuf(dev, "mkeep_alive", &id, sizeof(id), buf, sizeof(buf), NULL);
++ if (ret) {
++ goto exit;
++ } else {
++ mkeep_alive_pktp = (wl_mkeep_alive_pkt_t *) buf;
++ printf("Id :%d\n"
++ "Period (msec) :%d\n"
++ "Length :%d\n"
++ "Packet :0x",
++ mkeep_alive_pktp->keep_alive_id,
++ dtoh32(mkeep_alive_pktp->period_msec),
++ dtoh16(mkeep_alive_pktp->len_bytes));
++ for (i=0; ilen_bytes; i++) {
++ printf("%02x", mkeep_alive_pktp->data[i]);
++ }
++ printf("\n");
++ }
++ bytes_written = snprintf(command, total_len, "mkeep_alive_period_msec %d ", dtoh32(mkeep_alive_pktp->period_msec));
++ bytes_written += snprintf(command+bytes_written, total_len, "0x");
++ for (i=0; ilen_bytes; i++) {
++ bytes_written += snprintf(command+bytes_written, total_len, "%x", mkeep_alive_pktp->data[i]);
++ }
++ ANDROID_TRACE(("%s: command result is %s\n", __FUNCTION__, command));
++ ret = bytes_written;
++ }
++
++exit:
++ return ret;
++}
++
++int
++wl_ext_pm(struct net_device *dev, char *command, int total_len)
++{
++ int pm=-1, ret = -1;
++ char *pm_local;
++ int bytes_written=-1;
++
++ ANDROID_TRACE(("%s: cmd %s\n", __FUNCTION__, command));
++
++ sscanf(command, "%*s %d", &pm);
++
++ if (pm >= 0) {
++ ret = wldev_ioctl(dev, WLC_SET_PM, &pm, sizeof(pm), FALSE);
++ if (ret)
++ ANDROID_ERROR(("WLC_SET_PM ERROR %d ret=%d\n", pm, ret));
++ } else {
++ ret = wldev_ioctl(dev, WLC_GET_PM, &pm, sizeof(pm), FALSE);
++ if (!ret) {
++ ANDROID_TRACE(("%s: PM = %d\n", __func__, pm));
++ if (pm == PM_OFF)
++ pm_local = "PM_OFF";
++ else if(pm == PM_MAX)
++ pm_local = "PM_MAX";
++ else if(pm == PM_FAST)
++ pm_local = "PM_FAST";
++ else {
++ pm = 0;
++ pm_local = "Invalid";
++ }
++ bytes_written = snprintf(command, total_len, "PM %s", pm_local);
++ ANDROID_TRACE(("%s: command result is %s\n", __FUNCTION__, command));
++ ret = bytes_written;
++ }
++ }
++
++ return ret;
++}
++
++static int
++wl_ext_monitor(struct net_device *dev, char *command, int total_len)
++{
++ int val, ret = -1;
++ int bytes_written=-1;
++
++ sscanf(command, "%*s %d", &val);
++
++ if (val >=0) {
++ ret = wldev_ioctl(dev, WLC_SET_MONITOR, &val, sizeof(int), 1);
++ if (ret)
++ ANDROID_ERROR(("WLC_SET_MONITOR ERROR %d ret=%d\n", val, ret));
++ } else {
++ ret = wldev_ioctl(dev, WLC_GET_MONITOR, &val, sizeof(val), FALSE);
++ if (!ret) {
++ ANDROID_TRACE(("%s: monitor = %d\n", __FUNCTION__, val));
++ bytes_written = snprintf(command, total_len, "monitor %d", val);
++ ANDROID_TRACE(("%s: command result is %s\n", __FUNCTION__, command));
++ ret = bytes_written;
++ }
++ }
++
++ return ret;
++}
++
++#ifdef WL_EXT_IAPSTA
++struct wl_apsta_params g_apsta_params;
++static int
++wl_ext_parse_wep(char *key, struct wl_wsec_key *wsec_key)
++{
++ char hex[] = "XX";
++ unsigned char *data = wsec_key->data;
++ char *keystr = key;
++
++ switch (strlen(keystr)) {
++ case 5:
++ case 13:
++ case 16:
++ wsec_key->len = strlen(keystr);
++ memcpy(data, keystr, wsec_key->len + 1);
++ break;
++ case 12:
++ case 28:
++ case 34:
++ case 66:
++ /* strip leading 0x */
++ if (!strnicmp(keystr, "0x", 2))
++ keystr += 2;
++ else
++ return -1;
++ /* fall through */
++ case 10:
++ case 26:
++ case 32:
++ case 64:
++ wsec_key->len = strlen(keystr) / 2;
++ while (*keystr) {
++ strncpy(hex, keystr, 2);
++ *data++ = (char) strtoul(hex, NULL, 16);
++ keystr += 2;
++ }
++ break;
++ default:
++ return -1;
++ }
++
++ switch (wsec_key->len) {
++ case 5:
++ wsec_key->algo = CRYPTO_ALGO_WEP1;
++ break;
++ case 13:
++ wsec_key->algo = CRYPTO_ALGO_WEP128;
++ break;
++ case 16:
++ /* default to AES-CCM */
++ wsec_key->algo = CRYPTO_ALGO_AES_CCM;
++ break;
++ case 32:
++ wsec_key->algo = CRYPTO_ALGO_TKIP;
++ break;
++ default:
++ return -1;
++ }
++
++ /* Set as primary wsec_key by default */
++ wsec_key->flags |= WL_PRIMARY_KEY;
++
++ return 0;
++}
++
++static int
++wl_ext_set_bgnmode(struct wl_if_info *cur_if)
++{
++ struct net_device *dev = cur_if->dev;
++ bgnmode_t bgnmode = cur_if->bgnmode;
++ int val;
++
++ if (bgnmode == 0)
++ return 0;
++
++ wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1);
++ if (bgnmode == IEEE80211B) {
++ wl_ext_iovar_setint(dev, "nmode", 0);
++ val = 0;
++ wl_ext_ioctl(dev, WLC_SET_GMODE, &val, sizeof(val), 1);
++ ANDROID_TRACE(("%s: Network mode: B only\n", __FUNCTION__));
++ } else if (bgnmode == IEEE80211G) {
++ wl_ext_iovar_setint(dev, "nmode", 0);
++ val = 2;
++ wl_ext_ioctl(dev, WLC_SET_GMODE, &val, sizeof(val), 1);
++ ANDROID_TRACE(("%s: Network mode: G only\n", __FUNCTION__));
++ } else if (bgnmode == IEEE80211BG) {
++ wl_ext_iovar_setint(dev, "nmode", 0);
++ val = 1;
++ wl_ext_ioctl(dev, WLC_SET_GMODE, &val, sizeof(val), 1);
++ ANDROID_TRACE(("%s: Network mode: B/G mixed\n", __FUNCTION__));
++ } else if (bgnmode == IEEE80211BGN) {
++ wl_ext_iovar_setint(dev, "nmode", 0);
++ wl_ext_iovar_setint(dev, "nmode", 1);
++ wl_ext_iovar_setint(dev, "vhtmode", 0);
++ val = 1;
++ wl_ext_ioctl(dev, WLC_SET_GMODE, &val, sizeof(val), 1);
++ ANDROID_TRACE(("%s: Network mode: B/G/N mixed\n", __FUNCTION__));
++ } else if (bgnmode == IEEE80211BGNAC) {
++ wl_ext_iovar_setint(dev, "nmode", 0);
++ wl_ext_iovar_setint(dev, "nmode", 1);
++ wl_ext_iovar_setint(dev, "vhtmode", 1);
++ val = 1;
++ wl_ext_ioctl(dev, WLC_SET_GMODE, &val, sizeof(val), 1);
++ ANDROID_TRACE(("%s: Network mode: B/G/N/AC mixed\n", __FUNCTION__));
++ }
++ wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1);
++
++ return 0;
++}
++
++static void
++wl_ext_get_amode(struct wl_if_info *cur_if, char *amode)
++{
++ struct net_device *dev = cur_if->dev;
++ int auth=-1, wpa_auth=-1;
++
++ wl_ext_iovar_getint(dev, "auth", &auth);
++ wl_ext_iovar_getint(dev, "wpa_auth", &wpa_auth);
++
++ if (cur_if->ifmode == IMESH_MODE) {
++ if (auth == 0 && wpa_auth == 0) {
++ strcpy(amode, "open");
++ } else if (auth == 0 && wpa_auth == 128) {
++ strcpy(amode, "sae");
++ }
++ } else if (auth == 0 && wpa_auth == 0) {
++ strcpy(amode, "open");
++ } else if (auth == 1 && wpa_auth == 0) {
++ strcpy(amode, "shared");
++ } else if (auth == 0 && wpa_auth == 4) {
++ strcpy(amode, "wpapsk");
++ } else if (auth == 0 && wpa_auth == 128) {
++ strcpy(amode, "wpa2psk");
++ } else if (auth == 0 && wpa_auth == 132) {
++ strcpy(amode, "wpawpa2psk");
++ }
++}
++
++static void
++wl_ext_get_emode(struct wl_if_info *cur_if, char *emode)
++{
++ struct net_device *dev = cur_if->dev;
++ int wsec=0;
++
++ wl_ext_iovar_getint(dev, "wsec", &wsec);
++
++ if (cur_if->ifmode == IMESH_MODE) {
++ if (wsec == 0) {
++ strcpy(emode, "none");
++ } else {
++ strcpy(emode, "sae");
++ }
++ } else if (wsec == 0) {
++ strcpy(emode, "none");
++ } else if (wsec == 1) {
++ strcpy(emode, "wep");
++ } else if (wsec == 2 || wsec == 10) {
++ strcpy(emode, "tkip");
++ } else if (wsec == 4 || wsec == 12) {
++ strcpy(emode, "aes");
++ } else if (wsec == 6 || wsec == 14) {
++ strcpy(emode, "tkipaes");
++ }
++}
++
++static int
++wl_ext_set_amode(struct wl_if_info *cur_if)
++{
++ struct net_device *dev = cur_if->dev;
++ authmode_t amode = cur_if->amode;
++ int auth=0, wpa_auth=0;
++
++ if (cur_if->ifmode == IMESH_MODE) {
++ if (amode == AUTH_SAE) {
++ auth = 0;
++ wpa_auth = 128;
++ ANDROID_INFO(("%s: Authentication: SAE\n", __FUNCTION__));
++ } else {
++ auth = 0;
++ wpa_auth = 0;
++ ANDROID_INFO(("%s: Authentication: Open System\n", __FUNCTION__));
++ }
++ } else if (amode == AUTH_OPEN) {
++ auth = 0;
++ wpa_auth = 0;
++ ANDROID_INFO(("%s: Authentication: Open System\n", __FUNCTION__));
++ } else if (amode == AUTH_SHARED) {
++ auth = 1;
++ wpa_auth = 0;
++ ANDROID_INFO(("%s: Authentication: Shared Key\n", __FUNCTION__));
++ } else if (amode == AUTH_WPAPSK) {
++ auth = 0;
++ wpa_auth = 4;
++ ANDROID_INFO(("%s: Authentication: WPA-PSK\n", __FUNCTION__));
++ } else if (amode == AUTH_WPA2PSK) {
++ auth = 0;
++ wpa_auth = 128;
++ ANDROID_INFO(("%s: Authentication: WPA2-PSK\n", __FUNCTION__));
++ } else if (amode == AUTH_WPAWPA2PSK) {
++ auth = 0;
++ wpa_auth = 132;
++ ANDROID_INFO(("%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);
++
++ return 0;
++}
++
++static int
++wl_ext_set_emode(struct wl_if_info *cur_if, struct wl_apsta_params *apsta_params)
++{
++ struct net_device *dev = cur_if->dev;
++ 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];
++ struct dhd_pub *dhd = dhd_get_pub(dev);
++
++ memset(&wsec_key, 0, sizeof(wsec_key));
++ memset(&psk, 0, sizeof(psk));
++
++ if (cur_if->ifmode == IMESH_MODE) {
++ if (amode == AUTH_SAE) {
++ wsec = 4;
++ ANDROID_INFO(("%s: Encryption: AES\n", __FUNCTION__));
++ } else {
++ wsec = 0;
++ ANDROID_INFO(("%s: Encryption: No securiy\n", __FUNCTION__));
++ }
++ } else if (emode == ENC_NONE) {
++ wsec = 0;
++ ANDROID_INFO(("%s: Encryption: No securiy\n", __FUNCTION__));
++ } else if (emode == ENC_WEP) {
++ wsec = 1;
++ wl_ext_parse_wep(key, &wsec_key);
++ ANDROID_INFO(("%s: Encryption: WEP\n", __FUNCTION__));
++ ANDROID_INFO(("%s: Key: \"%s\"\n", __FUNCTION__, wsec_key.data));
++ } else if (emode == ENC_TKIP) {
++ wsec = 2;
++ psk.key_len = strlen(key);
++ psk.flags = WSEC_PASSPHRASE;
++ memcpy(psk.key, key, strlen(key));
++ ANDROID_INFO(("%s: Encryption: TKIP\n", __FUNCTION__));
++ ANDROID_INFO(("%s: Key: \"%s\"\n", __FUNCTION__, psk.key));
++ } else if (emode == ENC_AES || amode == AUTH_SAE) {
++ wsec = 4;
++ psk.key_len = strlen(key);
++ psk.flags = WSEC_PASSPHRASE;
++ memcpy(psk.key, key, strlen(key));
++ ANDROID_INFO(("%s: Encryption: AES\n", __FUNCTION__));
++ ANDROID_INFO(("%s: Key: \"%s\"\n", __FUNCTION__, psk.key));
++ } else if (emode == ENC_TKIPAES) {
++ wsec = 6;
++ psk.key_len = strlen(key);
++ psk.flags = WSEC_PASSPHRASE;
++ memcpy(psk.key, key, strlen(key));
++ ANDROID_INFO(("%s: Encryption: TKIP/AES\n", __FUNCTION__));
++ ANDROID_INFO(("%s: Key: \"%s\"\n", __FUNCTION__, psk.key));
++ }
++ if (dhd->conf->chip == BCM43430_CHIP_ID && cur_if->bssidx > 0 && wsec >= 2 &&
++ apsta_params->apstamode == IAPSTA_MODE) {
++ wsec |= 0x8; // terence 20180628: fix me, this is a workaround
++ }
++
++ wl_ext_iovar_setint(dev, "wsec", wsec);
++
++ if (cur_if->ifmode == IMESH_MODE) {
++ if (amode == AUTH_SAE) {
++ ANDROID_INFO(("%s: Key: \"%s\"\n", __FUNCTION__, key));
++ 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) {
++ if (cur_if->ifmode == ISTA_MODE)
++ wl_ext_iovar_setint(dev, "sup_wpa", 1);
++ wl_ext_ioctl(dev, WLC_SET_WSEC_PMK, &psk, sizeof(psk), 1);
++ } else {
++ ANDROID_ERROR(("%s: apdev is null\n", __FUNCTION__));
++ }
++ }
++
++ return 0;
++}
++
++static chanspec_t
++wl_ext_get_chanspec(struct net_device *dev, uint16 channel)
++{
++ s32 _chan = channel;
++ chanspec_t chspec = 0;
++ chanspec_t fw_chspec = 0;
++ u32 bw = WL_CHANSPEC_BW_20;
++ s32 err = BCME_OK;
++ s32 bw_cap = 0;
++ s8 iovar_buf[WLC_IOCTL_SMLEN];
++ struct {
++ u32 band;
++ u32 bw_cap;
++ } param = {0, 0};
++ uint band;
++ int ioctl_ver = 0;
++
++ if (_chan <= CH_MAX_2G_CHANNEL)
++ band = IEEE80211_BAND_2GHZ;
++ else
++ band = IEEE80211_BAND_5GHZ;
++ wl_ext_get_ioctl_ver(dev, &ioctl_ver);
++
++ if (band == IEEE80211_BAND_5GHZ) {
++ param.band = WLC_BAND_5G;
++ err = wldev_iovar_getbuf(dev, "bw_cap", ¶m, sizeof(param),
++ iovar_buf, WLC_IOCTL_SMLEN, NULL);
++ if (err) {
++ if (err != BCME_UNSUPPORTED) {
++ ANDROID_ERROR(("bw_cap failed, %d\n", err));
++ return err;
++ } else {
++ err = wldev_iovar_getint(dev, "mimo_bw_cap", &bw_cap);
++ if (err) {
++ ANDROID_ERROR(("error get mimo_bw_cap (%d)\n", err));
++ }
++ if (bw_cap != WLC_N_BW_20ALL)
++ bw = WL_CHANSPEC_BW_40;
++ }
++ } else {
++ if (WL_BW_CAP_80MHZ(iovar_buf[0]))
++ bw = WL_CHANSPEC_BW_80;
++ else if (WL_BW_CAP_40MHZ(iovar_buf[0]))
++ bw = WL_CHANSPEC_BW_40;
++ else
++ bw = WL_CHANSPEC_BW_20;
++
++ }
++ }
++ else if (band == IEEE80211_BAND_2GHZ)
++ bw = WL_CHANSPEC_BW_20;
++
++ chspec = wf_channel2chspec(_chan, bw);
++ if (wf_chspec_valid(chspec)) {
++ fw_chspec = wl_ext_chspec_host_to_driver(ioctl_ver, chspec);
++ if (fw_chspec == INVCHANSPEC) {
++ ANDROID_ERROR(("%s: failed to convert host chanspec to fw chanspec\n",
++ __FUNCTION__));
++ fw_chspec = 0;
++ }
++ }
++
++ return fw_chspec;
++}
++
++static void
++wl_ext_ch_to_chanspec(int ch, struct wl_join_params *join_params,
++ size_t *join_params_size)
++{
++ struct wl_apsta_params *apsta_params = &g_apsta_params;
++ chanspec_t chanspec = 0;
++
++ if (ch != 0) {
++ join_params->params.chanspec_num = 1;
++ join_params->params.chanspec_list[0] = ch;
++
++ if (join_params->params.chanspec_list[0] <= 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));
++ }
++}
++
++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_wait_netif_change(struct wl_apsta_params *apsta_params,
++ bool need_rtnl_unlock)
++{
++ if (need_rtnl_unlock)
++ rtnl_unlock();
++ wait_event_interruptible_timeout(apsta_params->netif_change_event,
++ apsta_params->netif_change, msecs_to_jiffies(1500));
++ if (need_rtnl_unlock)
++ rtnl_lock();
++}
++
++static void
++wl_ext_iapsta_preinit(struct net_device *dev, struct wl_apsta_params *apsta_params)
++{
++ struct dhd_pub *dhd;
++ apstamode_t apstamode = apsta_params->apstamode;
++ 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;
++
++ dhd = dhd_get_pub(dev);
++
++ for (i=0; iif_info[i];
++ if (i == 1 && !strlen(cur_if->ifname))
++ strcpy(cur_if->ifname, "wlan1");
++ if (i == 2 && !strlen(cur_if->ifname))
++ strcpy(cur_if->ifname, "wlan2");
++ if (cur_if->ifmode == ISTA_MODE) {
++ cur_if->channel = 0;
++ cur_if->maxassoc = -1;
++ cur_if->ifstate = IF_STATE_INIT;
++ cur_if->prio = PRIO_STA;
++ cur_if->prefix = 'S';
++ snprintf(cur_if->ssid, DOT11_MAX_SSID_LEN, "ttt_sta");
++ } else if (cur_if->ifmode == IAP_MODE) {
++ cur_if->channel = 1;
++ cur_if->maxassoc = -1;
++ cur_if->ifstate = IF_STATE_INIT;
++ cur_if->prio = PRIO_AP;
++ cur_if->prefix = 'A';
++ snprintf(cur_if->ssid, DOT11_MAX_SSID_LEN, "ttt_ap");
++ dfs = 0;
++ } else if (cur_if->ifmode == IMESH_MODE) {
++ cur_if->channel = 1;
++ cur_if->maxassoc = -1;
++ cur_if->ifstate = IF_STATE_INIT;
++ cur_if->prio = PRIO_MESH;
++ cur_if->prefix = 'M';
++ snprintf(cur_if->ssid, DOT11_MAX_SSID_LEN, "ttt_mesh");
++ 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);
++ }
++ wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1);
++ wl_ext_iovar_setint(dev, "dfs_chan_disable", 1);
++ wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1);
++ }
++
++ if (apstamode == IMESHONLY_MODE || apstamode == IMESHSTA_MODE ||
++ apstamode == IMESHAP_MODE || apstamode == IMESHAPSTA_MODE ||
++ apstamode == IMESHAPAP_MODE) {
++ wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1);
++ if (FW_SUPPORTED(dhd, rsdb)) {
++ wl_config_t rsdb_mode_cfg = {1, 0};
++ if (apsta_params->rsdb == 0) {
++ rsdb_mode_cfg.config = 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);
++ }
++ wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1);
++ }
++
++ if (apstamode == ISTAONLY_MODE) {
++ 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 (apstamode == IAPONLY_MODE) {
++ wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1);
++#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);
++ val = 1;
++ wl_ext_ioctl(dev, WLC_SET_AP, &val, sizeof(val), 1);
++#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 == 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);
++ apsta_params->netif_change = FALSE;
++ if (FW_SUPPORTED(dhd, rsdb)) {
++ 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);
++ } else {
++ wl_ext_iovar_setbuf_bsscfg(dev, "ssid", &ssid, sizeof(ssid),
++ iovar_buf, WLC_IOCTL_SMLEN, 1, NULL);
++ }
++ wl_ext_wait_netif_change(apsta_params, TRUE);
++ }
++ else if (apstamode == IDUALAP_MODE) {
++ wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 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, "mbcn", 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);
++ bzero(&iface, sizeof(wl_interface_create_t));
++ iface.ver = WL_INTERFACE_CREATE_VER;
++ iface.flags = WL_INTERFACE_CREATE_AP;
++ apsta_params->netif_change = FALSE;
++ wl_ext_iovar_getbuf_bsscfg(dev, "interface_create", &iface, sizeof(iface),
++ iovar_buf, WLC_IOCTL_SMLEN, 1, NULL);
++ wl_ext_wait_netif_change(apsta_params, TRUE);
++ }
++ else if (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
++ wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1);
++ // don't set WLC_SET_AP to 0, some parameters will be reset, such as bcn_timeout and roam_off
++ }
++ else if (apstamode == IMESHSTA_MODE) {
++ wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1);
++ wl_ext_iovar_setint(dev, "mpc", 0);
++ wl_ext_iovar_setint(dev, "mbcn", 1);
++ wl_ext_iovar_setint(dev, "apsta", 1);
++ wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1);
++ bzero(&iface, sizeof(wl_interface_create_t));
++ iface.ver = WL_INTERFACE_CREATE_VER;
++ iface.flags = WL_INTERFACE_CREATE_STA;
++ apsta_params->netif_change = FALSE;
++ wl_ext_iovar_getbuf_bsscfg(dev, "interface_create", &iface, sizeof(iface),
++ iovar_buf, WLC_IOCTL_SMLEN, 0, NULL);
++ wl_ext_wait_netif_change(apsta_params, TRUE);
++ }
++ else if (apstamode == IMESHAP_MODE) {
++ wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1);
++ wl_ext_iovar_setint(dev, "mpc", 0);
++ wl_ext_iovar_setint(dev, "mbcn", 1);
++ wl_ext_iovar_setint(dev, "apsta", 1); // keep 1 as we set in dhd_preinit_ioctls
++ wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1);
++ // 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;
++ apsta_params->netif_change = FALSE;
++ wl_ext_iovar_getbuf_bsscfg(dev, "interface_create", &iface, sizeof(iface),
++ iovar_buf, WLC_IOCTL_SMLEN, 0, NULL);
++ wl_ext_wait_netif_change(apsta_params, TRUE);
++ }
++ else if (apstamode == IMESHAPSTA_MODE) {
++ wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1);
++ wl_ext_iovar_setint(dev, "mpc", 0);
++ wl_ext_iovar_setint(dev, "mbcn", 1);
++ wl_ext_iovar_setint(dev, "apsta", 1); // keep 1 as we set in dhd_preinit_ioctls
++ wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1);
++ // 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;
++ apsta_params->netif_change = FALSE;
++ wl_ext_iovar_getbuf_bsscfg(dev, "interface_create", &iface, sizeof(iface),
++ iovar_buf, WLC_IOCTL_SMLEN, 0, NULL);
++ wl_ext_wait_netif_change(apsta_params, TRUE);
++ bzero(&iface, sizeof(wl_interface_create_t));
++ iface.ver = WL_INTERFACE_CREATE_VER;
++ iface.flags = WL_INTERFACE_CREATE_STA;
++ apsta_params->netif_change = FALSE;
++ wl_ext_iovar_getbuf_bsscfg(dev, "interface_create", &iface, sizeof(iface),
++ iovar_buf, WLC_IOCTL_SMLEN, 0, NULL);
++ wl_ext_wait_netif_change(apsta_params, TRUE);
++ }
++ else if (apstamode == IMESHAPAP_MODE) {
++ wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1);
++ wl_ext_iovar_setint(dev, "mpc", 0);
++ wl_ext_iovar_setint(dev, "mbcn", 1);
++ wl_ext_iovar_setint(dev, "apsta", 1); // keep 1 as we set in dhd_preinit_ioctls
++ wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1);
++ // 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;
++ apsta_params->netif_change = FALSE;
++ wl_ext_iovar_getbuf_bsscfg(dev, "interface_create", &iface, sizeof(iface),
++ iovar_buf, WLC_IOCTL_SMLEN, 0, NULL);
++ wl_ext_wait_netif_change(apsta_params, TRUE);
++ bzero(&iface, sizeof(wl_interface_create_t));
++ iface.ver = WL_INTERFACE_CREATE_VER;
++ iface.flags = WL_INTERFACE_CREATE_AP;
++ apsta_params->netif_change = FALSE;
++ wl_ext_iovar_getbuf_bsscfg(dev, "interface_create", &iface, sizeof(iface),
++ iovar_buf, WLC_IOCTL_SMLEN, 0, NULL);
++ wl_ext_wait_netif_change(apsta_params, TRUE);
++ }
++ else if (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_GO);
++ apsta_params->netif_change = FALSE;
++ wl_ext_iovar_setbuf(dev, "p2p_ifadd", &ifreq, sizeof(ifreq),
++ iovar_buf, WLC_IOCTL_SMLEN, NULL);
++ wl_ext_wait_netif_change(apsta_params, TRUE);
++ }
++
++ wl_ext_get_ioctl_ver(dev, &apsta_params->ioctl_ver);
++ apsta_params->init = TRUE;
++
++ printf("%s: apstamode=%d\n", __FUNCTION__, apstamode);
++}
++
++static int
++wl_ext_isam_init(struct net_device *dev, char *command, int total_len)
++{
++ char *pch, *pick_tmp, *pick_tmp2, *param;
++ struct wl_apsta_params *apsta_params = &g_apsta_params;
++ struct dhd_pub *dhd;
++ int i;
++
++ 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") ||
++ !strcmp(pick_tmp2, "sta-mesh")) {
++ apsta_params->apstamode = IMESHSTA_MODE;
++ } else if (!strcmp(pick_tmp2, "mesh-ap") ||
++ !strcmp(pick_tmp2, "ap-mesh")) {
++ apsta_params->apstamode = IMESHAP_MODE;
++ } else if (!strcmp(pick_tmp2, "mesh-ap-sta") ||
++ !strcmp(pick_tmp2, "sta-ap-mesh") ||
++ !strcmp(pick_tmp2, "sta-mesh-ap")) {
++ apsta_params->apstamode = IMESHAPSTA_MODE;
++ } else if (!strcmp(pick_tmp2, "mesh-ap-ap") ||
++ !strcmp(pick_tmp2, "ap-ap-mesh")) {
++ apsta_params->apstamode = IMESHAPAP_MODE;
++ } else if (!strcmp(pick_tmp2, "apsta")) {
++ apsta_params->apstamode = IAPSTA_MODE;
++ apsta_params->if_info[IF_PIF].ifmode = ISTA_MODE;
++ apsta_params->if_info[IF_VIF].ifmode = IAP_MODE;
++ } else if (!strcmp(pick_tmp2, "dualap")) {
++ apsta_params->apstamode = IDUALAP_MODE;
++ apsta_params->if_info[IF_PIF].ifmode = IAP_MODE;
++ apsta_params->if_info[IF_VIF].ifmode = IAP_MODE;
++ } else if (!strcmp(pick_tmp2, "gosta")) {
++ if (!FW_SUPPORTED(dhd, p2p)) {
++ return -1;
++ }
++ apsta_params->apstamode = IGOSTA_MODE;
++ apsta_params->if_info[IF_PIF].ifmode = ISTA_MODE;
++ apsta_params->if_info[IF_VIF].ifmode = IAP_MODE;
++ } else {
++ ANDROID_ERROR(("%s: mode [sta|ap|sta-ap|ap-ap]\n", __FUNCTION__));
++ return -1;
++ }
++ pch = bcmstrtok(&pick_tmp2, " -", 0);
++ for (i=0; iif_info[i].ifmode = ISTA_MODE;
++ else if (!strcmp(pch, "ap"))
++ apsta_params->if_info[i].ifmode = IAP_MODE;
++ else if (!strcmp(pch, "mesh")) {
++ if (dhd->conf->fw_type != FW_TYPE_MESH) {
++ ANDROID_ERROR(("%s: wrong fw type\n", __FUNCTION__));
++ return -1;
++ }
++ apsta_params->if_info[i].ifmode = IMESH_MODE;
++ }
++ pch = bcmstrtok(&pick_tmp2, " -", 0);
++ }
++ }
++ }
++ else if (!strcmp(param, "rsdb")) {
++ pch = bcmstrtok(&pick_tmp, " ", 0);
++ if (pch) {
++ if (!strcmp(pch, "y")) {
++ apsta_params->rsdb = TRUE;
++ } else if (!strcmp(pch, "n")) {
++ apsta_params->rsdb = FALSE;
++ } else {
++ ANDROID_ERROR(("%s: rsdb [y|n]\n", __FUNCTION__));
++ return -1;
++ }
++ }
++ } 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, "csa")) {
++ pch = bcmstrtok(&pick_tmp, " ", 0);
++ if (pch) {
++ apsta_params->csa = (int)simple_strtol(pch, NULL, 0);
++ }
++ } else if (!strcmp(param, "ifname")) {
++ pch = NULL;
++ pick_tmp2 = bcmstrtok(&pick_tmp, " ", 0);
++ if (pick_tmp2)
++ pch = bcmstrtok(&pick_tmp2, " -", 0);
++ for (i=0; iif_info[i].ifname, pch);
++ pch = bcmstrtok(&pick_tmp2, " -", 0);
++ }
++ } else if (!strcmp(param, "vifname")) {
++ pch = bcmstrtok(&pick_tmp, " ", 0);
++ if (pch)
++ strcpy(apsta_params->if_info[IF_VIF].ifname, pch);
++ else {
++ ANDROID_ERROR(("%s: vifname [wlan1]\n", __FUNCTION__));
++ return -1;
++ }
++ }
++ param = bcmstrtok(&pick_tmp, " ", 0);
++ }
++
++ if (apsta_params->apstamode == 0) {
++ ANDROID_ERROR(("%s: mode [sta|ap|sta-ap|ap-ap]\n", __FUNCTION__));
++ return -1;
++ }
++
++ wl_ext_iapsta_preinit(dev, apsta_params);
++
++ return 0;
++}
++
++static int
++wl_ext_parse_config(struct wl_if_info *cur_if, char *command, char **pick_next)
++{
++ char *pch, *pick_tmp;
++ char name[20], data[100];
++ int i, j, len;
++ 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;
++
++ // reset head and tail
++ 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);
++ }
++
++ // pick head
++ for (i = 0; i < sizeof(config_map)/sizeof(config_map[0]); i++) {
++ row = &config_map[i];
++ pch = strstr(pick_tmp, row->name);
++ if (pch) {
++ row->head = pch;
++ }
++ }
++
++ // sort by head
++ for (i = 0; i < sizeof(config_map)/sizeof(config_map[0]) - 1; i++) {
++ row_prev = &config_map[i];
++ for (j = i+1; j < sizeof(config_map)/sizeof(config_map[0]); j++) {
++ row = &config_map[j];
++ if (row->head < row_prev->head) {
++ strcpy(name, row_prev->name);
++ strcpy(row_prev->name, row->name);
++ strcpy(row->name, name);
++ pch = row_prev->head;
++ row_prev->head = row->head;
++ row->head = pch;
++ }
++ }
++ }
++
++ // pick tail
++ for (i = 0; i < sizeof(config_map)/sizeof(config_map[0]) - 1; i++) {
++ row_prev = &config_map[i];
++ row = &config_map[i+1];
++ if (row_prev->head) {
++ row_prev->tail = row->head;
++ }
++ }
++
++ // remove name from head
++ for (i = 0; i < sizeof(config_map)/sizeof(config_map[0]); i++) {
++ row = &config_map[i];
++ if (row->head) {
++ if (!strcmp(row->name, " ifname ")) {
++ ifname_head = row->head + 1;
++ break;
++ }
++ row->head += strlen(row->name);
++ }
++ }
++
++ for (i = 0; i < sizeof(config_map)/sizeof(config_map[0]); i++) {
++ row = &config_map[i];
++ if (row->head) {
++ memset(data, 0, sizeof(data));
++ if (row->tail && row->tail > row->head) {
++ strncpy(data, row->head, row->tail-row->head);
++ } else {
++ strcpy(data, row->head);
++ }
++ pick_tmp = data;
++
++ if (!strcmp(row->name, " ifname ")) {
++ break;
++ } else if (!strcmp(row->name, " ssid ")) {
++ len = strlen(pick_tmp);
++ memset(cur_if->ssid, 0, sizeof(cur_if->ssid));
++ if (pick_tmp[0] == '"' && pick_tmp[len-1] == '"')
++ strncpy(cur_if->ssid, &pick_tmp[1], len-2);
++ else
++ strcpy(cur_if->ssid, pick_tmp);
++ } else if (!strcmp(row->name, " bssid ")) {
++ pch = bcmstrtok(&pick_tmp, ": ", 0);
++ for (j=0; j<6 && pch; j++) {
++ ((u8 *)&cur_if->bssid)[j] = (int)simple_strtol(pch, NULL, 16);
++ pch = bcmstrtok(&pick_tmp, ": ", 0);
++ }
++ } else if (!strcmp(row->name, " bgnmode ")) {
++ if (!strcmp(pick_tmp, "b"))
++ cur_if->bgnmode = IEEE80211B;
++ else if (!strcmp(pick_tmp, "g"))
++ cur_if->bgnmode = IEEE80211G;
++ else if (!strcmp(pick_tmp, "bg"))
++ cur_if->bgnmode = IEEE80211BG;
++ else if (!strcmp(pick_tmp, "bgn"))
++ cur_if->bgnmode = IEEE80211BGN;
++ else if (!strcmp(pick_tmp, "bgnac"))
++ cur_if->bgnmode = IEEE80211BGNAC;
++ else {
++ ANDROID_ERROR(("%s: bgnmode [b|g|bg|bgn|bgnac]\n", __FUNCTION__));
++ return -1;
++ }
++ } else if (!strcmp(row->name, " hidden ")) {
++ if (!strcmp(pick_tmp, "n"))
++ cur_if->hidden = 0;
++ else if (!strcmp(pick_tmp, "y"))
++ cur_if->hidden = 1;
++ else {
++ ANDROID_ERROR(("%s: hidden [y|n]\n", __FUNCTION__));
++ return -1;
++ }
++ } else if (!strcmp(row->name, " maxassoc ")) {
++ cur_if->maxassoc = (int)simple_strtol(pick_tmp, NULL, 10);
++ } else if (!strcmp(row->name, " chan ")) {
++ cur_if->channel = (int)simple_strtol(pick_tmp, NULL, 10);
++ } else if (!strcmp(row->name, " amode ")) {
++ if (!strcmp(pick_tmp, "open"))
++ cur_if->amode = AUTH_OPEN;
++ else if (!strcmp(pick_tmp, "shared"))
++ cur_if->amode = AUTH_SHARED;
++ else if (!strcmp(pick_tmp, "wpapsk"))
++ cur_if->amode = AUTH_WPAPSK;
++ else if (!strcmp(pick_tmp, "wpa2psk"))
++ cur_if->amode = AUTH_WPA2PSK;
++ else if (!strcmp(pick_tmp, "wpawpa2psk"))
++ cur_if->amode = AUTH_WPAWPA2PSK;
++ else if (!strcmp(pick_tmp, "sae"))
++ cur_if->amode = AUTH_SAE;
++ else {
++ ANDROID_ERROR(("%s: amode [open|shared|wpapsk|wpa2psk|wpawpa2psk]\n",
++ __FUNCTION__));
++ return -1;
++ }
++ } else if (!strcmp(row->name, " emode ")) {
++ if (!strcmp(pick_tmp, "none"))
++ cur_if->emode = ENC_NONE;
++ else if (!strcmp(pick_tmp, "wep"))
++ cur_if->emode = ENC_WEP;
++ else if (!strcmp(pick_tmp, "tkip"))
++ cur_if->emode = ENC_TKIP;
++ else if (!strcmp(pick_tmp, "aes"))
++ cur_if->emode = ENC_AES;
++ else if (!strcmp(pick_tmp, "tkipaes"))
++ cur_if->emode = ENC_TKIPAES;
++ else {
++ ANDROID_ERROR(("%s: emode [none|wep|tkip|aes|tkipaes]\n",
++ __FUNCTION__));
++ return -1;
++ }
++ } else if (!strcmp(row->name, " key ")) {
++ len = strlen(pick_tmp);
++ memset(cur_if->key, 0, sizeof(cur_if->key));
++ if (pick_tmp[0] == '"' && pick_tmp[len-1] == '"')
++ strncpy(cur_if->key, &pick_tmp[1], len-2);
++ else
++ strcpy(cur_if->key, pick_tmp);
++ }
++ }
++ }
++
++ *pick_next = ifname_head;
++ return 0;
++}
++
++static int
++wl_ext_iapsta_config(struct net_device *dev, char *command, int total_len)
++{
++ int ret=0, i;
++ 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 = NULL;
++
++ 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_config
++
++ while (pick_tmp != NULL) {
++ memset(ifname, 0, IFNAMSIZ+1);
++ if (!strncmp(pick_tmp, "ifname ", strlen("ifname "))) {
++ pch = pick_tmp + strlen("ifname ");
++ pch2 = strchr(pch, ' ');
++ if (pch && pch2) {
++ strncpy(ifname, pch, pch2-pch);
++ } else {
++ ANDROID_ERROR(("%s: ifname [wlanX]\n", __FUNCTION__));
++ return -1;
++ }
++ for (i=0; iif_info[i].dev &&
++ !strcmp(apsta_params->if_info[i].dev->name, ifname)) {
++ cur_if = &apsta_params->if_info[i];
++ break;
++ }
++ }
++ if (!cur_if) {
++ ANDROID_ERROR(("%s: wrong ifname=%s in apstamode=%d\n", __FUNCTION__,
++ ifname, apsta_params->apstamode));
++ return -1;
++ }
++ ret = wl_ext_parse_config(cur_if, pick_tmp, &pick_next);
++ if (ret)
++ return -1;
++ pick_tmp = pick_next;
++ } else {
++ ANDROID_ERROR(("%s: first arg must be ifname\n", __FUNCTION__));
++ return -1;
++ }
++
++ }
++
++ return 0;
++}
++
++static int
++wl_ext_iapsta_disable(struct net_device *dev, char *command, int total_len)
++{
++ char *pch, *pick_tmp, *param;
++ s8 iovar_buf[WLC_IOCTL_SMLEN];
++ wlc_ssid_t ssid = { 0, {0} };
++ scb_val_t scbval;
++ struct {
++ s32 tmp;
++ 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 = NULL;
++ struct dhd_pub *dhd;
++ int i;
++
++ 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_disable
++ 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;
++ }
++ }
++ param = bcmstrtok(&pick_tmp, " ", 0);
++ }
++
++ for (i=0; iif_info[i].dev &&
++ !strcmp(apsta_params->if_info[i].dev->name, ifname)) {
++ cur_if = &apsta_params->if_info[i];
++ break;
++ }
++ }
++ if (!cur_if) {
++ ANDROID_ERROR(("%s: wrong ifname=%s or dev not ready\n", __FUNCTION__, ifname));
++ return -1;
++ }
++
++ printf("%s: Disabling %s\n", __FUNCTION__, ifname);
++
++ if (cur_if->ifmode == ISTA_MODE) {
++ wl_ext_ioctl(cur_if->dev, WLC_DISASSOC, NULL, 0, 1);
++ } 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 || 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) {
++ 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);
++ wl_ext_iovar_setint(dev, "mpc", 1);
++#ifdef ARP_OFFLOAD_SUPPORT
++ /* IF SoftAP is disabled, enable arpoe back for STA mode. */
++ 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);
++ }
++
++ cur_if->ifstate = IF_STATE_DISALBE;
++
++ printf("%s: disabled %s SSID: \"%s\"\n", __FUNCTION__, ifname, cur_if->ssid);
++
++ return 0;
++}
++
++static uint16
++wl_ext_get_chan(struct net_device *dev)
++{
++ struct wl_apsta_params *apsta_params = &g_apsta_params;
++ int ret = 0;
++ uint16 chan = 0, ctl_chan;
++ struct ether_addr bssid;
++ u32 chanspec = 0;
++
++ ret = wldev_ioctl(dev, WLC_GET_BSSID, &bssid, sizeof(bssid), 0);
++ if (ret != BCME_NOTASSOCIATED && memcmp(ðer_null, &bssid, ETHER_ADDR_LEN)) {
++ if (wldev_iovar_getint(dev, "chanspec", (s32 *)&chanspec) == BCME_OK) {
++ chanspec = wl_ext_chspec_driver_to_host(apsta_params->ioctl_ver, chanspec);
++ ctl_chan = wf_chspec_ctlchan(chanspec);
++ chan = (u16)(ctl_chan & 0x00FF);
++ ANDROID_INFO(("%s: cur_chan=%d(0x%x)\n", __FUNCTION__,
++ chan, chanspec));
++ return chan;
++ }
++ }
++
++ return 0;
++}
++
++static uint16
++wl_ext_get_vsdb_chan(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;
++ uint16 another_chan = 0, cur_chan = cur_if->channel;
++ struct dhd_pub *dhd;
++
++ dhd = dhd_get_pub(dev);
++
++ another_chan = wl_ext_get_chan(another_if->dev);
++ if (another_chan) {
++ ANDROID_INFO(("%s: cur_chan=%d, another_chan=%d\n",
++ __FUNCTION__, cur_chan, another_chan));
++ if ((cur_chan <= CH_MAX_2G_CHANNEL && another_chan > CH_MAX_2G_CHANNEL) ||
++ (cur_chan > CH_MAX_2G_CHANNEL && another_chan <= CH_MAX_2G_CHANNEL)) {
++ // different band
++ if (!FW_SUPPORTED(dhd, rsdb) || !apsta_params->rsdb)
++ return another_chan;
++ } else {
++ // same band
++ if (another_chan != cur_chan)
++ return another_chan;
++ }
++ }
++
++ return 0;
++}
++
++static int
++wl_ext_triger_csa(struct wl_if_info *cur_if)
++{
++ struct wl_apsta_params *apsta_params = &g_apsta_params;
++ s8 iovar_buf[WLC_IOCTL_SMLEN];
++
++ if (apsta_params->csa & CSA_DRV_BIT &&
++ (cur_if->ifmode == IAP_MODE || cur_if->ifmode == IMESH_MODE)) {
++ wl_chan_switch_t csa_arg;
++ memset(&csa_arg, 0, sizeof(csa_arg));
++ csa_arg.mode = 1;
++ csa_arg.count = 3;
++ csa_arg.chspec = wl_ext_get_chanspec(cur_if->dev, cur_if->channel);
++ printf("%s: Trigger CSA to channel %d\n", __FUNCTION__, cur_if->channel);
++ wl_ext_iovar_setbuf(cur_if->dev, "csa", &csa_arg, sizeof(csa_arg),
++ iovar_buf, sizeof(iovar_buf), NULL);
++ OSL_SLEEP(500);
++ }
++
++ return 0;
++}
++
++static void
++wl_ext_move_channel(struct wl_if_info *cur_if, uint16 chan)
++{
++ struct wl_apsta_params *apsta_params = &g_apsta_params;
++
++ if (chan) {
++ char cmd[32] = "";
++ cur_if->channel = chan;
++ if (apsta_params->csa == 0) {
++ printf("%s: %s deauthenticate all STA and move to channel %d\n",
++ __FUNCTION__, cur_if->ifname, chan);
++ snprintf(cmd, 32, "%s %s", "isam_disable ifname", cur_if->ifname);
++ wl_ext_iapsta_disable(cur_if->dev, cmd, strlen(cmd));
++
++ snprintf(cmd, 32, "%s %s", "isam_enable ifname", cur_if->ifname);
++ wl_ext_iapsta_enable(cur_if->dev, cmd, strlen(cmd));
++ } else {
++ wl_ext_triger_csa(cur_if);
++ }
++ }
++}
++
++static uint16
++wl_ext_move_cur_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;
++ wl_prio_t cur_prio;
++ int i;
++
++ if (apsta_params->vsdb) {
++ return cur_if->channel;
++ }
++
++ // find the max prio
++ cur_prio = cur_if->prio;
++ for (i=0; iif_info[i];
++ if (another_if->ifstate >= IF_STATE_INIT && cur_if != another_if &&
++ another_if->prio > cur_prio) {
++ new_chan = wl_ext_get_vsdb_chan(dev, cur_if, another_if);
++ if (new_chan) {
++ final_if = another_if;
++ cur_prio = another_if->prio;
++ }
++ }
++ }
++
++ if (new_chan) {
++ printf("%s: %s channel=%d => %s channel=%d\n", __FUNCTION__,
++ cur_if->ifname, cur_if->channel, final_if->ifname, new_chan);
++ cur_if->channel = new_chan;
++ }
++
++ return cur_if->channel;
++}
++
++static void
++wl_ext_move_other_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;
++ wl_prio_t prio = 0, cur_prio;
++ int i;
++
++ if (apsta_params->vsdb) {
++ return;
++ }
++
++ // find the max prio, but lower than cur_if
++ cur_prio = cur_if->prio;
++ for (i=0; iif_info[i];
++ if (another_if->ifstate >= IF_STATE_INIT && cur_if != another_if &&
++ another_if->prio >= prio && another_if->prio < cur_prio) {
++ new_chan = wl_ext_get_vsdb_chan(dev, cur_if, another_if);
++ if (new_chan) {
++ final_if = another_if;
++ prio = another_if->prio;
++ }
++ }
++ }
++
++ if (new_chan) {
++ printf("%s: %s channel=%d => %s channel=%d\n", __FUNCTION__,
++ final_if->ifname, final_if->channel, cur_if->ifname, cur_if->channel);
++ wl_ext_move_channel(final_if, cur_if->channel);
++ }
++
++}
++
++static int
++wl_ext_isam_dump_status(struct net_device *dev)
++{
++ struct wl_apsta_params *apsta_params = &g_apsta_params;
++ int i;
++ bool now_if;
++ struct wl_if_info *tmp_if;
++ uint16 chan = 0;
++ wlc_ssid_t ssid = { 0, {0} };
++ char amode[16], emode[16];
++
++ if (apsta_params->init == FALSE) {
++ return 0;
++ }
++
++ printf("****************************\n");
++ printf("%s: apstamode=%d\n", __FUNCTION__, apsta_params->apstamode);
++ for (i=0; iif_info[i];
++ if (dev == tmp_if->dev)
++ now_if = TRUE;
++ if (tmp_if->dev) {
++ chan = wl_ext_get_chan(tmp_if->dev);
++ if (chan) {
++ wl_ext_ioctl(tmp_if->dev, WLC_GET_SSID, &ssid, sizeof(ssid), 0);
++ wl_ext_get_amode(tmp_if, amode);
++ wl_ext_get_emode(tmp_if, emode);
++ }
++ if (chan) {
++ printf("%s[%c-%c%s]: chan %3d, amode %s, emode %s, SSID \"%s\"\n",
++ tmp_if->ifname, tmp_if->prefix, chan?'E':'D',
++ now_if?"*":" ", chan, amode, emode, ssid.SSID);
++ } else {
++ printf("%s[%c-%c%s]:\n",
++ tmp_if->ifname, tmp_if->prefix, chan?'E':'D',
++ now_if?"*":" ");
++ }
++ }
++ }
++ printf("****************************\n");
++
++ return 0;
++}
++
++static int
++wl_ext_enable_iface(struct net_device *dev, char *ifname)
++{
++ int i;
++ 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;
++ struct wl_if_info *cur_if = NULL;
++ struct dhd_pub *dhd;
++ uint16 cur_chan;
++
++ dhd = dhd_get_pub(dev);
++
++ for (i=0; iif_info[i].dev &&
++ !strcmp(apsta_params->if_info[i].dev->name, ifname)) {
++ cur_if = &apsta_params->if_info[i];
++ break;
++ }
++ }
++ if (!cur_if) {
++ ANDROID_ERROR(("%s: wrong ifname=%s or dev not ready\n", __FUNCTION__, ifname));
++ return -1;
++ }
++
++ printf("%s: Enabling %s\n", __FUNCTION__, ifname);
++
++ wl_ext_isam_dump_status(cur_if->dev);
++
++ wl_ext_move_cur_channel(dev, cur_if);
++
++ cur_chan = wl_ext_get_chan(cur_if->dev);
++ if (cur_chan) {
++ ANDROID_INFO(("%s: Associated!\n", __FUNCTION__));
++ if (cur_chan != cur_if->channel)
++ wl_ext_triger_csa(cur_if);
++ return 0;
++ }
++
++ wl_ext_move_other_channel(dev, cur_if);
++
++ if (cur_if->bssidx > 0) {
++ 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
++ ssid.SSID_len = strlen(cur_if->ssid);
++ memcpy(ssid.SSID, cur_if->ssid, ssid.SSID_len);
++ 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);
++ } else if (apstamode==IAPSTA_MODE || apstamode==IGOSTA_MODE) {
++ wl_ext_iovar_setbuf_bsscfg(cur_if->dev, "ssid", &ssid, sizeof(ssid),
++ iovar_buf, WLC_IOCTL_SMLEN, cur_if->bssidx, NULL);
++ }
++ }
++
++ if (cur_if->ifmode == IAP_MODE || cur_if->ifmode == IMESH_MODE) {
++ wl_ext_set_bgnmode(cur_if);
++ if (!cur_if->channel) {
++#ifdef WL_CFG80211
++ char *pick_tmp, *param;
++ char cmd[128];
++ uint16 cur_chan;
++ cur_chan = 1;
++ 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);
++ }
++ cur_if->channel = cur_chan;
++#else
++ cur_if->channel = 1;
++#endif
++ }
++ wl_ext_set_chanspec(cur_if->dev, cur_if->channel, &fw_chspec);
++ }
++
++ wl_ext_set_amode(cur_if);
++ wl_ext_set_emode(cur_if, apsta_params);
++
++ if (cur_if->ifmode == IAP_MODE) {
++ if (cur_if->maxassoc >= 0)
++ wl_ext_iovar_setint(dev, "maxassoc", cur_if->maxassoc);
++ // terence: fix me, hidden does not work in dualAP mode
++ 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) {
++ 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_connect(cur_if);
++ } else {
++ if (FW_SUPPORTED(dhd, rsdb)) {
++ wl_ext_ioctl(cur_if->dev, WLC_SET_SSID, &ssid, sizeof(ssid), 1);
++ } else {
++ bss_setbuf.cfg = htod32(cur_if->bssidx);
++ bss_setbuf.val = htod32(1);
++ wl_ext_iovar_setbuf(cur_if->dev, "bss", &bss_setbuf,
++ sizeof(bss_setbuf), iovar_buf, WLC_IOCTL_SMLEN, NULL);
++ }
++#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 */
++#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);
++ } 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);
++ }
++ }
++
++ OSL_SLEEP(1000);
++ printf("%s: enabled %s SSID: \"%s\"\n", __FUNCTION__, ifname, cur_if->ssid);
++
++ cur_if->ifstate = IF_STATE_ENABLE;
++
++ 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_enable_iface(dev, ifname);
++ if (ret)
++ return ret;
++ } else {
++ ANDROID_ERROR(("%s: ifname [wlanX]\n", __FUNCTION__));
++ return -1;
++ }
++ }
++ param = bcmstrtok(&pick_tmp, " ", 0);
++ }
++
++ return ret;
++}
++
++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;
++ int i;
++
++ if (apsta_params->init == TRUE) {
++ ANDROID_ERROR(("%s: don't init twice\n", __FUNCTION__));
++ return -1;
++ }
++
++ ANDROID_TRACE(("%s: Enter\n", __FUNCTION__));
++
++ for (i=0; iif_info[i];
++ if (i == 1 && !strlen(cur_if->ifname))
++ strcpy(cur_if->ifname, "wlan1");
++ if (i == 2 && !strlen(cur_if->ifname))
++ strcpy(cur_if->ifname, "wlan2");
++ if (cur_if->ifmode == ISTA_MODE) {
++ cur_if->channel = 0;
++ cur_if->maxassoc = -1;
++ cur_if->ifstate = IF_STATE_INIT;
++ cur_if->prio = PRIO_STA;
++ cur_if->prefix = 'S';
++ snprintf(cur_if->ssid, DOT11_MAX_SSID_LEN, "ttt_sta");
++ } else if (cur_if->ifmode == IAP_MODE) {
++ cur_if->channel = 1;
++ cur_if->maxassoc = -1;
++ cur_if->ifstate = IF_STATE_INIT;
++ cur_if->prio = PRIO_AP;
++ cur_if->prefix = 'A';
++ snprintf(cur_if->ssid, DOT11_MAX_SSID_LEN, "ttt_ap");
++ } else if (cur_if->ifmode == IMESH_MODE) {
++ cur_if->channel = 1;
++ cur_if->maxassoc = -1;
++ cur_if->ifstate = IF_STATE_INIT;
++ cur_if->prio = PRIO_MESH;
++ cur_if->prefix = 'M';
++ snprintf(cur_if->ssid, DOT11_MAX_SSID_LEN, "ttt_mesh");
++ }
++ }
++
++ 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;
++}
++
++#if defined(WL_WIRELESS_EXT)
++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;
++}
++#endif /* WL_WIRELESS_EXT */
++
++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;
++ int i;
++#if defined(WL_WIRELESS_EXT)
++ char extra[IW_CUSTOM_MAX + 1];
++ union iwreq_data wrqu;
++#endif
++ uint32 event_type = ntoh32(e->event_type);
++ uint32 status = ntoh32(e->status);
++ uint32 reason = ntoh32(e->reason);
++ uint16 flags = ntoh16(e->flags);
++
++ if (!apsta_params->init) {
++ ANDROID_TRACE(("%s: please init first\n", __FUNCTION__));
++ return -1;
++ }
++
++ for (i=0; iif_info[i].bssidx == e->ifidx) {
++ cur_if = &apsta_params->if_info[i];
++ break;
++ }
++ }
++ if (!cur_if || !cur_if->dev) {
++ ANDROID_ERROR(("%s: %s ifidx %d is not ready\n", __FUNCTION__,
++ dev->name, e->ifidx));
++ return -1;
++ }
++
++ if (cur_if->ifmode == ISTA_MODE) {
++ if (event_type == WLC_E_LINK) {
++ if (!(flags & WLC_EVENT_MSG_LINK)) {
++ printf("%s: %s[%c] Link Down with "MACSTR"\n", __FUNCTION__,
++ cur_if->ifname, cur_if->prefix, MAC2STR((u8 *)&e->addr));
++ } else {
++ printf("%s: %s[%c] Link UP with "MACSTR"\n", __FUNCTION__,
++ cur_if->ifname, cur_if->prefix, MAC2STR((u8 *)&e->addr));
++ }
++ }
++ }
++ else if (cur_if->ifmode == IAP_MODE || cur_if->ifmode == IMESH_MODE) {
++ if ((event_type == WLC_E_SET_SSID && status == WLC_E_STATUS_SUCCESS) ||
++ (event_type == WLC_E_LINK && status == WLC_E_STATUS_SUCCESS &&
++ reason == WLC_E_REASON_INITIAL_ASSOC)) {
++ printf("%s: %s[%c] Link up\n", __FUNCTION__,
++ cur_if->ifname, cur_if->prefix);
++ } else if ((event_type == WLC_E_LINK && reason == WLC_E_LINK_BSSCFG_DIS) ||
++ (event_type == WLC_E_LINK && status == WLC_E_STATUS_SUCCESS &&
++ reason == WLC_E_REASON_DEAUTH)) {
++ printf("%s: %s[%c] Link down\n", __FUNCTION__,
++ cur_if->ifname, cur_if->prefix);
++ }
++ else if ((event_type == WLC_E_ASSOC_IND || event_type == WLC_E_REASSOC_IND) &&
++ reason == DOT11_SC_SUCCESS) {
++ printf("%s: %s[%c] connected device "MACDBG"\n", __FUNCTION__,
++ cur_if->ifname, cur_if->prefix, MAC2STRDBG(e->addr.octet));
++ } else if (event_type == WLC_E_DISASSOC_IND) {
++ printf("%s: %s[%c] disassociated device "MACDBG"\n", __FUNCTION__,
++ cur_if->ifname, cur_if->prefix, MAC2STRDBG(e->addr.octet));
++ } else if (event_type == WLC_E_DEAUTH_IND ||
++ (event_type == WLC_E_DEAUTH && reason != DOT11_RC_RESERVED)) {
++ printf("%s: %s[%c] deauthenticated device "MACDBG"\n", __FUNCTION__,
++ cur_if->ifname, cur_if->prefix, MAC2STRDBG(e->addr.octet));
++ }
++ }
++
++#if defined(WL_WIRELESS_EXT)
++ memset(extra, 0, sizeof(extra));
++ memset(&wrqu, 0, sizeof(wrqu));
++ memcpy(wrqu.addr.sa_data, &e->addr, ETHER_ADDR_LEN);
++ wrqu.addr.sa_family = ARPHRD_ETHER;
++ if (wl_ext_conn_status_str(event_type, status, reason, extra, sizeof(extra))) {
++ wrqu.data.length = strlen(extra);
++ wireless_send_event(cur_if->dev, IWEVCUSTOM, &wrqu, extra);
++ ANDROID_INFO(("%s: %s[%c] event=%d, status=%d, reason=%d, flags=%d sent up\n",
++ __FUNCTION__, cur_if->ifname, cur_if->prefix, event_type, status,
++ reason, flags));
++ } else
++#endif /* WL_WIRELESS_EXT */
++ {
++ ANDROID_INFO(("%s: %s[%c] event=%d, status=%d, reason=%d, flags=%d\n",
++ __FUNCTION__, cur_if->ifname, cur_if->prefix, event_type, status,
++ reason, flags));
++ }
++
++ return 0;
++}
++
++u32
++wl_ext_iapsta_disconnect_sta(struct net_device *dev, u32 channel)
++{
++ struct wl_apsta_params *apsta_params = &g_apsta_params;
++ struct wl_if_info *cur_if = NULL;
++ int i;
++
++ wl_ext_isam_dump_status(dev);
++ for (i=0; iif_info[i];
++ if (cur_if->dev == dev) {
++ cur_if->channel = channel;
++ channel = wl_ext_move_cur_channel(apsta_params->if_info[IF_PIF].dev, cur_if);
++ wl_ext_move_other_channel(apsta_params->if_info[IF_PIF].dev, cur_if);
++ break;
++ }
++ }
++ return channel;
++}
++
++int
++wl_ext_iapsta_attach_name(struct net_device *net, uint8 bssidx)
++{
++ struct wl_apsta_params *apsta_params = &g_apsta_params;
++ struct dhd_pub *dhd;
++ struct wl_if_info *cur_if = NULL;
++
++ dhd = dhd_get_pub(net);
++
++ ANDROID_TRACE(("%s: bssidx=%d, %s\n", __FUNCTION__, bssidx, net->name));
++ if (bssidx < MAX_IF_NUM) {
++ cur_if = &apsta_params->if_info[bssidx];
++ }
++ if (bssidx == 0) {
++ if (dhd->conf->fw_type == FW_TYPE_MESH) {
++ apsta_params->rsdb = TRUE;
++ apsta_params->csa = CSA_FW_BIT | CSA_DRV_BIT;
++ }
++ strcpy(cur_if->ifname, net->name);
++ } else if (cur_if && cur_if->ifstate == IF_STATE_INIT) {
++ strcpy(cur_if->ifname, net->name);
++ apsta_params->netif_change = TRUE;
++ wake_up_interruptible(&apsta_params->netif_change_event);
++ }
++
++ return 0;
++}
++
++int
++wl_ext_iapsta_attach_netdev(struct net_device *net, uint8 bssidx)
++{
++ struct wl_apsta_params *apsta_params = &g_apsta_params;
++ struct dhd_pub *dhd;
++ struct wl_if_info *cur_if = NULL, *primary_if;
++
++ dhd = dhd_get_pub(net);
++
++ printf("%s: bssidx=%d\n", __FUNCTION__, bssidx);
++ if (bssidx < MAX_IF_NUM) {
++ cur_if = &apsta_params->if_info[bssidx];
++ }
++ if (bssidx == 0) {
++ memset(apsta_params, 0, sizeof(struct wl_apsta_params));
++ apsta_params->vsdb = FALSE;
++ cur_if->dev = net;
++ cur_if->bssidx = bssidx;
++ strcpy(cur_if->ifname, net->name);
++ init_waitqueue_head(&apsta_params->netif_change_event);
++ } else if (cur_if && cur_if->ifstate == IF_STATE_INIT) {
++ primary_if = &apsta_params->if_info[IF_PIF];
++ cur_if->dev = net;
++ cur_if->bssidx = bssidx;
++ if (strlen(cur_if->ifname)) {
++ memset(net->name, 0, sizeof(IFNAMSIZ));
++ strcpy(net->name, cur_if->ifname);
++ net->name[IFNAMSIZ-1] = '\0';
++ }
++ memcpy(net->dev_addr, primary_if->dev->dev_addr, ETHER_ADDR_LEN);
++ net->dev_addr[0] |= 0x02;
++ if (bssidx >= 2) {
++ net->dev_addr[4] ^= 0x80;
++ net->dev_addr[4] += bssidx;
++ net->dev_addr[5] += bssidx;
++ }
++ if (cur_if->ifmode == ISTA_MODE) {
++ wl_ext_iovar_setint(net, "roam_off", dhd->conf->roam_off);
++ wl_ext_iovar_setint(net, "bcn_timeout", dhd->conf->bcn_timeout);
++ }
++ }
++
++ return 0;
++}
++
++int
++wl_ext_iapsta_dettach_netdev(void)
++{
++ struct wl_apsta_params *apsta_params = &g_apsta_params;
++
++ 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)
++{
++ unsigned char bytes[4];
++ int bytes_written=-1;
++
++ bytes[0] = ip & 0xFF;
++ bytes[1] = (ip >> 8) & 0xFF;
++ bytes[2] = (ip >> 16) & 0xFF;
++ bytes[3] = (ip >> 24) & 0xFF;
++ bytes_written = sprintf(buf, "%d.%d.%d.%d", bytes[0], bytes[1], bytes[2], bytes[3]);
++
++ return bytes_written;
++}
++
++/*
++terence 20170215:
++dhd_priv dhcpc_dump ifname [wlan0|wlan1]
++dhd_priv dhcpc_enable [0|1]
++*/
++int
++wl_ext_dhcpc_enable(struct net_device *dev, char *command, int total_len)
++{
++ int enable = -1, ret = -1;
++ int bytes_written = -1;
++
++ ANDROID_TRACE(("%s: cmd %s\n", __FUNCTION__, command));
++
++ sscanf(command, "%*s %d", &enable);
++
++ if (enable >= 0)
++ ret = wl_ext_iovar_setint(dev, "dhcpc_enable", enable);
++ else {
++ ret = wl_ext_iovar_getint(dev, "dhcpc_enable", &enable);
++ if (!ret) {
++ bytes_written = snprintf(command, total_len, "%d", enable);
++ ANDROID_TRACE(("%s: command result is %s\n", __FUNCTION__, command));
++ ret = bytes_written;
++ }
++ }
++
++ return ret;
++}
++
++int
++wl_ext_dhcpc_dump(struct net_device *dev, char *command, int total_len)
++{
++ int ret = 0;
++ int bytes_written = 0;
++ uint32 ip_addr;
++ char buf[20]="";
++
++ ret = wl_ext_iovar_getint(dev, "dhcpc_ip_addr", &ip_addr);
++ if (!ret) {
++ wl_ext_ip_dump(ip_addr, buf);
++ bytes_written += snprintf(command+bytes_written, total_len, "ipaddr %s ", buf);
++ }
++
++ ret = wl_ext_iovar_getint(dev, "dhcpc_ip_mask", &ip_addr);
++ if (!ret) {
++ wl_ext_ip_dump(ip_addr, buf);
++ bytes_written += snprintf(command+bytes_written, total_len, "mask %s ", buf);
++ }
++
++ ret = wl_ext_iovar_getint(dev, "dhcpc_ip_gateway", &ip_addr);
++ if (!ret) {
++ wl_ext_ip_dump(ip_addr, buf);
++ bytes_written += snprintf(command+bytes_written, total_len, "gw %s ", buf);
++ }
++
++ ret = wl_ext_iovar_getint(dev, "dhcpc_ip_dnsserv", &ip_addr);
++ if (!ret) {
++ wl_ext_ip_dump(ip_addr, buf);
++ bytes_written += snprintf(command+bytes_written, total_len, "dnsserv %s ", buf);
++ }
++
++ if (!bytes_written)
++ bytes_written = -1;
++
++ ANDROID_TRACE(("%s: command result is %s\n", __FUNCTION__, command));
++
++ return bytes_written;
++}
++#endif
++
++/*
++dhd_priv dhd [string] ==> Not ready
++1. Get dhd val:
++ Ex: dhd_priv dhd bussleep
++2. Set dhd val:
++ Ex: dhd_priv dhd bussleep 1
++
++dhd_priv wl [WLC_GET_PM] ==> Ready to get int val
++dhd_priv wl [WLC_SET_PM] [int] ==> Ready to set int val
++dhd_priv wl [string] ==> Ready to get int val
++dhd_priv wl [string] [int] ==> Ready to set int val
++Ex: get/set WLC_PM
++ dhd_priv wl 85
++ dhd_priv wl 86 1
++Ex: get/set mpc
++ dhd_priv wl mpc
++ dhd_priv wl mpc 1
++*/
++int
++wl_ext_iovar(struct net_device *dev, char *command, int total_len)
++{
++ int ret = 0;
++ char wl[3]="\0", arg[20]="\0", cmd_str[20]="\0", val_str[20]="\0";
++ int cmd=-1, val=0;
++ int bytes_written=-1;
++
++ ANDROID_TRACE(("%s: cmd %s\n", __FUNCTION__, command));
++
++ sscanf(command, "%s %d %s", wl, &cmd, arg);
++ if (cmd < 0)
++ sscanf(command, "%s %s %s", wl, cmd_str, val_str);
++
++ if (!strcmp(wl, "wl")) {
++ if (cmd>=0 && cmd!=WLC_GET_VAR && cmd!=WLC_SET_VAR) {
++ ret = sscanf(arg, "%d", &val);
++ if (ret > 0) { // set
++ ret = wl_ext_ioctl(dev, cmd, &val, sizeof(val), TRUE);
++ } else { // get
++ ret = wl_ext_ioctl(dev, cmd, &val, sizeof(val), FALSE);
++ if (!ret) {
++ bytes_written = snprintf(command, total_len, "%d", val);
++ ANDROID_TRACE(("%s: command result is %s\n", __FUNCTION__, command));
++ ret = bytes_written;
++ }
++ }
++ } else if (strlen(cmd_str)) {
++ ret = sscanf(val_str, "%d", &val);
++ if (ret > 0) { // set
++ ret = wl_ext_iovar_setint(dev, cmd_str, val);
++ } else { // get
++ ret = wl_ext_iovar_getint(dev, cmd_str, &val);
++ if (!ret) {
++ bytes_written = snprintf(command, total_len, "%d", val);
++ ANDROID_TRACE(("%s: command result is %s\n", __FUNCTION__, command));
++ ret = bytes_written;
++ }
++ }
++ }
++ }
++
++ return ret;
++}
++
++int wl_android_ext_priv_cmd(struct net_device *net, char *command, int total_len,
++ int *bytes_written)
++{
++ int ret = 0;
++
++ if (strnicmp(command, CMD_CHANNELS, strlen(CMD_CHANNELS)) == 0) {
++ *bytes_written = wl_ext_channels(net, command, total_len);
++ }
++ else if (strnicmp(command, CMD_CHANNEL, strlen(CMD_CHANNEL)) == 0) {
++ *bytes_written = wl_ext_channel(net, command, total_len);
++ }
++ else if (strnicmp(command, CMD_ROAM_TRIGGER, strlen(CMD_ROAM_TRIGGER)) == 0) {
++ *bytes_written = wl_ext_roam_trigger(net, command, total_len);
++ }
++ else if (strnicmp(command, CMD_KEEP_ALIVE, strlen(CMD_KEEP_ALIVE)) == 0) {
++ *bytes_written = wl_ext_keep_alive(net, command, total_len);
++ }
++ else if (strnicmp(command, CMD_PM, strlen(CMD_PM)) == 0) {
++ *bytes_written = wl_ext_pm(net, command, total_len);
++ }
++ else if (strnicmp(command, CMD_MONITOR, strlen(CMD_MONITOR)) == 0) {
++ *bytes_written = wl_ext_monitor(net, command, total_len);
++ }
++ else if (strnicmp(command, CMD_SET_SUSPEND_BCN_LI_DTIM, strlen(CMD_SET_SUSPEND_BCN_LI_DTIM)) == 0) {
++ int bcn_li_dtim;
++ bcn_li_dtim = (int)simple_strtol((command + strlen(CMD_SET_SUSPEND_BCN_LI_DTIM) + 1), NULL, 10);
++ *bytes_written = net_os_set_suspend_bcn_li_dtim(net, bcn_li_dtim);
++ }
++#ifdef WL_EXT_IAPSTA
++ else if (strnicmp(command, CMD_IAPSTA_INIT, strlen(CMD_IAPSTA_INIT)) == 0) {
++ *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);
++ }
++ else if (strnicmp(command, CMD_ISAM_DUMP, strlen(CMD_ISAM_DUMP)) == 0) {
++ *bytes_written = wl_ext_isam_dump_status(net);
++ }
++#endif
++#ifdef IDHCP
++ else if (strnicmp(command, CMD_DHCPC_ENABLE, strlen(CMD_DHCPC_ENABLE)) == 0) {
++ *bytes_written = wl_ext_dhcpc_enable(net, command, total_len);
++ }
++ else if (strnicmp(command, CMD_DHCPC_DUMP, strlen(CMD_DHCPC_DUMP)) == 0) {
++ *bytes_written = wl_ext_dhcpc_dump(net, command, total_len);
++ }
++#endif
++#ifdef WL_CFG80211
++ else if (strnicmp(command, CMD_AUTOCHANNEL, strlen(CMD_AUTOCHANNEL)) == 0) {
++ *bytes_written = wl_cfg80211_autochannel(net, command, total_len);
++ }
++#endif
++#ifdef WL_ESCAN
++ else if (strnicmp(command, CMD_AUTOCHANNEL, strlen(CMD_AUTOCHANNEL)) == 0) {
++ *bytes_written = wl_escan_autochannel(net, command, total_len);
++ }
++#endif
++ else if (strnicmp(command, CMD_WL, strlen(CMD_WL)) == 0) {
++ *bytes_written = wl_ext_iovar(net, command, total_len);
++ }
++ else
++ ret = -1;
++
++ return ret;
++}
++
++#if defined(WL_CFG80211) || defined(WL_ESCAN)
++int
++wl_ext_get_distance(struct net_device *net, u32 band)
++{
++ u32 bw = WL_CHANSPEC_BW_20;
++ s32 bw_cap = 0, distance = 0;
++ struct {
++ u32 band;
++ u32 bw_cap;
++ } param = {0, 0};
++ char buf[WLC_IOCTL_SMLEN]="\0";
++ s32 err = BCME_OK;
++
++ param.band = band;
++ err = wldev_iovar_getbuf(net, "bw_cap", ¶m, sizeof(param), buf, sizeof(buf), NULL);
++ if (err) {
++ if (err != BCME_UNSUPPORTED) {
++ ANDROID_ERROR(("bw_cap failed, %d\n", err));
++ return err;
++ } else {
++ err = wl_ext_iovar_getint(net, "mimo_bw_cap", &bw_cap);
++ if (err) {
++ ANDROID_ERROR(("error get mimo_bw_cap (%d)\n", err));
++ }
++ if (bw_cap != WLC_N_BW_20ALL)
++ bw = WL_CHANSPEC_BW_40;
++ }
++ } else {
++ if (WL_BW_CAP_80MHZ(buf[0]))
++ bw = WL_CHANSPEC_BW_80;
++ else if (WL_BW_CAP_40MHZ(buf[0]))
++ bw = WL_CHANSPEC_BW_40;
++ else
++ bw = WL_CHANSPEC_BW_20;
++ }
++
++ if (bw == WL_CHANSPEC_BW_20)
++ distance = 2;
++ else if (bw == WL_CHANSPEC_BW_40)
++ distance = 4;
++ else if (bw == WL_CHANSPEC_BW_80)
++ distance = 8;
++ else
++ distance = 16;
++ ANDROID_INFO(("%s: bw=0x%x, distance=%d\n", __FUNCTION__, bw, distance));
++
++ return distance;
++}
++
++int
++wl_ext_get_best_channel(struct net_device *net,
++#if defined(BSSCACHE)
++ wl_bss_cache_ctrl_t *bss_cache_ctrl,
++#else
++ struct wl_scan_results *bss_list,
++#endif
++ int *best_2g_ch, int *best_5g_ch
++)
++{
++ struct wl_bss_info *bi = NULL; /* must be initialized */
++ s32 i, j;
++#if defined(BSSCACHE)
++ wl_bss_cache_t *node;
++#endif
++ int b_band[CH_MAX_2G_CHANNEL]={0}, a_band1[4]={0}, a_band4[5]={0};
++ s32 cen_ch, distance, distance_2g, distance_5g, ch, min_ap=999;
++ u8 valid_chan_list[sizeof(u32)*(WL_NUMCHANNELS + 1)];
++ wl_uint32_list_t *list;
++ int ret;
++ int ioctl_ver = 0;
++ chanspec_t chanspec;
++
++ memset(b_band, -1, sizeof(b_band));
++ memset(a_band1, -1, sizeof(a_band1));
++ memset(a_band4, -1, sizeof(a_band4));
++
++ memset(valid_chan_list, 0, sizeof(valid_chan_list));
++ list = (wl_uint32_list_t *)(void *) valid_chan_list;
++ list->count = htod32(WL_NUMCHANNELS);
++ ret = wldev_ioctl(net, WLC_GET_VALID_CHANNELS, valid_chan_list, sizeof(valid_chan_list), 0);
++ if (ret<0) {
++ ANDROID_ERROR(("%s: get channels failed with %d\n", __FUNCTION__, ret));
++ return 0;
++ } else {
++ for (i = 0; i < dtoh32(list->count); i++) {
++ ch = dtoh32(list->element[i]);
++ if (ch < CH_MAX_2G_CHANNEL)
++ b_band[ch-1] = 0;
++ else if (ch <= 48)
++ a_band1[(ch-36)/4] = 0;
++ else if (ch >= 149 && ch <= 161)
++ a_band4[(ch-149)/4] = 0;
++ }
++ }
++ wl_ext_get_ioctl_ver(net, &ioctl_ver);
++
++ distance_2g = wl_ext_get_distance(net, WLC_BAND_2G);
++ distance_5g = wl_ext_get_distance(net, WLC_BAND_5G);
++
++#if defined(BSSCACHE)
++ node = bss_cache_ctrl->m_cache_head;
++ for (i=0; node && i<256; i++)
++#else
++ for (i=0; i < bss_list->count; i++)
++#endif
++ {
++#if defined(BSSCACHE)
++ bi = node->results.bss_info;
++#else
++ bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : bss_list->bss_info;
++#endif
++ chanspec = wl_ext_chspec_driver_to_host(ioctl_ver, bi->chanspec);
++ cen_ch = CHSPEC_CHANNEL(bi->chanspec);
++ distance = 0;
++ if (CHSPEC_IS20(chanspec))
++ distance += 2;
++ else if (CHSPEC_IS40(chanspec))
++ distance += 4;
++ else if (CHSPEC_IS80(chanspec))
++ distance += 8;
++ else
++ distance += 16;
++
++ if (CHSPEC_IS2G(chanspec)) {
++ distance += distance_2g;
++ for (j=0; j= 0 && abs(cen_ch-(1+j)) <= distance)
++ b_band[j] += 1;
++ }
++ } else {
++ distance += distance_5g;
++ if (cen_ch <= 48) {
++ for (j=0; j= 0 && abs(cen_ch-(36+j*4)) <= distance)
++ a_band1[j] += 1;
++ }
++ } else if (cen_ch >= 149) {
++ for (j=0; j= 0 && abs(cen_ch-(149+j*4)) <= distance)
++ a_band4[j] += 1;
++ }
++ }
++ }
++#if defined(BSSCACHE)
++ node = node->next;
++#endif
++ }
++
++ *best_2g_ch = 0;
++ min_ap = 999;
++ for (i=0; i= 0) {
++ min_ap = b_band[i];
++ *best_2g_ch = i+1;
++ }
++ }
++ *best_5g_ch = 0;
++ min_ap = 999;
++ for (i=0; i= 0) {
++ min_ap = a_band1[i];
++ *best_5g_ch = i*4 + 36;
++ }
++ }
++ for (i=0; i= 0) {
++ min_ap = a_band4[i];
++ *best_5g_ch = i*4 + 149;
++ }
++ }
++
++ if (android_msg_level&ANDROID_INFO_LEVEL) {
++ printf("%s: b_band: ", __FUNCTION__);
++ for (j=0; jm_cache_head;
++ node = *rssi_head;
++
++ for (;node;) {
++ ANDROID_INFO(("%s: Free %d with BSSID %pM\n",
++ __FUNCTION__, i, &node->BSSID));
++ cur = node;
++ node = cur->next;
++ kfree(cur);
++ i++;
++ }
++ *rssi_head = NULL;
++}
++
++void
++wl_delete_dirty_rssi_cache(wl_rssi_cache_ctrl_t *rssi_cache_ctrl)
++{
++ wl_rssi_cache_t *node, *prev, **rssi_head;
++ int i = -1, tmp = 0;
++ struct timeval now;
++
++ do_gettimeofday(&now);
++
++ rssi_head = &rssi_cache_ctrl->m_cache_head;
++ node = *rssi_head;
++ prev = node;
++ for (;node;) {
++ i++;
++ if (now.tv_sec > node->tv.tv_sec) {
++ if (node == *rssi_head) {
++ tmp = 1;
++ *rssi_head = node->next;
++ } else {
++ tmp = 0;
++ prev->next = node->next;
++ }
++ ANDROID_INFO(("%s: Del %d with BSSID %pM\n",
++ __FUNCTION__, i, &node->BSSID));
++ kfree(node);
++ if (tmp == 1) {
++ node = *rssi_head;
++ prev = node;
++ } else {
++ node = prev->next;
++ }
++ continue;
++ }
++ prev = node;
++ node = node->next;
++ }
++}
++
++void
++wl_delete_disconnected_rssi_cache(wl_rssi_cache_ctrl_t *rssi_cache_ctrl, u8 *bssid)
++{
++ wl_rssi_cache_t *node, *prev, **rssi_head;
++ int i = -1, tmp = 0;
++
++ rssi_head = &rssi_cache_ctrl->m_cache_head;
++ node = *rssi_head;
++ prev = node;
++ for (;node;) {
++ i++;
++ if (!memcmp(&node->BSSID, bssid, ETHER_ADDR_LEN)) {
++ if (node == *rssi_head) {
++ tmp = 1;
++ *rssi_head = node->next;
++ } else {
++ tmp = 0;
++ prev->next = node->next;
++ }
++ ANDROID_INFO(("%s: Del %d with BSSID %pM\n",
++ __FUNCTION__, i, &node->BSSID));
++ kfree(node);
++ if (tmp == 1) {
++ node = *rssi_head;
++ prev = node;
++ } else {
++ node = prev->next;
++ }
++ continue;
++ }
++ prev = node;
++ node = node->next;
++ }
++}
++
++void
++wl_reset_rssi_cache(wl_rssi_cache_ctrl_t *rssi_cache_ctrl)
++{
++ wl_rssi_cache_t *node, **rssi_head;
++
++ rssi_head = &rssi_cache_ctrl->m_cache_head;
++
++ /* reset dirty */
++ node = *rssi_head;
++ for (;node;) {
++ node->dirty += 1;
++ node = node->next;
++ }
++}
++
++int
++wl_update_connected_rssi_cache(struct net_device *net, wl_rssi_cache_ctrl_t *rssi_cache_ctrl, int *rssi_avg)
++{
++ wl_rssi_cache_t *node, *prev, *leaf, **rssi_head;
++ int j, k=0;
++ int rssi, error=0;
++ struct ether_addr bssid;
++ struct timeval now, timeout;
++ scb_val_t scbval;
++
++ if (!g_wifi_on)
++ return 0;
++
++ error = wldev_ioctl(net, WLC_GET_BSSID, &bssid, sizeof(bssid), false);
++ if (error == BCME_NOTASSOCIATED) {
++ ANDROID_INFO(("%s: Not Associated! res:%d\n", __FUNCTION__, error));
++ return 0;
++ }
++ if (error) {
++ ANDROID_ERROR(("Could not get bssid (%d)\n", error));
++ }
++ error = wldev_get_rssi(net, &scbval);
++ if (error) {
++ ANDROID_ERROR(("Could not get rssi (%d)\n", error));
++ return error;
++ }
++ rssi = scbval.val;
++
++ do_gettimeofday(&now);
++ timeout.tv_sec = now.tv_sec + RSSICACHE_TIMEOUT;
++ if (timeout.tv_sec < now.tv_sec) {
++ /*
++ * Integer overflow - assume long enough timeout to be assumed
++ * to be infinite, i.e., the timeout would never happen.
++ */
++ ANDROID_TRACE(("%s: Too long timeout (secs=%d) to ever happen - now=%lu, timeout=%lu",
++ __FUNCTION__, RSSICACHE_TIMEOUT, now.tv_sec, timeout.tv_sec));
++ }
++
++ /* update RSSI */
++ rssi_head = &rssi_cache_ctrl->m_cache_head;
++ node = *rssi_head;
++ prev = NULL;
++ for (;node;) {
++ if (!memcmp(&node->BSSID, &bssid, ETHER_ADDR_LEN)) {
++ ANDROID_INFO(("%s: Update %d with BSSID %pM, RSSI=%d\n",
++ __FUNCTION__, k, &bssid, rssi));
++ for (j=0; jRSSI[j] = node->RSSI[j+1];
++ node->RSSI[j] = rssi;
++ node->dirty = 0;
++ node->tv = timeout;
++ goto exit;
++ }
++ prev = node;
++ node = node->next;
++ k++;
++ }
++
++ leaf = kmalloc(sizeof(wl_rssi_cache_t), GFP_KERNEL);
++ if (!leaf) {
++ ANDROID_ERROR(("%s: Memory alloc failure %d\n",
++ __FUNCTION__, (int)sizeof(wl_rssi_cache_t)));
++ return 0;
++ }
++ ANDROID_INFO(("%s: Add %d with cached BSSID %pM, RSSI=%3d in the leaf\n",
++ __FUNCTION__, k, &bssid, rssi));
++
++ leaf->next = NULL;
++ leaf->dirty = 0;
++ leaf->tv = timeout;
++ memcpy(&leaf->BSSID, &bssid, ETHER_ADDR_LEN);
++ for (j=0; jRSSI[j] = rssi;
++
++ if (!prev)
++ *rssi_head = leaf;
++ else
++ prev->next = leaf;
++
++exit:
++ *rssi_avg = (int)wl_get_avg_rssi(rssi_cache_ctrl, &bssid);
++
++ return error;
++}
++
++void
++wl_update_rssi_cache(wl_rssi_cache_ctrl_t *rssi_cache_ctrl, wl_scan_results_t *ss_list)
++{
++ wl_rssi_cache_t *node, *prev, *leaf, **rssi_head;
++ wl_bss_info_t *bi = NULL;
++ int i, j, k;
++ struct timeval now, timeout;
++
++ if (!ss_list->count)
++ return;
++
++ do_gettimeofday(&now);
++ timeout.tv_sec = now.tv_sec + RSSICACHE_TIMEOUT;
++ if (timeout.tv_sec < now.tv_sec) {
++ /*
++ * Integer overflow - assume long enough timeout to be assumed
++ * to be infinite, i.e., the timeout would never happen.
++ */
++ ANDROID_TRACE(("%s: Too long timeout (secs=%d) to ever happen - now=%lu, timeout=%lu",
++ __FUNCTION__, RSSICACHE_TIMEOUT, now.tv_sec, timeout.tv_sec));
++ }
++
++ rssi_head = &rssi_cache_ctrl->m_cache_head;
++
++ /* update RSSI */
++ for (i = 0; i < ss_list->count; i++) {
++ node = *rssi_head;
++ prev = NULL;
++ k = 0;
++ bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : ss_list->bss_info;
++ for (;node;) {
++ if (!memcmp(&node->BSSID, &bi->BSSID, ETHER_ADDR_LEN)) {
++ ANDROID_INFO(("%s: Update %d with BSSID %pM, RSSI=%3d, SSID \"%s\"\n",
++ __FUNCTION__, k, &bi->BSSID, dtoh16(bi->RSSI), bi->SSID));
++ for (j=0; jRSSI[j] = node->RSSI[j+1];
++ node->RSSI[j] = dtoh16(bi->RSSI);
++ node->dirty = 0;
++ node->tv = timeout;
++ break;
++ }
++ prev = node;
++ node = node->next;
++ k++;
++ }
++
++ if (node)
++ continue;
++
++ leaf = kmalloc(sizeof(wl_rssi_cache_t), GFP_KERNEL);
++ if (!leaf) {
++ ANDROID_ERROR(("%s: Memory alloc failure %d\n",
++ __FUNCTION__, (int)sizeof(wl_rssi_cache_t)));
++ return;
++ }
++ ANDROID_INFO(("%s: Add %d with cached BSSID %pM, RSSI=%3d, SSID \"%s\" in the leaf\n",
++ __FUNCTION__, k, &bi->BSSID, dtoh16(bi->RSSI), bi->SSID));
++
++ leaf->next = NULL;
++ leaf->dirty = 0;
++ leaf->tv = timeout;
++ memcpy(&leaf->BSSID, &bi->BSSID, ETHER_ADDR_LEN);
++ for (j=0; jRSSI[j] = dtoh16(bi->RSSI);
++
++ if (!prev)
++ *rssi_head = leaf;
++ else
++ prev->next = leaf;
++ }
++}
++
++int16
++wl_get_avg_rssi(wl_rssi_cache_ctrl_t *rssi_cache_ctrl, void *addr)
++{
++ wl_rssi_cache_t *node, **rssi_head;
++ int j, rssi_sum, rssi=RSSI_MINVAL;
++
++ rssi_head = &rssi_cache_ctrl->m_cache_head;
++
++ node = *rssi_head;
++ for (;node;) {
++ if (!memcmp(&node->BSSID, addr, ETHER_ADDR_LEN)) {
++ rssi_sum = 0;
++ rssi = 0;
++ for (j=0; jRSSI[RSSIAVG_LEN-j-1];
++ rssi = rssi_sum / j;
++ break;
++ }
++ node = node->next;
++ }
++ rssi = MIN(rssi, RSSI_MAXVAL);
++ if (rssi == RSSI_MINVAL) {
++ ANDROID_ERROR(("%s: BSSID %pM does not in RSSI cache\n",
++ __FUNCTION__, addr));
++ }
++ return (int16)rssi;
++}
++#endif
++
++#if defined(RSSIOFFSET)
++int
++wl_update_rssi_offset(struct net_device *net, int rssi)
++{
++#if defined(RSSIOFFSET_NEW)
++ int j;
++#endif
++
++ if (!g_wifi_on)
++ return rssi;
++
++#if defined(RSSIOFFSET_NEW)
++ for (j=0; jm_cache_head;
++ node = *bss_head;
++
++ for (;node;) {
++ ANDROID_TRACE(("%s: Free %d with BSSID %pM\n",
++ __FUNCTION__, i, &node->results.bss_info->BSSID));
++ cur = node;
++ node = cur->next;
++ kfree(cur);
++ i++;
++ }
++ *bss_head = NULL;
++}
++
++void
++wl_delete_dirty_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl)
++{
++ wl_bss_cache_t *node, *prev, **bss_head;
++ int i = -1, tmp = 0;
++ struct timeval now;
++
++ do_gettimeofday(&now);
++
++ bss_head = &bss_cache_ctrl->m_cache_head;
++ node = *bss_head;
++ prev = node;
++ for (;node;) {
++ i++;
++ if (now.tv_sec > node->tv.tv_sec) {
++ if (node == *bss_head) {
++ tmp = 1;
++ *bss_head = node->next;
++ } else {
++ tmp = 0;
++ prev->next = node->next;
++ }
++ ANDROID_TRACE(("%s: Del %d with BSSID %pM, RSSI=%3d, SSID \"%s\"\n",
++ __FUNCTION__, i, &node->results.bss_info->BSSID,
++ dtoh16(node->results.bss_info->RSSI), node->results.bss_info->SSID));
++ kfree(node);
++ if (tmp == 1) {
++ node = *bss_head;
++ prev = node;
++ } else {
++ node = prev->next;
++ }
++ continue;
++ }
++ prev = node;
++ node = node->next;
++ }
++}
++
++void
++wl_delete_disconnected_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl, u8 *bssid)
++{
++ wl_bss_cache_t *node, *prev, **bss_head;
++ int i = -1, tmp = 0;
++
++ bss_head = &bss_cache_ctrl->m_cache_head;
++ node = *bss_head;
++ prev = node;
++ for (;node;) {
++ i++;
++ if (!memcmp(&node->results.bss_info->BSSID, bssid, ETHER_ADDR_LEN)) {
++ if (node == *bss_head) {
++ tmp = 1;
++ *bss_head = node->next;
++ } else {
++ tmp = 0;
++ prev->next = node->next;
++ }
++ ANDROID_TRACE(("%s: Del %d with BSSID %pM, RSSI=%3d, SSID \"%s\"\n",
++ __FUNCTION__, i, &node->results.bss_info->BSSID,
++ dtoh16(node->results.bss_info->RSSI), node->results.bss_info->SSID));
++ kfree(node);
++ if (tmp == 1) {
++ node = *bss_head;
++ prev = node;
++ } else {
++ node = prev->next;
++ }
++ continue;
++ }
++ prev = node;
++ node = node->next;
++ }
++}
++
++void
++wl_reset_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl)
++{
++ wl_bss_cache_t *node, **bss_head;
++
++ bss_head = &bss_cache_ctrl->m_cache_head;
++
++ /* reset dirty */
++ node = *bss_head;
++ for (;node;) {
++ node->dirty += 1;
++ node = node->next;
++ }
++}
++
++void dump_bss_cache(
++#if defined(RSSIAVG)
++ wl_rssi_cache_ctrl_t *rssi_cache_ctrl,
++#endif
++ wl_bss_cache_t *node)
++{
++ int k = 0;
++ int16 rssi;
++
++ for (;node;) {
++#if defined(RSSIAVG)
++ rssi = wl_get_avg_rssi(rssi_cache_ctrl, &node->results.bss_info->BSSID);
++#else
++ rssi = dtoh16(node->results.bss_info->RSSI);
++#endif
++ ANDROID_TRACE(("%s: dump %d with cached BSSID %pM, RSSI=%3d, SSID \"%s\"\n",
++ __FUNCTION__, k, &node->results.bss_info->BSSID, rssi, node->results.bss_info->SSID));
++ k++;
++ node = node->next;
++ }
++}
++
++void
++wl_update_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl,
++#if defined(RSSIAVG)
++ wl_rssi_cache_ctrl_t *rssi_cache_ctrl,
++#endif
++ wl_scan_results_t *ss_list)
++{
++ wl_bss_cache_t *node, *prev, *leaf, **bss_head;
++ wl_bss_info_t *bi = NULL;
++ int i, k=0;
++#if defined(SORT_BSS_BY_RSSI)
++ int16 rssi, rssi_node;
++#endif
++ struct timeval now, timeout;
++
++ if (!ss_list->count)
++ return;
++
++ do_gettimeofday(&now);
++ timeout.tv_sec = now.tv_sec + BSSCACHE_TIMEOUT;
++ if (timeout.tv_sec < now.tv_sec) {
++ /*
++ * Integer overflow - assume long enough timeout to be assumed
++ * to be infinite, i.e., the timeout would never happen.
++ */
++ ANDROID_TRACE(("%s: Too long timeout (secs=%d) to ever happen - now=%lu, timeout=%lu",
++ __FUNCTION__, BSSCACHE_TIMEOUT, now.tv_sec, timeout.tv_sec));
++ }
++
++ bss_head = &bss_cache_ctrl->m_cache_head;
++
++ for (i=0; i < ss_list->count; i++) {
++ node = *bss_head;
++ prev = NULL;
++ bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : ss_list->bss_info;
++
++ for (;node;) {
++ if (!memcmp(&node->results.bss_info->BSSID, &bi->BSSID, ETHER_ADDR_LEN)) {
++ if (node == *bss_head)
++ *bss_head = node->next;
++ else {
++ prev->next = node->next;
++ }
++ break;
++ }
++ prev = node;
++ node = node->next;
++ }
++
++ leaf = kmalloc(dtoh32(bi->length) + sizeof(wl_bss_cache_t), GFP_KERNEL);
++ if (!leaf) {
++ ANDROID_ERROR(("%s: Memory alloc failure %d\n", __FUNCTION__,
++ dtoh32(bi->length) + (int)sizeof(wl_bss_cache_t)));
++ return;
++ }
++ if (node) {
++ kfree(node);
++ node = NULL;
++ ANDROID_TRACE(("%s: Update %d with cached BSSID %pM, RSSI=%3d, SSID \"%s\"\n",
++ __FUNCTION__, k, &bi->BSSID, dtoh16(bi->RSSI), bi->SSID));
++ } else
++ ANDROID_TRACE(("%s: Add %d with cached BSSID %pM, RSSI=%3d, SSID \"%s\"\n",
++ __FUNCTION__, k, &bi->BSSID, dtoh16(bi->RSSI), bi->SSID));
++
++ memcpy(leaf->results.bss_info, bi, dtoh32(bi->length));
++ leaf->next = NULL;
++ leaf->dirty = 0;
++ leaf->tv = timeout;
++ leaf->results.count = 1;
++ leaf->results.version = ss_list->version;
++ k++;
++
++ if (*bss_head == NULL)
++ *bss_head = leaf;
++ else {
++#if defined(SORT_BSS_BY_RSSI)
++ node = *bss_head;
++#if defined(RSSIAVG)
++ rssi = wl_get_avg_rssi(rssi_cache_ctrl, &leaf->results.bss_info->BSSID);
++#else
++ rssi = dtoh16(leaf->results.bss_info->RSSI);
++#endif
++ for (;node;) {
++#if defined(RSSIAVG)
++ rssi_node = wl_get_avg_rssi(rssi_cache_ctrl, &node->results.bss_info->BSSID);
++#else
++ rssi_node = dtoh16(node->results.bss_info->RSSI);
++#endif
++ if (rssi > rssi_node) {
++ leaf->next = node;
++ if (node == *bss_head)
++ *bss_head = leaf;
++ else
++ prev->next = leaf;
++ break;
++ }
++ prev = node;
++ node = node->next;
++ }
++ if (node == NULL)
++ prev->next = leaf;
++#else
++ leaf->next = *bss_head;
++ *bss_head = leaf;
++#endif
++ }
++ }
++ dump_bss_cache(
++#if defined(RSSIAVG)
++ rssi_cache_ctrl,
++#endif
++ *bss_head);
++}
++
++void
++wl_release_bss_cache_ctrl(wl_bss_cache_ctrl_t *bss_cache_ctrl)
++{
++ ANDROID_TRACE(("%s:\n", __FUNCTION__));
++ wl_free_bss_cache(bss_cache_ctrl);
++}
++#endif
++
++
+diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_cfg80211.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_cfg80211.c
+index aad697d70b09..1123b86f9406 100644
+--- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_cfg80211.c
++++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_cfg80211.c
+@@ -513,6 +513,14 @@ static s32 wl_cfg80211_del_station(struct wiphy *wiphy,
+ static s32 wl_cfg80211_del_station(struct wiphy *wiphy,
+ struct net_device *ndev, u8* mac_addr);
+ #endif
++#ifdef WLMESH
++static s32 wl_cfg80211_join_mesh(
++ struct wiphy *wiphy, struct net_device *dev,
++ const struct mesh_config *conf,
++ const struct mesh_setup *setup);
++static s32 wl_cfg80211_leave_mesh(struct wiphy *wiphy,
++ struct net_device *dev);
++#endif /* WLMESH */
+ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0))
+ static s32 wl_cfg80211_change_station(struct wiphy *wiphy,
+ struct net_device *dev, const u8 *mac, struct station_parameters *params);
+@@ -565,7 +573,11 @@ static s32 wl_cfg80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
+ #endif
+ #endif
+ #ifdef WL_SCHED_SCAN
+-static int wl_cfg80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev);
++static int wl_cfg80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev
++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0))
++ , u64 reqid
++#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */
++);
+ #endif
+ static s32 wl_cfg80211_set_ap_role(struct bcm_cfg80211 *cfg, struct net_device *dev);
+ #if defined(WL_VIRTUAL_APSTA) || defined(DUAL_STA_STATIC_IF)
+@@ -1312,6 +1324,13 @@ wl_cfg80211_ether_atoe(const char *a, struct ether_addr *n)
+ /* There isn't a lot of sense in it, but you can transmit anything you like */
+ static const struct ieee80211_txrx_stypes
+ wl_cfg80211_default_mgmt_stypes[NUM_NL80211_IFTYPES] = {
++#ifdef WLMESH
++ [NL80211_IFTYPE_MESH_POINT] = {
++ .tx = 0xffff,
++ .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
++ BIT(IEEE80211_STYPE_AUTH >> 4)
++ },
++#endif /* WLMESH */
+ [NL80211_IFTYPE_ADHOC] = {
+ .tx = 0xffff,
+ .rx = BIT(IEEE80211_STYPE_ACTION >> 4)
+@@ -1611,7 +1630,10 @@ wl_cfg80211_add_virtual_iface(struct wiphy *wiphy,
+ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0))
+ unsigned char name_assign_type,
+ #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)) */
+- enum nl80211_iftype type, u32 *flags,
++ enum nl80211_iftype type,
++#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0))
++ u32 *flags,
++#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0)) */
+ struct vif_params *params)
+ {
+ s32 err = -ENODEV;
+@@ -1628,10 +1650,10 @@ wl_cfg80211_add_virtual_iface(struct wiphy *wiphy,
+ struct ether_addr primary_mac;
+ bcm_struct_cfgdev *new_cfgdev;
+ #ifdef PROP_TXSTATUS_VSDB
+-#if defined(BCMSDIO)
++#if defined(BCMSDIO) || defined(BCMDBUS)
+ s32 up = 1;
+ bool enabled;
+-#endif
++#endif /* BCMSDIO || BCMDBUS */
+ #endif /* PROP_TXSTATUS_VSDB */
+ dhd_pub_t *dhd;
+ bool hang_required = false;
+@@ -1771,7 +1793,7 @@ wl_cfg80211_add_virtual_iface(struct wiphy *wiphy,
+
+ wl_cfg80211_scan_abort(cfg);
+ #ifdef PROP_TXSTATUS_VSDB
+-#if defined(BCMSDIO)
++#if defined(BCMSDIO) || defined(BCMDBUS)
+ if (!cfg->wlfc_on && !disable_proptx) {
+ dhd_wlfc_get_enable(dhd, &enabled);
+ if (!enabled && dhd->op_mode != DHD_FLAG_HOSTAP_MODE &&
+@@ -1783,7 +1805,7 @@ wl_cfg80211_add_virtual_iface(struct wiphy *wiphy,
+ }
+ cfg->wlfc_on = true;
+ }
+-#endif
++#endif /* BCMSDIO || BCMDBUS */
+ #endif /* PROP_TXSTATUS_VSDB */
+
+ /* Dual p2p doesn't support multiple P2PGO interfaces,
+@@ -1971,14 +1993,14 @@ wl_cfg80211_add_virtual_iface(struct wiphy *wiphy,
+ memset(cfg->p2p->vir_ifname, '\0', IFNAMSIZ);
+ wl_to_p2p_bss_bssidx(cfg, cfg_type) = -1;
+ #ifdef PROP_TXSTATUS_VSDB
+-#if defined(BCMSDIO)
++#if defined(BCMSDIO) || defined(BCMDBUS)
+ dhd_wlfc_get_enable(dhd, &enabled);
+ if (enabled && cfg->wlfc_on && dhd->op_mode != DHD_FLAG_HOSTAP_MODE &&
+ dhd->op_mode != DHD_FLAG_IBSS_MODE && dhd->conf->disable_proptx!=0) {
+ dhd_wlfc_deinit(dhd);
+ cfg->wlfc_on = false;
+ }
+-#endif
++#endif /* BCMSDIO || BCMDBUS */
+ #endif /* PROP_TXSTATUS_VSDB */
+ }
+ }
+@@ -2185,7 +2207,10 @@ done:
+
+ static s32
+ wl_cfg80211_change_virtual_iface(struct wiphy *wiphy, struct net_device *ndev,
+- enum nl80211_iftype type, u32 *flags,
++ enum nl80211_iftype type,
++#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0))
++ u32 *flags,
++#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0)) */
+ struct vif_params *params)
+ {
+ s32 ap = 0;
+@@ -2204,11 +2229,18 @@ wl_cfg80211_change_virtual_iface(struct wiphy *wiphy, struct net_device *ndev,
+ switch (type) {
+ case NL80211_IFTYPE_MONITOR:
+ case NL80211_IFTYPE_WDS:
++#ifndef WLMESH
+ case NL80211_IFTYPE_MESH_POINT:
++#endif /* WLMESH */
+ ap = 1;
+ WL_ERR(("type (%d) : currently we do not support this type\n",
+ type));
+ break;
++#ifdef WLMESH
++ case NL80211_IFTYPE_MESH_POINT:
++ infra_ibss = WL_BSSTYPE_MESH;
++ break;
++#endif /* WLMESH */
+ case NL80211_IFTYPE_ADHOC:
+ mode = WL_MODE_IBSS;
+ infra_ibss = 0;
+@@ -2433,10 +2465,10 @@ static s32 wl_cfg80211_handle_ifdel(struct bcm_cfg80211 *cfg, wl_if_event_info *
+ s32 type = -1;
+ s32 bssidx = -1;
+ #ifdef PROP_TXSTATUS_VSDB
+-#if defined(BCMSDIO)
++#if defined(BCMSDIO) || defined(BCMDBUS)
+ dhd_pub_t *dhd = (dhd_pub_t *)(cfg->pub);
+ bool enabled;
+-#endif
++#endif /* BCMSDIO || BCMDBUS */
+ #endif /* PROP_TXSTATUS_VSDB */
+
+ bssidx = if_event_info->bssidx;
+@@ -2466,14 +2498,14 @@ static s32 wl_cfg80211_handle_ifdel(struct bcm_cfg80211 *cfg, wl_if_event_info *
+ }
+
+ #ifdef PROP_TXSTATUS_VSDB
+-#if defined(BCMSDIO)
++#if defined(BCMSDIO) || defined(BCMDBUS)
+ dhd_wlfc_get_enable(dhd, &enabled);
+ if (enabled && cfg->wlfc_on && dhd->op_mode != DHD_FLAG_HOSTAP_MODE &&
+ dhd->op_mode != DHD_FLAG_IBSS_MODE && dhd->conf->disable_proptx!=0) {
+ dhd_wlfc_deinit(dhd);
+ cfg->wlfc_on = false;
+ }
+-#endif
++#endif /* BCMSDIO || BCMDBUS */
+ #endif /* PROP_TXSTATUS_VSDB */
+ }
+
+@@ -2789,9 +2821,7 @@ wl_run_escan(struct bcm_cfg80211 *cfg, struct net_device *ndev,
+
+ err = wldev_iovar_setbuf(ndev, "escan", params, params_size,
+ cfg->escan_ioctl_buf, WLC_IOCTL_MEDLEN, NULL);
+- WL_DBG(("LEGACY_SCAN sync ID: %d, bssidx: %d\n",
+- params->sync_id, bssidx));
+-
++ printf("%s: LEGACY_SCAN sync ID: %d, bssidx: %d\n", __FUNCTION__, params->sync_id, bssidx);
+ if (unlikely(err)) {
+ if (err == BCME_EPERM)
+ /* Scan Not permitted at this point of time */
+@@ -3664,6 +3694,51 @@ fail:
+ }
+ #endif /* WLAIBSS_MCHAN */
+
++#ifdef WLMESH
++s32
++wl_cfg80211_interface_ops(struct bcm_cfg80211 *cfg,
++ struct net_device *ndev, s32 bsscfg_idx,
++ enum nl80211_iftype iface_type, s32 del, u8 *addr)
++{
++ wl_interface_create_t iface;
++ s32 ret;
++ wl_interface_info_t *info;
++
++ bzero(&iface, sizeof(wl_interface_create_t));
++
++ iface.ver = WL_INTERFACE_CREATE_VER;
++
++ if (iface_type == NL80211_IFTYPE_AP)
++ iface.flags = WL_INTERFACE_CREATE_AP;
++ else
++ iface.flags = WL_INTERFACE_CREATE_STA;
++
++ if (del) {
++ ret = wldev_iovar_setbuf(ndev, "interface_remove",
++ NULL, 0, cfg->ioctl_buf, WLC_IOCTL_MEDLEN, NULL);
++ } else {
++ if (addr) {
++ memcpy(&iface.mac_addr.octet, addr, ETH_ALEN);
++ iface.flags |= WL_INTERFACE_MAC_USE;
++ }
++ ret = wldev_iovar_getbuf(ndev, "interface_create",
++ &iface, sizeof(wl_interface_create_t),
++ cfg->ioctl_buf, WLC_IOCTL_MAXLEN, &cfg->ioctl_buf_sync);
++ if (ret == 0) {
++ /* success */
++ info = (wl_interface_info_t *)cfg->ioctl_buf;
++ WL_DBG(("wl interface create success!! bssidx:%d \n",
++ info->bsscfgidx));
++ }
++ }
++
++ if (ret < 0)
++ WL_ERR(("Interface %s failed!! ret %d\n",
++ del ? "remove" : "create", ret));
++
++ return ret;
++}
++#else
+ s32
+ wl_cfg80211_interface_ops(struct bcm_cfg80211 *cfg,
+ struct net_device *ndev, s32 bsscfg_idx,
+@@ -3758,6 +3833,7 @@ wl_cfg80211_interface_ops(struct bcm_cfg80211 *cfg,
+ WL_DBG(("wl interface create success!! bssidx:%d \n", ret));
+ return ret;
+ }
++#endif
+
+ bool
+ wl_customer6_legacy_chip_check(struct bcm_cfg80211 *cfg,
+@@ -3792,7 +3868,27 @@ void
+ wl_bss_iovar_war(struct bcm_cfg80211 *cfg,
+ struct net_device *ndev, s32 *val)
+ {
+- if (wl_customer6_legacy_chip_check(cfg, ndev)) {
++ u32 chipnum;
++ wlc_rev_info_t revinfo;
++ int ret;
++ bool need_war = false;
++
++ /* Get the device rev info */
++ memset(&revinfo, 0, sizeof(revinfo));
++ ret = wldev_ioctl_get(ndev, WLC_GET_REVINFO, &revinfo, sizeof(revinfo));
++ if (ret < 0) {
++ WL_ERR(("%s: GET revinfo FAILED. ret:%d\n", __FUNCTION__, ret));
++ } else {
++ WL_DBG(("%s: GET_REVINFO device 0x%x, vendor 0x%x, chipnum 0x%x\n", __FUNCTION__,
++ dtoh32(revinfo.deviceid), dtoh32(revinfo.vendorid), dtoh32(revinfo.chipnum)));
++ chipnum = revinfo.chipnum;
++ if ((chipnum == BCM4359_CHIP_ID) || (chipnum == BCM43596_CHIP_ID)) {
++ /* WAR required */
++ need_war = true;
++ }
++ }
++
++ if (wl_customer6_legacy_chip_check(cfg, ndev) || need_war) {
+ /* Few firmware branches have issues in bss iovar handling and
+ * that can't be changed since they are in production.
+ */
+@@ -4182,6 +4278,9 @@ wl_cfg80211_create_iface(struct wiphy *wiphy,
+ wl_if_event_info *event = NULL;
+ u8 addr[ETH_ALEN];
+ struct net_info *iter, *next;
++#ifdef WLMESH
++ u16 role = 0, mode = 0;
++#endif
+
+ WL_DBG(("Enter\n"));
+ if (!name) {
+@@ -4283,6 +4382,11 @@ wl_cfg80211_create_iface(struct wiphy *wiphy,
+ }
+
+ event = &cfg->if_event_info;
++#ifdef WLMESH
++ cfg80211_to_wl_iftype(iface_type, &role, &mode);
++ event->role = role;
++#endif
++
+ /*
+ * Since FW operation is successful,we can go ahead with the
+ * the host interface creation.
+@@ -4359,6 +4463,157 @@ exit:
+ }
+ #endif /* defined(WL_VIRTUAL_APSTA) || defined(DUAL_STA_STATIC_IF) */
+
++#ifdef WLMESH
++s32 wl_cfg80211_set_sae_password(struct net_device *dev, char* buf, int len)
++{
++ struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
++
++ sscanf(buf, "%s %d", cfg->sae_password, &cfg->sae_password_len);
++ return 0;
++}
++
++static s32 wl_cfg80211_join_mesh(
++ struct wiphy *wiphy, struct net_device *dev,
++ const struct mesh_config *conf,
++ const struct mesh_setup *setup)
++{
++ struct bcm_cfg80211 *cfg = wiphy_priv(wiphy);
++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0))
++ struct ieee80211_channel *chan = setup->chandef.chan;
++#elif (LINUX_VERSION_CODE >= KERNEL_VERSION (3, 6, 0))
++ struct ieee80211_channel *chan = setup->channel;
++#endif
++ u32 param[2] = {0, 0};
++ s32 err = 0;
++ u32 bw_cap = 0;
++ u32 beacon_interval = setup->beacon_interval;
++ u32 dtim_period = setup->dtim_period;
++ size_t join_params_size;
++ struct wl_join_params join_params;
++ chanspec_t chanspec = 0;
++
++ cfg->channel = ieee80211_frequency_to_channel(chan->center_freq);
++
++ if (wl_get_drv_status(cfg, CONNECTED, dev)) {
++ struct wlc_ssid *lssid = (struct wlc_ssid *)wl_read_prof(cfg, dev, WL_PROF_SSID);
++ u8 *bssid = (u8 *)wl_read_prof(cfg, dev, WL_PROF_BSSID);
++ u32 *channel = (u32 *)wl_read_prof(cfg, dev, WL_PROF_CHAN);
++ if ((memcmp(setup->mesh_id, lssid->SSID, lssid->SSID_len) == 0) &&
++ (*channel == cfg->channel)) {
++ WL_ERR(("MESH connection already existed to " MACDBG "\n",
++ MAC2STRDBG((u8 *)wl_read_prof(cfg, dev, WL_PROF_BSSID))));
++ return -EISCONN;
++ }
++ WL_ERR(("Previous connecton existed, please disconnect mesh %s (" MACDBG ") first\n",
++ lssid->SSID, MAC2STRDBG(bssid)));
++ return -EISCONN;
++ }
++
++ if (chan) {
++ if (chan->band == IEEE80211_BAND_5GHZ)
++ param[0] = WLC_BAND_5G;
++ else if (chan->band == IEEE80211_BAND_2GHZ)
++ param[0] = WLC_BAND_2G;
++ err = wldev_iovar_getint(dev, "bw_cap", param);
++ if (unlikely(err)) {
++ WL_ERR(("Get bw_cap Failed (%d)\n", err));
++ return err;
++ }
++ bw_cap = param[0];
++ chanspec = channel_to_chanspec(wiphy, dev, cfg->channel, bw_cap);
++ }
++
++ memset(&join_params, 0, sizeof(join_params));
++ memcpy((void *)join_params.ssid.SSID, (void *)setup->mesh_id,
++ setup->mesh_id_len);
++
++ join_params.ssid.SSID_len = htod32(setup->mesh_id_len);
++ join_params.params.chanspec_list[0] = chanspec;
++ join_params.params.chanspec_num = 1;
++ wldev_iovar_setint(dev, "chanspec", chanspec);
++ join_params_size = sizeof(join_params);
++
++ wldev_iovar_setint(dev, "wpa_auth", WPA_AUTH_DISABLED);
++ wldev_iovar_setint(dev, "wsec", 0);
++
++ if (cfg->sae_password_len > 0) {
++ wldev_iovar_setint(dev, "mesh_auth_proto", 1);
++ wldev_iovar_setint(dev, "wpa_auth", WPA2_AUTH_PSK);
++ wldev_iovar_setint(dev, "wsec", AES_ENABLED);
++ wldev_iovar_setint(dev, "mfp", WL_MFP_REQUIRED);
++ printf("%s: password=%s, len=%d\n", __FUNCTION__,
++ cfg->sae_password, cfg->sae_password_len);
++ wldev_iovar_setbuf(dev, "sae_password", cfg->sae_password, cfg->sae_password_len,
++ cfg->ioctl_buf, WLC_IOCTL_MAXLEN, NULL);
++ } else {
++ wldev_iovar_setint(dev, "mesh_auth_proto", 0);
++ wldev_iovar_setint(dev, "mfp", WL_MFP_NONE);
++ }
++
++ if (beacon_interval) {
++ if ((err = wldev_ioctl_set(dev, WLC_SET_BCNPRD,
++ &beacon_interval, sizeof(s32))) < 0) {
++ WL_ERR(("Beacon Interval Set Error, %d\n", err));
++ return err;
++ }
++ }
++
++ if (dtim_period) {
++ if ((err = wldev_ioctl_set(dev, WLC_SET_DTIMPRD,
++ &dtim_period, sizeof(s32))) < 0) {
++ WL_ERR(("DTIM Interval Set Error, %d\n", err));
++ return err;
++ }
++ }
++ wldev_iovar_setint(dev, "mpc", 0);
++
++ WL_ERR(("JOIN %s on channel %d with chanspec 0x%4x\n",
++ join_params.ssid.SSID, cfg->channel, chanspec));
++
++ err = wldev_ioctl_set(dev, WLC_SET_SSID, &join_params,
++ join_params_size);
++
++ if (unlikely(err)) {
++ WL_ERR(("Error (%d)\n", err));
++ return err;
++ }
++
++ wl_update_prof(cfg, dev, NULL, &join_params.ssid, WL_PROF_SSID);
++ wl_update_prof(cfg, dev, NULL, &cfg->channel, WL_PROF_CHAN);
++ return err;
++}
++
++
++static s32 wl_cfg80211_leave_mesh(
++ struct wiphy *wiphy, struct net_device *dev)
++{
++ struct bcm_cfg80211 *cfg = wiphy_priv(wiphy);
++ s32 err = 0;
++ scb_val_t scbval;
++ u8 *curbssid;
++
++ RETURN_EIO_IF_NOT_UP(cfg);
++ wl_link_down(cfg);
++
++ WL_ERR(("Leave MESH\n"));
++ curbssid = wl_read_prof(cfg, dev, WL_PROF_BSSID);
++ wl_set_drv_status(cfg, DISCONNECTING, dev);
++ scbval.val = 0;
++ memcpy(&scbval.ea, curbssid, ETHER_ADDR_LEN);
++ err = wldev_ioctl_set(dev, WLC_DISASSOC, &scbval,
++ sizeof(scb_val_t));
++ if (unlikely(err)) {
++ wl_clr_drv_status(cfg, DISCONNECTING, dev);
++ WL_ERR(("error(%d)\n", err));
++ return err;
++ }
++ memset(cfg->sae_password, 0, SAE_MAX_PASSWD_LEN);
++ cfg->sae_password_len = 0;
++
++ return err;
++}
++#endif /* WLMESH */
++
+ static s32
+ wl_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_ibss_params *params)
+@@ -4824,7 +5079,7 @@ wl_cfg80211_set_mfp(struct bcm_cfg80211 *cfg,
+ /* 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 {
+@@ -5123,6 +5378,9 @@ wl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
+ WL_DBG(("In\n"));
+ BCM_REFERENCE(dhdp);
+
++#ifdef WLMESH
++ wl_config_ifmode(cfg, dev, dev->ieee80211_ptr->iftype);
++#endif
+ #if defined(SUPPORT_RANDOM_MAC_SCAN)
+ wl_cfg80211_set_random_mac(dev, FALSE);
+ #endif /* SUPPORT_RANDOM_MAC_SCAN */
+@@ -5181,7 +5439,11 @@ wl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
+ * A start scan occuring during connect is unlikely
+ */
+ if (cfg->sched_scan_req) {
++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0))
++ wl_cfg80211_sched_scan_stop(wiphy, bcmcfg_to_prmry_ndev(cfg), 0);
++#else
+ wl_cfg80211_sched_scan_stop(wiphy, bcmcfg_to_prmry_ndev(cfg));
++#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */
+ }
+ #endif
+ #if defined(ESCAN_RESULT_PATCH)
+@@ -5391,7 +5653,7 @@ wl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
+ DHD_DISABLE_RUNTIME_PM((dhd_pub_t *)cfg->pub);
+ #endif /* BCMDONGLEHOST && CUSTOMER_HW2 */
+ #ifdef WL_EXT_IAPSTA
+- wl_android_ext_iapsta_disconnect_sta(dev, cfg->channel);
++ wl_ext_iapsta_disconnect_sta(dev, cfg->channel);
+ #endif
+ err = wldev_iovar_setbuf_bsscfg(dev, "join", ext_join_params, join_params_size,
+ cfg->ioctl_buf, WLC_IOCTL_MAXLEN, bssidx, &cfg->ioctl_buf_sync);
+@@ -5783,9 +6045,18 @@ wl_cfg80211_interface_create(struct net_device *dev, char *name)
+ {
+ struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
+ bcm_struct_cfgdev *new_cfgdev;
++ 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;
++ }
+
+ new_cfgdev = wl_cfg80211_create_iface(cfg->wdev->wiphy,
+- NL80211_IFTYPE_STATION, NULL, name);
++ iface_type, NULL, ifname);
+ if (!new_cfgdev) {
+ return BCME_ERROR;
+ }
+@@ -6116,14 +6387,6 @@ wl_cfg80211_config_default_mgmt_key(struct wiphy *wiphy,
+ #endif /* MFP */
+ }
+
+-#if defined(RSSIAVG)
+-static wl_rssi_cache_ctrl_t g_rssi_cache_ctrl;
+-static wl_rssi_cache_ctrl_t g_connected_rssi_cache_ctrl;
+-#endif
+-#if defined(BSSCACHE)
+-static wl_bss_cache_ctrl_t g_bss_cache_ctrl;
+-#endif
+-
+ static s32
+ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0))
+ wl_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev,
+@@ -6281,13 +6544,13 @@ wl_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev,
+ }
+ rssi = dtoh32(scb_val.val);
+ #if defined(RSSIAVG)
+- err = wl_update_connected_rssi_cache(dev, &g_connected_rssi_cache_ctrl, &rssi);
++ err = wl_update_connected_rssi_cache(dev, &cfg->g_connected_rssi_cache_ctrl, &rssi);
+ if (err) {
+ WL_ERR(("Could not get rssi (%d)\n", err));
+ goto get_station_err;
+ }
+- wl_delete_dirty_rssi_cache(&g_connected_rssi_cache_ctrl);
+- wl_reset_rssi_cache(&g_connected_rssi_cache_ctrl);
++ wl_delete_dirty_rssi_cache(&cfg->g_connected_rssi_cache_ctrl);
++ wl_reset_rssi_cache(&cfg->g_connected_rssi_cache_ctrl);
+ #endif
+ #if defined(RSSIOFFSET)
+ rssi = wl_update_rssi_offset(dev, rssi);
+@@ -7788,6 +8051,9 @@ wl_cfg80211_set_channel(struct wiphy *wiphy, struct net_device *dev,
+
+ dev = ndev_to_wlc_ndev(dev, cfg);
+ _chan = ieee80211_frequency_to_channel(chan->center_freq);
++#ifdef WL_EXT_IAPSTA
++ _chan = wl_ext_iapsta_disconnect_sta(dev, _chan);
++#endif
+ printf("%s: netdev_ifidx(%d), chan_type(%d) target channel(%d) \n",
+ __FUNCTION__, dev->ifindex, channel_type, _chan);
+
+@@ -8824,6 +9090,9 @@ wl_cfg80211_bcn_bringup_ap(
+ s32 join_params_size = 0;
+ s32 ap = 1;
+ s32 wsec;
++#ifdef WLMESH
++ bool retried = false;
++#endif
+ #ifdef SOFTAP_UAPSD_OFF
+ uint32 wme_apsd = 0;
+ #endif /* SOFTAP_UAPSD_OFF */
+@@ -8960,6 +9229,9 @@ wl_cfg80211_bcn_bringup_ap(
+ }
+ #endif /* MFP */
+
++#ifdef WLMESH
++ssid_retry:
++#endif
+ memset(&join_params, 0, sizeof(join_params));
+ /* join parameters starts with ssid */
+ join_params_size = sizeof(join_params.ssid);
+@@ -8992,6 +9264,13 @@ wl_cfg80211_bcn_bringup_ap(
+ timeout = wait_event_interruptible_timeout(cfg->netif_change_event,
+ wl_get_drv_status(cfg, AP_CREATED, dev), msecs_to_jiffies(MAX_AP_LINK_WAIT_TIME));
+ if (timeout <= 0 || !wl_get_drv_status(cfg, AP_CREATED, dev)) {
++#ifdef WLMESH
++ if (!retried) {
++ retried = true;
++ WL_ERR(("Link up didn't come for AP interface. Try to set ssid again to recover it! \n"));
++ goto ssid_retry;
++ }
++#endif
+ WL_ERR(("Link up didn't come for AP interface. AP/GO creation failed! \n"));
+ if (timeout == -ERESTARTSYS) {
+ WL_ERR(("waitqueue was interrupted by a signal, returns -ERESTARTSYS\n"));
+@@ -9275,7 +9554,9 @@ wl_cfg80211_del_station(
+ } else {
+ #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)) */
+ #endif /* CUSTOM_BLOCK_DEAUTH_AT_EAP_FAILURE */
++#ifndef BCMDBUS
+ dhd_wait_pend8021x(dev);
++#endif /* !BCMDBUS */
+ scb_val.val = DOT11_RC_DEAUTH_LEAVING;
+ err = wldev_ioctl_set(dev, WLC_SCB_DEAUTHENTICATE_FOR_REASON, &scb_val,
+ sizeof(scb_val_t));
+@@ -9403,6 +9684,10 @@ wl_cfg80211_start_ap(
+ s32 bssidx = 0;
+ u32 dev_role = 0;
+ dhd_pub_t *dhd = (dhd_pub_t *)(cfg->pub);
++#ifdef WLMESH
++ struct wl_join_params join_params;
++ s32 join_params_size = 0;
++#endif
+
+ WL_DBG(("Enter \n"));
+
+@@ -9503,6 +9788,35 @@ wl_cfg80211_start_ap(
+ // goto fail;
+ }
+
++#ifdef WLMESH
++ OSL_SLEEP(1000);
++ if ((dev_role == NL80211_IFTYPE_P2P_GO) || (dev_role == NL80211_IFTYPE_AP)) {
++ memset(&join_params, 0, sizeof(join_params));
++ /* join parameters starts with ssid */
++ join_params_size = sizeof(join_params.ssid);
++ if (dev_role == NL80211_IFTYPE_P2P_GO) {
++ join_params.ssid.SSID_len = min(cfg->p2p->ssid.SSID_len,
++ (uint32)DOT11_MAX_SSID_LEN);
++ memcpy(join_params.ssid.SSID, cfg->p2p->ssid.SSID,
++ join_params.ssid.SSID_len);
++ } else if (dev_role == NL80211_IFTYPE_AP) {
++ join_params.ssid.SSID_len = min(cfg->hostapd_ssid.SSID_len,
++ (uint32)DOT11_MAX_SSID_LEN);
++ memcpy(join_params.ssid.SSID, cfg->hostapd_ssid.SSID,
++ join_params.ssid.SSID_len);
++ }
++ join_params.ssid.SSID_len = htod32(join_params.ssid.SSID_len);
++ /* create softap */
++ if ((err = wldev_ioctl_set(dev, WLC_SET_SSID, &join_params,
++ join_params_size)) != 0) {
++ WL_ERR(("SoftAP/GO set ssid failed! \n"));
++ goto fail;
++ } else {
++ WL_DBG((" SoftAP SSID \"%s\" \n", join_params.ssid.SSID));
++ }
++ }
++#endif
++
+ WL_DBG(("** AP/GO Created **\n"));
+
+ #ifdef WL_CFG80211_ACL
+@@ -10126,7 +10440,11 @@ exit:
+ }
+
+ static int
+-wl_cfg80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev)
++wl_cfg80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev
++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0))
++ , u64 reqid
++#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */
++)
+ {
+ struct bcm_cfg80211 *cfg = wiphy_priv(wiphy);
+ dhd_pub_t *dhdp = (dhd_pub_t *)(cfg->pub);
+@@ -10412,6 +10730,10 @@ static struct cfg80211_ops wl_cfg80211_ops = {
+ .change_station = wl_cfg80211_change_station,
+ .mgmt_tx_cancel_wait = wl_cfg80211_mgmt_tx_cancel_wait,
+ #endif /* WL_SUPPORT_BACKPORTED_KPATCHES || KERNEL_VERSION >= (3,2,0) */
++#ifdef WLMESH
++ .join_mesh = wl_cfg80211_join_mesh,
++ .leave_mesh = wl_cfg80211_leave_mesh,
++#endif /* WLMESH */
+ #if (LINUX_VERSION_CODE > KERNEL_VERSION(3, 2, 0))
+ .tdls_mgmt = wl_cfg80211_tdls_mgmt,
+ .tdls_oper = wl_cfg80211_tdls_oper,
+@@ -10440,6 +10762,10 @@ s32 wl_mode_to_nl80211_iftype(s32 mode)
+ return NL80211_IFTYPE_ADHOC;
+ case WL_MODE_AP:
+ return NL80211_IFTYPE_AP;
++#ifdef WLMESH
++ case WL_MODE_MESH:
++ return NL80211_IFTYPE_MESH_POINT;
++#endif
+ default:
+ return NL80211_IFTYPE_UNSPECIFIED;
+ }
+@@ -10552,8 +10878,15 @@ static s32 wl_setup_wiphy(struct wireless_dev *wdev, struct device *sdiofunc_dev
+ wdev->wiphy->max_sched_scan_ssids = MAX_PFN_LIST_COUNT;
+ wdev->wiphy->max_match_sets = MAX_PFN_LIST_COUNT;
+ wdev->wiphy->max_sched_scan_ie_len = WL_SCAN_IE_LEN_MAX;
++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0))
++ wdev->wiphy->max_sched_scan_plan_interval = PNO_SCAN_MAX_FW_SEC;
++#else
+ wdev->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
++#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */
+ #endif /* WL_SCHED_SCAN */
++#ifdef WLMESH
++ wdev->wiphy->flags |= WIPHY_FLAG_MESH_AUTH;
++#endif
+ wdev->wiphy->interface_modes =
+ BIT(NL80211_IFTYPE_STATION)
+ | BIT(NL80211_IFTYPE_ADHOC)
+@@ -10567,13 +10900,18 @@ static s32 wl_setup_wiphy(struct wireless_dev *wdev, struct device *sdiofunc_dev
+ #if defined(WL_CFG80211_P2P_DEV_IF)
+ | BIT(NL80211_IFTYPE_P2P_DEVICE)
+ #endif /* WL_CFG80211_P2P_DEV_IF */
++#ifdef WLMESH
++ | BIT(NL80211_IFTYPE_MESH_POINT)
++#endif /* WLMESH */
+ | BIT(NL80211_IFTYPE_AP);
+
+ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)) && \
+ (defined(WL_IFACE_COMB_NUM_CHANNELS) || defined(WL_CFG80211_P2P_DEV_IF))
+ WL_DBG(("Setting interface combinations for common mode\n"));
++#ifndef BCMDBUS
+ if (dhd->conf->num_different_channels >= 0)
+ common_iface_combinations[0].num_different_channels = dhd->conf->num_different_channels;
++#endif /* !BCMDBUS */
+ wdev->wiphy->iface_combinations = common_iface_combinations;
+ wdev->wiphy->n_iface_combinations =
+ ARRAY_SIZE(common_iface_combinations);
+@@ -10761,8 +11099,8 @@ static s32 wl_inform_bss(struct bcm_cfg80211 *cfg)
+ struct wl_bss_info *bi = NULL; /* must be initialized */
+ s32 err = 0;
+ s32 i;
+-#if defined(RSSIAVG)
+ struct net_device *ndev = bcmcfg_to_prmry_ndev(cfg);
++#if defined(RSSIAVG)
+ int rssi;
+ #endif
+ #if defined(BSSCACHE)
+@@ -10774,18 +11112,18 @@ static s32 wl_inform_bss(struct bcm_cfg80211 *cfg)
+ /* Free cache in p2p scanning*/
+ if (p2p_is_on(cfg) && p2p_scan(cfg)) {
+ #if defined(RSSIAVG)
+- wl_free_rssi_cache(&g_rssi_cache_ctrl);
++ wl_free_rssi_cache(&cfg->g_rssi_cache_ctrl);
+ #endif
+ #if defined(BSSCACHE)
+- wl_free_bss_cache(&g_bss_cache_ctrl);
++ wl_free_bss_cache(&cfg->g_bss_cache_ctrl);
+ #endif
+ }
+
+ /* Delete disconnected cache */
+ #if defined(BSSCACHE)
+- wl_delete_disconnected_bss_cache(&g_bss_cache_ctrl, (u8*)&cfg->disconnected_bssid);
++ wl_delete_disconnected_bss_cache(&cfg->g_bss_cache_ctrl, (u8*)&cfg->disconnected_bssid);
+ #if defined(RSSIAVG)
+- wl_delete_disconnected_rssi_cache(&g_rssi_cache_ctrl, (u8*)&cfg->disconnected_bssid);
++ wl_delete_disconnected_rssi_cache(&cfg->g_rssi_cache_ctrl, (u8*)&cfg->disconnected_bssid);
+ #endif
+ if (cfg->p2p_disconnected == 0)
+ memset(&cfg->disconnected_bssid, 0, ETHER_ADDR_LEN);
+@@ -10793,43 +11131,45 @@ static s32 wl_inform_bss(struct bcm_cfg80211 *cfg)
+
+ /* Update cache */
+ #if defined(RSSIAVG)
+- wl_update_rssi_cache(&g_rssi_cache_ctrl, bss_list);
++ wl_update_rssi_cache(&cfg->g_rssi_cache_ctrl, bss_list);
+ if (!in_atomic())
+- wl_update_connected_rssi_cache(ndev, &g_rssi_cache_ctrl, &rssi);
++ wl_update_connected_rssi_cache(ndev, &cfg->g_rssi_cache_ctrl, &rssi);
+ #endif
+ #if defined(BSSCACHE)
+- wl_update_bss_cache(&g_bss_cache_ctrl,
++ wl_update_bss_cache(&cfg->g_bss_cache_ctrl,
+ #if defined(RSSIAVG)
+- &g_rssi_cache_ctrl,
++ &cfg->g_rssi_cache_ctrl,
+ #endif
+ bss_list);
+ #endif
+
+ /* delete dirty cache */
+ #if defined(RSSIAVG)
+- wl_delete_dirty_rssi_cache(&g_rssi_cache_ctrl);
+- wl_reset_rssi_cache(&g_rssi_cache_ctrl);
++ wl_delete_dirty_rssi_cache(&cfg->g_rssi_cache_ctrl);
++ wl_reset_rssi_cache(&cfg->g_rssi_cache_ctrl);
+ #endif
+ #if defined(BSSCACHE)
+- wl_delete_dirty_bss_cache(&g_bss_cache_ctrl);
+- wl_reset_bss_cache(&g_bss_cache_ctrl);
++ wl_delete_dirty_bss_cache(&cfg->g_bss_cache_ctrl);
++ wl_reset_bss_cache(&cfg->g_bss_cache_ctrl);
+ #endif
+
+ #if defined(BSSCACHE)
+ if (cfg->p2p_disconnected > 0) {
+ // terence 20130703: Fix for wrong group_capab (timing issue)
+- wl_delete_disconnected_bss_cache(&g_bss_cache_ctrl, (u8*)&cfg->disconnected_bssid);
++ wl_delete_disconnected_bss_cache(&cfg->g_bss_cache_ctrl, (u8*)&cfg->disconnected_bssid);
+ #if defined(RSSIAVG)
+- wl_delete_disconnected_rssi_cache(&g_rssi_cache_ctrl, (u8*)&cfg->disconnected_bssid);
++ wl_delete_disconnected_rssi_cache(&cfg->g_rssi_cache_ctrl, (u8*)&cfg->disconnected_bssid);
+ #endif
+ }
+ WL_SCAN(("scanned AP count (%d)\n", bss_list->count));
+- node = g_bss_cache_ctrl.m_cache_head;
++ node = cfg->g_bss_cache_ctrl.m_cache_head;
+ for (i=0; node && iresults.bss_info;
+ err = wl_inform_single_bss(cfg, bi, false);
+ node = node->next;
+ }
++ if (cfg->autochannel)
++ wl_ext_get_best_channel(ndev, &cfg->g_bss_cache_ctrl, &cfg->best_2g_ch, &cfg->best_5g_ch);
+ #else
+ WL_SCAN(("scanned AP count (%d)\n", bss_list->count));
+ preempt_disable();
+@@ -10840,6 +11180,8 @@ static s32 wl_inform_bss(struct bcm_cfg80211 *cfg)
+ err = wl_inform_single_bss(cfg, bi, false);
+ }
+ preempt_enable();
++ if (cfg->autochannel)
++ wl_ext_get_best_channel(ndev, bss_list, &cfg->best_2g_ch, &cfg->best_5g_ch);
+ #endif
+
+ if (cfg->p2p_disconnected > 0) {
+@@ -10874,6 +11216,7 @@ static s32 wl_inform_single_bss(struct bcm_cfg80211 *cfg, struct wl_bss_info *bi
+ u32 freq;
+ s32 err = 0;
+ gfp_t aflags;
++ chanspec_t chanspec;
+
+ if (unlikely(dtoh32(bi->length) > WL_BSS_INFO_MAX)) {
+ WL_DBG(("Beacon is larger than buffer. Discarding\n"));
+@@ -10887,8 +11230,8 @@ static s32 wl_inform_single_bss(struct bcm_cfg80211 *cfg, struct wl_bss_info *bi
+ return -ENOMEM;
+ }
+ mgmt = (struct ieee80211_mgmt *)notif_bss_info->frame_buf;
+- notif_bss_info->channel =
+- wf_chspec_ctlchan(wl_chspec_driver_to_host(bi->chanspec));
++ chanspec = wl_chspec_driver_to_host(bi->chanspec);
++ notif_bss_info->channel = wf_chspec_ctlchan(chanspec);
+
+ if (notif_bss_info->channel <= CH_MAX_2G_CHANNEL)
+ band = wiphy->bands[IEEE80211_BAND_2GHZ];
+@@ -10901,7 +11244,7 @@ static s32 wl_inform_single_bss(struct bcm_cfg80211 *cfg, struct wl_bss_info *bi
+ }
+ notif_bss_info->rssi = dtoh16(bi->RSSI);
+ #if defined(RSSIAVG)
+- notif_bss_info->rssi = wl_get_avg_rssi(&g_rssi_cache_ctrl, &bi->BSSID);
++ notif_bss_info->rssi = wl_get_avg_rssi(&cfg->g_rssi_cache_ctrl, &bi->BSSID);
+ if (notif_bss_info->rssi == RSSI_MINVAL)
+ notif_bss_info->rssi = MIN(dtoh16(bi->RSSI), RSSI_MAXVAL);
+ #endif
+@@ -10943,8 +11286,12 @@ static s32 wl_inform_single_bss(struct bcm_cfg80211 *cfg, struct wl_bss_info *bi
+ return -EINVAL;
+ }
+ channel = ieee80211_get_channel(wiphy, freq);
+- WL_SCAN(("BSSID %pM, channel %2d, rssi %3d, capa 0x04%x, mgmt_type %d, "
+- "frame_len %d, SSID \"%s\"\n", &bi->BSSID, notif_bss_info->channel,
++ WL_SCAN(("BSSID %pM, channel %2d(%2d %sMHz), rssi %3d, capa 0x04%x, mgmt_type %d, "
++ "frame_len %d, SSID \"%s\"\n",
++ &bi->BSSID, notif_bss_info->channel, CHSPEC_CHANNEL(chanspec),
++ CHSPEC_IS20(chanspec)?"20":
++ CHSPEC_IS40(chanspec)?"40":
++ CHSPEC_IS80(chanspec)?"80":"160",
+ notif_bss_info->rssi, mgmt->u.beacon.capab_info, mgmt_type,
+ notif_bss_info->frame_len, bi->SSID));
+ if (unlikely(!channel)) {
+@@ -11209,6 +11556,9 @@ wl_notify_connect_status_ap(struct bcm_cfg80211 *cfg, struct net_device *ndev,
+ printf("%s: ** AP/GO Link up event **\n", __FUNCTION__);
+ wl_set_drv_status(cfg, AP_CREATED, ndev);
+ wake_up_interruptible(&cfg->netif_change_event);
++ if (!memcmp(ndev->name, WL_P2P_INTERFACE_PREFIX, strlen(WL_P2P_INTERFACE_PREFIX))) {
++ dhd_conf_set_mchan_bw(cfg->pub, WL_P2P_IF_GO, -1);
++ }
+ return 0;
+ }
+ }
+@@ -11863,6 +12213,9 @@ wl_notify_connect_status(struct bcm_cfg80211 *cfg, bcm_struct_cfgdev *cfgdev,
+ wl_update_prof(cfg, ndev, e, &act, WL_PROF_ACT);
+ wl_update_prof(cfg, ndev, NULL, (const void *)&e->addr, WL_PROF_BSSID);
+ dhd_conf_set_wme(cfg->pub, 0);
++ if (!memcmp(ndev->name, WL_P2P_INTERFACE_PREFIX, strlen(WL_P2P_INTERFACE_PREFIX))) {
++ dhd_conf_set_mchan_bw(cfg->pub, WL_P2P_IF_CLIENT, -1);
++ }
+ } else if (WL_IS_LINKDOWN(cfg, e, data) ||
+ ((event == WLC_E_SET_SSID) &&
+ (ntoh32(e->status) != WLC_E_STATUS_SUCCESS) &&
+@@ -11907,6 +12260,27 @@ wl_notify_connect_status(struct bcm_cfg80211 *cfg, bcm_struct_cfgdev *cfgdev,
+ wl_get_bss_info(cfg, ndev, (u8*)(&e->addr));
+ }
+ #endif /* DHD_ENABLE_BIGDATA_LOGGING */
++ if (wl_get_drv_status(cfg, CONNECTED, ndev)) {
++ u8 *curbssid = wl_read_prof(cfg, ndev, WL_PROF_BSSID);
++ if (memcmp(curbssid, &e->addr, ETHER_ADDR_LEN) != 0) {
++ bool fw_assoc_state = TRUE;
++ dhd_pub_t *dhd = (dhd_pub_t *)cfg->pub;
++ fw_assoc_state = dhd_is_associated(dhd, e->ifidx, &err);
++ if (!fw_assoc_state) {
++ WL_ERR(("Event sends up even different BSSID"
++ " cur: " MACDBG " event: " MACDBG"\n",
++ MAC2STRDBG(curbssid),
++ MAC2STRDBG((const u8*)(&e->addr))));
++ } else {
++ WL_ERR(("BSSID of event is not the connected BSSID"
++ "(ignore it) cur: " MACDBG
++ " event: " MACDBG"\n",
++ MAC2STRDBG(curbssid),
++ MAC2STRDBG((const u8*)(&e->addr))));
++ return 0;
++ }
++ }
++ }
+ /* Explicitly calling unlink to remove BSS in CFG */
+ wiphy = bcmcfg_to_wiphy(cfg);
+ ssid = (struct wlc_ssid *)wl_read_prof(cfg, ndev, WL_PROF_SSID);
+@@ -12096,8 +12470,10 @@ wl_notify_connect_status(struct bcm_cfg80211 *cfg, bcm_struct_cfgdev *cfgdev,
+ }
+ DHD_ENABLE_RUNTIME_PM((dhd_pub_t *)cfg->pub);
+ }
+- else {
+- WL_ERR(("Invalid ndev status %d\n", wl_get_mode_by_netdev(cfg, ndev)));
++ else {
++ printf("wl_notify_connect_status : Invalid %s mode %d event %d status %d\n",
++ ndev->name, wl_get_mode_by_netdev(cfg, ndev), ntoh32(e->event_type),
++ ntoh32(e->status));
+ }
+ return err;
+ }
+@@ -12694,6 +13070,9 @@ wl_bss_roaming_done(struct bcm_cfg80211 *cfg, struct net_device *ndev,
+ #if defined(WLADPS_SEAK_AP_WAR) || defined(WBTEXT)
+ dhd_pub_t *dhdp = (dhd_pub_t *)(cfg->pub);
+ #endif /* WLADPS_SEAK_AP_WAR || WBTEXT */
++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0))
++ struct cfg80211_roam_info roam_info = {};
++#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */
+
+ #ifdef WLADPS_SEAK_AP_WAR
+ BCM_REFERENCE(dhdp);
+@@ -12758,6 +13137,18 @@ wl_bss_roaming_done(struct bcm_cfg80211 *cfg, struct net_device *ndev,
+ MAC2STRDBG((const u8*)(&e->addr)), *channel);
+ dhd_conf_set_wme(cfg->pub, 0);
+
++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0))
++ roam_info.channel = notify_channel;
++ roam_info.bssid = curbssid;
++ roam_info.req_ie = conn_info->req_ie;
++ roam_info.req_ie_len = conn_info->req_ie_len;
++ roam_info.resp_ie = conn_info->resp_ie;
++ roam_info.resp_ie_len = conn_info->resp_ie_len;
++#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */
++
++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0))
++ cfg80211_roamed(ndev, &roam_info, GFP_KERNEL);
++#else
+ cfg80211_roamed(ndev,
+ #if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39))
+ notify_channel,
+@@ -12765,6 +13156,7 @@ wl_bss_roaming_done(struct bcm_cfg80211 *cfg, struct net_device *ndev,
+ curbssid,
+ conn_info->req_ie, conn_info->req_ie_len,
+ conn_info->resp_ie, conn_info->resp_ie_len, GFP_KERNEL);
++#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */
+ WL_DBG(("Report roaming result\n"));
+
+ memcpy(&cfg->last_roamed_addr, &e->addr, ETHER_ADDR_LEN);
+@@ -14077,10 +14469,20 @@ void wl_terminate_event_handler(struct net_device *dev)
+ }
+ }
+
+-static void wl_scan_timeout(unsigned long data)
++static void wl_scan_timeout(
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
++ struct timer_list *t
++#else
++ unsigned long data
++#endif
++)
+ {
+ wl_event_msg_t msg;
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
++ struct bcm_cfg80211 *cfg = from_timer(cfg, t, scan_timeout);
++#else
+ struct bcm_cfg80211 *cfg = (struct bcm_cfg80211 *)data;
++#endif
+ struct wireless_dev *wdev = NULL;
+ struct net_device *ndev = NULL;
+ struct wl_scan_results *bss_list;
+@@ -14161,9 +14563,19 @@ static void wl_del_roam_timeout(struct bcm_cfg80211 *cfg)
+
+ }
+
+-static void wl_roam_timeout(unsigned long data)
++static void wl_roam_timeout(
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
++ struct timer_list *t
++#else
++ unsigned long data
++#endif
++)
+ {
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
++ struct bcm_cfg80211 *cfg = from_timer(cfg, t, roam_timeout);
++#else
+ struct bcm_cfg80211 *cfg = (struct bcm_cfg80211 *)data;
++#endif
+ dhd_pub_t *dhdp = (dhd_pub_t *)(cfg->pub);
+
+ WL_ERR(("roam timer expired\n"));
+@@ -14371,8 +14783,13 @@ static s32 wl_notify_escan_complete(struct bcm_cfg80211 *cfg,
+ #ifdef WL_SCHED_SCAN
+ if (cfg->sched_scan_req && !cfg->scan_request) {
+ WL_PNO((">>> REPORTING SCHED SCAN RESULTS \n"));
+- if (!aborted)
++ if (!aborted) {
++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0))
++ cfg80211_sched_scan_results(cfg->sched_scan_req->wiphy, 0);
++#else
+ cfg80211_sched_scan_results(cfg->sched_scan_req->wiphy);
++#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */
++ }
+
+ DBG_EVENT_LOG(dhdp, WIFI_EVENT_DRIVER_PNO_SCAN_COMPLETE);
+ cfg->sched_scan_running = FALSE;
+@@ -15197,9 +15614,13 @@ static s32 wl_init_scan(struct bcm_cfg80211 *cfg)
+ wl_escan_init_sync_id(cfg);
+
+ /* Init scan_timeout timer */
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
++ timer_setup(&cfg->scan_timeout, wl_scan_timeout, 0);
++#else
+ init_timer(&cfg->scan_timeout);
+ cfg->scan_timeout.data = (unsigned long) cfg;
+ cfg->scan_timeout.function = wl_scan_timeout;
++#endif
+
+ return err;
+ }
+@@ -15210,9 +15631,13 @@ static s32 wl_init_roam_timeout(struct bcm_cfg80211 *cfg)
+ int err = 0;
+
+ /* Init roam timer */
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
++ timer_setup(&cfg->roam_timeout, wl_roam_timeout, 0);
++#else
+ init_timer(&cfg->roam_timeout);
+ cfg->roam_timeout.data = (unsigned long) cfg;
+ cfg->roam_timeout.function = wl_roam_timeout;
++#endif
+
+ return err;
+ }
+@@ -15234,9 +15659,9 @@ static s32 wl_init_priv(struct bcm_cfg80211 *cfg)
+ cfg->active_scan = true;
+ cfg->rf_blocked = false;
+ cfg->vsdb_mode = false;
+-#if defined(BCMSDIO)
++#if defined(BCMSDIO) || defined(BCMDBUS)
+ cfg->wlfc_on = false;
+-#endif
++#endif /* BCMSDIO || BCMDBUS */
+ cfg->roam_flags |= WL_ROAM_OFF_ON_CONCURRENT;
+ cfg->disable_roam_event = false;
+ /* register interested state */
+@@ -15433,7 +15858,11 @@ s32 wl_cfg80211_attach(struct net_device *ndev, void *context)
+ kfree(wdev);
+ return -ENOMEM;
+ }
++#ifdef WLMESH
++ wdev->iftype = wl_mode_to_nl80211_iftype(WL_MODE_MESH);
++#else
+ wdev->iftype = wl_mode_to_nl80211_iftype(WL_MODE_BSS);
++#endif
+ cfg = wiphy_priv(wdev->wiphy);
+ cfg->wdev = wdev;
+ cfg->pub = context;
+@@ -15560,14 +15989,14 @@ void wl_cfg80211_detach(struct bcm_cfg80211 *cfg)
+ wl_cfg80211_clear_mgmt_vndr_ies(cfg);
+ wl_deinit_priv(cfg);
+ wl_cfg80211_clear_parent_dev();
+- wl_free_wdev(cfg);
+ #if defined(RSSIAVG)
+- wl_free_rssi_cache(&g_rssi_cache_ctrl);
+- wl_free_rssi_cache(&g_connected_rssi_cache_ctrl);
++ wl_free_rssi_cache(&cfg->g_rssi_cache_ctrl);
++ wl_free_rssi_cache(&cfg->g_connected_rssi_cache_ctrl);
+ #endif
+ #if defined(BSSCACHE)
+- wl_release_bss_cache_ctrl(&g_bss_cache_ctrl);
++ wl_release_bss_cache_ctrl(&cfg->g_bss_cache_ctrl);
+ #endif
++ wl_free_wdev(cfg);
+ /* PLEASE do NOT call any function after wl_free_wdev, the driver's private
+ * structure "cfg", which is the private part of wiphy, has been freed in
+ * wl_free_wdev !!!!!!!!!!!
+@@ -15761,6 +16190,12 @@ static s32 wl_config_ifmode(struct bcm_cfg80211 *cfg, struct net_device *ndev, s
+ mode = WL_MODE_BSS;
+ infra = 1;
+ break;
++#ifdef WLMESH
++ case NL80211_IFTYPE_MESH_POINT:
++ mode = WL_MODE_MESH;
++ infra = WL_BSSTYPE_MESH;
++ break;
++#endif /* WLMESH */
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_P2P_GO:
+ mode = WL_MODE_AP;
+@@ -16375,9 +16810,9 @@ static s32 __wl_cfg80211_down(struct bcm_cfg80211 *cfg)
+ struct net_device *p2p_net = cfg->p2p_net;
+ #endif
+ #ifdef PROP_TXSTATUS_VSDB
+-#if defined(BCMSDIO)
++#if defined(BCMSDIO) || defined(BCMDBUS)
+ dhd_pub_t *dhd = (dhd_pub_t *)(cfg->pub);
+-#endif
++#endif /* BCMSDIO || BCMDBUS */
+ #endif /* PROP_TXSTATUS_VSDB */
+ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0))
+ struct cfg80211_scan_info info;
+@@ -16405,7 +16840,7 @@ static s32 __wl_cfg80211_down(struct bcm_cfg80211 *cfg)
+ if (cfg->p2p_supported) {
+ wl_clr_p2p_status(cfg, GO_NEG_PHASE);
+ #ifdef PROP_TXSTATUS_VSDB
+-#if defined(BCMSDIO)
++#if defined(BCMSDIO) || defined(BCMDBUS)
+ if (wl_cfgp2p_vif_created(cfg)) {
+ bool enabled = false;
+ dhd_wlfc_get_enable(dhd, &enabled);
+@@ -16415,7 +16850,7 @@ static s32 __wl_cfg80211_down(struct bcm_cfg80211 *cfg)
+ cfg->wlfc_on = false;
+ }
+ }
+-#endif
++#endif /* BCMSDIO || BCMDBUS */
+ #endif /* PROP_TXSTATUS_VSDB */
+ }
+
+@@ -16583,6 +17018,10 @@ s32 wl_cfg80211_up(struct net_device *net)
+ return err;
+ }
+ }
++#ifdef WLMESH
++ cfg->wdev->wiphy->features |= NL80211_FEATURE_USERSPACE_MPM;
++#endif /* WLMESH */
++
+ err = __wl_cfg80211_up(cfg);
+ if (unlikely(err))
+ WL_ERR(("__wl_cfg80211_up failed\n"));
+@@ -16658,10 +17097,10 @@ int wl_cfg80211_hang(struct net_device *dev, u16 reason)
+ CFG80211_DISCONNECTED(dev, reason, NULL, 0, false, GFP_KERNEL);
+ }
+ #if defined(RSSIAVG)
+- wl_free_rssi_cache(&g_rssi_cache_ctrl);
++ wl_free_rssi_cache(&cfg->g_rssi_cache_ctrl);
+ #endif
+ #if defined(BSSCACHE)
+- wl_free_bss_cache(&g_bss_cache_ctrl);
++ wl_free_bss_cache(&cfg->g_bss_cache_ctrl);
+ #endif
+ if (cfg != NULL) {
+ wl_link_down(cfg);
+@@ -16679,10 +17118,10 @@ s32 wl_cfg80211_down(struct net_device *dev)
+ return err;
+ mutex_lock(&cfg->usr_sync);
+ #if defined(RSSIAVG)
+- wl_free_rssi_cache(&g_rssi_cache_ctrl);
++ wl_free_rssi_cache(&cfg->g_rssi_cache_ctrl);
+ #endif
+ #if defined(BSSCACHE)
+- wl_free_bss_cache(&g_bss_cache_ctrl);
++ wl_free_bss_cache(&cfg->g_bss_cache_ctrl);
+ #endif
+ err = __wl_cfg80211_down(cfg);
+ mutex_unlock(&cfg->usr_sync);
+@@ -17489,13 +17928,14 @@ wl_cfg80211_get_best_channel(struct net_device *ndev, void *buf, int buflen,
+ ret = wldev_ioctl_get(ndev, WLC_GET_CHANNEL_SEL, &chosen, sizeof(chosen));
+ if ((ret == 0) && (dtoh32(chosen) != 0)) {
+ chip = dhd_conf_get_chip(dhd_get_pub(ndev));
+- if (chip != BCM43362_CHIP_ID && chip != BCM4330_CHIP_ID) {
++ if (chip != BCM43362_CHIP_ID && chip != BCM4330_CHIP_ID &&
++ chip != BCM43143_CHIP_ID) {
+ u32 chanspec = 0;
+ int ctl_chan;
+ chanspec = wl_chspec_driver_to_host(chosen);
+- printf("selected chanspec = 0x%x\n", chanspec);
++ WL_INFORM(("selected chanspec = 0x%x\n", chanspec));
+ ctl_chan = wf_chspec_ctlchan(chanspec);
+- printf("selected ctl_chan = %d\n", ctl_chan);
++ WL_INFORM(("selected ctl_chan = %d\n", ctl_chan));
+ *channel = (u16)(ctl_chan & 0x00FF);
+ } else
+ *channel = (u16)(chosen & 0x00FF);
+@@ -17597,8 +18037,10 @@ wl_cfg80211_get_best_channels(struct net_device *dev, char* cmd, int total_len)
+ // terence 20140120: fix for some chipsets only return 2.4GHz channel (4330b2/43341b0/4339a0)
+ band = band_cur==WLC_BAND_2G ? band_cur : WLC_BAND_5G;
+ ret = wldev_ioctl(dev, WLC_SET_BAND, &band, sizeof(band), true);
+- if (ret < 0)
++ if (ret < 0) {
+ WL_ERR(("WLC_SET_BAND error %d\n", ret));
++ goto done;
++ }
+
+ /* Best channel selection in 5GHz band. */
+ ret = wl_cfg80211_get_chanspecs_5g(ndev, (void *)buf, CHANSPEC_BUF_SIZE);
+@@ -21233,3 +21675,24 @@ wl_set_rssi_logging(struct net_device *dev, void *param)
+ return err;
+ }
+ #endif /* SUPPORT_RSSI_LOGGING */
++
++s32 wl_cfg80211_autochannel(struct net_device *dev, char* command, int total_len)
++{
++ struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
++ int ret = 0;
++ int bytes_written = -1;
++
++ sscanf(command, "%*s %d", &cfg->autochannel);
++
++ if (cfg->autochannel == 0) {
++ cfg->best_2g_ch = 0;
++ cfg->best_5g_ch = 0;
++ } else if (cfg->autochannel == 2) {
++ bytes_written = snprintf(command, total_len, "2g=%d 5g=%d",
++ cfg->best_2g_ch, cfg->best_5g_ch);
++ ANDROID_TRACE(("%s: command result is %s\n", __FUNCTION__, command));
++ ret = bytes_written;
++ }
++
++ return ret;
++}
+diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_cfg80211.h b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_cfg80211.h
+old mode 100755
+new mode 100644
+index 9d06534db7cd..395dfd5ae7f4
+--- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_cfg80211.h
++++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_cfg80211.h
+@@ -45,6 +45,7 @@
+ #include
+ #include
+ #include
++#include
+ struct wl_conf;
+ struct wl_iface;
+ struct bcm_cfg80211;
+@@ -205,6 +206,11 @@ do { \
+ #define IEEE80211_BAND_5GHZ NL80211_BAND_5GHZ
+ #define IEEE80211_NUM_BANDS NUM_NL80211_BANDS
+ #endif
++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0))
++#ifdef WLMESH
++#undef WLMESH
++#endif
++#endif
+
+ #define WL_SCAN_RETRY_MAX 3
+ #define WL_NUM_PMKIDS_MAX MAXPMKID
+@@ -339,7 +345,10 @@ enum wl_status {
+ enum wl_mode {
+ WL_MODE_BSS,
+ WL_MODE_IBSS,
+- WL_MODE_AP
++ WL_MODE_AP,
++#ifdef WLMESH
++ WL_MODE_MESH
++#endif
+ };
+
+ /* driver profile list */
+@@ -735,7 +744,7 @@ struct bcm_cfg80211 {
+ bool pwr_save;
+ bool roam_on; /* on/off switch for self-roaming */
+ bool scan_tried; /* indicates if first scan attempted */
+-#if defined(BCMSDIO) || defined(BCMPCIE)
++#if defined(BCMSDIO) || defined(BCMDBUS)
+ bool wlfc_on;
+ #endif
+ bool vsdb_mode;
+@@ -845,9 +854,23 @@ struct bcm_cfg80211 {
+
+ #ifdef STAT_REPORT
+ void *stat_report_info;
++#endif
++#ifdef WLMESH
++ char sae_password[SAE_MAX_PASSWD_LEN];
++ uint sae_password_len;
++#endif /* WLMESH */
++#if defined(RSSIAVG)
++ wl_rssi_cache_ctrl_t g_rssi_cache_ctrl;
++ wl_rssi_cache_ctrl_t g_connected_rssi_cache_ctrl;
++#endif
++#if defined(BSSCACHE)
++ wl_bss_cache_ctrl_t g_bss_cache_ctrl;
+ #endif
+ int p2p_disconnected; // terence 20130703: Fix for wrong group_capab (timing issue)
+ struct ether_addr disconnected_bssid;
++ int autochannel;
++ int best_2g_ch;
++ int best_5g_ch;
+ };
+
+ #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == \
+@@ -1462,6 +1485,9 @@ extern s32 wl_cfg80211_set_wps_p2p_ie(struct net_device *net, char *buf, int len
+ extern s32 wl_cfg80211_set_p2p_ps(struct net_device *net, char* buf, int len);
+ extern s32 wl_cfg80211_set_p2p_ecsa(struct net_device *net, char* buf, int len);
+ extern s32 wl_cfg80211_increase_p2p_bw(struct net_device *net, char* buf, int len);
++#ifdef WLMESH
++extern s32 wl_cfg80211_set_sae_password(struct net_device *net, char* buf, int len);
++#endif
+ #ifdef WL11ULB
+ extern s32 wl_cfg80211_set_ulb_mode(struct net_device *dev, int mode);
+ extern s32 wl_cfg80211_set_ulb_bw(struct net_device *dev,
+@@ -1652,4 +1678,5 @@ int wl_cfg80211_iface_count(struct net_device *dev);
+ struct net_device* wl_get_ap_netdev(struct bcm_cfg80211 *cfg, char *ifname);
+ struct net_device* wl_get_netdev_by_name(struct bcm_cfg80211 *cfg, char *ifname);
+ int wl_cfg80211_get_vndr_ouilist(struct bcm_cfg80211 *cfg, uint8 *buf, int max_cnt);
++s32 wl_cfg80211_autochannel(struct net_device *dev, char* command, int total_len);
+ #endif /* _wl_cfg80211_h_ */
+diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_cfg_btcoex.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_cfg_btcoex.c
+old mode 100755
+new mode 100644
+index 4fd40fd779fc..6d62b3a40f09
+--- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_cfg_btcoex.c
++++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_cfg_btcoex.c
+@@ -294,9 +294,19 @@ wl_cfg80211_bt_setflag(struct net_device *dev, bool set)
+ #endif
+ }
+
+-static void wl_cfg80211_bt_timerfunc(ulong data)
++static void wl_cfg80211_bt_timerfunc(
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
++ struct timer_list *t
++#else
++ unsigned long data
++#endif
++)
+ {
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
++ struct btcoex_info *bt_local = from_timer(bt_local, t, timer);
++#else
+ struct btcoex_info *bt_local = (struct btcoex_info *)data;
++#endif
+ WL_TRACE(("Enter\n"));
+ bt_local->timer_on = 0;
+ schedule_work(&bt_local->work);
+@@ -393,9 +403,13 @@ void* wl_cfg80211_btcoex_init(struct net_device *ndev)
+ btco_inf->ts_dhcp_ok = 0;
+ /* Set up timer for BT */
+ btco_inf->timer_ms = 10;
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
++ timer_setup(&btco_inf->timer, wl_cfg80211_bt_timerfunc, 0);
++#else
+ init_timer(&btco_inf->timer);
+ btco_inf->timer.data = (ulong)btco_inf;
+ btco_inf->timer.function = wl_cfg80211_bt_timerfunc;
++#endif
+
+ btco_inf->dev = ndev;
+
+diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_cfgp2p.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_cfgp2p.c
+old mode 100755
+new mode 100644
+index f0cf56e35bad..69f7a050cd14
+--- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_cfgp2p.c
++++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_cfgp2p.c
+@@ -56,6 +56,7 @@
+ #include
+ #include
+ #include
++#include
+
+ #if defined(BCMPCIE) && defined(DHD_FW_COREDUMP)
+ extern int dhd_bus_mem_dump(dhd_pub_t *dhd);
+@@ -333,6 +334,9 @@ wl_cfgp2p_init_priv(struct bcm_cfg80211 *cfg)
+ return -ENOMEM;
+ }
+
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
++ cfg->p2p->cfg = cfg;
++#endif
+ wl_to_p2p_bss_ndev(cfg, P2PAPI_BSSCFG_PRIMARY) = bcmcfg_to_prmry_ndev(cfg);
+ wl_to_p2p_bss_bssidx(cfg, P2PAPI_BSSCFG_PRIMARY) = 0;
+ wl_to_p2p_bss_ndev(cfg, P2PAPI_BSSCFG_DEVICE) = NULL;
+@@ -1385,10 +1389,21 @@ wl_cfgp2p_listen_complete(struct bcm_cfg80211 *cfg, bcm_struct_cfgdev *cfgdev,
+ * so lets do it from thread context.
+ */
+ void
+-wl_cfgp2p_listen_expired(unsigned long data)
++wl_cfgp2p_listen_expired(
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
++ struct timer_list *t
++#else
++ ulong data
++#endif
++)
+ {
+ wl_event_msg_t msg;
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
++ struct p2p_info *p2p = from_timer(p2p, t, listen_timer);
++ struct bcm_cfg80211 *cfg = p2p->cfg;
++#else
+ struct bcm_cfg80211 *cfg = (struct bcm_cfg80211 *) data;
++#endif
+ struct net_device *ndev;
+ CFGP2P_DBG((" Enter\n"));
+
+@@ -1742,6 +1757,8 @@ wl_cfgp2p_supported(struct bcm_cfg80211 *cfg, struct net_device *ndev)
+ return ret;
+ }
+ }
++ if (cfg->pub->conf->fw_type == FW_TYPE_MESH)
++ p2p_supported = 0;
+ if (p2p_supported == 1) {
+ CFGP2P_INFO(("p2p is supported\n"));
+ } else {
+@@ -1750,6 +1767,7 @@ wl_cfgp2p_supported(struct bcm_cfg80211 *cfg, struct net_device *ndev)
+ }
+ return p2p_supported;
+ }
++
+ /* Cleanup P2P resources */
+ s32
+ wl_cfgp2p_down(struct bcm_cfg80211 *cfg)
+diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_cfgp2p.h b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_cfgp2p.h
+old mode 100755
+new mode 100644
+index dba6b4783fba..ca930acc6553
+--- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_cfgp2p.h
++++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_cfgp2p.h
+@@ -71,6 +71,9 @@ struct p2p_bss {
+ };
+
+ struct p2p_info {
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
++ struct bcm_cfg80211 *cfg;
++#endif
+ bool on; /**< p2p on/off switch */
+ bool scan;
+ int16 search_state;
+@@ -183,6 +186,14 @@ enum wl_cfgp2p_status {
+ printk args; \
+ } \
+ } while (0)
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
++#define INIT_TIMER(timer, func, duration, extra_delay) \
++ do { \
++ timer_setup(timer, func, 0); \
++ timer->expires = jiffies + msecs_to_jiffies(duration + extra_delay); \
++ add_timer(timer); \
++ } while (0);
++#else
+ #define INIT_TIMER(timer, func, duration, extra_delay) \
+ do { \
+ init_timer(timer); \
+@@ -191,6 +202,7 @@ enum wl_cfgp2p_status {
+ timer->data = (unsigned long) cfg; \
+ add_timer(timer); \
+ } while (0);
++#endif
+
+ #if (LINUX_VERSION_CODE <= KERNEL_VERSION(3, 0, 8))
+ #ifdef WL_SUPPORT_BACKPORTED_KPATCHES
+@@ -245,7 +257,13 @@ enum wl_cfgp2p_status {
+ #define P2P_ECSA_CNT 50
+
+ extern void
+-wl_cfgp2p_listen_expired(unsigned long data);
++wl_cfgp2p_listen_expired(
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
++ struct timer_list *t
++#else
++ ulong data
++#endif
++);
+ extern bool
+ wl_cfgp2p_is_pub_action(void *frame, u32 frame_len);
+ extern bool
+diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_cfgvendor.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_cfgvendor.c
+index bd918e6583de..c5b4b2b05620 100644
+--- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_cfgvendor.c
++++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_cfgvendor.c
+@@ -2322,10 +2322,15 @@ static int wl_cfgvendor_lstats_get_info(struct wiphy *wiphy,
+ ((wl_cnt_info_t *)iovar_buf)->datalen,
+ WL_CNT_XTLV_CNTV_LE10_UCODE, NULL,
+ BCM_XTLV_OPTION_ALIGN32)) == NULL) {
+- macstat_cnt = bcm_get_data_from_xtlv_buf(((wl_cnt_info_t *)iovar_buf)->data,
++ if ((macstat_cnt = bcm_get_data_from_xtlv_buf(((wl_cnt_info_t *)iovar_buf)->data,
+ ((wl_cnt_info_t *)iovar_buf)->datalen,
+ WL_CNT_XTLV_GE40_UCODE_V1, NULL,
++ BCM_XTLV_OPTION_ALIGN32)) == NULL) {
++ macstat_cnt = bcm_get_data_from_xtlv_buf(((wl_cnt_info_t *)iovar_buf)->data,
++ ((wl_cnt_info_t *)iovar_buf)->datalen,
++ WL_CNT_XTLV_LT40_UCODE_V1, NULL,
+ BCM_XTLV_OPTION_ALIGN32);
++ }
+ }
+
+ if (macstat_cnt == NULL) {
+diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_escan.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_escan.c
+index a5aa76bad90c..3778d4977b78 100644
+--- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_escan.c
++++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_escan.c
+@@ -1,5 +1,4 @@
+ /* SPDX-License-Identifier: GPL-2.0 */
+-
+ #if defined(WL_ESCAN)
+
+ #include
+@@ -29,21 +28,21 @@
+ #define ESCAN_ERROR(x) \
+ do { \
+ if (iw_msg_level & ESCAN_ERROR_LEVEL) { \
+- printf(KERN_ERR "ESCAN-ERROR) "); \
++ printf(KERN_ERR "ESCAN-ERROR) %s : ", __func__); \
+ printf x; \
+ } \
+ } while (0)
+ #define ESCAN_SCAN(x) \
+ do { \
+ if (iw_msg_level & ESCAN_SCAN_LEVEL) { \
+- printf(KERN_ERR "ESCAN-SCAN) "); \
++ printf(KERN_ERR "ESCAN-SCAN) %s : ", __func__); \
+ printf x; \
+ } \
+ } while (0)
+ #define ESCAN_TRACE(x) \
+ do { \
+ if (iw_msg_level & ESCAN_TRACE_LEVEL) { \
+- printf(KERN_ERR "ESCAN-TRACE) "); \
++ printf(KERN_ERR "ESCAN-TRACE) %s : ", __func__); \
+ printf x; \
+ } \
+ } while (0)
+@@ -73,15 +72,6 @@ typedef struct {
+ #endif /* ESCAN_BUF_OVERFLOW_MGMT */
+
+ struct wl_escan_info *g_escan = NULL;
+-
+-#if defined(RSSIAVG)
+-static wl_rssi_cache_ctrl_t g_rssi_cache_ctrl;
+-static wl_rssi_cache_ctrl_t g_connected_rssi_cache_ctrl;
+-#endif
+-#if defined(BSSCACHE)
+-static wl_bss_cache_ctrl_t g_bss_cache_ctrl;
+-#endif
+-
+ /* Return a new chanspec given a legacy chanspec
+ * Returns INVCHANSPEC on error
+ */
+@@ -416,7 +406,7 @@ fail:
+ }
+
+ void
+-wl_escan_event(struct net_device *ndev, const wl_event_msg_t * e, void *data)
++wl_escan_event(struct net_device *dev, const wl_event_msg_t * e, void *data)
+ {
+ u32 event_type = ntoh32(e->event_type);
+ struct wl_escan_info *escan = g_escan;
+@@ -443,7 +433,7 @@ wl_escan_event(struct net_device *ndev, const wl_event_msg_t * e, void *data)
+ }
+
+ DHD_EVENT_WAKE_LOCK(escan->pub);
+- if (likely(!wl_enq_event(escan, ndev, event_type, e, data))) {
++ if (likely(!wl_enq_event(escan, dev, event_type, e, data))) {
+ wl_wakeup_event(escan);
+ } else {
+ DHD_EVENT_WAKE_UNLOCK(escan->pub);
+@@ -462,34 +452,39 @@ static s32 wl_escan_inform_bss(struct wl_escan_info *escan)
+
+ /* Delete disconnected cache */
+ #if defined(BSSCACHE)
+- wl_delete_disconnected_bss_cache(&g_bss_cache_ctrl, (u8*)&escan->disconnected_bssid);
++ wl_delete_disconnected_bss_cache(&escan->g_bss_cache_ctrl, (u8*)&escan->disconnected_bssid);
+ #if defined(RSSIAVG)
+- wl_delete_disconnected_rssi_cache(&g_rssi_cache_ctrl, (u8*)&escan->disconnected_bssid);
++ wl_delete_disconnected_rssi_cache(&escan->g_rssi_cache_ctrl, (u8*)&escan->disconnected_bssid);
+ #endif
+ #endif
+
+ /* Update cache */
+ #if defined(RSSIAVG)
+- wl_update_rssi_cache(&g_rssi_cache_ctrl, bss_list);
++ wl_update_rssi_cache(&escan->g_rssi_cache_ctrl, bss_list);
+ if (!in_atomic())
+- wl_update_connected_rssi_cache(escan->dev, &g_rssi_cache_ctrl, &rssi);
++ wl_update_connected_rssi_cache(escan->dev, &escan->g_rssi_cache_ctrl, &rssi);
+ #endif
+ #if defined(BSSCACHE)
+- wl_update_bss_cache(&g_bss_cache_ctrl,
++ wl_update_bss_cache(&escan->g_bss_cache_ctrl,
+ #if defined(RSSIAVG)
+- &g_rssi_cache_ctrl,
++ &escan->g_rssi_cache_ctrl,
+ #endif
+ bss_list);
+ #endif
+
+ /* delete dirty cache */
+ #if defined(RSSIAVG)
+- wl_delete_dirty_rssi_cache(&g_rssi_cache_ctrl);
+- wl_reset_rssi_cache(&g_rssi_cache_ctrl);
++ wl_delete_dirty_rssi_cache(&escan->g_rssi_cache_ctrl);
++ wl_reset_rssi_cache(&escan->g_rssi_cache_ctrl);
+ #endif
+ #if defined(BSSCACHE)
+- wl_delete_dirty_bss_cache(&g_bss_cache_ctrl);
+- wl_reset_bss_cache(&g_bss_cache_ctrl);
++ wl_delete_dirty_bss_cache(&escan->g_bss_cache_ctrl);
++ wl_reset_bss_cache(&escan->g_bss_cache_ctrl);
++ if (escan->autochannel)
++ wl_ext_get_best_channel(escan->dev, &escan->g_bss_cache_ctrl, &escan->best_2g_ch, &escan->best_5g_ch);
++#else
++ if (escan->autochannel)
++ wl_ext_get_best_channel(escan->dev, bss_list, &escan->best_2g_ch, &escan->best_5g_ch);
+ #endif
+
+ ESCAN_TRACE(("scanned AP count (%d)\n", bss_list->count));
+@@ -849,20 +844,21 @@ static s32 wl_escan_handler(struct wl_escan_info *escan,
+ }
+ else if (status == WLC_E_STATUS_SUCCESS) {
+ escan->escan_state = ESCAN_STATE_IDLE;
+-
+- ESCAN_TRACE(("ESCAN COMPLETED\n"));
+- escan->bss_list = wl_escan_get_buf(escan);
+- ESCAN_TRACE(("SCAN COMPLETED: scanned AP count=%d\n",
+- escan->bss_list->count));
+- wl_escan_inform_bss(escan);
+- wl_notify_escan_complete(escan, false);
+-
++ ESCAN_TRACE(("ESCAN COMPLETED\n"));
++ escan->bss_list = wl_escan_get_buf(escan);
++ ESCAN_TRACE(("SCAN COMPLETED: scanned AP count=%d\n",
++ escan->bss_list->count));
++ wl_escan_inform_bss(escan);
++ wl_notify_escan_complete(escan, false);
+ } else if ((status == WLC_E_STATUS_ABORT) || (status == WLC_E_STATUS_NEWSCAN) ||
+ (status == WLC_E_STATUS_11HQUIET) || (status == WLC_E_STATUS_CS_ABORT) ||
+ (status == WLC_E_STATUS_NEWASSOC)) {
+ /* Handle all cases of scan abort */
+ escan->escan_state = ESCAN_STATE_IDLE;
+ ESCAN_TRACE(("ESCAN ABORT reason: %d\n", status));
++ escan->bss_list = wl_escan_get_buf(escan);
++ ESCAN_TRACE(("SCAN ABORT: scanned AP count=%d\n",
++ escan->bss_list->count));
+ wl_escan_inform_bss(escan);
+ wl_notify_escan_complete(escan, false);
+ } else if (status == WLC_E_STATUS_TIMEOUT) {
+@@ -871,6 +867,7 @@ static s32 wl_escan_handler(struct wl_escan_info *escan,
+ if (e->reason == 0xFFFFFFFF) {
+ wl_notify_escan_complete(escan, true);
+ }
++ escan->escan_state = ESCAN_STATE_IDLE;
+ } else {
+ ESCAN_ERROR(("unexpected Escan Event %d : abort\n", status));
+ escan->escan_state = ESCAN_STATE_IDLE;
+@@ -983,9 +980,8 @@ wl_escan_prep(struct wl_escan_info *escan, wl_uint32_list_t *list,
+ return err;
+ }
+
+-static int wl_escan_reset(void) {
+- struct wl_escan_info *escan = g_escan;
+-
++static int wl_escan_reset(struct wl_escan_info *escan)
++{
+ if (timer_pending(&escan->scan_timeout))
+ del_timer_sync(&escan->scan_timeout);
+ escan->escan_state = ESCAN_STATE_IDLE;
+@@ -993,10 +989,20 @@ static int wl_escan_reset(void) {
+ return 0;
+ }
+
+-static void wl_escan_timeout(unsigned long data)
++static void wl_escan_timeout(
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
++ struct timer_list *t
++#else
++ unsigned long data
++#endif
++)
+ {
+ wl_event_msg_t msg;
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
++ struct wl_escan_info *escan = from_timer(escan, t, scan_timeout);
++#else
+ struct wl_escan_info *escan = (struct wl_escan_info *)data;
++#endif
+ struct wl_scan_results *bss_list;
+ struct wl_bss_info *bi = NULL;
+ s32 i;
+@@ -1047,7 +1053,7 @@ wl_escan_set_scan(
+ wl_escan_params_t *params = NULL;
+ scb_val_t scbval;
+ static int cnt = 0;
+- struct wl_escan_info *escan = NULL;
++ struct wl_escan_info *escan = g_escan;
+ wlc_ssid_t ssid;
+ u32 n_channels = 0;
+ wl_uint32_list_t *list;
+@@ -1056,9 +1062,8 @@ wl_escan_set_scan(
+
+ ESCAN_TRACE(("Enter \n"));
+
+- escan = g_escan;
+ if (!escan) {
+- ESCAN_ERROR(("device is not ready\n")); \
++ ESCAN_ERROR(("device is not ready\n"));
+ return -EIO;
+ }
+ mutex_lock(&escan->usr_sync);
+@@ -1145,7 +1150,7 @@ wl_escan_set_scan(
+ ESCAN_TRACE(("Escan not permitted at this time (%d)\n", err));
+ else
+ ESCAN_ERROR(("Escan set error (%d)\n", err));
+- wl_escan_reset();
++ wl_escan_reset(escan);
+ }
+ kfree(params);
+
+@@ -1206,10 +1211,15 @@ wl_escan_get_scan(
+ err = -EAGAIN;
+ goto exit;
+ }
++ if (!escan->bss_list) {
++ ESCAN_ERROR(("%s: scan not ready\n", dev->name));
++ err = -EAGAIN;
++ goto exit;
++ }
+
+ #if defined(BSSCACHE)
+- bss_list = &g_bss_cache_ctrl.m_cache_head->results;
+- node = g_bss_cache_ctrl.m_cache_head;
++ bss_list = &escan->g_bss_cache_ctrl.m_cache_head->results;
++ node = escan->g_bss_cache_ctrl.m_cache_head;
+ for (i=0; node && ibss_list;
+@@ -1228,7 +1238,7 @@ wl_escan_get_scan(
+ }
+
+ #if defined(RSSIAVG)
+- rssi = wl_get_avg_rssi(&g_rssi_cache_ctrl, &bi->BSSID);
++ rssi = wl_get_avg_rssi(&escan->g_rssi_cache_ctrl, &bi->BSSID);
+ if (rssi == RSSI_MINVAL)
+ rssi = MIN(dtoh16(bi->RSSI), RSSI_MAXVAL);
+ #else
+@@ -1236,8 +1246,8 @@ wl_escan_get_scan(
+ rssi = MIN(dtoh16(bi->RSSI), RSSI_MAXVAL);
+ #endif
+ channel = wf_chspec_ctlchan(wl_chspec_driver_to_host(escan->ioctl_ver, bi->chanspec));
+- ESCAN_SCAN(("%s: BSSID="MACSTR", channel=%d, RSSI=%d, SSID=\"%s\"\n",
+- __FUNCTION__, MAC2STR(bi->BSSID.octet), channel, rssi, bi->SSID));
++ ESCAN_SCAN(("BSSID="MACSTR", channel=%d, RSSI=%d, SSID=\"%s\"\n",
++ MAC2STR(bi->BSSID.octet), channel, rssi, bi->SSID));
+
+ /* First entry must be the BSSID */
+ iwe.cmd = SIOCGIWAP;
+@@ -1323,6 +1333,27 @@ exit:
+ return err;
+ }
+
++s32 wl_escan_autochannel(struct net_device *dev, char* command, int total_len)
++{
++ struct wl_escan_info *escan = g_escan;
++ int ret = 0;
++ int bytes_written = -1;
++
++ sscanf(command, "%*s %d", &escan->autochannel);
++
++ if (escan->autochannel == 0) {
++ escan->best_2g_ch = 0;
++ escan->best_5g_ch = 0;
++ } else if (escan->autochannel == 2) {
++ bytes_written = snprintf(command, total_len, "2g=%d 5g=%d",
++ escan->best_2g_ch, escan->best_5g_ch);
++ ANDROID_TRACE(("%s: command result is %s\n", __FUNCTION__, command));
++ ret = bytes_written;
++ }
++
++ return ret;
++}
++
+ static s32 wl_create_event_handler(struct wl_escan_info *escan)
+ {
+ int ret = 0;
+@@ -1343,42 +1374,44 @@ static void wl_destroy_event_handler(struct wl_escan_info *escan)
+ PROC_STOP(&escan->event_tsk);
+ }
+
+-static void wl_escan_deinit(void)
++static void wl_escan_deinit(struct wl_escan_info *escan)
+ {
+- struct wl_escan_info *escan = g_escan;
+-
+ printf("%s: Enter\n", __FUNCTION__);
+ if (!escan) {
+- ESCAN_ERROR(("device is not ready\n")); \
++ ESCAN_ERROR(("device is not ready\n"));
+ return;
+ }
+ wl_destroy_event_handler(escan);
+ wl_flush_eq(escan);
+ del_timer_sync(&escan->scan_timeout);
++ escan->escan_state = ESCAN_STATE_IDLE;
+
+ #if defined(RSSIAVG)
+- wl_free_rssi_cache(&g_rssi_cache_ctrl);
++ wl_free_rssi_cache(&escan->g_rssi_cache_ctrl);
+ #endif
+ #if defined(BSSCACHE)
+- wl_free_bss_cache(&g_bss_cache_ctrl);
++ wl_free_bss_cache(&escan->g_bss_cache_ctrl);
+ #endif
+ }
+
+-static s32 wl_escan_init(void)
++static s32 wl_escan_init(struct wl_escan_info *escan)
+ {
+- struct wl_escan_info *escan = g_escan;
+ int err = 0;
+
+ printf("%s: Enter\n", __FUNCTION__);
+ if (!escan) {
+- ESCAN_ERROR(("device is not ready\n")); \
++ ESCAN_ERROR(("device is not ready\n"));
+ return -EIO;
+ }
+
+ /* Init scan_timeout timer */
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
++ timer_setup(&escan->scan_timeout, wl_escan_timeout, 0);
++#else
+ init_timer(&escan->scan_timeout);
+ escan->scan_timeout.data = (unsigned long) escan;
+ escan->scan_timeout.function = wl_escan_timeout;
++#endif
+
+ if (wl_create_event_handler(escan)) {
+ err = -ENOMEM;
+@@ -1393,7 +1426,7 @@ static s32 wl_escan_init(void)
+
+ return 0;
+ err:
+- wl_escan_deinit();
++ wl_escan_deinit(escan);
+ return err;
+ }
+
+@@ -1404,11 +1437,11 @@ void wl_escan_detach(dhd_pub_t *dhdp)
+ printf("%s: Enter\n", __FUNCTION__);
+
+ if (!escan) {
+- ESCAN_ERROR(("device is not ready\n")); \
++ ESCAN_ERROR(("device is not ready\n"));
+ return;
+ }
+
+- wl_escan_deinit();
++ wl_escan_deinit(escan);
+
+ if (escan->escan_ioctl_buf) {
+ kfree(escan->escan_ioctl_buf);
+@@ -1430,10 +1463,10 @@ wl_escan_attach(struct net_device *dev, dhd_pub_t *dhdp)
+ escan = (wl_escan_info_t *)DHD_OS_PREALLOC(dhdp, DHD_PREALLOC_WL_ESCAN_INFO, sizeof(struct wl_escan_info));
+ if (!escan)
+ return -ENOMEM;
++ g_escan = escan;
+ memset(escan, 0, sizeof(struct wl_escan_info));
+
+ /* we only care about main interface so save a global here */
+- g_escan = escan;
+ escan->dev = dev;
+ escan->pub = dhdp;
+ escan->escan_state = ESCAN_STATE_IDLE;
+@@ -1444,9 +1477,7 @@ wl_escan_attach(struct net_device *dev, dhd_pub_t *dhdp)
+ goto err ;
+ }
+ wl_init_eq(escan);
+-#ifdef WL_ESCAN
+- wl_escan_init();
+-#endif
++ wl_escan_init(escan);
+
+ return 0;
+ err:
+diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_escan.h b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_escan.h
+index 62bc0d28b0e8..6d84ce56c547 100644
+--- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_escan.h
++++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_escan.h
+@@ -1,5 +1,4 @@
+-/* SPDX-License-Identifier: GPL-2.0 */
+-
++/* SPDX-License-Identifier: GPL-2.0 */
+ #ifndef _wl_escan_
+ #define _wl_escan_
+
+@@ -57,9 +56,19 @@ typedef struct wl_escan_info {
+ tsk_ctl_t event_tsk; /* task of main event handler thread */
+ ESCAN_EVENT_HANDLER evt_handler[WLC_E_LAST];
+ struct mutex usr_sync; /* maily for up/down synchronization */
++ int autochannel;
++ int best_2g_ch;
++ int best_5g_ch;
++#if defined(RSSIAVG)
++ wl_rssi_cache_ctrl_t g_rssi_cache_ctrl;
++ wl_rssi_cache_ctrl_t g_connected_rssi_cache_ctrl;
++#endif
++#if defined(BSSCACHE)
++ wl_bss_cache_ctrl_t g_bss_cache_ctrl;
++#endif
+ } wl_escan_info_t;
+
+-void wl_escan_event(struct net_device *ndev, const wl_event_msg_t * e, void *data);
++void wl_escan_event(struct net_device *dev, const wl_event_msg_t * e, void *data);
+
+ int wl_escan_set_scan(
+ struct net_device *dev,
+@@ -69,6 +78,7 @@ int wl_escan_set_scan(
+ );
+ int wl_escan_get_scan(struct net_device *dev, struct iw_request_info *info,
+ struct iw_point *dwrq, char *extra);
++s32 wl_escan_autochannel(struct net_device *dev, char* command, int total_len);
+ int wl_escan_attach(struct net_device *dev, dhd_pub_t *dhdp);
+ void wl_escan_detach(dhd_pub_t *dhdp);
+
+diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_iw.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_iw.c
+index c2883471ee1e..4713882c27ae 100644
+--- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_iw.c
++++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_iw.c
+@@ -627,16 +627,18 @@ wl_iw_get_freq(
+ char *extra
+ )
+ {
+- channel_info_t ci;
+ int error;
++ u32 chanspec = 0;
++ int ctl_chan;
+
+ WL_TRACE(("%s: SIOCGIWFREQ\n", dev->name));
+
+- if ((error = dev_wlc_ioctl(dev, WLC_GET_CHANNEL, &ci, sizeof(ci))))
++ if ((error = dev_wlc_intvar_get(dev, "chanspec", &chanspec)))
+ return error;
++ ctl_chan = wf_chspec_ctlchan(chanspec);
+
+ /* Return radio channel in channel form */
+- fwrq->m = dtoh32(ci.hw_channel);
++ fwrq->m = ctl_chan;
+ fwrq->e = dtoh32(0);
+ return 0;
+ }
+@@ -1779,15 +1781,14 @@ wl_iw_get_essid(
+ /* Max SSID length check */
+ if (ssid.SSID_len > IW_ESSID_MAX_SIZE) {
+ ssid.SSID_len = IW_ESSID_MAX_SIZE;
+- /* Get the current SSID */
+- memcpy(extra, ssid.SSID, ssid.SSID_len);
+- /* NULL terminating as length of extra buffer is IW_ESSID_MAX_SIZE ie 32 */
+- extra[IW_ESSID_MAX_SIZE - 1] = '\0';
+- } else {
+- /* Get the current SSID */
+- memcpy(extra, ssid.SSID, ssid.SSID_len);
+ }
+
++ /* Get the current SSID */
++ memcpy(extra, ssid.SSID, ssid.SSID_len);
++
++ /* NULL terminating as length of extra buffer is IW_ESSID_MAX_SIZE ie 32 */
++ extra[IW_ESSID_MAX_SIZE] = '\0';
++
+ dwrq->length = ssid.SSID_len;
+
+ dwrq->flags = 1; /* active */
+@@ -3304,6 +3305,7 @@ wl_iw_event(struct net_device *dev, wl_event_msg_t *e, void* data)
+ uint16 flags = ntoh16(e->flags);
+ uint32 datalen = ntoh32(e->datalen);
+ uint32 status = ntoh32(e->status);
++ uint32 reason = ntoh32(e->reason);
+
+ memset(&wrqu, 0, sizeof(wrqu));
+ memset(extra, 0, sizeof(extra));
+@@ -3333,12 +3335,12 @@ wl_iw_event(struct net_device *dev, wl_event_msg_t *e, void* data)
+ cmd = SIOCGIWAP;
+ wrqu.data.length = strlen(extra);
+ if (!(flags & WLC_EVENT_MSG_LINK)) {
+- printf("%s: Link Down with BSSID="MACSTR"\n", __FUNCTION__,
+- MAC2STR((u8 *)wrqu.addr.sa_data));
++ printf("%s: Link Down with "MACSTR", reason=%d\n", __FUNCTION__,
++ MAC2STR((u8 *)wrqu.addr.sa_data), reason);
+ bzero(wrqu.addr.sa_data, ETHER_ADDR_LEN);
+ bzero(&extra, ETHER_ADDR_LEN);
+ } else {
+- printf("%s: Link UP with BSSID="MACSTR"\n", __FUNCTION__,
++ printf("%s: Link UP with "MACSTR"\n", __FUNCTION__,
+ MAC2STR((u8 *)wrqu.addr.sa_data));
+ }
+ break;
+@@ -3545,15 +3547,19 @@ int wl_iw_get_wireless_stats(struct net_device *dev, struct iw_statistics *wstat
+ #endif /* WIRELESS_EXT > 11 */
+
+ phy_noise = 0;
+- if ((res = dev_wlc_ioctl(dev, WLC_GET_PHY_NOISE, &phy_noise, sizeof(phy_noise))))
++ if ((res = dev_wlc_ioctl(dev, WLC_GET_PHY_NOISE, &phy_noise, sizeof(phy_noise)))) {
++ WL_ERROR(("%s: WLC_GET_PHY_NOISE error=%d\n", __FUNCTION__, res));
+ goto done;
++ }
+
+ phy_noise = dtoh32(phy_noise);
+ WL_TRACE(("wl_iw_get_wireless_stats phy noise=%d\n *****", phy_noise));
+
+- scb_val.val = 0;
+- if ((res = dev_wlc_ioctl(dev, WLC_GET_RSSI, &scb_val, sizeof(scb_val_t))))
++ memset(&scb_val, 0, sizeof(scb_val));
++ if ((res = dev_wlc_ioctl(dev, WLC_GET_RSSI, &scb_val, sizeof(scb_val_t)))) {
++ WL_ERROR(("%s: WLC_GET_RSSI error=%d\n", __FUNCTION__, res));
+ goto done;
++ }
+
+ rssi = dtoh32(scb_val.val);
+ rssi = MIN(rssi, RSSI_MAXVAL);
+@@ -3647,9 +3653,19 @@ done:
+
+ #ifndef WL_ESCAN
+ static void
+-wl_iw_timerfunc(ulong data)
++wl_iw_timerfunc(
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
++ struct timer_list *t
++#else
++ unsigned long data
++#endif
++)
+ {
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
++ iscan_info_t *iscan = from_timer(iscan, t, timer);
++#else
+ iscan_info_t *iscan = (iscan_info_t *)data;
++#endif
+ iscan->timer_on = 0;
+ if (iscan->iscan_state != ISCAN_STATE_IDLE) {
+ WL_TRACE(("timer trigger\n"));
+@@ -3887,9 +3903,13 @@ wl_iw_attach(struct net_device *dev, void * dhdp)
+
+ /* Set up the timer */
+ iscan->timer_ms = 2000;
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
++ timer_setup(&iscan->timer, wl_iw_timerfunc, 0);
++#else
+ init_timer(&iscan->timer);
+ iscan->timer.data = (ulong)iscan;
+ iscan->timer.function = wl_iw_timerfunc;
++#endif
+
+ sema_init(&iscan->sysioc_sem, 0);
+ init_completion(&iscan->sysioc_exited);
+diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wldev_common.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wldev_common.c
+index 1b985b090880..8be455fc777e 100644
+--- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wldev_common.c
++++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wldev_common.c
+@@ -34,7 +34,9 @@
+
+ #include
+ #include
++#ifdef WL_CFG80211
+ #include
++#endif
+ #include
+
+ #define htod32(i) (i)
+@@ -478,9 +480,11 @@ int wldev_set_country(
+ wl_country_t cur_cspec = {{0}, 0, {0}}; /* current ccode */
+ scb_val_t scbval;
+ char smbuf[WLC_IOCTL_SMLEN];
++#ifdef WL_CFG80211
+ struct wireless_dev *wdev = ndev_to_wdev(dev);
+ struct wiphy *wiphy = wdev->wiphy;
+ struct bcm_cfg80211 *cfg = wiphy_priv(wiphy);
++#endif
+
+ if (!country_code)
+ return error;
+@@ -495,7 +499,7 @@ int wldev_set_country(
+ cspec.rev = revinfo;
+ memcpy(cspec.country_abbrev, country_code, WLC_CNTRY_BUF_SZ);
+ memcpy(cspec.ccode, country_code, WLC_CNTRY_BUF_SZ);
+- error = dhd_conf_get_country_from_config(dhd_get_pub(dev), &cspec);
++ error = dhd_conf_map_country_list(dhd_get_pub(dev), &cspec, 0);
+ if (error)
+ dhd_get_customized_country_code(dev, (char *)&cspec.country_abbrev, &cspec);
+
+@@ -506,7 +510,11 @@ int wldev_set_country(
+ dhd_force_country_change(dev) ||
+ (strncmp(cspec.ccode, cur_cspec.ccode, WLC_CNTRY_BUF_SZ) != 0)) {
+
+- if ((user_enforced) && (wl_get_drv_status(cfg, CONNECTED, dev))) {
++ if ((user_enforced)
++#ifdef WL_CFG80211
++ && (wl_get_drv_status(cfg, CONNECTED, dev))
++#endif
++ ) {
+ bzero(&scbval, sizeof(scb_val_t));
+ error = wldev_ioctl_set(dev, WLC_DISASSOC,
+ &scbval, sizeof(scb_val_t));
+
+From 8f71cec481e88d3292f72ffa20f4422736593142 Mon Sep 17 00:00:00 2001
+From: Yao Xiao
+Date: Fri, 28 Sep 2018 11:59:22 +0800
+Subject: [PATCH] net: wireless: bcmdhd: fix some issues for driver
+ "1.579.77.41.9 (r)"
+
+1. disable tuning during sdio sleep
+2. add get_oob_irq_flags interface for request_irq(flags)
+
+Change-Id: Ic77fd82fc3f93a813512ecceaed2c75fe204b750
+Signed-off-by: Yao Xiao
+---
+ .../net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_gpio.c | 14 +++++++++-----
+ .../net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_sdio.c | 13 +++++++++++++
+ 2 files changed, 22 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_gpio.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_gpio.c
+index 7291322edc4f..dbdcbfa77f0d 100644
+--- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_gpio.c
++++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_gpio.c
+@@ -241,6 +241,7 @@ int dhd_wlan_init_gpio(void)
+ #ifdef CUSTOMER_OOB
+ int host_oob_irq = -1;
+ uint host_oob_irq_flags = 0;
++ int irq_flags = -1;
+ #endif
+
+ /* Please check your schematic and fill right GPIO number which connected to
+@@ -286,11 +287,14 @@ int dhd_wlan_init_gpio(void)
+ host_oob_irq = rockchip_wifi_get_oob_irq();
+
+ #ifdef HW_OOB
+-#ifdef HW_OOB_LOW_LEVEL
+- host_oob_irq_flags = IORESOURCE_IRQ | IORESOURCE_IRQ_LOWLEVEL | IORESOURCE_IRQ_SHAREABLE;
+-#else
+- host_oob_irq_flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL | IORESOURCE_IRQ_SHAREABLE;
+-#endif
++ host_oob_irq_flags = IORESOURCE_IRQ | IORESOURCE_IRQ_SHAREABLE;
++ irq_flags = rockchip_wifi_get_oob_irq_flag();
++ if (irq_flags == 1)
++ host_oob_irq_flags |= IORESOURCE_IRQ_HIGHLEVEL;
++ else if (irq_flags == 0)
++ host_oob_irq_flags |= IORESOURCE_IRQ_LOWLEVEL;
++ else
++ pr_warn("%s: unknown oob irqflags !\n", __func__);
+ #else
+ host_oob_irq_flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE | IORESOURCE_IRQ_SHAREABLE;
+ #endif
+diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_sdio.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_sdio.c
+index 3e035df90f9b..342a53a4eb5e 100644
+--- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_sdio.c
++++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_sdio.c
+@@ -79,6 +79,10 @@
+ #include
+ #endif /* BT_OVER_SDIO */
+
++#include
++#include
++#include "bcmsdh_sdmmc.h"
++
+ bool dhd_mp_halting(dhd_pub_t *dhdp);
+ extern void bcmsdh_waitfor_iodrain(void *sdh);
+ extern void bcmsdh_reject_ioreqs(void *sdh, bool reject);
+@@ -1038,6 +1042,12 @@ dhdsdio_clk_kso_enab(dhd_bus_t *bus, bool on)
+ uint8 wr_val = 0, rd_val, cmp_val, bmask;
+ int err = 0;
+ int try_cnt = 0;
++ struct mmc_host *host;
++ struct sdioh_info *sd = (struct sdioh_info *)(bus->sdh->sdioh);
++ struct sdio_func *func = sd->func[SDIO_FUNC_0];
++
++ host = func->card->host;
++ mmc_retune_disable(host);
+
+ KSO_DBG(("%s> op:%s\n", __FUNCTION__, (on ? "KSO_SET" : "KSO_CLR")));
+
+@@ -1051,6 +1061,7 @@ dhdsdio_clk_kso_enab(dhd_bus_t *bus, bool on)
+ * after clearing KSO bit, to avoid polling of KSO bit.
+ */
+ if ((!on) && (bus->sih->chip == BCM43012_CHIP_ID)) {
++ mmc_retune_enable(host);
+ return err;
+ }
+
+@@ -1091,6 +1102,8 @@ dhdsdio_clk_kso_enab(dhd_bus_t *bus, bool on)
+ __FUNCTION__, (on ? "KSO_SET" : "KSO_CLR"), try_cnt, rd_val, err));
+ }
+
++ mmc_retune_enable(host);
++
+ return err;
+ }
+
+
+From 14174aa36436283cd3c51df73ed45c41f05e74ff Mon Sep 17 00:00:00 2001
+From: Yao Xiao
+Date: Wed, 10 Oct 2018 16:12:08 +0800
+Subject: [PATCH] net: wireless: rkwifi: add SUPPORT_P2P_GO_PS to fix suspend
+ issue
+
+Change-Id: I23f563c1de9527f2a17ccbe90a61516b4f76b2a8
+Signed-off-by: Yao Xiao
+---
+ drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/Makefile | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/Makefile b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/Makefile
+index c49feed9d913..5588178bfce2 100644
+--- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/Makefile
++++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/Makefile
+@@ -24,7 +24,7 @@ DHDCFLAGS = -Wall -Wstrict-prototypes -Dlinux -DBCMDRIVER \
+ -DKEEP_ALIVE -DPKT_FILTER_SUPPORT -DPNO_SUPPORT -DDHDTCPACK_SUPPRESS \
+ -DDHD_DONOT_FORWARD_BCMEVENT_AS_NETWORK_PKT \
+ -DMULTIPLE_SUPPLICANT -DTSQ_MULTIPLIER -DMFP \
+- -DWL_EXT_IAPSTA \
++ -DWL_EXT_IAPSTA -DSUPPORT_P2P_GO_PS \
+ -DENABLE_INSMOD_NO_FW_LOAD -DDHD_UNSUPPORT_IF_CNTS \
+ -Idrivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd \
+ -Idrivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include
+
+From dc1f78d0fd4d359bcf7ba89b7c3e8fe747ef0bef Mon Sep 17 00:00:00 2001
+From: Huibin Hong
+Date: Fri, 31 Aug 2018 17:16:16 +0800
+Subject: [PATCH] serial: 8250: fix cpu and dmac access uart fifo at the same
+ time
+
+If cpu frequency was 1.0 GHz, bluetooth music is unstable. But it
+is stable when cpu frequency is 400 MHz. Although I had test the
+uart dma driver many days on rk3088, maybe the cpu frequency was low,
+and I couldn't produce the issue. In one word, the code is unlogical.
+The new logic is that increase the fifo water level, if dmac bursts
+before it, wait until dmac finishes and read the rest data.
+If dmac doesn't burst after it, then read data from fifo directly.
+
+Change-Id: I6fec42a0895df8a0faeba97d05fd36b761744fa2
+Signed-off-by: Huibin Hong
+---
+ drivers/tty/serial/8250/8250_dma.c | 37 +++++++++++++++++--------------------
+ 1 file changed, 17 insertions(+), 20 deletions(-)
+
+diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c
+index cbac385b01c6..f0b3a84565dd 100644
+--- a/drivers/tty/serial/8250/8250_dma.c
++++ b/drivers/tty/serial/8250/8250_dma.c
+@@ -160,37 +160,34 @@ err:
+
+ int serial8250_rx_dma(struct uart_8250_port *p, unsigned int iir)
+ {
+- unsigned int rfl, i = 0, fcr = 0;
++ unsigned int rfl, i = 0, fcr = 0, cur_index = 0;
+ unsigned char buf[MAX_FIFO_SIZE];
+ struct uart_port *port = &p->port;
+ struct tty_port *tty_port = &p->port.state->port;
++ struct dma_tx_state state;
++ struct uart_8250_dma *dma = p->dma;
++
+
+ if ((iir & 0xf) != UART_IIR_RX_TIMEOUT)
+ return 0;
+
+- rfl = serial_port_in(port, UART_RFL_16550A);
++ fcr = UART_FCR_ENABLE_FIFO | UART_FCR_T_TRIG_10 | UART_FCR_R_TRIG_11;
++ serial_port_out(port, UART_FCR, fcr);
+
+- if (rfl > (p->port.fifosize / 2 - 4)) {
+- fcr = UART_FCR_ENABLE_FIFO | UART_FCR_T_TRIG_10 | UART_FCR_R_TRIG_01;
+- serial_port_out(port, UART_FCR, fcr);
+- } else {
+- while (i < rfl)
+- buf[i++] = serial_port_in(port, UART_RX);
+- }
++ do {
++ dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state);
++ cur_index = dma->rx_size - state.residue;
++ } while (cur_index % dma->rxconf.src_maxburst);
++
++ rfl = serial_port_in(port, UART_RFL_16550A);
++ while (i < rfl)
++ buf[i++] = serial_port_in(port, UART_RX);
+
+ __dma_rx_complete(p);
+
+- if (i == 0) {
+- rfl = serial_port_in(port, UART_RFL_16550A);
+- if (rfl == 0) {
+- __dma_rx_complete(p);
+- tty_flip_buffer_push(tty_port);
+- }
+- } else {
+- tty_insert_flip_string(tty_port, buf, i);
+- p->port.icount.rx += i;
+- tty_flip_buffer_push(tty_port);
+- }
++ tty_insert_flip_string(tty_port, buf, i);
++ p->port.icount.rx += i;
++ tty_flip_buffer_push(tty_port);
+
+ if (fcr)
+ serial_port_out(port, UART_FCR, p->fcr);
+
+From 9c03219fa35b6b5186df6b6defd2782a29e7967e Mon Sep 17 00:00:00 2001
+From: Huibin Hong
+Date: Wed, 12 Sep 2018 16:24:05 +0800
+Subject: [PATCH] serial: 8250: add line error log
+
+Change-Id: I69fe6f0c0857ade25e777be388fcab6261f1a533
+Signed-off-by: Huibin Hong
+---
+ drivers/tty/serial/8250/8250_port.c | 19 ++++++++++++++++++-
+ 1 file changed, 18 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
+index 14665c09d2ba..5650b1551c44 100644
+--- a/drivers/tty/serial/8250/8250_port.c
++++ b/drivers/tty/serial/8250/8250_port.c
+@@ -1551,7 +1551,7 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir)
+ unsigned char status;
+ unsigned long flags;
+ struct uart_8250_port *up = up_to_u8250p(port);
+- int dma_err = 0;
++ int dma_err = 0, idx;
+
+ if (iir & UART_IIR_NO_INT)
+ return 0;
+@@ -1573,7 +1573,24 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir)
+ if ((!up->dma || (up->dma && up->dma->tx_err)) &&
+ (status & UART_LSR_THRE))
+ serial8250_tx_chars(up);
++#ifdef CONFIG_ARCH_ROCKCHIP
++ if (status & UART_LSR_BRK_ERROR_BITS) {
++
++ idx = serial_index(port);
++
++ if (status & UART_LSR_OE)
++ pr_err("ttyS%d: Overrun error!\n", idx);
++ if (status & UART_LSR_PE)
++ pr_err("ttyS%d: Parity error!\n", idx);
++ if (status & UART_LSR_FE)
++ pr_err("ttyS%d: Frame error!\n", idx);
++ if (status & UART_LSR_BI)
++ pr_err("ttyS%d: Break interrupt!\n", idx);
+
++ pr_err("ttyS%d: maybe rx pin is low or baudrate is not correct!\n",
++ idx);
++ }
++#endif
+ spin_unlock_irqrestore(&port->lock, flags);
+ return 1;
+ }
+
+From 5bf471cff7903b03216bdb1c6c834071780db709 Mon Sep 17 00:00:00 2001
+From: Caesar Wang
+Date: Mon, 19 Nov 2018 16:46:31 +0800
+Subject: [PATCH] net/rfkill: bt: improve to set the bt power
+
+There is a bt power issue on Debian OS, the blueman app
+will callback to enable the bluetooth power during the bringup.
+
+As below log the system bringup about 10s.
+[BT_RFKILL]: rfkill_rk_set_power: set bt wake_host pin output high!
+[BT_RFKILL]: rfkill_rk_set_power: enable bt reset pin!
+[BT_RFKILL]: ENABLE UART_RTS
+[BT_RFKILL]: DISABLE UART_RTS
+[BT_RFKILL]: bt turn on power
+...
+
+So the rfkill-bt driver should judge the power status, otherwise the
+blueman app will cause the bluetoolth timeout error. e.g:
+root@linaro-alip:/# bluetoothctl
+[NEW] Controller 3B:A0:90:48:46:40 linaro-alip [default]
+[bluetooth]# scan on
+Discovery started
+[ 33.076522] Bluetooth: hci0 command 0x2005 tx timeout
+[ 35.080473] Bluetooth: hci0 command 0x200b tx timeout
+[ 37.084505] Bluetooth: hci0 command 0x200c tx timeout
+
+Change-Id: Ib0dd99a83c64dbaf469c3eb7a9226a2d83c53e6f
+Signed-off-by: Caesar Wang
+---
+ net/rfkill/rfkill-bt.c | 54 ++++++++++++++++++++++++++++----------------------
+ 1 file changed, 30 insertions(+), 24 deletions(-)
+
+diff --git a/net/rfkill/rfkill-bt.c b/net/rfkill/rfkill-bt.c
+index b19e0fb9ce82..29d88313e4a8 100644
+--- a/net/rfkill/rfkill-bt.c
++++ b/net/rfkill/rfkill-bt.c
+@@ -296,20 +296,23 @@ static int rfkill_rk_set_power(void *data, bool blocked)
+ msleep(20);
+ }
+
+- if (gpio_is_valid(poweron->io) && gpio_is_valid(wake_host->io))
+- {
+- gpio_direction_output(poweron->io, !poweron->enable);
+- msleep(20);
+- gpio_direction_output(poweron->io, poweron->enable);
+- msleep(20);
+- gpio_direction_input(wake_host->io);
+- LOG("%s: set bt wake_host pin input!\n", __func__);
++ if (gpio_is_valid(poweron->io) && gpio_is_valid(wake_host->io)) {
++ if (gpio_get_value(poweron->io) == !poweron->enable) {
++ gpio_direction_output(poweron->io, !poweron->enable);
++ msleep(20);
++ gpio_direction_output(poweron->io, poweron->enable);
++ msleep(20);
++ gpio_direction_input(wake_host->io);
++ LOG("%s: set bt wake_host pin input!\n", __func__);
++ }
+ }
+- if (gpio_is_valid(reset->io))
+- {
++
++ if (gpio_is_valid(reset->io)) {
++ if (gpio_get_value(reset->io) == !reset->enable) {
+ gpio_direction_output(reset->io, !reset->enable);
+- msleep(20);
++ msleep(20);
+ gpio_direction_output(reset->io, reset->enable);
++ }
+ }
+
+ if (pinctrl != NULL && gpio_is_valid(rts->io))
+@@ -326,20 +329,23 @@ static int rfkill_rk_set_power(void *data, bool blocked)
+ bt_power_state = 1;
+ LOG("bt turn on power\n");
+ } else {
+- if (gpio_is_valid(poweron->io))
+- {
+- gpio_direction_output(poweron->io, !poweron->enable);
+- msleep(20);
+- }
+-
+- bt_power_state = 0;
+- LOG("bt shut off power\n");
+- if (gpio_is_valid(reset->io))
+- {
+- gpio_direction_output(reset->io, !reset->enable);/* bt reset active*/
+- msleep(20);
+- }
++ if (gpio_is_valid(poweron->io)) {
++ if (gpio_get_value(poweron->io) == poweron->enable) {
++ gpio_direction_output(poweron->io,
++ !poweron->enable);
++ msleep(20);
++ }
++ }
+
++ bt_power_state = 0;
++ LOG("bt shut off power\n");
++ if (gpio_is_valid(reset->io)) {
++ if (gpio_get_value(reset->io) == reset->enable) {
++ gpio_direction_output(reset->io,
++ !reset->enable);
++ msleep(20);
++ }
++ }
+ }
+
+ return 0;
diff --git a/projects/Rockchip/patches/linux/rockchip-4.4/linux-0005-dts.patch b/projects/Rockchip/patches/linux/rockchip-4.4/linux-0005-dts.patch
index b6d64270ed..fa68eb7ccc 100644
--- a/projects/Rockchip/patches/linux/rockchip-4.4/linux-0005-dts.patch
+++ b/projects/Rockchip/patches/linux/rockchip-4.4/linux-0005-dts.patch
@@ -8168,3 +8168,946 @@ index 000000000000..a7c14f6ccd92
+&vopl_mmu {
+ status = "disabled";
+};
+
+From 2de45e1b4eb4b763bf396e768e5add15433ba0f0 Mon Sep 17 00:00:00 2001
+From: Jonas Karlman
+Date: Sat, 19 Jan 2019 19:31:40 +0100
+Subject: [PATCH] arm64: dts: rockchip: add rk3399-rock-pi-4 board
+
+---
+ arch/arm64/boot/dts/rockchip/rk3399-rock-pi-4.dts | 926 ++++++++++++++++++++++
+ 1 file changed, 926 insertions(+)
+ create mode 100644 arch/arm64/boot/dts/rockchip/rk3399-rock-pi-4.dts
+
+diff --git a/arch/arm64/boot/dts/rockchip/rk3399-rock-pi-4.dts b/arch/arm64/boot/dts/rockchip/rk3399-rock-pi-4.dts
+new file mode 100644
+index 000000000000..7b376aee97c4
+--- /dev/null
++++ b/arch/arm64/boot/dts/rockchip/rk3399-rock-pi-4.dts
+@@ -0,0 +1,926 @@
++/*
++ * Copyright (c) 2016 Fuzhou Rockchip Electronics Co., Ltd
++ *
++ * This file is dual-licensed: you can use it either under the terms
++ * of the GPL or the X11 license, at your option. Note that this dual
++ * licensing only applies to this file, and not this project as a
++ * whole.
++ *
++ * a) This file is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of the
++ * License, or (at your option) any later version.
++ *
++ * This file is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * Or, alternatively,
++ *
++ * b) Permission is hereby granted, free of charge, to any person
++ * obtaining a copy of this software and associated documentation
++ * files (the "Software"), to deal in the Software without
++ * restriction, including without limitation the rights to use,
++ * copy, modify, merge, publish, distribute, sublicense, and/or
++ * sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following
++ * conditions:
++ *
++ * The above copyright notice and this permission notice shall be
++ * included in all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
++ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
++ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
++ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
++ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
++ * OTHER DEALINGS IN THE SOFTWARE.
++ */
++
++/dts-v1/;
++#include
++#include
++#include "rk3399.dtsi"
++#include "rk3399-linux.dtsi"
++#include "rk3399-opp.dtsi"
++
++/ {
++ model = "Radxa ROCK Pi 4";
++ compatible = "radxa,rockpi4", "rockchip,rk3399";
++
++ xin32k: xin32k {
++ compatible = "fixed-clock";
++ clock-frequency = <32768>;
++ clock-output-names = "xin32k";
++ #clock-cells = <0>;
++ };
++
++ clkin_gmac: external-gmac-clock {
++ compatible = "fixed-clock";
++ clock-frequency = <125000000>;
++ clock-output-names = "clkin_gmac";
++ #clock-cells = <0>;
++ };
++
++ vcc1v8_s0: vcc1v8-s0 {
++ compatible = "regulator-fixed";
++ regulator-name = "vcc1v8_s0";
++ regulator-min-microvolt = <1800000>;
++ regulator-max-microvolt = <1800000>;
++ regulator-always-on;
++ };
++
++ vcc_sys: vcc-sys {
++ compatible = "regulator-fixed";
++ regulator-name = "vcc_sys";
++ regulator-min-microvolt = <5000000>;
++ regulator-max-microvolt = <5000000>;
++ regulator-always-on;
++ };
++
++ vcc_phy: vcc-phy-regulator {
++ compatible = "regulator-fixed";
++ regulator-name = "vcc_phy";
++ regulator-always-on;
++ regulator-boot-on;
++ };
++
++ vcc3v3_sys: vcc3v3-sys {
++ compatible = "regulator-fixed";
++ regulator-name = "vcc3v3_sys";
++ regulator-min-microvolt = <3300000>;
++ regulator-max-microvolt = <3300000>;
++ regulator-always-on;
++ vin-supply = <&vcc_sys>;
++ };
++
++ vcc3v3_pcie: vcc3v3-pcie-regulator {
++ compatible = "regulator-fixed";
++ enable-active-high;
++ gpio = <&gpio2 RK_PD2 GPIO_ACTIVE_HIGH>;
++ pinctrl-names = "default";
++ pinctrl-0 = <&pcie_drv>;
++ regulator-boot-on;
++ regulator-always-on;
++ regulator-name = "vcc3v3_pcie";
++ vin-supply = <&vcc3v3_sys>;
++ };
++
++ vcc5v0_host: vcc5v0-host-regulator {
++ compatible = "regulator-fixed";
++ enable-active-high;
++ gpio = <&gpio4 RK_PD1 GPIO_ACTIVE_HIGH>;
++ pinctrl-names = "default";
++ pinctrl-0 = <&host_vbus_drv>;
++ regulator-name = "vcc5v0_host";
++ regulator-always-on;
++ };
++
++ vcc5v0_otg: vcc5v0-otg-regulator {
++ compatible = "regulator-fixed";
++ enable-active-high;
++ gpio = <&gpio1 RK_PA3 GPIO_ACTIVE_HIGH>;
++ pinctrl-names = "default";
++ pinctrl-0 = <&otg_vbus_drv>;
++ regulator-name = "vcc5v0_otg";
++ regulator-always-on;
++ };
++
++ vdd_log: vdd-log {
++ compatible = "pwm-regulator";
++ pwms = <&pwm2 0 25000 1>;
++ regulator-name = "vdd_log";
++ regulator-min-microvolt = <800000>;
++ regulator-max-microvolt = <1400000>;
++ regulator-always-on;
++ regulator-boot-on;
++
++ /* for rockchip boot on */
++ rockchip,pwm_id= <2>;
++ rockchip,pwm_voltage = <900000>;
++
++ vin-supply = <&vcc_sys>;
++ };
++
++ leds {
++ compatible = "gpio-leds";
++
++ user-led1 {
++ gpios=<&gpio3 RK_PD4 GPIO_ACTIVE_HIGH>;
++ linux,default-trigger = "mmc0";
++ };
++
++ user-led2 {
++ gpios=<&gpio3 RK_PD5 GPIO_ACTIVE_HIGH>;
++ linux,default-trigger = "heartbeat";
++ };
++ };
++
++ hdmi-sound {
++ compatible = "simple-audio-card";
++ simple-audio-card,format = "i2s";
++ simple-audio-card,mclk-fs = <256>;
++ simple-audio-card,name = "HDMI";
++ simple-audio-card,cpu {
++ sound-dai = <&i2s2>;
++ };
++ simple-audio-card,codec {
++ sound-dai = <&hdmi>;
++ };
++ };
++
++ es8316-sound {
++ compatible = "simple-audio-card";
++ simple-audio-card,format = "i2s";
++ simple-audio-card,name = "ES8316";
++ simple-audio-card,mclk-fs = <256>;
++ simple-audio-card,widgets =
++ "Microphone", "Mic Jack",
++ "Headphone", "Headphone Jack";
++ simple-audio-card,routing =
++ "Mic Jack", "MICBIAS1",
++ "IN1P", "Mic Jack",
++ "Headphone Jack", "HPOL",
++ "Headphone Jack", "HPOR";
++ simple-audio-card,cpu {
++ sound-dai = <&i2s0>;
++ };
++ simple-audio-card,codec {
++ sound-dai = <&es8316>;
++ };
++ };
++
++ sdio_pwrseq: sdio-pwrseq {
++ compatible = "mmc-pwrseq-simple";
++ clocks = <&rk808 1>;
++ clock-names = "ext_clock";
++ pinctrl-names = "default";
++ pinctrl-0 = <&wifi_enable_h>;
++
++ /*
++ * On the module itself this is one of these (depending
++ * on the actual card populated):
++ * - SDIO_RESET_L_WL_REG_ON
++ * - PDN (power down when low)
++ */
++ reset-gpios = <&gpio0 RK_PB2 GPIO_ACTIVE_LOW>;
++ };
++
++ wireless-wlan {
++ compatible = "wlan-platdata";
++ rockchip,grf = <&grf>;
++ wifi_chip_type = "ap6256";
++ sdio_vref = <1800>;
++ WIFI,host_wake_irq = <&gpio0 RK_PA3 GPIO_ACTIVE_HIGH>;
++ status = "okay";
++ };
++
++ wireless-bluetooth {
++ compatible = "bluetooth-platdata";
++ clocks = <&rk808 1>;
++ clock-names = "ext_clock";
++ uart_rts_gpios = <&gpio2 RK_PC3 GPIO_ACTIVE_LOW>;
++ pinctrl-names = "default", "rts_gpio";
++ pinctrl-0 = <&uart0_rts>;
++ pinctrl-1 = <&uart0_gpios>;
++ BT,reset_gpio = <&gpio0 RK_PB1 GPIO_ACTIVE_HIGH>;
++ BT,wake_gpio = <&gpio2 RK_PD3 GPIO_ACTIVE_HIGH>;
++ BT,wake_host_irq = <&gpio0 RK_PA4 GPIO_ACTIVE_HIGH>;
++ status = "okay";
++ };
++};
++
++&cpu_l0 {
++ cpu-supply = <&vdd_cpu_l>;
++};
++
++&cpu_l1 {
++ cpu-supply = <&vdd_cpu_l>;
++};
++
++&cpu_l2 {
++ cpu-supply = <&vdd_cpu_l>;
++};
++
++&cpu_l3 {
++ cpu-supply = <&vdd_cpu_l>;
++};
++
++&cpu_b0 {
++ cpu-supply = <&vdd_cpu_b>;
++};
++
++&cpu_b1 {
++ cpu-supply = <&vdd_cpu_b>;
++};
++
++&display_subsystem {
++ status = "okay";
++
++ route {
++ route_hdmi: route-hdmi {
++ status = "okay";
++ connect = <&vopb_out_hdmi>;
++ };
++ };
++};
++
++&emmc_phy {
++ status = "okay";
++};
++
++&i2c0 {
++ status = "okay";
++ i2c-scl-rising-time-ns = <168>;
++ i2c-scl-falling-time-ns = <4>;
++ clock-frequency = <400000>;
++
++ vdd_cpu_b: syr827@40 {
++ compatible = "silergy,syr827";
++ reg = <0x40>;
++ regulator-compatible = "fan53555-reg";
++ pinctrl-0 = <&vsel1_gpio>;
++ vsel-gpios = <&gpio1 RK_PC1 GPIO_ACTIVE_HIGH>;
++ regulator-name = "vdd_cpu_b";
++ regulator-min-microvolt = <712500>;
++ regulator-max-microvolt = <1500000>;
++ regulator-ramp-delay = <1000>;
++ fcs,suspend-voltage-selector = <1>;
++ regulator-always-on;
++ regulator-boot-on;
++ vin-supply = <&vcc_sys>;
++ regulator-state-mem {
++ regulator-off-in-suspend;
++ };
++ };
++
++ vdd_gpu: syr828@41 {
++ compatible = "silergy,syr828";
++ reg = <0x41>;
++ regulator-compatible = "fan53555-reg";
++ pinctrl-0 = <&vsel2_gpio>;
++ vsel-gpios = <&gpio1 RK_PB6 GPIO_ACTIVE_HIGH>;
++ regulator-name = "vdd_gpu";
++ regulator-min-microvolt = <712500>;
++ regulator-max-microvolt = <1500000>;
++ regulator-ramp-delay = <1000>;
++ fcs,suspend-voltage-selector = <1>;
++ regulator-always-on;
++ regulator-boot-on;
++ vin-supply = <&vcc_sys>;
++ regulator-initial-mode = <1>; /* 1:force PWM 2:auto */
++ regulator-state-mem {
++ regulator-off-in-suspend;
++ };
++ };
++
++ rk808: pmic@1b {
++ compatible = "rockchip,rk808";
++ reg = <0x1b>;
++ interrupt-parent = <&gpio1>;
++ interrupts = <21 IRQ_TYPE_LEVEL_LOW>;
++ pinctrl-names = "default";
++ pinctrl-0 = <&pmic_int_l>;
++ rockchip,system-power-controller;
++ wakeup-source;
++ #clock-cells = <1>;
++ clock-output-names = "rk808-clkout1", "rk808-clkout2";
++
++ vcc1-supply = <&vcc_sys>;
++ vcc2-supply = <&vcc_sys>;
++ vcc3-supply = <&vcc_sys>;
++ vcc4-supply = <&vcc_sys>;
++ vcc6-supply = <&vcc_sys>;
++ vcc7-supply = <&vcc_sys>;
++ vcc8-supply = <&vcc3v3_sys>;
++ vcc9-supply = <&vcc_sys>;
++ vcc10-supply = <&vcc_sys>;
++ vcc11-supply = <&vcc_sys>;
++ vcc12-supply = <&vcc3v3_sys>;
++ vddio-supply = <&vcc_1v8>;
++
++ regulators {
++ vdd_center: DCDC_REG1 {
++ regulator-name = "vdd_center";
++ regulator-min-microvolt = <750000>;
++ regulator-max-microvolt = <1350000>;
++ regulator-ramp-delay = <6001>;
++ regulator-always-on;
++ regulator-boot-on;
++ regulator-state-mem {
++ regulator-off-in-suspend;
++ };
++ };
++
++ vdd_cpu_l: DCDC_REG2 {
++ regulator-name = "vdd_cpu_l";
++ regulator-min-microvolt = <750000>;
++ regulator-max-microvolt = <1350000>;
++ regulator-ramp-delay = <6001>;
++ regulator-always-on;
++ regulator-boot-on;
++ regulator-state-mem {
++ regulator-off-in-suspend;
++ };
++ };
++
++ vcc_ddr: DCDC_REG3 {
++ regulator-name = "vcc_ddr";
++ regulator-always-on;
++ regulator-boot-on;
++ regulator-state-mem {
++ regulator-on-in-suspend;
++ };
++ };
++
++ vcc_1v8: DCDC_REG4 {
++ regulator-name = "vcc_1v8";
++ regulator-min-microvolt = <1800000>;
++ regulator-max-microvolt = <1800000>;
++ regulator-always-on;
++ regulator-boot-on;
++ regulator-state-mem {
++ regulator-on-in-suspend;
++ regulator-suspend-microvolt = <1800000>;
++ };
++ };
++
++ vcc1v8_dvp: LDO_REG1 {
++ regulator-name = "vcc1v8_codec";
++ regulator-min-microvolt = <1800000>;
++ regulator-max-microvolt = <1800000>;
++ regulator-always-on;
++ regulator-boot-on;
++ regulator-state-mem {
++ regulator-on-in-suspend;
++ regulator-suspend-microvolt = <1800000>;
++ };
++ };
++
++ vcca1v8_hdmi: LDO_REG2 {
++ regulator-name = "vcca1v8_hdmi";
++ regulator-min-microvolt = <1800000>;
++ regulator-max-microvolt = <1800000>;
++ regulator-always-on;
++ regulator-boot-on;
++ regulator-state-mem {
++ regulator-on-in-suspend;
++ regulator-suspend-microvolt = <1800000>;
++ };
++ };
++
++ vcca_1v8: LDO_REG3 {
++ regulator-name = "vcca_1v8";
++ regulator-min-microvolt = <1800000>;
++ regulator-max-microvolt = <1800000>;
++ regulator-always-on;
++ regulator-boot-on;
++ regulator-state-mem {
++ regulator-on-in-suspend;
++ regulator-suspend-microvolt = <1800000>;
++ };
++ };
++
++ vcc_sd: LDO_REG4 {
++ regulator-name = "vcc_sd";
++ regulator-min-microvolt = <1800000>;
++ regulator-max-microvolt = <3000000>;
++ regulator-always-on;
++ regulator-boot-on;
++ regulator-state-mem {
++ regulator-on-in-suspend;
++ regulator-suspend-microvolt = <3000000>;
++ };
++ };
++
++ vcc3v0_sd: LDO_REG5 {
++ regulator-name = "vcc3v0_sd";
++ regulator-min-microvolt = <3000000>;
++ regulator-max-microvolt = <3000000>;
++ regulator-always-on;
++ regulator-boot-on;
++ regulator-state-mem {
++ regulator-on-in-suspend;
++ regulator-suspend-microvolt = <3000000>;
++ };
++ };
++
++ vcc_1v5: LDO_REG6 {
++ regulator-name = "vcc_1v5";
++ regulator-min-microvolt = <1500000>;
++ regulator-max-microvolt = <1500000>;
++ regulator-always-on;
++ regulator-boot-on;
++ regulator-state-mem {
++ regulator-on-in-suspend;
++ regulator-suspend-microvolt = <1500000>;
++ };
++ };
++
++ vcca0v9_hdmi: LDO_REG7 {
++ regulator-name = "vcca0v9_hdmi";
++ regulator-min-microvolt = <900000>;
++ regulator-max-microvolt = <900000>;
++ regulator-always-on;
++ regulator-boot-on;
++ regulator-state-mem {
++ regulator-on-in-suspend;
++ regulator-suspend-microvolt = <900000>;
++ };
++ };
++
++ vcc_3v0: LDO_REG8 {
++ regulator-name = "vcc_3v0";
++ regulator-min-microvolt = <3000000>;
++ regulator-max-microvolt = <3000000>;
++ regulator-always-on;
++ regulator-boot-on;
++ regulator-state-mem {
++ regulator-on-in-suspend;
++ regulator-suspend-microvolt = <3000000>;
++ };
++ };
++
++ vcc3v3_s3: SWITCH_REG1 {
++ regulator-name = "vcc3v3_s3";
++ regulator-always-on;
++ regulator-boot-on;
++ regulator-state-mem {
++ regulator-on-in-suspend;
++ };
++ };
++
++ vcc3v3_s0: SWITCH_REG2 {
++ regulator-name = "vcc3v3_s0";
++ regulator-always-on;
++ regulator-boot-on;
++ regulator-state-mem {
++ regulator-on-in-suspend;
++ };
++ };
++ };
++ };
++};
++
++&i2c1 {
++ status = "okay";
++ i2c-scl-rising-time-ns = <300>;
++ i2c-scl-falling-time-ns = <15>;
++
++ es8316: es8316@11 {
++ #sound-dai-cells = <0>;
++ compatible = "everest,es8316";
++ reg = <0x11>;
++ clocks = <&cru SCLK_I2S_8CH_OUT>;
++ clock-names = "mclk";
++ pinctrl-names = "default";
++ pinctrl-0 = <&i2s_8ch_mclk>;
++ };
++};
++
++&i2s0 {
++ status = "okay";
++ rockchip,i2s-broken-burst-len;
++ rockchip,playback-channels = <8>;
++ rockchip,capture-channels = <8>;
++ #sound-dai-cells = <0>;
++};
++
++&i2s2 {
++ #sound-dai-cells = <0>;
++ rockchip,bclk-fs = <128>;
++ status = "okay";
++};
++
++&gmac {
++ phy-supply = <&vcc_phy>;
++ phy-mode = "rgmii";
++ clock_in_out = "input";
++ snps,reset-gpio = <&gpio3 RK_PB7 GPIO_ACTIVE_LOW>;
++ snps,reset-active-low;
++ snps,reset-delays-us = <0 10000 50000>;
++ assigned-clocks = <&cru SCLK_RMII_SRC>;
++ assigned-clock-parents = <&clkin_gmac>;
++ pinctrl-names = "default", "sleep";
++ pinctrl-0 = <&rgmii_pins>;
++ pinctrl-1 = <&rgmii_sleep_pins>;
++ tx_delay = <0x28>;
++ rx_delay = <0x11>;
++ status = "okay";
++};
++
++&gpu {
++ status = "okay";
++ mali-supply = <&vdd_gpu>;
++};
++
++&hdmi {
++ #address-cells = <1>;
++ #size-cells = <0>;
++ #sound-dai-cells = <0>;
++ status = "okay";
++};
++
++&iep {
++ status = "okay";
++};
++
++&iep_mmu {
++ status = "okay";
++};
++
++&io_domains {
++ status = "okay";
++
++ bt656-supply = <&vcc_3v0>; /* bt656_gpio2ab_ms */
++ audio-supply = <&vcc_3v0>; /* audio_gpio3d4a_ms */
++ sdmmc-supply = <&vcc_sd>; /* sdmmc_gpio4b_ms */
++ gpio1830-supply = <&vcc_3v0>; /* gpio1833_gpio4cd_ms */
++};
++
++&saradc {
++ status = "okay";
++};
++
++&sdmmc {
++ clock-frequency = <100000000>;
++ max-frequency = <100000000>;
++ supports-sd;
++ bus-width = <4>;
++ cap-mmc-highspeed;
++ cap-sd-highspeed;
++ disable-wp;
++ num-slots = <1>;
++ sd-uhs-sdr12;
++ sd-uhs-sdr25;
++ sd-uhs-sdr50;
++ sd-uhs-sdr104;
++ vqmmc-supply = <&vcc_sd>;
++ pinctrl-names = "default";
++ pinctrl-0 = <&sdmmc_clk &sdmmc_cmd &sdmmc_cd &sdmmc_bus4>;
++ card-detect-delay = <800>;
++ status = "okay";
++};
++
++&sdio0 {
++ clock-frequency = <100000000>;
++ max-frequency = <100000000>;
++ supports-sdio;
++ bus-width = <4>;
++ disable-wp;
++ cap-sd-highspeed;
++ cap-sdio-irq;
++ keep-power-in-suspend;
++ mmc-pwrseq = <&sdio_pwrseq>;
++ non-removable;
++ num-slots = <1>;
++ pinctrl-names = "default";
++ pinctrl-0 = <&sdio0_bus4 &sdio0_cmd &sdio0_clk>;
++ sd-uhs-sdr104;
++ status = "okay";
++};
++
++&sdhci {
++ bus-width = <8>;
++ mmc-hs400-1_8v;
++ mmc-hs400-enhanced-strobe;
++ supports-emmc;
++ non-removable;
++ status = "okay";
++};
++
++&threshold {
++ temperature = <85000>;
++};
++
++&target {
++ temperature = <100000>;
++};
++
++&soc_crit {
++ temperature = <105000>;
++};
++
++&tcphy0 {
++ status = "okay";
++};
++
++&tcphy1 {
++ status = "okay";
++};
++
++&tsadc {
++ /* tshut mode 0:CRU 1:GPIO */
++ rockchip,hw-tshut-mode = <1>;
++ /* tshut polarity 0:LOW 1:HIGH */
++ rockchip,hw-tshut-polarity = <1>;
++ rockchip,hw-tshut-temp = <110000>;
++ status = "okay";
++};
++
++&u2phy0 {
++ status = "okay";
++ enable-active-high;
++ /* otg-vbus-gpios = <&gpio1 3 GPIO_ACTIVE_HIGH>;*/
++ gpio = <&gpio1 3 GPIO_ACTIVE_HIGH>;
++
++ u2phy0_otg: otg-port {
++ status = "okay";
++ };
++
++ u2phy0_host: host-port {
++ phy-supply = <&vcc5v0_host>;
++ status = "okay";
++ };
++};
++
++&u2phy1 {
++ status = "okay";
++
++ u2phy1_otg: otg-port {
++ status = "okay";
++ };
++
++ u2phy1_host: host-port {
++ phy-supply = <&vcc5v0_host>;
++ status = "okay";
++ };
++};
++
++&uart0 {
++ pinctrl-names = "default";
++ pinctrl-0 = <&uart0_xfer &uart0_cts>;
++ status = "okay";
++};
++
++&uart2 {
++ status = "okay";
++};
++
++&uart4 {
++ status = "okay";
++};
++
++&usb_host0_ehci {
++ status = "okay";
++};
++
++&usb_host0_ohci {
++ status = "okay";
++};
++
++&usb_host1_ehci {
++ status = "okay";
++};
++
++&usb_host1_ohci {
++ status = "okay";
++};
++
++&usbdrd3_0 {
++ extcon = <&u2phy0>;
++ status = "okay";
++};
++
++&usbdrd_dwc3_0 {
++ dr_mode = "otg";
++ status = "okay";
++};
++
++&usbdrd3_1 {
++ status = "okay";
++};
++
++&usbdrd_dwc3_1 {
++ dr_mode = "host";
++ status = "okay";
++};
++
++&pwm2 {
++ status = "okay";
++};
++
++&pinctrl {
++ gmac {
++ rgmii_sleep_pins: rgmii-sleep-pins {
++ rockchip,pins =
++ <3 RK_PB7 RK_FUNC_GPIO &pcfg_output_low>;
++ };
++ };
++
++ i2c4 {
++ i2c4_xfer: i2c4-xfer {
++ rockchip,pins =
++ <1 12 RK_FUNC_1 &pcfg_pull_up>,
++ <1 11 RK_FUNC_1 &pcfg_pull_up>;
++ };
++ };
++
++ i2s0 {
++ i2s0_8ch_bus: i2s0-8ch-bus {
++ rockchip,pins =
++ <3 28 0 &pcfg_pull_none>,
++ <3 29 0 &pcfg_pull_none>;
++ };
++ };
++
++ pcie {
++ pcie_drv: pcie-drv {
++ rockchip,pins =
++ <2 RK_PD2 RK_FUNC_GPIO &pcfg_pull_none>;
++ };
++ };
++
++ pmic {
++ pmic_int_l: pmic-int-l {
++ rockchip,pins =
++ <1 RK_PC5 RK_FUNC_GPIO &pcfg_pull_up>;
++ };
++
++ vsel1_gpio: vsel1-gpio {
++ rockchip,pins =
++ <1 RK_PC1 RK_FUNC_GPIO &pcfg_pull_down>;
++ };
++
++ vsel2_gpio: vsel2-gpio {
++ rockchip,pins =
++ <1 RK_PB6 RK_FUNC_GPIO &pcfg_pull_down>;
++ };
++ };
++
++ sdio-pwrseq {
++ wifi_enable_h: wifi-enable-h {
++ rockchip,pins =
++ <0 RK_PB2 RK_FUNC_GPIO &pcfg_pull_none>;
++ };
++ };
++
++ sdio0 {
++ sdio0_bus1: sdio0-bus1 {
++ rockchip,pins =
++ <2 RK_PC4 RK_FUNC_1 &pcfg_pull_up_20ma>;
++ };
++
++ sdio0_bus4: sdio0-bus4 {
++ rockchip,pins =
++ <2 RK_PC4 RK_FUNC_1 &pcfg_pull_up_20ma>,
++ <2 RK_PC5 RK_FUNC_1 &pcfg_pull_up_20ma>,
++ <2 RK_PC6 RK_FUNC_1 &pcfg_pull_up_20ma>,
++ <2 RK_PC7 RK_FUNC_1 &pcfg_pull_up_20ma>;
++ };
++
++ sdio0_cmd: sdio0-cmd {
++ rockchip,pins =
++ <2 RK_PD0 RK_FUNC_1 &pcfg_pull_up_20ma>;
++ };
++
++ sdio0_clk: sdio0-clk {
++ rockchip,pins =
++ <2 RK_PD1 RK_FUNC_1 &pcfg_pull_none_20ma>;
++ };
++ };
++
++ sdmmc {
++ sdmmc_bus1: sdmmc-bus1 {
++ rockchip,pins =
++ <4 RK_PB0 RK_FUNC_1 &pcfg_pull_up_8ma>;
++ };
++
++ sdmmc_bus4: sdmmc-bus4 {
++ rockchip,pins =
++ <4 RK_PB0 RK_FUNC_1 &pcfg_pull_up_8ma>,
++ <4 RK_PB1 RK_FUNC_1 &pcfg_pull_up_8ma>,
++ <4 RK_PB2 RK_FUNC_1 &pcfg_pull_up_8ma>,
++ <4 RK_PB3 RK_FUNC_1 &pcfg_pull_up_8ma>;
++ };
++
++ sdmmc_clk: sdmmc-clk {
++ rockchip,pins =
++ <4 RK_PB4 RK_FUNC_1 &pcfg_pull_none_18ma>;
++ };
++
++ sdmmc_cmd: sdmmc-cmd {
++ rockchip,pins =
++ <4 RK_PB5 RK_FUNC_1 &pcfg_pull_up_8ma>;
++ };
++ };
++
++ usb2 {
++ host_vbus_drv: host-vbus-drv {
++ rockchip,pins =
++ <4 RK_PD1 RK_FUNC_GPIO &pcfg_pull_none>;
++ };
++
++ otg_vbus_drv: otg-vbus-drv {
++ rockchip,pins =
++ <1 3 RK_FUNC_GPIO &pcfg_pull_none>;
++ };
++ };
++
++ wireless-bluetooth {
++ uart0_gpios: uart0-gpios {
++ rockchip,pins =
++ <2 RK_PC3 RK_FUNC_GPIO &pcfg_pull_none>;
++ };
++ };
++};
++
++&pvtm {
++ status = "okay";
++};
++
++&pmu_pvtm {
++ status = "okay";
++};
++
++&pmu_io_domains {
++ status = "okay";
++ pmu1830-supply = <&vcc_3v0>;
++};
++
++&pcie_phy {
++ status = "okay";
++};
++
++&pcie0 {
++ ep-gpios = <&gpio4 27 GPIO_ACTIVE_HIGH>;
++ num-lanes = <4>;
++ pinctrl-names = "default";
++ pinctrl-0 = <&pcie_clkreqnb_cpm>;
++ status = "okay";
++};
++
++&rkvdec {
++ status = "okay";
++};
++
++&vdec_mmu {
++ status = "okay";
++};
++
++&vpu {
++ status = "okay";
++};
++
++&vpu_mmu {
++ status = "okay";
++};
++
++&vopb {
++ status = "okay";
++};
++
++&vopb_mmu {
++ status = "okay";
++};
++
++&vopl {
++ status = "disabled";
++};
++
++&vopl_mmu {
++ status = "disabled";
++};
diff --git a/scripts/uboot_helper b/scripts/uboot_helper
index 1792159a66..c7e7e5c0c7 100755
--- a/scripts/uboot_helper
+++ b/scripts/uboot_helper
@@ -22,6 +22,7 @@ devices = {
'RK3399' : {
'khadas-edge' : { 'dtb' : 'rk3399-khadas-edge.dtb', 'config' : 'evb-rk3399_config' },
'rock960' : { 'dtb' : 'rk3399-rock960.dtb', 'config' : 'evb-rk3399_config' },
+ 'rock-pi-4' : { 'dtb' : 'rk3399-rock-pi-4.dtb', 'config' : 'evb-rk3399_config' },
'rockpro64' : { 'dtb' : 'rk3399-rockpro64.dtb', 'config' : 'evb-rk3399_config' },
'sapphire' : { 'dtb' : 'rk3399-sapphire.dtb', 'config' : 'evb-rk3399_config' },
},