From 9934ab8283b42a105b45ec73d9f1245944cd8406 Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Mon, 28 Jan 2019 20:40:06 +0000 Subject: [PATCH] RK3399: add rock-pi-4 --- projects/Rockchip/README.md | 1 + projects/Rockchip/devices/RK3399/README.md | 1 + projects/Rockchip/devices/RK3399/options | 1 + .../rockchip-4.4/linux-0001-rockchip.patch | 22864 ++++++++++++++++ .../linux/rockchip-4.4/linux-0005-dts.patch | 943 + scripts/uboot_helper | 1 + 6 files changed, 23811 insertions(+) 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' }, },